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.
|
* Custom syntax can no longer start with a keyword (even a _reserved_ one), even if it has been disabled. That is to avoid breaking scripts later when the keyword is no longer disabled.
|
||||||
* `EvalAltResult::ErrorAssignmentToUnknownLHS` is moved to `ParseError::AssignmentToInvalidLHS`. `ParseError::AssignmentToCopy` is removed.
|
* `EvalAltResult::ErrorAssignmentToUnknownLHS` is moved to `ParseError::AssignmentToInvalidLHS`. `ParseError::AssignmentToCopy` is removed.
|
||||||
|
* `EvalAltResult::ErrorDataTooLarge` is simplified.
|
||||||
|
* `Engine::on_progress` closure signature now returns `Option<Dynamic>` with the termination value passed on to `EvalAltResult::ErrorTerminated`.
|
||||||
|
* `ParseErrorType::BadInput` now wraps a `LexError` instead of a text string.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
@ -294,9 +294,8 @@ engine.register_custom_syntax_raw(
|
|||||||
"update" | "check" | "add" | "remove" => Ok(Some("$ident$".to_string())),
|
"update" | "check" | "add" | "remove" => Ok(Some("$ident$".to_string())),
|
||||||
"cleanup" => Ok(None),
|
"cleanup" => Ok(None),
|
||||||
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
||||||
format!("Improper command: {}", cmd))),
|
LexError::ImproperSymbol(format!("Improper command: {}", cmd))
|
||||||
Position::none(),
|
)), NO_POS)),
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
// perform command arg ...
|
// perform command arg ...
|
||||||
3 => match (stream[1].as_str(), stream[2].as_str()) {
|
3 => match (stream[1].as_str(), stream[2].as_str()) {
|
||||||
@ -308,9 +307,10 @@ engine.register_custom_syntax_raw(
|
|||||||
("add", arg) => Ok(None),
|
("add", arg) => Ok(None),
|
||||||
("remove", arg) => Ok(None),
|
("remove", arg) => Ok(None),
|
||||||
(cmd, arg) => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
(cmd, arg) => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
||||||
format!("Invalid argument for command {}: {}", cmd, arg))),
|
LexError::ImproperSymbol(
|
||||||
Position::none(),
|
format!("Invalid argument for command {}: {}", cmd, arg)
|
||||||
)),
|
)
|
||||||
|
)), NO_POS)),
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
@ -336,7 +336,7 @@ where:
|
|||||||
The return value is `Result<Option<String>, ParseError>` where:
|
The return value is `Result<Option<String>, ParseError>` where:
|
||||||
|
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `Ok(None)` | parsing complete and there are no more symbols to match |
|
| `Ok(None)` | parsing complete and there are no more symbols to match |
|
||||||
| `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` |
|
| `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` |
|
||||||
| `Err(ParseError)` | error that is reflected back to the [`Engine`].<br/>Normally this is `ParseError(ParseErrorType::BadInput(message), Position::none())` to indicate that there is a syntax error, but it can be any `ParseError`. |
|
| `Err(ParseError)` | error that is reflected back to the [`Engine`].<br/>Normally this is `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), NO_POS)` to indicate that there is a syntax error, but it can be any `ParseError`. |
|
||||||
|
@ -21,11 +21,11 @@ engine.on_var(|name, index, context| {
|
|||||||
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
||||||
// Override a variable - make it not found even if it exists!
|
// Override a variable - make it not found even if it exists!
|
||||||
"DO_NOT_USE" => Err(Box::new(
|
"DO_NOT_USE" => Err(Box::new(
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none())
|
EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS)
|
||||||
)),
|
)),
|
||||||
// Silently maps 'chameleon' into 'innocent'.
|
// Silently maps 'chameleon' into 'innocent'.
|
||||||
"chameleon" => context.scope.get_value("innocent").map(Some).ok_or_else(|| Box::new(
|
"chameleon" => context.scope.get_value("innocent").map(Some).ok_or_else(|| Box::new(
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none())
|
EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS)
|
||||||
)),
|
)),
|
||||||
// Return Ok(None) to continue with the normal variable resolution process.
|
// Return Ok(None) to continue with the normal variable resolution process.
|
||||||
_ => Ok(None)
|
_ => Ok(None)
|
||||||
@ -83,7 +83,7 @@ where:
|
|||||||
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where:
|
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where:
|
||||||
|
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] |
|
| `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] |
|
||||||
| `Ok(Some(Dynamic))` | value of the variable, treated as a constant |
|
| `Ok(Some(Dynamic))` | value of the variable, treated as a constant |
|
||||||
| `Err(Box<EvalAltResult>)` | error that is reflected back to the [`Engine`].<br/>Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, Position::none())` to indicate that the variable does not exist, but it can be any `EvalAltResult`. |
|
| `Err(Box<EvalAltResult>)` | error that is reflected back to the [`Engine`].<br/>Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, NO_POS)` to indicate that the variable does not exist, but it can be any `EvalAltResult`. |
|
||||||
|
@ -25,6 +25,14 @@ import "crypto_init"; // run the script file 'crypto_init.rhai' withou
|
|||||||
|
|
||||||
import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock'
|
import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock'
|
||||||
|
|
||||||
|
const SECRET_NUMBER = 42;
|
||||||
|
|
||||||
|
let mod_file = "crypto_" + SECRET_NUMBER;
|
||||||
|
|
||||||
|
import mod_file as my_mod; // load the script file "crypto_42.rhai" and import it as a module named 'my_mod'
|
||||||
|
// notice that module path names can be dynamically constructed!
|
||||||
|
// any expression that evaluates to a string is acceptable after the 'import' keyword
|
||||||
|
|
||||||
lock::encrypt(secret); // use functions defined under the module via '::'
|
lock::encrypt(secret); // use functions defined under the module via '::'
|
||||||
|
|
||||||
lock::hash::sha256(key); // sub-modules are also supported
|
lock::hash::sha256(key); // sub-modules are also supported
|
||||||
|
@ -117,7 +117,7 @@ pub fn greet(context: NativeCallContext, callback: FnPtr)
|
|||||||
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
|
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS};
|
||||||
use rhai::plugin::*; // a "prelude" import for macros
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
// This function builds an array of arbitrary size, but is protected
|
// This function builds an array of arbitrary size, but is protected
|
||||||
@ -137,7 +137,7 @@ pub fn grow(context: NativeCallContext, size: i64)
|
|||||||
"Size to grow".to_string(),
|
"Size to grow".to_string(),
|
||||||
context.engine().max_array_size(),
|
context.engine().max_array_size(),
|
||||||
size as usize,
|
size as usize,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
).into();
|
).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,7 +376,7 @@ mod my_module {
|
|||||||
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
|
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS};
|
||||||
use rhai::plugin::*; // a "prelude" import for macros
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
@ -397,7 +397,7 @@ mod my_module {
|
|||||||
"Size to grow".to_string(),
|
"Size to grow".to_string(),
|
||||||
context.engine().max_array_size(),
|
context.engine().max_array_size(),
|
||||||
size as usize,
|
size as usize,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
).into();
|
).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,13 +17,14 @@ engine.on_progress(|&count| { // parameter is '&u64' - number of operations al
|
|||||||
if count % 1000 == 0 {
|
if count % 1000 == 0 {
|
||||||
println!("{}", count); // print out a progress log every 1,000 operations
|
println!("{}", count); // print out a progress log every 1,000 operations
|
||||||
}
|
}
|
||||||
true // return 'true' to continue running the script
|
None // return 'None' to continue running the script
|
||||||
// return 'false' to immediately terminate the script
|
// return 'Some(token)' to immediately terminate the script
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
The closure passed to `Engine::on_progress` will be called once for every operation.
|
The closure passed to `Engine::on_progress` will be called once for every operation.
|
||||||
Return `false` to terminate the script immediately.
|
Return `Some(token)` to terminate the script immediately, with the provided value
|
||||||
|
(any [`Dynamic`] value) passed to `EvalAltResult::ErrorTerminated` as a termination token.
|
||||||
|
|
||||||
|
|
||||||
Operations Count vs. Progress Percentage
|
Operations Count vs. Progress Percentage
|
||||||
|
42
src/ast.rs
42
src/ast.rs
@ -861,24 +861,24 @@ pub struct BinaryExpr {
|
|||||||
/// A function call.
|
/// A function call.
|
||||||
#[derive(Debug, Clone, Hash, Default)]
|
#[derive(Debug, Clone, Hash, Default)]
|
||||||
pub struct FnCallInfo {
|
pub struct FnCallInfo {
|
||||||
/// Function name.
|
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
|
||||||
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
|
pub hash: u64,
|
||||||
/// and the function names are predictable, so no need to allocate a new `String`.
|
|
||||||
pub name: Cow<'static, str>,
|
|
||||||
/// Namespace of the function, if any.
|
|
||||||
pub namespace: Option<ModuleRef>,
|
|
||||||
/// Call native functions only? Set to `true` to skip searching for script-defined function overrides
|
/// Call native functions only? Set to `true` to skip searching for script-defined function overrides
|
||||||
/// when it is certain that the function must be native (e.g. an operator).
|
/// when it is certain that the function must be native (e.g. an operator).
|
||||||
pub native_only: bool,
|
pub native_only: bool,
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture: bool,
|
pub capture: bool,
|
||||||
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
|
|
||||||
pub hash: u64,
|
|
||||||
/// List of function call arguments.
|
|
||||||
pub args: StaticVec<Expr>,
|
|
||||||
/// Default value when the function is not found, mostly used to provide a default for comparison functions.
|
/// Default value when the function is not found, mostly used to provide a default for comparison functions.
|
||||||
/// Type is `bool` in order for `FnCallInfo` to be `Hash`
|
/// Type is `bool` in order for `FnCallInfo` to be `Hash`
|
||||||
pub def_value: Option<bool>,
|
pub def_value: Option<bool>,
|
||||||
|
/// Namespace of the function, if any.
|
||||||
|
pub namespace: Option<Box<ModuleRef>>,
|
||||||
|
/// Function name.
|
||||||
|
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
|
||||||
|
/// and the function names are predictable, so no need to allocate a new `String`.
|
||||||
|
pub name: Cow<'static, str>,
|
||||||
|
/// List of function call arguments.
|
||||||
|
pub args: StaticVec<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ An expression sub-tree.
|
/// _[INTERNALS]_ An expression sub-tree.
|
||||||
@ -903,10 +903,10 @@ pub enum Expr {
|
|||||||
StringConstant(Box<IdentX>),
|
StringConstant(Box<IdentX>),
|
||||||
/// FnPtr constant.
|
/// FnPtr constant.
|
||||||
FnPointer(Box<IdentX>),
|
FnPointer(Box<IdentX>),
|
||||||
/// Variable access - (variable name, optional modules, hash, optional index)
|
/// Variable access - (optional index, optional modules, hash, variable name)
|
||||||
Variable(Box<(Ident, Option<ModuleRef>, u64, Option<NonZeroUsize>)>),
|
Variable(Box<(Option<NonZeroUsize>, Option<Box<ModuleRef>>, u64, Ident)>),
|
||||||
/// Property access.
|
/// Property access - (getter, setter), prop
|
||||||
Property(Box<(IdentX, (String, String))>),
|
Property(Box<((String, String), IdentX)>),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Stmt(Box<Stmt>, Position),
|
Stmt(Box<Stmt>, Position),
|
||||||
/// Wrapped expression - should not be optimized away.
|
/// Wrapped expression - should not be optimized away.
|
||||||
@ -1014,7 +1014,7 @@ impl Expr {
|
|||||||
/// Is the expression a simple variable access?
|
/// Is the expression a simple variable access?
|
||||||
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
|
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.0).name.as_str()),
|
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.3).name.as_str()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1033,9 +1033,9 @@ impl Expr {
|
|||||||
Self::FnPointer(x) => x.pos,
|
Self::FnPointer(x) => x.pos,
|
||||||
Self::Array(_, pos) => *pos,
|
Self::Array(_, pos) => *pos,
|
||||||
Self::Map(_, pos) => *pos,
|
Self::Map(_, pos) => *pos,
|
||||||
Self::Property(x) => (x.0).pos,
|
Self::Property(x) => (x.1).pos,
|
||||||
Self::Stmt(_, pos) => *pos,
|
Self::Stmt(_, pos) => *pos,
|
||||||
Self::Variable(x) => (x.0).pos,
|
Self::Variable(x) => (x.3).pos,
|
||||||
Self::FnCall(_, pos) => *pos,
|
Self::FnCall(_, pos) => *pos,
|
||||||
|
|
||||||
Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(),
|
Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(),
|
||||||
@ -1064,8 +1064,8 @@ impl Expr {
|
|||||||
Self::FnPointer(x) => x.pos = new_pos,
|
Self::FnPointer(x) => x.pos = new_pos,
|
||||||
Self::Array(_, pos) => *pos = new_pos,
|
Self::Array(_, pos) => *pos = new_pos,
|
||||||
Self::Map(_, pos) => *pos = new_pos,
|
Self::Map(_, pos) => *pos = new_pos,
|
||||||
Self::Variable(x) => (x.0).pos = new_pos,
|
Self::Variable(x) => (x.3).pos = new_pos,
|
||||||
Self::Property(x) => (x.0).pos = new_pos,
|
Self::Property(x) => (x.1).pos = new_pos,
|
||||||
Self::Stmt(_, pos) => *pos = new_pos,
|
Self::Stmt(_, pos) => *pos = new_pos,
|
||||||
Self::FnCall(_, pos) => *pos = new_pos,
|
Self::FnCall(_, pos) => *pos = new_pos,
|
||||||
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
|
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
|
||||||
@ -1231,10 +1231,10 @@ impl Expr {
|
|||||||
pub(crate) fn into_property(self) -> Self {
|
pub(crate) fn into_property(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Variable(x) if x.1.is_none() => {
|
Self::Variable(x) if x.1.is_none() => {
|
||||||
let ident = x.0;
|
let ident = x.3;
|
||||||
let getter = make_getter(&ident.name);
|
let getter = make_getter(&ident.name);
|
||||||
let setter = make_setter(&ident.name);
|
let setter = make_setter(&ident.name);
|
||||||
Self::Property(Box::new((ident.into(), (getter, setter))))
|
Self::Property(Box::new(((getter, setter), ident.into())))
|
||||||
}
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
|
189
src/engine.rs
189
src/engine.rs
@ -11,7 +11,7 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::syntax::CustomSyntax;
|
use crate::syntax::CustomSyntax;
|
||||||
use crate::token::Position;
|
use crate::token::{Position, NO_POS};
|
||||||
use crate::{calc_native_fn_hash, StaticVec};
|
use crate::{calc_native_fn_hash, StaticVec};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -69,10 +69,58 @@ pub type Map = HashMap<ImmutableString, Dynamic>;
|
|||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
//
|
//
|
||||||
// Note - We cannot use &str or Cow<str> here because `eval` may load a module
|
// # Implementation Notes
|
||||||
// and the module name will live beyond the AST of the eval script text.
|
//
|
||||||
// The best we can do is a shared reference.
|
// We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
|
||||||
pub type Imports = Vec<(ImmutableString, Module)>;
|
// the AST of the eval script text. The best we can do is a shared reference.
|
||||||
|
//
|
||||||
|
// `Imports` is implemented as two `Vec`'s of exactly the same length. That's because a `Module` is large,
|
||||||
|
// so packing the import names together improves cache locality.
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Imports(StaticVec<Module>, StaticVec<ImmutableString>);
|
||||||
|
|
||||||
|
impl Imports {
|
||||||
|
/// Get the length of this stack of imported modules.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
/// Get the imported module at a particular index.
|
||||||
|
pub fn get(&self, index: usize) -> Option<&Module> {
|
||||||
|
self.0.get(index)
|
||||||
|
}
|
||||||
|
/// Get a mutable reference to the imported module at a particular index.
|
||||||
|
pub fn get_mut(&mut self, index: usize) -> Option<&mut Module> {
|
||||||
|
self.0.get_mut(index)
|
||||||
|
}
|
||||||
|
/// Get the index of an imported module by name.
|
||||||
|
pub fn find(&self, name: &str) -> Option<usize> {
|
||||||
|
self.1
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.rev()
|
||||||
|
.find(|(_, key)| key.as_str() == name)
|
||||||
|
.map(|(index, _)| index)
|
||||||
|
}
|
||||||
|
/// Push an imported module onto the stack.
|
||||||
|
pub fn push(&mut self, name: impl Into<ImmutableString>, module: Module) {
|
||||||
|
self.0.push(module);
|
||||||
|
self.1.push(name.into());
|
||||||
|
}
|
||||||
|
/// Truncate the stack of imported modules to a particular length.
|
||||||
|
pub fn truncate(&mut self, size: usize) {
|
||||||
|
self.0.truncate(size);
|
||||||
|
self.1.truncate(size);
|
||||||
|
}
|
||||||
|
/// Get an iterator to this stack of imported modules.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
|
self.1.iter().map(|name| name.as_str()).zip(self.0.iter())
|
||||||
|
}
|
||||||
|
/// Get a consuming iterator to this stack of imported modules.
|
||||||
|
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Module)> {
|
||||||
|
self.1.into_iter().zip(self.0.into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
@ -303,7 +351,7 @@ impl<'a> Target<'a> {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::StringChar(_, _, ch) => {
|
Self::StringChar(_, _, ch) => {
|
||||||
let char_value = ch.clone();
|
let char_value = ch.clone();
|
||||||
self.set_value((char_value, Position::none())).unwrap();
|
self.set_value((char_value, NO_POS)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -528,7 +576,7 @@ pub struct Engine {
|
|||||||
/// Callback closure for implementing the `debug` command.
|
/// Callback closure for implementing the `debug` command.
|
||||||
pub(crate) debug: Callback<str, ()>,
|
pub(crate) debug: Callback<str, ()>,
|
||||||
/// Callback closure for progress reporting.
|
/// Callback closure for progress reporting.
|
||||||
pub(crate) progress: Option<Callback<u64, bool>>,
|
pub(crate) progress: Option<Callback<u64, Option<Dynamic>>>,
|
||||||
|
|
||||||
/// Optimize the AST after compilation.
|
/// Optimize the AST after compilation.
|
||||||
pub(crate) optimization_level: OptimizationLevel,
|
pub(crate) optimization_level: OptimizationLevel,
|
||||||
@ -602,12 +650,10 @@ pub fn search_imports<'s>(
|
|||||||
|
|
||||||
Ok(if index > 0 {
|
Ok(if index > 0 {
|
||||||
let offset = mods.len() - index;
|
let offset = mods.len() - index;
|
||||||
&mods.get(offset).unwrap().1
|
mods.get(offset).expect("invalid index in Imports")
|
||||||
} else {
|
} else {
|
||||||
mods.iter()
|
mods.find(root)
|
||||||
.rev()
|
.map(|n| mods.get(n).expect("invalid index in Imports"))
|
||||||
.find(|(n, _)| n == root)
|
|
||||||
.map(|(_, m)| m)
|
|
||||||
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
|
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -630,12 +676,13 @@ pub fn search_imports_mut<'s>(
|
|||||||
|
|
||||||
Ok(if index > 0 {
|
Ok(if index > 0 {
|
||||||
let offset = mods.len() - index;
|
let offset = mods.len() - index;
|
||||||
&mut mods.get_mut(offset).unwrap().1
|
mods.get_mut(offset).expect("invalid index in Imports")
|
||||||
} else {
|
} else {
|
||||||
mods.iter_mut()
|
if let Some(n) = mods.find(root) {
|
||||||
.rev()
|
mods.get_mut(n)
|
||||||
.find(|(n, _)| n == root)
|
} else {
|
||||||
.map(|(_, m)| m)
|
None
|
||||||
|
}
|
||||||
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
|
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -765,7 +812,7 @@ impl Engine {
|
|||||||
match expr {
|
match expr {
|
||||||
Expr::Variable(v) => match v.as_ref() {
|
Expr::Variable(v) => match v.as_ref() {
|
||||||
// Qualified variable
|
// Qualified variable
|
||||||
(Ident { name, pos }, Some(modules), hash_var, _) => {
|
(_, Some(modules), hash_var, Ident { name, pos }) => {
|
||||||
let module = search_imports_mut(mods, state, modules)?;
|
let module = search_imports_mut(mods, state, modules)?;
|
||||||
let target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| {
|
let target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| {
|
||||||
match *err {
|
match *err {
|
||||||
@ -797,7 +844,7 @@ impl Engine {
|
|||||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||||
expr: &'a Expr,
|
expr: &'a Expr,
|
||||||
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
||||||
let (Ident { name, pos }, _, _, index) = match expr {
|
let (index, _, _, Ident { name, pos }) = match expr {
|
||||||
Expr::Variable(v) => v.as_ref(),
|
Expr::Variable(v) => v.as_ref(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
@ -937,7 +984,7 @@ impl Engine {
|
|||||||
let args = &mut [val, &mut idx_val2, &mut new_val.0];
|
let args = &mut [val, &mut idx_val2, &mut new_val.0];
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, FN_IDX_SET, 0, args, is_ref, true, false, None, &None,
|
state, lib, FN_IDX_SET, 0, args, is_ref, true, false, None, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
@ -946,7 +993,7 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
EvalAltResult::ErrorIndexingType(
|
EvalAltResult::ErrorIndexingType(
|
||||||
self.map_type_name(val_type_name).into(),
|
self.map_type_name(val_type_name).into(),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
err => err,
|
err => err,
|
||||||
@ -979,8 +1026,7 @@ impl Engine {
|
|||||||
let def_value = def_value.map(Into::<Dynamic>::into);
|
let def_value = def_value.map(Into::<Dynamic>::into);
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
self.make_method_call(
|
self.make_method_call(
|
||||||
state, lib, name, *hash, target, args, &def_value, *native, false,
|
state, lib, name, *hash, target, args, def_value, *native, false, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
}
|
}
|
||||||
@ -988,7 +1034,7 @@ impl Engine {
|
|||||||
Expr::FnCall(_, _) => unreachable!(),
|
Expr::FnCall(_, _) => unreachable!(),
|
||||||
// {xxx:map}.id = ???
|
// {xxx:map}.id = ???
|
||||||
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
|
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
|
||||||
let IdentX { name, pos } = &x.0;
|
let IdentX { name, pos } = &x.1;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
let mut val = self
|
let mut val = self
|
||||||
.get_indexed_mut(state, lib, target, index, *pos, true, false, level)?;
|
.get_indexed_mut(state, lib, target, index, *pos, true, false, level)?;
|
||||||
@ -998,7 +1044,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
Expr::Property(x) if target.is::<Map>() => {
|
Expr::Property(x) if target.is::<Map>() => {
|
||||||
let IdentX { name, pos } = &x.0;
|
let IdentX { name, pos } = &x.1;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
state, lib, target, index, *pos, false, false, level,
|
state, lib, target, index, *pos, false, false, level,
|
||||||
@ -1008,11 +1054,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx.id = ???
|
// xxx.id = ???
|
||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let (IdentX { pos, .. }, (_, setter)) = x.as_ref();
|
let ((_, setter), IdentX { pos, .. }) = x.as_ref();
|
||||||
let mut new_val = new_val;
|
let mut new_val = new_val;
|
||||||
let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0];
|
let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, setter, 0, &mut args, is_ref, true, false, None, &None,
|
state, lib, setter, 0, &mut args, is_ref, true, false, None, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, true))
|
.map(|(v, _)| (v, true))
|
||||||
@ -1020,10 +1066,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(x) => {
|
Expr::Property(x) => {
|
||||||
let (IdentX { pos, .. }, (getter, _)) = x.as_ref();
|
let ((getter, _), IdentX { pos, .. }) = x.as_ref();
|
||||||
let mut args = [target.as_mut()];
|
let mut args = [target.as_mut()];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, getter, 0, &mut args, is_ref, true, false, None, &None,
|
state, lib, getter, 0, &mut args, is_ref, true, false, None, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, false))
|
.map(|(v, _)| (v, false))
|
||||||
@ -1033,7 +1079,7 @@ impl Engine {
|
|||||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
|
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
|
||||||
let mut val = match &x.lhs {
|
let mut val = match &x.lhs {
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let IdentX { name, pos } = &p.0;
|
let IdentX { name, pos } = &p.1;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
self.get_indexed_mut(
|
self.get_indexed_mut(
|
||||||
state, lib, target, index, *pos, false, true, level,
|
state, lib, target, index, *pos, false, true, level,
|
||||||
@ -1052,7 +1098,7 @@ impl Engine {
|
|||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (val, _) = self
|
let (val, _) = self
|
||||||
.make_method_call(
|
.make_method_call(
|
||||||
state, lib, name, *hash, target, args, &def_value, *native,
|
state, lib, name, *hash, target, args, def_value, *native,
|
||||||
false, level,
|
false, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))?;
|
.map_err(|err| err.fill_position(*pos))?;
|
||||||
@ -1075,13 +1121,13 @@ impl Engine {
|
|||||||
match &x.lhs {
|
match &x.lhs {
|
||||||
// xxx.prop[expr] | xxx.prop.expr
|
// xxx.prop[expr] | xxx.prop.expr
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let (IdentX { pos, .. }, (getter, setter)) = p.as_ref();
|
let ((getter, setter), IdentX { pos, .. }) = p.as_ref();
|
||||||
let arg_values = &mut [target.as_mut(), &mut Default::default()];
|
let arg_values = &mut [target.as_mut(), &mut Default::default()];
|
||||||
let args = &mut arg_values[..1];
|
let args = &mut arg_values[..1];
|
||||||
let (mut val, updated) = self
|
let (mut val, updated) = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
state, lib, getter, 0, args, is_ref, true, false, None,
|
state, lib, getter, 0, args, is_ref, true, false, None,
|
||||||
&None, level,
|
None, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))?;
|
.map_err(|err| err.fill_position(*pos))?;
|
||||||
|
|
||||||
@ -1107,7 +1153,7 @@ impl Engine {
|
|||||||
arg_values[1] = val;
|
arg_values[1] = val;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, setter, 0, arg_values, is_ref, true, false,
|
state, lib, setter, 0, arg_values, is_ref, true, false,
|
||||||
None, &None, level,
|
None, None, level,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -1135,7 +1181,7 @@ impl Engine {
|
|||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (mut val, _) = self
|
let (mut val, _) = self
|
||||||
.make_method_call(
|
.make_method_call(
|
||||||
state, lib, name, *hash, target, args, &def_value, *native,
|
state, lib, name, *hash, target, args, def_value, *native,
|
||||||
false, level,
|
false, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))?;
|
.map_err(|err| err.fill_position(*pos))?;
|
||||||
@ -1210,7 +1256,7 @@ impl Engine {
|
|||||||
let Ident {
|
let Ident {
|
||||||
name: var_name,
|
name: var_name,
|
||||||
pos: var_pos,
|
pos: var_pos,
|
||||||
} = &x.0;
|
} = &x.3;
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(*var_pos))?;
|
.map_err(|err| err.fill_position(*var_pos))?;
|
||||||
@ -1411,25 +1457,21 @@ impl Engine {
|
|||||||
let mut idx = idx;
|
let mut idx = idx;
|
||||||
let args = &mut [val, &mut idx];
|
let args = &mut [val, &mut idx];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, _lib, FN_IDX_GET, 0, args, is_ref, true, false, None, &None, _level,
|
state, _lib, FN_IDX_GET, 0, args, is_ref, true, false, None, None, _level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
|
||||||
Box::new(EvalAltResult::ErrorIndexingType(
|
Box::new(EvalAltResult::ErrorIndexingType(type_name.into(), NO_POS))
|
||||||
type_name.into(),
|
|
||||||
Position::none(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
_ => err,
|
_ => err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => EvalAltResult::ErrorIndexingType(
|
_ => {
|
||||||
self.map_type_name(val.type_name()).into(),
|
EvalAltResult::ErrorIndexingType(self.map_type_name(val.type_name()).into(), NO_POS)
|
||||||
Position::none(),
|
.into()
|
||||||
)
|
}
|
||||||
.into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1461,13 +1503,14 @@ impl Engine {
|
|||||||
|
|
||||||
for value in rhs_value.iter_mut() {
|
for value in rhs_value.iter_mut() {
|
||||||
let args = &mut [&mut lhs_value.clone(), value];
|
let args = &mut [&mut lhs_value.clone(), value];
|
||||||
|
let def_value = def_value.clone();
|
||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||||
let hash =
|
let hash =
|
||||||
calc_native_fn_hash(empty(), OP_FUNC, args.iter().map(|a| a.type_id()));
|
calc_native_fn_hash(empty(), OP_FUNC, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.call_native_fn(state, lib, OP_FUNC, hash, args, false, false, &def_value)
|
.call_native_fn(state, lib, OP_FUNC, hash, args, false, false, def_value)
|
||||||
.map_err(|err| err.fill_position(rhs.position()))?
|
.map_err(|err| err.fill_position(rhs.position()))?
|
||||||
.0
|
.0
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -1477,7 +1520,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(def_value.unwrap())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(rhs_value)) => match lhs_value {
|
Dynamic(Union::Map(rhs_value)) => match lhs_value {
|
||||||
@ -1521,11 +1564,11 @@ impl Engine {
|
|||||||
Expr::FnPointer(x) => {
|
Expr::FnPointer(x) => {
|
||||||
Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into())
|
Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into())
|
||||||
}
|
}
|
||||||
Expr::Variable(x) if (x.0).name == KEYWORD_THIS => {
|
Expr::Variable(x) if (x.3).name == KEYWORD_THIS => {
|
||||||
if let Some(val) = this_ptr {
|
if let Some(val) = this_ptr {
|
||||||
Ok(val.clone())
|
Ok(val.clone())
|
||||||
} else {
|
} else {
|
||||||
EvalAltResult::ErrorUnboundThis((x.0).pos).into()
|
EvalAltResult::ErrorUnboundThis((x.3).pos).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Variable(_) => {
|
Expr::Variable(_) => {
|
||||||
@ -1580,7 +1623,7 @@ impl Engine {
|
|||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let def_value = def_value.map(Into::<Dynamic>::into);
|
let def_value = def_value.map(Into::<Dynamic>::into);
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, mods, state, lib, this_ptr, name, args, &def_value, *hash, *native,
|
scope, mods, state, lib, this_ptr, name, args, def_value, *hash, *native,
|
||||||
false, *cap_scope, level,
|
false, *cap_scope, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
@ -1596,8 +1639,9 @@ impl Engine {
|
|||||||
def_value,
|
def_value,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
|
let modules = namespace.as_ref().map(|v| v.as_ref());
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, *def_value, *hash,
|
scope, mods, state, lib, this_ptr, modules, name, args, *def_value, *hash,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
@ -1769,7 +1813,7 @@ impl Engine {
|
|||||||
// Run function
|
// Run function
|
||||||
let (value, _) = self
|
let (value, _) = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
state, lib, op, 0, args, false, false, false, None, &None,
|
state, lib, op, 0, args, false, false, false, None, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*op_pos))?;
|
.map_err(|err| err.fill_position(*op_pos))?;
|
||||||
@ -1807,7 +1851,7 @@ impl Engine {
|
|||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
state, lib, op, 0, args, false, false, false, None, &None, level,
|
state, lib, op, 0, args, false, false, false, None, None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| err.fill_position(*op_pos))?;
|
.map_err(|err| err.fill_position(*op_pos))?;
|
||||||
@ -1989,7 +2033,7 @@ impl Engine {
|
|||||||
let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err {
|
let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err {
|
||||||
x.clone()
|
x.clone()
|
||||||
} else {
|
} else {
|
||||||
err.set_position(Position::none());
|
err.set_position(NO_POS);
|
||||||
err.to_string().into()
|
err.to_string().into()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2070,7 +2114,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
unsafe_cast_var_name_to_lifetime(&var_def.name).into()
|
unsafe_cast_var_name_to_lifetime(&var_def.name).into()
|
||||||
};
|
};
|
||||||
scope.push_dynamic_value(var_name, entry_type, val, false);
|
scope.push_dynamic_value(var_name, entry_type, val);
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2092,7 +2136,7 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(name_def) = alias {
|
if let Some(name_def) = alias {
|
||||||
module.index_all_sub_modules();
|
module.index_all_sub_modules();
|
||||||
mods.push((name_def.name.clone(), module));
|
mods.push(name_def.name.clone(), module);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.modules += 1;
|
state.modules += 1;
|
||||||
@ -2251,34 +2295,17 @@ impl Engine {
|
|||||||
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
|
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
|
||||||
|
|
||||||
if s > self.max_string_size() {
|
if s > self.max_string_size() {
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into();
|
||||||
"Length of string".to_string(),
|
|
||||||
self.max_string_size(),
|
|
||||||
s,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if _arr > self.max_array_size() {
|
if _arr > self.max_array_size() {
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), NO_POS).into();
|
||||||
"Size of array".to_string(),
|
|
||||||
self.max_array_size(),
|
|
||||||
_arr,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if _map > self.max_map_size() {
|
if _map > self.max_map_size() {
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), NO_POS)
|
||||||
"Number of properties in object map".to_string(),
|
|
||||||
self.max_map_size(),
|
|
||||||
_map,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2293,14 +2320,14 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
// Guard against too many operations
|
// Guard against too many operations
|
||||||
if self.max_operations() > 0 && state.operations > self.max_operations() {
|
if self.max_operations() > 0 && state.operations > self.max_operations() {
|
||||||
return EvalAltResult::ErrorTooManyOperations(Position::none()).into();
|
return EvalAltResult::ErrorTooManyOperations(NO_POS).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report progress - only in steps
|
// Report progress - only in steps
|
||||||
if let Some(progress) = &self.progress {
|
if let Some(progress) = &self.progress {
|
||||||
if !progress(&state.operations) {
|
if let Some(token) = progress(&state.operations) {
|
||||||
// Terminate script if progress returns false
|
// Terminate script if progress returns a termination token
|
||||||
return EvalAltResult::ErrorTerminated(Position::none()).into();
|
return EvalAltResult::ErrorTerminated(token, NO_POS).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use crate::optimize::OptimizationLevel;
|
|||||||
use crate::parse_error::ParseError;
|
use crate::parse_error::ParseError;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::token::Position;
|
use crate::token::{Position, NO_POS};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -1391,7 +1391,7 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<T>()).into(),
|
self.map_type_name(type_name::<T>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
@ -1527,7 +1527,7 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<T>()).into(),
|
self.map_type_name(type_name::<T>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
@ -1617,7 +1617,7 @@ impl Engine {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let fn_def = lib
|
let fn_def = lib
|
||||||
.get_script_fn(name, args.len(), true)
|
.get_script_fn(name, args.len(), true)
|
||||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?;
|
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?;
|
||||||
|
|
||||||
let mut state = Default::default();
|
let mut state = Default::default();
|
||||||
let mut mods = Default::default();
|
let mut mods = Default::default();
|
||||||
@ -1732,12 +1732,12 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// engine.on_progress(move |&ops| {
|
/// engine.on_progress(move |&ops| {
|
||||||
/// if ops > 10000 {
|
/// if ops > 10000 {
|
||||||
/// false
|
/// Some("Over 10,000 operations!".into())
|
||||||
/// } else if ops % 800 == 0 {
|
/// } else if ops % 800 == 0 {
|
||||||
/// *logger.write().unwrap() = ops;
|
/// *logger.write().unwrap() = ops;
|
||||||
/// true
|
/// None
|
||||||
/// } else {
|
/// } else {
|
||||||
/// true
|
/// None
|
||||||
/// }
|
/// }
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
@ -1752,7 +1752,7 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_progress(
|
pub fn on_progress(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&u64) -> bool + SendSync + 'static,
|
callback: impl Fn(&u64) -> Option<Dynamic> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.progress = Some(Box::new(callback));
|
self.progress = Some(Box::new(callback));
|
||||||
self
|
self
|
||||||
|
@ -14,7 +14,7 @@ use crate::parse_error::ParseErrorType;
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::stdlib::ops::Deref;
|
use crate::stdlib::ops::Deref;
|
||||||
use crate::token::Position;
|
use crate::token::NO_POS;
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec, INT};
|
use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec, INT};
|
||||||
|
|
||||||
@ -36,10 +36,6 @@ use crate::engine::{Map, Target, FN_GET, FN_SET};
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
use crate::engine::KEYWORD_IS_SHARED;
|
use crate::engine::KEYWORD_IS_SHARED;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
use crate::scope::Entry as ScopeEntry;
|
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -163,7 +159,7 @@ pub fn ensure_no_data_race(
|
|||||||
{
|
{
|
||||||
return EvalAltResult::ErrorDataRace(
|
return EvalAltResult::ErrorDataRace(
|
||||||
format!("argument #{} of function '{}'", n + 1 + skip, fn_name),
|
format!("argument #{} of function '{}'", n + 1 + skip, fn_name),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -190,7 +186,7 @@ impl Engine {
|
|||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
def_val: &Option<Dynamic>,
|
def_val: Option<Dynamic>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
self.inc_operations(state)?;
|
self.inc_operations(state)?;
|
||||||
|
|
||||||
@ -227,7 +223,7 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
})?)
|
})?)
|
||||||
.into(),
|
.into(),
|
||||||
@ -238,7 +234,7 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
})?)
|
})?)
|
||||||
.into(),
|
.into(),
|
||||||
@ -258,7 +254,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Return default value (if any)
|
// Return default value (if any)
|
||||||
if let Some(val) = def_val {
|
if let Some(val) = def_val {
|
||||||
return Ok((val.clone(), false));
|
return Ok((val, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getter function not found?
|
// Getter function not found?
|
||||||
@ -269,7 +265,7 @@ impl Engine {
|
|||||||
prop,
|
prop,
|
||||||
self.map_type_name(args[0].type_name())
|
self.map_type_name(args[0].type_name())
|
||||||
),
|
),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -283,7 +279,7 @@ impl Engine {
|
|||||||
self.map_type_name(args[0].type_name()),
|
self.map_type_name(args[0].type_name()),
|
||||||
self.map_type_name(args[1].type_name()),
|
self.map_type_name(args[1].type_name()),
|
||||||
),
|
),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -297,7 +293,7 @@ impl Engine {
|
|||||||
self.map_type_name(args[0].type_name()),
|
self.map_type_name(args[0].type_name()),
|
||||||
self.map_type_name(args[1].type_name()),
|
self.map_type_name(args[1].type_name()),
|
||||||
),
|
),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -311,7 +307,7 @@ impl Engine {
|
|||||||
self.map_type_name(args[0].type_name()),
|
self.map_type_name(args[0].type_name()),
|
||||||
self.map_type_name(args[1].type_name()),
|
self.map_type_name(args[1].type_name()),
|
||||||
),
|
),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -330,7 +326,7 @@ impl Engine {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@ -361,9 +357,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if level > self.max_call_levels() {
|
if level > self.max_call_levels() {
|
||||||
return Err(Box::new(
|
return Err(Box::new(EvalAltResult::ErrorStackOverflow(NO_POS)));
|
||||||
EvalAltResult::ErrorStackOverflow(Position::none()),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_scope_level = state.scope_level;
|
let orig_scope_level = state.scope_level;
|
||||||
@ -400,8 +394,8 @@ impl Engine {
|
|||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
let stmt = &fn_def.body;
|
let stmt = &fn_def.body;
|
||||||
|
|
||||||
let result = self
|
let result =
|
||||||
.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1)
|
self.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Convert return statement to return value
|
// Convert return statement to return value
|
||||||
EvalAltResult::Return(x, _) => Ok(x),
|
EvalAltResult::Return(x, _) => Ok(x),
|
||||||
@ -409,18 +403,14 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorInFunctionCall(
|
EvalAltResult::ErrorInFunctionCall(
|
||||||
format!("{} > {}", fn_def.name, name),
|
format!("{} > {}", fn_def.name, name),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
// System errors are passed straight-through
|
// System errors are passed straight-through
|
||||||
err if err.is_system_exception() => Err(Box::new(err)),
|
err if err.is_system_exception() => Err(Box::new(err)),
|
||||||
// Other errors are wrapped in `ErrorInFunctionCall`
|
// Other errors are wrapped in `ErrorInFunctionCall`
|
||||||
_ => EvalAltResult::ErrorInFunctionCall(
|
_ => EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, NO_POS)
|
||||||
fn_def.name.to_string(),
|
|
||||||
err,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into(),
|
.into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -489,7 +479,7 @@ impl Engine {
|
|||||||
_is_method: bool,
|
_is_method: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
_capture_scope: Option<Scope>,
|
_capture_scope: Option<Scope>,
|
||||||
def_val: &Option<Dynamic>,
|
def_val: Option<Dynamic>,
|
||||||
_level: usize,
|
_level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
@ -523,7 +513,7 @@ impl Engine {
|
|||||||
fn_name, fn_name
|
fn_name, fn_name
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@ -553,22 +543,14 @@ impl Engine {
|
|||||||
if let Some(captured) = _capture_scope {
|
if let Some(captured) = _capture_scope {
|
||||||
captured
|
captured
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|ScopeEntry { name, .. }| {
|
.filter(|(name, _, _, _)| func.externals.contains(name.as_ref()))
|
||||||
func.externals.contains(name.as_ref())
|
.for_each(|(name, typ, value, _)| {
|
||||||
})
|
|
||||||
.for_each(
|
|
||||||
|ScopeEntry {
|
|
||||||
name, typ, value, ..
|
|
||||||
}| {
|
|
||||||
// Consume the scope values.
|
// Consume the scope values.
|
||||||
match typ {
|
match typ {
|
||||||
ScopeEntryType::Normal => scope.push(name, value),
|
ScopeEntryType::Normal => scope.push(name, value),
|
||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => scope.push_constant(name, value),
|
||||||
scope.push_constant(name, value)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = if _is_method {
|
let result = if _is_method {
|
||||||
@ -668,9 +650,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _level > self.max_call_levels() {
|
if _level > self.max_call_levels() {
|
||||||
return Err(Box::new(
|
return Err(Box::new(EvalAltResult::ErrorStackOverflow(NO_POS)));
|
||||||
EvalAltResult::ErrorStackOverflow(Position::none()),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the script text
|
// Compile the script text
|
||||||
@ -706,7 +686,7 @@ impl Engine {
|
|||||||
hash_script: u64,
|
hash_script: u64,
|
||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
mut call_args: StaticVec<Dynamic>,
|
mut call_args: StaticVec<Dynamic>,
|
||||||
def_val: &Option<Dynamic>,
|
def_val: Option<Dynamic>,
|
||||||
native: bool,
|
native: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -857,7 +837,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
name: &str,
|
name: &str,
|
||||||
args_expr: impl AsRef<[Expr]>,
|
args_expr: impl AsRef<[Expr]>,
|
||||||
def_val: &Option<Dynamic>,
|
def_val: Option<Dynamic>,
|
||||||
mut hash_script: u64,
|
mut hash_script: u64,
|
||||||
native: bool,
|
native: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
@ -1094,7 +1074,7 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
modules: &Option<ModuleRef>,
|
modules: Option<&ModuleRef>,
|
||||||
name: &str,
|
name: &str,
|
||||||
args_expr: impl AsRef<[Expr]>,
|
args_expr: impl AsRef<[Expr]>,
|
||||||
def_val: Option<bool>,
|
def_val: Option<bool>,
|
||||||
@ -1230,7 +1210,7 @@ impl Engine {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use crate::engine::{Engine, EvalContext};
|
|||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::plugin::PluginFunction;
|
use crate::plugin::PluginFunction;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::{is_valid_identifier, Position};
|
use crate::token::{is_valid_identifier, NO_POS};
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
use crate::{calc_script_fn_hash, StaticVec};
|
use crate::{calc_script_fn_hash, StaticVec};
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ impl FnPtr {
|
|||||||
/// clone them _before_ calling this function.
|
/// clone them _before_ calling this function.
|
||||||
pub fn call_dynamic(
|
pub fn call_dynamic(
|
||||||
&self,
|
&self,
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -182,11 +182,10 @@ impl FnPtr {
|
|||||||
args.insert(0, obj);
|
args.insert(0, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
context
|
ctx.engine()
|
||||||
.engine()
|
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
context.lib,
|
ctx.lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
hash_script,
|
hash_script,
|
||||||
args.as_mut(),
|
args.as_mut(),
|
||||||
@ -194,7 +193,7 @@ impl FnPtr {
|
|||||||
has_this,
|
has_this,
|
||||||
true,
|
true,
|
||||||
None,
|
None,
|
||||||
&None,
|
None,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
@ -216,7 +215,7 @@ impl TryFrom<ImmutableString> for FnPtr {
|
|||||||
if is_valid_identifier(value.chars()) {
|
if is_valid_identifier(value.chars()) {
|
||||||
Ok(Self(value, Default::default()))
|
Ok(Self(value, Default::default()))
|
||||||
} else {
|
} else {
|
||||||
EvalAltResult::ErrorFunctionNotFound(value.into(), Position::none()).into()
|
EvalAltResult::ErrorFunctionNotFound(value.into(), NO_POS).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,11 +115,11 @@ pub use engine::{Engine, EvalContext};
|
|||||||
pub use fn_native::{FnPtr, NativeCallContext};
|
pub use fn_native::{FnPtr, NativeCallContext};
|
||||||
pub use fn_register::{RegisterFn, RegisterResultFn};
|
pub use fn_register::{RegisterFn, RegisterResultFn};
|
||||||
pub use module::Module;
|
pub use module::Module;
|
||||||
pub use parse_error::{ParseError, ParseErrorType};
|
pub use parse_error::{LexError, ParseError, ParseErrorType};
|
||||||
pub use result::EvalAltResult;
|
pub use result::EvalAltResult;
|
||||||
pub use scope::Scope;
|
pub use scope::Scope;
|
||||||
pub use syntax::Expression;
|
pub use syntax::Expression;
|
||||||
pub use token::Position;
|
pub use token::{Position, NO_POS};
|
||||||
pub use utils::ImmutableString;
|
pub use utils::ImmutableString;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -5,7 +5,7 @@ use crate::dynamic::{Dynamic, Variant};
|
|||||||
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync};
|
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync};
|
||||||
use crate::fn_register::by_value as cast_arg;
|
use crate::fn_register::by_value as cast_arg;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::{Position, Token};
|
use crate::token::{Token, NO_POS};
|
||||||
use crate::utils::{ImmutableString, StraightHasherBuilder};
|
use crate::utils::{ImmutableString, StraightHasherBuilder};
|
||||||
use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec};
|
use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec};
|
||||||
|
|
||||||
@ -13,11 +13,7 @@ use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec};
|
|||||||
use crate::{ast::ScriptFnDef, fn_native::Shared};
|
use crate::{ast::ScriptFnDef, fn_native::Shared};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::{
|
use crate::{ast::AST, engine::Engine, scope::Scope};
|
||||||
ast::AST,
|
|
||||||
engine::{Engine, Imports},
|
|
||||||
scope::{Entry as ScopeEntry, Scope},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
|
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
|
||||||
@ -275,11 +271,11 @@ impl Module {
|
|||||||
hash_var: u64,
|
hash_var: u64,
|
||||||
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
||||||
if hash_var == 0 {
|
if hash_var == 0 {
|
||||||
Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into())
|
Err(EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into())
|
||||||
} else {
|
} else {
|
||||||
self.all_variables.get_mut(&hash_var).ok_or_else(|| {
|
self.all_variables
|
||||||
EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into()
|
.get_mut(&hash_var)
|
||||||
})
|
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,9 +529,8 @@ impl Module {
|
|||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |context: NativeCallContext, args: &mut FnCallArgs| {
|
let f =
|
||||||
func(context, args).map(Dynamic::from)
|
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
|
||||||
};
|
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
name,
|
name,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
@ -1340,7 +1335,7 @@ impl Module {
|
|||||||
ast: &AST,
|
ast: &AST,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
) -> Result<Self, Box<EvalAltResult>> {
|
) -> Result<Self, Box<EvalAltResult>> {
|
||||||
let mut mods = Imports::new();
|
let mut mods = Default::default();
|
||||||
|
|
||||||
// Run the script
|
// Run the script
|
||||||
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
|
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
|
||||||
@ -1348,12 +1343,10 @@ impl Module {
|
|||||||
// Create new module
|
// Create new module
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
scope
|
scope.into_iter().for_each(|(_, _, value, alias)| {
|
||||||
.into_iter()
|
|
||||||
.for_each(|ScopeEntry { value, alias, .. }| {
|
|
||||||
// Variables with an alias left in the scope become module variables
|
// Variables with an alias left in the scope become module variables
|
||||||
if let Some(alias) = alias {
|
if let Some(alias) = alias {
|
||||||
module.variables.insert(*alias, value);
|
module.variables.insert(alias, value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ use crate::engine::{
|
|||||||
use crate::fn_call::run_builtin_binary_op;
|
use crate::fn_call::run_builtin_binary_op;
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::parser::map_dynamic_to_expr;
|
use crate::parser::map_dynamic_to_expr;
|
||||||
use crate::scope::{Entry as ScopeEntry, Scope};
|
use crate::scope::Scope;
|
||||||
use crate::token::{is_valid_identifier, Position};
|
use crate::token::{is_valid_identifier, NO_POS};
|
||||||
use crate::{calc_native_fn_hash, StaticVec};
|
use crate::{calc_native_fn_hash, StaticVec};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -147,7 +147,7 @@ fn call_fn_with_constant_arguments(
|
|||||||
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
&None,
|
None,
|
||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
@ -156,11 +156,16 @@ fn call_fn_with_constant_arguments(
|
|||||||
/// Optimize a statement.
|
/// Optimize a statement.
|
||||||
fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
||||||
match stmt {
|
match stmt {
|
||||||
// id op= expr
|
// expr op= expr
|
||||||
Stmt::Assignment(x, pos) => Stmt::Assignment(
|
Stmt::Assignment(x, pos) => match x.0 {
|
||||||
|
Expr::Variable(_) => {
|
||||||
|
Stmt::Assignment(Box::new((x.0, x.1, optimize_expr(x.2, state))), pos)
|
||||||
|
}
|
||||||
|
_ => Stmt::Assignment(
|
||||||
Box::new((optimize_expr(x.0, state), x.1, optimize_expr(x.2, state))),
|
Box::new((optimize_expr(x.0, state), x.1, optimize_expr(x.2, state))),
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
|
},
|
||||||
// if false { if_block } -> Noop
|
// if false { if_block } -> Noop
|
||||||
Stmt::IfThenElse(Expr::False(pos), x, _) if x.1.is_none() => {
|
Stmt::IfThenElse(Expr::False(pos), x, _) if x.1.is_none() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
@ -454,7 +459,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
Expr::Dot(x, dot_pos) => match (x.lhs, x.rhs) {
|
Expr::Dot(x, dot_pos) => match (x.lhs, x.rhs) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
let prop = &p.0.name;
|
let prop = &p.1.name;
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
@ -462,6 +467,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
.map(|(_, mut expr)| { expr.set_position(pos); expr })
|
.map(|(_, mut expr)| { expr.set_position(pos); expr })
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
|
// var.rhs
|
||||||
|
(lhs @ Expr::Variable(_), rhs) => Expr::Dot(Box::new(BinaryExpr {
|
||||||
|
lhs,
|
||||||
|
rhs: optimize_expr(rhs, state),
|
||||||
|
}), dot_pos),
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
(lhs, rhs) => Expr::Dot(Box::new(BinaryExpr {
|
(lhs, rhs) => Expr::Dot(Box::new(BinaryExpr {
|
||||||
lhs: optimize_expr(lhs, state),
|
lhs: optimize_expr(lhs, state),
|
||||||
@ -498,6 +508,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Expr::CharConstant(s.name.chars().nth(i as usize).unwrap(), s.pos)
|
Expr::CharConstant(s.name.chars().nth(i as usize).unwrap(), s.pos)
|
||||||
}
|
}
|
||||||
|
// var[rhs]
|
||||||
|
(lhs @ Expr::Variable(_), rhs) => Expr::Index(Box::new(BinaryExpr {
|
||||||
|
lhs,
|
||||||
|
rhs: optimize_expr(rhs, state),
|
||||||
|
}), idx_pos),
|
||||||
// lhs[rhs]
|
// lhs[rhs]
|
||||||
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr {
|
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr {
|
||||||
lhs: optimize_expr(lhs, state),
|
lhs: optimize_expr(lhs, state),
|
||||||
@ -686,12 +701,12 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.0.name) => {
|
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.3.name) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
let mut expr = state.find_constant(&x.0.name).unwrap().clone();
|
let mut expr = state.find_constant(&x.3.name).unwrap().clone();
|
||||||
expr.set_position(x.0.pos);
|
expr.set_position(x.3.pos);
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,24 +736,15 @@ fn optimize(
|
|||||||
// Set up the state
|
// Set up the state
|
||||||
let mut state = State::new(engine, lib, level);
|
let mut state = State::new(engine, lib, level);
|
||||||
|
|
||||||
// Add constants from the scope into the state
|
// Add constants from the scope that can be made into a literal into the state
|
||||||
scope
|
scope
|
||||||
.to_iter()
|
.iter()
|
||||||
// Get all the constants that can be made into a constant literal.
|
.filter(|(_, typ, _)| *typ)
|
||||||
.filter(|ScopeEntry { typ, .. }| typ.is_constant())
|
.for_each(|(name, _, value)| {
|
||||||
.for_each(
|
if let Some(val) = map_dynamic_to_expr(value, NO_POS) {
|
||||||
|ScopeEntry {
|
state.push_constant(name, val);
|
||||||
name, expr, value, ..
|
|
||||||
}| {
|
|
||||||
if let Some(val) = expr
|
|
||||||
.as_ref()
|
|
||||||
.map(|expr| expr.as_ref().clone())
|
|
||||||
.or_else(|| map_dynamic_to_expr(value.clone(), Position::none()))
|
|
||||||
{
|
|
||||||
state.push_constant(name.as_ref(), val);
|
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
let orig_constants_len = state.constants.len();
|
let orig_constants_len = state.constants.len();
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use crate::def_package;
|
|||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::INT;
|
use crate::INT;
|
||||||
|
|
||||||
use crate::{result::EvalAltResult, token::Position};
|
use crate::{result::EvalAltResult, token::NO_POS};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
@ -17,7 +17,7 @@ use crate::stdlib::{format, string::String};
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn make_err(msg: impl Into<String>) -> Box<EvalAltResult> {
|
pub fn make_err(msg: impl Into<String>) -> Box<EvalAltResult> {
|
||||||
EvalAltResult::ErrorArithmetic(msg.into(), Position::none()).into()
|
EvalAltResult::ErrorArithmetic(msg.into(), NO_POS).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! gen_arithmetic_functions {
|
macro_rules! gen_arithmetic_functions {
|
||||||
|
@ -7,7 +7,7 @@ use crate::engine::Array;
|
|||||||
use crate::fn_native::{FnPtr, NativeCallContext};
|
use crate::fn_native::{FnPtr, NativeCallContext};
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::NO_POS;
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
use crate::INT;
|
use crate::INT;
|
||||||
|
|
||||||
@ -41,12 +41,12 @@ macro_rules! gen_array_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn pad(_context: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn pad(_ctx: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
// Check if array will be over max size limit
|
// Check if array will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _context.engine().max_array_size() > 0 && len > 0 && (len as usize) > _context.engine().max_array_size() {
|
if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() {
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
"Size of array".to_string(), _context.engine().max_array_size(), len as usize, Position::none(),
|
"Size of array".to_string(), NO_POS
|
||||||
).into();
|
).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn map(
|
pub fn map(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
mapper: FnPtr,
|
mapper: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -206,12 +206,12 @@ mod array_functions {
|
|||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
array.push(
|
array.push(
|
||||||
mapper
|
mapper
|
||||||
.call_dynamic(context, None, [item.clone()])
|
.call_dynamic(ctx, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(mapper.fn_name()) =>
|
if fn_sig.starts_with(mapper.fn_name()) =>
|
||||||
{
|
{
|
||||||
mapper.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
mapper.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -219,7 +219,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"map".to_string(),
|
"map".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?,
|
})?,
|
||||||
);
|
);
|
||||||
@ -229,7 +229,7 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn filter(
|
pub fn filter(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
filter: FnPtr,
|
filter: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -237,12 +237,12 @@ mod array_functions {
|
|||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if filter
|
if filter
|
||||||
.call_dynamic(context, None, [item.clone()])
|
.call_dynamic(ctx, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
{
|
{
|
||||||
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -250,7 +250,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"filter".to_string(),
|
"filter".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -264,18 +264,18 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn some(
|
pub fn some(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
filter: FnPtr,
|
filter: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if filter
|
if filter
|
||||||
.call_dynamic(context, None, [item.clone()])
|
.call_dynamic(ctx, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
{
|
{
|
||||||
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -283,7 +283,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"filter".to_string(),
|
"filter".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -297,18 +297,18 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn all(
|
pub fn all(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
filter: FnPtr,
|
filter: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if !filter
|
if !filter
|
||||||
.call_dynamic(context, None, [item.clone()])
|
.call_dynamic(ctx, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
{
|
{
|
||||||
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -316,7 +316,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"filter".to_string(),
|
"filter".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -330,7 +330,7 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn reduce(
|
pub fn reduce(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
reducer: FnPtr,
|
reducer: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -338,16 +338,12 @@ mod array_functions {
|
|||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(context, None, [result.clone(), item.clone()])
|
.call_dynamic(ctx, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
{
|
{
|
||||||
reducer.call_dynamic(
|
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
|
||||||
context,
|
|
||||||
None,
|
|
||||||
[result, item.clone(), (i as INT).into()],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -355,7 +351,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -364,31 +360,27 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(name = "reduce", return_raw)]
|
#[rhai_fn(name = "reduce", return_raw)]
|
||||||
pub fn reduce_with_initial(
|
pub fn reduce_with_initial(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
reducer: FnPtr,
|
reducer: FnPtr,
|
||||||
initial: FnPtr,
|
initial: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
|
let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
|
||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(context, None, [result.clone(), item.clone()])
|
.call_dynamic(ctx, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
{
|
{
|
||||||
reducer.call_dynamic(
|
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
|
||||||
context,
|
|
||||||
None,
|
|
||||||
[result, item.clone(), (i as INT).into()],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -396,7 +388,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -405,7 +397,7 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn reduce_rev(
|
pub fn reduce_rev(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
reducer: FnPtr,
|
reducer: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -413,16 +405,12 @@ mod array_functions {
|
|||||||
|
|
||||||
for (i, item) in list.iter().enumerate().rev() {
|
for (i, item) in list.iter().enumerate().rev() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(context, None, [result.clone(), item.clone()])
|
.call_dynamic(ctx, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
{
|
{
|
||||||
reducer.call_dynamic(
|
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
|
||||||
context,
|
|
||||||
None,
|
|
||||||
[result, item.clone(), (i as INT).into()],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -430,7 +418,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -439,31 +427,27 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(name = "reduce_rev", return_raw)]
|
#[rhai_fn(name = "reduce_rev", return_raw)]
|
||||||
pub fn reduce_rev_with_initial(
|
pub fn reduce_rev_with_initial(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
reducer: FnPtr,
|
reducer: FnPtr,
|
||||||
initial: FnPtr,
|
initial: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
|
let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
|
||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate().rev() {
|
for (i, item) in list.iter().enumerate().rev() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(context, None, [result.clone(), item.clone()])
|
.call_dynamic(ctx, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
{
|
{
|
||||||
reducer.call_dynamic(
|
reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()])
|
||||||
context,
|
|
||||||
None,
|
|
||||||
[result, item.clone(), (i as INT).into()],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -471,7 +455,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -480,13 +464,13 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn sort(
|
pub fn sort(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
comparer: FnPtr,
|
comparer: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
list.sort_by(|x, y| {
|
list.sort_by(|x, y| {
|
||||||
comparer
|
comparer
|
||||||
.call_dynamic(context, None, [x.clone(), y.clone()])
|
.call_dynamic(ctx, None, [x.clone(), y.clone()])
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|v| v.as_int().ok())
|
.and_then(|v| v.as_int().ok())
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
@ -516,7 +500,7 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn drain(
|
pub fn drain(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
filter: FnPtr,
|
filter: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -528,12 +512,12 @@ mod array_functions {
|
|||||||
i -= 1;
|
i -= 1;
|
||||||
|
|
||||||
if filter
|
if filter
|
||||||
.call_dynamic(context, None, [list[i].clone()])
|
.call_dynamic(ctx, None, [list[i].clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
{
|
{
|
||||||
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
|
filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -541,7 +525,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"filter".to_string(),
|
"filter".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -575,7 +559,7 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn retain(
|
pub fn retain(
|
||||||
context: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
filter: FnPtr,
|
filter: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -587,12 +571,12 @@ mod array_functions {
|
|||||||
i -= 1;
|
i -= 1;
|
||||||
|
|
||||||
if !filter
|
if !filter
|
||||||
.call_dynamic(context, None, [list[i].clone()])
|
.call_dynamic(ctx, None, [list[i].clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
{
|
{
|
||||||
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
|
filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -600,7 +584,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"filter".to_string(),
|
"filter".to_string(),
|
||||||
err,
|
err,
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::token::Position;
|
use crate::token::NO_POS;
|
||||||
use crate::INT;
|
use crate::INT;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -85,10 +85,7 @@ mod int_functions {
|
|||||||
#[rhai_fn(name = "parse_int", return_raw)]
|
#[rhai_fn(name = "parse_int", return_raw)]
|
||||||
pub fn parse_int_radix(s: &str, radix: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn parse_int_radix(s: &str, radix: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if radix < 2 || radix > 36 {
|
if radix < 2 || radix > 36 {
|
||||||
return EvalAltResult::ErrorArithmetic(
|
return EvalAltResult::ErrorArithmetic(format!("Invalid radix: '{}'", radix), NO_POS)
|
||||||
format!("Invalid radix: '{}'", radix),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +94,7 @@ mod int_functions {
|
|||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
format!("Error parsing integer number '{}': {}", s, err),
|
format!("Error parsing integer number '{}': {}", s, err),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
@ -206,10 +203,7 @@ mod float_functions {
|
|||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) {
|
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS)
|
||||||
format!("Integer overflow: to_int({})", x),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
Ok((x.trunc() as INT).into())
|
Ok((x.trunc() as INT).into())
|
||||||
@ -218,10 +212,7 @@ mod float_functions {
|
|||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
|
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS)
|
||||||
format!("Integer overflow: to_int({})", x),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
Ok((x.trunc() as INT).into())
|
Ok((x.trunc() as INT).into())
|
||||||
@ -235,7 +226,7 @@ mod float_functions {
|
|||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
format!("Error parsing floating-point number '{}': {}", s, err),
|
format!("Error parsing floating-point number '{}': {}", s, err),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,7 @@ use crate::StaticVec;
|
|||||||
use crate::INT;
|
use crate::INT;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
use crate::{result::EvalAltResult, token::Position};
|
use crate::{result::EvalAltResult, token::NO_POS};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec,
|
any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec,
|
||||||
@ -251,23 +251,15 @@ mod string_functions {
|
|||||||
|
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn pad(
|
pub fn pad(
|
||||||
_context: NativeCallContext,
|
_ctx: NativeCallContext,
|
||||||
s: &mut ImmutableString,
|
s: &mut ImmutableString,
|
||||||
len: INT,
|
len: INT,
|
||||||
ch: char,
|
ch: char,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
// Check if string will be over max size limit
|
// Check if string will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _context.engine().max_string_size() > 0
|
if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() {
|
||||||
&& len as usize > _context.engine().max_string_size()
|
return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into();
|
||||||
{
|
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
|
||||||
"Length of string".to_string(),
|
|
||||||
_context.engine().max_string_size(),
|
|
||||||
len as usize,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len > 0 {
|
if len > 0 {
|
||||||
@ -281,14 +273,11 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _context.engine().max_string_size() > 0
|
if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size()
|
||||||
&& s.len() > _context.engine().max_string_size()
|
|
||||||
{
|
{
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
_context.engine().max_string_size(),
|
NO_POS,
|
||||||
s.len(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -299,23 +288,15 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(name = "pad", return_raw)]
|
#[rhai_fn(name = "pad", return_raw)]
|
||||||
pub fn pad_with_string(
|
pub fn pad_with_string(
|
||||||
_context: NativeCallContext,
|
_ctx: NativeCallContext,
|
||||||
s: &mut ImmutableString,
|
s: &mut ImmutableString,
|
||||||
len: INT,
|
len: INT,
|
||||||
padding: &str,
|
padding: &str,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
// Check if string will be over max size limit
|
// Check if string will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _context.engine().max_string_size() > 0
|
if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() {
|
||||||
&& len as usize > _context.engine().max_string_size()
|
return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into();
|
||||||
{
|
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
|
||||||
"Length of string".to_string(),
|
|
||||||
_context.engine().max_string_size(),
|
|
||||||
len as usize,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len > 0 {
|
if len > 0 {
|
||||||
@ -336,14 +317,11 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _context.engine().max_string_size() > 0
|
if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size()
|
||||||
&& s.len() > _context.engine().max_string_size()
|
|
||||||
{
|
{
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
_context.engine().max_string_size(),
|
NO_POS,
|
||||||
s.len(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Module containing error definitions for the parsing process.
|
//! Module containing error definitions for the parsing process.
|
||||||
|
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::{Position, NO_POS};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -43,26 +43,34 @@ impl fmt::Display for LexError {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
|
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
|
||||||
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s),
|
Self::MalformedEscapeSequence(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||||
Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s),
|
Self::MalformedNumber(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||||
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s),
|
Self::MalformedChar(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||||
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s),
|
Self::MalformedIdentifier(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||||
Self::UnterminatedString => write!(f, "Open string is not terminated"),
|
Self::UnterminatedString => f.write_str(self.desc()),
|
||||||
Self::StringTooLong(max) => write!(
|
Self::StringTooLong(max) => write!(f, "{} ({})", self.desc(), max),
|
||||||
f,
|
|
||||||
"Length of string literal exceeds the maximum limit ({})",
|
|
||||||
max
|
|
||||||
),
|
|
||||||
Self::ImproperSymbol(s) => f.write_str(s),
|
Self::ImproperSymbol(s) => f.write_str(s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LexError {
|
impl LexError {
|
||||||
/// Convert a `LexError` into a `ParseError`.
|
pub(crate) fn desc(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::UnexpectedInput(_) => "Unexpected character encountered",
|
||||||
|
Self::UnterminatedString => "Open string is not terminated",
|
||||||
|
Self::StringTooLong(_) => "Length of string literal exceeds the maximum limit",
|
||||||
|
Self::MalformedEscapeSequence(_) => "Invalid escape sequence",
|
||||||
|
Self::MalformedNumber(_) => "Invalid number",
|
||||||
|
Self::MalformedChar(_) => "Invalid character",
|
||||||
|
Self::MalformedIdentifier(_) => "Variable name is not proper",
|
||||||
|
Self::ImproperSymbol(_) => "Invalid symbol encountered",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Convert a `&LexError` into a `ParseError`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn into_err(&self, pos: Position) -> ParseError {
|
pub fn into_err(&self, pos: Position) -> ParseError {
|
||||||
ParseError(Box::new(self.into()), pos)
|
ParseError(Box::new(self.clone().into()), pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,10 +82,10 @@ impl LexError {
|
|||||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum ParseErrorType {
|
pub enum ParseErrorType {
|
||||||
/// Error in the script text. Wrapped value is the error message.
|
|
||||||
BadInput(String),
|
|
||||||
/// The script ends prematurely.
|
/// The script ends prematurely.
|
||||||
UnexpectedEOF,
|
UnexpectedEOF,
|
||||||
|
/// Error in the script text. Wrapped value is the lex error.
|
||||||
|
BadInput(LexError),
|
||||||
/// An unknown operator is encountered. Wrapped value is the operator.
|
/// An unknown operator is encountered. Wrapped value is the operator.
|
||||||
UnknownOperator(String),
|
UnknownOperator(String),
|
||||||
/// Expecting a particular token but not finding one. Wrapped values are the token and description.
|
/// Expecting a particular token but not finding one. Wrapped values are the token and description.
|
||||||
@ -164,8 +172,8 @@ impl ParseErrorType {
|
|||||||
|
|
||||||
pub(crate) fn desc(&self) -> &str {
|
pub(crate) fn desc(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Self::BadInput(p) => p,
|
|
||||||
Self::UnexpectedEOF => "Script is incomplete",
|
Self::UnexpectedEOF => "Script is incomplete",
|
||||||
|
Self::BadInput(p) => p.desc(),
|
||||||
Self::UnknownOperator(_) => "Unknown operator",
|
Self::UnknownOperator(_) => "Unknown operator",
|
||||||
Self::MissingToken(_, _) => "Expecting a certain token that is missing",
|
Self::MissingToken(_, _) => "Expecting a certain token that is missing",
|
||||||
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
||||||
@ -196,9 +204,9 @@ impl ParseErrorType {
|
|||||||
impl fmt::Display for ParseErrorType {
|
impl fmt::Display for ParseErrorType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => {
|
Self::BadInput(err) => write!(f, "{}", err),
|
||||||
f.write_str(if s.is_empty() { self.desc() } else { s })
|
|
||||||
}
|
Self::MalformedCallExpr(s) => f.write_str(if s.is_empty() { self.desc() } else { s }),
|
||||||
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s),
|
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||||
|
|
||||||
Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => {
|
Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => {
|
||||||
@ -247,14 +255,14 @@ impl fmt::Display for ParseErrorType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&LexError> for ParseErrorType {
|
impl From<LexError> for ParseErrorType {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(err: &LexError) -> Self {
|
fn from(err: LexError) -> Self {
|
||||||
match err {
|
match err {
|
||||||
LexError::StringTooLong(max) => {
|
LexError::StringTooLong(max) => {
|
||||||
Self::LiteralTooLarge("Length of string literal".to_string(), *max)
|
Self::LiteralTooLarge("Length of string literal".to_string(), max)
|
||||||
}
|
}
|
||||||
_ => Self::BadInput(err.to_string()),
|
_ => Self::BadInput(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,7 +290,7 @@ impl fmt::Display for ParseError {
|
|||||||
impl From<ParseErrorType> for Box<EvalAltResult> {
|
impl From<ParseErrorType> for Box<EvalAltResult> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(err: ParseErrorType) -> Self {
|
fn from(err: ParseErrorType) -> Self {
|
||||||
Box::new(EvalAltResult::ErrorParsing(err, Position::none()))
|
Box::new(EvalAltResult::ErrorParsing(err, NO_POS))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
101
src/parser.rs
101
src/parser.rs
@ -10,7 +10,9 @@ use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
|||||||
use crate::parse_error::{LexError, ParseError, ParseErrorType};
|
use crate::parse_error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::syntax::CustomSyntax;
|
use crate::syntax::CustomSyntax;
|
||||||
use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream};
|
use crate::token::{
|
||||||
|
is_keyword_function, is_valid_identifier, Position, Token, TokenStream, NO_POS,
|
||||||
|
};
|
||||||
use crate::utils::StraightHasherBuilder;
|
use crate::utils::StraightHasherBuilder;
|
||||||
use crate::{calc_script_fn_hash, StaticVec};
|
use crate::{calc_script_fn_hash, StaticVec};
|
||||||
|
|
||||||
@ -265,7 +267,7 @@ fn parse_fn_call(
|
|||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
id: String,
|
id: String,
|
||||||
capture: bool,
|
capture: bool,
|
||||||
mut namespace: Option<ModuleRef>,
|
mut namespace: Option<Box<ModuleRef>>,
|
||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let (token, token_pos) = input.peek().unwrap();
|
let (token, token_pos) = input.peek().unwrap();
|
||||||
@ -786,7 +788,7 @@ fn parse_primary(
|
|||||||
{
|
{
|
||||||
state.allow_capture = true;
|
state.allow_capture = true;
|
||||||
}
|
}
|
||||||
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos))))
|
||||||
}
|
}
|
||||||
// Module qualification
|
// Module qualification
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -796,18 +798,18 @@ fn parse_primary(
|
|||||||
{
|
{
|
||||||
state.allow_capture = true;
|
state.allow_capture = true;
|
||||||
}
|
}
|
||||||
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos))))
|
||||||
}
|
}
|
||||||
// Normal variable access
|
// Normal variable access
|
||||||
Token::Identifier(s) => {
|
Token::Identifier(s) => {
|
||||||
let index = state.access_var(&s, settings.pos);
|
let index = state.access_var(&s, settings.pos);
|
||||||
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, index)))
|
Expr::Variable(Box::new((index, None, 0, Ident::new(s, settings.pos))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function call is allowed to have reserved keyword
|
// Function call is allowed to have reserved keyword
|
||||||
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
|
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
|
||||||
if is_keyword_function(&s) {
|
if is_keyword_function(&s) {
|
||||||
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos))))
|
||||||
} else {
|
} else {
|
||||||
return Err(PERR::Reserved(s).into_err(settings.pos));
|
return Err(PERR::Reserved(s).into_err(settings.pos));
|
||||||
}
|
}
|
||||||
@ -816,12 +818,13 @@ fn parse_primary(
|
|||||||
// Access to `this` as a variable is OK
|
// Access to `this` as a variable is OK
|
||||||
Token::Reserved(s) if s == KEYWORD_THIS && *next_token != Token::LeftParen => {
|
Token::Reserved(s) if s == KEYWORD_THIS && *next_token != Token::LeftParen => {
|
||||||
if !settings.is_function_scope {
|
if !settings.is_function_scope {
|
||||||
return Err(
|
return Err(PERR::BadInput(LexError::ImproperSymbol(format!(
|
||||||
PERR::BadInput(format!("'{}' can only be used in functions", s))
|
"'{}' can only be used in functions",
|
||||||
.into_err(settings.pos),
|
s
|
||||||
);
|
)))
|
||||||
|
.into_err(settings.pos));
|
||||||
} else {
|
} else {
|
||||||
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -840,7 +843,8 @@ fn parse_primary(
|
|||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(settings.pos)
|
PERR::BadInput(LexError::UnexpectedInput(token.syntax().to_string()))
|
||||||
|
.into_err(settings.pos),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -862,7 +866,9 @@ fn parse_primary(
|
|||||||
return Err(if !match_token(input, Token::LeftParen).0 {
|
return Err(if !match_token(input, Token::LeftParen).0 {
|
||||||
LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos)
|
LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos)
|
||||||
} else {
|
} else {
|
||||||
PERR::BadInput("'!' cannot be used to call module functions".to_string())
|
PERR::BadInput(LexError::ImproperSymbol(
|
||||||
|
"'!' cannot be used to call module functions".to_string(),
|
||||||
|
))
|
||||||
.into_err(token_pos)
|
.into_err(token_pos)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -877,13 +883,13 @@ fn parse_primary(
|
|||||||
.into_err(pos));
|
.into_err(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (Ident { name, pos }, modules, _, _) = *x;
|
let (_, modules, _, Ident { name, pos }) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
|
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
|
||||||
}
|
}
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(x), Token::LeftParen) => {
|
(Expr::Variable(x), Token::LeftParen) => {
|
||||||
let (Ident { name, pos }, modules, _, _) = *x;
|
let (_, modules, _, Ident { name, pos }) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
|
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
|
||||||
}
|
}
|
||||||
@ -891,17 +897,17 @@ fn parse_primary(
|
|||||||
// module access
|
// module access
|
||||||
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
||||||
(Token::Identifier(id2), pos2) => {
|
(Token::Identifier(id2), pos2) => {
|
||||||
let (var_name_def, mut modules, _, index) = *x;
|
let (index, mut modules, _, var_name_def) = *x;
|
||||||
|
|
||||||
if let Some(ref mut modules) = modules {
|
if let Some(ref mut modules) = modules {
|
||||||
modules.push(var_name_def);
|
modules.push(var_name_def);
|
||||||
} else {
|
} else {
|
||||||
let mut m: ModuleRef = Default::default();
|
let mut m: ModuleRef = Default::default();
|
||||||
m.push(var_name_def);
|
m.push(var_name_def);
|
||||||
modules = Some(m);
|
modules = Some(Box::new(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Variable(Box::new((Ident::new(id2, pos2), modules, 0, index)))
|
Expr::Variable(Box::new((index, modules, 0, Ident::new(id2, pos2))))
|
||||||
}
|
}
|
||||||
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
|
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
|
||||||
return Err(PERR::Reserved(id2).into_err(pos2));
|
return Err(PERR::Reserved(id2).into_err(pos2));
|
||||||
@ -925,7 +931,7 @@ fn parse_primary(
|
|||||||
match &mut root_expr {
|
match &mut root_expr {
|
||||||
// Cache the hash key for module-qualified variables
|
// Cache the hash key for module-qualified variables
|
||||||
Expr::Variable(x) if x.1.is_some() => {
|
Expr::Variable(x) if x.1.is_some() => {
|
||||||
let (Ident { name, .. }, modules, hash, _) = x.as_mut();
|
let (_, modules, hash, Ident { name, .. }) = x.as_mut();
|
||||||
let modules = modules.as_mut().unwrap();
|
let modules = modules.as_mut().unwrap();
|
||||||
|
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
@ -1081,19 +1087,19 @@ fn make_assignment_stmt<'a>(
|
|||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
match &lhs {
|
match &lhs {
|
||||||
// var (non-indexed) = rhs
|
// var (non-indexed) = rhs
|
||||||
Expr::Variable(x) if x.3.is_none() => {
|
Expr::Variable(x) if x.0.is_none() => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
||||||
}
|
}
|
||||||
// var (indexed) = rhs
|
// var (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (
|
let (
|
||||||
|
index,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
Ident {
|
Ident {
|
||||||
name,
|
name,
|
||||||
pos: name_pos,
|
pos: name_pos,
|
||||||
},
|
},
|
||||||
_,
|
|
||||||
_,
|
|
||||||
index,
|
|
||||||
) = x.as_ref();
|
) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
@ -1108,19 +1114,19 @@ fn make_assignment_stmt<'a>(
|
|||||||
// xxx[???] = rhs, xxx.??? = rhs
|
// xxx[???] = rhs, xxx.??? = rhs
|
||||||
Expr::Index(x, _) | Expr::Dot(x, _) => match &x.lhs {
|
Expr::Index(x, _) | Expr::Dot(x, _) => match &x.lhs {
|
||||||
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
|
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
|
||||||
Expr::Variable(x) if x.3.is_none() => {
|
Expr::Variable(x) if x.0.is_none() => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
||||||
}
|
}
|
||||||
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (
|
let (
|
||||||
|
index,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
Ident {
|
Ident {
|
||||||
name,
|
name,
|
||||||
pos: name_pos,
|
pos: name_pos,
|
||||||
},
|
},
|
||||||
_,
|
|
||||||
_,
|
|
||||||
index,
|
|
||||||
) = x.as_ref();
|
) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
@ -1140,9 +1146,10 @@ fn make_assignment_stmt<'a>(
|
|||||||
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
|
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
|
||||||
}
|
}
|
||||||
// ??? && ??? = rhs, ??? || ??? = rhs
|
// ??? && ??? = rhs, ??? || ??? = rhs
|
||||||
Expr::And(_, _) | Expr::Or(_, _) => {
|
Expr::And(_, _) | Expr::Or(_, _) => Err(PERR::BadInput(LexError::ImproperSymbol(
|
||||||
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(pos))
|
"Possibly a typo of '=='?".to_string(),
|
||||||
}
|
))
|
||||||
|
.into_err(pos)),
|
||||||
// expr = rhs
|
// expr = rhs
|
||||||
_ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(lhs.position())),
|
_ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(lhs.position())),
|
||||||
}
|
}
|
||||||
@ -1197,10 +1204,10 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
}
|
}
|
||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
||||||
let ident = x.0;
|
let ident = x.3;
|
||||||
let getter = make_getter(&ident.name);
|
let getter = make_getter(&ident.name);
|
||||||
let setter = make_setter(&ident.name);
|
let setter = make_setter(&ident.name);
|
||||||
let rhs = Expr::Property(Box::new((ident.into(), (getter, setter))));
|
let rhs = Expr::Property(Box::new(((getter, setter), ident.into())));
|
||||||
|
|
||||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
}
|
}
|
||||||
@ -1238,10 +1245,10 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
(_, Expr::FnCall(x, pos))
|
(_, Expr::FnCall(x, pos))
|
||||||
if x.args.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&x.name.as_ref()) =>
|
if x.args.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&x.name.as_ref()) =>
|
||||||
{
|
{
|
||||||
return Err(PERR::BadInput(format!(
|
return Err(PERR::BadInput(LexError::ImproperSymbol(format!(
|
||||||
"'{}' should not be called in method style. Try {}(...);",
|
"'{}' should not be called in method style. Try {}(...);",
|
||||||
x.name, x.name
|
x.name, x.name
|
||||||
))
|
)))
|
||||||
.into_err(pos));
|
.into_err(pos));
|
||||||
}
|
}
|
||||||
// lhs.func!(...)
|
// lhs.func!(...)
|
||||||
@ -1635,10 +1642,10 @@ fn parse_custom_syntax(
|
|||||||
(Token::Identifier(s), pos) => {
|
(Token::Identifier(s), pos) => {
|
||||||
segments.push(s.clone());
|
segments.push(s.clone());
|
||||||
exprs.push(Expr::Variable(Box::new((
|
exprs.push(Expr::Variable(Box::new((
|
||||||
Ident::new(s, pos),
|
None,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
None,
|
Ident::new(s, pos),
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
@ -1734,9 +1741,10 @@ fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result
|
|||||||
/// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`).
|
/// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`).
|
||||||
fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
|
fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Equals, pos) => {
|
(Token::Equals, pos) => Err(PERR::BadInput(LexError::ImproperSymbol(
|
||||||
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos))
|
"Possibly a typo of '=='?".to_string(),
|
||||||
}
|
))
|
||||||
|
.into_err(*pos)),
|
||||||
(Token::PlusAssign, pos)
|
(Token::PlusAssign, pos)
|
||||||
| (Token::MinusAssign, pos)
|
| (Token::MinusAssign, pos)
|
||||||
| (Token::MultiplyAssign, pos)
|
| (Token::MultiplyAssign, pos)
|
||||||
@ -1747,9 +1755,9 @@ fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
|
|||||||
| (Token::PowerOfAssign, pos)
|
| (Token::PowerOfAssign, pos)
|
||||||
| (Token::AndAssign, pos)
|
| (Token::AndAssign, pos)
|
||||||
| (Token::OrAssign, pos)
|
| (Token::OrAssign, pos)
|
||||||
| (Token::XOrAssign, pos) => Err(PERR::BadInput(
|
| (Token::XOrAssign, pos) => Err(PERR::BadInput(LexError::ImproperSymbol(
|
||||||
"Expecting a boolean expression, not an assignment".to_string(),
|
"Expecting a boolean expression, not an assignment".to_string(),
|
||||||
)
|
))
|
||||||
.into_err(*pos)),
|
.into_err(*pos)),
|
||||||
|
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
@ -2492,12 +2500,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals.iter().for_each(|x| {
|
externals.iter().for_each(|x| {
|
||||||
args.push(Expr::Variable(Box::new((x.clone(), None, 0, None))));
|
args.push(Expr::Variable(Box::new((None, None, 0, x.clone()))));
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
externals.into_iter().for_each(|x| {
|
externals.into_iter().for_each(|x| {
|
||||||
args.push(Expr::Variable(Box::new((x.clone(), None, 0, None))));
|
args.push(Expr::Variable(Box::new((None, None, 0, x.clone()))));
|
||||||
});
|
});
|
||||||
|
|
||||||
let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1);
|
let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1);
|
||||||
@ -2683,7 +2691,7 @@ impl Engine {
|
|||||||
is_function_scope: false,
|
is_function_scope: false,
|
||||||
is_breakable: false,
|
is_breakable: false,
|
||||||
level: 0,
|
level: 0,
|
||||||
pos: Position::none(),
|
pos: NO_POS,
|
||||||
};
|
};
|
||||||
let expr = parse_expr(input, &mut state, &mut functions, settings)?;
|
let expr = parse_expr(input, &mut state, &mut functions, settings)?;
|
||||||
|
|
||||||
@ -2694,7 +2702,8 @@ impl Engine {
|
|||||||
// Return error if the expression doesn't end
|
// Return error if the expression doesn't end
|
||||||
(token, pos) => {
|
(token, pos) => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(*pos)
|
PERR::BadInput(LexError::UnexpectedInput(token.syntax().to_string()))
|
||||||
|
.into_err(*pos),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2732,7 +2741,7 @@ impl Engine {
|
|||||||
is_function_scope: false,
|
is_function_scope: false,
|
||||||
is_breakable: false,
|
is_breakable: false,
|
||||||
level: 0,
|
level: 0,
|
||||||
pos: Position::none(),
|
pos: NO_POS,
|
||||||
};
|
};
|
||||||
|
|
||||||
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {
|
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::dynamic::Dynamic;
|
use crate::dynamic::Dynamic;
|
||||||
use crate::parse_error::ParseErrorType;
|
use crate::parse_error::ParseErrorType;
|
||||||
use crate::token::Position;
|
use crate::token::{Position, NO_POS};
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
use crate::INT;
|
use crate::INT;
|
||||||
|
|
||||||
@ -83,11 +83,11 @@ pub enum EvalAltResult {
|
|||||||
ErrorTooManyModules(Position),
|
ErrorTooManyModules(Position),
|
||||||
/// Call stack over maximum limit.
|
/// Call stack over maximum limit.
|
||||||
ErrorStackOverflow(Position),
|
ErrorStackOverflow(Position),
|
||||||
/// Data value over maximum size limit. Wrapped values are the type name, maximum size and current size.
|
/// Data value over maximum size limit. Wrapped value is the type name.
|
||||||
ErrorDataTooLarge(String, usize, usize, Position),
|
ErrorDataTooLarge(String, Position),
|
||||||
/// The script is prematurely terminated.
|
/// The script is prematurely terminated. Wrapped value is the termination token.
|
||||||
ErrorTerminated(Position),
|
ErrorTerminated(Dynamic, Position),
|
||||||
/// Run-time error encountered. Wrapped value is the error.
|
/// Run-time error encountered. Wrapped value is the error token.
|
||||||
ErrorRuntime(Dynamic, Position),
|
ErrorRuntime(Dynamic, Position),
|
||||||
|
|
||||||
/// Breaking out of loops - not an error if within a loop.
|
/// Breaking out of loops - not an error if within a loop.
|
||||||
@ -127,7 +127,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorVariableNotFound(_, _) => "Variable not found",
|
Self::ErrorVariableNotFound(_, _) => "Variable not found",
|
||||||
Self::ErrorModuleNotFound(_, _) => "Module not found",
|
Self::ErrorModuleNotFound(_, _) => "Module not found",
|
||||||
Self::ErrorDataRace(_, _) => "Data race detected when accessing variable",
|
Self::ErrorDataRace(_, _) => "Data race detected when accessing variable",
|
||||||
Self::ErrorAssignmentToConstant(_, _) => "Assignment to a constant variable",
|
Self::ErrorAssignmentToConstant(_, _) => "Cannot assign to a constant",
|
||||||
Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect",
|
Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect",
|
||||||
Self::ErrorInExpr(_) => "Malformed 'in' expression",
|
Self::ErrorInExpr(_) => "Malformed 'in' expression",
|
||||||
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
|
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
|
||||||
@ -135,8 +135,8 @@ impl EvalAltResult {
|
|||||||
Self::ErrorTooManyOperations(_) => "Too many operations",
|
Self::ErrorTooManyOperations(_) => "Too many operations",
|
||||||
Self::ErrorTooManyModules(_) => "Too many modules imported",
|
Self::ErrorTooManyModules(_) => "Too many modules imported",
|
||||||
Self::ErrorStackOverflow(_) => "Stack overflow",
|
Self::ErrorStackOverflow(_) => "Stack overflow",
|
||||||
Self::ErrorDataTooLarge(_, _, _, _) => "Data size exceeds maximum limit",
|
Self::ErrorDataTooLarge(_, _) => "Data size exceeds maximum limit",
|
||||||
Self::ErrorTerminated(_) => "Script terminated.",
|
Self::ErrorTerminated(_,_) => "Script terminated.",
|
||||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||||
Self::LoopBreak(true, _) => "Break statement not inside a loop",
|
Self::LoopBreak(true, _) => "Break statement not inside a loop",
|
||||||
Self::LoopBreak(false, _) => "Continue statement not inside a loop",
|
Self::LoopBreak(false, _) => "Continue statement not inside a loop",
|
||||||
@ -185,7 +185,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
| Self::ErrorTooManyOperations(_)
|
| Self::ErrorTooManyOperations(_)
|
||||||
| Self::ErrorTooManyModules(_)
|
| Self::ErrorTooManyModules(_)
|
||||||
| Self::ErrorStackOverflow(_)
|
| Self::ErrorStackOverflow(_)
|
||||||
| Self::ErrorTerminated(_) => f.write_str(desc)?,
|
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
|
||||||
|
|
||||||
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
|
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
|
||||||
let s = d.as_str().unwrap();
|
let s = d.as_str().unwrap();
|
||||||
@ -194,7 +194,9 @@ impl fmt::Display for EvalAltResult {
|
|||||||
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
||||||
Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?,
|
Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?,
|
||||||
|
|
||||||
Self::ErrorAssignmentToConstant(s, _) => write!(f, "{}: '{}'", desc, s)?,
|
Self::ErrorAssignmentToConstant(s, _) => {
|
||||||
|
write!(f, "Cannot assign to constant '{}'", s)?
|
||||||
|
}
|
||||||
Self::ErrorMismatchOutputType(r, s, _) => {
|
Self::ErrorMismatchOutputType(r, s, _) => {
|
||||||
write!(f, "Output type is incorrect: {} (expecting {})", r, s)?
|
write!(f, "Output type is incorrect: {} (expecting {})", r, s)?
|
||||||
}
|
}
|
||||||
@ -237,9 +239,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
"String index {} is out of bounds: only {} characters in the string",
|
"String index {} is out of bounds: only {} characters in the string",
|
||||||
index, max
|
index, max
|
||||||
)?,
|
)?,
|
||||||
Self::ErrorDataTooLarge(typ, max, size, _) => {
|
Self::ErrorDataTooLarge(typ, _) => write!(f, "{} exceeds maximum limit", typ)?,
|
||||||
write!(f, "{} ({}) exceeds the maximum limit ({})", typ, size, max)?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not write any position if None
|
// Do not write any position if None
|
||||||
@ -254,7 +254,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
Self::ErrorRuntime(err.as_ref().to_string().into(), Position::none())
|
Self::ErrorRuntime(err.as_ref().to_string().into(), NO_POS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
|||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
Box::new(EvalAltResult::ErrorRuntime(
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
err.as_ref().to_string().into(),
|
err.as_ref().to_string().into(),
|
||||||
Position::none(),
|
NO_POS,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,8 +297,8 @@ impl EvalAltResult {
|
|||||||
Self::ErrorTooManyOperations(_)
|
Self::ErrorTooManyOperations(_)
|
||||||
| Self::ErrorTooManyModules(_)
|
| Self::ErrorTooManyModules(_)
|
||||||
| Self::ErrorStackOverflow(_)
|
| Self::ErrorStackOverflow(_)
|
||||||
| Self::ErrorDataTooLarge(_, _, _, _)
|
| Self::ErrorDataTooLarge(_, _)
|
||||||
| Self::ErrorTerminated(_) => false,
|
| Self::ErrorTerminated(_, _) => false,
|
||||||
|
|
||||||
Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(),
|
Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -313,9 +313,9 @@ impl EvalAltResult {
|
|||||||
Self::ErrorTooManyOperations(_)
|
Self::ErrorTooManyOperations(_)
|
||||||
| Self::ErrorTooManyModules(_)
|
| Self::ErrorTooManyModules(_)
|
||||||
| Self::ErrorStackOverflow(_)
|
| Self::ErrorStackOverflow(_)
|
||||||
| Self::ErrorDataTooLarge(_, _, _, _) => true,
|
| Self::ErrorDataTooLarge(_, _) => true,
|
||||||
|
|
||||||
Self::ErrorTerminated(_) => true,
|
Self::ErrorTerminated(_, _) => true,
|
||||||
|
|
||||||
Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(),
|
Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(),
|
||||||
|
|
||||||
@ -326,7 +326,7 @@ impl EvalAltResult {
|
|||||||
/// Get the `Position` of this error.
|
/// Get the `Position` of this error.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
Self::ErrorSystem(_, _) => Position::none(),
|
Self::ErrorSystem(_, _) => NO_POS,
|
||||||
|
|
||||||
Self::ErrorParsing(_, pos)
|
Self::ErrorParsing(_, pos)
|
||||||
| Self::ErrorFunctionNotFound(_, pos)
|
| Self::ErrorFunctionNotFound(_, pos)
|
||||||
@ -349,8 +349,8 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorTooManyOperations(pos)
|
| Self::ErrorTooManyOperations(pos)
|
||||||
| Self::ErrorTooManyModules(pos)
|
| Self::ErrorTooManyModules(pos)
|
||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorDataTooLarge(_, _, _, pos)
|
| Self::ErrorDataTooLarge(_, pos)
|
||||||
| Self::ErrorTerminated(pos)
|
| Self::ErrorTerminated(_, pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
| Self::LoopBreak(_, pos)
|
| Self::LoopBreak(_, pos)
|
||||||
| Self::Return(_, pos) => *pos,
|
| Self::Return(_, pos) => *pos,
|
||||||
@ -383,8 +383,8 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorTooManyOperations(pos)
|
| Self::ErrorTooManyOperations(pos)
|
||||||
| Self::ErrorTooManyModules(pos)
|
| Self::ErrorTooManyModules(pos)
|
||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorDataTooLarge(_, _, _, pos)
|
| Self::ErrorDataTooLarge(_, pos)
|
||||||
| Self::ErrorTerminated(pos)
|
| Self::ErrorTerminated(_, pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
| Self::LoopBreak(_, pos)
|
| Self::LoopBreak(_, pos)
|
||||||
| Self::Return(_, pos) => *pos = new_position,
|
| Self::Return(_, pos) => *pos = new_position,
|
||||||
|
176
src/scope.rs
176
src/scope.rs
@ -1,11 +1,8 @@
|
|||||||
//! Module that defines the `Scope` type representing a function call-stack scope.
|
//! Module that defines the `Scope` type representing a function call-stack scope.
|
||||||
|
|
||||||
use crate::ast::Expr;
|
|
||||||
use crate::dynamic::{Dynamic, Variant};
|
use crate::dynamic::{Dynamic, Variant};
|
||||||
use crate::parser::map_dynamic_to_expr;
|
|
||||||
use crate::token::Position;
|
|
||||||
|
|
||||||
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec};
|
use crate::stdlib::{borrow::Cow, iter, string::String, vec::Vec};
|
||||||
|
|
||||||
/// Type of an entry in the Scope.
|
/// Type of an entry in the Scope.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||||
@ -27,21 +24,6 @@ impl EntryType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An entry in the Scope.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Entry<'a> {
|
|
||||||
/// Name of the entry.
|
|
||||||
pub name: Cow<'a, str>,
|
|
||||||
/// Type of the entry.
|
|
||||||
pub typ: EntryType,
|
|
||||||
/// Current value of the entry.
|
|
||||||
pub value: Dynamic,
|
|
||||||
/// Alias of the entry.
|
|
||||||
pub alias: Option<Box<String>>,
|
|
||||||
/// A constant expression if the initial value matches one of the recognized types.
|
|
||||||
pub expr: Option<Box<Expr>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type containing information about the current scope.
|
/// Type containing information about the current scope.
|
||||||
/// Useful for keeping state between `Engine` evaluation runs.
|
/// Useful for keeping state between `Engine` evaluation runs.
|
||||||
///
|
///
|
||||||
@ -72,8 +54,25 @@ pub struct Entry<'a> {
|
|||||||
///
|
///
|
||||||
/// When searching for entries, newly-added entries are found before similarly-named but older entries,
|
/// When searching for entries, newly-added entries are found before similarly-named but older entries,
|
||||||
/// allowing for automatic _shadowing_.
|
/// allowing for automatic _shadowing_.
|
||||||
|
//
|
||||||
|
// # Implementation Notes
|
||||||
|
//
|
||||||
|
// `Scope` is implemented as three `Vec`'s of exactly the same length. Variables data (name, type, etc.)
|
||||||
|
// is manually split into three equal-length arrays. That's because variable names take up the most space,
|
||||||
|
// with `Cow<str>` being four words long, but in the vast majority of cases the name is NOT used to look up
|
||||||
|
// a variable's value. Variable lookup is usually via direct index, by-passing the name altogether.
|
||||||
|
//
|
||||||
|
// Since `Dynamic` is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
||||||
|
// The variable type is packed separately into another array because it is even smaller.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Scope<'a>(Vec<Entry<'a>>);
|
pub struct Scope<'a> {
|
||||||
|
/// Current value of the entry.
|
||||||
|
values: Vec<Dynamic>,
|
||||||
|
/// Type of the entry.
|
||||||
|
types: Vec<EntryType>,
|
||||||
|
/// (Name, alias) of the entry.
|
||||||
|
names: Vec<(Cow<'a, str>, Option<String>)>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Scope<'a> {
|
impl<'a> Scope<'a> {
|
||||||
/// Create a new Scope.
|
/// Create a new Scope.
|
||||||
@ -114,7 +113,9 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) -> &mut Self {
|
pub fn clear(&mut self) -> &mut Self {
|
||||||
self.0.clear();
|
self.names.clear();
|
||||||
|
self.types.clear();
|
||||||
|
self.values.clear();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.values.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the Scope empty?
|
/// Is the Scope empty?
|
||||||
@ -151,7 +152,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.len() == 0
|
self.values.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new entry to the Scope.
|
/// Add (push) a new entry to the Scope.
|
||||||
@ -172,7 +173,7 @@ impl<'a> Scope<'a> {
|
|||||||
name: K,
|
name: K,
|
||||||
value: T,
|
value: T,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false)
|
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new `Dynamic` entry to the Scope.
|
/// Add (push) a new `Dynamic` entry to the Scope.
|
||||||
@ -189,7 +190,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self {
|
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Normal, value, false)
|
self.push_dynamic_value(name, EntryType::Normal, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new constant to the Scope.
|
/// Add (push) a new constant to the Scope.
|
||||||
@ -216,7 +217,7 @@ impl<'a> Scope<'a> {
|
|||||||
name: K,
|
name: K,
|
||||||
value: T,
|
value: T,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true)
|
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new constant with a `Dynamic` value to the Scope.
|
/// Add (push) a new constant with a `Dynamic` value to the Scope.
|
||||||
@ -244,7 +245,7 @@ impl<'a> Scope<'a> {
|
|||||||
name: K,
|
name: K,
|
||||||
value: Dynamic,
|
value: Dynamic,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Constant, value, true)
|
self.push_dynamic_value(name, EntryType::Constant, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new entry with a `Dynamic` value to the Scope.
|
/// Add (push) a new entry with a `Dynamic` value to the Scope.
|
||||||
@ -254,22 +255,10 @@ impl<'a> Scope<'a> {
|
|||||||
name: K,
|
name: K,
|
||||||
entry_type: EntryType,
|
entry_type: EntryType,
|
||||||
value: Dynamic,
|
value: Dynamic,
|
||||||
map_expr: bool,
|
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let expr = if map_expr {
|
self.names.push((name.into(), None));
|
||||||
map_dynamic_to_expr(value.clone(), Position::none()).map(Box::new)
|
self.types.push(entry_type);
|
||||||
} else {
|
self.values.push(value.into());
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
self.0.push(Entry {
|
|
||||||
name: name.into(),
|
|
||||||
typ: entry_type,
|
|
||||||
alias: None,
|
|
||||||
value: value.into(),
|
|
||||||
expr,
|
|
||||||
});
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +290,9 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
||||||
self.0.truncate(size);
|
self.names.truncate(size);
|
||||||
|
self.types.truncate(size);
|
||||||
|
self.values.truncate(size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,37 +311,28 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn contains(&self, name: &str) -> bool {
|
pub fn contains(&self, name: &str) -> bool {
|
||||||
self.0
|
self.names
|
||||||
.iter()
|
.iter()
|
||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.any(|Entry { name: key, .. }| name == key)
|
.any(|(key, _)| name == key.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find an entry in the Scope, starting from the last.
|
/// Find an entry in the Scope, starting from the last.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
||||||
self.0
|
self.names
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.find_map(|(index, Entry { name: key, typ, .. })| {
|
.find_map(|(index, (key, _))| {
|
||||||
if name == key {
|
if name == key.as_ref() {
|
||||||
Some((index, *typ))
|
Some((index, self.types[index]))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an entry in the Scope, starting from the last.
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn get_entry(&self, name: &str) -> Option<&Entry> {
|
|
||||||
self.0
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.find(|Entry { name: key, .. }| name == key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the value of an entry in the Scope, starting from the last.
|
/// Get the value of an entry in the Scope, starting from the last.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -365,8 +347,12 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||||
self.get_entry(name)
|
self.names
|
||||||
.and_then(|Entry { value, .. }| value.flatten_clone().try_cast())
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.rev()
|
||||||
|
.find(|(_, (key, _))| name == key.as_ref())
|
||||||
|
.and_then(|(index, _)| self.values[index].flatten_clone().try_cast())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the value of the named entry.
|
/// Update the value of the named entry.
|
||||||
@ -398,7 +384,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
||||||
Some((index, EntryType::Normal)) => {
|
Some((index, EntryType::Normal)) => {
|
||||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value);
|
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
@ -407,16 +393,18 @@ impl<'a> Scope<'a> {
|
|||||||
/// Get a mutable reference to an entry in the Scope.
|
/// Get a mutable reference to an entry in the Scope.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
||||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
(
|
||||||
(&mut entry.value, entry.typ)
|
self.values.get_mut(index).expect("invalid index in Scope"),
|
||||||
|
self.types[index],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the access type of an entry in the Scope.
|
/// Update the access type of an entry in the Scope.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
|
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
|
||||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
let entry = self.names.get_mut(index).expect("invalid index in Scope");
|
||||||
entry.alias = Some(Box::new(alias));
|
entry.1 = Some(alias);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,34 +412,36 @@ impl<'a> Scope<'a> {
|
|||||||
/// Shadowed variables are omitted in the copy.
|
/// Shadowed variables are omitted in the copy.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn clone_visible(&self) -> Self {
|
pub(crate) fn clone_visible(&self) -> Self {
|
||||||
let mut entries: Vec<Entry> = Default::default();
|
let mut entries: Self = Default::default();
|
||||||
|
|
||||||
self.0.iter().rev().for_each(|entry| {
|
self.names
|
||||||
if entries
|
|
||||||
.iter()
|
.iter()
|
||||||
.find(|Entry { name, .. }| &entry.name == name)
|
.enumerate()
|
||||||
.is_none()
|
.rev()
|
||||||
{
|
.for_each(|(index, (name, alias))| {
|
||||||
entries.push(entry.clone());
|
if !entries.names.iter().any(|(key, _)| key == name) {
|
||||||
|
entries.names.push((name.clone(), alias.clone()));
|
||||||
|
entries.types.push(self.types[index]);
|
||||||
|
entries.values.push(self.values[index].clone());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self(entries)
|
entries
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the Scope.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
|
pub(crate) fn into_iter(
|
||||||
self.0.into_iter()
|
self,
|
||||||
}
|
) -> impl Iterator<Item = (Cow<'a, str>, EntryType, Dynamic, Option<String>)> {
|
||||||
|
self.names
|
||||||
/// Get an iterator to entries in the Scope in reverse order.
|
.into_iter()
|
||||||
#[inline(always)]
|
.zip(self.types.into_iter().zip(self.values.into_iter()))
|
||||||
pub(crate) fn to_iter(&self) -> impl Iterator<Item = &Entry> {
|
.map(|((name, alias), (typ, value))| (name, typ, value, alias))
|
||||||
self.0.iter().rev() // Always search a Scope in reverse order
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the Scope.
|
||||||
|
/// Shared values are flatten-cloned.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -484,25 +474,21 @@ impl<'a> Scope<'a> {
|
|||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the Scope.
|
||||||
/// Shared values are not expanded.
|
/// Shared values are not expanded.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
|
pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator<Item = (&'a str, bool, &'x Dynamic)> + 'x {
|
||||||
self.0.iter().map(
|
self.names
|
||||||
|Entry {
|
.iter()
|
||||||
name, typ, value, ..
|
.zip(self.types.iter().zip(self.values.iter()))
|
||||||
}| { (name.as_ref(), typ.is_constant(), value) },
|
.map(|((name, _), (typ, value))| (name.as_ref(), typ.is_constant(), value))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||||
self.0
|
iter.into_iter().for_each(|(name, typ, value)| {
|
||||||
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
self.names.push((name.into(), None));
|
||||||
name: name.into(),
|
self.types.push(typ);
|
||||||
typ,
|
self.values.push(value);
|
||||||
alias: None,
|
});
|
||||||
value: value.into(),
|
|
||||||
expr: None,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
use super::str::ImmutableStringDeserializer;
|
use super::str::ImmutableStringDeserializer;
|
||||||
use crate::dynamic::{Dynamic, Union};
|
use crate::dynamic::{Dynamic, Union};
|
||||||
use crate::parse_error::ParseErrorType;
|
use crate::parse_error::{LexError, ParseErrorType};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::NO_POS;
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
use serde::de::{
|
use serde::de::{
|
||||||
@ -45,11 +45,7 @@ impl<'de> DynamicDeserializer<'de> {
|
|||||||
}
|
}
|
||||||
/// Shortcut for a type conversion error.
|
/// Shortcut for a type conversion error.
|
||||||
fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> {
|
fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> {
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(error.into(), self.value.type_name().into(), NO_POS)
|
||||||
error.into(),
|
|
||||||
self.value.type_name().into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
fn deserialize_int<V: Visitor<'de>>(
|
fn deserialize_int<V: Visitor<'de>>(
|
||||||
@ -127,7 +123,10 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>(
|
|||||||
|
|
||||||
impl Error for Box<EvalAltResult> {
|
impl Error for Box<EvalAltResult> {
|
||||||
fn custom<T: fmt::Display>(err: T) -> Self {
|
fn custom<T: fmt::Display>(err: T) -> Self {
|
||||||
EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none())
|
EvalAltResult::ErrorParsing(
|
||||||
|
ParseErrorType::BadInput(LexError::ImproperSymbol(err.to_string())),
|
||||||
|
NO_POS,
|
||||||
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::dynamic::Dynamic;
|
use crate::dynamic::Dynamic;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::NO_POS;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::engine::Array;
|
use crate::engine::Array;
|
||||||
@ -99,7 +99,7 @@ pub fn to_dynamic<T: Serialize>(value: T) -> Result<Dynamic, Box<EvalAltResult>>
|
|||||||
|
|
||||||
impl Error for Box<EvalAltResult> {
|
impl Error for Box<EvalAltResult> {
|
||||||
fn custom<T: fmt::Display>(err: T) -> Self {
|
fn custom<T: fmt::Display>(err: T) -> Self {
|
||||||
EvalAltResult::ErrorRuntime(err.to_string().into(), Position::none()).into()
|
EvalAltResult::ErrorRuntime(err.to_string().into(), NO_POS).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,11 +295,7 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
make_variant(_variant, content)
|
make_variant(_variant, content)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_object")]
|
#[cfg(feature = "no_object")]
|
||||||
return EvalAltResult::ErrorMismatchOutputType(
|
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
|
||||||
"Dynamic".into(),
|
|
||||||
"map".into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,11 +303,7 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
return Ok(DynamicSerializer::new(Array::new().into()));
|
return Ok(DynamicSerializer::new(Array::new().into()));
|
||||||
#[cfg(feature = "no_index")]
|
#[cfg(feature = "no_index")]
|
||||||
return EvalAltResult::ErrorMismatchOutputType(
|
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "array".into(), NO_POS)
|
||||||
"Dynamic".into(),
|
|
||||||
"array".into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,12 +337,7 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
let err_type = "map";
|
let err_type = "map";
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
let err_type = "array";
|
let err_type = "array";
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), err_type.into(), NO_POS).into()
|
||||||
"Dynamic".into(),
|
|
||||||
err_type.into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,11 +345,7 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
return Ok(DynamicSerializer::new(Map::new().into()));
|
return Ok(DynamicSerializer::new(Map::new().into()));
|
||||||
#[cfg(feature = "no_object")]
|
#[cfg(feature = "no_object")]
|
||||||
return EvalAltResult::ErrorMismatchOutputType(
|
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
|
||||||
"Dynamic".into(),
|
|
||||||
"map".into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,11 +370,7 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
map: Map::with_capacity(_len),
|
map: Map::with_capacity(_len),
|
||||||
});
|
});
|
||||||
#[cfg(feature = "no_object")]
|
#[cfg(feature = "no_object")]
|
||||||
return EvalAltResult::ErrorMismatchOutputType(
|
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
|
||||||
"Dynamic".into(),
|
|
||||||
"map".into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,11 +480,7 @@ impl SerializeMap for DynamicSerializer {
|
|||||||
let key = mem::take(&mut self._key)
|
let key = mem::take(&mut self._key)
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|typ| {
|
.map_err(|typ| {
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS)
|
||||||
"string".into(),
|
|
||||||
typ.into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
let _value = _value.serialize(&mut *self)?;
|
let _value = _value.serialize(&mut *self)?;
|
||||||
let map = self._value.downcast_mut::<Map>().unwrap();
|
let map = self._value.downcast_mut::<Map>().unwrap();
|
||||||
@ -525,11 +500,7 @@ impl SerializeMap for DynamicSerializer {
|
|||||||
{
|
{
|
||||||
let _key: Dynamic = _key.serialize(&mut *self)?;
|
let _key: Dynamic = _key.serialize(&mut *self)?;
|
||||||
let _key = _key.take_immutable_string().map_err(|typ| {
|
let _key = _key.take_immutable_string().map_err(|typ| {
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS)
|
||||||
"string".into(),
|
|
||||||
typ.into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
let _value = _value.serialize(&mut *self)?;
|
let _value = _value.serialize(&mut *self)?;
|
||||||
let map = self._value.downcast_mut::<Map>().unwrap();
|
let map = self._value.downcast_mut::<Map>().unwrap();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Implement deserialization support of `ImmutableString` for [`serde`](https://crates.io/crates/serde).
|
//! Implement deserialization support of `ImmutableString` for [`serde`](https://crates.io/crates/serde).
|
||||||
|
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::NO_POS;
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
use serde::de::{Deserializer, Visitor};
|
use serde::de::{Deserializer, Visitor};
|
||||||
@ -20,11 +20,7 @@ impl<'a> ImmutableStringDeserializer<'a> {
|
|||||||
}
|
}
|
||||||
/// Shortcut for a type conversion error.
|
/// Shortcut for a type conversion error.
|
||||||
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> {
|
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> {
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(type_name::<T>().into(), "string".into(), NO_POS)
|
||||||
type_name::<T>().into(),
|
|
||||||
"string".into(),
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use crate::engine::{Engine, EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT
|
|||||||
use crate::fn_native::{SendSync, Shared};
|
use crate::fn_native::{SendSync, Shared};
|
||||||
use crate::parse_error::{LexError, ParseError};
|
use crate::parse_error::{LexError, ParseError};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::{is_valid_identifier, Position, Token};
|
use crate::token::{is_valid_identifier, Position, Token, NO_POS};
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
use crate::StaticVec;
|
use crate::StaticVec;
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ impl Engine {
|
|||||||
segments.len() + 1,
|
segments.len() + 1,
|
||||||
s
|
s
|
||||||
))
|
))
|
||||||
.into_err(Position::none())
|
.into_err(NO_POS)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
// Identifier in first position
|
// Identifier in first position
|
||||||
@ -156,7 +156,7 @@ impl Engine {
|
|||||||
segments.len() + 1,
|
segments.len() + 1,
|
||||||
s
|
s
|
||||||
))
|
))
|
||||||
.into_err(Position::none())
|
.into_err(NO_POS)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
102
src/token.rs
102
src/token.rs
@ -44,7 +44,15 @@ pub struct Position {
|
|||||||
pos: u16,
|
pos: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// No `Position`.
|
||||||
|
pub const NO_POS: Position = Position::NONE;
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
|
/// A `Position` representing no position.
|
||||||
|
pub const NONE: Self = Self { line: 0, pos: 0 };
|
||||||
|
/// A `Position` representing the first position.
|
||||||
|
pub const START: Self = Self { line: 1, pos: 0 };
|
||||||
|
|
||||||
/// Create a new `Position`.
|
/// Create a new `Position`.
|
||||||
///
|
///
|
||||||
/// `line` must not be zero.
|
/// `line` must not be zero.
|
||||||
@ -62,27 +70,24 @@ impl Position {
|
|||||||
pos: position,
|
pos: position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the line number (1-based), or `None` if there is no position.
|
/// Get the line number (1-based), or `None` if there is no position.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn line(&self) -> Option<usize> {
|
pub fn line(self) -> Option<usize> {
|
||||||
if self.is_none() {
|
if self.is_none() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(self.line as usize)
|
Some(self.line as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the character position (1-based), or `None` if at beginning of a line.
|
/// Get the character position (1-based), or `None` if at beginning of a line.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn position(&self) -> Option<usize> {
|
pub fn position(self) -> Option<usize> {
|
||||||
if self.is_none() || self.pos == 0 {
|
if self.is_none() || self.pos == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(self.pos as usize)
|
Some(self.pos as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance by one character position.
|
/// Advance by one character position.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn advance(&mut self) {
|
pub(crate) fn advance(&mut self) {
|
||||||
@ -93,7 +98,6 @@ impl Position {
|
|||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Go backwards by one character position.
|
/// Go backwards by one character position.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -105,7 +109,6 @@ impl Position {
|
|||||||
assert!(self.pos > 0, "cannot rewind at position 0");
|
assert!(self.pos > 0, "cannot rewind at position 0");
|
||||||
self.pos -= 1;
|
self.pos -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance to the next line.
|
/// Advance to the next line.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn new_line(&mut self) {
|
pub(crate) fn new_line(&mut self) {
|
||||||
@ -117,24 +120,22 @@ impl Position {
|
|||||||
self.pos = 0;
|
self.pos = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Is this `Position` at the beginning of a line?
|
||||||
/// Create a `Position` representing no position.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn none() -> Self {
|
pub fn is_beginning_of_line(self) -> bool {
|
||||||
Self { line: 0, pos: 0 }
|
self.line == 0 && !self.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is there no `Position`?
|
/// Is there no `Position`?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_none(&self) -> bool {
|
pub fn is_none(self) -> bool {
|
||||||
self.line == 0 && self.pos == 0
|
self == Self::NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Position {
|
impl Default for Position {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(1, 0)
|
Self::START
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +328,7 @@ pub enum Token {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
As,
|
As,
|
||||||
/// A lexer error.
|
/// A lexer error.
|
||||||
LexError(Box<LexError>),
|
LexError(LexError),
|
||||||
/// A comment block.
|
/// A comment block.
|
||||||
Comment(String),
|
Comment(String),
|
||||||
/// A reserved symbol.
|
/// A reserved symbol.
|
||||||
@ -1117,9 +1118,7 @@ fn get_next_token_inner(
|
|||||||
INT::from_str_radix(&out, radix)
|
INT::from_str_radix(&out, radix)
|
||||||
.map(Token::IntegerConstant)
|
.map(Token::IntegerConstant)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
Token::LexError(Box::new(LERR::MalformedNumber(
|
Token::LexError(LERR::MalformedNumber(result.into_iter().collect()))
|
||||||
result.into_iter().collect(),
|
|
||||||
)))
|
|
||||||
}),
|
}),
|
||||||
start_pos,
|
start_pos,
|
||||||
));
|
));
|
||||||
@ -1133,9 +1132,7 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
return Some((
|
return Some((
|
||||||
num.unwrap_or_else(|_| {
|
num.unwrap_or_else(|_| {
|
||||||
Token::LexError(Box::new(LERR::MalformedNumber(
|
Token::LexError(LERR::MalformedNumber(result.into_iter().collect()))
|
||||||
result.into_iter().collect(),
|
|
||||||
)))
|
|
||||||
}),
|
}),
|
||||||
start_pos,
|
start_pos,
|
||||||
));
|
));
|
||||||
@ -1150,7 +1147,7 @@ fn get_next_token_inner(
|
|||||||
// " - string literal
|
// " - string literal
|
||||||
('"', _) => {
|
('"', _) => {
|
||||||
return parse_string_literal(stream, state, pos, '"').map_or_else(
|
return parse_string_literal(stream, state, pos, '"').map_or_else(
|
||||||
|err| Some((Token::LexError(Box::new(err.0)), err.1)),
|
|err| Some((Token::LexError(err.0), err.1)),
|
||||||
|out| Some((Token::StringConstant(out), start_pos)),
|
|out| Some((Token::StringConstant(out), start_pos)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1158,22 +1155,19 @@ fn get_next_token_inner(
|
|||||||
// ' - character literal
|
// ' - character literal
|
||||||
('\'', '\'') => {
|
('\'', '\'') => {
|
||||||
return Some((
|
return Some((
|
||||||
Token::LexError(Box::new(LERR::MalformedChar("".to_string()))),
|
Token::LexError(LERR::MalformedChar("".to_string())),
|
||||||
start_pos,
|
start_pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
('\'', _) => {
|
('\'', _) => {
|
||||||
return Some(parse_string_literal(stream, state, pos, '\'').map_or_else(
|
return Some(parse_string_literal(stream, state, pos, '\'').map_or_else(
|
||||||
|err| (Token::LexError(Box::new(err.0)), err.1),
|
|err| (Token::LexError(err.0), err.1),
|
||||||
|result| {
|
|result| {
|
||||||
let mut chars = result.chars();
|
let mut chars = result.chars();
|
||||||
let first = chars.next().unwrap();
|
let first = chars.next().unwrap();
|
||||||
|
|
||||||
if chars.next().is_some() {
|
if chars.next().is_some() {
|
||||||
(
|
(Token::LexError(LERR::MalformedChar(result)), start_pos)
|
||||||
Token::LexError(Box::new(LERR::MalformedChar(result))),
|
|
||||||
start_pos,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
(Token::CharConstant(first), start_pos)
|
(Token::CharConstant(first), start_pos)
|
||||||
}
|
}
|
||||||
@ -1449,7 +1443,7 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
(ch, _) => {
|
(ch, _) => {
|
||||||
return Some((
|
return Some((
|
||||||
Token::LexError(Box::new(LERR::UnexpectedInput(ch.to_string()))),
|
Token::LexError(LERR::UnexpectedInput(ch.to_string())),
|
||||||
start_pos,
|
start_pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -1491,7 +1485,7 @@ fn get_identifier(
|
|||||||
|
|
||||||
if !is_valid_identifier {
|
if !is_valid_identifier {
|
||||||
return Some((
|
return Some((
|
||||||
Token::LexError(Box::new(LERR::MalformedIdentifier(identifier))),
|
Token::LexError(LERR::MalformedIdentifier(identifier)),
|
||||||
start_pos,
|
start_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -1649,42 +1643,42 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
|
|||||||
Some((Token::Reserved(s), pos)) => Some((match
|
Some((Token::Reserved(s), pos)) => Some((match
|
||||||
(s.as_str(), self.engine.custom_keywords.contains_key(&s))
|
(s.as_str(), self.engine.custom_keywords.contains_key(&s))
|
||||||
{
|
{
|
||||||
("===", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
("===", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"'===' is not a valid operator. This is not JavaScript! Should it be '=='?".to_string(),
|
"'===' is not a valid operator. This is not JavaScript! Should it be '=='?".to_string(),
|
||||||
))),
|
)),
|
||||||
("!==", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
("!==", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"'!==' is not a valid operator. This is not JavaScript! Should it be '!='?".to_string(),
|
"'!==' is not a valid operator. This is not JavaScript! Should it be '!='?".to_string(),
|
||||||
))),
|
)),
|
||||||
("->", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
("->", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"'->' is not a valid symbol. This is not C or C++!".to_string()))),
|
"'->' is not a valid symbol. This is not C or C++!".to_string())),
|
||||||
("<-", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
("<-", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"'<-' is not a valid symbol. This is not Go! Should it be '<='?".to_string(),
|
"'<-' is not a valid symbol. This is not Go! Should it be '<='?".to_string(),
|
||||||
))),
|
)),
|
||||||
("=>", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
("=>", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"'=>' is not a valid symbol. This is not Rust! Should it be '>='?".to_string(),
|
"'=>' is not a valid symbol. This is not Rust! Should it be '>='?".to_string(),
|
||||||
))),
|
)),
|
||||||
(":=", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
(":=", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"':=' is not a valid assignment operator. This is not Go! Should it be simply '='?".to_string(),
|
"':=' is not a valid assignment operator. This is not Go! Should it be simply '='?".to_string(),
|
||||||
))),
|
)),
|
||||||
("::<", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
("::<", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"'::<>' is not a valid symbol. This is not Rust! Should it be '::'?".to_string(),
|
"'::<>' is not a valid symbol. This is not Rust! Should it be '::'?".to_string(),
|
||||||
))),
|
)),
|
||||||
("(*", false) | ("*)", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(),
|
"'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(),
|
||||||
))),
|
)),
|
||||||
("#", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
("#", false) => Token::LexError(LERR::ImproperSymbol(
|
||||||
"'#' is not a valid symbol. Should it be '#{'?".to_string(),
|
"'#' is not a valid symbol. Should it be '#{'?".to_string(),
|
||||||
))),
|
)),
|
||||||
// Reserved keyword/operator that is custom.
|
// Reserved keyword/operator that is custom.
|
||||||
(_, true) => Token::Custom(s),
|
(_, true) => Token::Custom(s),
|
||||||
// Reserved operator that is not custom.
|
// Reserved operator that is not custom.
|
||||||
(token, false) if !is_valid_identifier(token.chars()) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
(token, false) if !is_valid_identifier(token.chars()) => Token::LexError(LERR::ImproperSymbol(
|
||||||
format!("'{}' is a reserved symbol", token)
|
format!("'{}' is a reserved symbol", token)
|
||||||
))),
|
)),
|
||||||
// Reserved keyword that is not custom and disabled.
|
// Reserved keyword that is not custom and disabled.
|
||||||
(token, false) if self.engine.disabled_symbols.contains(token) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
(token, false) if self.engine.disabled_symbols.contains(token) => Token::LexError(LERR::ImproperSymbol(
|
||||||
format!("reserved symbol '{}' is disabled", token)
|
format!("reserved symbol '{}' is disabled", token)
|
||||||
))),
|
)),
|
||||||
// Reserved keyword/operator that is not custom.
|
// Reserved keyword/operator that is not custom.
|
||||||
(_, false) => Token::Reserved(s),
|
(_, false) => Token::Reserved(s),
|
||||||
}, pos)),
|
}, pos)),
|
||||||
@ -1705,7 +1699,7 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
|
|||||||
// Disabled operator
|
// Disabled operator
|
||||||
Some((token, pos)) if token.is_operator() && self.engine.disabled_symbols.contains(token.syntax().as_ref()) => {
|
Some((token, pos)) if token.is_operator() && self.engine.disabled_symbols.contains(token.syntax().as_ref()) => {
|
||||||
Some((
|
Some((
|
||||||
Token::LexError(Box::new(LexError::UnexpectedInput(token.syntax().into()))),
|
Token::LexError(LexError::UnexpectedInput(token.syntax().into())),
|
||||||
pos,
|
pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
|
use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -15,13 +15,28 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
*engine.consume("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
||||||
EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x"
|
EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x"
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_constant_scope() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
scope.push_constant("x", 42 as INT);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.consume_with_scope(&mut scope, "x = 1").expect_err("expects error"),
|
||||||
|
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "x"
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
|
fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
@ -38,7 +38,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 13, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -52,7 +52,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 100, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
engine.set_max_string_size(0);
|
engine.set_max_string_size(0);
|
||||||
@ -98,7 +98,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -112,7 +112,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 100, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@ -124,7 +124,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -137,7 +137,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@ -151,7 +151,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
engine.set_max_array_size(0);
|
engine.set_max_array_size(0);
|
||||||
@ -216,7 +216,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@ -228,7 +228,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -241,7 +241,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorDataTooLarge(_, 10, 12, _)
|
EvalAltResult::ErrorDataTooLarge(_, _)
|
||||||
));
|
));
|
||||||
|
|
||||||
engine.set_max_map_size(0);
|
engine.set_max_map_size(0);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![cfg(not(feature = "unchecked"))]
|
#![cfg(not(feature = "unchecked"))]
|
||||||
use rhai::{Engine, EvalAltResult};
|
use rhai::{Engine, EvalAltResult, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -10,7 +10,7 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
|||||||
if count % 100 == 0 {
|
if count % 100 == 0 {
|
||||||
println!("{}", count);
|
println!("{}", count);
|
||||||
}
|
}
|
||||||
true
|
None
|
||||||
});
|
});
|
||||||
|
|
||||||
engine.eval::<()>("let x = 0; while x < 20 { x += 1; }")?;
|
engine.eval::<()>("let x = 0; while x < 20 { x += 1; }")?;
|
||||||
@ -38,7 +38,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
if count % 100 == 0 {
|
if count % 100 == 0 {
|
||||||
println!("{}", count);
|
println!("{}", count);
|
||||||
}
|
}
|
||||||
true
|
None
|
||||||
});
|
});
|
||||||
|
|
||||||
engine.eval::<()>(
|
engine.eval::<()>(
|
||||||
@ -94,7 +94,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
if count % 100 == 0 {
|
if count % 100 == 0 {
|
||||||
println!("{}", count);
|
println!("{}", count);
|
||||||
}
|
}
|
||||||
true
|
None
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@ -117,13 +117,19 @@ fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.set_max_operations(500);
|
engine.set_max_operations(500);
|
||||||
|
|
||||||
engine.on_progress(|&count| count < 100);
|
engine.on_progress(|&count| {
|
||||||
|
if count < 100 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((42 as INT).into())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>("for x in range(0, 500) {}")
|
.eval::<()>("for x in range(0, 500) {}")
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorTerminated(_)
|
EvalAltResult::ErrorTerminated(x, _) if x.as_int()? == 42
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, ParseError, ParseErrorType, Position, INT};
|
use rhai::{Engine, EvalAltResult, LexError, ParseError, ParseErrorType, INT, NO_POS};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -68,9 +68,9 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
.register_custom_syntax(&["!"], 0, |_, _| Ok(().into()))
|
.register_custom_syntax(&["!"], 0, |_, _| Ok(().into()))
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
.0,
|
.0,
|
||||||
ParseErrorType::BadInput(
|
ParseErrorType::BadInput(LexError::ImproperSymbol(
|
||||||
"Improper symbol for custom syntax at position #1: '!'".to_string()
|
"Improper symbol for custom syntax at position #1: '!'".to_string()
|
||||||
)
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -88,8 +88,10 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
2 => match stream[1].as_str() {
|
2 => match stream[1].as_str() {
|
||||||
"world" | "kitty" => Ok(None),
|
"world" | "kitty" => Ok(None),
|
||||||
s => Err(ParseError(
|
s => Err(ParseError(
|
||||||
Box::new(ParseErrorType::BadInput(s.to_string())),
|
Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol(
|
||||||
Position::none(),
|
s.to_string(),
|
||||||
|
))),
|
||||||
|
NO_POS,
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -109,7 +111,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>("hello kitty")?, 42);
|
assert_eq!(engine.eval::<INT>("hello kitty")?, 42);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine.compile("hello hey").expect_err("should error").0,
|
*engine.compile("hello hey").expect_err("should error").0,
|
||||||
ParseErrorType::BadInput("hey".to_string())
|
ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string()))
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, ParseErrorType, RegisterFn, INT};
|
use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, RegisterFn, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tokens_disabled() {
|
fn test_tokens_disabled() {
|
||||||
@ -16,10 +16,13 @@ fn test_tokens_disabled() {
|
|||||||
|
|
||||||
engine.disable_symbol("+="); // disable the '+=' operator
|
engine.disable_symbol("+="); // disable the '+=' operator
|
||||||
|
|
||||||
assert!(matches!(
|
assert_eq!(
|
||||||
*engine.compile("let x = 40 + 2; x += 1;").expect_err("should error").0,
|
*engine
|
||||||
ParseErrorType::BadInput(ref s) if s == "Unexpected '+='"
|
.compile("let x = 40 + 2; x += 1;")
|
||||||
));
|
.expect_err("should error")
|
||||||
|
.0,
|
||||||
|
ParseErrorType::BadInput(LexError::UnexpectedInput("+=".to_string()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, Position, Scope, INT};
|
use rhai::{Engine, EvalAltResult, Scope, INT, NO_POS};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_var_scope() -> Result<(), Box<EvalAltResult>> {
|
fn test_var_scope() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -67,7 +67,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
||||||
// Override a variable - make it not found even if it exists!
|
// Override a variable - make it not found even if it exists!
|
||||||
"DO_NOT_USE" => {
|
"DO_NOT_USE" => {
|
||||||
Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into())
|
Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS).into())
|
||||||
}
|
}
|
||||||
// Silently maps 'chameleon' into 'innocent'.
|
// Silently maps 'chameleon' into 'innocent'.
|
||||||
"chameleon" => context
|
"chameleon" => context
|
||||||
@ -75,7 +75,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
.get_value("innocent")
|
.get_value("innocent")
|
||||||
.map(Some)
|
.map(Some)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::none()).into()
|
EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS).into()
|
||||||
}),
|
}),
|
||||||
// Return Ok(None) to continue with the normal variable resolution process.
|
// Return Ok(None) to continue with the normal variable resolution process.
|
||||||
_ => Ok(None),
|
_ => Ok(None),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user