Add no_inidex feature to disable arrays and indexing.

This commit is contained in:
Stephen Chung 2020-03-10 17:10:33 +08:00
parent f3bcb2a10d
commit 52b5732bcb
10 changed files with 225 additions and 145 deletions

View File

@ -18,6 +18,12 @@ include = [
num-traits = "*" num-traits = "*"
[features] [features]
default = []
debug_msgs = [] debug_msgs = []
no_stdlib = []
unchecked = [] unchecked = []
no_stdlib = []
no_index = []
[profile.release]
lto = "fat"
codegen-units = 1

View File

@ -43,6 +43,7 @@ Optional features
| `debug_msgs` | Print debug messages to stdout (using `println!`) related to function registrations and function calls. | | `debug_msgs` | Print debug messages to stdout (using `println!`) related to function registrations and function calls. |
| `no_stdlib` | Exclude the standard library of utility functions in the build, and only include the minimum necessary functionalities. | | `no_stdlib` | Exclude the standard library of utility functions in the build, and only include the minimum necessary functionalities. |
| `unchecked` | Exclude arithmetic checking in the standard library. Beware that a bad script may panic the entire system! | | `unchecked` | Exclude arithmetic checking in the standard library. Beware that a bad script may panic the entire system! |
| `no_index` | Disable arrays and indexing features |
Related Related
------- -------
@ -606,7 +607,7 @@ let booly = !true;
Numeric functions Numeric functions
----------------- -----------------
The following standard functions (defined in the standard library but excluded if `no_stdlib`) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only: The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
| Function | Description | | Function | Description |
| ---------- | ----------------------------------- | | ---------- | ----------------------------------- |
@ -617,7 +618,7 @@ The following standard functions (defined in the standard library but excluded i
Floating-point functions Floating-point functions
------------------------ ------------------------
The following standard functions (defined in the standard library but excluded if `no_stdlib`) operate on `f64` only: The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `f64` only:
| Category | Functions | | Category | Functions |
| ---------------- | ------------------------------------------------------------ | | ---------------- | ------------------------------------------------------------ |
@ -645,7 +646,7 @@ let age = 42;
let record = full_name + ": age " + age; let record = full_name + ": age " + age;
record == "Bob C. Davis: age 42"; record == "Bob C. Davis: age 42";
// Strings can be indexed to get a character // Strings can be indexed to get a character (disabled with the 'no_index' feature)
let c = record[4]; let c = record[4];
c == 'C'; c == 'C';
@ -669,7 +670,7 @@ record[4] = '\x58'; // 0x58 = 'X'
record == "Bob X. Davis: age 42 ❤\n"; record == "Bob X. Davis: age 42 ❤\n";
``` ```
The following standard functions (defined in the standard library but excluded if `no_stdlib`) operate on strings: The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on strings:
| Function | Description | | Function | Description |
| ---------- | ------------------------------------------------------------------------ | | ---------- | ------------------------------------------------------------------------ |
@ -716,7 +717,7 @@ Arrays
You can create arrays of values, and then access them with numeric indices. You can create arrays of values, and then access them with numeric indices.
The following functions (defined in the standard library but excluded if `no_stdlib`) operate on arrays: The following functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on arrays:
| Function | Description | | Function | Description |
| ---------- | ------------------------------------------------------------------------------------- | | ---------- | ------------------------------------------------------------------------------------- |
@ -788,6 +789,8 @@ engine.register_fn("push",
The type of a Rhai array is `rhai::Array`. `type_of()` returns `"array"`. The type of a Rhai array is `rhai::Array`. `type_of()` returns `"array"`.
Arrays are disabled via the [`no_index`](#optional-features) feature.
Comparison operators Comparison operators
-------------------- --------------------

View File

@ -104,8 +104,7 @@ impl<'e> Engine<'e> {
parse(&mut tokens.peekable(), self.optimize) parse(&mut tokens.peekable(), self.optimize)
} }
/// Compile a file into an AST. fn read_file(filename: &str) -> Result<String, EvalAltResult> {
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
let mut f = File::open(filename) let mut f = File::open(filename)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?; .map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
@ -113,19 +112,18 @@ impl<'e> Engine<'e> {
f.read_to_string(&mut contents) f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err)) .map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| self.compile(&contents).map_err(EvalAltResult::ErrorParsing)) .map(|_| contents)
}
/// Compile a file into an AST.
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
Self::read_file(filename)
.and_then(|contents| self.compile(&contents).map_err(|err| err.into()))
} }
/// Evaluate a file. /// Evaluate a file.
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> { pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
let mut f = File::open(filename) Self::read_file(filename).and_then(|contents| self.eval::<T>(&contents))
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
let mut contents = String::new();
f.read_to_string(&mut contents)
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
.and_then(|_| self.eval::<T>(&contents))
} }
/// Evaluate a string. /// Evaluate a string.
@ -164,10 +162,16 @@ impl<'e> Engine<'e> {
retain_functions: bool, retain_functions: bool,
ast: &AST, ast: &AST,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
fn eval_ast_internal(
engine: &mut Engine,
scope: &mut Scope,
retain_functions: bool,
ast: &AST,
) -> Result<Dynamic, EvalAltResult> {
let AST(statements, functions) = ast; let AST(statements, functions) = ast;
functions.iter().for_each(|f| { functions.iter().for_each(|f| {
self.script_functions.insert( engine.script_functions.insert(
FnSpec { FnSpec {
name: f.name.clone().into(), name: f.name.clone().into(),
args: None, args: None,
@ -178,13 +182,16 @@ impl<'e> Engine<'e> {
let result = statements let result = statements
.iter() .iter()
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt)); .try_fold(().into_dynamic(), |_, stmt| engine.eval_stmt(scope, stmt));
if !retain_functions { if !retain_functions {
self.clear_functions(); engine.clear_functions();
} }
match result { result
}
match eval_ast_internal(self, scope, retain_functions, ast) {
Err(EvalAltResult::Return(out, pos)) => out.downcast::<T>().map(|v| *v).map_err(|a| { Err(EvalAltResult::Return(out, pos)) => out.downcast::<T>().map(|v| *v).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).to_string(), self.map_type_name((*a).type_name()).to_string(),
@ -285,25 +292,14 @@ impl<'e> Engine<'e> {
ast: &AST, ast: &AST,
args: A, args: A,
) -> Result<T, EvalAltResult> { ) -> Result<T, EvalAltResult> {
let mut arg_values = args.into_vec(); fn call_fn_internal(
engine: &mut Engine,
self.call_fn_internal(
name,
ast,
arg_values.iter_mut().map(|v| v.as_mut()).collect(),
)
}
pub(crate) fn call_fn_internal<T: Any + Clone>(
&mut self,
name: &str, name: &str,
ast: &AST, ast: &AST,
args: FnCallArgs, args: FnCallArgs,
) -> Result<T, EvalAltResult> { ) -> Result<Dynamic, EvalAltResult> {
let pos = Default::default();
ast.1.iter().for_each(|f| { ast.1.iter().for_each(|f| {
self.script_functions.insert( engine.script_functions.insert(
FnSpec { FnSpec {
name: f.name.clone().into(), name: f.name.clone().into(),
args: None, args: None,
@ -312,18 +308,29 @@ impl<'e> Engine<'e> {
); );
}); });
let result = self.call_fn_raw(name, args, None, pos).and_then(|b| { let result = engine.call_fn_raw(name, args, None, Position::none());
engine.clear_functions();
result
}
let mut arg_values = args.into_vec();
call_fn_internal(
self,
name,
ast,
arg_values.iter_mut().map(|v| v.as_mut()).collect(),
)
.and_then(|b| {
b.downcast().map(|b| *b).map_err(|a| { b.downcast().map(|b| *b).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(), self.map_type_name((*a).type_name()).into(),
pos, Position::none(),
) )
}) })
}); })
self.clear_functions();
result
} }
/// Override default action of `print` (print to stdout using `println!`) /// Override default action of `print` (print to stdout using `println!`)

View File

@ -2,8 +2,12 @@
//! _standard library_ of utility functions. //! _standard library_ of utility functions.
use crate::any::Any; use crate::any::Any;
use crate::engine::{Array, Engine}; use crate::engine::Engine;
use crate::fn_register::RegisterFn; use crate::fn_register::RegisterFn;
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
i32, i64, i32, i64,
@ -15,14 +19,13 @@ use std::{
use std::ops::{Shl, Shr}; use std::ops::{Shl, Shr};
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
use crate::{parser::Position, result::EvalAltResult, RegisterResultFn}; use {
crate::{parser::Position, result::EvalAltResult, RegisterResultFn},
#[cfg(not(feature = "unchecked"))] num_traits::{
use std::convert::TryFrom; CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr,
CheckedSub,
#[cfg(not(feature = "unchecked"))] },
use num_traits::{ std::convert::TryFrom,
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
}; };
macro_rules! reg_op { macro_rules! reg_op {
@ -102,6 +105,7 @@ macro_rules! reg_func2y {
} }
#[cfg(not(feature = "no_stdlib"))] #[cfg(not(feature = "no_stdlib"))]
#[cfg(not(feature = "no_index"))]
macro_rules! reg_func3 { macro_rules! reg_func3 {
($self:expr, $x:expr, $op:expr, $v:ty, $w:ty, $r:ty, $( $y:ty ),*) => ( ($self:expr, $x:expr, $op:expr, $v:ty, $w:ty, $r:ty, $( $y:ty ),*) => (
$( $(
@ -438,19 +442,23 @@ impl Engine<'_> {
reg_func1!(self, "print", print, String, i8, u8, i16, u16); reg_func1!(self, "print", print, String, i8, u8, i16, u16);
reg_func1!(self, "print", print, String, i32, i64, u32, u64); reg_func1!(self, "print", print, String, i32, i64, u32, u64);
reg_func1!(self, "print", print, String, f32, f64, bool, char, String); reg_func1!(self, "print", print, String, f32, f64, bool, char, String);
reg_func1!(self, "print", print_debug, String, Array);
self.register_fn("print", || "".to_string()); self.register_fn("print", || "".to_string());
self.register_fn("print", |_: ()| "".to_string()); self.register_fn("print", |_: ()| "".to_string());
reg_func1!(self, "debug", print_debug, String, i8, u8, i16, u16); reg_func1!(self, "debug", print_debug, String, i8, u8, i16, u16);
reg_func1!(self, "debug", print_debug, String, i32, i64, u32, u64); reg_func1!(self, "debug", print_debug, String, i32, i64, u32, u64);
reg_func1!(self, "debug", print_debug, String, f32, f64, bool, char); reg_func1!(self, "debug", print_debug, String, f32, f64, bool, char);
#[cfg(not(feature = "no_index"))]
{
reg_func1!(self, "print", print_debug, String, Array);
reg_func1!(self, "debug", print_debug, String, String, Array, ()); reg_func1!(self, "debug", print_debug, String, String, Array, ());
// Register array iterator // Register array iterator
self.register_iterator::<Array, _>(|a| { self.register_iterator::<Array, _>(|a| {
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter()) Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
}); });
}
// Register range function // Register range function
self.register_iterator::<Range<i64>, _>(|a| { self.register_iterator::<Range<i64>, _>(|a| {
@ -468,6 +476,7 @@ impl Engine<'_> {
/// Register the built-in library. /// Register the built-in library.
#[cfg(not(feature = "no_stdlib"))] #[cfg(not(feature = "no_stdlib"))]
pub(crate) fn register_stdlib(&mut self) { pub(crate) fn register_stdlib(&mut self) {
#[cfg(not(feature = "no_index"))]
use crate::fn_register::RegisterDynamicFn; use crate::fn_register::RegisterDynamicFn;
// Advanced math functions // Advanced math functions
@ -547,6 +556,8 @@ impl Engine<'_> {
self.register_fn("to_int", |x: f64| x as i64); self.register_fn("to_int", |x: f64| x as i64);
} }
#[cfg(not(feature = "no_index"))]
{
// Register array utility functions // Register array utility functions
fn push<T: Any>(list: &mut Array, item: T) { fn push<T: Any>(list: &mut Array, item: T) {
list.push(Box::new(item)); list.push(Box::new(item));
@ -583,6 +594,7 @@ impl Engine<'_> {
list.truncate(len as usize); list.truncate(len as usize);
} }
}); });
}
// Register string concatenate functions // Register string concatenate functions
fn prepend<T: Display>(x: T, y: String) -> String { fn prepend<T: Display>(x: T, y: String) -> String {
@ -596,16 +608,20 @@ impl Engine<'_> {
self, "+", append, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, self, "+", append, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64,
bool, char bool, char
); );
self.register_fn("+", |x: String, y: Array| format!("{}{:?}", x, y));
self.register_fn("+", |x: String, _: ()| format!("{}", x)); self.register_fn("+", |x: String, _: ()| format!("{}", x));
reg_func2y!( reg_func2y!(
self, "+", prepend, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, self, "+", prepend, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64,
bool, char bool, char
); );
self.register_fn("+", |x: Array, y: String| format!("{:?}{}", x, y));
self.register_fn("+", |_: (), y: String| format!("{}", y)); self.register_fn("+", |_: (), y: String| format!("{}", y));
#[cfg(not(feature = "no_index"))]
{
self.register_fn("+", |x: String, y: Array| format!("{}{:?}", x, y));
self.register_fn("+", |x: Array, y: String| format!("{:?}{}", x, y));
}
// Register string utility functions // Register string utility functions
self.register_fn("len", |s: &mut String| s.chars().count() as i64); self.register_fn("len", |s: &mut String| s.chars().count() as i64);
self.register_fn("contains", |s: &mut String, ch: char| s.contains(ch)); self.register_fn("contains", |s: &mut String, ch: char| s.contains(ch));

View File

@ -14,6 +14,7 @@ use std::{
}; };
/// An dynamic array of `Dynamic` values. /// An dynamic array of `Dynamic` values.
#[cfg(not(feature = "no_index"))]
pub type Array = Vec<Dynamic>; pub type Array = Vec<Dynamic>;
pub type FnCallArgs<'a> = Vec<&'a mut Variant>; pub type FnCallArgs<'a> = Vec<&'a mut Variant>;
@ -29,6 +30,7 @@ pub(crate) const FUNC_GETTER: &'static str = "get$";
pub(crate) const FUNC_SETTER: &'static str = "set$"; pub(crate) const FUNC_SETTER: &'static str = "set$";
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[cfg(not(feature = "no_index"))]
enum IndexSourceType { enum IndexSourceType {
Array, Array,
String, String,
@ -82,8 +84,9 @@ impl Engine<'_> {
// User-friendly names for built-in types // User-friendly names for built-in types
let type_names = [ let type_names = [
(type_name::<String>(), "string"), (type_name::<String>(), "string"),
(type_name::<Array>(), "array"),
(type_name::<Dynamic>(), "dynamic"), (type_name::<Dynamic>(), "dynamic"),
#[cfg(not(feature = "no_index"))]
(type_name::<Array>(), "array"),
] ]
.iter() .iter()
.map(|(k, v)| (k.to_string(), v.to_string())) .map(|(k, v)| (k.to_string(), v.to_string()))
@ -251,7 +254,7 @@ impl Engine<'_> {
match dot_rhs { match dot_rhs {
// xxx.fn_name(args) // xxx.fn_name(args)
Expr::FunctionCall(fn_name, args, def_val, pos) => { Expr::FunctionCall(fn_name, args, def_val, pos) => {
let mut args: Array = args let mut args = args
.iter() .iter()
.map(|arg| self.eval_expr(scope, arg)) .map(|arg| self.eval_expr(scope, arg))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
@ -271,6 +274,7 @@ impl Engine<'_> {
} }
// xxx.idx_lhs[idx_expr] // xxx.idx_lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => { Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (expr, _) = match idx_lhs.as_ref() { let (expr, _) = match idx_lhs.as_ref() {
// xxx.id[idx_expr] // xxx.id[idx_expr]
@ -309,6 +313,7 @@ impl Engine<'_> {
.and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), rhs)) .and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), rhs))
} }
// xxx.idx_lhs[idx_expr].rhs // xxx.idx_lhs[idx_expr].rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => { Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (expr, _) = match idx_lhs.as_ref() { let (expr, _) = match idx_lhs.as_ref() {
// xxx.id[idx_expr].rhs // xxx.id[idx_expr].rhs
@ -371,6 +376,7 @@ impl Engine<'_> {
} }
// idx_lhs[idx_expr].??? // idx_lhs[idx_expr].???
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => { Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, mut target) = let (src_type, src, idx, mut target) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?; self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?;
@ -414,6 +420,7 @@ impl Engine<'_> {
} }
/// Evaluate the value of an index (must evaluate to i64) /// Evaluate the value of an index (must evaluate to i64)
#[cfg(not(feature = "no_index"))]
fn eval_index_value( fn eval_index_value(
&mut self, &mut self,
scope: &mut Scope, scope: &mut Scope,
@ -426,6 +433,7 @@ 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,
val: Dynamic, val: Dynamic,
@ -473,6 +481,7 @@ 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>(
&mut self, &mut self,
scope: &mut Scope, scope: &mut Scope,
@ -505,6 +514,7 @@ impl Engine<'_> {
} }
/// 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");
@ -518,6 +528,7 @@ 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(
src_type: IndexSourceType, src_type: IndexSourceType,
scope: &mut Scope, scope: &mut Scope,
@ -550,6 +561,7 @@ 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: usize, idx: usize,
@ -593,6 +605,7 @@ 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, idx_pos) => match lhs.as_ref() { Expr::Index(lhs, idx_expr, idx_pos) => match lhs.as_ref() {
// xxx.id[idx_expr] // xxx.id[idx_expr]
Expr::Identifier(id, pos) => { Expr::Identifier(id, pos) => {
@ -636,6 +649,7 @@ 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, idx_pos) => match lhs.as_ref() { Expr::Index(lhs, idx_expr, idx_pos) => match lhs.as_ref() {
// xxx.id[idx_expr].rhs // xxx.id[idx_expr].rhs
Expr::Identifier(id, pos) => { Expr::Identifier(id, pos) => {
@ -720,6 +734,7 @@ 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, idx_pos) => { Expr::Index(lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, mut target) = let (src_type, src, idx, mut target) =
self.eval_index_expr(scope, lhs, idx_expr, *idx_pos)?; self.eval_index_expr(scope, lhs, idx_expr, *idx_pos)?;
@ -762,6 +777,7 @@ impl Engine<'_> {
} }
// lhs[idx_expr] // lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, idx_pos) => self Expr::Index(lhs, idx_expr, idx_pos) => self
.eval_index_expr(scope, lhs, idx_expr, *idx_pos) .eval_index_expr(scope, lhs, idx_expr, *idx_pos)
.map(|(_, _, _, x)| x), .map(|(_, _, _, x)| x),
@ -785,6 +801,7 @@ impl Engine<'_> {
} }
// idx_lhs[idx_expr] = rhs // idx_lhs[idx_expr] = rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, idx_expr, idx_pos) => { Expr::Index(idx_lhs, idx_expr, idx_pos) => {
let (src_type, src, idx, _) = let (src_type, src, idx, _) =
self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?; self.eval_index_expr(scope, idx_lhs, idx_expr, *idx_pos)?;
@ -818,6 +835,7 @@ impl Engine<'_> {
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs), Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs),
#[cfg(not(feature = "no_index"))]
Expr::Array(contents, _) => { Expr::Array(contents, _) => {
let mut arr = Vec::new(); let mut arr = Vec::new();
@ -836,7 +854,7 @@ impl Engine<'_> {
let mut args = args let mut args = args
.iter() .iter()
.map(|expr| self.eval_expr(scope, expr)) .map(|expr| self.eval_expr(scope, expr))
.collect::<Result<Array, _>>()?; .collect::<Result<Vec<Dynamic>, _>>()?;
self.call_fn_raw( self.call_fn_raw(
fn_name, fn_name,

View File

@ -75,9 +75,12 @@ mod scope;
pub use any::{Any, AnyExt, Dynamic, Variant}; pub use any::{Any, AnyExt, Dynamic, Variant};
pub use call::FuncArgs; pub use call::FuncArgs;
pub use engine::{Array, Engine}; pub use engine::Engine;
pub use error::{ParseError, ParseErrorType}; pub use error::{ParseError, ParseErrorType};
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn}; pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};
pub use parser::{Position, AST}; pub use parser::{Position, AST};
pub use result::EvalAltResult; pub use result::EvalAltResult;
pub use scope::Scope; pub use scope::Scope;
#[cfg(not(feature = "no_index"))]
pub use engine::Array;

View File

@ -139,11 +139,24 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr {
Box::new(optimize_expr(*rhs, changed)), Box::new(optimize_expr(*rhs, changed)),
pos, pos,
), ),
Expr::Index(lhs, rhs, pos) => Expr::Index( #[cfg(not(feature = "no_index"))]
Box::new(optimize_expr(*lhs, changed)), Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) {
Box::new(optimize_expr(*rhs, changed)), (Expr::Array(mut items, _), Expr::IntegerConstant(i, _))
if i >= 0
&& (i as usize) < items.len()
&& !items.iter().any(|x| x.is_constant()) =>
{
// Array where everything is a constant - promote the item
*changed = true;
items.remove(i as usize)
}
(lhs, rhs) => Expr::Index(
Box::new(optimize_expr(lhs, changed)),
Box::new(optimize_expr(rhs, changed)),
pos, pos,
), ),
},
#[cfg(not(feature = "no_index"))]
Expr::Array(items, pos) => { Expr::Array(items, pos) => {
let original_len = items.len(); let original_len = items.len();

View File

@ -167,7 +167,9 @@ pub enum Expr {
FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position), FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position),
Assignment(Box<Expr>, Box<Expr>, Position), Assignment(Box<Expr>, Box<Expr>, Position),
Dot(Box<Expr>, Box<Expr>, Position), Dot(Box<Expr>, Box<Expr>, Position),
#[cfg(not(feature = "no_index"))]
Index(Box<Expr>, Box<Expr>, Position), Index(Box<Expr>, Box<Expr>, Position),
#[cfg(not(feature = "no_index"))]
Array(Vec<Expr>, Position), Array(Vec<Expr>, Position),
And(Box<Expr>, Box<Expr>), And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>), Or(Box<Expr>, Box<Expr>),
@ -186,16 +188,19 @@ impl Expr {
| Expr::StringConstant(_, pos) | Expr::StringConstant(_, pos)
| Expr::FunctionCall(_, _, _, pos) | Expr::FunctionCall(_, _, _, pos)
| Expr::Stmt(_, pos) | Expr::Stmt(_, pos)
| Expr::Array(_, pos)
| Expr::True(pos) | Expr::True(pos)
| Expr::False(pos) | Expr::False(pos)
| Expr::Unit(pos) => *pos, | Expr::Unit(pos) => *pos,
Expr::Index(e, _, _) Expr::Assignment(e, _, _) | Expr::Dot(e, _, _) | Expr::And(e, _) | Expr::Or(e, _) => {
| Expr::Assignment(e, _, _) e.position()
| Expr::Dot(e, _, _) }
| Expr::And(e, _)
| Expr::Or(e, _) => e.position(), #[cfg(not(feature = "no_index"))]
Expr::Index(e, _, _) => e.position(),
#[cfg(not(feature = "no_index"))]
Expr::Array(_, pos) => *pos,
} }
} }
@ -1166,6 +1171,7 @@ fn parse_call_expr<'a>(
} }
} }
#[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>>,
@ -1260,6 +1266,7 @@ fn parse_ident_expr<'a>(
input.next(); input.next();
parse_call_expr(id, input, begin) parse_call_expr(id, input, begin)
} }
#[cfg(not(feature = "no_index"))]
Some(&(Token::LeftBracket, pos)) => { Some(&(Token::LeftBracket, pos)) => {
input.next(); input.next();
parse_index_expr(Box::new(Expr::Identifier(id, begin)), input, pos) parse_index_expr(Box::new(Expr::Identifier(id, begin)), input, pos)
@ -1269,6 +1276,7 @@ fn parse_ident_expr<'a>(
} }
} }
#[cfg(not(feature = "no_index"))]
fn parse_array_expr<'a>( fn parse_array_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
begin: Position, begin: Position,
@ -1320,26 +1328,28 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
let token = input.next(); let token = input.next();
let mut follow_on = false; let mut can_be_indexed = false;
#[allow(unused_mut)]
let mut root_expr = match token { let mut root_expr = match token {
Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)), Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)),
Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)), Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)),
Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)), Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)),
Some((Token::StringConst(s), pos)) => { Some((Token::StringConst(s), pos)) => {
follow_on = true; can_be_indexed = true;
Ok(Expr::StringConstant(s, pos)) Ok(Expr::StringConstant(s, pos))
} }
Some((Token::Identifier(s), pos)) => { Some((Token::Identifier(s), pos)) => {
follow_on = true; can_be_indexed = true;
parse_ident_expr(s, input, pos) parse_ident_expr(s, input, pos)
} }
Some((Token::LeftParen, pos)) => { Some((Token::LeftParen, pos)) => {
follow_on = true; can_be_indexed = true;
parse_paren_expr(input, pos) parse_paren_expr(input, pos)
} }
#[cfg(not(feature = "no_index"))]
Some((Token::LeftBracket, pos)) => { Some((Token::LeftBracket, pos)) => {
follow_on = true; can_be_indexed = true;
parse_array_expr(input, pos) parse_array_expr(input, pos)
} }
Some((Token::True, pos)) => Ok(Expr::True(pos)), Some((Token::True, pos)) => Ok(Expr::True(pos)),
@ -1354,15 +1364,14 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
None => Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())), None => Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
}?; }?;
if !follow_on { if can_be_indexed {
return Ok(root_expr);
}
// 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() {
input.next(); input.next();
root_expr = parse_index_expr(Box::new(root_expr), input, pos)?; root_expr = parse_index_expr(Box::new(root_expr), input, pos)?;
} }
}
Ok(root_expr) Ok(root_expr)
} }
@ -1408,6 +1417,7 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
match expr { match expr {
Expr::Identifier(_, pos) => (true, *pos), Expr::Identifier(_, pos) => (true, *pos),
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() { Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
Expr::Identifier(_, _) => (true, idx_lhs.position()), Expr::Identifier(_, _) => (true, idx_lhs.position()),
_ => (false, idx_lhs.position()), _ => (false, idx_lhs.position()),
@ -1415,10 +1425,13 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() { Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() {
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs), Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
#[cfg(not(feature = "no_index"))]
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() { Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs), Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
_ => (false, idx_lhs.position()), _ => (false, idx_lhs.position()),
}, },
_ => (false, dot_lhs.position()), _ => (false, dot_lhs.position()),
}, },

View File

@ -173,6 +173,12 @@ impl From<ParseError> for EvalAltResult {
} }
} }
impl<T: AsRef<str>> From<T> for EvalAltResult {
fn from(err: T) -> Self {
Self::ErrorRuntime(err.as_ref().to_string(), Position::none())
}
}
impl EvalAltResult { impl EvalAltResult {
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
@ -225,9 +231,3 @@ impl EvalAltResult {
} }
} }
} }
impl<T: AsRef<str>> From<T> for EvalAltResult {
fn from(err: T) -> Self {
Self::ErrorRuntime(err.as_ref().to_string(), Position::none())
}
}

View File

@ -93,6 +93,7 @@ impl<'a> Scope<'a> {
} }
/// Get a mutable reference to a variable in the Scope and downcast it to a specific type /// Get a mutable reference to a variable in the Scope and downcast it to a specific type
#[cfg(not(feature = "no_index"))]
pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, key: &str, index: usize) -> &mut T { pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, key: &str, index: usize) -> &mut T {
self.get_mut(key, index) self.get_mut(key, index)
.downcast_mut::<T>() .downcast_mut::<T>()