Reduce cloning.
This commit is contained in:
parent
dbfc38763a
commit
952932f64c
31
src/api.rs
31
src/api.rs
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::any::{Any, AnyExt, Dynamic};
|
use crate::any::{Any, AnyExt, Dynamic};
|
||||||
use crate::call::FuncArgs;
|
use crate::call::FuncArgs;
|
||||||
use crate::engine::{Engine, FnAny, FnCallArgs, FnIntExt, FnSpec};
|
use crate::engine::{Engine, FnAny, FnCallArgs, FnSpec};
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
use crate::fn_register::RegisterFn;
|
use crate::fn_register::RegisterFn;
|
||||||
use crate::parser::{lex, parse, Position, AST};
|
use crate::parser::{lex, parse, Position, AST};
|
||||||
@ -12,7 +12,6 @@ use std::{
|
|||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
fs::File,
|
fs::File,
|
||||||
io::prelude::*,
|
io::prelude::*,
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'e> Engine<'e> {
|
impl<'e> Engine<'e> {
|
||||||
@ -41,7 +40,7 @@ impl<'e> Engine<'e> {
|
|||||||
args,
|
args,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.ext_functions.insert(spec, Arc::new(FnIntExt::Ext(f)));
|
self.ext_functions.insert(spec, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a custom type for use with the `Engine`.
|
/// Register a custom type for use with the `Engine`.
|
||||||
@ -63,7 +62,7 @@ impl<'e> Engine<'e> {
|
|||||||
where
|
where
|
||||||
F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static,
|
F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static,
|
||||||
{
|
{
|
||||||
self.type_iterators.insert(TypeId::of::<T>(), Arc::new(f));
|
self.type_iterators.insert(TypeId::of::<T>(), Box::new(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a getter function for a member of a registered type with the `Engine`.
|
/// Register a getter function for a member of a registered type with the `Engine`.
|
||||||
@ -176,13 +175,7 @@ impl<'e> Engine<'e> {
|
|||||||
let AST(statements, functions) = ast;
|
let AST(statements, functions) = ast;
|
||||||
|
|
||||||
functions.iter().for_each(|f| {
|
functions.iter().for_each(|f| {
|
||||||
engine.script_functions.insert(
|
engine.script_functions.push(f.clone());
|
||||||
FnSpec {
|
|
||||||
name: f.name.clone().into(),
|
|
||||||
args: None,
|
|
||||||
},
|
|
||||||
Arc::new(FnIntExt::Int(f.clone())),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
statements
|
statements
|
||||||
@ -261,13 +254,7 @@ impl<'e> Engine<'e> {
|
|||||||
let AST(ref statements, ref functions) = ast;
|
let AST(ref statements, ref functions) = ast;
|
||||||
|
|
||||||
functions.iter().for_each(|f| {
|
functions.iter().for_each(|f| {
|
||||||
self.script_functions.insert(
|
self.script_functions.push(f.clone());
|
||||||
FnSpec {
|
|
||||||
name: f.name.clone().into(),
|
|
||||||
args: None,
|
|
||||||
},
|
|
||||||
Arc::new(FnIntExt::Int(f.clone())),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
statements
|
statements
|
||||||
@ -322,13 +309,7 @@ impl<'e> Engine<'e> {
|
|||||||
args: FnCallArgs,
|
args: FnCallArgs,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
ast.1.iter().for_each(|f| {
|
ast.1.iter().for_each(|f| {
|
||||||
engine.script_functions.insert(
|
engine.script_functions.push(f.clone());
|
||||||
FnSpec {
|
|
||||||
name: f.name.clone().into(),
|
|
||||||
args: None,
|
|
||||||
},
|
|
||||||
Arc::new(FnIntExt::Int(f.clone())),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = engine.call_fn_raw(name, args, None, Position::none());
|
let result = engine.call_fn_raw(name, args, None, Position::none());
|
||||||
|
184
src/engine.rs
184
src/engine.rs
@ -1,7 +1,7 @@
|
|||||||
//! Main module defining the script evaluation `Engine`.
|
//! Main module defining the script evaluation `Engine`.
|
||||||
|
|
||||||
use crate::any::{Any, AnyExt, Dynamic, Variant};
|
use crate::any::{Any, AnyExt, Dynamic, Variant};
|
||||||
use crate::parser::{Expr, FnDef, Position, Stmt};
|
use crate::parser::{Expr, FnDef, Position, ReturnType, Stmt};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
|
||||||
@ -11,7 +11,6 @@ use crate::INT;
|
|||||||
use std::{
|
use std::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp::{PartialEq, PartialOrd},
|
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
iter::once,
|
iter::once,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
@ -34,7 +33,7 @@ pub(crate) const KEYWORD_TYPE_OF: &'static str = "type_of";
|
|||||||
pub(crate) const FUNC_GETTER: &'static str = "get$";
|
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(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
enum IndexSourceType {
|
enum IndexSourceType {
|
||||||
Array,
|
Array,
|
||||||
@ -42,7 +41,7 @@ enum IndexSourceType {
|
|||||||
Expression,
|
Expression,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct FnSpec<'a> {
|
pub struct FnSpec<'a> {
|
||||||
pub name: Cow<'a, str>,
|
pub name: Cow<'a, str>,
|
||||||
pub args: Option<Vec<TypeId>>,
|
pub args: Option<Vec<TypeId>>,
|
||||||
@ -66,11 +65,11 @@ pub struct Engine<'e> {
|
|||||||
/// Optimize the AST after compilation
|
/// Optimize the AST after compilation
|
||||||
pub(crate) optimize: bool,
|
pub(crate) optimize: bool,
|
||||||
/// A hashmap containing all compiled functions known to the engine
|
/// A hashmap containing all compiled functions known to the engine
|
||||||
pub(crate) ext_functions: HashMap<FnSpec<'e>, Arc<FnIntExt<'e>>>,
|
pub(crate) ext_functions: HashMap<FnSpec<'e>, Box<FnAny>>,
|
||||||
/// A hashmap containing all script-defined functions
|
/// A hashmap containing all script-defined functions
|
||||||
pub(crate) script_functions: HashMap<FnSpec<'e>, Arc<FnIntExt<'e>>>,
|
pub(crate) script_functions: Vec<Arc<FnDef>>,
|
||||||
/// A hashmap containing all iterators known to the engine
|
/// A hashmap containing all iterators known to the engine
|
||||||
pub(crate) type_iterators: HashMap<TypeId, Arc<IteratorFn>>,
|
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
||||||
pub(crate) type_names: HashMap<String, String>,
|
pub(crate) type_names: HashMap<String, String>,
|
||||||
|
|
||||||
// Closures for implementing the print/debug commands
|
// Closures for implementing the print/debug commands
|
||||||
@ -78,11 +77,6 @@ pub struct Engine<'e> {
|
|||||||
pub(crate) on_debug: Box<dyn FnMut(&str) + 'e>,
|
pub(crate) on_debug: Box<dyn FnMut(&str) + 'e>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FnIntExt<'a> {
|
|
||||||
Ext(Box<FnAny>),
|
|
||||||
Int(FnDef<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Engine<'_> {
|
impl Engine<'_> {
|
||||||
/// Create a new `Engine`
|
/// Create a new `Engine`
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -101,7 +95,7 @@ impl Engine<'_> {
|
|||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
optimize: true,
|
optimize: true,
|
||||||
ext_functions: HashMap::new(),
|
ext_functions: HashMap::new(),
|
||||||
script_functions: HashMap::new(),
|
script_functions: Vec::new(),
|
||||||
type_iterators: HashMap::new(),
|
type_iterators: HashMap::new(),
|
||||||
type_names,
|
type_names,
|
||||||
on_print: Box::new(default_print), // default print/debug implementations
|
on_print: Box::new(default_print), // default print/debug implementations
|
||||||
@ -140,26 +134,49 @@ impl Engine<'_> {
|
|||||||
.join(", ")
|
.join(", ")
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut spec = FnSpec {
|
// First search in script-defined functions (can override built-in)
|
||||||
|
if let Some(func) = self
|
||||||
|
.script_functions
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.find(|fn_def| fn_def.name == fn_name)
|
||||||
|
.map(|fn_def| fn_def.clone())
|
||||||
|
{
|
||||||
|
// First check number of parameters
|
||||||
|
if func.params.len() != args.len() {
|
||||||
|
return Err(EvalAltResult::ErrorFunctionArgsMismatch(
|
||||||
|
fn_name.into(),
|
||||||
|
func.params.len(),
|
||||||
|
args.len(),
|
||||||
|
pos,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
scope.extend(
|
||||||
|
// Put arguments into scope as variables
|
||||||
|
func.params
|
||||||
|
.iter()
|
||||||
|
.zip(args.iter().map(|x| (*x).into_dynamic())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Evaluate
|
||||||
|
return match self.eval_stmt(&mut scope, &func.body) {
|
||||||
|
// Convert return statement to return value
|
||||||
|
Err(EvalAltResult::Return(x, _)) => Ok(x),
|
||||||
|
other => other,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let spec = FnSpec {
|
||||||
name: fn_name.into(),
|
name: fn_name.into(),
|
||||||
args: None,
|
args: Some(args.iter().map(|a| Any::type_id(&**a)).collect()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// First search in script-defined functions (can override built-in),
|
// Then search built-in's and external functions
|
||||||
// then built-in's and external functions
|
if let Some(func) = self.ext_functions.get(&spec) {
|
||||||
let fn_def = self
|
|
||||||
.script_functions
|
|
||||||
.get(&spec)
|
|
||||||
.or_else(|| {
|
|
||||||
spec.args = Some(args.iter().map(|a| Any::type_id(&**a)).collect());
|
|
||||||
self.ext_functions.get(&spec)
|
|
||||||
})
|
|
||||||
.map(|f| f.clone());
|
|
||||||
|
|
||||||
if let Some(f) = fn_def {
|
|
||||||
match *f {
|
|
||||||
// Run external function
|
// Run external function
|
||||||
FnIntExt::Ext(ref func) => {
|
|
||||||
let result = func(args, pos)?;
|
let result = func(args, pos)?;
|
||||||
|
|
||||||
// See if the function match print/debug (which requires special processing)
|
// See if the function match print/debug (which requires special processing)
|
||||||
@ -174,67 +191,44 @@ impl Engine<'_> {
|
|||||||
.map(|s| *s)
|
.map(|s| *s)
|
||||||
.unwrap_or("error: not a string".into());
|
.unwrap_or("error: not a string".into());
|
||||||
|
|
||||||
Ok(callback(val).into_dynamic())
|
return Ok(callback(val).into_dynamic());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run script-defined function
|
if spec.name == KEYWORD_TYPE_OF && args.len() == 1 {
|
||||||
FnIntExt::Int(ref func) => {
|
|
||||||
// First check number of parameters
|
|
||||||
if func.params.len() != args.len() {
|
|
||||||
return Err(EvalAltResult::ErrorFunctionArgsMismatch(
|
|
||||||
spec.name.into(),
|
|
||||||
func.params.len(),
|
|
||||||
args.len(),
|
|
||||||
pos,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
|
|
||||||
scope.extend(
|
|
||||||
// Put arguments into scope as variables
|
|
||||||
func.params
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.zip(args.iter().map(|x| (*x).into_dynamic())),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Evaluate
|
|
||||||
match self.eval_stmt(&mut scope, &func.body) {
|
|
||||||
// Convert return statement to return value
|
|
||||||
Err(EvalAltResult::Return(x, _)) => Ok(x),
|
|
||||||
other => other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if spec.name == KEYWORD_TYPE_OF && args.len() == 1 {
|
|
||||||
// Handle `type_of` function
|
// Handle `type_of` function
|
||||||
Ok(self
|
return Ok(self
|
||||||
.map_type_name(args[0].type_name())
|
.map_type_name(args[0].type_name())
|
||||||
.to_string()
|
.to_string()
|
||||||
.into_dynamic())
|
.into_dynamic());
|
||||||
} else if spec.name.starts_with(FUNC_GETTER) {
|
}
|
||||||
|
|
||||||
|
if spec.name.starts_with(FUNC_GETTER) {
|
||||||
// Getter function not found
|
// Getter function not found
|
||||||
Err(EvalAltResult::ErrorDotExpr(
|
return Err(EvalAltResult::ErrorDotExpr(
|
||||||
format!(
|
format!(
|
||||||
"- property '{}' unknown or write-only",
|
"- property '{}' unknown or write-only",
|
||||||
&spec.name[FUNC_GETTER.len()..]
|
&spec.name[FUNC_GETTER.len()..]
|
||||||
),
|
),
|
||||||
pos,
|
pos,
|
||||||
))
|
));
|
||||||
} else if spec.name.starts_with(FUNC_SETTER) {
|
}
|
||||||
|
|
||||||
|
if spec.name.starts_with(FUNC_SETTER) {
|
||||||
// Setter function not found
|
// Setter function not found
|
||||||
Err(EvalAltResult::ErrorDotExpr(
|
return Err(EvalAltResult::ErrorDotExpr(
|
||||||
format!(
|
format!(
|
||||||
"- property '{}' unknown or read-only",
|
"- property '{}' unknown or read-only",
|
||||||
&spec.name[FUNC_SETTER.len()..]
|
&spec.name[FUNC_SETTER.len()..]
|
||||||
),
|
),
|
||||||
pos,
|
pos,
|
||||||
))
|
));
|
||||||
} else if let Some(val) = def_val {
|
}
|
||||||
|
|
||||||
|
if let Some(val) = def_val {
|
||||||
// Return default value
|
// Return default value
|
||||||
Ok(val.clone())
|
return Ok(val.clone());
|
||||||
} else {
|
}
|
||||||
|
|
||||||
// Raise error
|
// Raise error
|
||||||
let types_list = args
|
let types_list = args
|
||||||
.iter()
|
.iter()
|
||||||
@ -247,7 +241,6 @@ impl Engine<'_> {
|
|||||||
pos,
|
pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Chain-evaluate a dot setter
|
/// Chain-evaluate a dot setter
|
||||||
fn get_dot_val_helper(
|
fn get_dot_val_helper(
|
||||||
@ -281,7 +274,7 @@ impl Engine<'_> {
|
|||||||
// xxx.idx_lhs[idx_expr]
|
// xxx.idx_lhs[idx_expr]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[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 (val, _) = match idx_lhs.as_ref() {
|
||||||
// xxx.id[idx_expr]
|
// xxx.id[idx_expr]
|
||||||
Expr::Identifier(id, pos) => {
|
Expr::Identifier(id, pos) => {
|
||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
@ -304,7 +297,7 @@ impl Engine<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||||
self.get_indexed_value(expr, idx, idx_expr.position(), *idx_pos)
|
self.get_indexed_value(&val, idx, idx_expr.position(), *idx_pos)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +313,7 @@ impl Engine<'_> {
|
|||||||
// xxx.idx_lhs[idx_expr].rhs
|
// xxx.idx_lhs[idx_expr].rhs
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[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 (val, _) = match idx_lhs.as_ref() {
|
||||||
// xxx.id[idx_expr].rhs
|
// xxx.id[idx_expr].rhs
|
||||||
Expr::Identifier(id, pos) => {
|
Expr::Identifier(id, pos) => {
|
||||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||||
@ -343,7 +336,7 @@ impl Engine<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||||
self.get_indexed_value(expr, idx, idx_expr.position(), *idx_pos)
|
self.get_indexed_value(&val, idx, idx_expr.position(), *idx_pos)
|
||||||
.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))
|
||||||
}
|
}
|
||||||
// Syntax error
|
// Syntax error
|
||||||
@ -441,14 +434,14 @@ impl Engine<'_> {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
fn get_indexed_value(
|
fn get_indexed_value(
|
||||||
&self,
|
&self,
|
||||||
val: Dynamic,
|
val: &Dynamic,
|
||||||
idx: INT,
|
idx: INT,
|
||||||
val_pos: Position,
|
val_pos: Position,
|
||||||
idx_pos: Position,
|
idx_pos: Position,
|
||||||
) -> Result<(Dynamic, IndexSourceType), EvalAltResult> {
|
) -> Result<(Dynamic, IndexSourceType), EvalAltResult> {
|
||||||
if val.is::<Array>() {
|
if val.is::<Array>() {
|
||||||
// val_array[idx]
|
// val_array[idx]
|
||||||
let arr = val.downcast::<Array>().expect("array expected");
|
let arr = val.downcast_ref::<Array>().expect("array expected");
|
||||||
|
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
arr.get(idx as usize)
|
arr.get(idx as usize)
|
||||||
@ -460,7 +453,7 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
} else if val.is::<String>() {
|
} else if val.is::<String>() {
|
||||||
// val_string[idx]
|
// val_string[idx]
|
||||||
let s = val.downcast::<String>().expect("string expected");
|
let s = val.downcast_ref::<String>().expect("string expected");
|
||||||
|
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
s.chars()
|
s.chars()
|
||||||
@ -501,7 +494,7 @@ impl Engine<'_> {
|
|||||||
Expr::Identifier(id, _) => Self::search_scope(
|
Expr::Identifier(id, _) => Self::search_scope(
|
||||||
scope,
|
scope,
|
||||||
&id,
|
&id,
|
||||||
|val| self.get_indexed_value(val, idx, idx_expr.position(), idx_pos),
|
|val| self.get_indexed_value(&val, idx, idx_expr.position(), idx_pos),
|
||||||
lhs.position(),
|
lhs.position(),
|
||||||
)
|
)
|
||||||
.map(|(src_idx, (val, src_type))| {
|
.map(|(src_idx, (val, src_type))| {
|
||||||
@ -512,7 +505,7 @@ impl Engine<'_> {
|
|||||||
expr => {
|
expr => {
|
||||||
let val = self.eval_expr(scope, expr)?;
|
let val = self.eval_expr(scope, expr)?;
|
||||||
|
|
||||||
self.get_indexed_value(val, idx, idx_expr.position(), idx_pos)
|
self.get_indexed_value(&val, idx, idx_expr.position(), idx_pos)
|
||||||
.map(|(v, _)| (IndexSourceType::Expression, None, idx as usize, v))
|
.map(|(v, _)| (IndexSourceType::Expression, None, idx as usize, v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -663,12 +656,8 @@ impl Engine<'_> {
|
|||||||
self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)
|
self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)
|
||||||
.and_then(|v| {
|
.and_then(|v| {
|
||||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||||
let (mut target, _) = self.get_indexed_value(
|
let (mut target, _) =
|
||||||
v.clone(), // TODO - Avoid cloning this
|
self.get_indexed_value(&v, idx, idx_expr.position(), *idx_pos)?;
|
||||||
idx,
|
|
||||||
idx_expr.position(),
|
|
||||||
*idx_pos,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.set_dot_val_helper(
|
self.set_dot_val_helper(
|
||||||
scope,
|
scope,
|
||||||
@ -1042,22 +1031,22 @@ impl Engine<'_> {
|
|||||||
Stmt::Break(_) => Err(EvalAltResult::LoopBreak),
|
Stmt::Break(_) => Err(EvalAltResult::LoopBreak),
|
||||||
|
|
||||||
// Empty return
|
// Empty return
|
||||||
Stmt::ReturnWithVal(None, true, pos) => {
|
Stmt::ReturnWithVal(None, ReturnType::Return, pos) => {
|
||||||
Err(EvalAltResult::Return(().into_dynamic(), *pos))
|
Err(EvalAltResult::Return(().into_dynamic(), *pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return value
|
// Return value
|
||||||
Stmt::ReturnWithVal(Some(a), true, pos) => {
|
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => {
|
||||||
Err(EvalAltResult::Return(self.eval_expr(scope, a)?, *pos))
|
Err(EvalAltResult::Return(self.eval_expr(scope, a)?, *pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty throw
|
// Empty throw
|
||||||
Stmt::ReturnWithVal(None, false, pos) => {
|
Stmt::ReturnWithVal(None, ReturnType::Exception, pos) => {
|
||||||
Err(EvalAltResult::ErrorRuntime("".into(), *pos))
|
Err(EvalAltResult::ErrorRuntime("".into(), *pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw value
|
// Throw value
|
||||||
Stmt::ReturnWithVal(Some(a), false, pos) => {
|
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => {
|
||||||
let val = self.eval_expr(scope, a)?;
|
let val = self.eval_expr(scope, a)?;
|
||||||
Err(EvalAltResult::ErrorRuntime(
|
Err(EvalAltResult::ErrorRuntime(
|
||||||
val.downcast::<String>()
|
val.downcast::<String>()
|
||||||
@ -1068,13 +1057,14 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let statement
|
// Let statement
|
||||||
Stmt::Let(name, init, _) => {
|
Stmt::Let(name, Some(expr), _) => {
|
||||||
if let Some(v) = init {
|
let val = self.eval_expr(scope, expr)?;
|
||||||
let val = self.eval_expr(scope, v)?;
|
|
||||||
scope.push_dynamic(name.clone(), val);
|
scope.push_dynamic(name.clone(), val);
|
||||||
} else {
|
Ok(().into_dynamic())
|
||||||
scope.push(name.clone(), ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stmt::Let(name, None, _) => {
|
||||||
|
scope.push(name.clone(), ());
|
||||||
Ok(().into_dynamic())
|
Ok(().into_dynamic())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
119
src/parser.rs
119
src/parser.rs
@ -4,7 +4,7 @@ use crate::any::Dynamic;
|
|||||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::optimize::optimize;
|
use crate::optimize::optimize;
|
||||||
|
|
||||||
use std::{borrow::Cow, char, fmt, iter::Peekable, str::Chars, str::FromStr, usize};
|
use std::{borrow::Cow, char, fmt, iter::Peekable, str::Chars, str::FromStr, sync::Arc, usize};
|
||||||
|
|
||||||
/// The system integer type.
|
/// The system integer type.
|
||||||
///
|
///
|
||||||
@ -139,19 +139,26 @@ impl fmt::Debug for Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct AST(
|
pub struct AST(
|
||||||
pub(crate) Vec<Stmt>,
|
pub(crate) Vec<Stmt>,
|
||||||
#[cfg(not(feature = "no_function"))] pub(crate) Vec<FnDef<'static>>,
|
#[cfg(not(feature = "no_function"))] pub(crate) Vec<Arc<FnDef>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)] // Do not derive Clone because it is expensive
|
||||||
pub struct FnDef<'a> {
|
pub struct FnDef {
|
||||||
pub name: Cow<'a, str>,
|
pub name: String,
|
||||||
pub params: Vec<Cow<'a, str>>,
|
pub params: Vec<String>,
|
||||||
pub body: Stmt,
|
pub body: Stmt,
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
pub enum ReturnType {
|
||||||
|
Return,
|
||||||
|
Exception,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Noop(Position),
|
Noop(Position),
|
||||||
@ -163,7 +170,7 @@ pub enum Stmt {
|
|||||||
Block(Vec<Stmt>, Position),
|
Block(Vec<Stmt>, Position),
|
||||||
Expr(Box<Expr>),
|
Expr(Box<Expr>),
|
||||||
Break(Position),
|
Break(Position),
|
||||||
ReturnWithVal(Option<Box<Expr>>, bool, Position),
|
ReturnWithVal(Option<Box<Expr>>, ReturnType, Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
@ -173,6 +180,25 @@ impl Stmt {
|
|||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_var(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Stmt::Let(_, _, _) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(&self) -> Position {
|
||||||
|
match self {
|
||||||
|
Stmt::Noop(pos)
|
||||||
|
| Stmt::Let(_, _, pos)
|
||||||
|
| Stmt::Block(_, pos)
|
||||||
|
| Stmt::Break(pos)
|
||||||
|
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
||||||
|
Stmt::IfElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
|
||||||
|
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -221,9 +247,13 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_constant(&self) -> bool {
|
/// Is this expression pure?
|
||||||
|
///
|
||||||
|
/// A pure expression has no side effects.
|
||||||
|
pub fn is_pure(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Expr::IntegerConstant(_, _)
|
Expr::Identifier(_, _)
|
||||||
|
| Expr::IntegerConstant(_, _)
|
||||||
| Expr::CharConstant(_, _)
|
| Expr::CharConstant(_, _)
|
||||||
| Expr::StringConstant(_, _)
|
| Expr::StringConstant(_, _)
|
||||||
| Expr::True(_)
|
| Expr::True(_)
|
||||||
@ -233,6 +263,8 @@ impl Expr {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Expr::FloatConstant(_, _) => true,
|
Expr::FloatConstant(_, _) => true,
|
||||||
|
|
||||||
|
Expr::Array(expressions, _) => expressions.iter().all(Expr::is_pure),
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1425,17 +1457,16 @@ fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pars
|
|||||||
|
|
||||||
match parse_unary(input) {
|
match parse_unary(input) {
|
||||||
// Negative integer
|
// Negative integer
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Ok(Expr::IntegerConstant(i, _)) => Ok(i
|
|
||||||
.checked_neg()
|
|
||||||
.map(|x| Expr::IntegerConstant(x, pos))
|
|
||||||
.unwrap_or_else(|| Expr::FloatConstant(-(i as FLOAT), pos))),
|
|
||||||
|
|
||||||
// Negative integer
|
|
||||||
#[cfg(feature = "no_float")]
|
|
||||||
Ok(Expr::IntegerConstant(i, _)) => i
|
Ok(Expr::IntegerConstant(i, _)) => i
|
||||||
.checked_neg()
|
.checked_neg()
|
||||||
.map(|x| Expr::IntegerConstant(x, pos))
|
.map(|x| Expr::IntegerConstant(x, pos))
|
||||||
|
.or_else(|| {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
return Some(Expr::FloatConstant(-(i as FLOAT), pos));
|
||||||
|
|
||||||
|
#[cfg(feature = "no_float")]
|
||||||
|
return None;
|
||||||
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
ParseError::new(
|
ParseError::new(
|
||||||
PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string()),
|
PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string()),
|
||||||
@ -1485,10 +1516,11 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
|
|||||||
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
|
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
|
Expr::Index(idx_lhs, _, _) if idx_lhs.is_identifier() => {
|
||||||
Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs),
|
valid_assignment_chain(dot_rhs)
|
||||||
_ => (false, idx_lhs.position()),
|
}
|
||||||
},
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Expr::Index(idx_lhs, _, _) => (false, idx_lhs.position()),
|
||||||
|
|
||||||
_ => (false, dot_lhs.position()),
|
_ => (false, dot_lhs.position()),
|
||||||
},
|
},
|
||||||
@ -1497,7 +1529,7 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//println!("{:?} = {:?}", lhs, rhs);
|
//println!("{:#?} = {:#?}", lhs, rhs);
|
||||||
|
|
||||||
match valid_assignment_chain(&lhs) {
|
match valid_assignment_chain(&lhs) {
|
||||||
(true, _) => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
(true, _) => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
||||||
@ -1797,23 +1829,23 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
|
|||||||
Ok(Stmt::Break(pos))
|
Ok(Stmt::Break(pos))
|
||||||
}
|
}
|
||||||
Some(&(ref token @ Token::Return, _)) | Some(&(ref token @ Token::Throw, _)) => {
|
Some(&(ref token @ Token::Return, _)) | Some(&(ref token @ Token::Throw, _)) => {
|
||||||
let is_return = match token {
|
let return_type = match token {
|
||||||
Token::Return => true,
|
Token::Return => ReturnType::Return,
|
||||||
Token::Throw => false,
|
Token::Throw => ReturnType::Exception,
|
||||||
_ => panic!(),
|
_ => panic!("unexpected token!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
input.next();
|
input.next();
|
||||||
|
|
||||||
match input.peek() {
|
match input.peek() {
|
||||||
// return; or throw;
|
// return; or throw;
|
||||||
Some(&(Token::SemiColon, pos)) => Ok(Stmt::ReturnWithVal(None, is_return, pos)),
|
Some(&(Token::SemiColon, pos)) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
||||||
// Just a return/throw without anything at the end of script
|
// Just a return/throw without anything at the end of script
|
||||||
None => Ok(Stmt::ReturnWithVal(None, is_return, Position::eof())),
|
None => Ok(Stmt::ReturnWithVal(None, return_type, Position::eof())),
|
||||||
// return or throw with expression
|
// return or throw with expression
|
||||||
Some(&(_, pos)) => {
|
Some(&(_, pos)) => {
|
||||||
let ret = parse_expr(input)?;
|
let ret = parse_expr(input)?;
|
||||||
Ok(Stmt::ReturnWithVal(Some(Box::new(ret)), is_return, pos))
|
Ok(Stmt::ReturnWithVal(Some(Box::new(ret)), return_type, pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1824,7 +1856,7 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef<'static>, ParseError> {
|
fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef, ParseError> {
|
||||||
let pos = match input.next() {
|
let pos = match input.next() {
|
||||||
Some((_, tok_pos)) => tok_pos,
|
Some((_, tok_pos)) => tok_pos,
|
||||||
_ => return Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
|
_ => return Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
|
||||||
@ -1885,7 +1917,7 @@ fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef<'static
|
|||||||
let body = parse_block(input)?;
|
let body = parse_block(input)?;
|
||||||
|
|
||||||
Ok(FnDef {
|
Ok(FnDef {
|
||||||
name: name.into(),
|
name,
|
||||||
params,
|
params,
|
||||||
body,
|
body,
|
||||||
pos,
|
pos,
|
||||||
@ -1914,26 +1946,25 @@ fn parse_top_level<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(if optimize_ast {
|
return Ok(AST(
|
||||||
AST(
|
if optimize_ast {
|
||||||
optimize(statements),
|
optimize(statements)
|
||||||
|
} else {
|
||||||
|
statements
|
||||||
|
},
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions
|
functions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut fn_def| {
|
.map(|mut fn_def| {
|
||||||
|
if optimize_ast {
|
||||||
|
let pos = fn_def.body.position();
|
||||||
let mut body = optimize(vec![fn_def.body]);
|
let mut body = optimize(vec![fn_def.body]);
|
||||||
fn_def.body = body.pop().unwrap();
|
fn_def.body = body.pop().unwrap_or_else(|| Stmt::Noop(pos));
|
||||||
fn_def
|
}
|
||||||
|
Arc::new(fn_def)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
));
|
||||||
} else {
|
|
||||||
AST(
|
|
||||||
statements,
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
functions,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse<'a>(
|
pub fn parse<'a>(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user