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.
* `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
------------

View File

@ -294,9 +294,8 @@ engine.register_custom_syntax_raw(
"update" | "check" | "add" | "remove" => Ok(Some("$ident$".to_string())),
"cleanup" => Ok(None),
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
format!("Improper command: {}", cmd))),
Position::none(),
)),
LexError::ImproperSymbol(format!("Improper command: {}", cmd))
)), NO_POS)),
},
// perform command arg ...
3 => match (stream[1].as_str(), stream[2].as_str()) {
@ -308,9 +307,10 @@ engine.register_custom_syntax_raw(
("add", arg) => Ok(None),
("remove", arg) => Ok(None),
(cmd, arg) => Err(ParseError(Box::new(ParseErrorType::BadInput(
format!("Invalid argument for command {}: {}", cmd, arg))),
Position::none(),
)),
LexError::ImproperSymbol(
format!("Invalid argument for command {}: {}", cmd, arg)
)
)), NO_POS)),
},
_ => unreachable!(),
},
@ -336,7 +336,7 @@ where:
The return value is `Result<Option<String>, ParseError>` where:
| Value | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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$"` |
| `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())),
// Override a variable - make it not found even if it exists!
"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'.
"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.
_ => Ok(None)
@ -83,7 +83,7 @@ where:
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where:
| Value | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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 |
| `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'
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::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.
```rust
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS};
use rhai::plugin::*; // a "prelude" import for macros
// 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(),
context.engine().max_array_size(),
size as usize,
Position::none(),
NO_POS,
).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.
```rust
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS};
use rhai::plugin::*; // a "prelude" import for macros
#[export_module]
@ -397,7 +397,7 @@ mod my_module {
"Size to grow".to_string(),
context.engine().max_array_size(),
size as usize,
Position::none(),
NO_POS,
).into();
}

View File

