diff --git a/README.md b/README.md index 78fbae94..401b49fc 100644 --- a/README.md +++ b/README.md @@ -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); diff --git a/examples/repl.rs b/examples/repl.rs index 4a3bd561..f18a56e5 100644 --- a/examples/repl.rs +++ b/examples/repl.rs @@ -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(); diff --git a/src/engine.rs b/src/engine.rs index e1beb635..c94d1423 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2265,14 +2265,14 @@ fn run_builtin_binary_op( #[cfg(not(feature = "unchecked"))] match op { - "+" => 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), + "+" => 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::::into).map(Some), - ">>" => return shr_u(x, y).map(Into::::into).map(Some), - "<<" => return shl_u(x, y).map(Into::::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::::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())), diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 6b5928c1..4223b375 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -15,6 +15,7 @@ use crate::stdlib::{ fmt::Display, format, string::{String, ToString}, + vec::Vec, }; fn prepend(x: T, y: ImmutableString) -> FuncReturn { @@ -290,4 +291,12 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str Ok(()) }, ); + + // Register string iterator + lib.set_iter( + TypeId::of::(), + |arr| Box::new( + arr.cast::().chars().collect::>().into_iter().map(Into::into) + ) as Box>, + ); }); diff --git a/tests/for.rs b/tests/for.rs index cddff6ed..4f99c06f 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -30,6 +30,26 @@ fn test_for_array() -> Result<(), Box> { Ok(()) } +#[test] +fn test_for_string() -> Result<(), Box> { + 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::(script)?, 532); + + Ok(()) +} + #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] #[test]