Add stepped range function and keys/values for maps.
This commit is contained in:
parent
92b549b828
commit
12a379dd57
34
README.md
34
README.md
@ -68,7 +68,7 @@ Optional features
|
||||
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
|
||||
| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
||||
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||
| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, `Engine`, [`Scope`] and `AST` are all `Send + Sync`. |
|
||||
| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, [`Engine`], [`Scope`] and `AST` are all `Send + Sync`. |
|
||||
|
||||
By default, Rhai includes all the standard functionalities in a small, tight package. Most features are here to opt-**out** of certain functionalities that are not needed.
|
||||
Excluding unneeded functionalities can result in smaller, faster builds as well as less bugs due to a more restricted language.
|
||||
@ -1102,6 +1102,10 @@ last == 5;
|
||||
|
||||
print(y.len()); // prints 3
|
||||
|
||||
for item in y { // arrays can be iterated with a 'for' statement
|
||||
print(item);
|
||||
}
|
||||
|
||||
y.pad(10, "hello"); // pad the array up to 10 elements
|
||||
|
||||
print(y.len()); // prints 10
|
||||
@ -1148,6 +1152,8 @@ The following functions (defined in the standard library but excluded if [`no_st
|
||||
| `clear` | empties the object map |
|
||||
| `mixin` | mixes in all the properties of the second object map to the first (values of properties with the same names replace the existing values) |
|
||||
| `+` operator | merges the first object map with the second |
|
||||
| `keys` | returns an array of all the property names (in random order) |
|
||||
| `values` | returns an array of all the property values (in random order) |
|
||||
|
||||
Examples:
|
||||
|
||||
@ -1194,6 +1200,14 @@ y["xyz"] == ();
|
||||
|
||||
print(y.len()); // prints 3
|
||||
|
||||
for name in keys(y) { // get an array of all the property names via the 'keys' function
|
||||
print(name);
|
||||
}
|
||||
|
||||
for val in values(y) { // get an array of all the property values via the 'values' function
|
||||
print(val);
|
||||
}
|
||||
|
||||
y.clear(); // empty the object map
|
||||
|
||||
print(y.len()); // prints 0
|
||||
@ -1357,6 +1371,24 @@ for x in range(0, 50) {
|
||||
print(x);
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
|
||||
|
||||
// The 'range' function also takes a step
|
||||
for x in range(0, 50, 3) { // step by 3
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
|
||||
// Iterate through the values of an object map
|
||||
let map = #{a:1, b:3, c:5, d:7, e:9};
|
||||
|
||||
// Remember that keys are returned in random order
|
||||
for x in keys(map) {
|
||||
if x > 10 { continue; } // skip to the next iteration
|
||||
print(x);
|
||||
if x == 42 { break; } // break out of for loop
|
||||
}
|
||||
```
|
||||
|
||||
`return`-ing values
|
||||
|
@ -629,11 +629,22 @@ impl Engine<'_> {
|
||||
self.register_fn(KEYWORD_DEBUG, |x: &mut Map| -> String {
|
||||
format!("#{:?}", x)
|
||||
});
|
||||
|
||||
// Register map access functions
|
||||
self.register_fn("keys", |map: Map| {
|
||||
map.into_iter()
|
||||
.map(|(k, _)| k.into_dynamic())
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
self.register_fn("values", |map: Map| {
|
||||
map.into_iter().map(|(_, v)| v).collect::<Vec<_>>()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Register range function
|
||||
fn reg_iterator<T: Any + Clone>(engine: &mut Engine)
|
||||
fn reg_range<T: Any + Clone>(engine: &mut Engine)
|
||||
where
|
||||
Range<T>: Iterator<Item = T>,
|
||||
{
|
||||
@ -642,12 +653,12 @@ impl Engine<'_> {
|
||||
a.downcast_ref::<Range<T>>()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.map(|n| n.into_dynamic()),
|
||||
.map(|x| x.into_dynamic()),
|
||||
) as Box<dyn Iterator<Item = Dynamic>>
|
||||
});
|
||||
}
|
||||
|
||||
reg_iterator::<INT>(self);
|
||||
reg_range::<INT>(self);
|
||||
self.register_fn("range", |i1: INT, i2: INT| (i1..i2));
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
@ -656,7 +667,7 @@ impl Engine<'_> {
|
||||
macro_rules! reg_range {
|
||||
($self:expr, $x:expr, $( $y:ty ),*) => (
|
||||
$(
|
||||
reg_iterator::<$y>(self);
|
||||
reg_range::<$y>(self);
|
||||
$self.register_fn($x, (|x: $y, y: $y| x..y) as fn(x: $y, y: $y)->Range<$y>);
|
||||
)*
|
||||
)
|
||||
@ -664,6 +675,67 @@ impl Engine<'_> {
|
||||
|
||||
reg_range!(self, "range", i8, u8, i16, u16, i32, i64, u32, u64);
|
||||
}
|
||||
|
||||
// Register range function with step
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
struct StepRange<T>(T, T, T)
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Any + Clone + PartialOrd;
|
||||
|
||||
impl<T> Iterator for StepRange<T>
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Any + Clone + PartialOrd,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
if self.0 < self.1 {
|
||||
let v = self.0.clone();
|
||||
self.0 = &v + &self.2;
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reg_step<T>(engine: &mut Engine)
|
||||
where
|
||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||
T: Any + Clone + PartialOrd,
|
||||
StepRange<T>: Iterator<Item = T>,
|
||||
{
|
||||
engine.register_iterator::<StepRange<T>, _>(|a: &Dynamic| {
|
||||
Box::new(
|
||||
a.downcast_ref::<StepRange<T>>()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.map(|x| x.into_dynamic()),
|
||||
) as Box<dyn Iterator<Item = Dynamic>>
|
||||
});
|
||||
}
|
||||
|
||||
reg_step::<INT>(self);
|
||||
self.register_fn("range", |i1: INT, i2: INT, step: INT| {
|
||||
StepRange(i1, i2, step)
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
macro_rules! reg_step {
|
||||
($self:expr, $x:expr, $( $y:ty ),*) => (
|
||||
$(
|
||||
reg_step::<$y>(self);
|
||||
$self.register_fn($x, (|x: $y, y: $y, step: $y| StepRange(x,y,step)) as fn(x: $y, y: $y, step: $y)->StepRange<$y>);
|
||||
)*
|
||||
)
|
||||
}
|
||||
|
||||
reg_step!(self, "range", i8, u8, i16, u16, i32, i64, u32, u64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,7 +236,7 @@ impl Default for Engine<'_> {
|
||||
(type_name::<Dynamic>(), "dynamic"),
|
||||
]
|
||||
.iter()
|
||||
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect();
|
||||
|
||||
// Create the new scripting Engine
|
||||
|
35
tests/for.rs
35
tests/for.rs
@ -1,8 +1,8 @@
|
||||
#![cfg(not(feature = "no_index"))]
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[test]
|
||||
fn test_for() -> Result<(), EvalAltResult> {
|
||||
fn test_for_array() -> Result<(), EvalAltResult> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
let script = r"
|
||||
@ -18,10 +18,39 @@ fn test_for() -> Result<(), EvalAltResult> {
|
||||
sum2 += x;
|
||||
}
|
||||
|
||||
for x in range(1, 6, 3) {
|
||||
sum2 += x;
|
||||
}
|
||||
|
||||
sum1 + sum2
|
||||
";
|
||||
|
||||
assert_eq!(engine.eval::<INT>(script)?, 30);
|
||||
assert_eq!(engine.eval::<INT>(script)?, 35);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[test]
|
||||
fn test_for_object() -> Result<(), EvalAltResult> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
let script = r#"
|
||||
let sum = 0;
|
||||
let keys = "";
|
||||
let map = #{a: 1, b: 2, c: 3};
|
||||
|
||||
for key in keys(map) {
|
||||
keys += key;
|
||||
}
|
||||
for value in values(map) {
|
||||
sum += value;
|
||||
}
|
||||
|
||||
keys.len() + sum
|
||||
"#;
|
||||
|
||||
assert_eq!(engine.eval::<INT>(script)?, 9);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -100,3 +100,26 @@ fn test_map_return() -> Result<(), EvalAltResult> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_for() -> Result<(), EvalAltResult> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r#"
|
||||
let map = #{a: 1, b: true, c: 123.456};
|
||||
let s = "";
|
||||
|
||||
for key in keys(map) {
|
||||
s += key;
|
||||
}
|
||||
|
||||
s.len()
|
||||
"#
|
||||
)?,
|
||||
3
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user