Merge pull request #279 from schungx/master

Optimize code and refactor.
This commit is contained in:
Stephen Chung 2020-11-03 13:19:19 +08:00 committed by GitHub
commit 6cba637636
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 683 additions and 724 deletions

View File

@ -20,6 +20,9 @@ Breaking changes
* Custom syntax can no longer start with a keyword (even a _reserved_ one), even if it has been disabled. That is to avoid breaking scripts later when the keyword is no longer disabled. * Custom syntax can no longer start with a keyword (even a _reserved_ one), even if it has been disabled. That is to avoid breaking scripts later when the keyword is no longer disabled.
* `EvalAltResult::ErrorAssignmentToUnknownLHS` is moved to `ParseError::AssignmentToInvalidLHS`. `ParseError::AssignmentToCopy` is removed. * `EvalAltResult::ErrorAssignmentToUnknownLHS` is moved to `ParseError::AssignmentToInvalidLHS`. `ParseError::AssignmentToCopy` is removed.
* `EvalAltResult::ErrorDataTooLarge` is simplified.
* `Engine::on_progress` closure signature now returns `Option<Dynamic>` with the termination value passed on to `EvalAltResult::ErrorTerminated`.
* `ParseErrorType::BadInput` now wraps a `LexError` instead of a text string.
New features New features
------------ ------------

View File

@ -294,9 +294,8 @@ engine.register_custom_syntax_raw(
"update" | "check" | "add" | "remove" => Ok(Some("$ident$".to_string())), "update" | "check" | "add" | "remove" => Ok(Some("$ident$".to_string())),
"cleanup" => Ok(None), "cleanup" => Ok(None),
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput( cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
format!("Improper command: {}", cmd))), LexError::ImproperSymbol(format!("Improper command: {}", cmd))
Position::none(), )), NO_POS)),
)),
}, },
// perform command arg ... // perform command arg ...
3 => match (stream[1].as_str(), stream[2].as_str()) { 3 => match (stream[1].as_str(), stream[2].as_str()) {
@ -308,9 +307,10 @@ engine.register_custom_syntax_raw(
("add", arg) => Ok(None), ("add", arg) => Ok(None),
("remove", arg) => Ok(None), ("remove", arg) => Ok(None),
(cmd, arg) => Err(ParseError(Box::new(ParseErrorType::BadInput( (cmd, arg) => Err(ParseError(Box::new(ParseErrorType::BadInput(
format!("Invalid argument for command {}: {}", cmd, arg))), LexError::ImproperSymbol(
Position::none(), format!("Invalid argument for command {}: {}", cmd, arg)
)), )
)), NO_POS)),
}, },
_ => unreachable!(), _ => unreachable!(),
}, },
@ -336,7 +336,7 @@ where:
The return value is `Result<Option<String>, ParseError>` where: The return value is `Result<Option<String>, ParseError>` where:
| Value | Description | | Value | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Ok(None)` | parsing complete and there are no more symbols to match | | `Ok(None)` | parsing complete and there are no more symbols to match |
| `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` | | `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` |
| `Err(ParseError)` | error that is reflected back to the [`Engine`].<br/>Normally this is `ParseError(ParseErrorType::BadInput(message), Position::none())` to indicate that there is a syntax error, but it can be any `ParseError`. | | `Err(ParseError)` | error that is reflected back to the [`Engine`].<br/>Normally this is `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), NO_POS)` to indicate that there is a syntax error, but it can be any `ParseError`. |

View File

@ -21,11 +21,11 @@ engine.on_var(|name, index, context| {
"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!
"DO_NOT_USE" => Err(Box::new( "DO_NOT_USE" => Err(Box::new(
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()) EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS)
)), )),
// Silently maps 'chameleon' into 'innocent'. // Silently maps 'chameleon' into 'innocent'.
"chameleon" => context.scope.get_value("innocent").map(Some).ok_or_else(|| Box::new( "chameleon" => context.scope.get_value("innocent").map(Some).ok_or_else(|| Box::new(
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()) EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS)
)), )),
// Return Ok(None) to continue with the normal variable resolution process. // Return Ok(None) to continue with the normal variable resolution process.
_ => Ok(None) _ => Ok(None)
@ -83,7 +83,7 @@ where:
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where: The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where:
| Value | Description | | Value | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] | | `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] |
| `Ok(Some(Dynamic))` | value of the variable, treated as a constant | | `Ok(Some(Dynamic))` | value of the variable, treated as a constant |
| `Err(Box<EvalAltResult>)` | error that is reflected back to the [`Engine`].<br/>Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, Position::none())` to indicate that the variable does not exist, but it can be any `EvalAltResult`. | | `Err(Box<EvalAltResult>)` | error that is reflected back to the [`Engine`].<br/>Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, NO_POS)` to indicate that the variable does not exist, but it can be any `EvalAltResult`. |

View File

@ -25,6 +25,14 @@ import "crypto_init"; // run the script file 'crypto_init.rhai' withou
import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock' import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock'
const SECRET_NUMBER = 42;
let mod_file = "crypto_" + SECRET_NUMBER;
import mod_file as my_mod; // load the script file "crypto_42.rhai" and import it as a module named 'my_mod'
// notice that module path names can be dynamically constructed!
// any expression that evaluates to a string is acceptable after the 'import' keyword
lock::encrypt(secret); // use functions defined under the module via '::' lock::encrypt(secret); // use functions defined under the module via '::'
lock::hash::sha256(key); // sub-modules are also supported lock::hash::sha256(key); // sub-modules are also supported

View File

@ -117,7 +117,7 @@ pub fn greet(context: NativeCallContext, callback: FnPtr)
The native call context is also useful in another scenario: protecting a function from malicious scripts. The native call context is also useful in another scenario: protecting a function from malicious scripts.
```rust ```rust
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position}; use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS};
use rhai::plugin::*; // a "prelude" import for macros use rhai::plugin::*; // a "prelude" import for macros
// This function builds an array of arbitrary size, but is protected // This function builds an array of arbitrary size, but is protected
@ -137,7 +137,7 @@ pub fn grow(context: NativeCallContext, size: i64)
"Size to grow".to_string(), "Size to grow".to_string(),
context.engine().max_array_size(), context.engine().max_array_size(),
size as usize, size as usize,
Position::none(), NO_POS,
).into(); ).into();
} }

View File

@ -376,7 +376,7 @@ mod my_module {
The native call context is also useful in another scenario: protecting a function from malicious scripts. The native call context is also useful in another scenario: protecting a function from malicious scripts.
```rust ```rust
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position}; use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS};
use rhai::plugin::*; // a "prelude" import for macros use rhai::plugin::*; // a "prelude" import for macros
#[export_module] #[export_module]
@ -397,7 +397,7 @@ mod my_module {
"Size to grow".to_string(), "Size to grow".to_string(),
context.engine().max_array_size(), context.engine().max_array_size(),
size as usize, size as usize,
Position::none(), NO_POS,
).into(); ).into();
} }

View File

@ -17,13 +17,14 @@ engine.on_progress(|&count| { // parameter is '&u64' - number of operations al
if count % 1000 == 0 { if count % 1000 == 0 {
println!("{}", count); // print out a progress log every 1,000 operations println!("{}", count); // print out a progress log every 1,000 operations
} }
true // return 'true' to continue running the script None // return 'None' to continue running the script
// return 'false' to immediately terminate the script // return 'Some(token)' to immediately terminate the script
}); });
``` ```
The closure passed to `Engine::on_progress` will be called once for every operation. The closure passed to `Engine::on_progress` will be called once for every operation.
Return `false` to terminate the script immediately. Return `Some(token)` to terminate the script immediately, with the provided value
(any [`Dynamic`] value) passed to `EvalAltResult::ErrorTerminated` as a termination token.
Operations Count vs. Progress Percentage Operations Count vs. Progress Percentage

View File

