From 4c46c7e26ba59a85c8efbd0370d07c63bf187f16 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 30 May 2020 10:30:21 +0800 Subject: [PATCH] Register property versions of some methods. --- README.md | 120 ++++++++++++++++++------------------ benches/eval_expression.rs | 8 +-- benches/parsing.rs | 8 +-- scripts/fibonacci.rhai | 2 +- scripts/mat_mul.rhai | 14 ++--- scripts/primes.rhai | 2 +- scripts/speed_test.rhai | 2 +- scripts/string.rhai | 6 +- src/packages/array_basic.rs | 4 ++ src/packages/math_basic.rs | 12 ++++ src/packages/string_more.rs | 4 ++ src/packages/time_basic.rs | 45 ++++++++------ tests/arrays.rs | 2 +- tests/for.rs | 2 +- tests/time.rs | 1 - 15 files changed, 128 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index 89728438..97c65279 100644 --- a/README.md +++ b/README.md @@ -300,7 +300,7 @@ let ast = engine.compile(true, r" // a function with two parameters: String and i64 fn hello(x, y) { - x.len() + y + x.len + y } // functions can be overloaded: this one takes only one parameter @@ -355,7 +355,7 @@ use rhai::{Engine, Func}; // use 'Func' for 'create_from_s let engine = Engine::new(); // create a new 'Engine' just for this -let script = "fn calc(x, y) { x + y.len() < 42 }"; +let script = "fn calc(x, y) { x + y.len < 42 }"; // Func takes two type parameters: // 1) a tuple made up of the types of the script function's parameters @@ -945,8 +945,8 @@ If the [`no_object`] feature is turned on, however, the _method_ style of functi (i.e. calling a function as an object-method) is no longer supported. ```rust -// Below is a syntax error under 'no_object' because 'len' cannot be called in method style. -let result = engine.eval::("let x = [1, 2, 3]; x.len()")?; +// Below is a syntax error under 'no_object' because 'clear' cannot be called in method style. +let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?; ``` [`type_of()`] works fine with custom types and returns the name of the type. @@ -1082,7 +1082,7 @@ fn main() -> Result<(), Box> // First invocation engine.eval_with_scope::<()>(&mut scope, r" - let x = 4 + 5 - y + z + s.len(); + let x = 4 + 5 - y + z + s.len; y = 1; ")?; @@ -1312,16 +1312,16 @@ Floating-point functions The following standard functions (defined in the [`BasicMathPackage`](#packages) but excluded if using a [raw `Engine`]) operate on `f64` only: -| Category | Functions | -| ---------------- | ------------------------------------------------------------ | -| Trigonometry | `sin`, `cos`, `tan`, `sinh`, `cosh`, `tanh` in degrees | -| Arc-trigonometry | `asin`, `acos`, `atan`, `asinh`, `acosh`, `atanh` in degrees | -| Square root | `sqrt` | -| Exponential | `exp` (base _e_) | -| Logarithmic | `ln` (base _e_), `log10` (base 10), `log` (any base) | -| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` | -| Conversion | [`to_int`] | -| Testing | `is_nan`, `is_finite`, `is_infinite` | +| Category | Functions | +| ---------------- | --------------------------------------------------------------------- | +| Trigonometry | `sin`, `cos`, `tan`, `sinh`, `cosh`, `tanh` in degrees | +| Arc-trigonometry | `asin`, `acos`, `atan`, `asinh`, `acosh`, `atanh` in degrees | +| Square root | `sqrt` | +| Exponential | `exp` (base _e_) | +| Logarithmic | `ln` (base _e_), `log10` (base 10), `log` (any base) | +| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` methods and properties | +| Conversion | [`to_int`] | +| Testing | `is_nan`, `is_finite`, `is_infinite` methods and properties | Strings and Chars ----------------- @@ -1413,32 +1413,32 @@ record == "Bob X. Davis: age 42 ❤\n"; The following standard methods (mostly defined in the [`MoreStringPackage`](#packages) but excluded if using a [raw `Engine`]) operate on strings: -| Function | Parameter(s) | Description | -| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | -| `len` | _none_ | returns the number of characters (not number of bytes) in the string | -| `pad` | character to pad, target length | pads the string with an character to at least a specified length | -| `+=` operator, `append` | character/string to append | Adds a character or a string to the end of another string | -| `clear` | _none_ | empties the string | -| `truncate` | target length | cuts off the string at exactly a specified number of characters | -| `contains` | character/sub-string to search for | checks if a certain character or sub-string occurs in the string | -| `index_of` | character/sub-string to search for, start index _(optional)_ | returns the index that a certain character or sub-string occurs in the string, or -1 if not found | -| `sub_string` | start index, length _(optional)_ | extracts a sub-string (to the end of the string if length is not specified) | -| `crop` | start index, length _(optional)_ | retains only a portion of the string (to the end of the string if length is not specified) | -| `replace` | target character/sub-string, replacement character/string | replaces a sub-string with another | -| `trim` | _none_ | trims the string of whitespace at the beginning and end | +| Function | Parameter(s) | Description | +| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | +| `len` method and property | _none_ | returns the number of characters (not number of bytes) in the string | +| `pad` | character to pad, target length | pads the string with an character to at least a specified length | +| `+=` operator, `append` | character/string to append | Adds a character or a string to the end of another string | +| `clear` | _none_ | empties the string | +| `truncate` | target length | cuts off the string at exactly a specified number of characters | +| `contains` | character/sub-string to search for | checks if a certain character or sub-string occurs in the string | +| `index_of` | character/sub-string to search for, start index _(optional)_ | returns the index that a certain character or sub-string occurs in the string, or -1 if not found | +| `sub_string` | start index, length _(optional)_ | extracts a sub-string (to the end of the string if length is not specified) | +| `crop` | start index, length _(optional)_ | retains only a portion of the string (to the end of the string if length is not specified) | +| `replace` | target character/sub-string, replacement character/string | replaces a sub-string with another | +| `trim` | _none_ | trims the string of whitespace at the beginning and end | ### Examples ```rust let full_name == " Bob C. Davis "; -full_name.len() == 14; +full_name.len == 14; full_name.trim(); -full_name.len() == 12; +full_name.len == 12; full_name == "Bob C. Davis"; full_name.pad(15, '$'); -full_name.len() == 15; +full_name.len == 15; full_name == "Bob C. Davis$$$"; let n = full_name.index_of('$'); @@ -1449,11 +1449,11 @@ full_name.index_of("$$", n + 1) == 13; full_name.sub_string(n, 3) == "$$$"; full_name.truncate(6); -full_name.len() == 6; +full_name.len == 6; full_name == "Bob C."; full_name.replace("Bob", "John"); -full_name.len() == 7; +full_name.len == 7; full_name == "John C."; full_name.contains('C') == true; @@ -1466,7 +1466,7 @@ full_name.crop(0, 1); full_name == "C"; full_name.clear(); -full_name.len() == 0; +full_name.len == 0; ``` Arrays @@ -1488,19 +1488,19 @@ Arrays are disabled via the [`no_index`] feature. The following methods (mostly defined in the [`BasicArrayPackage`](#packages) but excluded if using a [raw `Engine`]) operate on arrays: -| Function | Parameter(s) | Description | -| ----------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -| `push` | element to insert | inserts an element at the end | -| `+=` operator, `append` | array to append | concatenates the second array to the end of the first | -| `+` operator | first array, second array | concatenates the first array with the second | -| `insert` | element to insert, position
(beginning if <= 0, end if >= length) | insert an element at a certain index | -| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) | -| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) | -| `remove` | index | removes an element at a particular index and returns it, or returns [`()`] if the index is not valid | -| `len` | _none_ | returns the number of elements | -| `pad` | element to pad, target length | pads the array with an element to at least a specified length | -| `clear` | _none_ | empties the array | -| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) | +| Function | Parameter(s) | Description | +| ------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `push` | element to insert | inserts an element at the end | +| `+=` operator, `append` | array to append | concatenates the second array to the end of the first | +| `+` operator | first array, second array | concatenates the first array with the second | +| `insert` | element to insert, position
(beginning if <= 0, end if >= length) | insert an element at a certain index | +| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) | +| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) | +| `remove` | index | removes an element at a particular index and returns it, or returns [`()`] if the index is not valid | +| `len` method and property | _none_ | returns the number of elements | +| `pad` | element to pad, target length | pads the array with an element to at least a specified length | +| `clear` | _none_ | empties the array | +| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) | ### Examples @@ -1510,7 +1510,7 @@ let y = [2, 3]; // array literal with 2 elements y.insert(0, 1); // insert element at the beginning y.insert(999, 4); // insert element at the end -y.len() == 4; +y.len == 4; y[0] == 1; y[1] == 2; @@ -1527,7 +1527,7 @@ y[1] = 42; // array elements can be reassigned y.remove(2) == 3; // remove element -y.len() == 3; +y.len == 3; y[2] == 4; // elements after the removed element are shifted @@ -1551,7 +1551,7 @@ foo == 1; y.push(4); // 4 elements y.push(5); // 5 elements -y.len() == 5; +y.len == 5; let first = y.shift(); // remove the first element, 4 elements remaining first == 1; @@ -1559,7 +1559,7 @@ first == 1; let last = y.pop(); // remove the last element, 3 elements remaining last == 5; -y.len() == 3; +y.len == 3; for item in y { // arrays can be iterated with a 'for' statement print(item); @@ -1567,15 +1567,15 @@ for item in y { // arrays can be iterated with a 'for' statement y.pad(10, "hello"); // pad the array up to 10 elements -y.len() == 10; +y.len == 10; y.truncate(5); // truncate the array to 5 elements -y.len() == 5; +y.len == 5; y.clear(); // empty the array -y.len() == 0; +y.len == 0; ``` `push` and `pad` are only defined for standard built-in types. For custom types, type-specific versions must be registered: @@ -1745,10 +1745,10 @@ The Rust type of a timestamp is `std::time::Instant`. [`type_of()`] a timestamp The following methods (defined in the [`BasicTimePackage`](#packages) but excluded if using a [raw `Engine`]) operate on timestamps: -| Function | Parameter(s) | Description | -| ------------ | ---------------------------------- | -------------------------------------------------------- | -| `elapsed` | _none_ | returns the number of seconds since the timestamp | -| `-` operator | later timestamp, earlier timestamp | returns the number of seconds between the two timestamps | +| Function | Parameter(s) | Description | +| ------------------ | ---------------------------------- | -------------------------------------------------------- | +| `elapsed` property | _none_ | returns the number of seconds since the timestamp | +| `-` operator | later timestamp, earlier timestamp | returns the number of seconds between the two timestamps | ### Examples @@ -1757,7 +1757,7 @@ let now = timestamp(); // Do some lengthy operation... -if now.elapsed() > 30.0 { +if now.elapsed > 30.0 { print("takes too long (over 30 seconds)!") } ``` @@ -2281,7 +2281,7 @@ let ast = engine.compile(r#" x + 1 } fn add_len(x, y) { - x + y.len() + x + y.len } // Imported modules can become sub-modules diff --git a/benches/eval_expression.rs b/benches/eval_expression.rs index affb25bb..ceaaaa17 100644 --- a/benches/eval_expression.rs +++ b/benches/eval_expression.rs @@ -48,7 +48,7 @@ fn bench_eval_expression_optimized_simple(bench: &mut Bencher) { 2 > 1 && "something" != "nothing" || "2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" && - [array, with, spaces].len() <= #{prop:name}.len() && + [array, with, spaces].len <= #{prop:name}.len && modifierTest + 1000 / 2 > (80 * 100 % 2) "#; @@ -65,7 +65,7 @@ fn bench_eval_expression_optimized_full(bench: &mut Bencher) { 2 > 1 && "something" != "nothing" || "2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" && - [array, with, spaces].len() <= #{prop:name}.len() && + [array, with, spaces].len <= #{prop:name}.len && modifierTest + 1000 / 2 > (80 * 100 % 2) "#; @@ -82,7 +82,7 @@ fn bench_eval_call_expression(bench: &mut Bencher) { 2 > 1 && "something" != "nothing" || "2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" && - [array, with, spaces].len() <= #{prop:name}.len() && + [array, with, spaces].len <= #{prop:name}.len && modifierTest + 1000 / 2 > (80 * 100 % 2) "#; @@ -97,7 +97,7 @@ fn bench_eval_call(bench: &mut Bencher) { 2 > 1 && "something" != "nothing" || "2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" && - [array, with, spaces].len() <= #{prop:name}.len() && + [array, with, spaces].len <= #{prop:name}.len && modifierTest + 1000 / 2 > (80 * 100 % 2) "#; diff --git a/benches/parsing.rs b/benches/parsing.rs index 01011877..8e84bd8d 100644 --- a/benches/parsing.rs +++ b/benches/parsing.rs @@ -32,7 +32,7 @@ fn bench_parse_full(bench: &mut Bencher) { 2 > 1 && "something" != "nothing" || "2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" && - [array, with, spaces].len() <= #{prop:name}.len() && + [array, with, spaces].len <= #{prop:name}.len && modifierTest + 1000 / 2 > (80 * 100 % 2) "#; @@ -94,7 +94,7 @@ fn bench_parse_primes(bench: &mut Bencher) { } print("Total " + total_primes_found + " primes."); - print("Run time = " + now.elapsed() + " seconds."); + print("Run time = " + now.elapsed + " seconds."); "#; let mut engine = Engine::new(); @@ -109,7 +109,7 @@ fn bench_parse_optimize_simple(bench: &mut Bencher) { 2 > 1 && "something" != "nothing" || "2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" && - [array, with, spaces].len() <= #{prop:name}.len() && + [array, with, spaces].len <= #{prop:name}.len && modifierTest + 1000 / 2 > (80 * 100 % 2) "#; @@ -125,7 +125,7 @@ fn bench_parse_optimize_full(bench: &mut Bencher) { 2 > 1 && "something" != "nothing" || "2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" && - [array, with, spaces].len() <= #{prop:name}.len() && + [array, with, spaces].len <= #{prop:name}.len && modifierTest + 1000 / 2 > (80 * 100 % 2) "#; diff --git a/scripts/fibonacci.rhai b/scripts/fibonacci.rhai index 730dadae..3455c5f6 100644 --- a/scripts/fibonacci.rhai +++ b/scripts/fibonacci.rhai @@ -17,7 +17,7 @@ let now = timestamp(); let result = fib(target); -print("Finished. Run time = " + now.elapsed() + " seconds."); +print("Finished. Run time = " + now.elapsed + " seconds."); print("Fibonacci number #" + target + " = " + result); diff --git a/scripts/mat_mul.rhai b/scripts/mat_mul.rhai index 59011e22..b93a1a5e 100644 --- a/scripts/mat_mul.rhai +++ b/scripts/mat_mul.rhai @@ -24,9 +24,9 @@ fn mat_gen(n) { } fn mat_mul(a, b) { - let m = a.len(); - let n = a[0].len(); - let p = b[0].len(); + let m = a.len; + let n = a[0].len; + let p = b[0].len; let b2 = new_mat(n, p); @@ -38,13 +38,13 @@ fn mat_mul(a, b) { let c = new_mat(m, p); - for i in range(0, c.len()) { + for i in range(0, c.len) { let ci = c[i]; - for j in range(0, ci.len()) { + for j in range(0, ci.len) { let b2j = b2[j]; ci[j] = 0.0; - for z in range(0, a[i].len()) { + for z in range(0, a[i].len) { let x = a[i][z]; let y = b2j[z]; ci[j] += x * y; @@ -66,4 +66,4 @@ for i in range(0, SIZE) { print(c[i]); } -print("Finished. Run time = " + now.elapsed() + " seconds."); +print("Finished. Run time = " + now.elapsed + " seconds."); diff --git a/scripts/primes.rhai b/scripts/primes.rhai index 2b67ec50..7f66976f 100644 --- a/scripts/primes.rhai +++ b/scripts/primes.rhai @@ -26,7 +26,7 @@ for p in range(2, MAX_NUMBER_TO_CHECK) { } print("Total " + total_primes_found + " primes <= " + MAX_NUMBER_TO_CHECK); -print("Run time = " + now.elapsed() + " seconds."); +print("Run time = " + now.elapsed + " seconds."); if total_primes_found != 9_592 { print("The answer is WRONG! Should be 9,592!"); diff --git a/scripts/speed_test.rhai b/scripts/speed_test.rhai index e07bd7ec..edb9bd0e 100644 --- a/scripts/speed_test.rhai +++ b/scripts/speed_test.rhai @@ -10,4 +10,4 @@ while x > 0 { x -= 1; } -print("Finished. Run time = " + now.elapsed() + " seconds."); +print("Finished. Run time = " + now.elapsed + " seconds."); diff --git a/scripts/string.rhai b/scripts/string.rhai index ab30a29a..835b2556 100644 --- a/scripts/string.rhai +++ b/scripts/string.rhai @@ -11,7 +11,7 @@ print("foo" >= "bar"); // string comparison print("the answer is " + 42); // string building using non-string types let s = "hello, world!"; // string variable -print("length=" + s.len()); // should be 13 +print("length=" + s.len); // should be 13 -s[s.len()-1] = '?'; // change the string -print(s); // should print 'hello, world?' +s[s.len-1] = '?'; // change the string +print("Question: " + s); // should print 'Question: hello, world?' diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 5b96ffe2..19206e36 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -105,6 +105,10 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, { }, ); lib.set_fn_1_mut("len", |list: &mut Array| Ok(list.len() as INT)); + + #[cfg(not(feature = "no_object"))] + lib.set_getter_fn("len", |list: &mut Array| Ok(list.len() as INT)); + lib.set_fn_1_mut("clear", |list: &mut Array| { list.clear(); Ok(()) diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 9c7313ed..eea49727 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -43,6 +43,18 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { lib.set_fn_1("is_finite", |x: FLOAT| Ok(x.is_finite())); lib.set_fn_1("is_infinite", |x: FLOAT| Ok(x.is_infinite())); + #[cfg(not(feature = "no_object"))] + { + lib.set_getter_fn("floor", |x: &mut FLOAT| Ok(x.floor())); + lib.set_getter_fn("ceiling", |x: &mut FLOAT| Ok(x.ceil())); + lib.set_getter_fn("round", |x: &mut FLOAT| Ok(x.ceil())); + lib.set_getter_fn("int", |x: &mut FLOAT| Ok(x.trunc())); + lib.set_getter_fn("fraction", |x: &mut FLOAT| Ok(x.fract())); + lib.set_getter_fn("is_nan", |x: &mut FLOAT| Ok(x.is_nan())); + lib.set_getter_fn("is_finite", |x: &mut FLOAT| Ok(x.is_finite())); + lib.set_getter_fn("is_infinite", |x: &mut FLOAT| Ok(x.is_infinite())); + } + // Register conversion functions lib.set_fn_1("to_float", |x: INT| Ok(x as FLOAT)); lib.set_fn_1("to_float", |x: f32| Ok(x as FLOAT)); diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 6f3cbc40..e86f5fb1 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -106,6 +106,10 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str } lib.set_fn_1_mut("len", |s: &mut ImmutableString| Ok(s.chars().count() as INT)); + + #[cfg(not(feature = "no_object"))] + lib.set_getter_fn("len", |s: &mut ImmutableString| Ok(s.chars().count() as INT)); + lib.set_fn_2_mut( "contains", |s: &mut ImmutableString, ch: char| Ok(s.contains(ch)), diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index 7d80344c..b8e13604 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -10,6 +10,9 @@ use crate::token::Position; #[cfg(not(feature = "no_std"))] use crate::stdlib::time::Instant; +#[cfg(not(feature = "no_float"))] +use crate::parser::FLOAT; + #[cfg(not(feature = "no_std"))] def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { // Register date/time functions @@ -70,27 +73,29 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { lib.set_fn_2("==", eq::); lib.set_fn_2("!=", ne::); - lib.set_fn_1( - "elapsed", - |timestamp: Instant| { - #[cfg(not(feature = "no_float"))] - return Ok(timestamp.elapsed().as_secs_f64()); + #[cfg(not(feature = "no_float"))] + fn elapsed (timestamp: &mut Instant) -> Result> { + Ok(timestamp.elapsed().as_secs_f64()) + } - #[cfg(feature = "no_float")] - { - let seconds = timestamp.elapsed().as_secs(); + #[cfg(feature = "no_float")] + fn elapsed (timestamp: &mut Instant) -> Result> { + let seconds = timestamp.elapsed().as_secs(); - #[cfg(not(feature = "unchecked"))] - { - if seconds > (MAX_INT as u64) { - return Err(Box::new(EvalAltResult::ErrorArithmetic( - format!("Integer overflow for timestamp.elapsed(): {}", seconds), - Position::none(), - ))); - } - } - return Ok(seconds as INT); + #[cfg(not(feature = "unchecked"))] + { + if seconds > (MAX_INT as u64) { + return Err(Box::new(EvalAltResult::ErrorArithmetic( + format!("Integer overflow for timestamp.elapsed: {}", seconds), + Position::none(), + ))); } - }, - ); + } + Ok(seconds as INT) + } + + lib.set_fn_1_mut("elapsed", elapsed); + + #[cfg(not(feature = "no_object"))] + lib.set_getter_fn("elapsed", elapsed); }); diff --git a/tests/arrays.rs b/tests/arrays.rs index ea710eba..741156e9 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -26,7 +26,7 @@ fn test_arrays() -> Result<(), Box> { let y = [4, 5]; x.append(y); - x.len() + r + x.len + r " )?, 14 diff --git a/tests/for.rs b/tests/for.rs index 2512ec72..cddff6ed 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -48,7 +48,7 @@ fn test_for_object() -> Result<(), Box> { sum += value; } - keys.len() + sum + keys.len + sum "#; assert_eq!(engine.eval::(script)?, 9); diff --git a/tests/time.rs b/tests/time.rs index aa5239b1..2590e174 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -1,4 +1,3 @@ -#![cfg(not(feature = "no_stdlib"))] #![cfg(not(feature = "no_std"))] use rhai::{Engine, EvalAltResult, INT};