@ -17,13 +17,14 @@ engine.on_progress(|&count| { // parameter is '&u64' - number of operations al
if count % 1000 == 0 {
println!("{}", count); // print out a progress log every 1,000 operations
}
true // return 'true' to continue running the script
// return 'false' to immediately terminate the script
None // return 'None' to continue running the script
// return 'Some(token)' to immediately terminate the script
});
```
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

View File

@ -861,24 +861,24 @@ pub struct BinaryExpr {
/// A function call.
#[derive(Debug, Clone, Hash, Default)]
pub struct FnCallInfo {
/// 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>,
/// Namespace of the function, if any.
pub namespace: Option<ModuleRef>,
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
pub hash: u64,
/// 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).
pub native_only: bool,
/// Does this function call capture the parent scope?
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.
/// Type is `bool` in order for `FnCallInfo` to be `Hash`
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.
@ -903,10 +903,10 @@ pub enum Expr {
StringConstant(Box<IdentX>),
/// FnPtr constant.
FnPointer(Box<IdentX>),
/// Variable access - (variable name, optional modules, hash, optional index)
Variable(Box<(Ident, Option<ModuleRef>, u64, Option<NonZeroUsize>)>),
/// Property access.
Property(Box<(IdentX, (String, String))>),
/// Variable access - (optional index, optional modules, hash, variable name)
Variable(Box<(Option<NonZeroUsize>, Option<Box<ModuleRef>>, u64, Ident)>),
/// Property access - (getter, setter), prop
Property(Box<((String, String), IdentX)>),
/// { stmt }
Stmt(Box<Stmt>, Position),
/// Wrapped expression - should not be optimized away.
@ -1014,7 +1014,7 @@ impl Expr {
/// Is the expression a simple variable access?
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
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,
}
}
@ -1033,9 +1033,9 @@ impl Expr {
Self::FnPointer(x) => x.pos,
Self::Array(_, pos) => *pos,
Self::Map(_, pos) => *pos,
Self::Property(x) => (x.0).pos,
Self::Property(x) => (x.1).pos,
Self::Stmt(_, pos) => *pos,
Self::Variable(x) => (x.0).pos,
Self::Variable(x) => (x.3).pos,
Self::FnCall(_, pos) => *pos,
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::Array(_, pos) => *pos = new_pos,
Self::Map(_, pos) => *pos = new_pos,
Self::Variable(x) => (x.0).pos = new_pos,
Self::Property(x) => (x.0).pos = new_pos,
Self::Variable(x) => (x.3).pos = new_pos,
Self::Property(x) => (x.1).pos = new_pos,
Self::Stmt(_, pos) => *pos = new_pos,
Self::FnCall(_, 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 {
match self {
Self::Variable(x) if x.1.is_none() => {
let ident = x.0;
let ident = x.3;
let getter = make_getter(&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,
}

View File

@ -11,7 +11,7 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::syntax::CustomSyntax;
use crate::token::Position;
use crate::token::{Position, NO_POS};
use crate::{calc_native_fn_hash, StaticVec};
#[cfg(not(feature = "no_index"))]
@ -69,10 +69,58 @@ pub type Map = HashMap<ImmutableString, Dynamic>;
///
/// This type is volatile and may change.
//
// Note - We cannot use &str or Cow<str> here because `eval` may load a module
// and the module name will live beyond the AST of the eval script text.
// The best we can do is a shared reference.
pub type Imports = Vec<(ImmutableString, Module)>;
// # Implementation Notes
//
// We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
// 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(debug_assertions)]
@ -303,7 +351,7 @@ impl<'a> Target<'a> {
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, ch) => {
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.
pub(crate) debug: Callback<str, ()>,
/// 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.
pub(crate) optimization_level: OptimizationLevel,
@ -602,12 +650,10 @@ pub fn search_imports<'s>(
Ok(if index > 0 {
let offset = mods.len() - index;
&mods.get(offset).unwrap().1
mods.get(offset).expect("invalid index in Imports")
} else {
mods.iter()
.rev()
.find(|(n, _)| n == root)
.map(|(_, m)| m)
mods.find(root)
.map(|n| mods.get(n).expect("invalid index in Imports"))
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
})
}
@ -630,12 +676,13 @@ pub fn search_imports_mut<'s>(
Ok(if index > 0 {
let offset = mods.len() - index;
&mut mods.get_mut(offset).unwrap().1
mods.get_mut(offset).expect("invalid index in Imports")
} else {
mods.iter_mut()
.rev()
.find(|(n, _)| n == root)
.map(|(_, m)| m)
if let Some(n) = mods.find(root) {
mods.get_mut(n)
} else {
None
}
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
})
}
@ -765,7 +812,7 @@ impl Engine {
match expr {
Expr::Variable(v) => match v.as_ref() {
// 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 target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| {
match *err {
@ -797,7 +844,7 @@ impl Engine {
this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr,
) -> 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(),
_ => unreachable!(),
};
@ -937,7 +984,7 @@ impl Engine {
let args = &mut [val, &mut idx_val2, &mut new_val.0];
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,
)
.map_err(|err| match *err {
@ -946,7 +993,7 @@ impl Engine {
{
EvalAltResult::ErrorIndexingType(
self.map_type_name(val_type_name).into(),
Position::none(),
NO_POS,
)
}
err => err,
@ -979,8 +1026,7 @@ impl Engine {
let def_value = def_value.map(Into::<Dynamic>::into);
let args = idx_val.as_fn_call_args();
self.make_method_call(
state, lib, name, *hash, target, args, &def_value, *native, false,
level,
state, lib, name, *hash, target, args, def_value, *native, false, level,
)
.map_err(|err| err.fill_position(*pos))
}
@ -988,7 +1034,7 @@ impl Engine {
Expr::FnCall(_, _) => unreachable!(),
// {xxx:map}.id = ???
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 mut val = self
.get_indexed_mut(state, lib, target, index, *pos, true, false, level)?;
@ -998,7 +1044,7 @@ impl Engine {
}
// {xxx:map}.id
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 val = self.get_indexed_mut(
state, lib, target, index, *pos, false, false, level,
@ -1008,11 +1054,11 @@ impl Engine {
}
// xxx.id = ???
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 args = [target.as_mut(), &mut new_val.as_mut().unwrap().0];
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,
)
.map(|(v, _)| (v, true))
@ -1020,10 +1066,10 @@ impl Engine {
}
// xxx.id
Expr::Property(x) => {
let (IdentX { pos, .. }, (getter, _)) = x.as_ref();
let ((getter, _), IdentX { pos, .. }) = x.as_ref();
let mut args = [target.as_mut()];
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,
)
.map(|(v, _)| (v, false))
@ -1033,7 +1079,7 @@ impl Engine {
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
let mut val = match &x.lhs {
Expr::Property(p) => {
let IdentX { name, pos } = &p.0;
let IdentX { name, pos } = &p.1;
let index = name.clone().into();
self.get_indexed_mut(
state, lib, target, index, *pos, false, true, level,
@ -1052,7 +1098,7 @@ impl Engine {
let args = idx_val.as_fn_call_args();
let (val, _) = self
.make_method_call(
state, lib, name, *hash, target, args, &def_value, *native,
state, lib, name, *hash, target, args, def_value, *native,
false, level,
)
.map_err(|err| err.fill_position(*pos))?;
@ -1075,13 +1121,13 @@ impl Engine {
match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr
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 args = &mut arg_values[..1];
let (mut val, updated) = self
.exec_fn_call(
state, lib, getter, 0, args, is_ref, true, false, None,
&None, level,
None, level,
)
.map_err(|err| err.fill_position(*pos))?;
@ -1107,7 +1153,7 @@ impl Engine {
arg_values[1] = val;
self.exec_fn_call(
state, lib, setter, 0, arg_values, is_ref, true, false,
None, &None, level,
None, None, level,
)
.or_else(
|err| match *err {
@ -1135,7 +1181,7 @@ impl Engine {
let args = idx_val.as_fn_call_args();
let (mut val, _) = self
.make_method_call(
state, lib, name, *hash, target, args, &def_value, *native,
state, lib, name, *hash, target, args, def_value, *native,
false, level,
)
.map_err(|err| err.fill_position(*pos))?;
@ -1210,7 +1256,7 @@ impl Engine {
let Ident {
name: var_name,
pos: var_pos,
} = &x.0;
} = &x.3;
self.inc_operations(state)
.map_err(|err| err.fill_position(*var_pos))?;
@ -1411,25 +1457,21 @@ impl Engine {
let mut idx = idx;
let args = &mut [val, &mut idx];
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_err(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
Box::new(EvalAltResult::ErrorIndexingType(
type_name.into(),
Position::none(),
))
Box::new(EvalAltResult::ErrorIndexingType(type_name.into(), NO_POS))
}
_ => err,
})
}
_ => EvalAltResult::ErrorIndexingType(
self.map_type_name(val.type_name()).into(),
Position::none(),
)
.into(),
_ => {
EvalAltResult::ErrorIndexingType(self.map_type_name(val.type_name()).into(), NO_POS)
.into()
}
}
}
@ -1461,13 +1503,14 @@ impl Engine {
for value in rhs_value.iter_mut() {
let args = &mut [&mut lhs_value.clone(), value];
let def_value = def_value.clone();
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
let hash =
calc_native_fn_hash(empty(), OP_FUNC, args.iter().map(|a| a.type_id()));
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()))?
.0
.as_bool()
@ -1477,7 +1520,7 @@ impl Engine {
}
}
Ok(def_value.unwrap())
Ok(false.into())
}
#[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(rhs_value)) => match lhs_value {
@ -1521,11 +1564,11 @@ impl Engine {
Expr::FnPointer(x) => {
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 {
Ok(val.clone())
} else {
EvalAltResult::ErrorUnboundThis((x.0).pos).into()
EvalAltResult::ErrorUnboundThis((x.3).pos).into()
}
}
Expr::Variable(_) => {
@ -1580,7 +1623,7 @@ impl Engine {
} = x.as_ref();
let def_value = def_value.map(Into::<Dynamic>::into);
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,
)
.map_err(|err| err.fill_position(*pos))
@ -1596,8 +1639,9 @@ impl Engine {
def_value,
..
} = x.as_ref();
let modules = namespace.as_ref().map(|v| v.as_ref());
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,
)
.map_err(|err| err.fill_position(*pos))
@ -1769,7 +1813,7 @@ impl Engine {
// Run function
let (value, _) = self
.exec_fn_call(
state, lib, op, 0, args, false, false, false, None, &None,
state, lib, op, 0, args, false, false, false, None, None,
level,
)
.map_err(|err| err.fill_position(*op_pos))?;
@ -1807,7 +1851,7 @@ impl Engine {
let result = self
.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_err(|err| err.fill_position(*op_pos))?;
@ -1989,7 +2033,7 @@ impl Engine {
let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err {
x.clone()
} else {
err.set_position(Position::none());
err.set_position(NO_POS);
err.to_string().into()
};
@ -2070,7 +2114,7 @@ impl Engine {
} else {
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())
}
@ -2092,7 +2136,7 @@ impl Engine {
if let Some(name_def) = alias {
module.index_all_sub_modules();
mods.push((name_def.name.clone(), module));
mods.push(name_def.name.clone(), module);
}
state.modules += 1;
@ -2251,34 +2295,17 @@ impl Engine {
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
if s > self.max_string_size() {
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
self.max_string_size(),
s,
Position::none(),
)
.into();
return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into();
}
#[cfg(not(feature = "no_index"))]
if _arr > self.max_array_size() {
return EvalAltResult::ErrorDataTooLarge(
"Size of array".to_string(),
self.max_array_size(),
_arr,
Position::none(),
)
.into();
return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), NO_POS).into();
}
#[cfg(not(feature = "no_object"))]
if _map > self.max_map_size() {
return EvalAltResult::ErrorDataTooLarge(
"Number of properties in object map".to_string(),
self.max_map_size(),
_map,
Position::none(),
)
return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), NO_POS)
.into();
}
@ -2293,14 +2320,14 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
// Guard against too many 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
if let Some(progress) = &self.progress {
if !progress(&state.operations) {
// Terminate script if progress returns false
return EvalAltResult::ErrorTerminated(Position::none()).into();
if let Some(token) = progress(&state.operations) {
// Terminate script if progress returns a termination token
return EvalAltResult::ErrorTerminated(token, NO_POS).into();
}
}

View File

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

View File

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

View File

@ -6,7 +6,7 @@ use crate::engine::{Engine, EvalContext};
use crate::module::Module;
use crate::plugin::PluginFunction;
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::{calc_script_fn_hash, StaticVec};
@ -160,7 +160,7 @@ impl FnPtr {
/// clone them _before_ calling this function.
pub fn call_dynamic(
&self,
context: NativeCallContext,
ctx: NativeCallContext,
this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>,
) -> Result<Dynamic, Box<EvalAltResult>> {
@ -182,11 +182,10 @@ impl FnPtr {
args.insert(0, obj);
}
context
.engine()
ctx.engine()
.exec_fn_call(
&mut Default::default(),
context.lib,
ctx.lib,
fn_name,
hash_script,
args.as_mut(),
@ -194,7 +193,7 @@ impl FnPtr {
has_this,
true,
None,
&None,
None,
0,
)
.map(|(v, _)| v)
@ -216,7 +215,7 @@ impl TryFrom<ImmutableString> for FnPtr {
if is_valid_identifier(value.chars()) {
Ok(Self(value, Default::default()))
} 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_register::{RegisterFn, RegisterResultFn};
pub use module::Module;
pub use parse_error::{ParseError, ParseErrorType};
pub use parse_error::{LexError, ParseError, ParseErrorType};
pub use result::EvalAltResult;
pub use scope::Scope;
pub use syntax::Expression;
pub use token::Position;
pub use token::{Position, NO_POS};
pub use utils::ImmutableString;
#[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_register::by_value as cast_arg;
use crate::result::EvalAltResult;
use crate::token::{Position, Token};
use crate::token::{Token, NO_POS};
use crate::utils::{ImmutableString, StraightHasherBuilder};
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};
#[cfg(not(feature = "no_module"))]
use crate::{
ast::AST,
engine::{Engine, Imports},
scope::{Entry as ScopeEntry, Scope},
};
use crate::{ast::AST, engine::Engine, scope::Scope};
#[cfg(not(feature = "no_index"))]
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
@ -275,11 +271,11 @@ impl Module {
hash_var: u64,
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
if hash_var == 0 {
Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into())
Err(EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into())
} else {
self.all_variables.get_mut(&hash_var).ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into()
})
self.all_variables
.get_mut(&hash_var)
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into())
}
}
@ -533,9 +529,8 @@ impl Module {
+ SendSync
+ 'static,
) -> u64 {
let f = move |context: NativeCallContext, args: &mut FnCallArgs| {
func(context, args).map(Dynamic::from)
};
let f =
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
self.set_fn(
name,
FnAccess::Public,
@ -1340,7 +1335,7 @@ impl Module {
ast: &AST,
engine: &Engine,
) -> Result<Self, Box<EvalAltResult>> {
let mut mods = Imports::new();
let mut mods = Default::default();
// Run the script
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
@ -1348,12 +1343,10 @@ impl Module {
// Create new module
let mut module = Module::new();
scope
.into_iter()
.for_each(|ScopeEntry { value, alias, .. }| {
scope.into_iter().for_each(|(_, _, value, alias)| {
// Variables with an alias left in the scope become module variables
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::module::Module;
use crate::parser::map_dynamic_to_expr;
use crate::scope::{Entry as ScopeEntry, Scope};
use crate::token::{is_valid_identifier, Position};
use crate::scope::Scope;
use crate::token::{is_valid_identifier, NO_POS};
use crate::{calc_native_fn_hash, StaticVec};
#[cfg(not(feature = "no_function"))]
@ -147,7 +147,7 @@ fn call_fn_with_constant_arguments(
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
false,
true,
&None,
None,
)
.ok()
.map(|(v, _)| v)
@ -156,11 +156,16 @@ fn call_fn_with_constant_arguments(
/// Optimize a statement.
fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
match stmt {
// id op= expr
Stmt::Assignment(x, pos) => Stmt::Assignment(
// expr op= expr
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))),
pos,
),
},
// if false { if_block } -> Noop
Stmt::IfThenElse(Expr::False(pos), x, _) if x.1.is_none() => {
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) {
// map.string
(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.
// All other items can be thrown away.
state.set_dirty();
@ -462,6 +467,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
.map(|(_, mut expr)| { expr.set_position(pos); expr })
.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) => Expr::Dot(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state),
@ -498,6 +508,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty();
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) => Expr::Index(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state),
@ -686,12 +701,12 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
}
// 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();
// Replace constant with value
let mut expr = state.find_constant(&x.0.name).unwrap().clone();
expr.set_position(x.0.pos);
let mut expr = state.find_constant(&x.3.name).unwrap().clone();
expr.set_position(x.3.pos);
expr
}
@ -721,24 +736,15 @@ fn optimize(
// Set up the state
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
.to_iter()
// Get all the constants that can be made into a constant literal.
.filter(|ScopeEntry { typ, .. }| typ.is_constant())
.for_each(
|ScopeEntry {
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);
.iter()
.filter(|(_, typ, _)| *typ)
.for_each(|(name, _, value)| {
if let Some(val) = map_dynamic_to_expr(value, NO_POS) {
state.push_constant(name, val);
}
},
);
});
let orig_constants_len = state.constants.len();

View File

@ -4,7 +4,7 @@ use crate::def_package;
use crate::plugin::*;
use crate::INT;
use crate::{result::EvalAltResult, token::Position};
use crate::{result::EvalAltResult, token::NO_POS};
#[cfg(not(feature = "no_float"))]
use crate::FLOAT;
@ -17,7 +17,7 @@ use crate::stdlib::{format, string::String};
#[inline(always)]
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 {

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
//! Module containing error definitions for the parsing process.
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::token::{Position, NO_POS};
use crate::stdlib::{
boxed::Box,
@ -43,26 +43,34 @@ impl fmt::Display for LexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s),
Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s),
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s),
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s),
Self::UnterminatedString => write!(f, "Open string is not terminated"),
Self::StringTooLong(max) => write!(
f,
"Length of string literal exceeds the maximum limit ({})",
max
),
Self::MalformedEscapeSequence(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::MalformedNumber(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::MalformedChar(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::MalformedIdentifier(s) => write!(f, "{}: '{}'", self.desc(), s),
Self::UnterminatedString => f.write_str(self.desc()),
Self::StringTooLong(max) => write!(f, "{} ({})", self.desc(), max),
Self::ImproperSymbol(s) => f.write_str(s),
}
}
}
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)]
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)]
#[non_exhaustive]
pub enum ParseErrorType {
/// Error in the script text. Wrapped value is the error message.
BadInput(String),
/// The script ends prematurely.
UnexpectedEOF,
/// Error in the script text. Wrapped value is the lex error.
BadInput(LexError),
/// An unknown operator is encountered. Wrapped value is the operator.
UnknownOperator(String),
/// 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 {
match self {
Self::BadInput(p) => p,
Self::UnexpectedEOF => "Script is incomplete",
Self::BadInput(p) => p.desc(),
Self::UnknownOperator(_) => "Unknown operator",
Self::MissingToken(_, _) => "Expecting a certain token that is missing",
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
@ -196,9 +204,9 @@ impl ParseErrorType {
impl fmt::Display for ParseErrorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => {
f.write_str(if s.is_empty() { self.desc() } else { s })
}
Self::BadInput(err) => write!(f, "{}", err),
Self::MalformedCallExpr(s) => f.write_str(if s.is_empty() { self.desc() } else { s }),
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), 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)]
fn from(err: &LexError) -> Self {
fn from(err: LexError) -> Self {
match err {
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> {
#[inline(always)]
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::scope::{EntryType as ScopeEntryType, Scope};
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::{calc_script_fn_hash, StaticVec};
@ -265,7 +267,7 @@ fn parse_fn_call(
lib: &mut FunctionsLib,
id: String,
capture: bool,
mut namespace: Option<ModuleRef>,
mut namespace: Option<Box<ModuleRef>>,
settings: ParseSettings,
) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap();
@ -786,7 +788,7 @@ fn parse_primary(
{
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
#[cfg(not(feature = "no_module"))]
@ -796,18 +798,18 @@ fn parse_primary(
{
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
Token::Identifier(s) => {
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
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
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 {
return Err(PERR::Reserved(s).into_err(settings.pos));
}
@ -816,12 +818,13 @@ fn parse_primary(
// Access to `this` as a variable is OK
Token::Reserved(s) if s == KEYWORD_THIS && *next_token != Token::LeftParen => {
if !settings.is_function_scope {
return Err(
PERR::BadInput(format!("'{}' can only be used in functions", s))
.into_err(settings.pos),
);
return Err(PERR::BadInput(LexError::ImproperSymbol(format!(
"'{}' can only be used in functions",
s
)))
.into_err(settings.pos));
} 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(
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 {
LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos)
} 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)
});
}
@ -877,13 +883,13 @@ fn parse_primary(
.into_err(pos));
}
let (Ident { name, pos }, modules, _, _) = *x;
let (_, modules, _, Ident { name, pos }) = *x;
settings.pos = pos;
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
}
// Function call
(Expr::Variable(x), Token::LeftParen) => {
let (Ident { name, pos }, modules, _, _) = *x;
let (_, modules, _, Ident { name, pos }) = *x;
settings.pos = pos;
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
}
@ -891,17 +897,17 @@ fn parse_primary(
// module access
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
(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 {
modules.push(var_name_def);
} else {
let mut m: ModuleRef = Default::default();
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()) => {
return Err(PERR::Reserved(id2).into_err(pos2));
@ -925,7 +931,7 @@ fn parse_primary(
match &mut root_expr {
// Cache the hash key for module-qualified variables
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();
// Qualifiers + variable name
@ -1081,19 +1087,19 @@ fn make_assignment_stmt<'a>(
) -> Result<Stmt, ParseError> {
match &lhs {
// 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))
}
// var (indexed) = rhs
Expr::Variable(x) => {
let (
index,
_,
_,
Ident {
name,
pos: name_pos,
},
_,
_,
index,
) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => {
@ -1108,19 +1114,19 @@ fn make_assignment_stmt<'a>(
// xxx[???] = rhs, xxx.??? = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => match &x.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))
}
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
Expr::Variable(x) => {
let (
index,
_,
_,
Ident {
name,
pos: name_pos,
},
_,
_,
index,
) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => {
@ -1140,9 +1146,10 @@ fn make_assignment_stmt<'a>(
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
}
// ??? && ??? = rhs, ??? || ??? = rhs
Expr::And(_, _) | Expr::Or(_, _) => {
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(pos))
}
Expr::And(_, _) | Expr::Or(_, _) => Err(PERR::BadInput(LexError::ImproperSymbol(
"Possibly a typo of '=='?".to_string(),
))
.into_err(pos)),
// expr = rhs
_ => 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, Expr::Variable(x)) if x.1.is_none() => {
let ident = x.0;
let ident = x.3;
let getter = make_getter(&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)
}
@ -1238,10 +1245,10 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
(_, Expr::FnCall(x, pos))
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 {}(...);",
x.name, x.name
))
)))
.into_err(pos));
}
// lhs.func!(...)
@ -1635,10 +1642,10 @@ fn parse_custom_syntax(
(Token::Identifier(s), pos) => {
segments.push(s.clone());
exprs.push(Expr::Variable(Box::new((
Ident::new(s, pos),
None,
None,
0,
None,
Ident::new(s, pos),
))));
}
(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`).
fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
match input.peek().unwrap() {
(Token::Equals, pos) => {
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos))
}
(Token::Equals, pos) => Err(PERR::BadInput(LexError::ImproperSymbol(
"Possibly a typo of '=='?".to_string(),
))
.into_err(*pos)),
(Token::PlusAssign, pos)
| (Token::MinusAssign, pos)
| (Token::MultiplyAssign, pos)
@ -1747,9 +1755,9 @@ fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
| (Token::PowerOfAssign, pos)
| (Token::AndAssign, 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(),
)
))
.into_err(*pos)),
_ => Ok(()),
@ -2492,12 +2500,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
#[cfg(not(feature = "no_closure"))]
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")]
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);
@ -2683,7 +2691,7 @@ impl Engine {
is_function_scope: false,
is_breakable: false,
level: 0,
pos: Position::none(),
pos: NO_POS,
};
let expr = parse_expr(input, &mut state, &mut functions, settings)?;
@ -2694,7 +2702,8 @@ impl Engine {
// Return error if the expression doesn't end
(token, pos) => {
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_breakable: false,
level: 0,
pos: Position::none(),
pos: NO_POS,
};
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {

View File

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

View File

@ -1,11 +1,8 @@
//! Module that defines the `Scope` type representing a function call-stack scope.
use crate::ast::Expr;
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.
#[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.
/// 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,
/// 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)]
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> {
/// Create a new Scope.
@ -114,7 +113,9 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
pub fn clear(&mut self) -> &mut Self {
self.0.clear();
self.names.clear();
self.types.clear();
self.values.clear();
self
}
@ -133,7 +134,7 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
pub fn len(&self) -> usize {
self.0.len()
self.values.len()
}
/// Is the Scope empty?
@ -151,7 +152,7 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.0.len() == 0
self.values.len() == 0
}
/// Add (push) a new entry to the Scope.
@ -172,7 +173,7 @@ impl<'a> Scope<'a> {
name: K,
value: T,
) -> &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.
@ -189,7 +190,7 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
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.
@ -216,7 +217,7 @@ impl<'a> Scope<'a> {
name: K,
value: T,
) -> &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.
@ -244,7 +245,7 @@ impl<'a> Scope<'a> {
name: K,
value: Dynamic,
) -> &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.
@ -254,22 +255,10 @@ impl<'a> Scope<'a> {
name: K,
entry_type: EntryType,
value: Dynamic,
map_expr: bool,
) -> &mut Self {
let expr = if map_expr {
map_dynamic_to_expr(value.clone(), Position::none()).map(Box::new)
} else {
None
};
self.0.push(Entry {
name: name.into(),
typ: entry_type,
alias: None,
value: value.into(),
expr,
});
self.names.push((name.into(), None));
self.types.push(entry_type);
self.values.push(value.into());
self
}
@ -301,7 +290,9 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
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
}
@ -320,37 +311,28 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
pub fn contains(&self, name: &str) -> bool {
self.0
self.names
.iter()
.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.
#[inline(always)]
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
self.0
self.names
.iter()
.enumerate()
.rev() // Always search a Scope in reverse order
.find_map(|(index, Entry { name: key, typ, .. })| {
if name == key {
Some((index, *typ))
.find_map(|(index, (key, _))| {
if name == key.as_ref() {
Some((index, self.types[index]))
} else {
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.
///
/// # Example
@ -365,8 +347,12 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.get_entry(name)
.and_then(|Entry { value, .. }| value.flatten_clone().try_cast())
self.names
.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.
@ -398,7 +384,7 @@ impl<'a> Scope<'a> {
}
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
Some((index, EntryType::Normal)) => {
self.0.get_mut(index).unwrap().value = Dynamic::from(value);
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
}
}
self
@ -407,16 +393,18 @@ impl<'a> Scope<'a> {
/// Get a mutable reference to an entry in the Scope.
#[inline(always)]
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.
#[cfg(not(feature = "no_module"))]
#[inline(always)]
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");
entry.alias = Some(Box::new(alias));
let entry = self.names.get_mut(index).expect("invalid index in Scope");
entry.1 = Some(alias);
self
}
@ -424,34 +412,36 @@ impl<'a> Scope<'a> {
/// Shadowed variables are omitted in the copy.
#[inline]
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| {
if entries
self.names
.iter()
.find(|Entry { name, .. }| &entry.name == name)
.is_none()
{
entries.push(entry.clone());
.enumerate()
.rev()
.for_each(|(index, (name, alias))| {
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.
#[inline(always)]
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
self.0.into_iter()
}
/// Get an iterator to entries in the Scope in reverse order.
#[inline(always)]
pub(crate) fn to_iter(&self) -> impl Iterator<Item = &Entry> {
self.0.iter().rev() // Always search a Scope in reverse order
pub(crate) fn into_iter(
self,
) -> impl Iterator<Item = (Cow<'a, str>, EntryType, Dynamic, Option<String>)> {
self.names
.into_iter()
.zip(self.types.into_iter().zip(self.values.into_iter()))
.map(|((name, alias), (typ, value))| (name, typ, value, alias))
}
/// Get an iterator to entries in the Scope.
/// Shared values are flatten-cloned.
///
/// # Example
///
@ -484,25 +474,21 @@ impl<'a> Scope<'a> {
/// Get an iterator to entries in the Scope.
/// Shared values are not expanded.
#[inline(always)]
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
self.0.iter().map(
|Entry {
name, typ, value, ..
}| { (name.as_ref(), typ.is_constant(), value) },
)
pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator<Item = (&'a str, bool, &'x Dynamic)> + 'x {
self.names
.iter()
.zip(self.types.iter().zip(self.values.iter()))
.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> {
#[inline(always)]
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
self.0
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
name: name.into(),
typ,
alias: None,
value: value.into(),
expr: None,
}));
iter.into_iter().for_each(|(name, typ, value)| {
self.names.push((name.into(), None));
self.types.push(typ);
self.values.push(value);
});
}
}

View File

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

View File

@ -2,7 +2,7 @@
use crate::dynamic::Dynamic;
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::token::NO_POS;
#[cfg(not(feature = "no_index"))]
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> {
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)
}
#[cfg(feature = "no_object")]
return EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(),
"map".into(),
Position::none(),
)
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
.into();
}
@ -307,11 +303,7 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(not(feature = "no_index"))]
return Ok(DynamicSerializer::new(Array::new().into()));
#[cfg(feature = "no_index")]
return EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(),
"array".into(),
Position::none(),
)
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "array".into(), NO_POS)
.into();
}
@ -345,12 +337,7 @@ impl Serializer for &mut DynamicSerializer {
let err_type = "map";
#[cfg(not(feature = "no_object"))]
let err_type = "array";
EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(),
err_type.into(),
Position::none(),
)
.into()
EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), err_type.into(), NO_POS).into()
}
}
@ -358,11 +345,7 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(not(feature = "no_object"))]
return Ok(DynamicSerializer::new(Map::new().into()));
#[cfg(feature = "no_object")]
return EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(),
"map".into(),
Position::none(),
)
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
.into();
}
@ -387,11 +370,7 @@ impl Serializer for &mut DynamicSerializer {
map: Map::with_capacity(_len),
});
#[cfg(feature = "no_object")]
return EvalAltResult::ErrorMismatchOutputType(
"Dynamic".into(),
"map".into(),
Position::none(),
)
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
.into();
}
}
@ -501,11 +480,7 @@ impl SerializeMap for DynamicSerializer {
let key = mem::take(&mut self._key)
.take_immutable_string()
.map_err(|typ| {
EvalAltResult::ErrorMismatchOutputType(
"string".into(),
typ.into(),
Position::none(),
)
EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS)
})?;
let _value = _value.serialize(&mut *self)?;
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 = _key.take_immutable_string().map_err(|typ| {
EvalAltResult::ErrorMismatchOutputType(
"string".into(),
typ.into(),
Position::none(),
)
EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS)
})?;
let _value = _value.serialize(&mut *self)?;
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).
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::token::NO_POS;
use crate::utils::ImmutableString;
use serde::de::{Deserializer, Visitor};
@ -20,11 +20,7 @@ impl<'a> ImmutableStringDeserializer<'a> {
}
/// Shortcut for a type conversion error.
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> {
EvalAltResult::ErrorMismatchOutputType(
type_name::<T>().into(),
"string".into(),
Position::none(),
)
EvalAltResult::ErrorMismatchOutputType(type_name::<T>().into(), "string".into(), NO_POS)
.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::parse_error::{LexError, ParseError};
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::StaticVec;
@ -139,7 +139,7 @@ impl Engine {
segments.len() + 1,
s
))
.into_err(Position::none())
.into_err(NO_POS)
.into());
}
// Identifier in first position
@ -156,7 +156,7 @@ impl Engine {
segments.len() + 1,
s
))
.into_err(Position::none())
.into_err(NO_POS)
.into());
}
};

