diff --git a/README.md b/README.md index 07b7bc5..630cc13 100644 --- a/README.md +++ b/README.md @@ -747,46 +747,45 @@ 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 | +| ----------- | ---------------------------- | ------------------------------------------ | +| 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+")` | #### Matching regexes -There are two built-in functions for matching a regex against a string. You must use F-string ("""s""") -notation for the pattern: +There are two built-in functions for matching a regex against a string: ```python pattern = """(?i)a+(?-i)b+""" diff --git a/adana-script/src/string_parser.rs b/adana-script/src/string_parser.rs index 158326f..4abae7f 100644 --- a/adana-script/src/string_parser.rs +++ b/adana-script/src/string_parser.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; /// copied from https://github.com/rust-bakery/nom/blob/7.1.3/examples/string.rs use nom::branch::alt; use nom::bytes::streaming::{is_not, take_while_m_n}; +use nom::character::complete::anychar; use nom::character::streaming::{char, multispace1}; use nom::combinator::{map, map_opt, map_res, value, verify}; use nom::error::{FromExternalError, ParseError}; @@ -84,6 +85,11 @@ fn parse_escaped_whitespace<'a, E: ParseError<&'a str>>( ) -> IResult<&'a str, &'a str, E> { preceded(char('\\'), multispace1)(input) } +fn parse_escaped_anychar<'a, E: ParseError<&'a str>>( + input: &'a str, +) -> IResult<&'a str, char, E> { + preceded(char('\\'), anychar)(input) +} /// Parse a non-empty block of text that doesn't include \ or " fn parse_literal<'a, E: ParseError<&'a str>>( @@ -108,6 +114,7 @@ enum StringFragment<'a> { Literal(&'a str), EscapedChar(char), EscapedWS, + EscapedAnychar(char), } /// Combine parse_literal, parse_escaped_whitespace, and parse_escaped_char @@ -125,6 +132,7 @@ where map(parse_literal, StringFragment::Literal), map(parse_escaped_char, StringFragment::EscapedChar), value(StringFragment::EscapedWS, parse_escaped_whitespace), + map(parse_escaped_anychar, StringFragment::EscapedAnychar), ))(input) } @@ -152,6 +160,10 @@ where StringFragment::Literal(s) => string.push_str(s), StringFragment::EscapedChar(c) => string.push(c), StringFragment::EscapedWS => {} + StringFragment::EscapedAnychar(c) => { + string.push('\\'); + string.push(c) + } } string }, diff --git a/adana-script/src/tests/builtin.rs b/adana-script/src/tests/builtin.rs index 5a315d5..20238eb 100644 --- a/adana-script/src/tests/builtin.rs +++ b/adana-script/src/tests/builtin.rs @@ -77,7 +77,7 @@ fn test_is_match() { let mut ctx = BTreeMap::new(); let r = compute( r#" - pattern = """(?i)a+(?-i)b+""" + pattern = "(?i)a+(?-i)b+" text = "AaAaAbbBBBb" is_match(text, pattern) @@ -90,7 +90,7 @@ fn test_is_match() { let mut ctx = BTreeMap::new(); let r = compute( r#" - pattern = """(\w+): \$(\d+)""" + pattern = "(\w+): \$(\d+)" text = "Item1: $100, Item2: $200, Item3: $300" is_match(text, pattern) @@ -106,7 +106,7 @@ fn test_match() { let mut ctx = BTreeMap::new(); let r = compute( r#" - pattern = """(?i)a+(?-i)b+""" + pattern = "(?i)a+(?-i)b+" text = "AaAaAbbBBBb" match(text, pattern) @@ -122,7 +122,7 @@ fn test_match() { let mut ctx = BTreeMap::new(); let r = compute( r#" - pattern = """(\w+): \$(\d+)""" + pattern = "(\w+): \$(\d+)" text = "Item1: $100, Item2: $200, Item3: $300" match(text, pattern) diff --git a/adana-script/src/tests/strings.rs b/adana-script/src/tests/strings.rs index d47b16a..ffc62f5 100644 --- a/adana-script/src/tests/strings.rs +++ b/adana-script/src/tests/strings.rs @@ -37,6 +37,18 @@ fn test_string_escape() { ); } #[test] +fn test_string_regex() { + let expr = r#" + s = "(\w+): \$(\d+)" + "#; + let mut ctx = BTreeMap::new(); + let _ = compute(expr, &mut ctx, "N/A").unwrap(); + assert_eq!( + Primitive::String(r#"(\w+): \$(\d+)"#.to_string()), + ctx["s"].read().unwrap().clone() + ); +} +#[test] fn test_string_block_with_parameters() { let expr = r#" name = "nordine"