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_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`. |
|
| `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. |
|
| `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.
|
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.
|
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
|
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
|
y.pad(10, "hello"); // pad the array up to 10 elements
|
||||||
|
|
||||||
print(y.len()); // prints 10
|
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 |
|
| `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) |
|
| `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 |
|
| `+` 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:
|
Examples:
|
||||||
|
|
||||||
@ -1194,6 +1200,14 @@ y["xyz"] == ();
|
|||||||
|
|
||||||
print(y.len()); // prints 3
|
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
|
y.clear(); // empty the object map
|
||||||
|
|
||||||
print(y.len()); // prints 0
|
print(y.len()); // prints 0
|
||||||
@ -1357,6 +1371,24 @@ for x in range(0, 50) {
|
|||||||
print(x);
|
print(x);
|
||||||
if x == 42 { break; } // break out of for loop
|
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
|
`return`-ing values
|
||||||
|
@ -629,11 +629,22 @@ impl Engine<'_> {
|
|||||||
self.register_fn(KEYWORD_DEBUG, |x: &mut Map| -> String {
|
self.register_fn(KEYWORD_DEBUG, |x: &mut Map| -> String {
|
||||||
format!("#{:?}", x)
|
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
|
// Register range function
|
||||||
fn reg_iterator<T: Any + Clone>(engine: &mut Engine)
|
fn reg_range<T: Any + Clone>(engine: &mut Engine)
|
||||||
where
|
where
|
||||||
Range<T>: Iterator<Item = T>,
|
Range<T>: Iterator<Item = T>,
|
||||||
{
|
{
|
||||||
@ -642,12 +653,12 @@ impl Engine<'_> {
|
|||||||
a.downcast_ref::<Range<T>>()
|
a.downcast_ref::<Range<T>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone()
|
.clone()
|
||||||
.map(|n| n.into_dynamic()),
|
.map(|x| x.into_dynamic()),
|
||||||
) as Box<dyn Iterator<Item = 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));
|
self.register_fn("range", |i1: INT, i2: INT| (i1..i2));
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
@ -656,7 +667,7 @@ impl Engine<'_> {
|
|||||||
macro_rules! reg_range {
|
macro_rules! reg_range {
|
||||||
($self:expr, $x:expr, $( $y:ty ),*) => (
|
($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>);
|
$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);
|
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"),
|
(type_name::<Dynamic>(), "dynamic"),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
|
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Create the new scripting Engine
|
// 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};
|
use rhai::{Engine, EvalAltResult, INT};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_for() -> Result<(), EvalAltResult> {
|
fn test_for_array() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
let script = r"
|
let script = r"
|
||||||
@ -18,10 +18,39 @@ fn test_for() -> Result<(), EvalAltResult> {
|
|||||||
sum2 += x;
|
sum2 += x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for x in range(1, 6, 3) {
|
||||||
|
sum2 += x;
|
||||||
|
}
|
||||||
|
|
||||||
sum1 + sum2
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -100,3 +100,26 @@ fn test_map_return() -> Result<(), EvalAltResult> {
|
|||||||
|
|
||||||
Ok(())
|
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