Encapsulate scope into EvalContext;

Fix bug with custom syntax delta.
This commit is contained in:
Stephen Chung 2020-10-19 19:11:55 +08:00
parent d1f9f4096d
commit a9fd0ff4de
15 changed files with 138 additions and 108 deletions

View File

@ -11,6 +11,7 @@ Breaking changes
* `EvalAltResult::ErrorReadingScriptFile` is removed in favor of the new `EvalAltResult::ErrorSystem`. * `EvalAltResult::ErrorReadingScriptFile` is removed in favor of the new `EvalAltResult::ErrorSystem`.
* `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`. * `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`.
* `Engine::register_raw_fn` and `FnPtr::call_dynamic` function signatures have changed. * `Engine::register_raw_fn` and `FnPtr::call_dynamic` function signatures have changed.
* Callback signatures to `Engine::on_var` and `Engine::register_custom_syntax` have changed.
New features New features
------------ ------------

View File

@ -114,25 +114,19 @@ Any custom syntax must include an _implementation_ of it.
The function signature of an implementation is: The function signature of an implementation is:
> `Fn(scope: &mut Scope, context: &mut EvalContext, inputs: &[Expression])` > `Fn(context: &mut EvalContext, inputs: &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>`
> `-> Result<Dynamic, Box<EvalAltResult>>`
where: where:
* `scope: &mut Scope` - mutable reference to the current [`Scope`]; variables can be added to it. * `context: &mut EvalContext` - mutable reference to the current evaluation _context_, exposing the following:
* `context.scope: &mut Scope` - mutable reference to the current [`Scope`]; variables can be added to/removed from it.
* `context: &mut EvalContext` - mutable reference to the current evaluation _context_ (**do not touch**) which exposes the following fields:
* `context.engine(): &Engine` - reference to the current [`Engine`]. * `context.engine(): &Engine` - reference to the current [`Engine`].
* `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions. * `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions.
* `context.this_ptr(): Option<&Dynamic>` - reference to the current bound [`this`] pointer, if any.
* `context.call_level(): usize` - the current nesting level of function calls. * `context.call_level(): usize` - the current nesting level of function calls.
* `inputs: &[Expression]` - a list of input expression trees. * `inputs: &[Expression]` - a list of input expression trees.
#### WARNING - Lark's Vomit
The `context` parameter contains the evaluation _context_ and should not be touched or Bad Things Happen™.
It should simply be passed straight-through the the [`Engine`].
### Access Arguments ### Access Arguments
The most important argument is `inputs` where the matched identifiers (`$ident$`), expressions/statements (`$expr$`) The most important argument is `inputs` where the matched identifiers (`$ident$`), expressions/statements (`$expr$`)
@ -152,8 +146,8 @@ Use the `EvalContext::eval_expression_tree` method to evaluate an arbitrary expr
within the current evaluation context. within the current evaluation context.
```rust ```rust
let expr = inputs.get(0).unwrap(); let expression = inputs.get(0).unwrap();
let result = context.eval_expression_tree(scope, expr)?; let result = context.eval_expression_tree(expression)?;
``` ```
### Declare Variables ### Declare Variables
@ -163,15 +157,16 @@ New variables maybe declared (usually with a variable name that is passed in via
It can simply be pushed into the [`Scope`]. It can simply be pushed into the [`Scope`].
However, beware that all new variables must be declared _prior_ to evaluating any expression tree. However, beware that all new variables must be declared _prior_ to evaluating any expression tree.
In other words, any `Scope::push` calls must come _before_ any `EvalContext::eval_expression_tree` calls. In other words, any [`Scope`] calls that change the list of must come _before_ any
`EvalContext::eval_expression_tree` calls.
```rust ```rust
let var_name = inputs[0].get_variable_name().unwrap().to_string(); let var_name = inputs[0].get_variable_name().unwrap();
let expr = inputs.get(1).unwrap(); let expression = inputs.get(1).unwrap();
scope.push(var_name, 0 as INT); // do this BEFORE 'context.eval_expression_tree'! context.scope.push(var_name, 0 as INT); // do this BEFORE 'context.eval_expression_tree'!
let result = context.eval_expression_tree(context, scope, expr)?; let result = context.eval_expression_tree(expression)?;
``` ```
@ -188,7 +183,6 @@ The syntax is passed simply as a slice of `&str`.
```rust ```rust
// Custom syntax implementation // Custom syntax implementation
fn implementation_func( fn implementation_func(
scope: &mut Scope,
context: &mut EvalContext, context: &mut EvalContext,
inputs: &[Expression] inputs: &[Expression]
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -196,15 +190,15 @@ fn implementation_func(
let stmt = inputs.get(1).unwrap(); let stmt = inputs.get(1).unwrap();
let condition = inputs.get(2).unwrap(); let condition = inputs.get(2).unwrap();
// Push one new variable into the 'scope' BEFORE 'context.eval_expression_tree' // Push one new variable into the scope BEFORE 'context.eval_expression_tree'
scope.push(var_name, 0 as INT); context.scope.push(var_name, 0 as INT);
loop { loop {
// Evaluate the statement block // Evaluate the statement block
context.eval_expression_tree(scope, stmt)?; context.eval_expression_tree(stmt)?;
// Evaluate the condition expression // Evaluate the condition expression
let stop = !context.eval_expression_tree(scope, condition)? let stop = !context.eval_expression_tree(condition)?
.as_bool().map_err(|err| Box::new( .as_bool().map_err(|err| Box::new(
EvalAltResult::ErrorMismatchDataType( EvalAltResult::ErrorMismatchDataType(
"bool".to_string(), "bool".to_string(),

View File

@ -62,7 +62,7 @@ Function Signature
The function signature passed to `Engine::on_var` takes the following form: The function signature passed to `Engine::on_var` takes the following form:
> `Fn(name: &str, index: usize, scope: &Scope, context: &EvalContext)` > `Fn(name: &str, index: usize, context: &EvalContext)`
> `-> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static` > `-> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static`
where: where:
@ -74,11 +74,11 @@ where:
If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed.
* `scope: &Scope` - reference to the current [`Scope`] containing all variables up to the current evaluation position.
* `context: &EvalContext` - reference to the current evaluation _context_, which exposes the following fields: * `context: &EvalContext` - reference to the current evaluation _context_, which exposes the following fields:
* `context.scope: &Scope` - reference to the current [`Scope`] containing all variables up to the current evaluation position.
* `context.engine(): &Engine` - reference to the current [`Engine`]. * `context.engine(): &Engine` - reference to the current [`Engine`].
* `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions. * `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions.
* `context.this_ptr(): Option<&Dynamic>` - reference to the current bound [`this`] pointer, if any.
* `context.call_level(): usize` - the current nesting level of function calls. * `context.call_level(): usize` - the current nesting level of function calls.
### Return Value ### Return Value

View File

@ -3,12 +3,12 @@
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::engine::{Engine, EvalContext, Imports, State}; use crate::engine::{Engine, EvalContext, Imports, State};
use crate::error::ParseError; use crate::error::ParseError;
use crate::fn_native::{NativeCallContext, SendSync}; use crate::fn_native::{FnCallArgs, NativeCallContext, SendSync};
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::parser::AST; use crate::parser::AST;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::token::{lex, Position}; use crate::token::Position;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::{ use crate::{
@ -68,7 +68,7 @@ impl Engine {
&mut self, &mut self,
name: &str, name: &str,
arg_types: &[TypeId], arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>> func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> &mut Self { ) -> &mut Self {
@ -913,7 +913,7 @@ impl Engine {
scripts: &[&str], scripts: &[&str],
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> { ) -> Result<AST, ParseError> {
let stream = lex(scripts, None, self); let stream = self.lex(scripts, None);
self.parse(&mut stream.peekable(), scope, optimization_level) self.parse(&mut stream.peekable(), scope, optimization_level)
} }
@ -1067,7 +1067,7 @@ impl Engine {
.into()); .into());
}; };
let stream = lex( let stream = self.lex(
&scripts, &scripts,
if has_null { if has_null {
Some(Box::new(|token| match token { Some(Box::new(|token| match token {
@ -1078,7 +1078,6 @@ impl Engine {
} else { } else {
None None
}, },
self,
); );
let ast = let ast =
self.parse_global_expr(&mut stream.peekable(), &scope, OptimizationLevel::None)?; self.parse_global_expr(&mut stream.peekable(), &scope, OptimizationLevel::None)?;
@ -1162,7 +1161,7 @@ impl Engine {
script: &str, script: &str,
) -> Result<AST, ParseError> { ) -> Result<AST, ParseError> {
let scripts = [script]; let scripts = [script];
let stream = lex(&scripts, None, self); let stream = self.lex(&scripts, None);
{ {
let mut peekable = stream.peekable(); let mut peekable = stream.peekable();
self.parse_global_expr(&mut peekable, scope, self.optimization_level) self.parse_global_expr(&mut peekable, scope, self.optimization_level)
@ -1323,7 +1322,7 @@ impl Engine {
script: &str, script: &str,
) -> Result<T, Box<EvalAltResult>> { ) -> Result<T, Box<EvalAltResult>> {
let scripts = [script]; let scripts = [script];
let stream = lex(&scripts, None, self); let stream = self.lex(&scripts, None);
// No need to optimize a lone expression // No need to optimize a lone expression
let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?; let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
@ -1421,6 +1420,7 @@ impl Engine {
}) })
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::Return(out, _) => Ok(out), EvalAltResult::Return(out, _) => Ok(out),
EvalAltResult::LoopBreak(_, _) => unreachable!(),
_ => Err(err), _ => Err(err),
}) })
.map(|v| (v, state.operations)) .map(|v| (v, state.operations))
@ -1464,7 +1464,7 @@ impl Engine {
script: &str, script: &str,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
let scripts = [script]; let scripts = [script];
let stream = lex(&scripts, None, self); let stream = self.lex(&scripts, None);
let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?; let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
self.consume_ast_with_scope(scope, &ast) self.consume_ast_with_scope(scope, &ast)
} }
@ -1495,7 +1495,8 @@ impl Engine {
.map_or_else( .map_or_else(
|err| match *err { |err| match *err {
EvalAltResult::Return(_, _) => Ok(()), EvalAltResult::Return(_, _) => Ok(()),
err => Err(Box::new(err)), EvalAltResult::LoopBreak(_, _) => unreachable!(),
_ => Err(err),
}, },
|_| Ok(()), |_| Ok(()),
) )
@ -1643,7 +1644,7 @@ impl Engine {
lib: &Module, lib: &Module,
name: &str, name: &str,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
args: &mut [&mut Dynamic], args: &mut FnCallArgs,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let fn_def = lib let fn_def = lib
.get_script_fn(name, args.len(), true) .get_script_fn(name, args.len(), true)
@ -1713,7 +1714,7 @@ impl Engine {
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
/// ///
/// // Register a variable resolver. /// // Register a variable resolver.
/// engine.on_var(|name, _, _, _| { /// engine.on_var(|name, _, _| {
/// match name { /// match name {
/// "MYSTIC_NUMBER" => Ok(Some(42_i64.into())), /// "MYSTIC_NUMBER" => Ok(Some(42_i64.into())),
/// _ => Ok(None) /// _ => Ok(None)
@ -1728,7 +1729,7 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn on_var( pub fn on_var(
&mut self, &mut self,
callback: impl Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> callback: impl Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> &mut Self { ) -> &mut Self {

View File

@ -441,16 +441,17 @@ pub struct Limits {
/// Context of a script evaluation process. /// Context of a script evaluation process.
#[derive(Debug)] #[derive(Debug)]
pub struct EvalContext<'e, 'a, 's, 'm, 't, 'd: 't> { pub struct EvalContext<'e, 'x, 'px: 'x, 'a, 's, 'm, 't, 'pt: 't> {
engine: &'e Engine, engine: &'e Engine,
pub scope: &'x mut Scope<'px>,
pub(crate) mods: &'a mut Imports, pub(crate) mods: &'a mut Imports,
pub(crate) state: &'s mut State, pub(crate) state: &'s mut State,
lib: &'m Module, lib: &'m Module,
pub(crate) this_ptr: &'t mut Option<&'d mut Dynamic>, pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>,
level: usize, level: usize,
} }
impl<'e, 'a, 's, 'm, 't, 'd> EvalContext<'e, 'a, 's, 'm, 't, 'd> { impl<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> {
/// The current `Engine`. /// The current `Engine`.
#[inline(always)] #[inline(always)]
pub fn engine(&self) -> &'e Engine { pub fn engine(&self) -> &'e Engine {
@ -469,6 +470,11 @@ impl<'e, 'a, 's, 'm, 't, 'd> EvalContext<'e, 'a, 's, 'm, 't, 'd> {
pub fn namespace(&self) -> &'m Module { pub fn namespace(&self) -> &'m Module {
self.lib self.lib
} }
/// The current bound `this` pointer, if any.
#[inline(always)]
pub fn this_ptr(&self) -> Option<&Dynamic> {
self.this_ptr.as_ref().map(|v| &**v)
}
/// The current nesting level of function calls. /// The current nesting level of function calls.
#[inline(always)] #[inline(always)]
pub fn call_level(&self) -> usize { pub fn call_level(&self) -> usize {
@ -815,6 +821,7 @@ impl Engine {
if let Some(ref resolve_var) = self.resolve_var { if let Some(ref resolve_var) = self.resolve_var {
let context = EvalContext { let context = EvalContext {
engine: self, engine: self,
scope,
mods, mods,
state, state,
lib, lib,
@ -822,7 +829,7 @@ impl Engine {
level: 0, level: 0,
}; };
if let Some(result) = if let Some(result) =
resolve_var(name, index, scope, &context).map_err(|err| err.fill_position(*pos))? resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))?
{ {
return Ok((result.into(), name, ScopeEntryType::Constant, *pos)); return Ok((result.into(), name, ScopeEntryType::Constant, *pos));
} }
@ -1742,17 +1749,22 @@ impl Engine {
Expr::Unit(_) => Ok(().into()), Expr::Unit(_) => Ok(().into()),
Expr::Custom(x) => { Expr::Custom(x) => {
let func = (x.0).1.as_ref(); let func = (x.0).func();
let ep = (x.0).0.iter().map(|e| e.into()).collect::<StaticVec<_>>(); let expressions = (x.0)
.keywords()
.iter()
.map(Into::into)
.collect::<StaticVec<_>>();
let mut context = EvalContext { let mut context = EvalContext {
engine: self, engine: self,
scope,
mods, mods,
state, state,
lib, lib,
this_ptr, this_ptr,
level, level,
}; };
func(scope, &mut context, ep.as_ref()) func(&mut context, &expressions)
} }
_ => unreachable!(), _ => unreachable!(),

View File

@ -435,9 +435,11 @@ impl Engine {
&self, &self,
lib: &Module, lib: &Module,
name: &str, name: &str,
arg_types: &[TypeId], arg_types: impl AsRef<[TypeId]>,
pub_only: bool, pub_only: bool,
) -> bool { ) -> bool {
let arg_types = arg_types.as_ref();
let arg_len = if arg_types.is_empty() { let arg_len = if arg_types.is_empty() {
usize::MAX usize::MAX
} else { } else {
@ -840,7 +842,7 @@ impl Engine {
lib: &Module, lib: &Module,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
name: &str, name: &str,
args_expr: &[Expr], args_expr: impl AsRef<[Expr]>,
def_val: &Option<Dynamic>, def_val: &Option<Dynamic>,
mut hash_script: u64, mut hash_script: u64,
native: bool, native: bool,
@ -848,6 +850,8 @@ impl Engine {
capture: bool, capture: bool,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let args_expr = args_expr.as_ref();
// Handle Fn() // Handle Fn()
if name == KEYWORD_FN_PTR && args_expr.len() == 1 { if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>())); let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
@ -1085,11 +1089,13 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
modules: &Option<Box<ModuleRef>>, modules: &Option<Box<ModuleRef>>,
name: &str, name: &str,
args_expr: &[Expr], args_expr: impl AsRef<[Expr]>,
def_val: Option<bool>, def_val: Option<bool>,
hash_script: u64, hash_script: u64,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let args_expr = args_expr.as_ref();
let modules = modules.as_ref().unwrap(); let modules = modules.as_ref().unwrap();
let mut arg_values: StaticVec<_>; let mut arg_values: StaticVec<_>;
let mut first_arg_value = None; let mut first_arg_value = None;

View File

@ -6,7 +6,6 @@ use crate::module::Module;
use crate::parser::{FnAccess, ScriptFnDef}; use crate::parser::{FnAccess, ScriptFnDef};
use crate::plugin::PluginFunction; use crate::plugin::PluginFunction;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::Scope;
use crate::token::{is_valid_identifier, Position}; use crate::token::{is_valid_identifier, Position};
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use crate::{calc_fn_hash, StaticVec}; use crate::{calc_fn_hash, StaticVec};
@ -265,14 +264,12 @@ pub type Callback<T, R> = Box<dyn Fn(&T) -> R + Send + Sync + 'static>;
/// A standard callback function. /// A standard callback function.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub type OnVarCallback = Box< pub type OnVarCallback =
dyn Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> Box<dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static>;
+ 'static,
>;
/// A standard callback function. /// A standard callback function.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub type OnVarCallback = Box< pub type OnVarCallback = Box<
dyn Fn(&str, usize, &Scope, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,

View File

@ -536,11 +536,11 @@ impl Module {
&mut self, &mut self,
name: impl Into<String>, name: impl Into<String>,
arg_types: &[TypeId], arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>> func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> u64 { ) -> u64 {
let f = move |context: NativeCallContext, args: &mut [&mut Dynamic]| { let f = move |context: NativeCallContext, args: &mut FnCallArgs| {
func(context, args).map(Dynamic::from) func(context, args).map(Dynamic::from)
}; };
self.set_fn( self.set_fn(

View File

@ -919,6 +919,19 @@ impl Hash for CustomExpr {
} }
} }
impl CustomExpr {
/// Get the keywords for this `CustomExpr`.
#[inline(always)]
pub fn keywords(&self) -> &[Expr] {
&self.0
}
/// Get the implementation function for this `CustomExpr`.
#[inline(always)]
pub fn func(&self) -> &FnCustomSyntaxEval {
self.1.as_ref()
}
}
/// _[INTERNALS]_ A type wrapping a floating-point number. /// _[INTERNALS]_ A type wrapping a floating-point number.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
@ -2670,7 +2683,10 @@ fn parse_expr(
// Adjust the variables stack // Adjust the variables stack
match syntax.scope_delta { match syntax.scope_delta {
delta if delta > 0 => { delta if delta > 0 => {
state.stack.push(("".to_string(), ScopeEntryType::Normal)) state.stack.resize(
state.stack.len() + delta as usize,
("".to_string(), ScopeEntryType::Normal),
);
} }
delta if delta < 0 && state.stack.len() <= delta.abs() as usize => { delta if delta < 0 && state.stack.len() <= delta.abs() as usize => {
state.stack.clear() state.stack.clear()

View File

@ -2,7 +2,7 @@
pub use crate::any::Dynamic; pub use crate::any::Dynamic;
pub use crate::engine::Engine; pub use crate::engine::Engine;
pub use crate::fn_native::{CallableFunction, NativeCallContext}; pub use crate::fn_native::{CallableFunction, FnCallArgs, NativeCallContext};
pub use crate::fn_register::{RegisterFn, RegisterResultFn}; pub use crate::fn_register::{RegisterFn, RegisterResultFn};
pub use crate::module::Module; pub use crate::module::Module;
pub use crate::parser::FnAccess; pub use crate::parser::FnAccess;
@ -25,7 +25,7 @@ pub trait PluginFunction {
fn call( fn call(
&self, &self,
context: NativeCallContext, context: NativeCallContext,
args: &mut [&mut Dynamic], args: &mut FnCallArgs,
) -> Result<Dynamic, Box<EvalAltResult>>; ) -> Result<Dynamic, Box<EvalAltResult>>;
/// Is this plugin function a method? /// Is this plugin function a method?

View File

@ -6,7 +6,6 @@ use crate::error::{LexError, ParseError};
use crate::fn_native::{SendSync, Shared}; use crate::fn_native::{SendSync, Shared};
use crate::parser::Expr; use crate::parser::Expr;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::Scope;
use crate::token::{is_valid_identifier, Position, Token}; use crate::token::{is_valid_identifier, Position, Token};
use crate::StaticVec; use crate::StaticVec;
@ -19,12 +18,11 @@ use crate::stdlib::{
/// A general expression evaluation trait object. /// A general expression evaluation trait object.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub type FnCustomSyntaxEval = pub type FnCustomSyntaxEval =
dyn Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>; dyn Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>;
/// A general expression evaluation trait object. /// A general expression evaluation trait object.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub type FnCustomSyntaxEval = dyn Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>> pub type FnCustomSyntaxEval =
+ Send dyn Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
+ Sync;
/// An expression sub-tree in an AST. /// An expression sub-tree in an AST.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
@ -55,7 +53,7 @@ impl Expression<'_> {
} }
} }
impl EvalContext<'_, '_, '_, '_, '_, '_> { impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
/// Evaluate an expression tree. /// Evaluate an expression tree.
/// ///
/// ## WARNING - Low Level API /// ## WARNING - Low Level API
@ -64,11 +62,10 @@ impl EvalContext<'_, '_, '_, '_, '_, '_> {
#[inline(always)] #[inline(always)]
pub fn eval_expression_tree( pub fn eval_expression_tree(
&mut self, &mut self,
scope: &mut Scope,
expr: &Expression, expr: &Expression,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
self.engine().eval_expr( self.engine().eval_expr(
scope, self.scope,
self.mods, self.mods,
self.state, self.state,
self.namespace(), self.namespace(),
@ -101,12 +98,14 @@ impl Engine {
/// * `func` is the implementation function. /// * `func` is the implementation function.
pub fn register_custom_syntax<S: AsRef<str> + ToString>( pub fn register_custom_syntax<S: AsRef<str> + ToString>(
&mut self, &mut self,
keywords: &[S], keywords: impl AsRef<[S]>,
new_vars: isize, new_vars: isize,
func: impl Fn(&mut Scope, &mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>> func: impl Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> Result<&mut Self, ParseError> { ) -> Result<&mut Self, ParseError> {
let keywords = keywords.as_ref();
let mut segments: StaticVec<_> = Default::default(); let mut segments: StaticVec<_> = Default::default();
for s in keywords { for s in keywords {

View File

@ -1736,18 +1736,19 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
} }
} }
impl Engine {
/// Tokenize an input text stream. /// Tokenize an input text stream.
#[inline] #[inline]
pub fn lex<'a, 'e>( pub fn lex<'a, 'e>(
input: &'a [&'a str], &'e self,
input: impl IntoIterator<Item = &'a &'a str>,
map: Option<Box<dyn Fn(Token) -> Token>>, map: Option<Box<dyn Fn(Token) -> Token>>,
engine: &'e Engine,
) -> TokenIterator<'a, 'e> { ) -> TokenIterator<'a, 'e> {
TokenIterator { TokenIterator {
engine, engine: self,
state: TokenizeState { state: TokenizeState {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
max_string_size: engine.limits_set.max_string_size, max_string_size: self.limits_set.max_string_size,
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
max_string_size: 0, max_string_size: 0,
non_unary: false, non_unary: false,
@ -1758,9 +1759,10 @@ pub fn lex<'a, 'e>(
pos: Position::new(1, 0), pos: Position::new(1, 0),
stream: MultiInputsStream { stream: MultiInputsStream {
buf: None, buf: None,
streams: input.iter().map(|s| s.chars().peekable()).collect(), streams: input.into_iter().map(|s| s.chars().peekable()).collect(),
index: 0, index: 0,
}, },
map, map,
} }
} }
}

View File

@ -1,7 +1,5 @@
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
use rhai::{ use rhai::{Engine, EvalAltResult, FnPtr, Func, ParseErrorType, RegisterFn, Scope, INT};
Dynamic, Engine, EvalAltResult, FnPtr, Func, Module, ParseErrorType, RegisterFn, Scope, INT,
};
use std::any::TypeId; use std::any::TypeId;
#[test] #[test]

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, EvalContext, Expression, ParseErrorType, Scope, INT}; use rhai::{Engine, EvalAltResult, EvalContext, Expression, ParseErrorType, INT};
#[test] #[test]
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> { fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
@ -22,18 +22,18 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
"do", "|", "$ident$", "|", "->", "$block$", "while", "$expr$", "do", "|", "$ident$", "|", "->", "$block$", "while", "$expr$",
], ],
1, 1,
|scope: &mut Scope, context: &mut EvalContext, inputs: &[Expression]| { |context: &mut EvalContext, inputs: &[Expression]| {
let var_name = inputs[0].get_variable_name().unwrap().to_string(); let var_name = inputs[0].get_variable_name().unwrap().to_string();
let stmt = inputs.get(1).unwrap(); let stmt = inputs.get(1).unwrap();
let condition = inputs.get(2).unwrap(); let condition = inputs.get(2).unwrap();
scope.push(var_name, 0 as INT); context.scope.push(var_name, 0 as INT);
loop { loop {
context.eval_expression_tree(scope, stmt)?; context.eval_expression_tree(stmt)?;
let stop = !context let stop = !context
.eval_expression_tree(scope, condition)? .eval_expression_tree(condition)?
.as_bool() .as_bool()
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorMismatchDataType( Box::new(EvalAltResult::ErrorMismatchDataType(
@ -68,7 +68,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
// The first symbol must be an identifier // The first symbol must be an identifier
assert_eq!( assert_eq!(
*engine *engine
.register_custom_syntax(&["!"], 0, |_, _, _| Ok(().into())) .register_custom_syntax(&["!"], 0, |_, _| Ok(().into()))
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::BadInput("Improper symbol for custom syntax: '!'".to_string()) ParseErrorType::BadInput("Improper symbol for custom syntax: '!'".to_string())

View File

@ -62,7 +62,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
scope.push("chameleon", 123 as INT); scope.push("chameleon", 123 as INT);
scope.push("DO_NOT_USE", 999 as INT); scope.push("DO_NOT_USE", 999 as INT);
engine.on_var(|name, _, scope, _| { engine.on_var(|name, _, context| {
match name { match name {
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())), "MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
// Override a variable - make it not found even if it exists! // Override a variable - make it not found even if it exists!
@ -70,7 +70,11 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into()) Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into())
} }
// Silently maps 'chameleon' into 'innocent'. // Silently maps 'chameleon' into 'innocent'.
"chameleon" => scope.get_value("innocent").map(Some).ok_or_else(|| { "chameleon" => context
.scope
.get_value("innocent")
.map(Some)
.ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into() EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into()
}), }),
// Return Ok(None) to continue with the normal variable resolution process. // Return Ok(None) to continue with the normal variable resolution process.