Skip to content

Commit

Permalink
add new builtin
Browse files Browse the repository at this point in the history
Add new builtin functions:
- to_lower
- to_upper
- capitalize
- round
- floor
- ceil
- replace
- replace_all
  • Loading branch information
nbittich committed Aug 18, 2024
1 parent f623e43 commit 176f127
Show file tree
Hide file tree
Showing 7 changed files with 487 additions and 47 deletions.
78 changes: 43 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -747,41 +747,49 @@ You already have seen `length` to find the length of an array or string, `includ

Here is a list of built-in functions available:

| name | description | example |
| ----------- | ---------------------------- | ------------------------------------------ |
| sqrt | square root | `sqrt(2)` |
| abs | absolute value | `abs(-2)` |
| log | logarithm | `log(2)` |
| ln | natural logarithm | `ln(2)` |
| length | length of an array or string | `length("azert")` |
| sin | sine of a number | `sin(2)` |
| cos | cosine of a number | `cos(2)` |
| tan | tangent of a number | `tan(2.2)` |
| print | print without a newline | `print("hello")` |
| println | print with a newline | `println("hello")` |
| include | include a script | `include("scripts/test_fn.adana")` |
| require | load a shared object | `require("my_lib.so")` |
| to_int | cast to int | `to_int("2")`<br>`to_int(2.2)` |
| to_hex | format num to hex | `to_hex(2)`<br>`to_hex(2.2)` |
| to_binary | format num to binary | `to_binary(2)` |
| to_double | cast to double | `to_double("2.2")` |
| to_bool | cast to bool | `to_bool("true")` |
| to_string | cast to string | `to_string(true)` |
| drop | drop a variable from context | `drop("myvar")`<br>`drop(arr[0])` |
| eval | Evaluate a string as code | `eval("sqrt(9)")` |
| type_of | Type of variable | `type_of(true)` |
| is_u8 | Check if u8 | `is_u8(0x1)` |
| is_i8 | Check if i8 | `is_i8(-1)` |
| is_int | Check if int | `is_int(512)` |
| is_double | Check if double | `is_double(1.2)` |
| is_function | Check if function | `is_function(()=> {1})` |
| is_struct | Check if struct | `is_struct(struct {})` |
| is_bool | Check if bool | `is_bool(false)` |
| is_array | Check if array | `is_bool([1,2])` |
| is_error | Check if error | `is_error(err)` |
| make_err | Create an error | `make_err("oops")` |
| is_match | Check matching regex | `is_match("AaAaAbbBBBb", "(?i)a+(?-i)b+")` |
| match | Match regex | `match("AaAaAbbBBBb", "(?i)a+(?-i)b+")` |
| name | description | example |
| ----------- | ---------------------------- | ------------------------------------------------ |
| floor | floor double | `floor(4.7)` |
| ceil | ceil double | `ceil(4.7)` |
| round | round to places | `round(4.46790, 3)` |
| sqrt | square root | `sqrt(2)` |
| abs | absolute value | `abs(-2)` |
| log | logarithm | `log(2)` |
| ln | natural logarithm | `ln(2)` |
| length | length of an array or string | `length("azert")` |
| sin | sine of a number | `sin(2)` |
| cos | cosine of a number | `cos(2)` |
| tan | tangent of a number | `tan(2.2)` |
| print | print without a newline | `print("hello")` |
| println | print with a newline | `println("hello")` |
| include | include a script | `include("scripts/test_fn.adana")` |
| require | load a shared object | `require("my_lib.so")` |
| to_int | cast to int | `to_int("2")`<br>`to_int(2.2)` |
| to_hex | format num to hex | `to_hex(2)`<br>`to_hex(2.2)` |
| to_binary | format num to binary | `to_binary(2)` |
| to_double | cast to double | `to_double("2.2")` |
| to_bool | cast to bool | `to_bool("true")` |
| to_string | cast to string | `to_string(true)` |
| drop | drop a variable from context | `drop("myvar")`<br>`drop(arr[0])` |
| eval | evaluate a string as code | `eval("sqrt(9)")` |
| type_of | type of variable | `type_of(true)` |
| is_u8 | check if u8 | `is_u8(0x1)` |
| is_i8 | check if i8 | `is_i8(-1)` |
| is_int | check if int | `is_int(512)` |
| is_double | check if double | `is_double(1.2)` |
| is_function | check if function | `is_function(()=> {1})` |
| is_struct | check if struct | `is_struct(struct {})` |
| is_bool | check if bool | `is_bool(false)` |
| is_array | check if array | `is_bool([1,2])` |
| is_error | check if error | `is_error(err)` |
| make_err | create an error | `make_err("oops")` |
| is_match | check matching regex | `is_match("AaAaAbbBBBb", "(?i)a+(?-i)b+")` |
| capitalize | capitalize str | `capitalize("nordine")` |
| to_upper | to Upper str | `to_upper("hello")` |
| to_lower | to Lower str | `to_lower("HELLO")` |
| match | match regex | `match("AaAaAbbBBBb", "(?i)a+(?-i)b+")` |
| replace | replace | `replace("AaAaAbbBBBb", "(?i)a+(?-i)b+", "xxx")` |
| replace_all | replace all | `replace_all("AaAaAbbBBBb", "A", "b")` |

#### Matching regexes

Expand Down
41 changes: 37 additions & 4 deletions adana-script-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ pub mod primitive;
use std::collections::BTreeMap;

use constants::{
BREAK, DROP, ELSE, EULER_NUMBER, FALSE, FOR, IF, IN, IS_ARRAY, IS_BOOL,
IS_DOUBLE, IS_ERROR, IS_FUNCTION, IS_I8, IS_INT, IS_MATCH, IS_STRUCT,
IS_U8, MAKE_ERROR, MATCH, MULTILINE, NULL, PI, REQUIRE, RETURN, STRUCT,
TAU, TO_BINARY, TO_HEX, TRUE, WHILE,
BREAK, CAPITALIZE, CEIL, DROP, ELSE, EULER_NUMBER, FALSE, FLOOR, FOR, IF,
IN, IS_ARRAY, IS_BOOL, IS_DOUBLE, IS_ERROR, IS_FUNCTION, IS_I8, IS_INT,
IS_MATCH, IS_STRUCT, IS_U8, MAKE_ERROR, MATCH, MULTILINE, NULL, PI,
REPLACE, REPLACE_ALL, REQUIRE, RETURN, ROUND, STRUCT, TAU, TO_BINARY,
TO_HEX, TO_LOWER, TO_UPPER, TRUE, WHILE,
};

use primitive::Primitive;
Expand Down Expand Up @@ -48,6 +49,14 @@ pub mod constants {
pub const TO_BINARY: &str = "to_binary";
pub const TO_INT: &str = "to_int";
pub const TO_BOOL: &str = "to_bool";
pub const TO_UPPER: &str = "to_upper";
pub const TO_LOWER: &str = "to_lower";
pub const CAPITALIZE: &str = "capitalize";
pub const REPLACE: &str = "replace";
pub const REPLACE_ALL: &str = "replace_all";
pub const FLOOR: &str = "floor";
pub const CEIL: &str = "ceil";
pub const ROUND: &str = "round";
pub const TO_DOUBLE: &str = "to_double";
pub const TO_STRING: &str = "to_string";
pub const IS_ERROR: &str = "is_error";
Expand Down Expand Up @@ -194,6 +203,14 @@ pub enum BuiltInFunctionType {
ToHex,
ToDouble,
ToBool,
ToUpper,
ToLower,
Capitalize,
Replace,
ReplaceAll,
Floor,
Round,
Ceil,
ToString,
Tan,
Println,
Expand Down Expand Up @@ -293,6 +310,14 @@ impl BuiltInFunctionType {
BuiltInFunctionType::ToBinary => TO_BINARY,
BuiltInFunctionType::ToDouble => TO_DOUBLE,
BuiltInFunctionType::ToBool => TO_BOOL,
BuiltInFunctionType::ToUpper => TO_UPPER,
BuiltInFunctionType::ToLower => TO_LOWER,
BuiltInFunctionType::Capitalize => CAPITALIZE,
BuiltInFunctionType::Replace => REPLACE,
BuiltInFunctionType::ReplaceAll => REPLACE_ALL,
BuiltInFunctionType::Floor => FLOOR,
BuiltInFunctionType::Ceil => CEIL,
BuiltInFunctionType::Round => ROUND,
BuiltInFunctionType::ToString => TO_STRING,
BuiltInFunctionType::IsError => IS_ERROR,
BuiltInFunctionType::IsU8 => IS_U8,
Expand Down Expand Up @@ -352,6 +377,14 @@ pub const FORBIDDEN_VARIABLE_NAME: &[&str] = &[
TO_INT,
TO_DOUBLE,
TO_HEX,
CEIL,
ROUND,
FLOOR,
TO_UPPER,
TO_LOWER,
REPLACE,
REPLACE_ALL,
CAPITALIZE,
IS_MATCH,
MATCH,
TO_BINARY,
Expand Down
204 changes: 200 additions & 4 deletions adana-script-core/src/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,14 @@ impl Primitive {
Ok(v)
}
}
pub trait Match {
pub trait StringManipulation {
fn match_regex(&self, regex: &Primitive) -> Self;
fn is_match(&self, regex: &Primitive) -> Self;
fn replace(&self, regex: &Primitive, new_value: &Primitive) -> Self;
fn replace_all(&self, regex: &Primitive, new_value: &Primitive) -> Self;
fn to_upper(&self) -> Self;
fn to_lower(&self) -> Self;
fn capitalize(&self) -> Self;
}

pub trait TypeOf {
Expand Down Expand Up @@ -302,6 +307,11 @@ pub trait Div {
pub trait Rem {
fn rem(&self, rhs: &Self) -> Self;
}
pub trait Round {
fn floor(&self) -> Self;
fn round(&self, decimals: &Self) -> Self;
fn ceil(&self) -> Self;
}
// endregion traits

// region: impl primitive
Expand Down Expand Up @@ -1730,12 +1740,78 @@ impl PartialOrd for Primitive {
}
}

impl Match for Primitive {
impl Round for Primitive {
fn floor(&self) -> Self {
match self {
Primitive::Ref(l) => {
let l = l.read().expect("FLOOR ERROR: could not acquire lock!");
l.floor()
}

Primitive::Int(i) => Primitive::Int(*i),
Primitive::I8(i) => Primitive::I8(*i),
Primitive::U8(i) => Primitive::U8(*i),
Primitive::Double(d) => Primitive::Double(d.floor()),

r => Primitive::Error(format!("illegal call to floor!! {r}")),
}
}

fn ceil(&self) -> Self {
match self {
Primitive::Ref(l) => {
let l = l.read().expect("CEIL ERROR: could not acquire lock!");
l.floor()
}

Primitive::Int(i) => Primitive::Int(*i),
Primitive::I8(i) => Primitive::I8(*i),
Primitive::U8(i) => Primitive::U8(*i),
Primitive::Double(d) => Primitive::Double(d.ceil()),

r => Primitive::Error(format!("illegal call to ceil!! {r}")),
}
}

fn round(&self, decimals: &Self) -> Self {
match (self, decimals) {
(Primitive::Ref(s), decimals) => {
let l = s.read().expect("ROUND ERROR: could not acquire lock!");
l.round(decimals)
}
(l, Primitive::Ref(decimals)) => {
let decimals = decimals
.read()
.expect("ROUND ERROR: could not acquire lock!");
l.round(&decimals)
}
(v @ Primitive::Double(_), Primitive::U8(u)) => {
v.round(&Primitive::Int(*u as i128))
}
(v @ Primitive::Double(_), Primitive::I8(u)) => {
v.round(&Primitive::Int(*u as i128))
}
(Primitive::Double(x), Primitive::Int(u)) => {
if u < &1 || u > &(u32::MAX as i128) {
return Primitive::Error(format!(
"illegal call to round!! {u}"
));
}
let decimals = *u as u32;
let y = 10i32.pow(decimals) as f64;
Primitive::Double((x * y).round() / y)
}
(p, r) => {
Primitive::Error(format!("illegal call to round!! {p} {r}"))
}
}
}
}
impl StringManipulation for Primitive {
fn match_regex(&self, regex: &Primitive) -> Self {
match self {
Primitive::Ref(l) => {
let l =
l.read().expect("TYPE_OF ERROR: could not acquire lock!");
let l = l.read().expect("MATCH ERROR: could not acquire lock!");
l.match_regex(regex)
}
v @ Primitive::String(s) => match regex {
Expand Down Expand Up @@ -1805,6 +1881,126 @@ impl Match for Primitive {
p => Primitive::Error(format!("illegal call to is_match!! {p}")),
}
}
fn replace(&self, regex: &Primitive, new_value: &Primitive) -> Self {
match self {
Primitive::Ref(l) => {
let l =
l.read().expect("REPLACE ERROR: could not acquire lock!");
l.match_regex(regex)
}
v @ Primitive::String(s) => match (regex, new_value) {
(Primitive::Ref(regex), new_value) => {
let r = regex
.read()
.expect("REPLACE ERROR: could not acquire lock!");
v.replace(&r, new_value)
}
(regex, Primitive::Ref(new_value)) => {
let r = new_value
.read()
.expect("REPLACE ERROR: could not acquire lock!");
v.replace(regex, &r)
}
(Primitive::String(r), Primitive::String(new_value)) => {
match regex::Regex::new(r) {
Ok(re) => Primitive::String(
re.replace(s, new_value).to_string(),
),
Err(e) => {
Primitive::Error(format!("replace error: {e}"))
}
}
}
(r, l) => {
Primitive::Error(format!("bad call to replace! {r} {l}"))
}
},
p => Primitive::Error(format!("illegal call to replace!! {p}")),
}
}
fn replace_all(&self, regex: &Primitive, new_value: &Primitive) -> Self {
match self {
Primitive::Ref(l) => {
let l = l
.read()
.expect("REPLACE_ALL ERROR: could not acquire lock!");
l.match_regex(regex)
}
v @ Primitive::String(s) => match (regex, new_value) {
(Primitive::Ref(regex), new_value) => {
let r = regex
.read()
.expect("REPLACE_ALL ERROR: could not acquire lock!");
v.replace(&r, new_value)
}
(regex, Primitive::Ref(new_value)) => {
let r = new_value
.read()
.expect("REPLACE_ALL ERROR: could not acquire lock!");
v.replace(regex, &r)
}
(Primitive::String(r), Primitive::String(new_value)) => {
match regex::Regex::new(r) {
Ok(re) => Primitive::String(
re.replace_all(s, new_value).to_string(),
),
Err(e) => {
Primitive::Error(format!("replace_all error: {e}"))
}
}
}
(r, l) => Primitive::Error(format!(
"bad call to replace_all! {r} {l}"
)),
},
p => {
Primitive::Error(format!("illegal call to replace_all!! {p}"))
}
}
}
fn to_upper(&self) -> Self {
match self {
Primitive::Ref(l) => {
let l =
l.read().expect("TO_UPPER ERROR: could not acquire lock!");
l.to_upper()
}
Primitive::String(s) => Primitive::String(s.to_uppercase()),
p => Primitive::Error(format!("illegal call to to_upper!! {p}")),
}
}

fn to_lower(&self) -> Self {
match self {
Primitive::Ref(l) => {
let l =
l.read().expect("TO_LOWER ERROR: could not acquire lock!");
l.to_upper()
}
Primitive::String(s) => Primitive::String(s.to_lowercase()),
p => Primitive::Error(format!("illegal call to to_lower!! {p}")),
}
}

fn capitalize(&self) -> Self {
match self {
Primitive::Ref(l) => {
let l = l
.read()
.expect("CAPITALIZE ERROR: could not acquire lock!");
l.to_upper()
}
Primitive::String(s) => {
let mut c = s.chars();
let new_s = match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().chain(c).collect(),
};
Primitive::String(new_s)
}
p => Primitive::Error(format!("illegal call to capitalize!! {p}")),
}
}
}

impl TypeOf for Primitive {
Expand Down
Loading

0 comments on commit 176f127

Please sign in to comment.