Merge pull request #279 from schungx/master
Optimize code and refactor.
This commit is contained in:
commit
6cba637636
@ -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
|
||||
------------
|
||||
|
@ -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!(),
|
||||
},
|
||||
@ -335,8 +335,8 @@ 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`. |
|
||||
| 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(LexError::ImproperSymbol(message)), NO_POS)` to indicate that there is a syntax error, but it can be any `ParseError`. |
|
||||
|
@ -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)
|
||||
@ -82,8 +82,8 @@ 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`. |
|
||||
| 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, NO_POS)` to indicate that the variable does not exist, but it can be any `EvalAltResult`. |
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
42
src/ast.rs
42
src/ast.rs
@ -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,
|
||||
}
|
||||
|
193
src/engine.rs
193
src/engine.rs
@ -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,13 +676,14 @@ 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)
|
||||
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
|
||||
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,35 +2295,18 @@ 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(),
|
||||
)
|
||||
.into();
|
||||
return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), NO_POS)
|
||||
.into();
|
||||
}
|
||||
|
||||
result
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
112
src/fn_call.rs
112
src/fn_call.rs
@ -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,29 +394,25 @@ 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)
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
EvalAltResult::Return(x, _) => Ok(x),
|
||||
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
|
||||
EvalAltResult::ErrorInFunctionCall(
|
||||
format!("{} > {}", fn_def.name, name),
|
||||
err,
|
||||
Position::none(),
|
||||
)
|
||||
.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(),
|
||||
)
|
||||
.into(),
|
||||
});
|
||||
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),
|
||||
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
|
||||
EvalAltResult::ErrorInFunctionCall(
|
||||
format!("{} > {}", fn_def.name, name),
|
||||
err,
|
||||
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, NO_POS)
|
||||
.into(),
|
||||
});
|
||||
|
||||
// Remove all local variables
|
||||
scope.rewind(prev_scope_len);
|
||||
@ -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, ..
|
||||
}| {
|
||||
// Consume the scope values.
|
||||
match typ {
|
||||
ScopeEntryType::Normal => scope.push(name, value),
|
||||
ScopeEntryType::Constant => {
|
||||
scope.push_constant(name, 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),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")]
|
||||
|
@ -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,14 +1343,12 @@ impl Module {
|
||||
// Create new module
|
||||
let mut module = Module::new();
|
||||
|
||||
scope
|
||||
.into_iter()
|
||||
.for_each(|ScopeEntry { value, alias, .. }| {
|
||||
// Variables with an alias left in the scope become module variables
|
||||
if let Some(alias) = alias {
|
||||
module.variables.insert(*alias, value);
|
||||
}
|
||||
});
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
// Modules left in the scope become sub-modules
|
||||
mods.into_iter().for_each(|(alias, m)| {
|
||||
|
@ -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(
|
||||
Box::new((optimize_expr(x.0, state), x.1, optimize_expr(x.2, state))),
|
||||
pos,
|
||||
),
|
||||
// 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();
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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,11 +85,8 @@ 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(),
|
||||
)
|
||||
.into();
|
||||
return EvalAltResult::ErrorArithmetic(format!("Invalid radix: '{}'", radix), NO_POS)
|
||||
.into();
|
||||
}
|
||||
|
||||
INT::from_str_radix(s.trim(), radix as u32)
|
||||
@ -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,11 +203,8 @@ 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(),
|
||||
)
|
||||
.into()
|
||||
EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS)
|
||||
.into()
|
||||
} else {
|
||||
Ok((x.trunc() as INT).into())
|
||||
}
|
||||
@ -218,11 +212,8 @@ 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(),
|
||||
)
|
||||
.into()
|
||||
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()
|
||||
})
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
103
src/parser.rs
103
src/parser.rs
@ -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,8 +866,10 @@ 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())
|
||||
.into_err(token_pos)
|
||||
PERR::BadInput(LexError::ImproperSymbol(
|
||||
"'!' cannot be used to call module functions".to_string(),
|
||||
))
|
||||
.into_err(token_pos)
|
||||
});
|
||||
}
|
||||
// Function call with !
|
||||
@ -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)? {
|
||||
|
@ -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,
|
||||
|
182
src/scope.rs
182
src/scope.rs
@ -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
|
||||
.iter()
|
||||
.find(|Entry { name, .. }| &entry.name == name)
|
||||
.is_none()
|
||||
{
|
||||
entries.push(entry.clone());
|
||||
}
|
||||
});
|
||||
self.names
|
||||
.iter()
|
||||
.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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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,12 +45,8 @@ 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(),
|
||||
)
|
||||
.into()
|
||||
EvalAltResult::ErrorMismatchOutputType(error.into(), self.value.type_name().into(), NO_POS)
|
||||
.into()
|
||||
}
|
||||
fn deserialize_int<V: Visitor<'de>>(
|
||||
&mut self,
|
||||
@ -127,8 +123,11 @@ 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())
|
||||
.into()
|
||||
EvalAltResult::ErrorParsing(
|
||||
ParseErrorType::BadInput(LexError::ImproperSymbol(err.to_string())),
|
||||
NO_POS,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,24 +295,16 @@ impl Serializer for &mut DynamicSerializer {
|
||||
make_variant(_variant, content)
|
||||
}
|
||||
#[cfg(feature = "no_object")]
|
||||
return EvalAltResult::ErrorMismatchOutputType(
|
||||
"Dynamic".into(),
|
||||
"map".into(),
|
||||
Position::none(),
|
||||
)
|
||||
.into();
|
||||
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
|
||||
.into();
|
||||
}
|
||||
|
||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Box<EvalAltResult>> {
|
||||
#[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(),
|
||||
)
|
||||
.into();
|
||||
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "array".into(), NO_POS)
|
||||
.into();
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Box<EvalAltResult>> {
|
||||
@ -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,12 +345,8 @@ 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(),
|
||||
)
|
||||
.into();
|
||||
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
|
||||
.into();
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
@ -387,12 +370,8 @@ impl Serializer for &mut DynamicSerializer {
|
||||
map: Map::with_capacity(_len),
|
||||
});
|
||||
#[cfg(feature = "no_object")]
|
||||
return EvalAltResult::ErrorMismatchOutputType(
|
||||
"Dynamic".into(),
|
||||
"map".into(),
|
||||
Position::none(),
|
||||
)
|
||||
.into();
|
||||
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();
|
||||
|
@ -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,12 +20,8 @@ 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(),
|
||||
)
|
||||
.into()
|
||||
EvalAltResult::ErrorMismatchOutputType(type_name::<T>().into(), "string".into(), NO_POS)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
102
src/token.rs
102
src/token.rs
@ -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,
|
||||
))
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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(())
|
||||
|
@ -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(())
|
||||
|
@ -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]
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user