Merge branch 'master' into enum-Dynamic
This commit is contained in:
commit
20bc66c5f6
42
README.md
42
README.md
@ -322,8 +322,6 @@ Use `Engine::new_raw` to create a _raw_ `Engine`, in which:
|
||||
let mut engine = Engine::new_raw(); // create a 'raw' Engine
|
||||
|
||||
engine.register_stdlib(); // register the standard library manually
|
||||
|
||||
engine.
|
||||
```
|
||||
|
||||
Evaluate expressions only
|
||||
@ -1149,16 +1147,19 @@ record == "Bob X. Davis: age 42 ❤\n";
|
||||
|
||||
The following standard methods (defined in the standard library 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 a specified length |
|
||||
| `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 |
|
||||
| `replace` | target sub-string, replacement string | replaces a substring with another |
|
||||
| `trim` | _none_ | trims the string of whitespace at the beginning and end |
|
||||
| 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 |
|
||||
| `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 sub-string, replacement string | replaces a sub-string with another |
|
||||
| `trim` | _none_ | trims the string of whitespace at the beginning and end |
|
||||
|
||||
### Examples
|
||||
|
||||
@ -1174,17 +1175,30 @@ full_name.pad(15, '$');
|
||||
full_name.len() == 15;
|
||||
full_name == "Bob C. Davis$$$";
|
||||
|
||||
let n = full_name.index_of('$');
|
||||
n == 12;
|
||||
|
||||
full_name.index_of("$$", n + 1) == 13;
|
||||
|
||||
full_name.sub_string(n, 3) == "$$$";
|
||||
|
||||
full_name.truncate(6);
|
||||
full_name.len() == 6;
|
||||
full_name == "Bob C.";
|
||||
|
||||
full_name.replace("Bob", "John");
|
||||
full_name.len() == 7;
|
||||
full_name = "John C.";
|
||||
full_name == "John C.";
|
||||
|
||||
full_name.contains('C') == true;
|
||||
full_name.contains("John") == true;
|
||||
|
||||
full_name.crop(5);
|
||||
full_name == "C.";
|
||||
|
||||
full_name.crop(0, 1);
|
||||
full_name == "C";
|
||||
|
||||
full_name.clear();
|
||||
full_name.len() == 0;
|
||||
```
|
||||
@ -1218,7 +1232,7 @@ The following methods (defined in the standard library but excluded if using a [
|
||||
| `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 until a specified length |
|
||||
| `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) |
|
||||
|
||||
|
43
benches/eval_expression.rs
Normal file
43
benches/eval_expression.rs
Normal file
@ -0,0 +1,43 @@
|
||||
#![feature(test)]
|
||||
|
||||
///! Test evaluating expressions
|
||||
extern crate test;
|
||||
|
||||
use rhai::{Engine, OptimizationLevel};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_expression_single(bench: &mut Bencher) {
|
||||
let script = "1";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_expression_number_literal(bench: &mut Bencher) {
|
||||
let script = "2 > 1";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_expression_number_operators(bench: &mut Bencher) {
|
||||
let script = "2 + 2 == 4";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
}
|
77
benches/eval_scope.rs
Normal file
77
benches/eval_scope.rs
Normal file
@ -0,0 +1,77 @@
|
||||
#![feature(test)]
|
||||
|
||||
///! Test evaluating with scope
|
||||
extern crate test;
|
||||
|
||||
use rhai::{Engine, OptimizationLevel, Scope, INT};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_scope_single(bench: &mut Bencher) {
|
||||
let script = "requests_made == requests_made";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("requests_made", 99 as INT);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_scope_multiple(bench: &mut Bencher) {
|
||||
let script = "requests_made > requests_succeeded";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("requests_made", 99 as INT);
|
||||
scope.push("requests_succeeded", 90 as INT);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_scope_longer(bench: &mut Bencher) {
|
||||
let script = "(requests_made * requests_succeeded / 100) >= 90";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("requests_made", 99 as INT);
|
||||
scope.push("requests_succeeded", 90 as INT);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_scope_complex(bench: &mut Bencher) {
|
||||
let script = r#"
|
||||
2 > 1 &&
|
||||
"something" != "nothing" ||
|
||||
"2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" &&
|
||||
Variable_name_with_spaces <= variableName &&
|
||||
modifierTest + 1000 / 2 > (80 * 100 % 2)
|
||||
"#;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("Variable_name_with_spaces", 99 as INT);
|
||||
scope.push("variableName", 90 as INT);
|
||||
scope.push("modifierTest", 5 as INT);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
100
benches/eval_type.rs
Normal file
100
benches/eval_type.rs
Normal file
@ -0,0 +1,100 @@
|
||||
#![feature(test)]
|
||||
|
||||
///! Test evaluating expressions
|
||||
extern crate test;
|
||||
|
||||
use rhai::{Engine, OptimizationLevel, RegisterFn, Scope, INT};
|
||||
use test::Bencher;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Test {
|
||||
x: INT,
|
||||
}
|
||||
|
||||
impl Test {
|
||||
pub fn get_x(&mut self) -> INT {
|
||||
self.x
|
||||
}
|
||||
pub fn action(&mut self) {
|
||||
self.x = 0;
|
||||
}
|
||||
pub fn update(&mut self, val: INT) {
|
||||
self.x = val;
|
||||
}
|
||||
pub fn get_nest(&mut self) -> Test {
|
||||
Test { x: 9 }
|
||||
}
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_type_field(bench: &mut Bencher) {
|
||||
let script = "foo.field";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
engine.register_type_with_name::<Test>("Test");
|
||||
engine.register_get("field", Test::get_x);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("foo", Test { x: 42 });
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_type_method(bench: &mut Bencher) {
|
||||
let script = "foo.action()";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
engine.register_type_with_name::<Test>("Test");
|
||||
engine.register_fn("action", Test::action);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("foo", Test { x: 42 });
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_type_method_with_params(bench: &mut Bencher) {
|
||||
let script = "foo.update(1)";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
engine.register_type_with_name::<Test>("Test");
|
||||
engine.register_fn("update", Test::update);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("foo", Test { x: 42 });
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_type_method_nested(bench: &mut Bencher) {
|
||||
let script = "foo.nest.field";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
engine.register_type_with_name::<Test>("Test");
|
||||
engine.register_get("field", Test::get_x);
|
||||
engine.register_get("nest", Test::get_nest);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("foo", Test { x: 42 });
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
25
benches/iterations.rs
Normal file
25
benches/iterations.rs
Normal file
@ -0,0 +1,25 @@
|
||||
#![feature(test)]
|
||||
|
||||
///! Test 1,000 iterations
|
||||
extern crate test;
|
||||
|
||||
use rhai::{Engine, OptimizationLevel};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_iterations_1000(bench: &mut Bencher) {
|
||||
let script = r#"
|
||||
let x = 1_000;
|
||||
|
||||
while x > 0 {
|
||||
x = x - 1;
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
}
|
83
benches/parsing.rs
Normal file
83
benches/parsing.rs
Normal file
@ -0,0 +1,83 @@
|
||||
#![feature(test)]
|
||||
|
||||
///! Test parsing expressions
|
||||
extern crate test;
|
||||
|
||||
use rhai::{Engine, OptimizationLevel};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_single(bench: &mut Bencher) {
|
||||
let script = "1";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
bench.iter(|| engine.compile_expression(script).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_simple(bench: &mut Bencher) {
|
||||
let script = "(requests_made * requests_succeeded / 100) >= 90";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
bench.iter(|| engine.compile_expression(script).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_full(bench: &mut Bencher) {
|
||||
let script = r#"
|
||||
2 > 1 &&
|
||||
"something" != "nothing" ||
|
||||
"2014-01-20" < "Wed Jul 8 23:07:35 MDT 2015" &&
|
||||
[array, with, spaces].len() <= #{prop:name}.len() &&
|
||||
modifierTest + 1000 / 2 > (80 * 100 % 2)
|
||||
"#;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
bench.iter(|| engine.compile_expression(script).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_primes(bench: &mut Bencher) {
|
||||
let script = r#"
|
||||
// This script uses the Sieve of Eratosthenes to calculate prime numbers.
|
||||
|
||||
let now = timestamp();
|
||||
|
||||
const MAX_NUMBER_TO_CHECK = 10_000; // 1229 primes <= 10000
|
||||
|
||||
let prime_mask = [];
|
||||
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
||||
|
||||
prime_mask[0] = prime_mask[1] = false;
|
||||
|
||||
let total_primes_found = 0;
|
||||
|
||||
for p in range(2, MAX_NUMBER_TO_CHECK) {
|
||||
if prime_mask[p] {
|
||||
print(p);
|
||||
|
||||
total_primes_found += 1;
|
||||
let i = 2 * p;
|
||||
|
||||
while i < MAX_NUMBER_TO_CHECK {
|
||||
prime_mask[i] = false;
|
||||
i += p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("Total " + total_primes_found + " primes.");
|
||||
print("Run time = " + now.elapsed() + " seconds.");
|
||||
"#;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
|
||||
bench.iter(|| engine.compile(script).unwrap());
|
||||
}
|
@ -1024,17 +1024,113 @@ impl Engine<'_> {
|
||||
}
|
||||
|
||||
// Register string utility functions
|
||||
fn sub_string(s: &mut String, start: INT, len: INT) -> String {
|
||||
let offset = if s.is_empty() || len <= 0 {
|
||||
return "".to_string();
|
||||
} else if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
return "".to_string();
|
||||
} else {
|
||||
start as usize
|
||||
};
|
||||
|
||||
let chars: Vec<_> = s.chars().collect();
|
||||
|
||||
let len = if offset + (len as usize) > chars.len() {
|
||||
chars.len() - offset
|
||||
} else {
|
||||
len as usize
|
||||
};
|
||||
|
||||
chars[offset..][..len].into_iter().collect::<String>()
|
||||
}
|
||||
|
||||
fn crop_string(s: &mut String, start: INT, len: INT) {
|
||||
let offset = if s.is_empty() || len <= 0 {
|
||||
s.clear();
|
||||
return;
|
||||
} else if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
s.clear();
|
||||
return;
|
||||
} else {
|
||||
start as usize
|
||||
};
|
||||
|
||||
let chars: Vec<_> = s.chars().collect();
|
||||
|
||||
let len = if offset + (len as usize) > chars.len() {
|
||||
chars.len() - offset
|
||||
} else {
|
||||
len as usize
|
||||
};
|
||||
|
||||
s.clear();
|
||||
|
||||
chars[offset..][..len]
|
||||
.into_iter()
|
||||
.for_each(|&ch| s.push(ch));
|
||||
}
|
||||
|
||||
self.register_fn("len", |s: &mut String| s.chars().count() as INT);
|
||||
self.register_fn("contains", |s: &mut String, ch: char| s.contains(ch));
|
||||
self.register_fn("contains", |s: &mut String, find: String| s.contains(&find));
|
||||
self.register_fn("index_of", |s: &mut String, ch: char, start: INT| {
|
||||
let start = if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
return -1 as INT;
|
||||
} else {
|
||||
s.chars().take(start as usize).collect::<String>().len()
|
||||
};
|
||||
|
||||
s[start..]
|
||||
.find(ch)
|
||||
.map(|index| s[0..start + index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
});
|
||||
self.register_fn("index_of", |s: &mut String, ch: char| {
|
||||
s.find(ch)
|
||||
.map(|index| s[0..index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
});
|
||||
self.register_fn("index_of", |s: &mut String, find: String, start: INT| {
|
||||
let start = if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
return -1 as INT;
|
||||
} else {
|
||||
s.chars().take(start as usize).collect::<String>().len()
|
||||
};
|
||||
|
||||
s[start..]
|
||||
.find(&find)
|
||||
.map(|index| s[0..start + index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
});
|
||||
self.register_fn("index_of", |s: &mut String, find: String| {
|
||||
s.find(&find)
|
||||
.map(|index| s[0..index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
});
|
||||
self.register_fn("clear", |s: &mut String| s.clear());
|
||||
self.register_fn("append", |s: &mut String, ch: char| s.push(ch));
|
||||
self.register_fn("append", |s: &mut String, add: String| s.push_str(&add));
|
||||
self.register_fn("sub_string", sub_string);
|
||||
self.register_fn("sub_string", |s: &mut String, start: INT| {
|
||||
sub_string(s, start, s.len() as INT)
|
||||
});
|
||||
self.register_fn("crop", crop_string);
|
||||
self.register_fn("crop", |s: &mut String, start: INT| {
|
||||
crop_string(s, start, s.len() as INT)
|
||||
});
|
||||
self.register_fn("truncate", |s: &mut String, len: INT| {
|
||||
if len >= 0 {
|
||||
let chars: Vec<_> = s.chars().take(len as usize).collect();
|
||||
s.clear();
|
||||
chars.iter().for_each(|&ch| s.push(ch));
|
||||
chars.into_iter().for_each(|ch| s.push(ch));
|
||||
} else {
|
||||
s.clear();
|
||||
}
|
||||
|
109
tests/string.rs
109
tests/string.rs
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
fn test_string() -> Result<(), EvalAltResult> {
|
||||
@ -33,3 +33,110 @@ fn test_string() -> Result<(), EvalAltResult> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_stdlib"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[test]
|
||||
fn test_string_substring() -> Result<(), EvalAltResult> {
|
||||
let engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.sub_string(-1, 2)"#
|
||||
)?,
|
||||
"❤❤"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.sub_string(1, 5)"#
|
||||
)?,
|
||||
"❤❤ he"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.sub_string(1)"#
|
||||
)?,
|
||||
"❤❤ hello! ❤❤❤"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.sub_string(99)"#
|
||||
)?,
|
||||
""
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.sub_string(1, -1)"#
|
||||
)?,
|
||||
""
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.sub_string(1, 999)"#
|
||||
)?,
|
||||
"❤❤ hello! ❤❤❤"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.crop(1, -1); x"#
|
||||
)?,
|
||||
""
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.crop(4, 6); x"#
|
||||
)?,
|
||||
"hello!"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.crop(1, 999); x"#
|
||||
)?,
|
||||
"❤❤ hello! ❤❤❤"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('\u2764')"#
|
||||
)?,
|
||||
0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('\u2764', 5)"#
|
||||
)?,
|
||||
11
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('\u2764', -1)"#
|
||||
)?,
|
||||
0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('\u2764', 999)"#
|
||||
)?,
|
||||
-1
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r#"let x = "\u2764\u2764\u2764 hello! \u2764\u2764\u2764"; x.index_of('x')"#
|
||||
)?,
|
||||
-1
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user