View File

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

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT};
#[test]
fn test_constant() -> Result<(), Box<EvalAltResult>> {
@ -15,13 +15,28 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))]
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"
));
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]
fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();

View File

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

View File

@ -1,5 +1,5 @@
#![cfg(not(feature = "unchecked"))]
use rhai::{Engine, EvalAltResult};
use rhai::{Engine, EvalAltResult, INT};
#[test]
fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
@ -10,7 +10,7 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
if count % 100 == 0 {
println!("{}", count);
}
true
None
});
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 {
println!("{}", count);
}
true
None
});
engine.eval::<()>(
@ -94,7 +94,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
if count % 100 == 0 {
println!("{}", count);
}
true
None
});
assert!(matches!(
@ -117,13 +117,19 @@ fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
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!(
*engine
.eval::<()>("for x in range(0, 500) {}")
.expect_err("should error"),
EvalAltResult::ErrorTerminated(_)
EvalAltResult::ErrorTerminated(x, _) if x.as_int()? == 42
));
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]
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
@ -68,9 +68,9 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
.register_custom_syntax(&["!"], 0, |_, _| Ok(().into()))
.expect_err("should error")
.0,
ParseErrorType::BadInput(
ParseErrorType::BadInput(LexError::ImproperSymbol(
"Improper symbol for custom syntax at position #1: '!'".to_string()
)
))
);
Ok(())
@ -88,8 +88,10 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
2 => match stream[1].as_str() {
"world" | "kitty" => Ok(None),
s => Err(ParseError(
Box::new(ParseErrorType::BadInput(s.to_string())),
Position::none(),
Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol(
s.to_string(),
))),
NO_POS,
)),
},
_ => unreachable!(),
@ -109,7 +111,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<INT>("hello kitty")?, 42);
assert_eq!(
*engine.compile("hello hey").expect_err("should error").0,
ParseErrorType::BadInput("hey".to_string())
ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string()))
);
Ok(())

View File

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

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, Position, Scope, INT};
use rhai::{Engine, EvalAltResult, Scope, INT, NO_POS};
#[test]
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())),
// Override a variable - make it not found even if it exists!
"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'.
"chameleon" => context
@ -75,7 +75,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
.get_value("innocent")
.map(Some)
.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.
_ => Ok(None),