Reduce cloning.

This commit is contained in:
Stephen Chung 2020-03-11 23:43:04 +08:00
parent dbfc38763a
commit 952932f64c
3 changed files with 200 additions and 198 deletions

View File

@ -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());

View File

@ -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())
} }
} }

View File

@ -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>(