Add iterator support for strings.

This commit is contained in:
Stephen Chung 2020-06-16 09:34:30 +08:00
parent 4603f8026f
commit ff37e02443
5 changed files with 57 additions and 14 deletions

View File

@ -1499,6 +1499,11 @@ record == "Bob X. Davis: age 42 ❤\n";
"Davis" in record == true;
'X' in record == true;
'C' in record == false;
// Strings can be iterated with a 'for' statement, yielding characters
for ch in record {
print(ch);
}
```
The maximum allowed length of a string can be controlled via `Engine::set_max_string_size`
@ -2011,9 +2016,18 @@ loop {
Iterating through a range or an [array] is provided by the `for` ... `in` loop.
```rust
let array = [1, 3, 5, 7, 9, 42];
// Iterate through string, yielding characters
let s = "hello, world!";
for ch in s {
if ch > 'z' { continue; } // skip to the next iteration
print(ch);
if x == '@' { break; } // break out of for loop
}
// Iterate through array
let array = [1, 3, 5, 7, 9, 42];
for x in array {
if x > 10 { continue; } // skip to the next iteration
print(x);

View File

@ -125,7 +125,7 @@ fn main() {
match engine
.compile_with_scope(&scope, &script)
.map_err(|err| err.into())
.map_err(Into::into)
.and_then(|r| {
ast_u = r.clone();

View File

@ -2265,14 +2265,14 @@ fn run_builtin_binary_op(
#[cfg(not(feature = "unchecked"))]
match op {
"+" => return add(x, y).map(Into::<Dynamic>::into).map(Some),
"-" => return sub(x, y).map(Into::<Dynamic>::into).map(Some),
"*" => return mul(x, y).map(Into::<Dynamic>::into).map(Some),
"/" => return div(x, y).map(Into::<Dynamic>::into).map(Some),
"%" => return modulo(x, y).map(Into::<Dynamic>::into).map(Some),
"~" => return pow_i_i(x, y).map(Into::<Dynamic>::into).map(Some),
">>" => return shr(x, y).map(Into::<Dynamic>::into).map(Some),
"<<" => return shl(x, y).map(Into::<Dynamic>::into).map(Some),
"+" => return add(x, y).map(Into::into).map(Some),
"-" => return sub(x, y).map(Into::into).map(Some),
"*" => return mul(x, y).map(Into::into).map(Some),
"/" => return div(x, y).map(Into::into).map(Some),
"%" => return modulo(x, y).map(Into::into).map(Some),
"~" => return pow_i_i(x, y).map(Into::into).map(Some),
">>" => return shr(x, y).map(Into::into).map(Some),
"<<" => return shl(x, y).map(Into::into).map(Some),
_ => (),
}
@ -2283,9 +2283,9 @@ fn run_builtin_binary_op(
"*" => return Ok(Some((x * y).into())),
"/" => return Ok(Some((x / y).into())),
"%" => return Ok(Some((x % y).into())),
"~" => return pow_i_i_u(x, y).map(Into::<Dynamic>::into).map(Some),
">>" => return shr_u(x, y).map(Into::<Dynamic>::into).map(Some),
"<<" => return shl_u(x, y).map(Into::<Dynamic>::into).map(Some),
"~" => return pow_i_i_u(x, y).map(Into::into).map(Some),
">>" => return shr_u(x, y).map(Into::into).map(Some),
"<<" => return shl_u(x, y).map(Into::into).map(Some),
_ => (),
}
@ -2359,7 +2359,7 @@ fn run_builtin_binary_op(
"*" => return Ok(Some((x * y).into())),
"/" => return Ok(Some((x / y).into())),
"%" => return Ok(Some((x % y).into())),
"~" => return pow_f_f(x, y).map(Into::<Dynamic>::into).map(Some),
"~" => return pow_f_f(x, y).map(Into::into).map(Some),
"==" => return Ok(Some((x == y).into())),
"!=" => return Ok(Some((x != y).into())),
">" => return Ok(Some((x > y).into())),

View File

@ -15,6 +15,7 @@ use crate::stdlib::{
fmt::Display,
format,
string::{String, ToString},
vec::Vec,
};
fn prepend<T: Display>(x: T, y: ImmutableString) -> FuncReturn<ImmutableString> {
@ -290,4 +291,12 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
Ok(())
},
);
// Register string iterator
lib.set_iter(
TypeId::of::<ImmutableString>(),
|arr| Box::new(
arr.cast::<ImmutableString>().chars().collect::<Vec<_>>().into_iter().map(Into::into)
) as Box<dyn Iterator<Item = Dynamic>>,
);
});

View File

@ -30,6 +30,26 @@ fn test_for_array() -> Result<(), Box<EvalAltResult>> {
Ok(())
}
#[test]
fn test_for_string() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
let script = r#"
let s = "hello";
let sum = 0;
for ch in s {
sum += ch.to_int();
}
sum
"#;
assert_eq!(engine.eval::<INT>(script)?, 532);
Ok(())
}
#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))]
#[test]