@ -861,24 +861,24 @@ pub struct BinaryExpr {
/// A function call. /// A function call.
#[derive(Debug, Clone, Hash, Default)] #[derive(Debug, Clone, Hash, Default)]
pub struct FnCallInfo { pub struct FnCallInfo {
/// Function name. /// Pre-calculated hash for a script-defined function of the same name and number of parameters.
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls pub hash: u64,
/// and the function names are predictable, so no need to allocate a new `String`.
pub name: Cow<'static, str>,
/// Namespace of the function, if any.
pub namespace: Option<ModuleRef>,
/// Call native functions only? Set to `true` to skip searching for script-defined function overrides /// Call native functions only? Set to `true` to skip searching for script-defined function overrides
/// when it is certain that the function must be native (e.g. an operator). /// when it is certain that the function must be native (e.g. an operator).
pub native_only: bool, pub native_only: bool,
/// Does this function call capture the parent scope? /// Does this function call capture the parent scope?
pub capture: bool, pub capture: bool,
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
pub hash: u64,
/// List of function call arguments.
pub args: StaticVec<Expr>,
/// Default value when the function is not found, mostly used to provide a default for comparison functions. /// Default value when the function is not found, mostly used to provide a default for comparison functions.
/// Type is `bool` in order for `FnCallInfo` to be `Hash` /// Type is `bool` in order for `FnCallInfo` to be `Hash`
pub def_value: Option<bool>, pub def_value: Option<bool>,
/// Namespace of the function, if any.
pub namespace: Option<Box<ModuleRef>>,
/// Function name.
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
/// and the function names are predictable, so no need to allocate a new `String`.
pub name: Cow<'static, str>,
/// List of function call arguments.
pub args: StaticVec<Expr>,
} }
/// _[INTERNALS]_ An expression sub-tree. /// _[INTERNALS]_ An expression sub-tree.
@ -903,10 +903,10 @@ pub enum Expr {
StringConstant(Box<IdentX>), StringConstant(Box<IdentX>),
/// FnPtr constant. /// FnPtr constant.
FnPointer(Box<IdentX>), FnPointer(Box<IdentX>),
/// Variable access - (variable name, optional modules, hash, optional index) /// Variable access - (optional index, optional modules, hash, variable name)
Variable(Box<(Ident, Option<ModuleRef>, u64, Option<NonZeroUsize>)>), Variable(Box<(Option<NonZeroUsize>, Option<Box<ModuleRef>>, u64, Ident)>),
/// Property access. /// Property access - (getter, setter), prop
Property(Box<(IdentX, (String, String))>), Property(Box<((String, String), IdentX)>),
/// { stmt } /// { stmt }
Stmt(Box<Stmt>, Position), Stmt(Box<Stmt>, Position),
/// Wrapped expression - should not be optimized away. /// Wrapped expression - should not be optimized away.
@ -1014,7 +1014,7 @@ impl Expr {
/// Is the expression a simple variable access? /// Is the expression a simple variable access?
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> { pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
match self { match self {
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.0).name.as_str()), Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.3).name.as_str()),
_ => None, _ => None,
} }
} }
@ -1033,9 +1033,9 @@ impl Expr {
Self::FnPointer(x) => x.pos, Self::FnPointer(x) => x.pos,
Self::Array(_, pos) => *pos, Self::Array(_, pos) => *pos,
Self::Map(_, pos) => *pos, Self::Map(_, pos) => *pos,
Self::Property(x) => (x.0).pos, Self::Property(x) => (x.1).pos,
Self::Stmt(_, pos) => *pos, Self::Stmt(_, pos) => *pos,
Self::Variable(x) => (x.0).pos, Self::Variable(x) => (x.3).pos,
Self::FnCall(_, pos) => *pos, Self::FnCall(_, pos) => *pos,
Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(), Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(),
@ -1064,8 +1064,8 @@ impl Expr {
Self::FnPointer(x) => x.pos = new_pos, Self::FnPointer(x) => x.pos = new_pos,
Self::Array(_, pos) => *pos = new_pos, Self::Array(_, pos) => *pos = new_pos,
Self::Map(_, pos) => *pos = new_pos, Self::Map(_, pos) => *pos = new_pos,
Self::Variable(x) => (x.0).pos = new_pos, Self::Variable(x) => (x.3).pos = new_pos,
Self::Property(x) => (x.0).pos = new_pos, Self::Property(x) => (x.1).pos = new_pos,
Self::Stmt(_, pos) => *pos = new_pos, Self::Stmt(_, pos) => *pos = new_pos,
Self::FnCall(_, pos) => *pos = new_pos, Self::FnCall(_, pos) => *pos = new_pos,
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos, Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
@ -1231,10 +1231,10 @@ impl Expr {
pub(crate) fn into_property(self) -> Self { pub(crate) fn into_property(self) -> Self {
match self { match self {
Self::Variable(x) if x.1.is_none() => { Self::Variable(x) if x.1.is_none() => {
let ident = x.0; let ident = x.3;
let getter = make_getter(&ident.name); let getter = make_getter(&ident.name);
let setter = make_setter(&ident.name); let setter = make_setter(&ident.name);
Self::Property(Box::new((ident.into(), (getter, setter)))) Self::Property(Box::new(((getter, setter), ident.into())))
} }
_ => self, _ => self,
} }

View File

@ -11,7 +11,7 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::syntax::CustomSyntax; use crate::syntax::CustomSyntax;
use crate::token::Position; use crate::token::{Position, NO_POS};
use crate::{calc_native_fn_hash, StaticVec}; use crate::{calc_native_fn_hash, StaticVec};
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -69,10 +69,58 @@ pub type Map = HashMap<ImmutableString, Dynamic>;
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
// //
// Note - We cannot use &str or Cow<str> here because `eval` may load a module // # Implementation Notes
// and the module name will live beyond the AST of the eval script text. //
// The best we can do is a shared reference. // We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
pub type Imports = Vec<(ImmutableString, Module)>; // the AST of the eval script text. The best we can do is a shared reference.
//
// `Imports` is implemented as two `Vec`'s of exactly the same length. That's because a `Module` is large,
// so packing the import names together improves cache locality.
#[derive(Debug, Clone, Default)]
pub struct Imports(StaticVec<Module>, StaticVec<ImmutableString>);
impl Imports {
/// Get the length of this stack of imported modules.
pub fn len(&self) -> usize {
self.0.len()
}
/// Get the imported module at a particular index.
pub fn get(&self, index: usize) -> Option<&Module> {
self.0.get(index)
}
/// Get a mutable reference to the imported module at a particular index.
pub fn get_mut(&mut self, index: usize) -> Option<&mut Module> {
self.0.get_mut(index)
}
/// Get the index of an imported module by name.
pub fn find(&self, name: &str) -> Option<usize> {
self.1
.iter()
.enumerate()
.rev()
.find(|(_, key)| key.as_str() == name)
.map(|(index, _)| index)
}
/// Push an imported module onto the stack.
pub fn push(&mut self, name: impl Into<ImmutableString>, module: Module) {
self.0.push(module);
self.1.push(name.into());
}
/// Truncate the stack of imported modules to a particular length.
pub fn truncate(&mut self, size: usize) {
self.0.truncate(size);
self.1.truncate(size);
}
/// Get an iterator to this stack of imported modules.
#[allow(dead_code)]
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
self.1.iter().map(|name| name.as_str()).zip(self.0.iter())
}
/// Get a consuming iterator to this stack of imported modules.
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Module)> {
self.1.into_iter().zip(self.0.into_iter())
}
}
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -303,7 +351,7 @@ impl<'a> Target<'a> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, ch) => { Self::StringChar(_, _, ch) => {
let char_value = ch.clone(); let char_value = ch.clone();
self.set_value((char_value, Position::none())).unwrap(); self.set_value((char_value, NO_POS)).unwrap();
} }
} }
} }
@ -528,7 +576,7 @@ pub struct Engine {
/// Callback closure for implementing the `debug` command. /// Callback closure for implementing the `debug` command.
pub(crate) debug: Callback<str, ()>, pub(crate) debug: Callback<str, ()>,
/// Callback closure for progress reporting. /// Callback closure for progress reporting.
pub(crate) progress: Option<Callback<u64, bool>>, pub(crate) progress: Option<Callback<u64, Option<Dynamic>>>,
/// Optimize the AST after compilation. /// Optimize the AST after compilation.
pub(crate) optimization_level: OptimizationLevel, pub(crate) optimization_level: OptimizationLevel,
@ -602,12 +650,10 @@ pub fn search_imports<'s>(
Ok(if index > 0 { Ok(if index > 0 {
let offset = mods.len() - index; let offset = mods.len() - index;
&mods.get(offset).unwrap().1 mods.get(offset).expect("invalid index in Imports")
} else { } else {
mods.iter() mods.find(root)
.rev() .map(|n| mods.get(n).expect("invalid index in Imports"))
.find(|(n, _)| n == root)
.map(|(_, m)| m)
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))? .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
}) })
} }
@ -630,12 +676,13 @@ pub fn search_imports_mut<'s>(
Ok(if index > 0 { Ok(if index > 0 {
let offset = mods.len() - index; let offset = mods.len() - index;
&mut mods.get_mut(offset).unwrap().1 mods.get_mut(offset).expect("invalid index in Imports")
} else { } else {
mods.iter_mut() if let Some(n) = mods.find(root) {
.rev() mods.get_mut(n)
.find(|(n, _)| n == root) } else {
.map(|(_, m)| m) None
}
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))? .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
}) })
} }
@ -765,7 +812,7 @@ impl Engine {
match expr { match expr {
Expr::Variable(v) => match v.as_ref() { Expr::Variable(v) => match v.as_ref() {
// Qualified variable // Qualified variable
(Ident { name, pos }, Some(modules), hash_var, _) => { (_, Some(modules), hash_var, Ident { name, pos }) => {
let module = search_imports_mut(mods, state, modules)?; let module = search_imports_mut(mods, state, modules)?;
let target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| { let target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| {
match *err { match *err {
@ -797,7 +844,7 @@ impl Engine {
this_ptr: &'s mut Option<&mut Dynamic>, this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr, expr: &'a Expr,
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> { ) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
let (Ident { name, pos }, _, _, index) = match expr { let (index, _, _, Ident { name, pos }) = match expr {
Expr::Variable(v) => v.as_ref(), Expr::Variable(v) => v.as_ref(),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -937,7 +984,7 @@ impl Engine {
let args = &mut [val, &mut idx_val2, &mut new_val.0]; let args = &mut [val, &mut idx_val2, &mut new_val.0];
self.exec_fn_call( self.exec_fn_call(
state, lib, FN_IDX_SET, 0, args, is_ref, true, false, None, &None, state, lib, FN_IDX_SET, 0, args, is_ref, true, false, None, None,
level, level,
) )
.map_err(|err| match *err { .map_err(|err| match *err {
@ -946,7 +993,7 @@ impl Engine {
{ {
EvalAltResult::ErrorIndexingType( EvalAltResult::ErrorIndexingType(
self.map_type_name(val_type_name).into(), self.map_type_name(val_type_name).into(),
Position::none(), NO_POS,
) )
} }
err => err, err => err,
@ -979,8 +1026,7 @@ impl Engine {
let def_value = def_value.map(Into::<Dynamic>::into); let def_value = def_value.map(Into::<Dynamic>::into);
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
self.make_method_call( self.make_method_call(
state, lib, name, *hash, target, args, &def_value, *native, false, state, lib, name, *hash, target, args, def_value, *native, false, level,
level,
) )
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
} }
@ -988,7 +1034,7 @@ impl Engine {
Expr::FnCall(_, _) => unreachable!(), Expr::FnCall(_, _) => unreachable!(),
// {xxx:map}.id = ??? // {xxx:map}.id = ???
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => { Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
let IdentX { name, pos } = &x.0; let IdentX { name, pos } = &x.1;
let index = name.clone().into(); let index = name.clone().into();
let mut val = self let mut val = self
.get_indexed_mut(state, lib, target, index, *pos, true, false, level)?; .get_indexed_mut(state, lib, target, index, *pos, true, false, level)?;
@ -998,7 +1044,7 @@ impl Engine {
} }
// {xxx:map}.id // {xxx:map}.id
Expr::Property(x) if target.is::<Map>() => { Expr::Property(x) if target.is::<Map>() => {
let IdentX { name, pos } = &x.0; let IdentX { name, pos } = &x.1;
let index = name.clone().into(); let index = name.clone().into();
let val = self.get_indexed_mut( let val = self.get_indexed_mut(
state, lib, target, index, *pos, false, false, level, state, lib, target, index, *pos, false, false, level,
@ -1008,11 +1054,11 @@ impl Engine {
} }
// xxx.id = ??? // xxx.id = ???
Expr::Property(x) if new_val.is_some() => { Expr::Property(x) if new_val.is_some() => {
let (IdentX { pos, .. }, (_, setter)) = x.as_ref(); let ((_, setter), IdentX { pos, .. }) = x.as_ref();
let mut new_val = new_val; let mut new_val = new_val;
let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0]; let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0];
self.exec_fn_call( self.exec_fn_call(
state, lib, setter, 0, &mut args, is_ref, true, false, None, &None, state, lib, setter, 0, &mut args, is_ref, true, false, None, None,
level, level,
) )
.map(|(v, _)| (v, true)) .map(|(v, _)| (v, true))
@ -1020,10 +1066,10 @@ impl Engine {
} }
// xxx.id // xxx.id
Expr::Property(x) => { Expr::Property(x) => {
let (IdentX { pos, .. }, (getter, _)) = x.as_ref(); let ((getter, _), IdentX { pos, .. }) = x.as_ref();
let mut args = [target.as_mut()]; let mut args = [target.as_mut()];
self.exec_fn_call( self.exec_fn_call(
state, lib, getter, 0, &mut args, is_ref, true, false, None, &None, state, lib, getter, 0, &mut args, is_ref, true, false, None, None,
level, level,
) )
.map(|(v, _)| (v, false)) .map(|(v, _)| (v, false))
@ -1033,7 +1079,7 @@ impl Engine {
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
let mut val = match &x.lhs { let mut val = match &x.lhs {
Expr::Property(p) => { Expr::Property(p) => {
let IdentX { name, pos } = &p.0; let IdentX { name, pos } = &p.1;
let index = name.clone().into(); let index = name.clone().into();
self.get_indexed_mut( self.get_indexed_mut(
state, lib, target, index, *pos, false, true, level, state, lib, target, index, *pos, false, true, level,
@ -1052,7 +1098,7 @@ impl Engine {
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
let (val, _) = self let (val, _) = self
.make_method_call( .make_method_call(
state, lib, name, *hash, target, args, &def_value, *native, state, lib, name, *hash, target, args, def_value, *native,
false, level, false, level,
) )
.map_err(|err| err.fill_position(*pos))?; .map_err(|err| err.fill_position(*pos))?;
@ -1075,13 +1121,13 @@ impl Engine {
match &x.lhs { match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr // xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => { Expr::Property(p) => {
let (IdentX { pos, .. }, (getter, setter)) = p.as_ref(); let ((getter, setter), IdentX { pos, .. }) = p.as_ref();
let arg_values = &mut [target.as_mut(), &mut Default::default()]; let arg_values = &mut [target.as_mut(), &mut Default::default()];
let args = &mut arg_values[..1]; let args = &mut arg_values[..1];
let (mut val, updated) = self let (mut val, updated) = self
.exec_fn_call( .exec_fn_call(
state, lib, getter, 0, args, is_ref, true, false, None, state, lib, getter, 0, args, is_ref, true, false, None,
&None, level, None, level,
) )
.map_err(|err| err.fill_position(*pos))?; .map_err(|err| err.fill_position(*pos))?;
@ -1107,7 +1153,7 @@ impl Engine {
arg_values[1] = val; arg_values[1] = val;
self.exec_fn_call( self.exec_fn_call(
state, lib, setter, 0, arg_values, is_ref, true, false, state, lib, setter, 0, arg_values, is_ref, true, false,
None, &None, level, None, None, level,
) )
.or_else( .or_else(
|err| match *err { |err| match *err {
@ -1135,7 +1181,7 @@ impl Engine {
let args = idx_val.as_fn_call_args(); let args = idx_val.as_fn_call_args();
let (mut val, _) = self let (mut val, _) = self
.make_method_call( .make_method_call(
state, lib, name, *hash, target, args, &def_value, *native, state, lib, name, *hash, target, args, def_value, *native,
false, level, false, level,
) )
.map_err(|err| err.fill_position(*pos))?; .map_err(|err| err.fill_position(*pos))?;
@ -1210,7 +1256,7 @@ impl Engine {
let Ident { let Ident {
name: var_name, name: var_name,
pos: var_pos, pos: var_pos,
} = &x.0; } = &x.3;
self.inc_operations(state) self.inc_operations(state)
.map_err(|err| err.fill_position(*var_pos))?; .map_err(|err| err.fill_position(*var_pos))?;
@ -1411,25 +1457,21 @@ impl Engine {
let mut idx = idx; let mut idx = idx;
let args = &mut [val, &mut idx]; let args = &mut [val, &mut idx];
self.exec_fn_call( self.exec_fn_call(
state, _lib, FN_IDX_GET, 0, args, is_ref, true, false, None, &None, _level, state, _lib, FN_IDX_GET, 0, args, is_ref, true, false, None, None, _level,
) )
.map(|(v, _)| v.into()) .map(|(v, _)| v.into())
.map_err(|err| match *err { .map_err(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
Box::new(EvalAltResult::ErrorIndexingType( Box::new(EvalAltResult::ErrorIndexingType(type_name.into(), NO_POS))
type_name.into(),
Position::none(),
))
} }
_ => err, _ => err,
}) })
} }
_ => EvalAltResult::ErrorIndexingType( _ => {
self.map_type_name(val.type_name()).into(), EvalAltResult::ErrorIndexingType(self.map_type_name(val.type_name()).into(), NO_POS)
Position::none(), .into()
) }
.into(),
} }
} }
@ -1461,13 +1503,14 @@ impl Engine {
for value in rhs_value.iter_mut() { for value in rhs_value.iter_mut() {
let args = &mut [&mut lhs_value.clone(), value]; let args = &mut [&mut lhs_value.clone(), value];
let def_value = def_value.clone();
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
let hash = let hash =
calc_native_fn_hash(empty(), OP_FUNC, args.iter().map(|a| a.type_id())); calc_native_fn_hash(empty(), OP_FUNC, args.iter().map(|a| a.type_id()));
if self if self
.call_native_fn(state, lib, OP_FUNC, hash, args, false, false, &def_value) .call_native_fn(state, lib, OP_FUNC, hash, args, false, false, def_value)
.map_err(|err| err.fill_position(rhs.position()))? .map_err(|err| err.fill_position(rhs.position()))?
.0 .0
.as_bool() .as_bool()
@ -1477,7 +1520,7 @@ impl Engine {
} }
} }
Ok(def_value.unwrap()) Ok(false.into())
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(rhs_value)) => match lhs_value { Dynamic(Union::Map(rhs_value)) => match lhs_value {
@ -1521,11 +1564,11 @@ impl Engine {
Expr::FnPointer(x) => { Expr::FnPointer(x) => {
Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into()) Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into())
} }
Expr::Variable(x) if (x.0).name == KEYWORD_THIS => { Expr::Variable(x) if (x.3).name == KEYWORD_THIS => {
if let Some(val) = this_ptr { if let Some(val) = this_ptr {
Ok(val.clone()) Ok(val.clone())
} else { } else {
EvalAltResult::ErrorUnboundThis((x.0).pos).into() EvalAltResult::ErrorUnboundThis((x.3).pos).into()
} }
} }
Expr::Variable(_) => { Expr::Variable(_) => {
@ -1580,7 +1623,7 @@ impl Engine {
} = x.as_ref(); } = x.as_ref();
let def_value = def_value.map(Into::<Dynamic>::into); let def_value = def_value.map(Into::<Dynamic>::into);
self.make_function_call( self.make_function_call(
scope, mods, state, lib, this_ptr, name, args, &def_value, *hash, *native, scope, mods, state, lib, this_ptr, name, args, def_value, *hash, *native,
false, *cap_scope, level, false, *cap_scope, level,
) )
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
@ -1596,8 +1639,9 @@ impl Engine {
def_value, def_value,
.. ..
} = x.as_ref(); } = x.as_ref();
let modules = namespace.as_ref().map(|v| v.as_ref());
self.make_qualified_function_call( self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, namespace, name, args, *def_value, *hash, scope, mods, state, lib, this_ptr, modules, name, args, *def_value, *hash,
level, level,
) )
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
@ -1769,7 +1813,7 @@ impl Engine {
// Run function // Run function
let (value, _) = self let (value, _) = self
.exec_fn_call( .exec_fn_call(
state, lib, op, 0, args, false, false, false, None, &None, state, lib, op, 0, args, false, false, false, None, None,
level, level,
) )
.map_err(|err| err.fill_position(*op_pos))?; .map_err(|err| err.fill_position(*op_pos))?;
@ -1807,7 +1851,7 @@ impl Engine {
let result = self let result = self
.exec_fn_call( .exec_fn_call(
state, lib, op, 0, args, false, false, false, None, &None, level, state, lib, op, 0, args, false, false, false, None, None, level,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
.map_err(|err| err.fill_position(*op_pos))?; .map_err(|err| err.fill_position(*op_pos))?;
@ -1989,7 +2033,7 @@ impl Engine {
let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err { let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err {
x.clone() x.clone()
} else { } else {
err.set_position(Position::none()); err.set_position(NO_POS);
err.to_string().into() err.to_string().into()
}; };
@ -2070,7 +2114,7 @@ impl Engine {
} else { } else {
unsafe_cast_var_name_to_lifetime(&var_def.name).into() unsafe_cast_var_name_to_lifetime(&var_def.name).into()
}; };
scope.push_dynamic_value(var_name, entry_type, val, false); scope.push_dynamic_value(var_name, entry_type, val);
Ok(Default::default()) Ok(Default::default())
} }
@ -2092,7 +2136,7 @@ impl Engine {
if let Some(name_def) = alias { if let Some(name_def) = alias {
module.index_all_sub_modules(); module.index_all_sub_modules();
mods.push((name_def.name.clone(), module)); mods.push(name_def.name.clone(), module);
} }
state.modules += 1; state.modules += 1;
@ -2251,34 +2295,17 @@ impl Engine {
let (_arr, _map, s) = calc_size(result.as_ref().unwrap()); let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
if s > self.max_string_size() { if s > self.max_string_size() {
return EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into();
"Length of string".to_string(),
self.max_string_size(),
s,
Position::none(),
)
.into();
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
if _arr > self.max_array_size() { if _arr > self.max_array_size() {
return EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), NO_POS).into();
"Size of array".to_string(),
self.max_array_size(),
_arr,
Position::none(),
)
.into();
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if _map > self.max_map_size() { if _map > self.max_map_size() {
return EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), NO_POS)
"Number of properties in object map".to_string(),
self.max_map_size(),
_map,
Position::none(),
)
.into(); .into();
} }
@ -2293,14 +2320,14 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
// Guard against too many operations // Guard against too many operations
if self.max_operations() > 0 && state.operations > self.max_operations() { if self.max_operations() > 0 && state.operations > self.max_operations() {
return EvalAltResult::ErrorTooManyOperations(Position::none()).into(); return EvalAltResult::ErrorTooManyOperations(NO_POS).into();
} }
// Report progress - only in steps // Report progress - only in steps
if let Some(progress) = &self.progress { if let Some(progress) = &self.progress {
if !progress(&state.operations) { if let Some(token) = progress(&state.operations) {
// Terminate script if progress returns false // Terminate script if progress returns a termination token
return EvalAltResult::ErrorTerminated(Position::none()).into(); return EvalAltResult::ErrorTerminated(token, NO_POS).into();
} }
} }

View File

@ -8,7 +8,7 @@ use crate::optimize::OptimizationLevel;
use crate::parse_error::ParseError; use crate::parse_error::ParseError;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::token::Position; use crate::token::{Position, NO_POS};
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::{ use crate::{
@ -1391,7 +1391,7 @@ impl Engine {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(), self.map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::none(), NO_POS,
) )
.into() .into()
}); });
@ -1527,7 +1527,7 @@ impl Engine {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(), self.map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::none(), NO_POS,
) )
.into() .into()
}); });
@ -1617,7 +1617,7 @@ impl Engine {
) -> 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)
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?; .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?;
let mut state = Default::default(); let mut state = Default::default();
let mut mods = Default::default(); let mut mods = Default::default();
@ -1732,12 +1732,12 @@ impl Engine {
/// ///
/// engine.on_progress(move |&ops| { /// engine.on_progress(move |&ops| {
/// if ops > 10000 { /// if ops > 10000 {
/// false /// Some("Over 10,000 operations!".into())
/// } else if ops % 800 == 0 { /// } else if ops % 800 == 0 {
/// *logger.write().unwrap() = ops; /// *logger.write().unwrap() = ops;
/// true /// None
/// } else { /// } else {
/// true /// None
/// } /// }
/// }); /// });
/// ///
@ -1752,7 +1752,7 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn on_progress( pub fn on_progress(
&mut self, &mut self,
callback: impl Fn(&u64) -> bool + SendSync + 'static, callback: impl Fn(&u64) -> Option<Dynamic> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
self.progress = Some(Box::new(callback)); self.progress = Some(Box::new(callback));
self self

View File

@ -14,7 +14,7 @@ use crate::parse_error::ParseErrorType;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::stdlib::ops::Deref; use crate::stdlib::ops::Deref;
use crate::token::Position; use crate::token::NO_POS;
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec, INT}; use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec, INT};
@ -36,10 +36,6 @@ use crate::engine::{Map, Target, FN_GET, FN_SET};
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
use crate::engine::KEYWORD_IS_SHARED; use crate::engine::KEYWORD_IS_SHARED;
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_function"))]
use crate::scope::Entry as ScopeEntry;
use crate::stdlib::{ use crate::stdlib::{
any::{type_name, TypeId}, any::{type_name, TypeId},
boxed::Box, boxed::Box,
@ -163,7 +159,7 @@ pub fn ensure_no_data_race(
{ {
return EvalAltResult::ErrorDataRace( return EvalAltResult::ErrorDataRace(
format!("argument #{} of function '{}'", n + 1 + skip, fn_name), format!("argument #{} of function '{}'", n + 1 + skip, fn_name),
Position::none(), NO_POS,
) )
.into(); .into();
} }
@ -190,7 +186,7 @@ impl Engine {
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
pub_only: bool, pub_only: bool,
def_val: &Option<Dynamic>, def_val: Option<Dynamic>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
self.inc_operations(state)?; self.inc_operations(state)?;
@ -227,7 +223,7 @@ impl Engine {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(), self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(), typ.into(),
Position::none(), NO_POS,
) )
})?) })?)
.into(), .into(),
@ -238,7 +234,7 @@ impl Engine {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<ImmutableString>()).into(), self.map_type_name(type_name::<ImmutableString>()).into(),
typ.into(), typ.into(),
Position::none(), NO_POS,
) )
})?) })?)
.into(), .into(),
@ -258,7 +254,7 @@ impl Engine {
// Return default value (if any) // Return default value (if any)
if let Some(val) = def_val { if let Some(val) = def_val {
return Ok((val.clone(), false)); return Ok((val, false));
} }
// Getter function not found? // Getter function not found?
@ -269,7 +265,7 @@ impl Engine {
prop, prop,
self.map_type_name(args[0].type_name()) self.map_type_name(args[0].type_name())
), ),
Position::none(), NO_POS,
) )
.into(); .into();
} }
@ -283,7 +279,7 @@ impl Engine {
self.map_type_name(args[0].type_name()), self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()), self.map_type_name(args[1].type_name()),
), ),
Position::none(), NO_POS,
) )
.into(); .into();
} }
@ -297,7 +293,7 @@ impl Engine {
self.map_type_name(args[0].type_name()), self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()), self.map_type_name(args[1].type_name()),
), ),
Position::none(), NO_POS,
) )
.into(); .into();
} }
@ -311,7 +307,7 @@ impl Engine {
self.map_type_name(args[0].type_name()), self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()), self.map_type_name(args[1].type_name()),
), ),
Position::none(), NO_POS,
) )
.into(); .into();
} }
@ -330,7 +326,7 @@ impl Engine {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
), ),
Position::none(), NO_POS,
) )
.into() .into()
} }
@ -361,9 +357,7 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if level > self.max_call_levels() { if level > self.max_call_levels() {
return Err(Box::new( return Err(Box::new(EvalAltResult::ErrorStackOverflow(NO_POS)));
EvalAltResult::ErrorStackOverflow(Position::none()),
));
} }
let orig_scope_level = state.scope_level; let orig_scope_level = state.scope_level;
@ -400,8 +394,8 @@ impl Engine {
// Evaluate the function at one higher level of call depth // Evaluate the function at one higher level of call depth
let stmt = &fn_def.body; let stmt = &fn_def.body;
let result = self let result =
.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1) self.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1)
.or_else(|err| match *err { .or_else(|err| match *err {
// Convert return statement to return value // Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x), EvalAltResult::Return(x, _) => Ok(x),
@ -409,18 +403,14 @@ impl Engine {
EvalAltResult::ErrorInFunctionCall( EvalAltResult::ErrorInFunctionCall(
format!("{} > {}", fn_def.name, name), format!("{} > {}", fn_def.name, name),
err, err,
Position::none(), NO_POS,
) )
.into() .into()
} }
// System errors are passed straight-through // System errors are passed straight-through
err if err.is_system_exception() => Err(Box::new(err)), err if err.is_system_exception() => Err(Box::new(err)),
// Other errors are wrapped in `ErrorInFunctionCall` // Other errors are wrapped in `ErrorInFunctionCall`
_ => EvalAltResult::ErrorInFunctionCall( _ => EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, NO_POS)
fn_def.name.to_string(),
err,
Position::none(),
)
.into(), .into(),
}); });
@ -489,7 +479,7 @@ impl Engine {
_is_method: bool, _is_method: bool,
pub_only: bool, pub_only: bool,
_capture_scope: Option<Scope>, _capture_scope: Option<Scope>,
def_val: &Option<Dynamic>, def_val: Option<Dynamic>,
_level: usize, _level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
// Check for data race. // Check for data race.
@ -523,7 +513,7 @@ impl Engine {
fn_name, fn_name fn_name, fn_name
) )
.into(), .into(),
Position::none(), NO_POS,
) )
.into() .into()
} }
@ -553,22 +543,14 @@ impl Engine {
if let Some(captured) = _capture_scope { if let Some(captured) = _capture_scope {
captured captured
.into_iter() .into_iter()
.filter(|ScopeEntry { name, .. }| { .filter(|(name, _, _, _)| func.externals.contains(name.as_ref()))
func.externals.contains(name.as_ref()) .for_each(|(name, typ, value, _)| {
})
.for_each(
|ScopeEntry {
name, typ, value, ..
}| {
// Consume the scope values. // Consume the scope values.
match typ { match typ {
ScopeEntryType::Normal => scope.push(name, value), ScopeEntryType::Normal => scope.push(name, value),
ScopeEntryType::Constant => { ScopeEntryType::Constant => scope.push_constant(name, value),
scope.push_constant(name, value)
}
}; };
}, });
);
} }
let result = if _is_method { let result = if _is_method {
@ -668,9 +650,7 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _level > self.max_call_levels() { if _level > self.max_call_levels() {
return Err(Box::new( return Err(Box::new(EvalAltResult::ErrorStackOverflow(NO_POS)));
EvalAltResult::ErrorStackOverflow(Position::none()),
));
} }
// Compile the script text // Compile the script text
@ -706,7 +686,7 @@ impl Engine {
hash_script: u64, hash_script: u64,
target: &mut Target, target: &mut Target,
mut call_args: StaticVec<Dynamic>, mut call_args: StaticVec<Dynamic>,
def_val: &Option<Dynamic>, def_val: Option<Dynamic>,
native: bool, native: bool,
pub_only: bool, pub_only: bool,
level: usize, level: usize,
@ -857,7 +837,7 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
name: &str, name: &str,
args_expr: impl AsRef<[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,
pub_only: bool, pub_only: bool,
@ -1094,7 +1074,7 @@ impl Engine {
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
modules: &Option<ModuleRef>, modules: Option<&ModuleRef>,
name: &str, name: &str,
args_expr: impl AsRef<[Expr]>, args_expr: impl AsRef<[Expr]>,
def_val: Option<bool>, def_val: Option<bool>,
@ -1230,7 +1210,7 @@ impl Engine {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
), ),
Position::none(), NO_POS,
) )
.into(), .into(),
} }

View File

@ -6,7 +6,7 @@ use crate::engine::{Engine, EvalContext};
use crate::module::Module; use crate::module::Module;
use crate::plugin::PluginFunction; use crate::plugin::PluginFunction;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::{is_valid_identifier, Position}; use crate::token::{is_valid_identifier, NO_POS};
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use crate::{calc_script_fn_hash, StaticVec}; use crate::{calc_script_fn_hash, StaticVec};
@ -160,7 +160,7 @@ impl FnPtr {
/// clone them _before_ calling this function. /// clone them _before_ calling this function.
pub fn call_dynamic( pub fn call_dynamic(
&self, &self,
context: NativeCallContext, ctx: NativeCallContext,
this_ptr: Option<&mut Dynamic>, this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>, mut arg_values: impl AsMut<[Dynamic]>,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -182,11 +182,10 @@ impl FnPtr {
args.insert(0, obj); args.insert(0, obj);
} }
context ctx.engine()
.engine()
.exec_fn_call( .exec_fn_call(
&mut Default::default(), &mut Default::default(),
context.lib, ctx.lib,
fn_name, fn_name,
hash_script, hash_script,
args.as_mut(), args.as_mut(),
@ -194,7 +193,7 @@ impl FnPtr {
has_this, has_this,
true, true,
None, None,
&None, None,
0, 0,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
@ -216,7 +215,7 @@ impl TryFrom<ImmutableString> for FnPtr {
if is_valid_identifier(value.chars()) { if is_valid_identifier(value.chars()) {
Ok(Self(value, Default::default())) Ok(Self(value, Default::default()))
} else { } else {
EvalAltResult::ErrorFunctionNotFound(value.into(), Position::none()).into() EvalAltResult::ErrorFunctionNotFound(value.into(), NO_POS).into()
} }
} }
} }

View File

@ -115,11 +115,11 @@ pub use engine::{Engine, EvalContext};
pub use fn_native::{FnPtr, NativeCallContext}; pub use fn_native::{FnPtr, NativeCallContext};
pub use fn_register::{RegisterFn, RegisterResultFn}; pub use fn_register::{RegisterFn, RegisterResultFn};
pub use module::Module; pub use module::Module;
pub use parse_error::{ParseError, ParseErrorType}; pub use parse_error::{LexError, ParseError, ParseErrorType};
pub use result::EvalAltResult; pub use result::EvalAltResult;
pub use scope::Scope; pub use scope::Scope;
pub use syntax::Expression; pub use syntax::Expression;
pub use token::Position; pub use token::{Position, NO_POS};
pub use utils::ImmutableString; pub use utils::ImmutableString;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]

View File

@ -5,7 +5,7 @@ use crate::dynamic::{Dynamic, Variant};
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync}; use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync};
use crate::fn_register::by_value as cast_arg; use crate::fn_register::by_value as cast_arg;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::{Position, Token}; use crate::token::{Token, NO_POS};
use crate::utils::{ImmutableString, StraightHasherBuilder}; use crate::utils::{ImmutableString, StraightHasherBuilder};
use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec}; use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec};
@ -13,11 +13,7 @@ use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec};
use crate::{ast::ScriptFnDef, fn_native::Shared}; use crate::{ast::ScriptFnDef, fn_native::Shared};
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
use crate::{ use crate::{ast::AST, engine::Engine, scope::Scope};
ast::AST,
engine::{Engine, Imports},
scope::{Entry as ScopeEntry, Scope},
};
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET}; use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
@ -275,11 +271,11 @@ impl Module {
hash_var: u64, hash_var: u64,
) -> Result<&mut Dynamic, Box<EvalAltResult>> { ) -> Result<&mut Dynamic, Box<EvalAltResult>> {
if hash_var == 0 { if hash_var == 0 {
Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into()) Err(EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into())
} else { } else {
self.all_variables.get_mut(&hash_var).ok_or_else(|| { self.all_variables
EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into() .get_mut(&hash_var)
}) .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into())
} }
} }
@ -533,9 +529,8 @@ impl Module {
+ SendSync + SendSync
+ 'static, + 'static,
) -> u64 { ) -> u64 {
let f = move |context: NativeCallContext, args: &mut FnCallArgs| { let f =
func(context, args).map(Dynamic::from) move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
};
self.set_fn( self.set_fn(
name, name,
FnAccess::Public, FnAccess::Public,
@ -1340,7 +1335,7 @@ impl Module {
ast: &AST, ast: &AST,
engine: &Engine, engine: &Engine,
) -> Result<Self, Box<EvalAltResult>> { ) -> Result<Self, Box<EvalAltResult>> {
let mut mods = Imports::new(); let mut mods = Default::default();
// Run the script // Run the script
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?; engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
@ -1348,12 +1343,10 @@ impl Module {
// Create new module // Create new module
let mut module = Module::new(); let mut module = Module::new();
scope scope.into_iter().for_each(|(_, _, value, alias)| {
.into_iter()
.for_each(|ScopeEntry { value, alias, .. }| {
// Variables with an alias left in the scope become module variables // Variables with an alias left in the scope become module variables
if let Some(alias) = alias { if let Some(alias) = alias {
module.variables.insert(*alias, value); module.variables.insert(alias, value);
} }
}); });

View File

@ -9,8 +9,8 @@ use crate::engine::{
use crate::fn_call::run_builtin_binary_op; use crate::fn_call::run_builtin_binary_op;
use crate::module::Module; use crate::module::Module;
use crate::parser::map_dynamic_to_expr; use crate::parser::map_dynamic_to_expr;
use crate::scope::{Entry as ScopeEntry, Scope}; use crate::scope::Scope;
use crate::token::{is_valid_identifier, Position}; use crate::token::{is_valid_identifier, NO_POS};
use crate::{calc_native_fn_hash, StaticVec}; use crate::{calc_native_fn_hash, StaticVec};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -147,7 +147,7 @@ fn call_fn_with_constant_arguments(
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(), arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
false, false,
true, true,
&None, None,
) )
.ok() .ok()
.map(|(v, _)| v) .map(|(v, _)| v)
@ -156,11 +156,16 @@ fn call_fn_with_constant_arguments(
/// Optimize a statement. /// Optimize a statement.
fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
match stmt { match stmt {
// id op= expr // expr op= expr
Stmt::Assignment(x, pos) => Stmt::Assignment( Stmt::Assignment(x, pos) => match x.0 {
Expr::Variable(_) => {
Stmt::Assignment(Box::new((x.0, x.1, optimize_expr(x.2, state))), pos)
}
_ => Stmt::Assignment(
Box::new((optimize_expr(x.0, state), x.1, optimize_expr(x.2, state))), Box::new((optimize_expr(x.0, state), x.1, optimize_expr(x.2, state))),
pos, pos,
), ),
},
// if false { if_block } -> Noop // if false { if_block } -> Noop
Stmt::IfThenElse(Expr::False(pos), x, _) if x.1.is_none() => { Stmt::IfThenElse(Expr::False(pos), x, _) if x.1.is_none() => {
state.set_dirty(); state.set_dirty();
@ -454,7 +459,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
Expr::Dot(x, dot_pos) => match (x.lhs, x.rhs) { Expr::Dot(x, dot_pos) => match (x.lhs, x.rhs) {
// map.string // map.string
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => { (Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
let prop = &p.0.name; let prop = &p.1.name;
// Map literal where everything is pure - promote the indexed item. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
@ -462,6 +467,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
.map(|(_, mut expr)| { expr.set_position(pos); expr }) .map(|(_, mut expr)| { expr.set_position(pos); expr })
.unwrap_or_else(|| Expr::Unit(pos)) .unwrap_or_else(|| Expr::Unit(pos))
} }
// var.rhs
(lhs @ Expr::Variable(_), rhs) => Expr::Dot(Box::new(BinaryExpr {
lhs,
rhs: optimize_expr(rhs, state),
}), dot_pos),
// lhs.rhs // lhs.rhs
(lhs, rhs) => Expr::Dot(Box::new(BinaryExpr { (lhs, rhs) => Expr::Dot(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state), lhs: optimize_expr(lhs, state),
@ -498,6 +508,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty(); state.set_dirty();
Expr::CharConstant(s.name.chars().nth(i as usize).unwrap(), s.pos) Expr::CharConstant(s.name.chars().nth(i as usize).unwrap(), s.pos)
} }
// var[rhs]
(lhs @ Expr::Variable(_), rhs) => Expr::Index(Box::new(BinaryExpr {
lhs,
rhs: optimize_expr(rhs, state),
}), idx_pos),
// lhs[rhs] // lhs[rhs]
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr { (lhs, rhs) => Expr::Index(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state), lhs: optimize_expr(lhs, state),
@ -686,12 +701,12 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
} }
// constant-name // constant-name
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.0.name) => { Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.3.name) => {
state.set_dirty(); state.set_dirty();
// Replace constant with value // Replace constant with value
let mut expr = state.find_constant(&x.0.name).unwrap().clone(); let mut expr = state.find_constant(&x.3.name).unwrap().clone();
expr.set_position(x.0.pos); expr.set_position(x.3.pos);
expr expr
} }
@ -721,24 +736,15 @@ fn optimize(
// Set up the state // Set up the state
let mut state = State::new(engine, lib, level); let mut state = State::new(engine, lib, level);
// Add constants from the scope into the state // Add constants from the scope that can be made into a literal into the state
scope scope
.to_iter() .iter()
// Get all the constants that can be made into a constant literal. .filter(|(_, typ, _)| *typ)
.filter(|ScopeEntry { typ, .. }| typ.is_constant()) .for_each(|(name, _, value)| {
.for_each( if let Some(val) = map_dynamic_to_expr(value, NO_POS) {
|ScopeEntry { state.push_constant(name, val);
name, expr, value, ..
}| {
if let Some(val) = expr
.as_ref()
.map(|expr| expr.as_ref().clone())
.or_else(|| map_dynamic_to_expr(value.clone(), Position::none()))
{
state.push_constant(name.as_ref(), val);
} }
}, });
);
let orig_constants_len = state.constants.len(); let orig_constants_len = state.constants.len();

View File

@ -4,7 +4,7 @@ use crate::def_package;
use crate::plugin::*; use crate::plugin::*;
use crate::INT; use crate::INT;
use crate::{result::EvalAltResult, token::Position}; use crate::{result::EvalAltResult, token::NO_POS};
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::FLOAT; use crate::FLOAT;
@ -17,7 +17,7 @@ use crate::stdlib::{format, string::String};
#[inline(always)] #[inline(always)]
pub fn make_err(msg: impl Into<String>) -> Box<EvalAltResult> { pub fn make_err(msg: impl Into<String>) -> Box<EvalAltResult> {
EvalAltResult::ErrorArithmetic(msg.into(), Position::none()).into() EvalAltResult::ErrorArithmetic(msg.into(), NO_POS).into()
} }
macro_rules! gen_arithmetic_functions { macro_rules! gen_arithmetic_functions {

View File

@ -7,7 +7,7 @@ use crate::engine::Array;
use crate::fn_native::{FnPtr, NativeCallContext}; use crate::fn_native::{FnPtr, NativeCallContext};
use crate::plugin::*; use crate::plugin::*;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::Position; use crate::token::NO_POS;
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use crate::INT; use crate::INT;
@ -41,12 +41,12 @@ macro_rules! gen_array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn pad(_context: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> { pub fn pad(_ctx: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if array will be over max size limit // Check if array will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _context.engine().max_array_size() > 0 && len > 0 && (len as usize) > _context.engine().max_array_size() { if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() {
return EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge(
"Size of array".to_string(), _context.engine().max_array_size(), len as usize, Position::none(), "Size of array".to_string(), NO_POS
).into(); ).into();
} }
@ -197,7 +197,7 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn map( pub fn map(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
mapper: FnPtr, mapper: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -206,12 +206,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate() { for (i, item) in list.iter().enumerate() {
array.push( array.push(
mapper mapper
.call_dynamic(context, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(mapper.fn_name()) => if fn_sig.starts_with(mapper.fn_name()) =>
{ {
mapper.call_dynamic(context, None, [item.clone(), (i as INT).into()]) mapper.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
} }
_ => Err(err), _ => Err(err),
}) })
@ -219,7 +219,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"map".to_string(), "map".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})?, })?,
); );
@ -229,7 +229,7 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn filter( pub fn filter(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -237,12 +237,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate() { for (i, item) in list.iter().enumerate() {
if filter if filter
.call_dynamic(context, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
} }
_ => Err(err), _ => Err(err),
}) })
@ -250,7 +250,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"filter".to_string(), "filter".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})? })?
.as_bool() .as_bool()
@ -264,18 +264,18 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn some( pub fn some(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
for (i, item) in list.iter().enumerate() { for (i, item) in list.iter().enumerate() {
if filter if filter
.call_dynamic(context, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
} }
_ => Err(err), _ => Err(err),
}) })
@ -283,7 +283,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"filter".to_string(), "filter".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})? })?
.as_bool() .as_bool()
@ -297,18 +297,18 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn all( pub fn all(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
for (i, item) in list.iter().enumerate() { for (i, item) in list.iter().enumerate() {
if !filter if !filter
.call_dynamic(context, None, [item.clone()]) .call_dynamic(ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
} }
_ => Err(err), _ => Err(err),
}) })
@ -316,7 +316,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"filter".to_string(), "filter".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})? })?
.as_bool() .as_bool()
@ -330,7 +330,7 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn reduce( pub fn reduce(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
reducer: FnPtr, reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -338,16 +338,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate() { for (i, item) in list.iter().enumerate() {
result = reducer result = reducer
.call_dynamic(context, None, [result.clone(), item.clone()]) .call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_dynamic( reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
context,
None,
[result, item.clone(), (i as INT).into()],
)
} }
_ => Err(err), _ => Err(err),
}) })
@ -355,7 +351,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"reduce".to_string(), "reduce".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})?; })?;
} }
@ -364,31 +360,27 @@ mod array_functions {
} }
#[rhai_fn(name = "reduce", return_raw)] #[rhai_fn(name = "reduce", return_raw)]
pub fn reduce_with_initial( pub fn reduce_with_initial(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
reducer: FnPtr, reducer: FnPtr,
initial: FnPtr, initial: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut result = initial.call_dynamic(context, None, []).map_err(|err| { let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"reduce".to_string(), "reduce".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})?; })?;
for (i, item) in list.iter().enumerate() { for (i, item) in list.iter().enumerate() {
result = reducer result = reducer
.call_dynamic(context, None, [result.clone(), item.clone()]) .call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_dynamic( reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
context,
None,
[result, item.clone(), (i as INT).into()],
)
} }
_ => Err(err), _ => Err(err),
}) })
@ -396,7 +388,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"reduce".to_string(), "reduce".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})?; })?;
} }
@ -405,7 +397,7 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn reduce_rev( pub fn reduce_rev(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
reducer: FnPtr, reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -413,16 +405,12 @@ mod array_functions {
for (i, item) in list.iter().enumerate().rev() { for (i, item) in list.iter().enumerate().rev() {
result = reducer result = reducer
.call_dynamic(context, None, [result.clone(), item.clone()]) .call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_dynamic( reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
context,
None,
[result, item.clone(), (i as INT).into()],
)
} }
_ => Err(err), _ => Err(err),
}) })
@ -430,7 +418,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"reduce".to_string(), "reduce".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})?; })?;
} }
@ -439,31 +427,27 @@ mod array_functions {
} }
#[rhai_fn(name = "reduce_rev", return_raw)] #[rhai_fn(name = "reduce_rev", return_raw)]
pub fn reduce_rev_with_initial( pub fn reduce_rev_with_initial(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
reducer: FnPtr, reducer: FnPtr,
initial: FnPtr, initial: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut result = initial.call_dynamic(context, None, []).map_err(|err| { let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"reduce".to_string(), "reduce".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})?; })?;
for (i, item) in list.iter().enumerate().rev() { for (i, item) in list.iter().enumerate().rev() {
result = reducer result = reducer
.call_dynamic(context, None, [result.clone(), item.clone()]) .call_dynamic(ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_dynamic( reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
context,
None,
[result, item.clone(), (i as INT).into()],
)
} }
_ => Err(err), _ => Err(err),
}) })
@ -471,7 +455,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"reduce".to_string(), "reduce".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})?; })?;
} }
@ -480,13 +464,13 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn sort( pub fn sort(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
comparer: FnPtr, comparer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
list.sort_by(|x, y| { list.sort_by(|x, y| {
comparer comparer
.call_dynamic(context, None, [x.clone(), y.clone()]) .call_dynamic(ctx, None, [x.clone(), y.clone()])
.ok() .ok()
.and_then(|v| v.as_int().ok()) .and_then(|v| v.as_int().ok())
.map(|v| { .map(|v| {
@ -516,7 +500,7 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn drain( pub fn drain(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -528,12 +512,12 @@ mod array_functions {
i -= 1; i -= 1;
if filter if filter
.call_dynamic(context, None, [list[i].clone()]) .call_dynamic(ctx, None, [list[i].clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()]) filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()])
} }
_ => Err(err), _ => Err(err),
}) })
@ -541,7 +525,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"filter".to_string(), "filter".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})? })?
.as_bool() .as_bool()
@ -575,7 +559,7 @@ mod array_functions {
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn retain( pub fn retain(
context: NativeCallContext, ctx: NativeCallContext,
list: &mut Array, list: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -587,12 +571,12 @@ mod array_functions {
i -= 1; i -= 1;
if !filter if !filter
.call_dynamic(context, None, [list[i].clone()]) .call_dynamic(ctx, None, [list[i].clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()]) filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()])
} }
_ => Err(err), _ => Err(err),
}) })
@ -600,7 +584,7 @@ mod array_functions {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(EvalAltResult::ErrorInFunctionCall(
"filter".to_string(), "filter".to_string(),
err, err,
Position::none(), NO_POS,
)) ))
})? })?
.as_bool() .as_bool()

