Reducing boxing and sizes of Expr/Stmt.

This commit is contained in:
Stephen Chung 2020-05-09 21:46:38 +08:00
parent 17e4adc049
commit a7bfac21bd
5 changed files with 851 additions and 891 deletions

View File

@ -857,78 +857,94 @@ impl Engine {
if is_index { if is_index {
match rhs { match rhs {
// xxx[idx].dot_rhs... // xxx[idx].dot_rhs... | xxx[idx][dot_rhs]...
Expr::Dot(idx, idx_rhs, pos) | Expr::Dot(x) | Expr::Index(x) => {
// xxx[idx][dot_rhs]... let is_index = matches!(rhs, Expr::Index(_));
Expr::Index(idx, idx_rhs, pos) => {
let is_index = matches!(rhs, Expr::Index(_,_,_));
let indexed_val = self.get_indexed_mut(state, obj, idx_val, idx.position(), op_pos, false)?; let indexed_val =
self.get_indexed_mut(state, obj, idx_val, x.0.position(), op_pos, false)?;
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, indexed_val, idx_rhs.as_ref(), idx_values, is_index, *pos, level, new_val state,
indexed_val,
&x.1,
idx_values,
is_index,
x.2,
level,
new_val,
) )
} }
// xxx[rhs] = new_val // xxx[rhs] = new_val
_ if new_val.is_some() => { _ if new_val.is_some() => {
let mut indexed_val = self.get_indexed_mut(state, obj, idx_val, rhs.position(), op_pos, true)?; let mut indexed_val =
self.get_indexed_mut(state, obj, idx_val, rhs.position(), op_pos, true)?;
indexed_val.set_value(new_val.unwrap(), rhs.position())?; indexed_val.set_value(new_val.unwrap(), rhs.position())?;
Ok((Default::default(), true)) Ok((Default::default(), true))
} }
// xxx[rhs] // xxx[rhs]
_ => self _ => self
.get_indexed_mut(state, obj, idx_val, rhs.position(), op_pos, false) .get_indexed_mut(state, obj, idx_val, rhs.position(), op_pos, false)
.map(|v| (v.clone_into_dynamic(), false)) .map(|v| (v.clone_into_dynamic(), false)),
} }
} else { } else {
match rhs { match rhs {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(fn_name, None, hash, _, def_val, pos) => { Expr::FnCall(x) if x.1.is_none() => {
let mut args: Vec<_> = once(obj) let mut args: Vec<_> = once(obj)
.chain(idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap().iter_mut()) .chain(
idx_val
.downcast_mut::<StaticVec<Dynamic>>()
.unwrap()
.iter_mut(),
)
.collect(); .collect();
let def_val = def_val.as_deref(); let def_val = x.4.as_deref();
// A function call is assumed to have side effects, so the value is changed // A function call is assumed to have side effects, so the value is changed
// TODO - Remove assumption of side effects by checking whether the first parameter is &mut // TODO - Remove assumption of side effects by checking whether the first parameter is &mut
self.exec_fn_call(state, fn_name, *hash, &mut args, def_val, *pos, 0).map(|v| (v, true)) self.exec_fn_call(state, &x.0, x.2, &mut args, def_val, x.5, 0)
.map(|v| (v, true))
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_, _, _, _, _, _) => unreachable!(), Expr::FnCall(_) => unreachable!(),
// {xxx:map}.id = ??? // {xxx:map}.id = ???
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => { Expr::Property(x) if obj.is::<Map>() && new_val.is_some() => {
let index = x.0.clone().into();
let mut indexed_val = let mut indexed_val =
self.get_indexed_mut(state, obj, id.to_string().into(), *pos, op_pos, true)?; self.get_indexed_mut(state, obj, index, x.1, op_pos, true)?;
indexed_val.set_value(new_val.unwrap(), rhs.position())?; indexed_val.set_value(new_val.unwrap(), rhs.position())?;
Ok((Default::default(), true)) Ok((Default::default(), true))
} }
// {xxx:map}.id // {xxx:map}.id
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(id, pos) if obj.is::<Map>() => { Expr::Property(x) if obj.is::<Map>() => {
let index = x.0.clone().into();
let indexed_val = let indexed_val =
self.get_indexed_mut(state, obj, id.to_string().into(), *pos, op_pos, false)?; self.get_indexed_mut(state, obj, index, x.1, op_pos, false)?;
Ok((indexed_val.clone_into_dynamic(), false)) Ok((indexed_val.clone_into_dynamic(), false))
} }
// xxx.id = ??? a // xxx.id = ??? a
Expr::Property(id, pos) if new_val.is_some() => { Expr::Property(x) if new_val.is_some() => {
let fn_name = make_setter(id); let fn_name = make_setter(&x.0);
let mut args = [obj, new_val.as_mut().unwrap()]; let mut args = [obj, new_val.as_mut().unwrap()];
self.exec_fn_call(state, &fn_name, 0, &mut args, None, *pos, 0).map(|v| (v, true)) self.exec_fn_call(state, &fn_name, 0, &mut args, None, x.1, 0)
.map(|v| (v, true))
} }
// xxx.id // xxx.id
Expr::Property(id, pos) => { Expr::Property(x) => {
let fn_name = make_getter(id); let fn_name = make_getter(&x.0);
let mut args = [obj]; let mut args = [obj];
self.exec_fn_call(state, &fn_name, 0, &mut args, None, *pos, 0).map(|v| (v, false)) self.exec_fn_call(state, &fn_name, 0, &mut args, None, x.1, 0)
.map(|v| (v, false))
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
// {xxx:map}.idx_lhs[idx_expr] // {xxx:map}.idx_lhs[idx_expr] | {xxx:map}.dot_lhs.rhs
Expr::Index(dot_lhs, dot_rhs, pos) | Expr::Index(x) | Expr::Dot(x) if obj.is::<Map>() => {
// {xxx:map}.dot_lhs.rhs let is_index = matches!(rhs, Expr::Index(_));
Expr::Dot(dot_lhs, dot_rhs, pos) if obj.is::<Map>() => {
let is_index = matches!(rhs, Expr::Index(_,_,_));
let indexed_val = if let Expr::Property(id, pos) = dot_lhs.as_ref() { let indexed_val = if let Expr::Property(p) = &x.0 {
self.get_indexed_mut(state, obj, id.to_string().into(), *pos, op_pos, false)? let index = p.0.clone().into();
self.get_indexed_mut(state, obj, index, x.2, op_pos, false)?
} else { } else {
// Syntax error // Syntax error
return Err(Box::new(EvalAltResult::ErrorDotExpr( return Err(Box::new(EvalAltResult::ErrorDotExpr(
@ -937,19 +953,24 @@ impl Engine {
))); )));
}; };
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, indexed_val, dot_rhs, idx_values, is_index, *pos, level, new_val state,
indexed_val,
&x.1,
idx_values,
is_index,
x.2,
level,
new_val,
) )
} }
// xxx.idx_lhs[idx_expr] // xxx.idx_lhs[idx_expr] | xxx.dot_lhs.rhs
Expr::Index(dot_lhs, dot_rhs, pos) | Expr::Index(x) | Expr::Dot(x) => {
// xxx.dot_lhs.rhs let is_index = matches!(rhs, Expr::Index(_));
Expr::Dot(dot_lhs, dot_rhs, pos) => {
let is_index = matches!(rhs, Expr::Index(_,_,_));
let mut args = [obj, &mut Default::default()]; let mut args = [obj, &mut Default::default()];
let indexed_val = &mut (if let Expr::Property(id, pos) = dot_lhs.as_ref() { let indexed_val = &mut (if let Expr::Property(p) = &x.0 {
let fn_name = make_getter(id); let fn_name = make_getter(&p.0);
self.exec_fn_call(state, &fn_name, 0, &mut args[..1], None, *pos, 0)? self.exec_fn_call(state, &fn_name, 0, &mut args[..1], None, x.2, 0)?
} else { } else {
// Syntax error // Syntax error
return Err(Box::new(EvalAltResult::ErrorDotExpr( return Err(Box::new(EvalAltResult::ErrorDotExpr(
@ -958,20 +979,28 @@ impl Engine {
))); )));
}); });
let (result, may_be_changed) = self.eval_dot_index_chain_helper( let (result, may_be_changed) = self.eval_dot_index_chain_helper(
state, indexed_val.into(), dot_rhs, idx_values, is_index, *pos, level, new_val state,
indexed_val.into(),
&x.1,
idx_values,
is_index,
x.2,
level,
new_val,
)?; )?;
// Feed the value back via a setter just in case it has been updated // Feed the value back via a setter just in case it has been updated
if may_be_changed { if may_be_changed {
if let Expr::Property(id, pos) = dot_lhs.as_ref() { if let Expr::Property(p) = &x.0 {
let fn_name = make_setter(id); let fn_name = make_setter(&p.0);
// Re-use args because the first &mut parameter will not be consumed // Re-use args because the first &mut parameter will not be consumed
args[1] = indexed_val; args[1] = indexed_val;
self.exec_fn_call(state, &fn_name, 0, &mut args, None, *pos, 0).or_else(|err| match *err { self.exec_fn_call(state, &fn_name, 0, &mut args, None, x.2, 0)
// If there is no setter, no need to feed it back because the property is read-only .or_else(|err| match *err {
EvalAltResult::ErrorDotExpr(_,_) => Ok(Default::default()), // If there is no setter, no need to feed it back because the property is read-only
err => Err(Box::new(err)) EvalAltResult::ErrorDotExpr(_, _) => Ok(Default::default()),
})?; err => Err(Box::new(err)),
})?;
} }
} }
@ -1004,18 +1033,18 @@ impl Engine {
match dot_lhs { match dot_lhs {
// id.??? or id[???] // id.??? or id[???]
Expr::Variable(id, modules, hash, index, pos) => { Expr::Variable(x) => {
let index = if state.always_search { None } else { *index }; let index = if state.always_search { None } else { x.3 };
let (target, typ) = let (target, typ) =
search_scope(scope, id, modules.as_ref().map(|m| (m, *hash)), index, *pos)?; search_scope(scope, &x.0, x.1.as_ref().map(|m| (m, x.2)), index, x.4)?;
// Constants cannot be modified // Constants cannot be modified
match typ { match typ {
ScopeEntryType::Module => unreachable!(), ScopeEntryType::Module => unreachable!(),
ScopeEntryType::Constant if new_val.is_some() => { ScopeEntryType::Constant if new_val.is_some() => {
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
id.to_string(), x.0.clone(),
*pos, x.4,
))); )));
} }
ScopeEntryType::Constant | ScopeEntryType::Normal => (), ScopeEntryType::Constant | ScopeEntryType::Normal => (),
@ -1060,26 +1089,26 @@ impl Engine {
level: usize, level: usize,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
match expr { match expr {
Expr::FnCall(_, None, _, arg_exprs, _, _) => { Expr::FnCall(x) if x.1.is_none() => {
let mut arg_values = StaticVec::<Dynamic>::new(); let mut arg_values = StaticVec::<Dynamic>::new();
for arg_expr in arg_exprs.iter() { for arg_expr in x.3.iter() {
arg_values.push(self.eval_expr(scope, state, arg_expr, level)?); arg_values.push(self.eval_expr(scope, state, arg_expr, level)?);
} }
idx_values.push(Dynamic::from(arg_values)); idx_values.push(Dynamic::from(arg_values));
} }
Expr::FnCall(_, _, _, _, _, _) => unreachable!(), Expr::FnCall(_) => unreachable!(),
Expr::Property(_, _) => idx_values.push(()), // Store a placeholder - no need to copy the property name Expr::Property(_) => idx_values.push(()), // Store a placeholder - no need to copy the property name
Expr::Index(lhs, rhs, _) | Expr::Dot(lhs, rhs, _) => { Expr::Index(x) | Expr::Dot(x) => {
// Evaluate in left-to-right order // Evaluate in left-to-right order
let lhs_val = match lhs.as_ref() { let lhs_val = match x.0 {
Expr::Property(_, _) => Default::default(), // Store a placeholder in case of a property Expr::Property(_) => Default::default(), // Store a placeholder in case of a property
_ => self.eval_expr(scope, state, lhs, level)?, _ => self.eval_expr(scope, state, &x.0, level)?,
}; };
// Push in reverse order // Push in reverse order
self.eval_indexed_chain(scope, state, rhs, idx_values, size, level)?; self.eval_indexed_chain(scope, state, &x.1, idx_values, size, level)?;
idx_values.push(lhs_val); idx_values.push(lhs_val);
} }
@ -1255,35 +1284,36 @@ impl Engine {
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
match expr { match expr {
Expr::IntegerConstant(i, _) => Ok((*i).into()), Expr::IntegerConstant(x) => Ok(x.0.into()),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Expr::FloatConstant(f, _) => Ok((*f).into()), Expr::FloatConstant(x) => Ok(x.0.into()),
Expr::StringConstant(s, _) => Ok(s.to_string().into()), Expr::StringConstant(x) => Ok(x.0.to_string().into()),
Expr::CharConstant(c, _) => Ok((*c).into()), Expr::CharConstant(x) => Ok(x.0.into()),
Expr::Variable(id, modules, hash, index, pos) => { Expr::Variable(x) => {
let index = if state.always_search { None } else { *index }; let index = if state.always_search { None } else { x.3 };
let mod_and_hash = modules.as_ref().map(|m| (m, *hash)); let mod_and_hash = x.1.as_ref().map(|m| (m, x.2));
let (val, _) = search_scope(scope, id, mod_and_hash, index, *pos)?; let (val, _) = search_scope(scope, &x.0, mod_and_hash, index, x.4)?;
Ok(val.clone()) Ok(val.clone())
} }
Expr::Property(_, _) => unreachable!(), Expr::Property(_) => unreachable!(),
// Statement block // Statement block
Expr::Stmt(stmt, _) => self.eval_stmt(scope, state, stmt, level), Expr::Stmt(stmt) => self.eval_stmt(scope, state, &stmt.0, level),
// lhs = rhs // lhs = rhs
Expr::Assignment(lhs, rhs, op_pos) => { Expr::Assignment(x) => {
let rhs_val = self.eval_expr(scope, state, rhs, level)?; let op_pos = x.2;
let rhs_val = self.eval_expr(scope, state, &x.1, level)?;
match lhs.as_ref() { match &x.0 {
// name = rhs // name = rhs
Expr::Variable(id, modules, hash, index, pos) => { Expr::Variable(x) => {
let index = if state.always_search { None } else { *index }; let index = if state.always_search { None } else { x.3 };
let mod_and_hash = modules.as_ref().map(|m| (m, *hash)); let mod_and_hash = x.1.as_ref().map(|m| (m, x.2));
let (value_ptr, typ) = search_scope(scope, id, mod_and_hash, index, *pos)?; let (value_ptr, typ) = search_scope(scope, &x.0, mod_and_hash, index, x.4)?;
match typ { match typ {
ScopeEntryType::Constant => Err(Box::new( ScopeEntryType::Constant => Err(Box::new(
EvalAltResult::ErrorAssignmentToConstant(id.to_string(), *pos), EvalAltResult::ErrorAssignmentToConstant(x.0.clone(), x.4),
)), )),
ScopeEntryType::Normal => { ScopeEntryType::Normal => {
*value_ptr = rhs_val; *value_ptr = rhs_val;
@ -1295,58 +1325,56 @@ 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(x) => {
let new_val = Some(rhs_val); let new_val = Some(rhs_val);
self.eval_dot_index_chain( self.eval_dot_index_chain(
scope, state, idx_lhs, idx_expr, true, *op_pos, level, new_val, scope, state, &x.0, &x.1, true, x.2, level, new_val,
) )
} }
// dot_lhs.dot_rhs = rhs // dot_lhs.dot_rhs = rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(dot_lhs, dot_rhs, _) => { Expr::Dot(x) => {
let new_val = Some(rhs_val); let new_val = Some(rhs_val);
self.eval_dot_index_chain( self.eval_dot_index_chain(
scope, state, dot_lhs, dot_rhs, false, *op_pos, level, new_val, scope, state, &x.0, &x.1, false, op_pos, level, new_val,
) )
} }
// Error assignment to constant // Error assignment to constant
expr if expr.is_constant() => { expr if expr.is_constant() => {
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
expr.get_constant_str(), expr.get_constant_str(),
lhs.position(), expr.position(),
))) )))
} }
// Syntax error // Syntax error
_ => Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS( expr => Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS(
lhs.position(), expr.position(),
))), ))),
} }
} }
// lhs[idx_expr] // lhs[idx_expr]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(lhs, idx_expr, op_pos) => { Expr::Index(x) => {
self.eval_dot_index_chain(scope, state, lhs, idx_expr, true, *op_pos, level, None) self.eval_dot_index_chain(scope, state, &x.0, &x.1, true, x.2, level, None)
} }
// lhs.dot_rhs // lhs.dot_rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(lhs, dot_rhs, op_pos) => { Expr::Dot(x) => {
self.eval_dot_index_chain(scope, state, lhs, dot_rhs, false, *op_pos, level, None) self.eval_dot_index_chain(scope, state, &x.0, &x.1, false, x.2, level, None)
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(contents, _) => Ok(Dynamic(Union::Array(Box::new( Expr::Array(x) => Ok(Dynamic(Union::Array(Box::new(
contents x.0.iter()
.iter()
.map(|item| self.eval_expr(scope, state, item, level)) .map(|item| self.eval_expr(scope, state, item, level))
.collect::<Result<Vec<_>, _>>()?, .collect::<Result<Vec<_>, _>>()?,
)))), )))),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(contents, _) => Ok(Dynamic(Union::Map(Box::new( Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
contents x.0.iter()
.iter()
.map(|(key, expr, _)| { .map(|(key, expr, _)| {
self.eval_expr(scope, state, expr, level) self.eval_expr(scope, state, expr, level)
.map(|val| (key.clone(), val)) .map(|val| (key.clone(), val))
@ -1355,27 +1383,26 @@ impl Engine {
)))), )))),
// Normal function call // Normal function call
Expr::FnCall(fn_name, None, hash, arg_exprs, def_val, pos) => { Expr::FnCall(x) if x.1.is_none() => {
let mut arg_values = arg_exprs let mut arg_values =
.iter() x.3.iter()
.map(|expr| self.eval_expr(scope, state, expr, level)) .map(|expr| self.eval_expr(scope, state, expr, level))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let mut args: Vec<_> = arg_values.iter_mut().collect(); let mut args: Vec<_> = arg_values.iter_mut().collect();
let hash_fn_spec = let hash_fn_spec =
calc_fn_hash(empty(), KEYWORD_EVAL, once(TypeId::of::<String>())); calc_fn_hash(empty(), KEYWORD_EVAL, once(TypeId::of::<String>()));
if fn_name.as_ref() == KEYWORD_EVAL if x.0 == KEYWORD_EVAL
&& args.len() == 1 && args.len() == 1
&& !self.has_override(state, hash_fn_spec, *hash) && !self.has_override(state, hash_fn_spec, x.2)
{ {
// eval - only in function call style // eval - only in function call style
let prev_len = scope.len(); let prev_len = scope.len();
// Evaluate the text string as a script // Evaluate the text string as a script
let result = let result = self.eval_script_expr(scope, state, args[0], x.3[0].position());
self.eval_script_expr(scope, state, args[0], arg_exprs[0].position());
if scope.len() != prev_len { if scope.len() != prev_len {
// IMPORTANT! If the eval defines new variables in the current scope, // IMPORTANT! If the eval defines new variables in the current scope,
@ -1386,20 +1413,20 @@ impl Engine {
result result
} else { } else {
// Normal function call - except for eval (handled above) // Normal function call - except for eval (handled above)
let def_value = def_val.as_deref(); let def_value = x.4.as_deref();
self.exec_fn_call(state, fn_name, *hash, &mut args, def_value, *pos, level) self.exec_fn_call(state, &x.0, x.2, &mut args, def_value, x.5, level)
} }
} }
// Module-qualified function call // Module-qualified function call
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Expr::FnCall(fn_name, Some(modules), hash1, arg_exprs, def_val, pos) => { Expr::FnCall(x) if x.1.is_some() => {
let modules = modules.as_ref(); let modules = x.1.as_ref().unwrap();
let mut arg_values = arg_exprs let mut arg_values =
.iter() x.3.iter()
.map(|expr| self.eval_expr(scope, state, expr, level)) .map(|expr| self.eval_expr(scope, state, expr, level))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let mut args: Vec<_> = arg_values.iter_mut().collect(); let mut args: Vec<_> = arg_values.iter_mut().collect();
@ -1418,8 +1445,8 @@ impl Engine {
}; };
// First search in script-defined functions (can override built-in) // First search in script-defined functions (can override built-in)
if let Some(fn_def) = module.get_qualified_scripted_fn(*hash1) { if let Some(fn_def) = module.get_qualified_scripted_fn(x.2) {
self.call_script_fn(None, state, fn_def, &mut args, *pos, level) self.call_script_fn(None, state, fn_def, &mut args, x.5, level)
} else { } else {
// Then search in Rust functions // Then search in Rust functions
@ -1430,47 +1457,45 @@ impl Engine {
// the actual list of parameter `TypeId`'.s // the actual list of parameter `TypeId`'.s
let hash2 = calc_fn_hash(empty(), "", args.iter().map(|a| a.type_id())); let hash2 = calc_fn_hash(empty(), "", args.iter().map(|a| a.type_id()));
// 3) The final hash is the XOR of the two hashes. // 3) The final hash is the XOR of the two hashes.
let hash = *hash1 ^ hash2; let hash = x.2 ^ hash2;
match module.get_qualified_fn(fn_name, hash, *pos) { match module.get_qualified_fn(&x.0, hash, x.5) {
Ok(func) => func(&mut args, *pos), Ok(func) => func(&mut args, x.5),
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()), Err(_) if x.4.is_some() => Ok(x.4.as_deref().unwrap().clone()),
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
} }
Expr::In(lhs, rhs, _) => { Expr::In(x) => self.eval_in_expr(scope, state, &x.0, &x.1, level),
self.eval_in_expr(scope, state, lhs.as_ref(), rhs.as_ref(), level)
}
Expr::And(lhs, rhs, _) => Ok((self Expr::And(x) => Ok((self
.eval_expr(scope, state, lhs.as_ref(), level)? .eval_expr(scope, state, &x.0, level)?
.as_bool() .as_bool()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position()) EvalAltResult::ErrorBooleanArgMismatch("AND".into(), x.0.position())
})? })?
&& // Short-circuit using && && // Short-circuit using &&
self self
.eval_expr(scope, state, rhs.as_ref(), level)? .eval_expr(scope, state, &x.1, level)?
.as_bool() .as_bool()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position()) EvalAltResult::ErrorBooleanArgMismatch("AND".into(), x.1.position())
})?) })?)
.into()), .into()),
Expr::Or(lhs, rhs, _) => Ok((self Expr::Or(x) => Ok((self
.eval_expr(scope, state, lhs.as_ref(), level)? .eval_expr(scope, state, &x.0, level)?
.as_bool() .as_bool()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position()) EvalAltResult::ErrorBooleanArgMismatch("OR".into(), x.0.position())
})? })?
|| // Short-circuit using || || // Short-circuit using ||
self self
.eval_expr(scope, state, rhs.as_ref(), level)? .eval_expr(scope, state, &x.1, level)?
.as_bool() .as_bool()
.map_err(|_| { .map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position()) EvalAltResult::ErrorBooleanArgMismatch("OR".into(), x.1.position())
})?) })?)
.into()), .into()),
@ -1498,7 +1523,7 @@ impl Engine {
Stmt::Expr(expr) => { Stmt::Expr(expr) => {
let result = self.eval_expr(scope, state, expr, level)?; let result = self.eval_expr(scope, state, expr, level)?;
Ok(if let Expr::Assignment(_, _, _) = *expr.as_ref() { Ok(if let Expr::Assignment(_) = *expr.as_ref() {
// If it is an assignment, erase the result at the root // If it is an assignment, erase the result at the root
Default::default() Default::default()
} else { } else {
@ -1507,10 +1532,10 @@ impl Engine {
} }
// Block scope // Block scope
Stmt::Block(block, _) => { Stmt::Block(x) => {
let prev_len = scope.len(); let prev_len = scope.len();
let result = block.iter().try_fold(Default::default(), |_, stmt| { let result = x.0.iter().try_fold(Default::default(), |_, stmt| {
self.eval_stmt(scope, state, stmt, level) self.eval_stmt(scope, state, stmt, level)
}); });
@ -1524,24 +1549,24 @@ impl Engine {
} }
// If-else statement // If-else statement
Stmt::IfThenElse(guard, if_body, else_body) => self Stmt::IfThenElse(x) => self
.eval_expr(scope, state, guard, level)? .eval_expr(scope, state, &x.0, level)?
.as_bool() .as_bool()
.map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(guard.position()))) .map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(x.0.position())))
.and_then(|guard_val| { .and_then(|guard_val| {
if guard_val { if guard_val {
self.eval_stmt(scope, state, if_body, level) self.eval_stmt(scope, state, &x.1, level)
} else if let Some(stmt) = else_body { } else if let Some(stmt) = &x.2 {
self.eval_stmt(scope, state, stmt.as_ref(), level) self.eval_stmt(scope, state, stmt, level)
} else { } else {
Ok(Default::default()) Ok(Default::default())
} }
}), }),
// While loop // While loop
Stmt::While(guard, body) => loop { Stmt::While(x) => loop {
match self.eval_expr(scope, state, guard, level)?.as_bool() { match self.eval_expr(scope, state, &x.0, level)?.as_bool() {
Ok(true) => match self.eval_stmt(scope, state, body, level) { Ok(true) => match self.eval_stmt(scope, state, &x.1, level) {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (), EvalAltResult::ErrorLoopBreak(false, _) => (),
@ -1550,9 +1575,7 @@ impl Engine {
}, },
}, },
Ok(false) => return Ok(Default::default()), Ok(false) => return Ok(Default::default()),
Err(_) => { Err(_) => return Err(Box::new(EvalAltResult::ErrorLogicGuard(x.0.position()))),
return Err(Box::new(EvalAltResult::ErrorLogicGuard(guard.position())))
}
} }
}, },
@ -1569,9 +1592,9 @@ impl Engine {
}, },
// For loop // For loop
Stmt::For(name, expr, body) => { Stmt::For(x) => {
let arr = self.eval_expr(scope, state, expr, level)?; let iter_type = self.eval_expr(scope, state, &x.1, level)?;
let tid = arr.type_id(); let tid = iter_type.type_id();
if let Some(iter_fn) = self if let Some(iter_fn) = self
.base_package .base_package
@ -1579,14 +1602,14 @@ impl Engine {
.or_else(|| self.packages.get_iterator(tid)) .or_else(|| self.packages.get_iterator(tid))
{ {
// Add the loop variable // Add the loop variable
let var_name = name.as_ref().clone(); let name = x.0.clone();
scope.push(var_name, ()); scope.push(name, ());
let index = scope.len() - 1; let index = scope.len() - 1;
for a in iter_fn(arr) { for a in iter_fn(iter_type) {
*scope.get_mut(index).0 = a; *scope.get_mut(index).0 = a;
match self.eval_stmt(scope, state, body, level) { match self.eval_stmt(scope, state, &x.2, level) {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (), EvalAltResult::ErrorLoopBreak(false, _) => (),
@ -1599,7 +1622,7 @@ impl Engine {
scope.rewind(scope.len() - 1); scope.rewind(scope.len() - 1);
Ok(Default::default()) Ok(Default::default())
} else { } else {
Err(Box::new(EvalAltResult::ErrorFor(expr.position()))) Err(Box::new(EvalAltResult::ErrorFor(x.1.position())))
} }
} }
@ -1609,67 +1632,74 @@ impl Engine {
// Break statement // Break statement
Stmt::Break(pos) => Err(Box::new(EvalAltResult::ErrorLoopBreak(true, *pos))), Stmt::Break(pos) => Err(Box::new(EvalAltResult::ErrorLoopBreak(true, *pos))),
// Empty return
Stmt::ReturnWithVal(None, ReturnType::Return, pos) => {
Err(Box::new(EvalAltResult::Return(Default::default(), *pos)))
}
// Return value // Return value
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(Box::new( Stmt::ReturnWithVal(x) if x.0.is_some() && x.1 == ReturnType::Return => {
EvalAltResult::Return(self.eval_expr(scope, state, a, level)?, *pos), Err(Box::new(EvalAltResult::Return(
)), self.eval_expr(scope, state, x.0.as_ref().unwrap(), level)?,
x.2,
// Empty throw
Stmt::ReturnWithVal(None, ReturnType::Exception, pos) => {
Err(Box::new(EvalAltResult::ErrorRuntime("".into(), *pos)))
}
// Throw value
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => {
let val = self.eval_expr(scope, state, a, level)?;
Err(Box::new(EvalAltResult::ErrorRuntime(
val.take_string().unwrap_or_else(|_| "".to_string()),
*pos,
))) )))
} }
// Empty return
Stmt::ReturnWithVal(x) if x.1 == ReturnType::Return => {
Err(Box::new(EvalAltResult::Return(Default::default(), x.2)))
}
// Throw value
Stmt::ReturnWithVal(x) if x.0.is_some() && x.1 == ReturnType::Exception => {
let val = self.eval_expr(scope, state, x.0.as_ref().unwrap(), level)?;
Err(Box::new(EvalAltResult::ErrorRuntime(
val.take_string().unwrap_or_else(|_| "".to_string()),
x.2,
)))
}
// Empty throw
Stmt::ReturnWithVal(x) if x.1 == ReturnType::Exception => {
Err(Box::new(EvalAltResult::ErrorRuntime("".into(), x.2)))
}
Stmt::ReturnWithVal(_) => unreachable!(),
// Let statement // Let statement
Stmt::Let(name, Some(expr), _) => { Stmt::Let(x) if x.1.is_some() => {
let val = self.eval_expr(scope, state, expr, level)?; let val = self.eval_expr(scope, state, x.1.as_ref().unwrap(), level)?;
// TODO - avoid copying variable name in inner block? // TODO - avoid copying variable name in inner block?
let var_name = name.as_ref().clone(); let var_name = x.0.clone();
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false); scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
Ok(Default::default()) Ok(Default::default())
} }
Stmt::Let(name, None, _) => { Stmt::Let(x) => {
// TODO - avoid copying variable name in inner block? // TODO - avoid copying variable name in inner block?
let var_name = name.as_ref().clone(); let var_name = x.0.clone();
scope.push(var_name, ()); scope.push(var_name, ());
Ok(Default::default()) Ok(Default::default())
} }
// Const statement // Const statement
Stmt::Const(name, expr, _) if expr.is_constant() => { Stmt::Const(x) if x.1.is_constant() => {
let val = self.eval_expr(scope, state, expr, level)?; let val = self.eval_expr(scope, state, &x.1, level)?;
// TODO - avoid copying variable name in inner block? // TODO - avoid copying variable name in inner block?
let var_name = name.as_ref().clone(); let var_name = x.0.clone();
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true); scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
Ok(Default::default()) Ok(Default::default())
} }
// Const expression not constant // Const expression not constant
Stmt::Const(_, _, _) => unreachable!(), Stmt::Const(_) => unreachable!(),
// Import statement // Import statement
Stmt::Import(expr, name, _) => { Stmt::Import(x) => {
let (expr, name, _) = x.as_ref();
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
unreachable!(); unreachable!();
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
if let Some(path) = self if let Some(path) = self
.eval_expr(scope, state, expr, level)? .eval_expr(scope, state, &expr, level)?
.try_cast::<String>() .try_cast::<String>()
{ {
if let Some(resolver) = self.module_resolver.as_ref() { if let Some(resolver) = self.module_resolver.as_ref() {
@ -1678,7 +1708,7 @@ impl Engine {
resolver.resolve(self, Scope::new(), &path, expr.position())?; resolver.resolve(self, Scope::new(), &path, expr.position())?;
// TODO - avoid copying module name in inner block? // TODO - avoid copying module name in inner block?
let mod_name = name.as_ref().clone(); let mod_name = name.clone();
scope.push_module(mod_name, module); scope.push_module(mod_name, module);
Ok(Default::default()) Ok(Default::default())
} else { } else {
@ -1695,7 +1725,7 @@ impl Engine {
// Export statement // Export statement
Stmt::Export(list) => { Stmt::Export(list) => {
for (id, id_pos, rename) in list { for (id, id_pos, rename) in list.as_ref() {
let mut found = false; let mut found = false;
// Mark scope variables as public // Mark scope variables as public

View File

@ -131,63 +131,63 @@ fn call_fn(
fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -> Stmt { fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -> Stmt {
match stmt { match stmt {
// if expr { Noop } // if expr { Noop }
Stmt::IfThenElse(expr, if_block, None) if matches!(*if_block, Stmt::Noop(_)) => { Stmt::IfThenElse(x) if matches!(x.1, Stmt::Noop(_)) => {
state.set_dirty(); state.set_dirty();
let pos = expr.position(); let pos = x.0.position();
let expr = optimize_expr(*expr, state); let expr = optimize_expr(x.0, state);
if preserve_result { if preserve_result {
// -> { expr, Noop } // -> { expr, Noop }
Stmt::Block(vec![Stmt::Expr(Box::new(expr)), *if_block], pos) Stmt::Block(Box::new((vec![Stmt::Expr(Box::new(expr)), x.1], pos)))
} else { } else {
// -> expr // -> expr
Stmt::Expr(Box::new(expr)) Stmt::Expr(Box::new(expr))
} }
} }
// if expr { if_block } // if expr { if_block }
Stmt::IfThenElse(expr, if_block, None) => match *expr { Stmt::IfThenElse(x) if x.2.is_none() => match x.0 {
// if false { if_block } -> Noop // if false { if_block } -> Noop
Expr::False(pos) => { Expr::False(pos) => {
state.set_dirty(); state.set_dirty();
Stmt::Noop(pos) Stmt::Noop(pos)
} }
// if true { if_block } -> if_block // if true { if_block } -> if_block
Expr::True(_) => optimize_stmt(*if_block, state, true), Expr::True(_) => optimize_stmt(x.1, state, true),
// if expr { if_block } // if expr { if_block }
expr => Stmt::IfThenElse( expr => Stmt::IfThenElse(Box::new((
Box::new(optimize_expr(expr, state)), optimize_expr(expr, state),
Box::new(optimize_stmt(*if_block, state, true)), optimize_stmt(x.1, state, true),
None, None,
), ))),
}, },
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
Stmt::IfThenElse(expr, if_block, Some(else_block)) => match *expr { Stmt::IfThenElse(x) if x.2.is_some() => match x.0 {
// if false { if_block } else { else_block } -> else_block // if false { if_block } else { else_block } -> else_block
Expr::False(_) => optimize_stmt(*else_block, state, true), Expr::False(_) => optimize_stmt(x.2.unwrap(), state, true),
// if true { if_block } else { else_block } -> if_block // if true { if_block } else { else_block } -> if_block
Expr::True(_) => optimize_stmt(*if_block, state, true), Expr::True(_) => optimize_stmt(x.1, state, true),
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
expr => Stmt::IfThenElse( expr => Stmt::IfThenElse(Box::new((
Box::new(optimize_expr(expr, state)), optimize_expr(expr, state),
Box::new(optimize_stmt(*if_block, state, true)), optimize_stmt(x.1, state, true),
match optimize_stmt(*else_block, state, true) { match optimize_stmt(x.2.unwrap(), state, true) {
Stmt::Noop(_) => None, // Noop -> no else block Stmt::Noop(_) => None, // Noop -> no else block
stmt => Some(Box::new(stmt)), stmt => Some(stmt),
}, },
), ))),
}, },
// while expr { block } // while expr { block }
Stmt::While(expr, block) => match *expr { Stmt::While(x) => match x.0 {
// while false { block } -> Noop // while false { block } -> Noop
Expr::False(pos) => { Expr::False(pos) => {
state.set_dirty(); state.set_dirty();
Stmt::Noop(pos) Stmt::Noop(pos)
} }
// while true { block } -> loop { block } // while true { block } -> loop { block }
Expr::True(_) => Stmt::Loop(Box::new(optimize_stmt(*block, state, false))), Expr::True(_) => Stmt::Loop(Box::new(optimize_stmt(x.1, state, false))),
// while expr { block } // while expr { block }
expr => match optimize_stmt(*block, state, false) { expr => match optimize_stmt(x.1, state, false) {
// while expr { break; } -> { expr; } // while expr { break; } -> { expr; }
Stmt::Break(pos) => { Stmt::Break(pos) => {
// Only a single break statement - turn into running the guard expression once // Only a single break statement - turn into running the guard expression once
@ -196,10 +196,10 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
if preserve_result { if preserve_result {
statements.push(Stmt::Noop(pos)) statements.push(Stmt::Noop(pos))
} }
Stmt::Block(statements, pos) Stmt::Block(Box::new((statements, pos)))
} }
// while expr { block } // while expr { block }
stmt => Stmt::While(Box::new(optimize_expr(expr, state)), Box::new(stmt)), stmt => Stmt::While(Box::new((optimize_expr(expr, state), stmt))),
}, },
}, },
// loop { block } // loop { block }
@ -214,38 +214,41 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
stmt => Stmt::Loop(Box::new(stmt)), stmt => Stmt::Loop(Box::new(stmt)),
}, },
// for id in expr { block } // for id in expr { block }
Stmt::For(id, expr, block) => Stmt::For( Stmt::For(x) => Stmt::For(Box::new((
id, x.0,
Box::new(optimize_expr(*expr, state)), optimize_expr(x.1, state),
Box::new(optimize_stmt(*block, state, false)), optimize_stmt(x.2, state, false),
), ))),
// let id = expr; // let id = expr;
Stmt::Let(id, Some(expr), pos) => { Stmt::Let(x) if x.1.is_some() => Stmt::Let(Box::new((
Stmt::Let(id, Some(Box::new(optimize_expr(*expr, state))), pos) x.0,
} Some(optimize_expr(x.1.unwrap(), state)),
x.2,
))),
// let id; // let id;
Stmt::Let(_, None, _) => stmt, stmt @ Stmt::Let(_) => stmt,
// import expr as id; // import expr as id;
Stmt::Import(expr, id, pos) => Stmt::Import(Box::new(optimize_expr(*expr, state)), id, pos), Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1, x.2))),
// { block } // { block }
Stmt::Block(block, pos) => { Stmt::Block(x) => {
let orig_len = block.len(); // Original number of statements in the block, for change detection let orig_len = x.0.len(); // Original number of statements in the block, for change detection
let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later
let pos = x.1;
// Optimize each statement in the block // Optimize each statement in the block
let mut result: Vec<_> = block let mut result: Vec<_> =
.into_iter() x.0.into_iter()
.map(|stmt| match stmt { .map(|stmt| match stmt {
// Add constant into the state // Add constant into the state
Stmt::Const(name, value, pos) => { Stmt::Const(v) => {
state.push_constant(&name, *value); state.push_constant(&v.0, v.1);
state.set_dirty(); state.set_dirty();
Stmt::Noop(pos) // No need to keep constants Stmt::Noop(v.2) // No need to keep constants
} }
// Optimize the statement // Optimize the statement
_ => optimize_stmt(stmt, state, preserve_result), _ => optimize_stmt(stmt, state, preserve_result),
}) })
.collect(); .collect();
// Remove all raw expression statements that are pure except for the very last statement // Remove all raw expression statements that are pure except for the very last statement
let last_stmt = if preserve_result { result.pop() } else { None }; let last_stmt = if preserve_result { result.pop() } else { None };
@ -263,9 +266,9 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
while let Some(expr) = result.pop() { while let Some(expr) = result.pop() {
match expr { match expr {
Stmt::Let(_, None, _) => removed = true, Stmt::Let(x) if x.1.is_none() => removed = true,
Stmt::Let(_, Some(val_expr), _) => removed = val_expr.is_pure(), Stmt::Let(x) if x.1.is_some() => removed = x.1.unwrap().is_pure(),
Stmt::Import(expr, _, _) => removed = expr.is_pure(), Stmt::Import(x) => removed = x.0.is_pure(),
_ => { _ => {
result.push(expr); result.push(expr);
break; break;
@ -297,7 +300,7 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
} }
match stmt { match stmt {
Stmt::ReturnWithVal(_, _, _) | Stmt::Break(_) => { Stmt::ReturnWithVal(_) | Stmt::Break(_) => {
dead_code = true; dead_code = true;
} }
_ => (), _ => (),
@ -321,21 +324,23 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
Stmt::Noop(pos) Stmt::Noop(pos)
} }
// Only one let/import statement - leave it alone // Only one let/import statement - leave it alone
[Stmt::Let(_, _, _)] | [Stmt::Import(_, _, _)] => Stmt::Block(result, pos), [Stmt::Let(_)] | [Stmt::Import(_)] => Stmt::Block(Box::new((result, pos))),
// Only one statement - promote // Only one statement - promote
[_] => { [_] => {
state.set_dirty(); state.set_dirty();
result.remove(0) result.remove(0)
} }
_ => Stmt::Block(result, pos), _ => Stmt::Block(Box::new((result, pos))),
} }
} }
// expr; // expr;
Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))), Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))),
// return expr; // return expr;
Stmt::ReturnWithVal(Some(expr), is_return, pos) => { Stmt::ReturnWithVal(x) if x.0.is_some() => Stmt::ReturnWithVal(Box::new((
Stmt::ReturnWithVal(Some(Box::new(optimize_expr(*expr, state))), is_return, pos) Some(optimize_expr(x.0.unwrap(), state)),
} x.1,
x.2,
))),
// All other statements - skip // All other statements - skip
stmt => stmt, stmt => stmt,
} }
@ -348,11 +353,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
match expr { match expr {
// ( stmt ) // ( stmt )
Expr::Stmt(stmt, pos) => match optimize_stmt(*stmt, state, true) { Expr::Stmt(x) => match optimize_stmt(x.0, state, true) {
// ( Noop ) -> () // ( Noop ) -> ()
Stmt::Noop(_) => { Stmt::Noop(_) => {
state.set_dirty(); state.set_dirty();
Expr::Unit(pos) Expr::Unit(x.1)
} }
// ( expr ) -> expr // ( expr ) -> expr
Stmt::Expr(expr) => { Stmt::Expr(expr) => {
@ -360,151 +365,128 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
*expr *expr
} }
// ( stmt ) // ( stmt )
stmt => Expr::Stmt(Box::new(stmt), pos), stmt => Expr::Stmt(Box::new((stmt, x.1))),
}, },
// id = expr // id = expr
Expr::Assignment(id, expr, pos) => match *expr { Expr::Assignment(x) => match x.1 {
//id = id2 = expr2 //id = id2 = expr2
Expr::Assignment(id2, expr2, pos2) => match (*id, *id2) { Expr::Assignment(x2) => match (x.0, x2.0) {
// var = var = expr2 -> var = expr2 // var = var = expr2 -> var = expr2
(Expr::Variable(var, None, index, sp, _), Expr::Variable(var2, None, _, sp2, _)) (Expr::Variable(a), Expr::Variable(b))
if var == var2 && sp == sp2 => if a.1.is_none() && b.1.is_none() && a.0 == b.0 && a.3 == b.3 =>
{ {
// Assignment to the same variable - fold // Assignment to the same variable - fold
state.set_dirty(); state.set_dirty();
Expr::Assignment(Box::new((Expr::Variable(a), optimize_expr(x2.1, state), x.2)))
Expr::Assignment(
Box::new(Expr::Variable(var, None, index, sp, pos)),
Box::new(optimize_expr(*expr2, state))
, pos
)
} }
// id1 = id2 = expr2 // id1 = id2 = expr2
(id1, id2) => Expr::Assignment( (id1, id2) => {
Box::new(id1), Expr::Assignment(Box::new((
Box::new(Expr::Assignment(Box::new(id2), Box::new(optimize_expr(*expr2, state)), pos2)), id1, Expr::Assignment(Box::new((id2, optimize_expr(x2.1, state), x2.2))), x.2,
pos, )))
), }
}, },
// id = expr // id = expr
expr => Expr::Assignment(id, Box::new(optimize_expr(expr, state)), pos), expr => Expr::Assignment(Box::new((x.0, optimize_expr(expr, state), x.2))),
}, },
// lhs.rhs // lhs.rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(lhs, rhs, pos) => match (*lhs, *rhs) { Expr::Dot(x) => match (x.0, x.1) {
// map.string // map.string
(Expr::Map(items, pos), Expr::Property(s, _)) if items.iter().all(|(_, x, _)| x.is_pure()) => { (Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x, _)| x.is_pure()) => {
// Map literal where everything is pure - promote the indexed item. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
items.into_iter().find(|(name, _, _)| name == &s) let pos = m.1;
m.0.into_iter().find(|(name, _, _)| name == &p.0)
.map(|(_, expr, _)| expr.set_position(pos)) .map(|(_, expr, _)| expr.set_position(pos))
.unwrap_or_else(|| Expr::Unit(pos)) .unwrap_or_else(|| Expr::Unit(pos))
} }
// lhs.rhs // lhs.rhs
(lhs, rhs) => Expr::Dot( (lhs, rhs) => Expr::Dot(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2)))
Box::new(optimize_expr(lhs, state)),
Box::new(optimize_expr(rhs, state)),
pos,
)
} }
// lhs[rhs] // lhs[rhs]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) { Expr::Index(x) => match (x.0, x.1) {
// array[int] // array[int]
(Expr::Array(mut items, pos), Expr::IntegerConstant(i, _)) (Expr::Array(mut a), Expr::IntegerConstant(i))
if i >= 0 && (i as usize) < items.len() && items.iter().all(Expr::is_pure) => if i.0 >= 0 && (i.0 as usize) < a.0.len() && a.0.iter().all(Expr::is_pure) =>
{ {
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
items.remove(i as usize).set_position(pos) a.0.remove(i.0 as usize).set_position(a.1)
} }
// map[string] // map[string]
(Expr::Map(items, pos), Expr::StringConstant(s, _)) if items.iter().all(|(_, x, _)| x.is_pure()) => { (Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x, _)| x.is_pure()) => {
// Map literal where everything is pure - promote the indexed item. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
items.into_iter().find(|(name, _, _)| name == &s) let pos = m.1;
m.0.into_iter().find(|(name, _, _)| name == &s.0)
.map(|(_, expr, _)| expr.set_position(pos)) .map(|(_, expr, _)| expr.set_position(pos))
.unwrap_or_else(|| Expr::Unit(pos)) .unwrap_or_else(|| Expr::Unit(pos))
} }
// string[int] // string[int]
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if i >= 0 && (i as usize) < s.chars().count() => { (Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.0.chars().count() => {
// String literal indexing - get the character // String literal indexing - get the character
state.set_dirty(); state.set_dirty();
Expr::CharConstant(s.chars().nth(i as usize).expect("should get char"), pos) Expr::CharConstant(Box::new((s.0.chars().nth(i.0 as usize).expect("should get char"), s.1)))
} }
// lhs[rhs] // lhs[rhs]
(lhs, rhs) => Expr::Index( (lhs, rhs) => Expr::Index(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
Box::new(optimize_expr(lhs, state)),
Box::new(optimize_expr(rhs, state)),
pos,
),
}, },
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(items, pos) => Expr::Array(items Expr::Array(a) => Expr::Array(Box::new((a.0
.into_iter() .into_iter()
.map(|expr| optimize_expr(expr, state)) .map(|expr| optimize_expr(expr, state))
.collect(), pos), .collect(), a.1))),
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(items, pos) => Expr::Map(items Expr::Map(m) => Expr::Map(Box::new((m.0
.into_iter() .into_iter()
.map(|(key, expr, pos)| (key, optimize_expr(expr, state), pos)) .map(|(key, expr, pos)| (key, optimize_expr(expr, state), pos))
.collect(), pos), .collect(), m.1))),
// lhs in rhs // lhs in rhs
Expr::In(lhs, rhs, pos) => match (*lhs, *rhs) { Expr::In(x) => match (x.0, x.1) {
// "xxx" in "xxxxx" // "xxx" in "xxxxx"
(Expr::StringConstant(lhs, pos), Expr::StringConstant(rhs, _)) => { (Expr::StringConstant(a), Expr::StringConstant(b)) => {
state.set_dirty(); state.set_dirty();
if rhs.contains(&lhs) { if b.0.contains(&a.0) { Expr::True(a.1) } else { Expr::False(a.1) }
Expr::True(pos)
} else {
Expr::False(pos)
}
} }
// 'x' in "xxxxx" // 'x' in "xxxxx"
(Expr::CharConstant(lhs, pos), Expr::StringConstant(rhs, _)) => { (Expr::CharConstant(a), Expr::StringConstant(b)) => {
state.set_dirty(); state.set_dirty();
if rhs.contains(&lhs.to_string()) { if b.0.contains(a.0) { Expr::True(a.1) } else { Expr::False(a.1) }
Expr::True(pos)
} else {
Expr::False(pos)
}
} }
// "xxx" in #{...} // "xxx" in #{...}
(Expr::StringConstant(lhs, pos), Expr::Map(items, _)) => { (Expr::StringConstant(a), Expr::Map(b)) => {
state.set_dirty(); state.set_dirty();
if items.iter().find(|(name, _, _)| name == &lhs).is_some() { if b.0.iter().find(|(name, _, _)| name == &a.0).is_some() {
Expr::True(pos) Expr::True(a.1)
} else { } else {
Expr::False(pos) Expr::False(a.1)
} }
} }
// 'x' in #{...} // 'x' in #{...}
(Expr::CharConstant(lhs, pos), Expr::Map(items, _)) => { (Expr::CharConstant(a), Expr::Map(b)) => {
state.set_dirty(); state.set_dirty();
let lhs = lhs.to_string(); let ch = a.0.to_string();
if items.iter().find(|(name, _, _)| name == &lhs).is_some() { if b.0.iter().find(|(name, _, _)| name == &ch).is_some() {
Expr::True(pos) Expr::True(a.1)
} else { } else {
Expr::False(pos) Expr::False(a.1)
} }
} }
// lhs in rhs // lhs in rhs
(lhs, rhs) => Expr::In( (lhs, rhs) => Expr::In(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
Box::new(optimize_expr(lhs, state)),
Box::new(optimize_expr(rhs, state)),
pos
),
}, },
// lhs && rhs // lhs && rhs
Expr::And(lhs, rhs, pos) => match (*lhs, *rhs) { Expr::And(x) => match (x.0, x.1) {
// true && rhs -> rhs // true && rhs -> rhs
(Expr::True(_), rhs) => { (Expr::True(_), rhs) => {
state.set_dirty(); state.set_dirty();
@ -521,14 +503,10 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
optimize_expr(lhs, state) optimize_expr(lhs, state)
} }
// lhs && rhs // lhs && rhs
(lhs, rhs) => Expr::And( (lhs, rhs) => Expr::And(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
Box::new(optimize_expr(lhs, state)),
Box::new(optimize_expr(rhs, state)),
pos
),
}, },
// lhs || rhs // lhs || rhs
Expr::Or(lhs, rhs, pos) => match (*lhs, *rhs) { Expr::Or(x) => match (x.0, x.1) {
// false || rhs -> rhs // false || rhs -> rhs
(Expr::False(_), rhs) => { (Expr::False(_), rhs) => {
state.set_dirty(); state.set_dirty();
@ -545,36 +523,40 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
optimize_expr(lhs, state) optimize_expr(lhs, state)
} }
// lhs || rhs // lhs || rhs
(lhs, rhs) => Expr::Or(Box::new(optimize_expr(lhs, state)), Box::new(optimize_expr(rhs, state)), pos), (lhs, rhs) => Expr::Or(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
}, },
// Do not call some special keywords // Do not call some special keywords
Expr::FnCall(id, None, index, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref().as_ref())=> Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&x.0.as_ref().as_ref())=> {
Expr::FnCall(id, None, index, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos), x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x)
}
// Eagerly call functions // Eagerly call functions
Expr::FnCall(id, None, index, args, def_value, pos) Expr::FnCall(mut x)
if state.optimization_level == OptimizationLevel::Full // full optimizations if x.1.is_none() // Non-qualified
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants && state.optimization_level == OptimizationLevel::Full // full optimizations
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
=> { => {
// First search in script-defined functions (can override built-in) // First search in script-defined functions (can override built-in)
if state.fn_lib.iter().find(|(name, len)| name == id.as_ref() && *len == args.len()).is_some() { if state.fn_lib.iter().find(|(name, len)| *name == x.0 && *len == x.3.len()).is_some() {
// A script-defined function overrides the built-in function - do not make the call // A script-defined function overrides the built-in function - do not make the call
return Expr::FnCall(id, None, index, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos); x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
return Expr::FnCall(x);
} }
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect(); let mut arg_values: Vec<_> = x.3.iter().map(Expr::get_constant_value).collect();
let mut call_args: Vec<_> = arg_values.iter_mut().collect(); let mut call_args: Vec<_> = arg_values.iter_mut().collect();
// Save the typename of the first argument if it is `type_of()` // Save the typename of the first argument if it is `type_of()`
// This is to avoid `call_args` being passed into the closure // This is to avoid `call_args` being passed into the closure
let arg_for_type_of = if *id == KEYWORD_TYPE_OF && call_args.len() == 1 { let arg_for_type_of = if x.0 == KEYWORD_TYPE_OF && call_args.len() == 1 {
state.engine.map_type_name(call_args[0].type_name()) state.engine.map_type_name(call_args[0].type_name())
} else { } else {
"" ""
}; };
call_fn(&state.engine.packages, &state.engine.base_package, &id, &mut call_args, pos).ok() call_fn(&state.engine.packages, &state.engine.base_package, &x.0, &mut call_args, x.5).ok()
.and_then(|result| .and_then(|result|
result.or_else(|| { result.or_else(|| {
if !arg_for_type_of.is_empty() { if !arg_for_type_of.is_empty() {
@ -582,29 +564,32 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
Some(arg_for_type_of.to_string().into()) Some(arg_for_type_of.to_string().into())
} else { } else {
// Otherwise use the default value, if any // Otherwise use the default value, if any
def_value.clone().map(|v| *v) x.4.clone().map(|v| *v)
} }
}).and_then(|result| map_dynamic_to_expr(result, pos)) }).and_then(|result| map_dynamic_to_expr(result, x.5))
.map(|expr| { .map(|expr| {
state.set_dirty(); state.set_dirty();
expr expr
}) })
).unwrap_or_else(|| ).unwrap_or_else(|| {
// Optimize function call arguments // Optimize function call arguments
Expr::FnCall(id, None, index, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos) x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
) Expr::FnCall(x)
})
} }
// id(args ..) -> optimize function call arguments // id(args ..) -> optimize function call arguments
Expr::FnCall(id, modules, index, args, def_value, pos) => Expr::FnCall(mut x) => {
Expr::FnCall(id, modules, index, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos), x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x)
}
// constant-name // constant-name
Expr::Variable(name, None, _, _, pos) if state.contains_constant(&name) => { Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.0) => {
state.set_dirty(); state.set_dirty();
// Replace constant with value // Replace constant with value
state.find_constant(&name).expect("should find constant in scope!").clone().set_position(pos) state.find_constant(&x.0).expect("should find constant in scope!").clone().set_position(x.4)
} }
// All other expressions - skip // All other expressions - skip
@ -657,17 +642,17 @@ fn optimize<'a>(
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(i, stmt)| { .map(|(i, stmt)| {
match stmt { match &stmt {
Stmt::Const(ref name, ref value, _) => { Stmt::Const(x) => {
// Load constants // Load constants
state.push_constant(name.as_ref(), value.as_ref().clone()); state.push_constant(&x.0, x.1.clone());
stmt // Keep it in the global scope stmt // Keep it in the global scope
} }
_ => { _ => {
// Keep all variable declarations at this level // Keep all variable declarations at this level
// and always keep the last return value // and always keep the last return value
let keep = match stmt { let keep = match stmt {
Stmt::Let(_, _, _) | Stmt::Import(_, _, _) => true, Stmt::Let(_) | Stmt::Import(_) => true,
_ => i == num_statements - 1, _ => i == num_statements - 1,
}; };
optimize_stmt(stmt, &mut state, keep) optimize_stmt(stmt, &mut state, keep)
@ -728,19 +713,21 @@ pub fn optimize_into_ast(
// Optimize the function body // Optimize the function body
let mut body = let mut body =
optimize(vec![*fn_def.body], engine, &Scope::new(), &fn_lib, level); optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib, level);
// {} -> Noop // {} -> Noop
fn_def.body = Box::new(match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
// { return val; } -> val // { return val; } -> val
Stmt::ReturnWithVal(Some(val), ReturnType::Return, _) => Stmt::Expr(val), Stmt::ReturnWithVal(x) if x.0.is_some() && x.1 == ReturnType::Return => {
Stmt::Expr(Box::new(x.0.unwrap()))
}
// { return; } -> () // { return; } -> ()
Stmt::ReturnWithVal(None, ReturnType::Return, pos) => { Stmt::ReturnWithVal(x) if x.0.is_none() && x.1 == ReturnType::Return => {
Stmt::Expr(Box::new(Expr::Unit(pos))) Stmt::Expr(Box::new(Expr::Unit(x.2)))
} }
// All others // All others
stmt => stmt, stmt => stmt,
}); };
} }
fn_def fn_def
}) })

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ use crate::token::Position;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
use crate::module::Module; use crate::module::Module;
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec, vec::Vec}; use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec::Vec};
/// Type of an entry in the Scope. /// Type of an entry in the Scope.
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]

View File

@ -206,14 +206,14 @@ pub enum Token {
impl Token { impl Token {
/// Get the syntax of the token. /// Get the syntax of the token.
pub fn syntax(&self) -> Cow<str> { pub fn syntax(&self) -> Cow<'static, str> {
use Token::*; use Token::*;
match self { match self {
IntegerConstant(i) => i.to_string().into(), IntegerConstant(i) => i.to_string().into(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
FloatConstant(f) => f.to_string().into(), FloatConstant(f) => f.to_string().into(),
Identifier(s) => s.into(), Identifier(s) => s.clone().into(),
CharConstant(c) => c.to_string().into(), CharConstant(c) => c.to_string().into(),
LexError(err) => err.to_string().into(), LexError(err) => err.to_string().into(),