Do not omit error enum variants.
This commit is contained in:
parent
3609150dcf
commit
adbfceb5be
37
README.md
37
README.md
@ -314,9 +314,9 @@ Use `Engine::new_raw` to create a _raw_ `Engine`, in which:
|
|||||||
* the _standard library_ of utility functions is _not_ loaded by default (load it using the `register_stdlib` method).
|
* the _standard library_ of utility functions is _not_ loaded by default (load it using the `register_stdlib` method).
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let mut engine = Engine::new_raw(); // Create a 'raw' Engine
|
let mut engine = Engine::new_raw(); // create a 'raw' Engine
|
||||||
|
|
||||||
engine.register_stdlib(); // Register the standard library manually
|
engine.register_stdlib(); // register the standard library manually
|
||||||
|
|
||||||
engine.
|
engine.
|
||||||
```
|
```
|
||||||
@ -439,7 +439,7 @@ The `cast` method (from the `rhai::AnyExt` trait) then converts the value into a
|
|||||||
Alternatively, use the `try_cast` method which does not panic but returns an error when the cast fails.
|
Alternatively, use the `try_cast` method which does not panic but returns an error when the cast fails.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::AnyExt; // Pull in the trait.
|
use rhai::AnyExt; // pull in the trait.
|
||||||
|
|
||||||
let list: Array = engine.eval("...")?; // return type is 'Array'
|
let list: Array = engine.eval("...")?; // return type is 'Array'
|
||||||
let item = list[0]; // an element in an 'Array' is 'Dynamic'
|
let item = list[0]; // an element in an 'Array' is 'Dynamic'
|
||||||
@ -455,14 +455,14 @@ let value = item.try_cast::<i64>()?; // 'try_cast' does not panic whe
|
|||||||
The `type_name` method gets the name of the actual type as a static string slice, which you may match against.
|
The `type_name` method gets the name of the actual type as a static string slice, which you may match against.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::Any; // Pull in the trait.
|
use rhai::Any; // pull in the trait.
|
||||||
|
|
||||||
let list: Array = engine.eval("...")?; // return type is 'Array'
|
let list: Array = engine.eval("...")?; // return type is 'Array'
|
||||||
let item = list[0]; // an element in an 'Array' is 'Dynamic'
|
let item = list[0]; // an element in an 'Array' is 'Dynamic'
|
||||||
|
|
||||||
match item.type_name() { // 'type_name' returns the name of the actual Rust type
|
match item.type_name() { // 'type_name' returns the name of the actual Rust type
|
||||||
"i64" => ...
|
"i64" => ...
|
||||||
"std::string::String" => ...
|
"alloc::string::String" => ...
|
||||||
"bool" => ...
|
"bool" => ...
|
||||||
"path::to::module::TestStruct" => ...
|
"path::to::module::TestStruct" => ...
|
||||||
}
|
}
|
||||||
@ -548,7 +548,7 @@ To return a [`Dynamic`] value from a Rust function, use the `into_dynamic()` met
|
|||||||
(under the `rhai::Any` trait) to convert it.
|
(under the `rhai::Any` trait) to convert it.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::Any; // Pull in the trait
|
use rhai::Any; // pull in the trait
|
||||||
|
|
||||||
fn decide(yes_no: bool) -> Dynamic {
|
fn decide(yes_no: bool) -> Dynamic {
|
||||||
if yes_no {
|
if yes_no {
|
||||||
@ -598,13 +598,13 @@ and the error text gets converted into `EvalAltResult::ErrorRuntime`.
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, EvalAltResult, Position};
|
use rhai::{Engine, EvalAltResult, Position};
|
||||||
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
|
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
|
||||||
|
|
||||||
// Function that may fail
|
// Function that may fail
|
||||||
fn safe_divide(x: i64, y: i64) -> Result<i64, EvalAltResult> {
|
fn safe_divide(x: i64, y: i64) -> Result<i64, EvalAltResult> {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
// Return an error if y is zero
|
// Return an error if y is zero
|
||||||
Err("Division by zero detected!".into()) // short-cut to create EvalAltResult
|
Err("Division by zero!".into()) // short-cut to create EvalAltResult
|
||||||
} else {
|
} else {
|
||||||
Ok(x / y)
|
Ok(x / y)
|
||||||
}
|
}
|
||||||
@ -618,7 +618,7 @@ fn main()
|
|||||||
engine.register_result_fn("divide", safe_divide);
|
engine.register_result_fn("divide", safe_divide);
|
||||||
|
|
||||||
if let Err(error) = engine.eval::<i64>("divide(40, 0)") {
|
if let Err(error) = engine.eval::<i64>("divide(40, 0)") {
|
||||||
println!("Error: {:?}", error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
|
println!("Error: {:?}", error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -672,7 +672,7 @@ fn main() -> Result<(), EvalAltResult>
|
|||||||
|
|
||||||
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
|
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
|
||||||
|
|
||||||
println!("result: {}", result.field); // prints 42
|
println!("result: {}", result.field); // prints 42
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1329,7 +1329,7 @@ foo == 42;
|
|||||||
y.has("a") == true;
|
y.has("a") == true;
|
||||||
y.has("xyz") == false;
|
y.has("xyz") == false;
|
||||||
|
|
||||||
y.xyz == (); // A non-existing property returns '()'
|
y.xyz == (); // a non-existing property returns '()'
|
||||||
y["xyz"] == ();
|
y["xyz"] == ();
|
||||||
|
|
||||||
print(y.len()); // prints 3
|
print(y.len()); // prints 3
|
||||||
@ -1449,10 +1449,11 @@ if (decision) print("I've decided!");
|
|||||||
Like Rust, `if` statements can also be used as _expressions_, replacing the `? :` conditional operators in other C-like languages.
|
Like Rust, `if` statements can also be used as _expressions_, replacing the `? :` conditional operators in other C-like languages.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = 1 + if true { 42 } else { 123 } / 2;
|
// The following is equivalent to C: int x = 1 + (decision ? 42 : 123) / 2;
|
||||||
|
let x = 1 + if decision { 42 } else { 123 } / 2;
|
||||||
x == 22;
|
x == 22;
|
||||||
|
|
||||||
let x = if false { 42 }; // No else branch defaults to '()'
|
let x = if decision { 42 }; // no else branch defaults to '()'
|
||||||
x == ();
|
x == ();
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1506,7 +1507,6 @@ for x in range(0, 50) {
|
|||||||
if x == 42 { break; } // break out of for loop
|
if x == 42 { break; } // break out of for loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The 'range' function also takes a step
|
// The 'range' function also takes a step
|
||||||
for x in range(0, 50, 3) { // step by 3
|
for x in range(0, 50, 3) { // step by 3
|
||||||
if x > 10 { continue; } // skip to the next iteration
|
if x > 10 { continue; } // skip to the next iteration
|
||||||
@ -1514,15 +1514,20 @@ for x in range(0, 50, 3) { // step by 3
|
|||||||
if x == 42 { break; } // break out of for loop
|
if x == 42 { break; } // break out of for loop
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through the values of an object map
|
// Iterate through object map
|
||||||
let map = #{a:1, b:3, c:5, d:7, e:9};
|
let map = #{a:1, b:3, c:5, d:7, e:9};
|
||||||
|
|
||||||
// Remember that keys are returned in random order
|
// Property names are returned in random order
|
||||||
for x in keys(map) {
|
for x in keys(map) {
|
||||||
if x > 10 { continue; } // skip to the next iteration
|
if x > 10 { continue; } // skip to the next iteration
|
||||||
print(x);
|
print(x);
|
||||||
if x == 42 { break; } // break out of for loop
|
if x == 42 { break; } // break out of for loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Property values are returned in random order
|
||||||
|
for val in values(map) {
|
||||||
|
print(val);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`return`-ing values
|
`return`-ing values
|
||||||
|
15
src/api.rs
15
src/api.rs
@ -5,13 +5,11 @@ use crate::engine::{make_getter, make_setter, Engine, FnAny, FnSpec};
|
|||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
use crate::fn_call::FuncArgs;
|
use crate::fn_call::FuncArgs;
|
||||||
use crate::fn_register::RegisterFn;
|
use crate::fn_register::RegisterFn;
|
||||||
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||||
use crate::parser::{lex, parse, parse_global_expr, Position, AST};
|
use crate::parser::{lex, parse, parse_global_expr, Position, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -387,12 +385,7 @@ impl<'e> Engine<'e> {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> {
|
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_with_scope_and_optimization_level(
|
self.compile_with_scope_and_optimization_level(scope, script, self.optimization_level)
|
||||||
scope,
|
|
||||||
script,
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
self.optimization_level,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile a string into an `AST` using own scope at a specific optimization level.
|
/// Compile a string into an `AST` using own scope at a specific optimization level.
|
||||||
@ -400,7 +393,7 @@ impl<'e> Engine<'e> {
|
|||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
script: &str,
|
script: &str,
|
||||||
#[cfg(not(feature = "no_optimize"))] optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let tokens_stream = lex(script);
|
let tokens_stream = lex(script);
|
||||||
|
|
||||||
@ -408,7 +401,6 @@ impl<'e> Engine<'e> {
|
|||||||
&mut tokens_stream.peekable(),
|
&mut tokens_stream.peekable(),
|
||||||
self,
|
self,
|
||||||
scope,
|
scope,
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
optimization_level,
|
optimization_level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -821,7 +813,6 @@ impl<'e> Engine<'e> {
|
|||||||
&mut tokens_stream.peekable(),
|
&mut tokens_stream.peekable(),
|
||||||
self,
|
self,
|
||||||
scope,
|
scope,
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
self.optimization_level,
|
self.optimization_level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
333
src/engine.rs
333
src/engine.rs
@ -2,13 +2,11 @@
|
|||||||
|
|
||||||
use crate::any::{Any, AnyExt, Dynamic, Variant};
|
use crate::any::{Any, AnyExt, Dynamic, Variant};
|
||||||
use crate::error::ParseErrorType;
|
use crate::error::ParseErrorType;
|
||||||
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::parser::{Expr, FnDef, Position, ReturnType, Stmt, INT};
|
use crate::parser::{Expr, FnDef, Position, ReturnType, Stmt, INT};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
use crate::optimize::OptimizationLevel;
|
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -28,13 +26,11 @@ use crate::stdlib::{
|
|||||||
/// An dynamic array of `Dynamic` values.
|
/// An dynamic array of `Dynamic` values.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_index` feature.
|
/// Not available under the `no_index` feature.
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
pub type Array = Vec<Dynamic>;
|
pub type Array = Vec<Dynamic>;
|
||||||
|
|
||||||
/// An dynamic hash map of `Dynamic` values with `String` keys.
|
/// An dynamic hash map of `Dynamic` values with `String` keys.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_object` feature.
|
/// Not available under the `no_object` feature.
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
pub type Map = HashMap<String, Dynamic>;
|
pub type Map = HashMap<String, Dynamic>;
|
||||||
|
|
||||||
pub type FnCallArgs<'a> = [&'a mut Variant];
|
pub type FnCallArgs<'a> = [&'a mut Variant];
|
||||||
@ -57,7 +53,6 @@ pub const MAX_CALL_STACK_DEPTH: usize = 256;
|
|||||||
|
|
||||||
pub const KEYWORD_PRINT: &str = "print";
|
pub const KEYWORD_PRINT: &str = "print";
|
||||||
pub const KEYWORD_DEBUG: &str = "debug";
|
pub const KEYWORD_DEBUG: &str = "debug";
|
||||||
pub const KEYWORD_DUMP_AST: &str = "dump_ast";
|
|
||||||
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
||||||
pub const KEYWORD_EVAL: &str = "eval";
|
pub const KEYWORD_EVAL: &str = "eval";
|
||||||
pub const FUNC_TO_STRING: &str = "to_string";
|
pub const FUNC_TO_STRING: &str = "to_string";
|
||||||
@ -65,12 +60,10 @@ pub const FUNC_GETTER: &str = "get$";
|
|||||||
pub const FUNC_SETTER: &str = "set$";
|
pub const FUNC_SETTER: &str = "set$";
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
enum IndexSourceType {
|
enum IndexSourceType {
|
||||||
Expression,
|
Expression,
|
||||||
String,
|
String,
|
||||||
Array,
|
Array,
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Map,
|
Map,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,7 +265,6 @@ pub struct Engine<'e> {
|
|||||||
pub(crate) on_debug: Option<Box<dyn Fn(&str) + 'e>>,
|
pub(crate) on_debug: Option<Box<dyn Fn(&str) + 'e>>,
|
||||||
|
|
||||||
/// Optimize the AST after compilation.
|
/// Optimize the AST after compilation.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
pub(crate) optimization_level: OptimizationLevel,
|
pub(crate) optimization_level: OptimizationLevel,
|
||||||
|
|
||||||
/// Maximum levels of call-stack to prevent infinite recursion.
|
/// Maximum levels of call-stack to prevent infinite recursion.
|
||||||
@ -290,18 +282,13 @@ impl Default for Engine<'_> {
|
|||||||
type_names: None,
|
type_names: None,
|
||||||
|
|
||||||
// default print/debug implementations
|
// default print/debug implementations
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
on_print: Some(Box::new(default_print)),
|
on_print: Some(Box::new(default_print)),
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
on_debug: Some(Box::new(default_print)),
|
on_debug: Some(Box::new(default_print)),
|
||||||
|
|
||||||
// default print/debug implementations
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
on_print: None,
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
on_debug: None,
|
|
||||||
|
|
||||||
// optimization level
|
// optimization level
|
||||||
|
#[cfg(feature = "no_optimize")]
|
||||||
|
optimization_level: OptimizationLevel::None,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
#[cfg(not(feature = "optimize_full"))]
|
#[cfg(not(feature = "optimize_full"))]
|
||||||
optimization_level: OptimizationLevel::Simple,
|
optimization_level: OptimizationLevel::Simple,
|
||||||
@ -330,9 +317,16 @@ pub fn make_getter(id: &str) -> String {
|
|||||||
|
|
||||||
/// Extract the property name from a getter function name.
|
/// Extract the property name from a getter function name.
|
||||||
fn extract_prop_from_getter(fn_name: &str) -> Option<&str> {
|
fn extract_prop_from_getter(fn_name: &str) -> Option<&str> {
|
||||||
if fn_name.starts_with(FUNC_GETTER) {
|
#[cfg(not(feature = "no_object"))]
|
||||||
Some(&fn_name[FUNC_GETTER.len()..])
|
{
|
||||||
} else {
|
if fn_name.starts_with(FUNC_GETTER) {
|
||||||
|
Some(&fn_name[FUNC_GETTER.len()..])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_object")]
|
||||||
|
{
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,9 +338,16 @@ pub fn make_setter(id: &str) -> String {
|
|||||||
|
|
||||||
/// Extract the property name from a setter function name.
|
/// Extract the property name from a setter function name.
|
||||||
fn extract_prop_from_setter(fn_name: &str) -> Option<&str> {
|
fn extract_prop_from_setter(fn_name: &str) -> Option<&str> {
|
||||||
if fn_name.starts_with(FUNC_SETTER) {
|
#[cfg(not(feature = "no_object"))]
|
||||||
Some(&fn_name[FUNC_SETTER.len()..])
|
{
|
||||||
} else {
|
if fn_name.starts_with(FUNC_SETTER) {
|
||||||
|
Some(&fn_name[FUNC_SETTER.len()..])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_object")]
|
||||||
|
{
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,6 +385,9 @@ impl Engine<'_> {
|
|||||||
on_print: None,
|
on_print: None,
|
||||||
on_debug: None,
|
on_debug: None,
|
||||||
|
|
||||||
|
#[cfg(feature = "no_optimize")]
|
||||||
|
optimization_level: OptimizationLevel::None,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
#[cfg(not(feature = "optimize_full"))]
|
#[cfg(not(feature = "optimize_full"))]
|
||||||
optimization_level: OptimizationLevel::Simple,
|
optimization_level: OptimizationLevel::Simple,
|
||||||
@ -431,6 +435,9 @@ impl Engine<'_> {
|
|||||||
return Err(EvalAltResult::ErrorStackOverflow(pos));
|
return Err(EvalAltResult::ErrorStackOverflow(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
const fn_lib: Option<&FunctionsLib> = None;
|
||||||
|
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if let Some(lib) = fn_lib {
|
if let Some(lib) = fn_lib {
|
||||||
if let Some(fn_def) = lib.get_function(fn_name, args.len()) {
|
if let Some(fn_def) = lib.get_function(fn_name, args.len()) {
|
||||||
@ -523,12 +530,9 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
||||||
#[cfg(not(feature = "no_object"))]
|
// Map property access
|
||||||
{
|
if let Some(map) = args[0].downcast_ref::<Map>() {
|
||||||
// Map property access
|
return Ok(map.get(prop).cloned().unwrap_or_else(|| ().into_dynamic()));
|
||||||
if let Some(map) = args[0].downcast_ref::<Map>() {
|
|
||||||
return Ok(map.get(prop).cloned().unwrap_or_else(|| ().into_dynamic()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getter function not found
|
// Getter function not found
|
||||||
@ -539,15 +543,12 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prop) = extract_prop_from_setter(fn_name) {
|
if let Some(prop) = extract_prop_from_setter(fn_name) {
|
||||||
#[cfg(not(feature = "no_object"))]
|
let value = args[1].into_dynamic();
|
||||||
{
|
|
||||||
let value = args[1].into_dynamic();
|
|
||||||
|
|
||||||
// Map property update
|
// Map property update
|
||||||
if let Some(map) = args[0].downcast_mut::<Map>() {
|
if let Some(map) = args[0].downcast_mut::<Map>() {
|
||||||
map.insert(prop.to_string(), value);
|
map.insert(prop.to_string(), value);
|
||||||
return Ok(().into_dynamic());
|
return Ok(().into_dynamic());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setter function not found
|
// Setter function not found
|
||||||
@ -576,7 +577,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Chain-evaluate a dot setter.
|
/// Chain-evaluate a dot setter.
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
fn get_dot_val_helper(
|
fn get_dot_val_helper(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -611,7 +611,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// xxx.idx_lhs[idx_expr]
|
// xxx.idx_lhs[idx_expr]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
let value = match idx_lhs.as_ref() {
|
let value = match idx_lhs.as_ref() {
|
||||||
// xxx.id[idx_expr]
|
// xxx.id[idx_expr]
|
||||||
@ -648,7 +647,6 @@ impl Engine<'_> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// xxx.idx_lhs[idx_expr].rhs
|
// xxx.idx_lhs[idx_expr].rhs
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
let val = match idx_lhs.as_ref() {
|
let val = match idx_lhs.as_ref() {
|
||||||
// xxx.id[idx_expr].rhs
|
// xxx.id[idx_expr].rhs
|
||||||
@ -692,7 +690,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a dot chain getter
|
/// Evaluate a dot chain getter
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
fn get_dot_val(
|
fn get_dot_val(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -715,9 +712,8 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// idx_lhs[idx_expr].???
|
// idx_lhs[idx_expr].???
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
let (idx_src_type, src, idx, mut val) =
|
let (idx_src_type, src, index, mut val) =
|
||||||
self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?;
|
self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?;
|
||||||
let target = Target::from(val.as_mut());
|
let target = Target::from(val.as_mut());
|
||||||
let value = self.get_dot_val_helper(scope, fn_lib, target, dot_rhs, level);
|
let value = self.get_dot_val_helper(scope, fn_lib, target, dot_rhs, level);
|
||||||
@ -736,7 +732,7 @@ impl Engine<'_> {
|
|||||||
idx_src_type,
|
idx_src_type,
|
||||||
scope,
|
scope,
|
||||||
src,
|
src,
|
||||||
idx,
|
index,
|
||||||
(val, dot_rhs.position()),
|
(val, dot_rhs.position()),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@ -766,7 +762,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value at the indexed position of a base type
|
/// Get the value at the indexed position of a base type
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
fn get_indexed_value(
|
fn get_indexed_value(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -780,62 +775,61 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// val_array[idx]
|
// val_array[idx]
|
||||||
if let Some(arr) = val.downcast_ref::<Array>() {
|
if let Some(arr) = val.downcast_ref::<Array>() {
|
||||||
let idx = self
|
let index = self
|
||||||
.eval_expr(scope, fn_lib, idx_expr, level)?
|
.eval_expr(scope, fn_lib, idx_expr, level)?
|
||||||
.try_cast::<INT>()
|
.try_cast::<INT>()
|
||||||
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
|
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
|
||||||
|
|
||||||
return if idx >= 0 {
|
return if index >= 0 {
|
||||||
arr.get(idx as usize)
|
arr.get(index as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|v| (v, IndexSourceType::Array, IndexValue::from_num(idx)))
|
.map(|v| (v, IndexSourceType::Array, IndexValue::from_num(index)))
|
||||||
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos))
|
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos))
|
||||||
} else {
|
} else {
|
||||||
Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos))
|
Err(EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
// val_map[idx]
|
||||||
{
|
if let Some(map) = val.downcast_ref::<Map>() {
|
||||||
// val_map[idx]
|
let index = self
|
||||||
if let Some(map) = val.downcast_ref::<Map>() {
|
.eval_expr(scope, fn_lib, idx_expr, level)?
|
||||||
let idx = self
|
.try_cast::<String>()
|
||||||
.eval_expr(scope, fn_lib, idx_expr, level)?
|
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?;
|
||||||
.try_cast::<String>()
|
|
||||||
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?;
|
|
||||||
|
|
||||||
return Ok((
|
return Ok((
|
||||||
map.get(&idx).cloned().unwrap_or_else(|| ().into_dynamic()),
|
map.get(&index)
|
||||||
IndexSourceType::Map,
|
.cloned()
|
||||||
IndexValue::from_str(idx),
|
.unwrap_or_else(|| ().into_dynamic()),
|
||||||
));
|
IndexSourceType::Map,
|
||||||
}
|
IndexValue::from_str(index),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// val_string[idx]
|
// val_string[idx]
|
||||||
if let Some(s) = val.downcast_ref::<String>() {
|
if let Some(s) = val.downcast_ref::<String>() {
|
||||||
let idx = self
|
let index = self
|
||||||
.eval_expr(scope, fn_lib, idx_expr, level)?
|
.eval_expr(scope, fn_lib, idx_expr, level)?
|
||||||
.try_cast::<INT>()
|
.try_cast::<INT>()
|
||||||
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
|
.map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?;
|
||||||
|
|
||||||
return if idx >= 0 {
|
return if index >= 0 {
|
||||||
s.chars()
|
s.chars()
|
||||||
.nth(idx as usize)
|
.nth(index as usize)
|
||||||
.map(|ch| {
|
.map(|ch| {
|
||||||
(
|
(
|
||||||
ch.into_dynamic(),
|
ch.into_dynamic(),
|
||||||
IndexSourceType::String,
|
IndexSourceType::String,
|
||||||
IndexValue::from_num(idx),
|
IndexValue::from_num(index),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
EvalAltResult::ErrorStringBounds(s.chars().count(), idx, idx_pos)
|
EvalAltResult::ErrorStringBounds(s.chars().count(), index, idx_pos)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(EvalAltResult::ErrorStringBounds(
|
Err(EvalAltResult::ErrorStringBounds(
|
||||||
s.chars().count(),
|
s.chars().count(),
|
||||||
idx,
|
index,
|
||||||
idx_pos,
|
idx_pos,
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
@ -849,7 +843,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an index expression
|
/// Evaluate an index expression
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
fn eval_index_expr<'a>(
|
fn eval_index_expr<'a>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -879,7 +872,7 @@ impl Engine<'_> {
|
|||||||
val,
|
val,
|
||||||
) = Self::search_scope(scope, &id, lhs.position())?;
|
) = Self::search_scope(scope, &id, lhs.position())?;
|
||||||
|
|
||||||
let (val, idx_src_type, idx) =
|
let (val, idx_src_type, index) =
|
||||||
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)?;
|
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
@ -889,7 +882,7 @@ impl Engine<'_> {
|
|||||||
typ: src_type,
|
typ: src_type,
|
||||||
index: src_idx,
|
index: src_idx,
|
||||||
}),
|
}),
|
||||||
idx,
|
index,
|
||||||
val,
|
val,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -899,13 +892,12 @@ impl Engine<'_> {
|
|||||||
let val = self.eval_expr(scope, fn_lib, expr, level)?;
|
let val = self.eval_expr(scope, fn_lib, expr, level)?;
|
||||||
|
|
||||||
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)
|
self.get_indexed_value(scope, fn_lib, &val, idx_expr, op_pos, level)
|
||||||
.map(|(val, _, idx)| (IndexSourceType::Expression, None, idx, val))
|
.map(|(val, _, index)| (IndexSourceType::Expression, None, index, val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace a character at an index position in a mutable string
|
/// Replace a character at an index position in a mutable string
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
fn str_replace_char(s: &mut String, idx: usize, new_ch: char) {
|
fn str_replace_char(s: &mut String, idx: usize, new_ch: char) {
|
||||||
let mut chars: Vec<char> = s.chars().collect();
|
let mut chars: Vec<char> = s.chars().collect();
|
||||||
let ch = *chars.get(idx).expect("string index out of bounds");
|
let ch = *chars.get(idx).expect("string index out of bounds");
|
||||||
@ -919,7 +911,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the value at an index position in a variable inside the scope
|
/// Update the value at an index position in a variable inside the scope
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
fn update_indexed_var_in_scope(
|
fn update_indexed_var_in_scope(
|
||||||
idx_src_type: IndexSourceType,
|
idx_src_type: IndexSourceType,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -936,7 +927,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// map_id[idx] = val
|
// map_id[idx] = val
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
IndexSourceType::Map => {
|
IndexSourceType::Map => {
|
||||||
let arr = scope.get_mut_by_type::<Map>(src);
|
let arr = scope.get_mut_by_type::<Map>(src);
|
||||||
arr.insert(idx.as_str(), new_val.0);
|
arr.insert(idx.as_str(), new_val.0);
|
||||||
@ -961,7 +951,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the value at an index position
|
/// Update the value at an index position
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
fn update_indexed_value(
|
fn update_indexed_value(
|
||||||
mut target: Dynamic,
|
mut target: Dynamic,
|
||||||
idx: IndexValue,
|
idx: IndexValue,
|
||||||
@ -973,12 +962,9 @@ impl Engine<'_> {
|
|||||||
return Ok(target);
|
return Ok(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
if let Some(map) = target.downcast_mut::<Map>() {
|
||||||
{
|
map.insert(idx.as_str(), new_val);
|
||||||
if let Some(map) = target.downcast_mut::<Map>() {
|
return Ok(target);
|
||||||
map.insert(idx.as_str(), new_val);
|
|
||||||
return Ok(target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = target.downcast_mut::<String>() {
|
if let Some(s) = target.downcast_mut::<String>() {
|
||||||
@ -995,7 +981,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Chain-evaluate a dot setter
|
/// Chain-evaluate a dot setter
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
fn set_dot_val_helper(
|
fn set_dot_val_helper(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1014,17 +999,16 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// xxx.lhs[idx_expr]
|
// xxx.lhs[idx_expr]
|
||||||
// TODO - Allow chaining of indexing!
|
// TODO - Allow chaining of indexing!
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() {
|
Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() {
|
||||||
// xxx.id[idx_expr]
|
// xxx.id[idx_expr]
|
||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let fn_name = make_getter(id);
|
let fn_name = make_getter(id);
|
||||||
self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0)
|
self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0)
|
||||||
.and_then(|val| {
|
.and_then(|val| {
|
||||||
let (_, _, idx) = self
|
let (_, _, index) = self
|
||||||
.get_indexed_value(scope, fn_lib, &val, idx_expr, *op_pos, level)?;
|
.get_indexed_value(scope, fn_lib, &val, idx_expr, *op_pos, level)?;
|
||||||
|
|
||||||
Self::update_indexed_value(val, idx, new_val.0.clone(), new_val.1)
|
Self::update_indexed_value(val, index, new_val.0.clone(), new_val.1)
|
||||||
})
|
})
|
||||||
.and_then(|mut val| {
|
.and_then(|mut val| {
|
||||||
let fn_name = make_setter(id);
|
let fn_name = make_setter(id);
|
||||||
@ -1060,14 +1044,13 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// xxx.lhs[idx_expr].rhs
|
// xxx.lhs[idx_expr].rhs
|
||||||
// TODO - Allow chaining of indexing!
|
// TODO - Allow chaining of indexing!
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() {
|
Expr::Index(lhs, idx_expr, op_pos) => match lhs.as_ref() {
|
||||||
// xxx.id[idx_expr].rhs
|
// xxx.id[idx_expr].rhs
|
||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let fn_name = make_getter(id);
|
let fn_name = make_getter(id);
|
||||||
self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0)
|
self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0)
|
||||||
.and_then(|v| {
|
.and_then(|v| {
|
||||||
let (mut value, _, idx) = self.get_indexed_value(
|
let (mut value, _, index) = self.get_indexed_value(
|
||||||
scope, fn_lib, &v, idx_expr, *op_pos, level,
|
scope, fn_lib, &v, idx_expr, *op_pos, level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1078,7 +1061,7 @@ impl Engine<'_> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||||
Self::update_indexed_value(v, idx, value, val_pos)
|
Self::update_indexed_value(v, index, value, val_pos)
|
||||||
})
|
})
|
||||||
.and_then(|mut v| {
|
.and_then(|mut v| {
|
||||||
let fn_name = make_setter(id);
|
let fn_name = make_setter(id);
|
||||||
@ -1110,7 +1093,6 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate a dot chain setter
|
// Evaluate a dot chain setter
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
fn set_dot_val(
|
fn set_dot_val(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1148,9 +1130,8 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
// lhs[idx_expr].???
|
// lhs[idx_expr].???
|
||||||
// TODO - Allow chaining of indexing!
|
// TODO - Allow chaining of indexing!
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(lhs, idx_expr, op_pos) => {
|
Expr::Index(lhs, idx_expr, op_pos) => {
|
||||||
let (idx_src_type, src, idx, mut target) =
|
let (idx_src_type, src, index, mut target) =
|
||||||
self.eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level)?;
|
self.eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level)?;
|
||||||
let val_pos = new_val.1;
|
let val_pos = new_val.1;
|
||||||
let this_ptr = target.as_mut();
|
let this_ptr = target.as_mut();
|
||||||
@ -1171,7 +1152,7 @@ impl Engine<'_> {
|
|||||||
idx_src_type,
|
idx_src_type,
|
||||||
scope,
|
scope,
|
||||||
src,
|
src,
|
||||||
idx,
|
index,
|
||||||
(target, val_pos),
|
(target, val_pos),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@ -1201,56 +1182,46 @@ impl Engine<'_> {
|
|||||||
let mut lhs_value = self.eval_expr(scope, fn_lib, lhs, level)?;
|
let mut lhs_value = self.eval_expr(scope, fn_lib, lhs, level)?;
|
||||||
let rhs_value = self.eval_expr(scope, fn_lib, rhs, level)?;
|
let rhs_value = self.eval_expr(scope, fn_lib, rhs, level)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
if rhs_value.is::<Array>() {
|
||||||
{
|
let mut rhs_value = rhs_value.cast::<Array>();
|
||||||
if rhs_value.is::<Array>() {
|
let def_value = false.into_dynamic();
|
||||||
let mut rhs_value = rhs_value.cast::<Array>();
|
let mut result = false;
|
||||||
let def_value = false.into_dynamic();
|
|
||||||
let mut result = false;
|
|
||||||
|
|
||||||
// Call the '==' operator to compare each value
|
// Call the '==' operator to compare each value
|
||||||
for value in rhs_value.iter_mut() {
|
for value in rhs_value.iter_mut() {
|
||||||
let args = &mut [lhs_value.as_mut(), value.as_mut()];
|
let args = &mut [lhs_value.as_mut(), value.as_mut()];
|
||||||
let def_value = Some(&def_value);
|
let def_value = Some(&def_value);
|
||||||
if self
|
if self
|
||||||
.call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)?
|
.call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)?
|
||||||
.try_cast::<bool>()
|
.try_cast::<bool>()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
result = true;
|
result = true;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(result.into_dynamic());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
Ok(result.into_dynamic())
|
||||||
{
|
} else if rhs_value.is::<Map>() {
|
||||||
if rhs_value.is::<Map>() {
|
let rhs_value = rhs_value.cast::<Map>();
|
||||||
let rhs_value = rhs_value.cast::<Map>();
|
|
||||||
|
|
||||||
// Only allows String or char
|
// Only allows String or char
|
||||||
return if lhs_value.is::<String>() {
|
if lhs_value.is::<String>() {
|
||||||
Ok(rhs_value
|
Ok(rhs_value
|
||||||
.contains_key(&lhs_value.cast::<String>())
|
.contains_key(&lhs_value.cast::<String>())
|
||||||
.into_dynamic())
|
.into_dynamic())
|
||||||
} else if lhs_value.is::<char>() {
|
} else if lhs_value.is::<char>() {
|
||||||
Ok(rhs_value
|
Ok(rhs_value
|
||||||
.contains_key(&lhs_value.cast::<char>().to_string())
|
.contains_key(&lhs_value.cast::<char>().to_string())
|
||||||
.into_dynamic())
|
.into_dynamic())
|
||||||
} else {
|
} else {
|
||||||
Err(EvalAltResult::ErrorInExpr(lhs.position()))
|
Err(EvalAltResult::ErrorInExpr(lhs.position()))
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
} else if rhs_value.is::<String>() {
|
||||||
|
|
||||||
if rhs_value.is::<String>() {
|
|
||||||
let rhs_value = rhs_value.cast::<String>();
|
let rhs_value = rhs_value.cast::<String>();
|
||||||
|
|
||||||
// Only allows String or char
|
// Only allows String or char
|
||||||
return if lhs_value.is::<String>() {
|
if lhs_value.is::<String>() {
|
||||||
Ok(rhs_value
|
Ok(rhs_value
|
||||||
.contains(&lhs_value.cast::<String>())
|
.contains(&lhs_value.cast::<String>())
|
||||||
.into_dynamic())
|
.into_dynamic())
|
||||||
@ -1258,10 +1229,10 @@ impl Engine<'_> {
|
|||||||
Ok(rhs_value.contains(lhs_value.cast::<char>()).into_dynamic())
|
Ok(rhs_value.contains(lhs_value.cast::<char>()).into_dynamic())
|
||||||
} else {
|
} else {
|
||||||
Err(EvalAltResult::ErrorInExpr(lhs.position()))
|
Err(EvalAltResult::ErrorInExpr(lhs.position()))
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
Err(EvalAltResult::ErrorInExpr(rhs.position()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(EvalAltResult::ErrorInExpr(rhs.position()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an expression
|
/// Evaluate an expression
|
||||||
@ -1273,21 +1244,13 @@ impl Engine<'_> {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
match expr {
|
match expr {
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Expr::FloatConstant(f, _) => Ok(f.into_dynamic()),
|
|
||||||
|
|
||||||
Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()),
|
Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()),
|
||||||
|
Expr::FloatConstant(f, _) => Ok(f.into_dynamic()),
|
||||||
Expr::StringConstant(s, _) => Ok(s.clone().into_owned().into_dynamic()),
|
Expr::StringConstant(s, _) => Ok(s.clone().into_owned().into_dynamic()),
|
||||||
Expr::CharConstant(c, _) => Ok(c.into_dynamic()),
|
Expr::CharConstant(c, _) => Ok(c.into_dynamic()),
|
||||||
Expr::Variable(id, pos) => Self::search_scope(scope, id, *pos).map(|(_, val)| val),
|
Expr::Variable(id, pos) => Self::search_scope(scope, id, *pos).map(|(_, val)| val),
|
||||||
Expr::Property(_, _) => panic!("unexpected property."),
|
Expr::Property(_, _) => panic!("unexpected property."),
|
||||||
|
|
||||||
// lhs[idx_expr]
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(lhs, idx_expr, op_pos) => self
|
|
||||||
.eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level)
|
|
||||||
.map(|(_, _, _, x)| x),
|
|
||||||
|
|
||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(stmt, _) => self.eval_stmt(scope, fn_lib, stmt, level),
|
Expr::Stmt(stmt, _) => self.eval_stmt(scope, fn_lib, stmt, level),
|
||||||
|
|
||||||
@ -1312,7 +1275,6 @@ impl Engine<'_> {
|
|||||||
} => {
|
} => {
|
||||||
// Avoid referencing scope which is used below as mut
|
// Avoid referencing scope which is used below as mut
|
||||||
let entry = ScopeSource { name, ..entry };
|
let entry = ScopeSource { name, ..entry };
|
||||||
|
|
||||||
*scope.get_mut(entry) = rhs_val.clone();
|
*scope.get_mut(entry) = rhs_val.clone();
|
||||||
Ok(rhs_val)
|
Ok(rhs_val)
|
||||||
}
|
}
|
||||||
@ -1329,7 +1291,7 @@ impl Engine<'_> {
|
|||||||
// idx_lhs[idx_expr] = rhs
|
// idx_lhs[idx_expr] = rhs
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||||
let (idx_src_type, src, idx, _) =
|
let (idx_src_type, src, index, _) =
|
||||||
self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?;
|
self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?;
|
||||||
|
|
||||||
if let Some(src) = src {
|
if let Some(src) = src {
|
||||||
@ -1344,7 +1306,7 @@ impl Engine<'_> {
|
|||||||
idx_src_type,
|
idx_src_type,
|
||||||
scope,
|
scope,
|
||||||
src,
|
src,
|
||||||
idx,
|
index,
|
||||||
(rhs_val, rhs.position()),
|
(rhs_val, rhs.position()),
|
||||||
)?),
|
)?),
|
||||||
}
|
}
|
||||||
@ -1373,6 +1335,12 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lhs[idx_expr]
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Expr::Index(lhs, idx_expr, op_pos) => self
|
||||||
|
.eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level)
|
||||||
|
.map(|(_, _, _, x)| x),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, fn_lib, lhs, rhs, level),
|
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, fn_lib, lhs, rhs, level),
|
||||||
|
|
||||||
@ -1417,35 +1385,14 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match fn_name.as_ref() {
|
match fn_name.as_ref() {
|
||||||
// Dump AST
|
|
||||||
KEYWORD_DUMP_AST => {
|
|
||||||
let pos = if args_expr_list.is_empty() {
|
|
||||||
*pos
|
|
||||||
} else {
|
|
||||||
args_expr_list[0].position()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Change the argument to a debug dump of the expressions
|
|
||||||
let mut result = args_expr_list
|
|
||||||
.iter()
|
|
||||||
.map(|expr| format!("{:#?}", expr))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n")
|
|
||||||
.into_dynamic();
|
|
||||||
|
|
||||||
// Redirect call to `print`
|
|
||||||
let mut args = [result.as_mut()];
|
|
||||||
self.call_fn_raw(None, fn_lib, KEYWORD_PRINT, &mut args, None, pos, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// type_of
|
// type_of
|
||||||
KEYWORD_TYPE_OF
|
KEYWORD_TYPE_OF
|
||||||
if args_expr_list.len() == 1
|
if args_expr_list.len() == 1
|
||||||
&& !has_override(self, fn_lib, KEYWORD_TYPE_OF) =>
|
&& !has_override(self, fn_lib, KEYWORD_TYPE_OF) =>
|
||||||
{
|
{
|
||||||
let r = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?;
|
let result = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?;
|
||||||
Ok(self
|
Ok(self
|
||||||
.map_type_name((*r).type_name())
|
.map_type_name((*result).type_name())
|
||||||
.to_string()
|
.to_string()
|
||||||
.into_dynamic())
|
.into_dynamic())
|
||||||
}
|
}
|
||||||
@ -1456,18 +1403,18 @@ impl Engine<'_> {
|
|||||||
&& !has_override(self, fn_lib, KEYWORD_EVAL) =>
|
&& !has_override(self, fn_lib, KEYWORD_EVAL) =>
|
||||||
{
|
{
|
||||||
let pos = args_expr_list[0].position();
|
let pos = args_expr_list[0].position();
|
||||||
let r = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?;
|
let result = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?;
|
||||||
|
|
||||||
// Get the script text by evaluating the expression
|
// Get the script text by evaluating the expression
|
||||||
let script =
|
let script = result
|
||||||
r.downcast_ref::<String>()
|
.downcast_ref::<String>()
|
||||||
.map(String::as_str)
|
.map(String::as_str)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
r.type_name().into(),
|
result.type_name().into(),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Compile the script text
|
// Compile the script text
|
||||||
// No optimizations because we only run it once
|
// No optimizations because we only run it once
|
||||||
@ -1475,19 +1422,15 @@ impl Engine<'_> {
|
|||||||
.compile_with_scope_and_optimization_level(
|
.compile_with_scope_and_optimization_level(
|
||||||
&Scope::new(),
|
&Scope::new(),
|
||||||
script,
|
script,
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
OptimizationLevel::None,
|
OptimizationLevel::None,
|
||||||
)
|
)
|
||||||
.map_err(EvalAltResult::ErrorParsing)?;
|
.map_err(EvalAltResult::ErrorParsing)?;
|
||||||
|
|
||||||
// If new functions are defined within the eval string, it is an error
|
// If new functions are defined within the eval string, it is an error
|
||||||
#[cfg(not(feature = "no_function"))]
|
if ast.1.len() > 0 {
|
||||||
{
|
return Err(EvalAltResult::ErrorParsing(
|
||||||
if ast.1.len() > 0 {
|
ParseErrorType::WrongFnDefinition.into_err(pos),
|
||||||
return Err(EvalAltResult::ErrorParsing(
|
));
|
||||||
ParseErrorType::WrongFnDefinition.into_err(pos),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(lib) = fn_lib {
|
if let Some(lib) = fn_lib {
|
||||||
@ -1559,6 +1502,8 @@ impl Engine<'_> {
|
|||||||
Expr::True(_) => Ok(true.into_dynamic()),
|
Expr::True(_) => Ok(true.into_dynamic()),
|
||||||
Expr::False(_) => Ok(false.into_dynamic()),
|
Expr::False(_) => Ok(false.into_dynamic()),
|
||||||
Expr::Unit(_) => Ok(().into_dynamic()),
|
Expr::Unit(_) => Ok(().into_dynamic()),
|
||||||
|
|
||||||
|
expr => panic!("should not appear: {:?}", expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1753,7 +1698,7 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Print/debug to stdout
|
/// Print/debug to stdout
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
fn default_print(s: &str) {
|
fn default_print(s: &str) {
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
println!("{}", s);
|
println!("{}", s);
|
||||||
}
|
}
|
||||||
|
41
src/error.rs
41
src/error.rs
@ -37,6 +37,10 @@ impl fmt::Display for LexError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Type of error encountered when parsing a script.
|
/// Type of error encountered when parsing a script.
|
||||||
|
///
|
||||||
|
/// Some errors never appear when certain features are turned on.
|
||||||
|
/// They still exist so that the application can turn features on and off without going through
|
||||||
|
/// massive code changes to remove/add back enum variants in match statements.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum ParseErrorType {
|
pub enum ParseErrorType {
|
||||||
/// Error in the script text. Wrapped value is the error message.
|
/// Error in the script text. Wrapped value is the error message.
|
||||||
@ -51,19 +55,21 @@ pub enum ParseErrorType {
|
|||||||
MalformedCallExpr(String),
|
MalformedCallExpr(String),
|
||||||
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error description (if any).
|
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error description (if any).
|
||||||
///
|
///
|
||||||
/// Not available under the `no_index` feature.
|
/// Never appears under the `no_index` feature.
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
MalformedIndexExpr(String),
|
MalformedIndexExpr(String),
|
||||||
/// An expression in an `in` expression has syntax error. Wrapped value is the error description (if any).
|
/// An expression in an `in` expression has syntax error. Wrapped value is the error description (if any).
|
||||||
|
///
|
||||||
|
/// Never appears under the `no_object` and `no_index` features combination.
|
||||||
MalformedInExpr(String),
|
MalformedInExpr(String),
|
||||||
/// A map definition has duplicated property names. Wrapped value is the property name.
|
/// A map definition has duplicated property names. Wrapped value is the property name.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_object` feature.
|
/// Never appears under the `no_object` feature.
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
DuplicatedProperty(String),
|
DuplicatedProperty(String),
|
||||||
/// Invalid expression assigned to constant. Wrapped value is the name of the constant.
|
/// Invalid expression assigned to constant. Wrapped value is the name of the constant.
|
||||||
ForbiddenConstantExpr(String),
|
ForbiddenConstantExpr(String),
|
||||||
/// Missing a property name for custom types and maps.
|
/// Missing a property name for custom types and maps.
|
||||||
|
///
|
||||||
|
/// Never appears under the `no_object` feature.
|
||||||
PropertyExpected,
|
PropertyExpected,
|
||||||
/// Missing a variable name after the `let`, `const` or `for` keywords.
|
/// Missing a variable name after the `let`, `const` or `for` keywords.
|
||||||
VariableExpected,
|
VariableExpected,
|
||||||
@ -71,28 +77,23 @@ pub enum ParseErrorType {
|
|||||||
ExprExpected(String),
|
ExprExpected(String),
|
||||||
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
||||||
///
|
///
|
||||||
/// Not available under the `no_function` feature.
|
/// Never appears under the `no_function` feature.
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
WrongFnDefinition,
|
WrongFnDefinition,
|
||||||
/// Missing a function name after the `fn` keyword.
|
/// Missing a function name after the `fn` keyword.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_function` feature.
|
/// Never appears under the `no_function` feature.
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
FnMissingName,
|
FnMissingName,
|
||||||
/// A function definition is missing the parameters list. Wrapped value is the function name.
|
/// A function definition is missing the parameters list. Wrapped value is the function name.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_function` feature.
|
/// Never appears under the `no_function` feature.
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
FnMissingParams(String),
|
FnMissingParams(String),
|
||||||
/// A function definition has duplicated parameters. Wrapped values are the function name and parameter name.
|
/// A function definition has duplicated parameters. Wrapped values are the function name and parameter name.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_function` feature.
|
/// Never appears under the `no_function` feature.
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
FnDuplicatedParam(String, String),
|
FnDuplicatedParam(String, String),
|
||||||
/// A function definition is missing the body. Wrapped value is the function name.
|
/// A function definition is missing the body. Wrapped value is the function name.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_function` feature.
|
/// Never appears under the `no_function` feature.
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
FnMissingBody(String),
|
FnMissingBody(String),
|
||||||
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
||||||
AssignmentToInvalidLHS,
|
AssignmentToInvalidLHS,
|
||||||
@ -138,24 +139,17 @@ impl ParseError {
|
|||||||
ParseErrorType::UnknownOperator(_) => "Unknown operator",
|
ParseErrorType::UnknownOperator(_) => "Unknown operator",
|
||||||
ParseErrorType::MissingToken(_, _) => "Expecting a certain token that is missing",
|
ParseErrorType::MissingToken(_, _) => "Expecting a certain token that is missing",
|
||||||
ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
||||||
ParseErrorType::MalformedInExpr(_) => "Invalid 'in' expression",
|
ParseErrorType::MalformedInExpr(_) => "Invalid 'in' expression",
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
ParseErrorType::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
ParseErrorType::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
||||||
ParseErrorType::ForbiddenConstantExpr(_) => "Expecting a constant",
|
ParseErrorType::ForbiddenConstantExpr(_) => "Expecting a constant",
|
||||||
ParseErrorType::PropertyExpected => "Expecting name of a property",
|
ParseErrorType::PropertyExpected => "Expecting name of a property",
|
||||||
ParseErrorType::VariableExpected => "Expecting name of a variable",
|
ParseErrorType::VariableExpected => "Expecting name of a variable",
|
||||||
ParseErrorType::ExprExpected(_) => "Expecting an expression",
|
ParseErrorType::ExprExpected(_) => "Expecting an expression",
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ParseErrorType::FnMissingName => "Expecting name in function declaration",
|
ParseErrorType::FnMissingName => "Expecting name in function declaration",
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration",
|
ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ParseErrorType::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
ParseErrorType::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||||
ParseErrorType::AssignmentToInvalidLHS => "Cannot assign to this expression",
|
ParseErrorType::AssignmentToInvalidLHS => "Cannot assign to this expression",
|
||||||
ParseErrorType::AssignmentToCopy => "Cannot assign to this expression because it will only be changing a copy of the value",
|
ParseErrorType::AssignmentToCopy => "Cannot assign to this expression because it will only be changing a copy of the value",
|
||||||
@ -178,7 +172,6 @@ impl fmt::Display for ParseError {
|
|||||||
}
|
}
|
||||||
ParseErrorType::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s)?,
|
ParseErrorType::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s)?,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
ParseErrorType::MalformedIndexExpr(s) => {
|
ParseErrorType::MalformedIndexExpr(s) => {
|
||||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
||||||
}
|
}
|
||||||
@ -187,24 +180,20 @@ impl fmt::Display for ParseError {
|
|||||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
ParseErrorType::DuplicatedProperty(s) => {
|
ParseErrorType::DuplicatedProperty(s) => {
|
||||||
write!(f, "Duplicated property '{}' for object map literal", s)?
|
write!(f, "Duplicated property '{}' for object map literal", s)?
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseErrorType::ExprExpected(s) => write!(f, "Expecting {} expression", s)?,
|
ParseErrorType::ExprExpected(s) => write!(f, "Expecting {} expression", s)?,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ParseErrorType::FnMissingParams(s) => {
|
ParseErrorType::FnMissingParams(s) => {
|
||||||
write!(f, "Expecting parameters for function '{}'", s)?
|
write!(f, "Expecting parameters for function '{}'", s)?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ParseErrorType::FnMissingBody(s) => {
|
ParseErrorType::FnMissingBody(s) => {
|
||||||
write!(f, "Expecting body statement block for function '{}'", s)?
|
write!(f, "Expecting body statement block for function '{}'", s)?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
ParseErrorType::FnDuplicatedParam(s, arg) => {
|
ParseErrorType::FnDuplicatedParam(s, arg) => {
|
||||||
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)?
|
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)?
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#![cfg(not(feature = "no_optimize"))]
|
|
||||||
|
|
||||||
use crate::any::{Any, Dynamic};
|
use crate::any::{Any, Dynamic};
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_EVAL,
|
Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
|
||||||
KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, Position, ReturnType, Stmt, AST};
|
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, Position, ReturnType, Stmt, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
@ -33,6 +31,17 @@ pub enum OptimizationLevel {
|
|||||||
Full,
|
Full,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OptimizationLevel {
|
||||||
|
/// Is the `OptimizationLevel` None.
|
||||||
|
pub fn is_none(self) -> bool {
|
||||||
|
self == Self::None
|
||||||
|
}
|
||||||
|
/// Is the `OptimizationLevel` Full.
|
||||||
|
pub fn is_full(self) -> bool {
|
||||||
|
self == Self::Full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Mutable state throughout an optimization pass.
|
/// Mutable state throughout an optimization pass.
|
||||||
struct State<'a> {
|
struct State<'a> {
|
||||||
/// Has the AST been changed during this pass?
|
/// Has the AST been changed during this pass?
|
||||||
@ -519,10 +528,6 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Do not optimize anything within dump_ast
|
|
||||||
Expr::FunctionCall(id, args, def_value, pos) if id == KEYWORD_DUMP_AST =>
|
|
||||||
Expr::FunctionCall(id, args, def_value, pos),
|
|
||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FunctionCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref())=>
|
Expr::FunctionCall(id, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref())=>
|
||||||
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos),
|
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos),
|
||||||
@ -587,7 +592,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn optimize<'a>(
|
fn optimize<'a>(
|
||||||
statements: Vec<Stmt>,
|
statements: Vec<Stmt>,
|
||||||
engine: &Engine<'a>,
|
engine: &Engine<'a>,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -675,6 +680,9 @@ pub fn optimize_into_ast(
|
|||||||
functions: Vec<FnDef>,
|
functions: Vec<FnDef>,
|
||||||
level: OptimizationLevel,
|
level: OptimizationLevel,
|
||||||
) -> AST {
|
) -> AST {
|
||||||
|
#[cfg(feature = "no_optimize")]
|
||||||
|
const level: OptimizationLevel = OptimizationLevel::None;
|
||||||
|
|
||||||
let fn_lib: Vec<_> = functions
|
let fn_lib: Vec<_> = functions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|fn_def| (fn_def.name.as_str(), fn_def.params.len()))
|
.map(|fn_def| (fn_def.name.as_str(), fn_def.params.len()))
|
||||||
@ -685,7 +693,7 @@ pub fn optimize_into_ast(
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|mut fn_def| {
|
.map(|mut fn_def| {
|
||||||
if level != OptimizationLevel::None {
|
if !level.is_none() {
|
||||||
let pos = fn_def.body.position();
|
let pos = fn_def.body.position();
|
||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
|
201
src/parser.rs
201
src/parser.rs
@ -1,12 +1,10 @@
|
|||||||
//! Main module defining the lexer and parser.
|
//! Main module defining the lexer and parser.
|
||||||
|
|
||||||
use crate::any::{Any, AnyExt, Dynamic};
|
use crate::any::{Any, AnyExt, Dynamic};
|
||||||
use crate::engine::{Engine, FunctionsLib};
|
use crate::engine::{Array, Engine, FunctionsLib, Map};
|
||||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||||
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -42,7 +40,6 @@ pub type INT = i32;
|
|||||||
/// The system floating-point type.
|
/// The system floating-point type.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_float` feature.
|
/// Not available under the `no_float` feature.
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
pub type FLOAT = f64;
|
pub type FLOAT = f64;
|
||||||
|
|
||||||
type LERR = LexError;
|
type LERR = LexError;
|
||||||
@ -398,7 +395,6 @@ pub enum Expr {
|
|||||||
/// Integer constant.
|
/// Integer constant.
|
||||||
IntegerConstant(INT, Position),
|
IntegerConstant(INT, Position),
|
||||||
/// Floating-point constant.
|
/// Floating-point constant.
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
FloatConstant(FLOAT, Position),
|
FloatConstant(FLOAT, Position),
|
||||||
/// Character constant.
|
/// Character constant.
|
||||||
CharConstant(char, Position),
|
CharConstant(char, Position),
|
||||||
@ -415,16 +411,12 @@ pub enum Expr {
|
|||||||
/// expr = expr
|
/// expr = expr
|
||||||
Assignment(Box<Expr>, Box<Expr>, Position),
|
Assignment(Box<Expr>, Box<Expr>, Position),
|
||||||
/// lhs.rhs
|
/// lhs.rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Dot(Box<Expr>, Box<Expr>, Position),
|
Dot(Box<Expr>, Box<Expr>, Position),
|
||||||
/// expr[expr]
|
/// expr[expr]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Index(Box<Expr>, Box<Expr>, Position),
|
Index(Box<Expr>, Box<Expr>, Position),
|
||||||
/// [ expr, ... ]
|
/// [ expr, ... ]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Array(Vec<Expr>, Position),
|
Array(Vec<Expr>, Position),
|
||||||
/// #{ name:expr, ... }
|
/// #{ name:expr, ... }
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Map(Vec<(String, Expr, Position)>, Position),
|
Map(Vec<(String, Expr, Position)>, Position),
|
||||||
/// lhs in rhs
|
/// lhs in rhs
|
||||||
In(Box<Expr>, Box<Expr>, Position),
|
In(Box<Expr>, Box<Expr>, Position),
|
||||||
@ -449,29 +441,25 @@ impl Expr {
|
|||||||
pub fn get_constant_value(&self) -> Dynamic {
|
pub fn get_constant_value(&self) -> Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::IntegerConstant(i, _) => i.into_dynamic(),
|
Self::IntegerConstant(i, _) => i.into_dynamic(),
|
||||||
|
Self::FloatConstant(f, _) => f.into_dynamic(),
|
||||||
Self::CharConstant(c, _) => c.into_dynamic(),
|
Self::CharConstant(c, _) => c.into_dynamic(),
|
||||||
Self::StringConstant(s, _) => s.clone().into_owned().into_dynamic(),
|
Self::StringConstant(s, _) => s.clone().into_owned().into_dynamic(),
|
||||||
Self::True(_) => true.into_dynamic(),
|
Self::True(_) => true.into_dynamic(),
|
||||||
Self::False(_) => false.into_dynamic(),
|
Self::False(_) => false.into_dynamic(),
|
||||||
Self::Unit(_) => ().into_dynamic(),
|
Self::Unit(_) => ().into_dynamic(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::Array(items, _) if items.iter().all(Self::is_constant) => items
|
Self::Array(items, _) if items.iter().all(Self::is_constant) => items
|
||||||
.iter()
|
.iter()
|
||||||
.map(Self::get_constant_value)
|
.map(Self::get_constant_value)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_dynamic(),
|
.into_dynamic(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => items
|
Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v, _)| (k.clone(), v.get_constant_value()))
|
.map(|(k, v, _)| (k.clone(), v.get_constant_value()))
|
||||||
.collect::<HashMap<_, _>>()
|
.collect::<HashMap<_, _>>()
|
||||||
.into_dynamic(),
|
.into_dynamic(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Self::FloatConstant(f, _) => f.into_dynamic(),
|
|
||||||
|
|
||||||
_ => panic!("cannot get value of non-constant expression"),
|
_ => panic!("cannot get value of non-constant expression"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -484,18 +472,15 @@ impl Expr {
|
|||||||
pub fn get_constant_str(&self) -> String {
|
pub fn get_constant_str(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::IntegerConstant(i, _) => i.to_string(),
|
Self::IntegerConstant(i, _) => i.to_string(),
|
||||||
|
Self::FloatConstant(f, _) => f.to_string(),
|
||||||
Self::CharConstant(c, _) => c.to_string(),
|
Self::CharConstant(c, _) => c.to_string(),
|
||||||
Self::StringConstant(_, _) => "string".to_string(),
|
Self::StringConstant(_, _) => "string".to_string(),
|
||||||
Self::True(_) => "true".to_string(),
|
Self::True(_) => "true".to_string(),
|
||||||
Self::False(_) => "false".to_string(),
|
Self::False(_) => "false".to_string(),
|
||||||
Self::Unit(_) => "()".to_string(),
|
Self::Unit(_) => "()".to_string(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::Array(items, _) if items.iter().all(Self::is_constant) => "array".to_string(),
|
Self::Array(items, _) if items.iter().all(Self::is_constant) => "array".to_string(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Self::FloatConstant(f, _) => f.to_string(),
|
|
||||||
|
|
||||||
_ => panic!("cannot get value of non-constant expression"),
|
_ => panic!("cannot get value of non-constant expression"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -504,8 +489,11 @@ impl Expr {
|
|||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
Self::IntegerConstant(_, pos)
|
Self::IntegerConstant(_, pos)
|
||||||
|
| Self::FloatConstant(_, pos)
|
||||||
| Self::CharConstant(_, pos)
|
| Self::CharConstant(_, pos)
|
||||||
| Self::StringConstant(_, pos)
|
| Self::StringConstant(_, pos)
|
||||||
|
| Self::Array(_, pos)
|
||||||
|
| Self::Map(_, pos)
|
||||||
| Self::Variable(_, pos)
|
| Self::Variable(_, pos)
|
||||||
| Self::Property(_, pos)
|
| Self::Property(_, pos)
|
||||||
| Self::Stmt(_, pos)
|
| Self::Stmt(_, pos)
|
||||||
@ -517,22 +505,9 @@ impl Expr {
|
|||||||
| Self::False(pos)
|
| Self::False(pos)
|
||||||
| Self::Unit(pos) => *pos,
|
| Self::Unit(pos) => *pos,
|
||||||
|
|
||||||
Self::Assignment(expr, _, _) => expr.position(),
|
Self::Assignment(expr, _, _) | Self::Dot(expr, _, _) | Self::Index(expr, _, _) => {
|
||||||
|
expr.position()
|
||||||
#[cfg(not(feature = "no_object"))]
|
}
|
||||||
Self::Dot(expr, _, _) => expr.position(),
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Self::FloatConstant(_, pos) => *pos,
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::Array(_, pos) => *pos,
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Self::Map(_, pos) => *pos,
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::Index(expr, _, _) => expr.position(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,13 +516,9 @@ impl Expr {
|
|||||||
/// A pure expression has no side effects.
|
/// A pure expression has no side effects.
|
||||||
pub fn is_pure(&self) -> bool {
|
pub fn is_pure(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::Array(expressions, _) => expressions.iter().all(Self::is_pure),
|
Self::Array(expressions, _) => expressions.iter().all(Self::is_pure),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
Self::Index(x, y, _) | Self::And(x, y, _) | Self::Or(x, y, _) | Self::In(x, y, _) => {
|
||||||
Self::Index(x, y, _) => x.is_pure() && y.is_pure(),
|
|
||||||
|
|
||||||
Self::And(x, y, _) | Self::Or(x, y, _) | Self::In(x, y, _) => {
|
|
||||||
x.is_pure() && y.is_pure()
|
x.is_pure() && y.is_pure()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,21 +532,17 @@ impl Expr {
|
|||||||
pub fn is_constant(&self) -> bool {
|
pub fn is_constant(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::IntegerConstant(_, _)
|
Self::IntegerConstant(_, _)
|
||||||
|
| Self::FloatConstant(_, _)
|
||||||
| Self::CharConstant(_, _)
|
| Self::CharConstant(_, _)
|
||||||
| Self::StringConstant(_, _)
|
| Self::StringConstant(_, _)
|
||||||
| Self::True(_)
|
| Self::True(_)
|
||||||
| Self::False(_)
|
| Self::False(_)
|
||||||
| Self::Unit(_) => true,
|
| Self::Unit(_) => true,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Self::FloatConstant(_, _) => true,
|
|
||||||
|
|
||||||
// An array literal is constant if all items are constant
|
// An array literal is constant if all items are constant
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::Array(expressions, _) => expressions.iter().all(Self::is_constant),
|
Self::Array(expressions, _) => expressions.iter().all(Self::is_constant),
|
||||||
|
|
||||||
// An map literal is constant if all items are constant
|
// An map literal is constant if all items are constant
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Self::Map(items, _) => items.iter().map(|(_, expr, _)| expr).all(Self::is_constant),
|
Self::Map(items, _) => items.iter().map(|(_, expr, _)| expr).all(Self::is_constant),
|
||||||
|
|
||||||
// Check in expression
|
// Check in expression
|
||||||
@ -594,7 +561,6 @@ impl Expr {
|
|||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
IntegerConstant(INT),
|
IntegerConstant(INT),
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
FloatConstant(FLOAT),
|
FloatConstant(FLOAT),
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
CharConstant(char),
|
CharConstant(char),
|
||||||
@ -603,9 +569,7 @@ pub enum Token {
|
|||||||
RightBrace,
|
RightBrace,
|
||||||
LeftParen,
|
LeftParen,
|
||||||
RightParen,
|
RightParen,
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
LeftBracket,
|
LeftBracket,
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
RightBracket,
|
RightBracket,
|
||||||
Plus,
|
Plus,
|
||||||
UnaryPlus,
|
UnaryPlus,
|
||||||
@ -673,7 +637,6 @@ impl Token {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
IntegerConstant(i) => i.to_string().into(),
|
IntegerConstant(i) => i.to_string().into(),
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
FloatConstant(f) => f.to_string().into(),
|
FloatConstant(f) => f.to_string().into(),
|
||||||
Identifier(s) => s.into(),
|
Identifier(s) => s.into(),
|
||||||
CharConstant(c) => c.to_string().into(),
|
CharConstant(c) => c.to_string().into(),
|
||||||
@ -685,9 +648,7 @@ impl Token {
|
|||||||
RightBrace => "}",
|
RightBrace => "}",
|
||||||
LeftParen => "(",
|
LeftParen => "(",
|
||||||
RightParen => ")",
|
RightParen => ")",
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
LeftBracket => "[",
|
LeftBracket => "[",
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
RightBracket => "]",
|
RightBracket => "]",
|
||||||
Plus => "+",
|
Plus => "+",
|
||||||
UnaryPlus => "+",
|
UnaryPlus => "+",
|
||||||
@ -762,6 +723,8 @@ impl Token {
|
|||||||
// RightBrace | {expr} - expr not unary & is closing
|
// RightBrace | {expr} - expr not unary & is closing
|
||||||
LeftParen | // {-expr} - is unary
|
LeftParen | // {-expr} - is unary
|
||||||
// RightParen | (expr) - expr not unary & is closing
|
// RightParen | (expr) - expr not unary & is closing
|
||||||
|
LeftBracket | // [-expr] - is unary
|
||||||
|
// RightBracket | [expr] - expr not unary & is closing
|
||||||
Plus |
|
Plus |
|
||||||
UnaryPlus |
|
UnaryPlus |
|
||||||
Minus |
|
Minus |
|
||||||
@ -805,10 +768,6 @@ impl Token {
|
|||||||
In |
|
In |
|
||||||
PowerOfAssign => true,
|
PowerOfAssign => true,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
LeftBracket => true, // [-expr] - is unary
|
|
||||||
// RightBracket | [expr] - expr not unary & is closing
|
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1211,9 +1170,7 @@ impl<'a> TokenIterator<'a> {
|
|||||||
(')', _) => return Some((Token::RightParen, pos)),
|
(')', _) => return Some((Token::RightParen, pos)),
|
||||||
|
|
||||||
// Indexing
|
// Indexing
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
('[', _) => return Some((Token::LeftBracket, pos)),
|
('[', _) => return Some((Token::LeftBracket, pos)),
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
(']', _) => return Some((Token::RightBracket, pos)),
|
(']', _) => return Some((Token::RightBracket, pos)),
|
||||||
|
|
||||||
// Map literal
|
// Map literal
|
||||||
@ -1506,7 +1463,6 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an indexing expression.
|
/// Parse an indexing expression.
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
fn parse_index_expr<'a>(
|
fn parse_index_expr<'a>(
|
||||||
lhs: Box<Expr>,
|
lhs: Box<Expr>,
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
@ -1528,7 +1484,6 @@ fn parse_index_expr<'a>(
|
|||||||
Expr::IntegerConstant(_, pos) => match *lhs {
|
Expr::IntegerConstant(_, pos) => match *lhs {
|
||||||
Expr::Array(_, _) | Expr::StringConstant(_, _) => (),
|
Expr::Array(_, _) | Expr::StringConstant(_, _) => (),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Map(_, _) => {
|
Expr::Map(_, _) => {
|
||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
"Object map access expects string index, not a number".into(),
|
"Object map access expects string index, not a number".into(),
|
||||||
@ -1556,7 +1511,6 @@ fn parse_index_expr<'a>(
|
|||||||
|
|
||||||
// lhs[string]
|
// lhs[string]
|
||||||
Expr::StringConstant(_, pos) => match *lhs {
|
Expr::StringConstant(_, pos) => match *lhs {
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Map(_, _) => (),
|
Expr::Map(_, _) => (),
|
||||||
|
|
||||||
Expr::Array(_, _) | Expr::StringConstant(_, _) => {
|
Expr::Array(_, _) | Expr::StringConstant(_, _) => {
|
||||||
@ -1584,7 +1538,6 @@ fn parse_index_expr<'a>(
|
|||||||
},
|
},
|
||||||
|
|
||||||
// lhs[float]
|
// lhs[float]
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Expr::FloatConstant(_, pos) => {
|
Expr::FloatConstant(_, pos) => {
|
||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
"Array access expects integer index, not a float".into(),
|
"Array access expects integer index, not a float".into(),
|
||||||
@ -1673,7 +1626,6 @@ fn parse_ident_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an array literal.
|
/// Parse an array literal.
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
fn parse_array_literal<'a>(
|
fn parse_array_literal<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
@ -1715,7 +1667,6 @@ fn parse_array_literal<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a map literal.
|
/// Parse a map literal.
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
fn parse_map_literal<'a>(
|
fn parse_map_literal<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
@ -1840,10 +1791,8 @@ fn parse_primary<'a>(
|
|||||||
let mut can_be_indexed = false;
|
let mut can_be_indexed = false;
|
||||||
|
|
||||||
let mut root_expr = match token {
|
let mut root_expr = match token {
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
(Token::FloatConstant(x), pos) => Ok(Expr::FloatConstant(x, pos)),
|
|
||||||
|
|
||||||
(Token::IntegerConstant(x), pos) => Ok(Expr::IntegerConstant(x, pos)),
|
(Token::IntegerConstant(x), pos) => Ok(Expr::IntegerConstant(x, pos)),
|
||||||
|
(Token::FloatConstant(x), pos) => Ok(Expr::FloatConstant(x, pos)),
|
||||||
(Token::CharConstant(c), pos) => Ok(Expr::CharConstant(c, pos)),
|
(Token::CharConstant(c), pos) => Ok(Expr::CharConstant(c, pos)),
|
||||||
(Token::StringConst(s), pos) => {
|
(Token::StringConst(s), pos) => {
|
||||||
can_be_indexed = true;
|
can_be_indexed = true;
|
||||||
@ -1875,9 +1824,11 @@ fn parse_primary<'a>(
|
|||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
let can_be_indexed = false;
|
||||||
|
|
||||||
if can_be_indexed {
|
if can_be_indexed {
|
||||||
// Tail processing all possible indexing
|
// Tail processing all possible indexing
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
while let Some((Token::LeftBracket, pos)) = input.peek() {
|
while let Some((Token::LeftBracket, pos)) = input.peek() {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
input.next();
|
input.next();
|
||||||
@ -1918,10 +1869,13 @@ fn parse_unary<'a>(
|
|||||||
.map(|x| Expr::IntegerConstant(x, pos))
|
.map(|x| Expr::IntegerConstant(x, pos))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
return Some(Expr::FloatConstant(-(i as FLOAT), pos));
|
{
|
||||||
|
Some(Expr::FloatConstant(-(i as FLOAT), pos))
|
||||||
|
}
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
return None;
|
{
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string())
|
PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string())
|
||||||
@ -1976,48 +1930,41 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// var[...]
|
// var[...]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) => {
|
Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) => {
|
||||||
assert!(is_top, "property expected but gets variable");
|
assert!(is_top, "property expected but gets variable");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
// property[...]
|
// property[...]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) => {
|
Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) => {
|
||||||
assert!(!is_top, "variable expected but gets property");
|
assert!(!is_top, "variable expected but gets property");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// idx_lhs[...]
|
// idx_lhs[...]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, _, pos) => match idx_lhs.as_ref() {
|
Expr::Index(idx_lhs, _, pos) => match idx_lhs.as_ref() {
|
||||||
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
|
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
|
||||||
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(*pos)),
|
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(*pos)),
|
||||||
},
|
},
|
||||||
|
|
||||||
// dot_lhs.dot_rhs
|
// dot_lhs.dot_rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() {
|
Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() {
|
||||||
// var.dot_rhs
|
// var.dot_rhs
|
||||||
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
|
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
|
||||||
// property.dot_rhs
|
// property.dot_rhs
|
||||||
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
|
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
|
||||||
// var[...]
|
// var[...]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, _, _)
|
Expr::Index(idx_lhs, _, _)
|
||||||
if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) && is_top =>
|
if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) && is_top =>
|
||||||
{
|
{
|
||||||
valid_assignment_chain(dot_rhs, false)
|
valid_assignment_chain(dot_rhs, false)
|
||||||
}
|
}
|
||||||
// property[...]
|
// property[...]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, _, _)
|
Expr::Index(idx_lhs, _, _)
|
||||||
if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) && !is_top =>
|
if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) && !is_top =>
|
||||||
{
|
{
|
||||||
valid_assignment_chain(dot_rhs, false)
|
valid_assignment_chain(dot_rhs, false)
|
||||||
}
|
}
|
||||||
// idx_lhs[...]
|
// idx_lhs[...]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(idx_lhs, _, _) => {
|
Expr::Index(idx_lhs, _, _) => {
|
||||||
Some(ParseErrorType::AssignmentToCopy.into_err(idx_lhs.position()))
|
Some(ParseErrorType::AssignmentToCopy.into_err(idx_lhs.position()))
|
||||||
}
|
}
|
||||||
@ -2055,15 +2002,8 @@ fn parse_op_assignment<S: Into<Cow<'static, str>>>(
|
|||||||
/// Parse an 'in' expression.
|
/// Parse an 'in' expression.
|
||||||
fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
|
fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
|
||||||
match (&lhs, &rhs) {
|
match (&lhs, &rhs) {
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
(_, Expr::FloatConstant(_, pos)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression expects a string, array or object map".into(),
|
|
||||||
)
|
|
||||||
.into_err(*pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
(_, Expr::IntegerConstant(_, pos))
|
(_, Expr::IntegerConstant(_, pos))
|
||||||
|
| (_, Expr::FloatConstant(_, pos))
|
||||||
| (_, Expr::And(_, _, pos))
|
| (_, Expr::And(_, _, pos))
|
||||||
| (_, Expr::Or(_, _, pos))
|
| (_, Expr::Or(_, _, pos))
|
||||||
| (_, Expr::In(_, _, pos))
|
| (_, Expr::In(_, _, pos))
|
||||||
@ -2082,7 +2022,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
| (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (),
|
| (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (),
|
||||||
|
|
||||||
// 123.456 in "xxxx"
|
// 123.456 in "xxxx"
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
(Expr::FloatConstant(_, pos), Expr::StringConstant(_, _)) => {
|
(Expr::FloatConstant(_, pos), Expr::StringConstant(_, _)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for a string expects a string, not a float".into(),
|
"'in' expression for a string expects a string, not a float".into(),
|
||||||
@ -2109,7 +2048,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
// [???, ???, ???] in "xxxx"
|
// [???, ???, ???] in "xxxx"
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
(Expr::Array(_, pos), Expr::StringConstant(_, _)) => {
|
(Expr::Array(_, pos), Expr::StringConstant(_, _)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for a string expects a string, not an array".into(),
|
"'in' expression for a string expects a string, not an array".into(),
|
||||||
@ -2117,7 +2055,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
// #{...} in "xxxx"
|
// #{...} in "xxxx"
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::Map(_, pos), Expr::StringConstant(_, _)) => {
|
(Expr::Map(_, pos), Expr::StringConstant(_, _)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for a string expects a string, not an object map".into(),
|
"'in' expression for a string expects a string, not an object map".into(),
|
||||||
@ -2134,13 +2071,10 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// "xxx" in #{...}, 'x' in #{...} - OK!
|
// "xxx" in #{...}, 'x' in #{...} - OK!
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::StringConstant(_, _), Expr::Map(_, _))
|
(Expr::StringConstant(_, _), Expr::Map(_, _))
|
||||||
| (Expr::CharConstant(_, _), Expr::Map(_, _)) => (),
|
| (Expr::CharConstant(_, _), Expr::Map(_, _)) => (),
|
||||||
|
|
||||||
// 123.456 in #{...}
|
// 123.456 in #{...}
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::FloatConstant(_, pos), Expr::Map(_, _)) => {
|
(Expr::FloatConstant(_, pos), Expr::Map(_, _)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for an object map expects a string, not a float".into(),
|
"'in' expression for an object map expects a string, not a float".into(),
|
||||||
@ -2148,7 +2082,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
// 123 in #{...}
|
// 123 in #{...}
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::IntegerConstant(_, pos), Expr::Map(_, _)) => {
|
(Expr::IntegerConstant(_, pos), Expr::Map(_, _)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for an object map expects a string, not a number".into(),
|
"'in' expression for an object map expects a string, not a number".into(),
|
||||||
@ -2157,7 +2090,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
}
|
}
|
||||||
// (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...},
|
// (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...},
|
||||||
// true in #{...}, false in #{...}
|
// true in #{...}, false in #{...}
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::And(_, _, pos), Expr::Map(_, _))
|
(Expr::And(_, _, pos), Expr::Map(_, _))
|
||||||
| (Expr::Or(_, _, pos), Expr::Map(_, _))
|
| (Expr::Or(_, _, pos), Expr::Map(_, _))
|
||||||
| (Expr::In(_, _, pos), Expr::Map(_, _))
|
| (Expr::In(_, _, pos), Expr::Map(_, _))
|
||||||
@ -2169,8 +2101,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
// [???, ???, ???] in #{..}
|
// [???, ???, ???] in #{..}
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::Array(_, pos), Expr::Map(_, _)) => {
|
(Expr::Array(_, pos), Expr::Map(_, _)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for an object map expects a string, not an array".into(),
|
"'in' expression for an object map expects a string, not an array".into(),
|
||||||
@ -2178,7 +2108,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
// #{...} in #{..}
|
// #{...} in #{..}
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::Map(_, pos), Expr::Map(_, _)) => {
|
(Expr::Map(_, pos), Expr::Map(_, _)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for an object map expects a string, not an object map".into(),
|
"'in' expression for an object map expects a string, not an object map".into(),
|
||||||
@ -2186,7 +2115,6 @@ fn parse_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
// (??? = ???) in #{...}, () in #{...}
|
// (??? = ???) in #{...}, () in #{...}
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
(Expr::Assignment(_, _, pos), Expr::Map(_, _)) | (Expr::Unit(pos), Expr::Map(_, _)) => {
|
(Expr::Assignment(_, _, pos), Expr::Map(_, _)) | (Expr::Unit(pos), Expr::Map(_, _)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for an object map expects a string, not ()".into(),
|
"'in' expression for an object map expects a string, not ()".into(),
|
||||||
@ -2269,7 +2197,6 @@ fn parse_binary_op<'a>(
|
|||||||
pos,
|
pos,
|
||||||
)),
|
)),
|
||||||
// xxx.lhs[idx]
|
// xxx.lhs[idx]
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(lhs, idx, pos) => {
|
Expr::Index(lhs, idx, pos) => {
|
||||||
Ok(Expr::Index(Box::new(check_property(*lhs)?), idx, pos))
|
Ok(Expr::Index(Box::new(check_property(*lhs)?), idx, pos))
|
||||||
}
|
}
|
||||||
@ -2290,37 +2217,37 @@ fn parse_binary_op<'a>(
|
|||||||
Token::EqualsTo => Expr::FunctionCall(
|
Token::EqualsTo => Expr::FunctionCall(
|
||||||
"==".into(),
|
"==".into(),
|
||||||
vec![current_lhs, rhs],
|
vec![current_lhs, rhs],
|
||||||
Some(Box::new(false)),
|
Some((false).into_dynamic()),
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::NotEqualsTo => Expr::FunctionCall(
|
Token::NotEqualsTo => Expr::FunctionCall(
|
||||||
"!=".into(),
|
"!=".into(),
|
||||||
vec![current_lhs, rhs],
|
vec![current_lhs, rhs],
|
||||||
Some(Box::new(false)),
|
Some((false).into_dynamic()),
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::LessThan => Expr::FunctionCall(
|
Token::LessThan => Expr::FunctionCall(
|
||||||
"<".into(),
|
"<".into(),
|
||||||
vec![current_lhs, rhs],
|
vec![current_lhs, rhs],
|
||||||
Some(Box::new(false)),
|
Some((false).into_dynamic()),
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::LessThanEqualsTo => Expr::FunctionCall(
|
Token::LessThanEqualsTo => Expr::FunctionCall(
|
||||||
"<=".into(),
|
"<=".into(),
|
||||||
vec![current_lhs, rhs],
|
vec![current_lhs, rhs],
|
||||||
Some(Box::new(false)),
|
Some((false).into_dynamic()),
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::GreaterThan => Expr::FunctionCall(
|
Token::GreaterThan => Expr::FunctionCall(
|
||||||
">".into(),
|
">".into(),
|
||||||
vec![current_lhs, rhs],
|
vec![current_lhs, rhs],
|
||||||
Some(Box::new(false)),
|
Some((false).into_dynamic()),
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
Token::GreaterThanEqualsTo => Expr::FunctionCall(
|
Token::GreaterThanEqualsTo => Expr::FunctionCall(
|
||||||
">=".into(),
|
">=".into(),
|
||||||
vec![current_lhs, rhs],
|
vec![current_lhs, rhs],
|
||||||
Some(Box::new(false)),
|
Some((false).into_dynamic()),
|
||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -2680,7 +2607,6 @@ fn parse_stmt<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a function definition.
|
/// Parse a function definition.
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
fn parse_fn<'a>(
|
fn parse_fn<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
@ -2780,7 +2706,6 @@ pub fn parse_global_expr<'a, 'e>(
|
|||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
// Optimize AST
|
// Optimize AST
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
optimize_into_ast(
|
optimize_into_ast(
|
||||||
engine,
|
engine,
|
||||||
scope,
|
scope,
|
||||||
@ -2788,20 +2713,6 @@ pub fn parse_global_expr<'a, 'e>(
|
|||||||
vec![],
|
vec![],
|
||||||
engine.optimization_level,
|
engine.optimization_level,
|
||||||
),
|
),
|
||||||
//
|
|
||||||
// Do not optimize AST if `no_optimize`
|
|
||||||
#[cfg(feature = "no_optimize")]
|
|
||||||
AST(
|
|
||||||
vec![Stmt::Expr(Box::new(expr))],
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
{
|
|
||||||
Arc::new(FunctionsLib::new())
|
|
||||||
},
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
{
|
|
||||||
Rc::new(FunctionsLib::new())
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2813,9 +2724,9 @@ fn parse_global_level<'a>(
|
|||||||
let mut functions = Vec::<FnDef>::new();
|
let mut functions = Vec::<FnDef>::new();
|
||||||
|
|
||||||
while input.peek().is_some() {
|
while input.peek().is_some() {
|
||||||
|
// Collect all the function definitions
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
{
|
{
|
||||||
// Collect all the function definitions
|
|
||||||
if matches!(input.peek().expect("should not be None"), (Token::Fn, _)) {
|
if matches!(input.peek().expect("should not be None"), (Token::Fn, _)) {
|
||||||
let f = parse_fn(input, true)?;
|
let f = parse_fn(input, true)?;
|
||||||
|
|
||||||
@ -2866,18 +2777,13 @@ pub fn parse<'a, 'e>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
engine: &Engine<'e>,
|
engine: &Engine<'e>,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
#[cfg(not(feature = "no_optimize"))] optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let (statements, functions) = parse_global_level(input)?;
|
let (statements, functions) = parse_global_level(input)?;
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
// Optimize AST
|
// Optimize AST
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
|
||||||
optimize_into_ast(engine, scope, statements, functions, optimization_level),
|
optimize_into_ast(engine, scope, statements, functions, optimization_level),
|
||||||
//
|
|
||||||
// Do not optimize AST if `no_optimize`
|
|
||||||
#[cfg(feature = "no_optimize")]
|
|
||||||
AST(statements, Arc::new(FunctionsLib::from_vec(functions))),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2898,6 +2804,47 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
|||||||
Expr::False(pos)
|
Expr::False(pos)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
{
|
||||||
|
if value.is::<Array>() {
|
||||||
|
let array = value.cast::<Array>();
|
||||||
|
let items: Vec<_> = array
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| map_dynamic_to_expr(x, pos))
|
||||||
|
.collect();
|
||||||
|
if items.iter().all(Option::is_some) {
|
||||||
|
return Some(Expr::Array(
|
||||||
|
items.into_iter().map(Option::unwrap).collect(),
|
||||||
|
pos,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
if value.is::<Map>() {
|
||||||
|
let map = value.cast::<Map>();
|
||||||
|
let items: Vec<_> = map
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| (k, map_dynamic_to_expr(v, pos), pos))
|
||||||
|
.collect();
|
||||||
|
if items.iter().all(|(_, expr, _)| expr.is_some()) {
|
||||||
|
return Some(Expr::Map(
|
||||||
|
items
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, expr, pos)| (k, expr.unwrap(), pos))
|
||||||
|
.collect(),
|
||||||
|
pos,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
if value.is::<FLOAT>() {
|
if value.is::<FLOAT>() {
|
||||||
|
@ -25,7 +25,7 @@ pub enum EvalAltResult {
|
|||||||
|
|
||||||
/// Error reading from a script file. Wrapped value is the path of the script file.
|
/// Error reading from a script file. Wrapped value is the path of the script file.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_std` feature.
|
/// Never appears under the `no_std` feature.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
ErrorReadingScriptFile(PathBuf, std::io::Error),
|
ErrorReadingScriptFile(PathBuf, std::io::Error),
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user