View File

@ -2,7 +2,7 @@
use crate::def_package; use crate::def_package;
use crate::plugin::*; use crate::plugin::*;
use crate::token::Position; use crate::token::NO_POS;
use crate::INT; use crate::INT;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -85,10 +85,7 @@ mod int_functions {
#[rhai_fn(name = "parse_int", return_raw)] #[rhai_fn(name = "parse_int", return_raw)]
pub fn parse_int_radix(s: &str, radix: INT) -> Result<Dynamic, Box<EvalAltResult>> { pub fn parse_int_radix(s: &str, radix: INT) -> Result<Dynamic, Box<EvalAltResult>> {
if radix < 2 || radix > 36 { if radix < 2 || radix > 36 {
return EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(format!("Invalid radix: '{}'", radix), NO_POS)
format!("Invalid radix: '{}'", radix),
Position::none(),
)
.into(); .into();
} }
@ -97,7 +94,7 @@ mod int_functions {
.map_err(|err| { .map_err(|err| {
EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Error parsing integer number '{}': {}", s, err), format!("Error parsing integer number '{}': {}", s, err),
Position::none(), NO_POS,
) )
.into() .into()
}) })
@ -206,10 +203,7 @@ mod float_functions {
#[rhai_fn(name = "to_int", return_raw)] #[rhai_fn(name = "to_int", return_raw)]
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> { pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) { if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) {
EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS)
format!("Integer overflow: to_int({})", x),
Position::none(),
)
.into() .into()
} else { } else {
Ok((x.trunc() as INT).into()) Ok((x.trunc() as INT).into())
@ -218,10 +212,7 @@ mod float_functions {
#[rhai_fn(name = "to_int", return_raw)] #[rhai_fn(name = "to_int", return_raw)]
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> { pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) { if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS)
format!("Integer overflow: to_int({})", x),
Position::none(),
)
.into() .into()
} else { } else {
Ok((x.trunc() as INT).into()) Ok((x.trunc() as INT).into())
@ -235,7 +226,7 @@ mod float_functions {
.map_err(|err| { .map_err(|err| {
EvalAltResult::ErrorArithmetic( EvalAltResult::ErrorArithmetic(
format!("Error parsing floating-point number '{}': {}", s, err), format!("Error parsing floating-point number '{}': {}", s, err),
Position::none(), NO_POS,
) )
.into() .into()
}) })

View File

@ -9,7 +9,7 @@ use crate::StaticVec;
use crate::INT; use crate::INT;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
use crate::{result::EvalAltResult, token::Position}; use crate::{result::EvalAltResult, token::NO_POS};
use crate::stdlib::{ use crate::stdlib::{
any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec, any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec,
@ -251,23 +251,15 @@ mod string_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn pad( pub fn pad(
_context: NativeCallContext, _ctx: NativeCallContext,
s: &mut ImmutableString, s: &mut ImmutableString,
len: INT, len: INT,
ch: char, ch: char,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if string will be over max size limit // Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0 if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() {
&& len as usize > _context.engine().max_string_size() return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into();
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
len as usize,
Position::none(),
)
.into();
} }
if len > 0 { if len > 0 {
@ -281,14 +273,11 @@ mod string_functions {
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0 if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size()
&& s.len() > _context.engine().max_string_size()
{ {
return EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
_context.engine().max_string_size(), NO_POS,
s.len(),
Position::none(),
) )
.into(); .into();
} }
@ -299,23 +288,15 @@ mod string_functions {
} }
#[rhai_fn(name = "pad", return_raw)] #[rhai_fn(name = "pad", return_raw)]
pub fn pad_with_string( pub fn pad_with_string(
_context: NativeCallContext, _ctx: NativeCallContext,
s: &mut ImmutableString, s: &mut ImmutableString,
len: INT, len: INT,
padding: &str, padding: &str,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if string will be over max size limit // Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0 if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() {
&& len as usize > _context.engine().max_string_size() return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into();
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
len as usize,
Position::none(),
)
.into();
} }
if len > 0 { if len > 0 {
@ -336,14 +317,11 @@ mod string_functions {
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0 if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size()
&& s.len() > _context.engine().max_string_size()
{ {
return EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
_context.engine().max_string_size(), NO_POS,
s.len(),
Position::none(),
) )
.into(); .into();
} }

View File

@ -1,7 +1,7 @@
//! Module containing error definitions for the parsing process. //! Module containing error definitions for the parsing process.
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::Position; use crate::token::{Position, NO_POS};
use crate::stdlib::{ use crate::stdlib::{
boxed::Box, boxed::Box,
@ -43,26 +43,34 @@ impl fmt::Display for LexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s), Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s), Self::MalformedEscapeSequence(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s), Self::MalformedNumber(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s), Self::MalformedChar(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s), Self::MalformedIdentifier(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::UnterminatedString => write!(f, "Open string is not terminated"), Self::UnterminatedString => f.write_str(self.desc()),
Self::StringTooLong(max) => write!( Self::StringTooLong(max) => write!(f, "{} ({})", self.desc(), max),
f,
"Length of string literal exceeds the maximum limit ({})",
max
),
Self::ImproperSymbol(s) => f.write_str(s), Self::ImproperSymbol(s) => f.write_str(s),
} }
} }
} }
impl LexError { impl LexError {
/// Convert a `LexError` into a `ParseError`. pub(crate) fn desc(&self) -> &str {
match self {
Self::UnexpectedInput(_) => "Unexpected character encountered",
Self::UnterminatedString => "Open string is not terminated",
Self::StringTooLong(_) => "Length of string literal exceeds the maximum limit",
Self::MalformedEscapeSequence(_) => "Invalid escape sequence",
Self::MalformedNumber(_) => "Invalid number",
Self::MalformedChar(_) => "Invalid character",
Self::MalformedIdentifier(_) => "Variable name is not proper",
Self::ImproperSymbol(_) => "Invalid symbol encountered",
}
}
/// Convert a `&LexError` into a `ParseError`.
#[inline(always)] #[inline(always)]
pub fn into_err(&self, pos: Position) -> ParseError { pub fn into_err(&self, pos: Position) -> ParseError {
ParseError(Box::new(self.into()), pos) ParseError(Box::new(self.clone().into()), pos)
} }
} }
@ -74,10 +82,10 @@ impl LexError {
#[derive(Debug, Eq, PartialEq, Clone, Hash)] #[derive(Debug, Eq, PartialEq, Clone, Hash)]
#[non_exhaustive] #[non_exhaustive]
pub enum ParseErrorType { pub enum ParseErrorType {
/// Error in the script text. Wrapped value is the error message.
BadInput(String),
/// The script ends prematurely. /// The script ends prematurely.
UnexpectedEOF, UnexpectedEOF,
/// Error in the script text. Wrapped value is the lex error.
BadInput(LexError),
/// An unknown operator is encountered. Wrapped value is the operator. /// An unknown operator is encountered. Wrapped value is the operator.
UnknownOperator(String), UnknownOperator(String),
/// Expecting a particular token but not finding one. Wrapped values are the token and description. /// Expecting a particular token but not finding one. Wrapped values are the token and description.
@ -164,8 +172,8 @@ impl ParseErrorType {
pub(crate) fn desc(&self) -> &str { pub(crate) fn desc(&self) -> &str {
match self { match self {
Self::BadInput(p) => p,
Self::UnexpectedEOF => "Script is incomplete", Self::UnexpectedEOF => "Script is incomplete",
Self::BadInput(p) => p.desc(),
Self::UnknownOperator(_) => "Unknown operator", Self::UnknownOperator(_) => "Unknown operator",
Self::MissingToken(_, _) => "Expecting a certain token that is missing", Self::MissingToken(_, _) => "Expecting a certain token that is missing",
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments", Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
@ -196,9 +204,9 @@ impl ParseErrorType {
impl fmt::Display for ParseErrorType { impl fmt::Display for ParseErrorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => { Self::BadInput(err) => write!(f, "{}", err),
f.write_str(if s.is_empty() { self.desc() } else { s })
} Self::MalformedCallExpr(s) => f.write_str(if s.is_empty() { self.desc() } else { s }),
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s), Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => { Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => {
@ -247,14 +255,14 @@ impl fmt::Display for ParseErrorType {
} }
} }
impl From<&LexError> for ParseErrorType { impl From<LexError> for ParseErrorType {
#[inline(always)] #[inline(always)]
fn from(err: &LexError) -> Self { fn from(err: LexError) -> Self {
match err { match err {
LexError::StringTooLong(max) => { LexError::StringTooLong(max) => {
Self::LiteralTooLarge("Length of string literal".to_string(), *max) Self::LiteralTooLarge("Length of string literal".to_string(), max)
} }
_ => Self::BadInput(err.to_string()), _ => Self::BadInput(err),
} }
} }
} }
@ -282,7 +290,7 @@ impl fmt::Display for ParseError {
impl From<ParseErrorType> for Box<EvalAltResult> { impl From<ParseErrorType> for Box<EvalAltResult> {
#[inline(always)] #[inline(always)]
fn from(err: ParseErrorType) -> Self { fn from(err: ParseErrorType) -> Self {
Box::new(EvalAltResult::ErrorParsing(err, Position::none())) Box::new(EvalAltResult::ErrorParsing(err, NO_POS))
} }
} }

View File

@ -10,7 +10,9 @@ use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::parse_error::{LexError, ParseError, ParseErrorType}; use crate::parse_error::{LexError, ParseError, ParseErrorType};
use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::syntax::CustomSyntax; use crate::syntax::CustomSyntax;
use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream}; use crate::token::{
is_keyword_function, is_valid_identifier, Position, Token, TokenStream, NO_POS,
};
use crate::utils::StraightHasherBuilder; use crate::utils::StraightHasherBuilder;
use crate::{calc_script_fn_hash, StaticVec}; use crate::{calc_script_fn_hash, StaticVec};
@ -265,7 +267,7 @@ fn parse_fn_call(
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
id: String, id: String,
capture: bool, capture: bool,
mut namespace: Option<ModuleRef>, mut namespace: Option<Box<ModuleRef>>,
settings: ParseSettings, settings: ParseSettings,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap(); let (token, token_pos) = input.peek().unwrap();
@ -786,7 +788,7 @@ fn parse_primary(
{ {
state.allow_capture = true; state.allow_capture = true;
} }
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None))) Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos))))
} }
// Module qualification // Module qualification
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -796,18 +798,18 @@ fn parse_primary(
{ {
state.allow_capture = true; state.allow_capture = true;
} }
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None))) Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos))))
} }
// Normal variable access // Normal variable access
Token::Identifier(s) => { Token::Identifier(s) => {
let index = state.access_var(&s, settings.pos); let index = state.access_var(&s, settings.pos);
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, index))) Expr::Variable(Box::new((index, None, 0, Ident::new(s, settings.pos))))
} }
// Function call is allowed to have reserved keyword // Function call is allowed to have reserved keyword
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => { Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
if is_keyword_function(&s) { if is_keyword_function(&s) {
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None))) Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos))))
} else { } else {
return Err(PERR::Reserved(s).into_err(settings.pos)); return Err(PERR::Reserved(s).into_err(settings.pos));
} }
@ -816,12 +818,13 @@ fn parse_primary(
// Access to `this` as a variable is OK // Access to `this` as a variable is OK
Token::Reserved(s) if s == KEYWORD_THIS && *next_token != Token::LeftParen => { Token::Reserved(s) if s == KEYWORD_THIS && *next_token != Token::LeftParen => {
if !settings.is_function_scope { if !settings.is_function_scope {
return Err( return Err(PERR::BadInput(LexError::ImproperSymbol(format!(
PERR::BadInput(format!("'{}' can only be used in functions", s)) "'{}' can only be used in functions",
.into_err(settings.pos), s
); )))
.into_err(settings.pos));
} else { } else {
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None))) Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos))))
} }
} }
@ -840,7 +843,8 @@ fn parse_primary(
_ => { _ => {
return Err( return Err(
PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(settings.pos) PERR::BadInput(LexError::UnexpectedInput(token.syntax().to_string()))
.into_err(settings.pos),
); );
} }
}; };
@ -862,7 +866,9 @@ fn parse_primary(
return Err(if !match_token(input, Token::LeftParen).0 { return Err(if !match_token(input, Token::LeftParen).0 {
LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos) LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos)
} else { } else {
PERR::BadInput("'!' cannot be used to call module functions".to_string()) PERR::BadInput(LexError::ImproperSymbol(
"'!' cannot be used to call module functions".to_string(),
))
.into_err(token_pos) .into_err(token_pos)
}); });
} }
@ -877,13 +883,13 @@ fn parse_primary(
.into_err(pos)); .into_err(pos));
} }
let (Ident { name, pos }, modules, _, _) = *x; let (_, modules, _, Ident { name, pos }) = *x;
settings.pos = pos; settings.pos = pos;
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())? parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
} }
// Function call // Function call
(Expr::Variable(x), Token::LeftParen) => { (Expr::Variable(x), Token::LeftParen) => {
let (Ident { name, pos }, modules, _, _) = *x; let (_, modules, _, Ident { name, pos }) = *x;
settings.pos = pos; settings.pos = pos;
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())? parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
} }
@ -891,17 +897,17 @@ fn parse_primary(
// module access // module access
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() { (Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
(Token::Identifier(id2), pos2) => { (Token::Identifier(id2), pos2) => {
let (var_name_def, mut modules, _, index) = *x; let (index, mut modules, _, var_name_def) = *x;
if let Some(ref mut modules) = modules { if let Some(ref mut modules) = modules {
modules.push(var_name_def); modules.push(var_name_def);
} else { } else {
let mut m: ModuleRef = Default::default(); let mut m: ModuleRef = Default::default();
m.push(var_name_def); m.push(var_name_def);
modules = Some(m); modules = Some(Box::new(m));
} }
Expr::Variable(Box::new((Ident::new(id2, pos2), modules, 0, index))) Expr::Variable(Box::new((index, modules, 0, Ident::new(id2, pos2))))
} }
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => { (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
return Err(PERR::Reserved(id2).into_err(pos2)); return Err(PERR::Reserved(id2).into_err(pos2));
@ -925,7 +931,7 @@ fn parse_primary(
match &mut root_expr { match &mut root_expr {
// Cache the hash key for module-qualified variables // Cache the hash key for module-qualified variables
Expr::Variable(x) if x.1.is_some() => { Expr::Variable(x) if x.1.is_some() => {
let (Ident { name, .. }, modules, hash, _) = x.as_mut(); let (_, modules, hash, Ident { name, .. }) = x.as_mut();
let modules = modules.as_mut().unwrap(); let modules = modules.as_mut().unwrap();
// Qualifiers + variable name // Qualifiers + variable name
@ -1081,19 +1087,19 @@ fn make_assignment_stmt<'a>(
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
match &lhs { match &lhs {
// var (non-indexed) = rhs // var (non-indexed) = rhs
Expr::Variable(x) if x.3.is_none() => { Expr::Variable(x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos)) Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
} }
// var (indexed) = rhs // var (indexed) = rhs
Expr::Variable(x) => { Expr::Variable(x) => {
let ( let (
index,
_,
_,
Ident { Ident {
name, name,
pos: name_pos, pos: name_pos,
}, },
_,
_,
index,
) = x.as_ref(); ) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 { match state.stack[(state.stack.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => { ScopeEntryType::Normal => {
@ -1108,19 +1114,19 @@ fn make_assignment_stmt<'a>(
// xxx[???] = rhs, xxx.??? = rhs // xxx[???] = rhs, xxx.??? = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => match &x.lhs { Expr::Index(x, _) | Expr::Dot(x, _) => match &x.lhs {
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs // var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
Expr::Variable(x) if x.3.is_none() => { Expr::Variable(x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos)) Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
} }
// var[???] (indexed) = rhs, var.??? (indexed) = rhs // var[???] (indexed) = rhs, var.??? (indexed) = rhs
Expr::Variable(x) => { Expr::Variable(x) => {
let ( let (
index,
_,
_,
Ident { Ident {
name, name,
pos: name_pos, pos: name_pos,
}, },
_,
_,
index,
) = x.as_ref(); ) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 { match state.stack[(state.stack.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => { ScopeEntryType::Normal => {
@ -1140,9 +1146,10 @@ fn make_assignment_stmt<'a>(
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position())) Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
} }
// ??? && ??? = rhs, ??? || ??? = rhs // ??? && ??? = rhs, ??? || ??? = rhs
Expr::And(_, _) | Expr::Or(_, _) => { Expr::And(_, _) | Expr::Or(_, _) => Err(PERR::BadInput(LexError::ImproperSymbol(
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(pos)) "Possibly a typo of '=='?".to_string(),
} ))
.into_err(pos)),
// expr = rhs // expr = rhs
_ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(lhs.position())), _ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(lhs.position())),
} }
@ -1197,10 +1204,10 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
} }
// lhs.id // lhs.id
(lhs, Expr::Variable(x)) if x.1.is_none() => { (lhs, Expr::Variable(x)) if x.1.is_none() => {
let ident = x.0; let ident = x.3;
let getter = make_getter(&ident.name); let getter = make_getter(&ident.name);
let setter = make_setter(&ident.name); let setter = make_setter(&ident.name);
let rhs = Expr::Property(Box::new((ident.into(), (getter, setter)))); let rhs = Expr::Property(Box::new(((getter, setter), ident.into())));
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
} }
@ -1238,10 +1245,10 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
(_, Expr::FnCall(x, pos)) (_, Expr::FnCall(x, pos))
if x.args.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&x.name.as_ref()) => if x.args.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&x.name.as_ref()) =>
{ {
return Err(PERR::BadInput(format!( return Err(PERR::BadInput(LexError::ImproperSymbol(format!(
"'{}' should not be called in method style. Try {}(...);", "'{}' should not be called in method style. Try {}(...);",
x.name, x.name x.name, x.name
)) )))
.into_err(pos)); .into_err(pos));
} }
// lhs.func!(...) // lhs.func!(...)
@ -1635,10 +1642,10 @@ fn parse_custom_syntax(
(Token::Identifier(s), pos) => { (Token::Identifier(s), pos) => {
segments.push(s.clone()); segments.push(s.clone());
exprs.push(Expr::Variable(Box::new(( exprs.push(Expr::Variable(Box::new((
Ident::new(s, pos), None,
None, None,
0, 0,
None, Ident::new(s, pos),
)))); ))));
} }
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
@ -1734,9 +1741,10 @@ fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result
/// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`). /// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`).
fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> { fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
match input.peek().unwrap() { match input.peek().unwrap() {
(Token::Equals, pos) => { (Token::Equals, pos) => Err(PERR::BadInput(LexError::ImproperSymbol(
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos)) "Possibly a typo of '=='?".to_string(),
} ))
.into_err(*pos)),
(Token::PlusAssign, pos) (Token::PlusAssign, pos)
| (Token::MinusAssign, pos) | (Token::MinusAssign, pos)
| (Token::MultiplyAssign, pos) | (Token::MultiplyAssign, pos)
@ -1747,9 +1755,9 @@ fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
| (Token::PowerOfAssign, pos) | (Token::PowerOfAssign, pos)
| (Token::AndAssign, pos) | (Token::AndAssign, pos)
| (Token::OrAssign, pos) | (Token::OrAssign, pos)
| (Token::XOrAssign, pos) => Err(PERR::BadInput( | (Token::XOrAssign, pos) => Err(PERR::BadInput(LexError::ImproperSymbol(
"Expecting a boolean expression, not an assignment".to_string(), "Expecting a boolean expression, not an assignment".to_string(),
) ))
.into_err(*pos)), .into_err(*pos)),
_ => Ok(()), _ => Ok(()),
@ -2492,12 +2500,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
externals.iter().for_each(|x| { externals.iter().for_each(|x| {
args.push(Expr::Variable(Box::new((x.clone(), None, 0, None)))); args.push(Expr::Variable(Box::new((None, None, 0, x.clone()))));
}); });
#[cfg(feature = "no_closure")] #[cfg(feature = "no_closure")]
externals.into_iter().for_each(|x| { externals.into_iter().for_each(|x| {
args.push(Expr::Variable(Box::new((x.clone(), None, 0, None)))); args.push(Expr::Variable(Box::new((None, None, 0, x.clone()))));
}); });
let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1); let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1);
@ -2683,7 +2691,7 @@ impl Engine {
is_function_scope: false, is_function_scope: false,
is_breakable: false, is_breakable: false,
level: 0, level: 0,
pos: Position::none(), pos: NO_POS,
}; };
let expr = parse_expr(input, &mut state, &mut functions, settings)?; let expr = parse_expr(input, &mut state, &mut functions, settings)?;
@ -2694,7 +2702,8 @@ impl Engine {
// Return error if the expression doesn't end // Return error if the expression doesn't end
(token, pos) => { (token, pos) => {
return Err( return Err(
PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(*pos) PERR::BadInput(LexError::UnexpectedInput(token.syntax().to_string()))
.into_err(*pos),
) )
} }
} }
@ -2732,7 +2741,7 @@ impl Engine {
is_function_scope: false, is_function_scope: false,
is_breakable: false, is_breakable: false,
level: 0, level: 0,
pos: Position::none(), pos: NO_POS,
}; };
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? { let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {

View File

@ -2,7 +2,7 @@
use crate::dynamic::Dynamic; use crate::dynamic::Dynamic;
use crate::parse_error::ParseErrorType; use crate::parse_error::ParseErrorType;
use crate::token::Position; use crate::token::{Position, NO_POS};
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use crate::INT; use crate::INT;
@ -83,11 +83,11 @@ pub enum EvalAltResult {
ErrorTooManyModules(Position), ErrorTooManyModules(Position),
/// Call stack over maximum limit. /// Call stack over maximum limit.
ErrorStackOverflow(Position), ErrorStackOverflow(Position),
/// Data value over maximum size limit. Wrapped values are the type name, maximum size and current size. /// Data value over maximum size limit. Wrapped value is the type name.
ErrorDataTooLarge(String, usize, usize, Position), ErrorDataTooLarge(String, Position),
/// The script is prematurely terminated. /// The script is prematurely terminated. Wrapped value is the termination token.
ErrorTerminated(Position), ErrorTerminated(Dynamic, Position),
/// Run-time error encountered. Wrapped value is the error. /// Run-time error encountered. Wrapped value is the error token.
ErrorRuntime(Dynamic, Position), ErrorRuntime(Dynamic, Position),
/// Breaking out of loops - not an error if within a loop. /// Breaking out of loops - not an error if within a loop.
@ -127,7 +127,7 @@ impl EvalAltResult {
Self::ErrorVariableNotFound(_, _) => "Variable not found", Self::ErrorVariableNotFound(_, _) => "Variable not found",
Self::ErrorModuleNotFound(_, _) => "Module not found", Self::ErrorModuleNotFound(_, _) => "Module not found",
Self::ErrorDataRace(_, _) => "Data race detected when accessing variable", Self::ErrorDataRace(_, _) => "Data race detected when accessing variable",
Self::ErrorAssignmentToConstant(_, _) => "Assignment to a constant variable", Self::ErrorAssignmentToConstant(_, _) => "Cannot assign to a constant",
Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect", Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect",
Self::ErrorInExpr(_) => "Malformed 'in' expression", Self::ErrorInExpr(_) => "Malformed 'in' expression",
Self::ErrorDotExpr(_, _) => "Malformed dot expression", Self::ErrorDotExpr(_, _) => "Malformed dot expression",
@ -135,8 +135,8 @@ impl EvalAltResult {
Self::ErrorTooManyOperations(_) => "Too many operations", Self::ErrorTooManyOperations(_) => "Too many operations",
Self::ErrorTooManyModules(_) => "Too many modules imported", Self::ErrorTooManyModules(_) => "Too many modules imported",
Self::ErrorStackOverflow(_) => "Stack overflow", Self::ErrorStackOverflow(_) => "Stack overflow",
Self::ErrorDataTooLarge(_, _, _, _) => "Data size exceeds maximum limit", Self::ErrorDataTooLarge(_, _) => "Data size exceeds maximum limit",
Self::ErrorTerminated(_) => "Script terminated.", Self::ErrorTerminated(_,_) => "Script terminated.",
Self::ErrorRuntime(_, _) => "Runtime error", Self::ErrorRuntime(_, _) => "Runtime error",
Self::LoopBreak(true, _) => "Break statement not inside a loop", Self::LoopBreak(true, _) => "Break statement not inside a loop",
Self::LoopBreak(false, _) => "Continue statement not inside a loop", Self::LoopBreak(false, _) => "Continue statement not inside a loop",
@ -185,7 +185,7 @@ impl fmt::Display for EvalAltResult {
| Self::ErrorTooManyOperations(_) | Self::ErrorTooManyOperations(_)
| Self::ErrorTooManyModules(_) | Self::ErrorTooManyModules(_)
| Self::ErrorStackOverflow(_) | Self::ErrorStackOverflow(_)
| Self::ErrorTerminated(_) => f.write_str(desc)?, | Self::ErrorTerminated(_, _) => f.write_str(desc)?,
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => { Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
let s = d.as_str().unwrap(); let s = d.as_str().unwrap();
@ -194,7 +194,9 @@ impl fmt::Display for EvalAltResult {
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?, Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?, Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?,
Self::ErrorAssignmentToConstant(s, _) => write!(f, "{}: '{}'", desc, s)?, Self::ErrorAssignmentToConstant(s, _) => {
write!(f, "Cannot assign to constant '{}'", s)?
}
Self::ErrorMismatchOutputType(r, s, _) => { Self::ErrorMismatchOutputType(r, s, _) => {
write!(f, "Output type is incorrect: {} (expecting {})", r, s)? write!(f, "Output type is incorrect: {} (expecting {})", r, s)?
} }
@ -237,9 +239,7 @@ impl fmt::Display for EvalAltResult {
"String index {} is out of bounds: only {} characters in the string", "String index {} is out of bounds: only {} characters in the string",
index, max index, max
)?, )?,
Self::ErrorDataTooLarge(typ, max, size, _) => { Self::ErrorDataTooLarge(typ, _) => write!(f, "{} exceeds maximum limit", typ)?,
write!(f, "{} ({}) exceeds the maximum limit ({})", typ, size, max)?
}
} }
// Do not write any position if None // Do not write any position if None
@ -254,7 +254,7 @@ impl fmt::Display for EvalAltResult {
impl<T: AsRef<str>> From<T> for EvalAltResult { impl<T: AsRef<str>> From<T> for EvalAltResult {
#[inline(always)] #[inline(always)]
fn from(err: T) -> Self { fn from(err: T) -> Self {
Self::ErrorRuntime(err.as_ref().to_string().into(), Position::none()) Self::ErrorRuntime(err.as_ref().to_string().into(), NO_POS)
} }
} }
@ -263,7 +263,7 @@ impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
fn from(err: T) -> Self { fn from(err: T) -> Self {
Box::new(EvalAltResult::ErrorRuntime( Box::new(EvalAltResult::ErrorRuntime(
err.as_ref().to_string().into(), err.as_ref().to_string().into(),
Position::none(), NO_POS,
)) ))
} }
} }
@ -297,8 +297,8 @@ impl EvalAltResult {
Self::ErrorTooManyOperations(_) Self::ErrorTooManyOperations(_)
| Self::ErrorTooManyModules(_) | Self::ErrorTooManyModules(_)
| Self::ErrorStackOverflow(_) | Self::ErrorStackOverflow(_)
| Self::ErrorDataTooLarge(_, _, _, _) | Self::ErrorDataTooLarge(_, _)
| Self::ErrorTerminated(_) => false, | Self::ErrorTerminated(_, _) => false,
Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(), Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(),
} }
@ -313,9 +313,9 @@ impl EvalAltResult {
Self::ErrorTooManyOperations(_) Self::ErrorTooManyOperations(_)
| Self::ErrorTooManyModules(_) | Self::ErrorTooManyModules(_)
| Self::ErrorStackOverflow(_) | Self::ErrorStackOverflow(_)
| Self::ErrorDataTooLarge(_, _, _, _) => true, | Self::ErrorDataTooLarge(_, _) => true,
Self::ErrorTerminated(_) => true, Self::ErrorTerminated(_, _) => true,
Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(), Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(),
@ -326,7 +326,7 @@ impl EvalAltResult {
/// Get the `Position` of this error. /// Get the `Position` of this error.
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
Self::ErrorSystem(_, _) => Position::none(), Self::ErrorSystem(_, _) => NO_POS,
Self::ErrorParsing(_, pos) Self::ErrorParsing(_, pos)
| Self::ErrorFunctionNotFound(_, pos) | Self::ErrorFunctionNotFound(_, pos)
@ -349,8 +349,8 @@ impl EvalAltResult {
| Self::ErrorTooManyOperations(pos) | Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyModules(pos) | Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos) | Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(_, _, _, pos) | Self::ErrorDataTooLarge(_, pos)
| Self::ErrorTerminated(pos) | Self::ErrorTerminated(_, pos)
| Self::ErrorRuntime(_, pos) | Self::ErrorRuntime(_, pos)
| Self::LoopBreak(_, pos) | Self::LoopBreak(_, pos)
| Self::Return(_, pos) => *pos, | Self::Return(_, pos) => *pos,
@ -383,8 +383,8 @@ impl EvalAltResult {
| Self::ErrorTooManyOperations(pos) | Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyModules(pos) | Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos) | Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(_, _, _, pos) | Self::ErrorDataTooLarge(_, pos)
| Self::ErrorTerminated(pos) | Self::ErrorTerminated(_, pos)
| Self::ErrorRuntime(_, pos) | Self::ErrorRuntime(_, pos)
| Self::LoopBreak(_, pos) | Self::LoopBreak(_, pos)
| Self::Return(_, pos) => *pos = new_position, | Self::Return(_, pos) => *pos = new_position,

View File

@ -1,11 +1,8 @@
//! Module that defines the `Scope` type representing a function call-stack scope. //! Module that defines the `Scope` type representing a function call-stack scope.
use crate::ast::Expr;
use crate::dynamic::{Dynamic, Variant}; use crate::dynamic::{Dynamic, Variant};
use crate::parser::map_dynamic_to_expr;
use crate::token::Position;
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec}; use crate::stdlib::{borrow::Cow, iter, string::String, vec::Vec};
/// Type of an entry in the Scope. /// Type of an entry in the Scope.
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
@ -27,21 +24,6 @@ impl EntryType {
} }
} }
/// An entry in the Scope.
#[derive(Debug, Clone)]
pub struct Entry<'a> {
/// Name of the entry.
pub name: Cow<'a, str>,
/// Type of the entry.
pub typ: EntryType,
/// Current value of the entry.
pub value: Dynamic,
/// Alias of the entry.
pub alias: Option<Box<String>>,
/// A constant expression if the initial value matches one of the recognized types.
pub expr: Option<Box<Expr>>,
}
/// Type containing information about the current scope. /// Type containing information about the current scope.
/// Useful for keeping state between `Engine` evaluation runs. /// Useful for keeping state between `Engine` evaluation runs.
/// ///
@ -72,8 +54,25 @@ pub struct Entry<'a> {
/// ///
/// When searching for entries, newly-added entries are found before similarly-named but older entries, /// When searching for entries, newly-added entries are found before similarly-named but older entries,
/// allowing for automatic _shadowing_. /// allowing for automatic _shadowing_.
//
// # Implementation Notes
//
// `Scope` is implemented as three `Vec`'s of exactly the same length. Variables data (name, type, etc.)
// is manually split into three equal-length arrays. That's because variable names take up the most space,
// with `Cow<str>` being four words long, but in the vast majority of cases the name is NOT used to look up
// a variable's value. Variable lookup is usually via direct index, by-passing the name altogether.
//
// Since `Dynamic` is reasonably small, packing it tightly improves cache locality when variables are accessed.
// The variable type is packed separately into another array because it is even smaller.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Scope<'a>(Vec<Entry<'a>>); pub struct Scope<'a> {
/// Current value of the entry.
values: Vec<Dynamic>,
/// Type of the entry.
types: Vec<EntryType>,
/// (Name, alias) of the entry.
names: Vec<(Cow<'a, str>, Option<String>)>,
}
impl<'a> Scope<'a> { impl<'a> Scope<'a> {
/// Create a new Scope. /// Create a new Scope.
@ -114,7 +113,9 @@ impl<'a> Scope<'a> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn clear(&mut self) -> &mut Self { pub fn clear(&mut self) -> &mut Self {
self.0.clear(); self.names.clear();
self.types.clear();
self.values.clear();
self self
} }
@ -133,7 +134,7 @@ impl<'a> Scope<'a> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.values.len()
} }
/// Is the Scope empty? /// Is the Scope empty?
@ -151,7 +152,7 @@ impl<'a> Scope<'a> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.len() == 0 self.values.len() == 0
} }
/// Add (push) a new entry to the Scope. /// Add (push) a new entry to the Scope.
@ -172,7 +173,7 @@ impl<'a> Scope<'a> {
name: K, name: K,
value: T, value: T,
) -> &mut Self { ) -> &mut Self {
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false) self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
} }
/// Add (push) a new `Dynamic` entry to the Scope. /// Add (push) a new `Dynamic` entry to the Scope.
@ -189,7 +190,7 @@ impl<'a> Scope<'a> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self { pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self {
self.push_dynamic_value(name, EntryType::Normal, value, false) self.push_dynamic_value(name, EntryType::Normal, value)
} }
/// Add (push) a new constant to the Scope. /// Add (push) a new constant to the Scope.
@ -216,7 +217,7 @@ impl<'a> Scope<'a> {
name: K, name: K,
value: T, value: T,
) -> &mut Self { ) -> &mut Self {
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true) self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
} }
/// Add (push) a new constant with a `Dynamic` value to the Scope. /// Add (push) a new constant with a `Dynamic` value to the Scope.
@ -244,7 +245,7 @@ impl<'a> Scope<'a> {
name: K, name: K,
value: Dynamic, value: Dynamic,
) -> &mut Self { ) -> &mut Self {
self.push_dynamic_value(name, EntryType::Constant, value, true) self.push_dynamic_value(name, EntryType::Constant, value)
} }
/// Add (push) a new entry with a `Dynamic` value to the Scope. /// Add (push) a new entry with a `Dynamic` value to the Scope.
@ -254,22 +255,10 @@ impl<'a> Scope<'a> {
name: K, name: K,
entry_type: EntryType, entry_type: EntryType,
value: Dynamic, value: Dynamic,
map_expr: bool,
) -> &mut Self { ) -> &mut Self {
let expr = if map_expr { self.names.push((name.into(), None));
map_dynamic_to_expr(value.clone(), Position::none()).map(Box::new) self.types.push(entry_type);
} else { self.values.push(value.into());
None
};
self.0.push(Entry {
name: name.into(),
typ: entry_type,
alias: None,
value: value.into(),
expr,
});
self self
} }
@ -301,7 +290,9 @@ impl<'a> Scope<'a> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn rewind(&mut self, size: usize) -> &mut Self { pub fn rewind(&mut self, size: usize) -> &mut Self {
self.0.truncate(size); self.names.truncate(size);
self.types.truncate(size);
self.values.truncate(size);
self self
} }
@ -320,37 +311,28 @@ impl<'a> Scope<'a> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn contains(&self, name: &str) -> bool { pub fn contains(&self, name: &str) -> bool {
self.0 self.names
.iter() .iter()
.rev() // Always search a Scope in reverse order .rev() // Always search a Scope in reverse order
.any(|Entry { name: key, .. }| name == key) .any(|(key, _)| name == key.as_ref())
} }
/// Find an entry in the Scope, starting from the last. /// Find an entry in the Scope, starting from the last.
#[inline(always)] #[inline(always)]
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> { pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
self.0 self.names
.iter() .iter()
.enumerate() .enumerate()
.rev() // Always search a Scope in reverse order .rev() // Always search a Scope in reverse order
.find_map(|(index, Entry { name: key, typ, .. })| { .find_map(|(index, (key, _))| {
if name == key { if name == key.as_ref() {
Some((index, *typ)) Some((index, self.types[index]))
} else { } else {
None None
} }
}) })
} }
/// Get an entry in the Scope, starting from the last.
#[inline(always)]
pub(crate) fn get_entry(&self, name: &str) -> Option<&Entry> {
self.0
.iter()
.rev()
.find(|Entry { name: key, .. }| name == key)
}
/// Get the value of an entry in the Scope, starting from the last. /// Get the value of an entry in the Scope, starting from the last.
/// ///
/// # Example /// # Example
@ -365,8 +347,12 @@ impl<'a> Scope<'a> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> { pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.get_entry(name) self.names
.and_then(|Entry { value, .. }| value.flatten_clone().try_cast()) .iter()
.enumerate()
.rev()
.find(|(_, (key, _))| name == key.as_ref())
.and_then(|(index, _)| self.values[index].flatten_clone().try_cast())
} }
/// Update the value of the named entry. /// Update the value of the named entry.
@ -398,7 +384,7 @@ impl<'a> Scope<'a> {
} }
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name), Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
Some((index, EntryType::Normal)) => { Some((index, EntryType::Normal)) => {
self.0.get_mut(index).unwrap().value = Dynamic::from(value); *self.values.get_mut(index).unwrap() = Dynamic::from(value);
} }
} }
self self
@ -407,16 +393,18 @@ impl<'a> Scope<'a> {
/// Get a mutable reference to an entry in the Scope. /// Get a mutable reference to an entry in the Scope.
#[inline(always)] #[inline(always)]
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) { pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
let entry = self.0.get_mut(index).expect("invalid index in Scope"); (
(&mut entry.value, entry.typ) self.values.get_mut(index).expect("invalid index in Scope"),
self.types[index],
)
} }
/// Update the access type of an entry in the Scope. /// Update the access type of an entry in the Scope.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self { pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
let entry = self.0.get_mut(index).expect("invalid index in Scope"); let entry = self.names.get_mut(index).expect("invalid index in Scope");
entry.alias = Some(Box::new(alias)); entry.1 = Some(alias);
self self
} }
@ -424,34 +412,36 @@ impl<'a> Scope<'a> {
/// Shadowed variables are omitted in the copy. /// Shadowed variables are omitted in the copy.
#[inline] #[inline]
pub(crate) fn clone_visible(&self) -> Self { pub(crate) fn clone_visible(&self) -> Self {
let mut entries: Vec<Entry> = Default::default(); let mut entries: Self = Default::default();
self.0.iter().rev().for_each(|entry| { self.names
if entries
.iter() .iter()
.find(|Entry { name, .. }| &entry.name == name) .enumerate()
.is_none() .rev()
{ .for_each(|(index, (name, alias))| {
entries.push(entry.clone()); if !entries.names.iter().any(|(key, _)| key == name) {
entries.names.push((name.clone(), alias.clone()));
entries.types.push(self.types[index]);
entries.values.push(self.values[index].clone());
} }
}); });
Self(entries) entries
} }
/// Get an iterator to entries in the Scope. /// Get an iterator to entries in the Scope.
#[inline(always)] #[inline(always)]
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> { pub(crate) fn into_iter(
self.0.into_iter() self,
} ) -> impl Iterator<Item = (Cow<'a, str>, EntryType, Dynamic, Option<String>)> {
self.names
/// Get an iterator to entries in the Scope in reverse order. .into_iter()
#[inline(always)] .zip(self.types.into_iter().zip(self.values.into_iter()))
pub(crate) fn to_iter(&self) -> impl Iterator<Item = &Entry> { .map(|((name, alias), (typ, value))| (name, typ, value, alias))
self.0.iter().rev() // Always search a Scope in reverse order
} }
/// Get an iterator to entries in the Scope. /// Get an iterator to entries in the Scope.
/// Shared values are flatten-cloned.
/// ///
/// # Example /// # Example
/// ///
@ -484,25 +474,21 @@ impl<'a> Scope<'a> {
/// Get an iterator to entries in the Scope. /// Get an iterator to entries in the Scope.
/// Shared values are not expanded. /// Shared values are not expanded.
#[inline(always)] #[inline(always)]
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> { pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator<Item = (&'a str, bool, &'x Dynamic)> + 'x {
self.0.iter().map( self.names
|Entry { .iter()
name, typ, value, .. .zip(self.types.iter().zip(self.values.iter()))
}| { (name.as_ref(), typ.is_constant(), value) }, .map(|((name, _), (typ, value))| (name.as_ref(), typ.is_constant(), value))
)
} }
} }
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> { impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
#[inline(always)] #[inline(always)]
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
self.0 iter.into_iter().for_each(|(name, typ, value)| {
.extend(iter.into_iter().map(|(name, typ, value)| Entry { self.names.push((name.into(), None));
name: name.into(), self.types.push(typ);
typ, self.values.push(value);
alias: None, });
value: value.into(),
expr: None,
}));
} }
} }

