Add set_value to Scope.

This commit is contained in:
Stephen Chung 2020-04-05 19:17:48 +08:00
parent 44d6a5e466
commit c4498d147d
3 changed files with 48 additions and 10 deletions

View File

@ -733,12 +733,14 @@ fn main() -> Result<(), EvalAltResult>
// First create the state
let mut scope = Scope::new();
// Then push some initialized variables into the state
// NOTE: Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64.
// Better stick to them or it gets hard working with the script.
// Then push (i.e. add) some initialized variables into the state.
// Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64.
// Better stick to them or it gets hard working with the script.
scope.push("y", 42_i64);
scope.push("z", 999_i64);
scope.push("s", "hello, world!".to_string()); // remember to use 'String', not '&str'
// 'set_value' adds a variable when one doesn't exist
scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str'
// First invocation
engine.eval_with_scope::<()>(&mut scope, r"
@ -749,10 +751,14 @@ fn main() -> Result<(), EvalAltResult>
// Second invocation using the same state
let result = engine.eval_with_scope::<i64>(&mut scope, "x")?;
println!("result: {}", result); // prints 979
println!("result: {}", result); // prints 979
// Variable y is changed in the script
assert_eq!(scope.get_value::<i64>("y").expect("variable x should exist"), 1);
// Variable y is changed in the script - read it with 'get_value'
assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 1);
// We can modify scope variables directly with 'set_value'
scope.set_value("y", 42_i64);
assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 42);
Ok(())
}

View File

@ -179,16 +179,44 @@ impl<'a> Scope<'a> {
}
/// Get the value of an entry in the Scope, starting from the last.
pub fn get_value<T: Any + Clone>(&self, key: &str) -> Option<T> {
pub fn get_value<T: Any + Clone>(&self, name: &str) -> Option<T> {
self.0
.iter()
.enumerate()
.rev() // Always search a Scope in reverse order
.find(|(_, Entry { name, .. })| name == key)
.find(|(_, Entry { name: key, .. })| name == key)
.and_then(|(_, Entry { value, .. })| value.downcast_ref::<T>())
.map(T::clone)
}
/// Update the value of the named variable.
/// Search starts from the last, and only the last variable matching the specified name is updated.
/// If no variable matching the specified name is found, a new variable is added.
///
/// # Panics
///
/// Panics when trying to update the value of a constant.
pub fn set_value<T: Any + Clone>(&mut self, name: &'a str, value: T) {
match self.get(name) {
Some((
EntryRef {
typ: EntryType::Constant,
..
},
_,
)) => panic!("variable {} is constant", name),
Some((
EntryRef {
index,
typ: EntryType::Normal,
..
},
_,
)) => self.0.get_mut(index).unwrap().value = value.into_dynamic(),
None => self.push(name, value.into_dynamic()),
}
}
/// Get a mutable reference to an entry in the Scope.
pub(crate) fn get_mut(&mut self, key: EntryRef) -> &mut Dynamic {
let entry = self.0.get_mut(key.index).expect("invalid index in Scope");

View File

@ -9,8 +9,12 @@ fn test_var_scope() -> Result<(), EvalAltResult> {
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x")?, 9);
engine.eval_with_scope::<()>(&mut scope, "x = x + 1; x = x + 2;")?;
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x")?, 12);
scope.set_value("x", 42 as INT);
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x")?, 42);
engine.eval_with_scope::<()>(&mut scope, "{let x = 3}")?;
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x")?, 12);
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x")?, 42);
Ok(())
}