Merge branch 'master' into plugins

This commit is contained in:
Stephen Chung 2020-08-14 11:45:52 +08:00
commit d2ea981fac
5 changed files with 130 additions and 25 deletions

View File

@ -46,7 +46,7 @@ struct Config {
### Make Shared Object ### Make Shared Object
```rust ```rust
let config: Rc<RefCell<Config>> = Rc::new(RefCell::(Default::default())); let config: Rc<RefCell<Config>> = Rc::new(RefCell::new(Default::default()));
``` ```
### Register Config API ### Register Config API
@ -76,7 +76,17 @@ engine.register_fn("config_add", move |values: &mut Array|
let cfg = config.clone(); let cfg = config.clone();
engine.register_fn("config_add", move |key: String, value: bool| engine.register_fn("config_add", move |key: String, value: bool|
cfg.borrow_mut().som_map.insert(key, value) cfg.borrow_mut().some_map.insert(key, value)
);
let cfg = config.clone();
engine.register_fn("config_contains", move |value: String|
cfg.borrow().some_list.contains(&value)
);
let cfg = config.clone();
engine.register_fn("config_is_set", move |value: String|
cfg.borrow().some_map.get(&value).cloned().unwrap_or(false)
); );
``` ```
@ -91,7 +101,10 @@ config_set_id("hello");
config_add("foo"); // add to list config_add("foo"); // add to list
config_add("bar", true); // add to map config_add("bar", true); // add to map
config_add("baz", false); // add to map
if config_contains("hey") || config_is_set("hey") {
config_add("baz", false); // add to map
}
``` ```
### Load the Configuration ### Load the Configuration
@ -103,3 +116,35 @@ let id = config_get_id();
id == "hello"; id == "hello";
``` ```
Consider a Custom Syntax
------------------------
This is probably one of the few scenarios where a [custom syntax] can be recommended.
A properly-designed [custom syntax] can make the configuration file clean, simple to write,
easy to understand and quick to modify.
For example, the above configuration example may be expressed by this custom syntax:
```rust
------------------
| my_config.rhai |
------------------
// Configure ID
id "hello";
// Add to list
list +"foo"
// Add to map
map "bar" => true;
if config contains "hey" || config is_set "hey" {
map "baz" => false;
}
```
Notice that `contains` and `is_set` may be implemented as a [custom operator].

View File

@ -171,9 +171,9 @@ to partition the slice:
let (first, rest) = args.split_at_mut(1); let (first, rest) = args.split_at_mut(1);
// Mutable reference to the first parameter // Mutable reference to the first parameter
let this_ptr: &mut Dynamic = &mut *first[0].write_lock::<A>().unwrap(); let this_ptr: &mut A = &mut *first[0].write_lock::<A>().unwrap();
// Immutable reference to the second value parameter // Immutable reference to the second value parameter
// This can be mutable but there is no point because the parameter is passed by value // This can be mutable but there is no point because the parameter is passed by value
let value_ref: &Dynamic = &*rest[0].read_lock::<B>().unwrap(); let value_ref: &B = &*rest[0].read_lock::<B>().unwrap();
``` ```

View File

@ -611,9 +611,9 @@ impl Module {
) -> u64 { ) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let b = mem::take(args[1]).cast::<B>(); let b = mem::take(args[1]).cast::<B>();
let mut a = args[0].write_lock::<A>().unwrap(); let a = &mut args[0].write_lock::<A>().unwrap();
func(&mut a, b).map(Dynamic::from) func(a, b).map(Dynamic::from)
}; };
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()]; let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f))) self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f)))
@ -735,9 +735,9 @@ impl Module {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let b = mem::take(args[1]).cast::<B>(); let b = mem::take(args[1]).cast::<B>();
let c = mem::take(args[2]).cast::<C>(); let c = mem::take(args[2]).cast::<C>();
let mut a = args[0].write_lock::<A>().unwrap(); let a = &mut args[0].write_lock::<A>().unwrap();
func(&mut a, b, c).map(Dynamic::from) func(a, b, c).map(Dynamic::from)
}; };
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]; let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f))) self.set_fn(name, Public, &arg_types, Func::from_method(Box::new(f)))
@ -769,9 +769,9 @@ impl Module {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let b = mem::take(args[1]).cast::<B>(); let b = mem::take(args[1]).cast::<B>();
let c = mem::take(args[2]).cast::<C>(); let c = mem::take(args[2]).cast::<C>();
let mut a = args[0].write_lock::<A>().unwrap(); let a = &mut args[0].write_lock::<A>().unwrap();
func(&mut a, b, c).map(Dynamic::from) func(a, b, c).map(Dynamic::from)
}; };
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]; let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn( self.set_fn(
@ -892,9 +892,9 @@ impl Module {
let b = mem::take(args[1]).cast::<B>(); let b = mem::take(args[1]).cast::<B>();
let c = mem::take(args[2]).cast::<C>(); let c = mem::take(args[2]).cast::<C>();
let d = mem::take(args[3]).cast::<D>(); let d = mem::take(args[3]).cast::<D>();
let mut a = args[0].write_lock::<A>().unwrap(); let a = &mut args[0].write_lock::<A>().unwrap();
func(&mut a, b, c, d).map(Dynamic::from) func(a, b, c, d).map(Dynamic::from)
}; };
let arg_types = [ let arg_types = [
TypeId::of::<A>(), TypeId::of::<A>(),

View File

@ -414,7 +414,8 @@ struct ParseState<'e> {
/// Tracks a list of external variables (variables that are not explicitly declared in the scope). /// Tracks a list of external variables (variables that are not explicitly declared in the scope).
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
externals: HashMap<String, Position>, externals: HashMap<String, Position>,
/// An indicator that disables variable capturing into externals one single time. /// An indicator that disables variable capturing into externals one single time
/// up until the nearest consumed Identifier token.
/// If set to false the next call to `access_var` will not capture the variable. /// If set to false the next call to `access_var` will not capture the variable.
/// All consequent calls to `access_var` will not be affected /// All consequent calls to `access_var` will not be affected
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
@ -1637,6 +1638,11 @@ fn parse_primary(
// Function call // Function call
Token::Identifier(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => { Token::Identifier(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
// Once the identifier consumed we must enable next variables capturing
#[cfg(not(feature = "no_closure"))]
{
state.allow_capture = true;
}
Expr::Variable(Box::new(((s, settings.pos), None, 0, None))) Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
} }
// Normal variable access // Normal variable access

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Module, RegisterFn, INT}; use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Module, RegisterFn, INT};
use std::any::TypeId; use std::any::TypeId;
use std::mem::take;
#[test] #[test]
fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> { fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
@ -88,6 +89,59 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
42 42
); );
assert_eq!(
engine.eval::<INT>(
r#"
let a = 40;
let f = |x| {
let f = |x| {
let f = |x| plus_one(a) + x;
f.call(x)
};
f.call(x)
};
f.call(1)
"#
)?,
42
);
assert_eq!(
engine.eval::<INT>(
r#"
let a = 21;
let f = |x| a += x;
f.call(a);
a
"#
)?,
42
);
#[allow(deprecated)]
engine.register_raw_fn(
"custom_call",
&[TypeId::of::<INT>(), TypeId::of::<FnPtr>()],
|engine: &Engine, module: &Module, args: &mut [&mut Dynamic]| {
let func = take(args[1]).cast::<FnPtr>();
func.call_dynamic(engine, module, None, [])
},
);
assert_eq!(
engine.eval::<INT>(
r#"
let a = 41;
let b = 0;
let f = || b.custom_call(|| a + 1);
f.call()
"#
)?,
42
);
Ok(()) Ok(())
} }
@ -101,12 +155,12 @@ fn test_closures_data_race() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r#" r#"
let a = 1; let a = 1;
let b = 40; let b = 40;
let foo = |x| { this += a + x }; let foo = |x| { this += a + x };
b.call(foo, 1); b.call(foo, 1);
b b
"# "#
)?, )?,
42 42
); );
@ -115,11 +169,11 @@ fn test_closures_data_race() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.eval::<INT>( .eval::<INT>(
r#" r#"
let a = 20; let a = 20;
let foo = |x| { this += a + x }; let foo = |x| { this += a + x };
a.call(foo, 1); a.call(foo, 1);
a a
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataRace(_, _) EvalAltResult::ErrorDataRace(_, _)