View File

@ -2,9 +2,9 @@
use super::str::ImmutableStringDeserializer; use super::str::ImmutableStringDeserializer;
use crate::dynamic::{Dynamic, Union}; use crate::dynamic::{Dynamic, Union};
use crate::parse_error::ParseErrorType; use crate::parse_error::{LexError, ParseErrorType};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::Position; use crate::token::NO_POS;
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use serde::de::{ use serde::de::{
@ -45,11 +45,7 @@ impl<'de> DynamicDeserializer<'de> {
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> { fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(error.into(), self.value.type_name().into(), NO_POS)
error.into(),
self.value.type_name().into(),
Position::none(),
)
.into() .into()
} }
fn deserialize_int<V: Visitor<'de>>( fn deserialize_int<V: Visitor<'de>>(
@ -127,7 +123,10 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>(
impl Error for Box<EvalAltResult> { impl Error for Box<EvalAltResult> {
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none()) EvalAltResult::ErrorParsing(
ParseErrorType::BadInput(LexError::ImproperSymbol(err.to_string())),
NO_POS,
)
.into() .into()
} }
} }

View File

@ -2,7 +2,7 @@
use crate::dynamic::Dynamic; use crate::dynamic::Dynamic;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::Position; use crate::token::NO_POS;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::engine::Array; use crate::engine::Array;
@ -99,7 +99,7 @@ pub fn to_dynamic<T: Serialize>(value: T) -> Result<Dynamic, Box<EvalAltResult>>
impl Error for Box<EvalAltResult> { impl Error for Box<EvalAltResult> {
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
EvalAltResult::ErrorRuntime(err.to_string().into(), Position::none()).into() EvalAltResult::ErrorRuntime(err.to_string().into(), NO_POS).into()
} }
} }
@ -295,11 +295,7 @@ impl Serializer for &mut DynamicSerializer {
make_variant(_variant, content) make_variant(_variant, content)
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
"Dynamic".into(),
"map".into(),
Position::none(),
)
.into(); .into();
} }
@ -307,11 +303,7 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(DynamicSerializer::new(Array::new().into())); return Ok(DynamicSerializer::new(Array::new().into()));
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "array".into(), NO_POS)
"Dynamic".into(),
"array".into(),
Position::none(),
)
.into(); .into();
} }
@ -345,12 +337,7 @@ impl Serializer for &mut DynamicSerializer {
let err_type = "map"; let err_type = "map";
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
let err_type = "array"; let err_type = "array";
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), err_type.into(), NO_POS).into()
"Dynamic".into(),
err_type.into(),
Position::none(),
)
.into()
} }
} }
@ -358,11 +345,7 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(DynamicSerializer::new(Map::new().into())); return Ok(DynamicSerializer::new(Map::new().into()));
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
"Dynamic".into(),
"map".into(),
Position::none(),
)
.into(); .into();
} }
@ -387,11 +370,7 @@ impl Serializer for &mut DynamicSerializer {
map: Map::with_capacity(_len), map: Map::with_capacity(_len),
}); });
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return EvalAltResult::ErrorMismatchOutputType( return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
"Dynamic".into(),
"map".into(),
Position::none(),
)
.into(); .into();
} }
} }
@ -501,11 +480,7 @@ impl SerializeMap for DynamicSerializer {
let key = mem::take(&mut self._key) let key = mem::take(&mut self._key)
.take_immutable_string() .take_immutable_string()
.map_err(|typ| { .map_err(|typ| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS)
"string".into(),
typ.into(),
Position::none(),
)
})?; })?;
let _value = _value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let map = self._value.downcast_mut::<Map>().unwrap(); let map = self._value.downcast_mut::<Map>().unwrap();
@ -525,11 +500,7 @@ impl SerializeMap for DynamicSerializer {
{ {
let _key: Dynamic = _key.serialize(&mut *self)?; let _key: Dynamic = _key.serialize(&mut *self)?;
let _key = _key.take_immutable_string().map_err(|typ| { let _key = _key.take_immutable_string().map_err(|typ| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS)
"string".into(),
typ.into(),
Position::none(),
)
})?; })?;
let _value = _value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let map = self._value.downcast_mut::<Map>().unwrap(); let map = self._value.downcast_mut::<Map>().unwrap();

