diff --git a/README.md b/README.md index 630cc13..14cdcb6 100644 --- a/README.md +++ b/README.md @@ -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")`
`to_int(2.2)` | -| to_hex | format num to hex | `to_hex(2)`
`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")`
`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")`
`to_int(2.2)` | +| to_hex | format num to hex | `to_hex(2)`
`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")`
`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 diff --git a/adana-script-core/src/lib.rs b/adana-script-core/src/lib.rs index 9f4e1b7..fd587eb 100644 --- a/adana-script-core/src/lib.rs +++ b/adana-script-core/src/lib.rs @@ -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; @@ -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"; @@ -194,6 +203,14 @@ pub enum BuiltInFunctionType { ToHex, ToDouble, ToBool, + ToUpper, + ToLower, + Capitalize, + Replace, + ReplaceAll, + Floor, + Round, + Ceil, ToString, Tan, Println, @@ -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, @@ -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, diff --git a/adana-script-core/src/primitive.rs b/adana-script-core/src/primitive.rs index 26f285a..f3ad1d3 100644 --- a/adana-script-core/src/primitive.rs +++ b/adana-script-core/src/primitive.rs @@ -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 { @@ -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 @@ -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 { @@ -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 { diff --git a/adana-script/src/compute.rs b/adana-script/src/compute.rs index 5ce7910..89825ad 100644 --- a/adana-script/src/compute.rs +++ b/adana-script/src/compute.rs @@ -14,10 +14,10 @@ use super::{ast::to_ast, require_dynamic_lib::require_dynamic_lib}; use adana_script_core::{ primitive::{ Abs, Add, And, Array, BitShift, Cos, DisplayBinary, DisplayHex, Div, - Logarithm, Match, Mul, Neg, Not, Or, Pow, Primitive, RefPrimitive, Rem, - Sin, Sqrt, Sub, Tan, ToBool, ToNumber, TypeOf, TYPE_ARRAY, TYPE_BOOL, - TYPE_DOUBLE, TYPE_ERROR, TYPE_FUNCTION, TYPE_I8, TYPE_INT, TYPE_STRUCT, - TYPE_U8, + Logarithm, Mul, Neg, Not, Or, Pow, Primitive, RefPrimitive, Rem, Round, + Sin, Sqrt, StringManipulation, Sub, Tan, ToBool, ToNumber, TypeOf, + TYPE_ARRAY, TYPE_BOOL, TYPE_DOUBLE, TYPE_ERROR, TYPE_FUNCTION, TYPE_I8, + TYPE_INT, TYPE_STRUCT, TYPE_U8, }, BuiltInFunctionType, Operator, TreeNodeValue, Value, }; @@ -462,6 +462,76 @@ fn compute_recur( adana_script_core::BuiltInFunctionType::TypeOf => { Ok(v.type_of()) } + adana_script_core::BuiltInFunctionType::Floor => { + Ok(v.floor()) + } + adana_script_core::BuiltInFunctionType::Ceil => { + Ok(v.ceil()) + } + adana_script_core::BuiltInFunctionType::Round => match v { + Primitive::Array(arr) => { + if arr.is_empty() { + return Ok(Primitive::Error(format!( + "Invalid argument len {}", + arr.len() + ))); + } + let s = &arr[0]; + let decimals = if arr.len() == 2 { + &arr[1] + } else { + &Primitive::Int(2) + }; + Ok(s.round(decimals)) + } + _ => Ok(Primitive::Error( + "invalid call to builtin fn match".to_string(), + )), + }, + adana_script_core::BuiltInFunctionType::ToUpper => { + Ok(v.to_upper()) + } + adana_script_core::BuiltInFunctionType::ToLower => { + Ok(v.to_lower()) + } + adana_script_core::BuiltInFunctionType::Capitalize => { + Ok(v.capitalize()) + } + + adana_script_core::BuiltInFunctionType::Replace => { + match v { + Primitive::Array(arr) => { + let [s, r, p] = &arr[0..=2] else { + return Ok(Primitive::Error(format!( + "Invalid argument len {}", + arr.len() + ))); + }; + Ok(s.replace(r, p)) + } + _ => Ok(Primitive::Error( + "invalid call to builtin fn replace" + .to_string(), + )), + } + } + adana_script_core::BuiltInFunctionType::ReplaceAll => { + match v { + Primitive::Array(arr) => { + let [s, r, p] = &arr[0..=2] else { + return Ok(Primitive::Error(format!( + "Invalid argument len {}", + arr.len() + ))); + }; + Ok(s.replace_all(r, p)) + } + _ => Ok(Primitive::Error( + "invalid call to builtin fn replace_all" + .to_string(), + )), + } + } adana_script_core::BuiltInFunctionType::Match => match v { Primitive::Array(arr) => { let [s, r] = &arr[0..=1] else { diff --git a/adana-script/src/parser.rs b/adana-script/src/parser.rs index bebddce..3132d1e 100644 --- a/adana-script/src/parser.rs +++ b/adana-script/src/parser.rs @@ -450,10 +450,18 @@ fn parse_builtin_fn(s: &str) -> Res { parse_builtin(BuiltInFunctionType::Log), parse_builtin(BuiltInFunctionType::Sin), parse_builtin(BuiltInFunctionType::Cos), + parse_builtin(BuiltInFunctionType::ToUpper), + parse_builtin(BuiltInFunctionType::ToLower), + parse_builtin(BuiltInFunctionType::Capitalize), + parse_builtin(BuiltInFunctionType::Ceil), + parse_builtin(BuiltInFunctionType::Floor), )), alt(( parse_builtin_many_args(BuiltInFunctionType::IsMatch), parse_builtin_many_args(BuiltInFunctionType::Match), + parse_builtin_many_args(BuiltInFunctionType::Replace), + parse_builtin_many_args(BuiltInFunctionType::Round), + parse_builtin_many_args(BuiltInFunctionType::ReplaceAll), )), // parse_builtin(BuiltInFunctionType::ReadLines), ))(s) } diff --git a/adana-script/src/tests/builtin.rs b/adana-script/src/tests/builtin.rs index 20238eb..f71a1d5 100644 --- a/adana-script/src/tests/builtin.rs +++ b/adana-script/src/tests/builtin.rs @@ -72,6 +72,93 @@ fn test_to_binary() { assert_eq!(r, Primitive::String("0b1111111".into())); } +#[test] +fn test_floor() { + let mut ctx = BTreeMap::new(); + let r = compute(r#"floor(4.7)"#, &mut ctx, "N/A").unwrap(); + assert_eq!(Primitive::Double(4.), r); +} + +#[test] +fn test_ceil() { + let mut ctx = BTreeMap::new(); + let r = compute(r#"ceil(4.7)"#, &mut ctx, "N/A").unwrap(); + assert_eq!(Primitive::Double(5.), r); +} +#[test] +fn test_round() { + let mut ctx = BTreeMap::new(); + let r = compute(r#"round(4.46790, 2)"#, &mut ctx, "N/A").unwrap(); + assert_eq!(Primitive::Double(4.47), r); + let r = compute(r#"round(4.46790, 3)"#, &mut ctx, "N/A").unwrap(); + assert_eq!(Primitive::Double(4.468), r); +} +#[test] +fn test_to_lower() { + let mut ctx = BTreeMap::new(); + let r = compute(r#"to_lower("HELLO")"#, &mut ctx, "N/A").unwrap(); + assert_eq!(Primitive::String("hello".into()), r); +} +#[test] +fn test_to_upper() { + let mut ctx = BTreeMap::new(); + let r = compute(r#"to_upper("hello")"#, &mut ctx, "N/A").unwrap(); + assert_eq!(Primitive::String("HELLO".into()), r); +} +#[test] +fn test_capitalize() { + let mut ctx = BTreeMap::new(); + let r = compute(r#"capitalize("nordine")"#, &mut ctx, "N/A").unwrap(); + assert_eq!(Primitive::String("Nordine".into()), r); +} + +#[test] +fn test_replace() { + let mut ctx = BTreeMap::new(); + let r = compute(r#"replace("Hello, world! Welcome to the world of JavaScript.", "world", "universe")"#, &mut ctx, "N/A").unwrap(); + assert_eq!( + Primitive::String( + "Hello, universe! Welcome to the world of JavaScript.".into() + ), + r + ); + let r = compute( + r#"replace("AaAaAbbBBBb", "(?i)a+(?-i)b+", "xxx")"#, + &mut ctx, + "N/A", + ) + .unwrap(); + assert_eq!(Primitive::String("xxxBBBb".into()), r); + let r = compute(r#"replace("The event is on 12/31/2023. Another date is 31-12-2023. And another one is 2023.12.31.", "(\d{2})/(\d{2})/(\d{4})", "$2-$1-$3")"#, &mut ctx, "N/A").unwrap(); + assert_eq!( + Primitive::String( + "The event is on 31-12-2023. Another date is 31-12-2023. And another one is 2023.12.31.".into() + ), + r + ); +} + +#[test] +fn test_replace_all() { + let mut ctx = BTreeMap::new(); + let r = compute(r#"replace_all("Hello, world! Welcome to the world of JavaScript.", "world", "universe")"#, &mut ctx, "N/A").unwrap(); + assert_eq!( + Primitive::String( + "Hello, universe! Welcome to the universe of JavaScript.".into() + ), + r + ); + let r = compute(r#"replace_all("AaAaAbbBBBb", "A", "b")"#, &mut ctx, "N/A") + .unwrap(); + assert_eq!(Primitive::String("bababbbBBBb".into()), r); + let r = compute(r#"replace_all("The event is on 12/31/2023. Another date is 31-12-2023. And another one is 12/31/2023.", "(\d{2})/(\d{2})/(\d{4})", "$2-$1-$3")"#, &mut ctx, "N/A").unwrap(); + assert_eq!( + Primitive::String( + "The event is on 31-12-2023. Another date is 31-12-2023. And another one is 31-12-2023.".into() + ), + r + ); +} #[test] fn test_is_match() { let mut ctx = BTreeMap::new(); diff --git a/adana-script/src/tests/struc.rs b/adana-script/src/tests/struc.rs index 05499f8..38506dd 100644 --- a/adana-script/src/tests/struc.rs +++ b/adana-script/src/tests/struc.rs @@ -260,6 +260,44 @@ fn test_struct_access_key5() { let r = compute(expr, &mut ctx, "N/A").unwrap(); assert_eq!(r, Primitive::String("nordine".into())); } +#[test] +fn test_struct_access_key6() { + let mut ctx = BTreeMap::new(); + let expr = r#" + struct { + other_struct: struct { + name: () => {"nordine"}, + }, + age: 34, + members: ["natalie", "roger","fred"], + }.other_struct["name"]() + + "#; + let r = compute(expr, &mut ctx, "N/A").unwrap(); + assert_eq!(r, Primitive::String("nordine".into())); +} + +#[test] +fn test_struct_access_key7() { + let mut ctx = BTreeMap::new(); + let expr = r#" + x= struct { + other_struct: struct { + name: struct { + first_name: () => {"nordine"} + last_name: () => {"bittich"} + age: 36 + }, + }, + age: 34, + members: ["natalie", "roger","fred"], + } + x.other_struct.name.first_name() + " " + x.other_struct["name"]["last_name"]() + ":" + x.other_struct["name"]["age"] + + "#; + let r = compute(expr, &mut ctx, "N/A").unwrap(); + assert_eq!(r, Primitive::String("nordine bittich:36".into())); +} #[test] fn test_struct_empty() {