View File

@ -1,7 +1,7 @@
//! Implement deserialization support of `ImmutableString` for [`serde`](https://crates.io/crates/serde). //! Implement deserialization support of `ImmutableString` for [`serde`](https://crates.io/crates/serde).
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::Position; use crate::token::NO_POS;
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use serde::de::{Deserializer, Visitor}; use serde::de::{Deserializer, Visitor};
@ -20,11 +20,7 @@ impl<'a> ImmutableStringDeserializer<'a> {
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> { fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(type_name::<T>().into(), "string".into(), NO_POS)
type_name::<T>().into(),
"string".into(),
Position::none(),
)
.into() .into()
} }
} }

View File

@ -6,7 +6,7 @@ use crate::engine::{Engine, EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT
use crate::fn_native::{SendSync, Shared}; use crate::fn_native::{SendSync, Shared};
use crate::parse_error::{LexError, ParseError}; use crate::parse_error::{LexError, ParseError};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::{is_valid_identifier, Position, Token}; use crate::token::{is_valid_identifier, Position, Token, NO_POS};
use crate::utils::ImmutableString; use crate::utils::ImmutableString;
use crate::StaticVec; use crate::StaticVec;
@ -139,7 +139,7 @@ impl Engine {
segments.len() + 1, segments.len() + 1,
s s
)) ))
.into_err(Position::none()) .into_err(NO_POS)
.into()); .into());
} }
// Identifier in first position // Identifier in first position
@ -156,7 +156,7 @@ impl Engine {
segments.len() + 1, segments.len() + 1,
s s
)) ))
.into_err(Position::none()) .into_err(NO_POS)
.into()); .into());
} }
}; };

View File

@ -44,7 +44,15 @@ pub struct Position {
pos: u16, pos: u16,
} }
/// No `Position`.
pub const NO_POS: Position = Position::NONE;
impl Position { impl Position {
/// A `Position` representing no position.
pub const NONE: Self = Self { line: 0, pos: 0 };
/// A `Position` representing the first position.
pub const START: Self = Self { line: 1, pos: 0 };
/// Create a new `Position`. /// Create a new `Position`.
/// ///
/// `line` must not be zero. /// `line` must not be zero.
@ -62,27 +70,24 @@ impl Position {
pos: position, pos: position,
} }
} }
/// Get the line number (1-based), or `None` if there is no position. /// Get the line number (1-based), or `None` if there is no position.
#[inline(always)] #[inline(always)]
pub fn line(&self) -> Option<usize> { pub fn line(self) -> Option<usize> {
if self.is_none() { if self.is_none() {
None None
} else { } else {
Some(self.line as usize) Some(self.line as usize)
} }
} }
/// Get the character position (1-based), or `None` if at beginning of a line. /// Get the character position (1-based), or `None` if at beginning of a line.
#[inline(always)] #[inline(always)]
pub fn position(&self) -> Option<usize> { pub fn position(self) -> Option<usize> {
if self.is_none() || self.pos == 0 { if self.is_none() || self.pos == 0 {
None None
} else { } else {
Some(self.pos as usize) Some(self.pos as usize)
} }
} }
/// Advance by one character position. /// Advance by one character position.
#[inline(always)] #[inline(always)]
pub(crate) fn advance(&mut self) { pub(crate) fn advance(&mut self) {
@ -93,7 +98,6 @@ impl Position {
self.pos += 1; self.pos += 1;
} }
} }
/// Go backwards by one character position. /// Go backwards by one character position.
/// ///
/// # Panics /// # Panics
@ -105,7 +109,6 @@ impl Position {
assert!(self.pos > 0, "cannot rewind at position 0"); assert!(self.pos > 0, "cannot rewind at position 0");
self.pos -= 1; self.pos -= 1;
} }
/// Advance to the next line. /// Advance to the next line.
#[inline(always)] #[inline(always)]
pub(crate) fn new_line(&mut self) { pub(crate) fn new_line(&mut self) {
@ -117,24 +120,22 @@ impl Position {
self.pos = 0; self.pos = 0;
} }
} }
/// Is this `Position` at the beginning of a line?
/// Create a `Position` representing no position.
#[inline(always)] #[inline(always)]
pub fn none() -> Self { pub fn is_beginning_of_line(self) -> bool {
Self { line: 0, pos: 0 } self.line == 0 && !self.is_none()
} }
/// Is there no `Position`? /// Is there no `Position`?
#[inline(always)] #[inline(always)]
pub fn is_none(&self) -> bool { pub fn is_none(self) -> bool {
self.line == 0 && self.pos == 0 self == Self::NONE
} }
} }
impl Default for Position { impl Default for Position {
#[inline(always)] #[inline(always)]
fn default() -> Self { fn default() -> Self {
Self::new(1, 0) Self::START
} }
} }
@ -327,7 +328,7 @@ pub enum Token {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
As, As,
/// A lexer error. /// A lexer error.
LexError(Box<LexError>), LexError(LexError),
/// A comment block. /// A comment block.
Comment(String), Comment(String),
/// A reserved symbol. /// A reserved symbol.
@ -1117,9 +1118,7 @@ fn get_next_token_inner(
INT::from_str_radix(&out, radix) INT::from_str_radix(&out, radix)
.map(Token::IntegerConstant) .map(Token::IntegerConstant)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
Token::LexError(Box::new(LERR::MalformedNumber( Token::LexError(LERR::MalformedNumber(result.into_iter().collect()))
result.into_iter().collect(),
)))
}), }),
start_pos, start_pos,
)); ));
@ -1133,9 +1132,7 @@ fn get_next_token_inner(
return Some(( return Some((
num.unwrap_or_else(|_| { num.unwrap_or_else(|_| {
Token::LexError(Box::new(LERR::MalformedNumber( Token::LexError(LERR::MalformedNumber(result.into_iter().collect()))
result.into_iter().collect(),
)))
}), }),
start_pos, start_pos,
)); ));
@ -1150,7 +1147,7 @@ fn get_next_token_inner(
// " - string literal // " - string literal
('"', _) => { ('"', _) => {
return parse_string_literal(stream, state, pos, '"').map_or_else( return parse_string_literal(stream, state, pos, '"').map_or_else(
|err| Some((Token::LexError(Box::new(err.0)), err.1)), |err| Some((Token::LexError(err.0), err.1)),
|out| Some((Token::StringConstant(out), start_pos)), |out| Some((Token::StringConstant(out), start_pos)),
) )
} }
@ -1158,22 +1155,19 @@ fn get_next_token_inner(
// ' - character literal // ' - character literal
('\'', '\'') => { ('\'', '\'') => {
return Some(( return Some((
Token::LexError(Box::new(LERR::MalformedChar("".to_string()))), Token::LexError(LERR::MalformedChar("".to_string())),
start_pos, start_pos,
)) ))
} }
('\'', _) => { ('\'', _) => {
return Some(parse_string_literal(stream, state, pos, '\'').map_or_else( return Some(parse_string_literal(stream, state, pos, '\'').map_or_else(
|err| (Token::LexError(Box::new(err.0)), err.1), |err| (Token::LexError(err.0), err.1),
|result| { |result| {
let mut chars = result.chars(); let mut chars = result.chars();
let first = chars.next().unwrap(); let first = chars.next().unwrap();
if chars.next().is_some() { if chars.next().is_some() {
( (Token::LexError(LERR::MalformedChar(result)), start_pos)
Token::LexError(Box::new(LERR::MalformedChar(result))),
start_pos,
)
} else { } else {
(Token::CharConstant(first), start_pos) (Token::CharConstant(first), start_pos)
} }
@ -1449,7 +1443,7 @@ fn get_next_token_inner(
} }
(ch, _) => { (ch, _) => {
return Some(( return Some((
Token::LexError(Box::new(LERR::UnexpectedInput(ch.to_string()))), Token::LexError(LERR::UnexpectedInput(ch.to_string())),
start_pos, start_pos,
)) ))
} }
@ -1491,7 +1485,7 @@ fn get_identifier(
if !is_valid_identifier { if !is_valid_identifier {
return Some(( return Some((
Token::LexError(Box::new(LERR::MalformedIdentifier(identifier))), Token::LexError(LERR::MalformedIdentifier(identifier)),
start_pos, start_pos,
)); ));
} }
@ -1649,42 +1643,42 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
Some((Token::Reserved(s), pos)) => Some((match Some((Token::Reserved(s), pos)) => Some((match
(s.as_str(), self.engine.custom_keywords.contains_key(&s)) (s.as_str(), self.engine.custom_keywords.contains_key(&s))
{ {
("===", false) => Token::LexError(Box::new(LERR::ImproperSymbol( ("===", false) => Token::LexError(LERR::ImproperSymbol(
"'===' is not a valid operator. This is not JavaScript! Should it be '=='?".to_string(), "'===' is not a valid operator. This is not JavaScript! Should it be '=='?".to_string(),
))), )),
("!==", false) => Token::LexError(Box::new(LERR::ImproperSymbol( ("!==", false) => Token::LexError(LERR::ImproperSymbol(
"'!==' is not a valid operator. This is not JavaScript! Should it be '!='?".to_string(), "'!==' is not a valid operator. This is not JavaScript! Should it be '!='?".to_string(),
))), )),
("->", false) => Token::LexError(Box::new(LERR::ImproperSymbol( ("->", false) => Token::LexError(LERR::ImproperSymbol(
"'->' is not a valid symbol. This is not C or C++!".to_string()))), "'->' is not a valid symbol. This is not C or C++!".to_string())),
("<-", false) => Token::LexError(Box::new(LERR::ImproperSymbol( ("<-", false) => Token::LexError(LERR::ImproperSymbol(
"'<-' is not a valid symbol. This is not Go! Should it be '<='?".to_string(), "'<-' is not a valid symbol. This is not Go! Should it be '<='?".to_string(),
))), )),
("=>", false) => Token::LexError(Box::new(LERR::ImproperSymbol( ("=>", false) => Token::LexError(LERR::ImproperSymbol(
"'=>' is not a valid symbol. This is not Rust! Should it be '>='?".to_string(), "'=>' is not a valid symbol. This is not Rust! Should it be '>='?".to_string(),
))), )),
(":=", false) => Token::LexError(Box::new(LERR::ImproperSymbol( (":=", false) => Token::LexError(LERR::ImproperSymbol(
"':=' is not a valid assignment operator. This is not Go! Should it be simply '='?".to_string(), "':=' is not a valid assignment operator. This is not Go! Should it be simply '='?".to_string(),
))), )),
("::<", false) => Token::LexError(Box::new(LERR::ImproperSymbol( ("::<", false) => Token::LexError(LERR::ImproperSymbol(
"'::<>' is not a valid symbol. This is not Rust! Should it be '::'?".to_string(), "'::<>' is not a valid symbol. This is not Rust! Should it be '::'?".to_string(),
))), )),
("(*", false) | ("*)", false) => Token::LexError(Box::new(LERR::ImproperSymbol( ("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol(
"'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(), "'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(),
))), )),
("#", false) => Token::LexError(Box::new(LERR::ImproperSymbol( ("#", false) => Token::LexError(LERR::ImproperSymbol(
"'#' is not a valid symbol. Should it be '#{'?".to_string(), "'#' is not a valid symbol. Should it be '#{'?".to_string(),
))), )),
// Reserved keyword/operator that is custom. // Reserved keyword/operator that is custom.
(_, true) => Token::Custom(s), (_, true) => Token::Custom(s),
// Reserved operator that is not custom. // Reserved operator that is not custom.
(token, false) if !is_valid_identifier(token.chars()) => Token::LexError(Box::new(LERR::ImproperSymbol( (token, false) if !is_valid_identifier(token.chars()) => Token::LexError(LERR::ImproperSymbol(
format!("'{}' is a reserved symbol", token) format!("'{}' is a reserved symbol", token)
))), )),
// Reserved keyword that is not custom and disabled. // Reserved keyword that is not custom and disabled.
(token, false) if self.engine.disabled_symbols.contains(token) => Token::LexError(Box::new(LERR::ImproperSymbol( (token, false) if self.engine.disabled_symbols.contains(token) => Token::LexError(LERR::ImproperSymbol(
format!("reserved symbol '{}' is disabled", token) format!("reserved symbol '{}' is disabled", token)
))), )),
// Reserved keyword/operator that is not custom. // Reserved keyword/operator that is not custom.
(_, false) => Token::Reserved(s), (_, false) => Token::Reserved(s),
}, pos)), }, pos)),
@ -1705,7 +1699,7 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
// Disabled operator // Disabled operator
Some((token, pos)) if token.is_operator() && self.engine.disabled_symbols.contains(token.syntax().as_ref()) => { Some((token, pos)) if token.is_operator() && self.engine.disabled_symbols.contains(token.syntax().as_ref()) => {
Some(( Some((
Token::LexError(Box::new(LexError::UnexpectedInput(token.syntax().into()))), Token::LexError(LexError::UnexpectedInput(token.syntax().into())),
pos, pos,
)) ))
} }

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, ParseErrorType, INT}; use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT};
#[test] #[test]
fn test_constant() -> Result<(), Box<EvalAltResult>> { fn test_constant() -> Result<(), Box<EvalAltResult>> {
@ -15,13 +15,28 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
assert!(matches!( assert!(matches!(
*engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"), *engine.consume("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x" EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x"
)); ));
Ok(()) Ok(())
} }
#[test]
fn test_constant_scope() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
let mut scope = Scope::new();
scope.push_constant("x", 42 as INT);
assert!(matches!(
*engine.consume_with_scope(&mut scope, "x = 1").expect_err("expects error"),
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "x"
));
Ok(())
}
#[test] #[test]
fn test_var_is_def() -> Result<(), Box<EvalAltResult>> { fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();

View File

@ -38,7 +38,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 13, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -52,7 +52,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 100, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
engine.set_max_string_size(0); engine.set_max_string_size(0);
@ -98,7 +98,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -112,7 +112,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 100, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
assert!(matches!( assert!(matches!(
@ -124,7 +124,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -137,7 +137,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
assert!(matches!( assert!(matches!(
@ -151,7 +151,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
engine.set_max_array_size(0); engine.set_max_array_size(0);
@ -216,7 +216,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
assert!(matches!( assert!(matches!(
@ -228,7 +228,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -241,7 +241,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
engine.set_max_map_size(0); engine.set_max_map_size(0);

View File

@ -1,5 +1,5 @@
#![cfg(not(feature = "unchecked"))] #![cfg(not(feature = "unchecked"))]
use rhai::{Engine, EvalAltResult}; use rhai::{Engine, EvalAltResult, INT};
#[test] #[test]
fn test_max_operations() -> Result<(), Box<EvalAltResult>> { fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
@ -10,7 +10,7 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
if count % 100 == 0 { if count % 100 == 0 {
println!("{}", count); println!("{}", count);
} }
true None
}); });
engine.eval::<()>("let x = 0; while x < 20 { x += 1; }")?; engine.eval::<()>("let x = 0; while x < 20 { x += 1; }")?;
@ -38,7 +38,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
if count % 100 == 0 { if count % 100 == 0 {
println!("{}", count); println!("{}", count);
} }
true None
}); });
engine.eval::<()>( engine.eval::<()>(
@ -94,7 +94,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
if count % 100 == 0 { if count % 100 == 0 {
println!("{}", count); println!("{}", count);
} }
true None
}); });
assert!(matches!( assert!(matches!(
@ -117,13 +117,19 @@ fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_max_operations(500); engine.set_max_operations(500);
engine.on_progress(|&count| count < 100); engine.on_progress(|&count| {
if count < 100 {
None
} else {
Some((42 as INT).into())
}
});
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<()>("for x in range(0, 500) {}") .eval::<()>("for x in range(0, 500) {}")
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorTerminated(_) EvalAltResult::ErrorTerminated(x, _) if x.as_int()? == 42
)); ));
Ok(()) Ok(())

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, ParseError, ParseErrorType, Position, INT}; use rhai::{Engine, EvalAltResult, LexError, ParseError, ParseErrorType, INT, NO_POS};
#[test] #[test]
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> { fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
@ -68,9 +68,9 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
.register_custom_syntax(&["!"], 0, |_, _| Ok(().into())) .register_custom_syntax(&["!"], 0, |_, _| Ok(().into()))
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::BadInput( ParseErrorType::BadInput(LexError::ImproperSymbol(
"Improper symbol for custom syntax at position #1: '!'".to_string() "Improper symbol for custom syntax at position #1: '!'".to_string()
) ))
); );
Ok(()) Ok(())
@ -88,8 +88,10 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
2 => match stream[1].as_str() { 2 => match stream[1].as_str() {
"world" | "kitty" => Ok(None), "world" | "kitty" => Ok(None),
s => Err(ParseError( s => Err(ParseError(
Box::new(ParseErrorType::BadInput(s.to_string())), Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol(
Position::none(), s.to_string(),
))),
NO_POS,
)), )),
}, },
_ => unreachable!(), _ => unreachable!(),
@ -109,7 +111,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<INT>("hello kitty")?, 42); assert_eq!(engine.eval::<INT>("hello kitty")?, 42);
assert_eq!( assert_eq!(
*engine.compile("hello hey").expect_err("should error").0, *engine.compile("hello hey").expect_err("should error").0,
ParseErrorType::BadInput("hey".to_string()) ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string()))
); );
Ok(()) Ok(())

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, ParseErrorType, RegisterFn, INT}; use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, RegisterFn, INT};
#[test] #[test]
fn test_tokens_disabled() { fn test_tokens_disabled() {
@ -16,10 +16,13 @@ fn test_tokens_disabled() {
engine.disable_symbol("+="); // disable the '+=' operator engine.disable_symbol("+="); // disable the '+=' operator
assert!(matches!( assert_eq!(
*engine.compile("let x = 40 + 2; x += 1;").expect_err("should error").0, *engine
ParseErrorType::BadInput(ref s) if s == "Unexpected '+='" .compile("let x = 40 + 2; x += 1;")
)); .expect_err("should error")
.0,
ParseErrorType::BadInput(LexError::UnexpectedInput("+=".to_string()))
);
} }
#[test] #[test]

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, Position, Scope, INT}; use rhai::{Engine, EvalAltResult, Scope, INT, NO_POS};
#[test] #[test]
fn test_var_scope() -> Result<(), Box<EvalAltResult>> { fn test_var_scope() -> Result<(), Box<EvalAltResult>> {
@ -67,7 +67,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
"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!
"DO_NOT_USE" => { "DO_NOT_USE" => {
Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into()) Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS).into())
} }
// Silently maps 'chameleon' into 'innocent'. // Silently maps 'chameleon' into 'innocent'.
"chameleon" => context "chameleon" => context
@ -75,7 +75,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
.get_value("innocent") .get_value("innocent")
.map(Some) .map(Some)
.ok_or_else(|| { .ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into() EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS).into()
}), }),
// Return Ok(None) to continue with the normal variable resolution process. // Return Ok(None) to continue with the normal variable resolution process.
_ => Ok(None), _ => Ok(None),