Optimize layout.
This commit is contained in:
parent
874b3fc843
commit
728ed81173
50
src/ast.rs
50
src/ast.rs
@ -46,7 +46,7 @@ pub enum FnAccess {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScriptFnDef {
|
||||
/// Function body.
|
||||
pub body: Stmt,
|
||||
pub body: StmtBlock,
|
||||
/// Encapsulated running environment, if any.
|
||||
pub lib: Option<Shared<Module>>,
|
||||
/// Encapsulated imported modules.
|
||||
@ -692,7 +692,7 @@ impl AST {
|
||||
.chain({
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
self.iter_fn_def().map(|f| &f.body)
|
||||
self.iter_fn_def().flat_map(|f| f.body.statements.iter())
|
||||
}
|
||||
#[cfg(feature = "no_function")]
|
||||
{
|
||||
@ -862,8 +862,8 @@ pub enum Stmt {
|
||||
Switch(
|
||||
Expr,
|
||||
Box<(
|
||||
HashableHashMap<u64, Stmt, StraightHasherBuilder>,
|
||||
Option<Stmt>,
|
||||
HashableHashMap<u64, StmtBlock, StraightHasherBuilder>,
|
||||
StmtBlock,
|
||||
)>,
|
||||
Position,
|
||||
),
|
||||
@ -914,6 +914,7 @@ impl Default for Stmt {
|
||||
}
|
||||
|
||||
impl From<Stmt> for StmtBlock {
|
||||
#[inline(always)]
|
||||
fn from(stmt: Stmt) -> Self {
|
||||
match stmt {
|
||||
Stmt::Block(block, pos) => Self {
|
||||
@ -924,7 +925,11 @@ impl From<Stmt> for StmtBlock {
|
||||
statements: Default::default(),
|
||||
pos,
|
||||
},
|
||||
_ => panic!("cannot convert {:?} into a StmtBlock", stmt),
|
||||
_ => {
|
||||
let pos = stmt.position();
|
||||
let statements = vec![stmt].into();
|
||||
Self { statements, pos }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1043,8 +1048,11 @@ impl Stmt {
|
||||
}
|
||||
Self::Switch(expr, x, _) => {
|
||||
expr.is_pure()
|
||||
&& x.0.values().all(Stmt::is_pure)
|
||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
||||
&& x.0
|
||||
.values()
|
||||
.flat_map(|block| block.statements.iter())
|
||||
.all(Stmt::is_pure)
|
||||
&& x.1.statements.iter().all(Stmt::is_pure)
|
||||
}
|
||||
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
||||
condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
|
||||
@ -1083,10 +1091,10 @@ impl Stmt {
|
||||
}
|
||||
Self::Switch(e, x, _) => {
|
||||
e.walk(path, on_node);
|
||||
x.0.values().for_each(|s| s.walk(path, on_node));
|
||||
if let Some(ref s) = x.1 {
|
||||
s.walk(path, on_node);
|
||||
}
|
||||
x.0.values()
|
||||
.flat_map(|block| block.statements.iter())
|
||||
.for_each(|s| s.walk(path, on_node));
|
||||
x.1.statements.iter().for_each(|s| s.walk(path, on_node));
|
||||
}
|
||||
Self::While(e, s, _) | Self::Do(s, e, _, _) => {
|
||||
e.walk(path, on_node);
|
||||
@ -1384,10 +1392,10 @@ pub enum Expr {
|
||||
Unit(Position),
|
||||
/// Variable access - (optional index, optional (hash, modules), variable name)
|
||||
Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>),
|
||||
/// Property access - (getter, hash, setter, hash, prop)
|
||||
Property(Box<(ImmutableString, u64, ImmutableString, u64, Ident)>),
|
||||
/// Property access - ((getter, hash), (setter, hash), prop)
|
||||
Property(Box<((ImmutableString, u64), (ImmutableString, u64), Ident)>),
|
||||
/// { [statement][Stmt] ... }
|
||||
Stmt(Box<StaticVec<Stmt>>, Position),
|
||||
Stmt(Box<StmtBlock>),
|
||||
/// func `(` expr `,` ... `)`
|
||||
FnCall(Box<FnCallExpr>, Position),
|
||||
/// lhs `.` rhs
|
||||
@ -1478,8 +1486,8 @@ impl Expr {
|
||||
Self::FnPointer(_, pos) => *pos,
|
||||
Self::Array(_, pos) => *pos,
|
||||
Self::Map(_, pos) => *pos,
|
||||
Self::Property(x) => (x.4).pos,
|
||||
Self::Stmt(_, pos) => *pos,
|
||||
Self::Property(x) => (x.2).pos,
|
||||
Self::Stmt(x) => x.pos,
|
||||
Self::Variable(x) => (x.2).pos,
|
||||
Self::FnCall(_, pos) => *pos,
|
||||
|
||||
@ -1508,8 +1516,8 @@ impl Expr {
|
||||
Self::Array(_, pos) => *pos = new_pos,
|
||||
Self::Map(_, pos) => *pos = new_pos,
|
||||
Self::Variable(x) => (x.2).pos = new_pos,
|
||||
Self::Property(x) => (x.4).pos = new_pos,
|
||||
Self::Stmt(_, pos) => *pos = new_pos,
|
||||
Self::Property(x) => (x.2).pos = new_pos,
|
||||
Self::Stmt(x) => x.pos = new_pos,
|
||||
Self::FnCall(_, pos) => *pos = new_pos,
|
||||
Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos,
|
||||
Self::Unit(pos) => *pos = new_pos,
|
||||
@ -1533,7 +1541,7 @@ impl Expr {
|
||||
x.lhs.is_pure() && x.rhs.is_pure()
|
||||
}
|
||||
|
||||
Self::Stmt(x, _) => x.iter().all(Stmt::is_pure),
|
||||
Self::Stmt(x) => x.statements.iter().all(Stmt::is_pure),
|
||||
|
||||
Self::Variable(_) => true,
|
||||
|
||||
@ -1596,7 +1604,7 @@ impl Expr {
|
||||
|
||||
Self::StringConstant(_, _)
|
||||
| Self::FnCall(_, _)
|
||||
| Self::Stmt(_, _)
|
||||
| Self::Stmt(_)
|
||||
| Self::Dot(_, _)
|
||||
| Self::Index(_, _)
|
||||
| Self::Array(_, _)
|
||||
@ -1632,7 +1640,7 @@ impl Expr {
|
||||
on_node(path);
|
||||
|
||||
match self {
|
||||
Self::Stmt(x, _) => x.iter().for_each(|s| s.walk(path, on_node)),
|
||||
Self::Stmt(x) => x.statements.iter().for_each(|s| s.walk(path, on_node)),
|
||||
Self::Array(x, _) => x.iter().for_each(|e| e.walk(path, on_node)),
|
||||
Self::Map(x, _) => x.iter().for_each(|(_, e)| e.walk(path, on_node)),
|
||||
Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => {
|
||||
|
133
src/engine.rs
133
src/engine.rs
@ -1181,7 +1181,7 @@ impl Engine {
|
||||
}
|
||||
// {xxx:map}.id op= ???
|
||||
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
||||
let Ident { name, pos, .. } = &x.4;
|
||||
let Ident { name, pos, .. } = &x.2;
|
||||
let index = name.clone().into();
|
||||
let val = self.get_indexed_mut(
|
||||
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
||||
@ -1194,7 +1194,7 @@ impl Engine {
|
||||
}
|
||||
// {xxx:map}.id
|
||||
Expr::Property(x) if target_val.is::<Map>() => {
|
||||
let Ident { name, pos, .. } = &x.4;
|
||||
let Ident { name, pos, .. } = &x.2;
|
||||
let index = name.clone().into();
|
||||
let val = self.get_indexed_mut(
|
||||
mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
|
||||
@ -1204,7 +1204,7 @@ impl Engine {
|
||||
}
|
||||
// xxx.id = ???
|
||||
Expr::Property(x) if new_val.is_some() => {
|
||||
let (_, _, setter, hash_set, Ident { pos, .. }) = x.as_ref();
|
||||
let (_, (setter, hash_set), Ident { pos, .. }) = x.as_ref();
|
||||
let hash = FnHash::from_native(*hash_set);
|
||||
let mut new_val = new_val;
|
||||
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
||||
@ -1216,7 +1216,7 @@ impl Engine {
|
||||
}
|
||||
// xxx.id
|
||||
Expr::Property(x) => {
|
||||
let (getter, hash_get, _, _, Ident { pos, .. }) = x.as_ref();
|
||||
let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref();
|
||||
let hash = FnHash::from_native(*hash_get);
|
||||
let mut args = [target_val];
|
||||
self.exec_fn_call(
|
||||
@ -1229,7 +1229,7 @@ impl Engine {
|
||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::<Map>() => {
|
||||
let mut val = match &x.lhs {
|
||||
Expr::Property(p) => {
|
||||
let Ident { name, pos, .. } = &p.4;
|
||||
let Ident { name, pos, .. } = &p.2;
|
||||
let index = name.clone().into();
|
||||
self.get_indexed_mut(
|
||||
mods, state, lib, target_val, index, *pos, false, is_ref, true,
|
||||
@ -1264,7 +1264,7 @@ impl Engine {
|
||||
match &x.lhs {
|
||||
// xxx.prop[expr] | xxx.prop.expr
|
||||
Expr::Property(p) => {
|
||||
let (getter, hash_get, setter, hash_set, Ident { pos, .. }) =
|
||||
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
||||
p.as_ref();
|
||||
let hash_get = FnHash::from_native(*hash_get);
|
||||
let hash_set = FnHash::from_native(*hash_set);
|
||||
@ -1456,7 +1456,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
Expr::Property(x) if parent_chain_type == ChainType::Dot => {
|
||||
idx_values.push(ChainArgument::Property(x.4.pos))
|
||||
idx_values.push(ChainArgument::Property(x.2.pos))
|
||||
}
|
||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
|
||||
@ -1466,7 +1466,7 @@ impl Engine {
|
||||
// Evaluate in left-to-right order
|
||||
let lhs_val = match lhs {
|
||||
Expr::Property(x) if parent_chain_type == ChainType::Dot => {
|
||||
ChainArgument::Property(x.4.pos)
|
||||
ChainArgument::Property(x.2.pos)
|
||||
}
|
||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
Expr::FnCall(x, _)
|
||||
@ -1655,16 +1655,11 @@ impl Engine {
|
||||
.map(|(val, _)| val.take_or_clone()),
|
||||
|
||||
// Statement block
|
||||
Expr::Stmt(x, _) => self.eval_stmt_block(
|
||||
scope,
|
||||
mods,
|
||||
state,
|
||||
lib,
|
||||
this_ptr,
|
||||
x.as_ref().as_ref(),
|
||||
true,
|
||||
level,
|
||||
),
|
||||
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
||||
Expr::Stmt(x) => {
|
||||
let statements = &x.statements;
|
||||
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level)
|
||||
}
|
||||
|
||||
// lhs[idx_expr]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -1804,6 +1799,10 @@ impl Engine {
|
||||
restore_prev_state: bool,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
if statements.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
let mut _extra_fn_resolution_cache = false;
|
||||
let prev_always_search = state.always_search;
|
||||
let prev_scope_len = scope.len();
|
||||
@ -2025,6 +2024,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Block scope
|
||||
Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT),
|
||||
Stmt::Block(statements, _) => {
|
||||
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level)
|
||||
}
|
||||
@ -2046,15 +2046,21 @@ impl Engine {
|
||||
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))
|
||||
.and_then(|guard_val| {
|
||||
if guard_val {
|
||||
self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, if_stmt, true, level,
|
||||
)
|
||||
} else if !else_stmt.is_empty() {
|
||||
self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, else_stmt, true, level,
|
||||
)
|
||||
if !if_stmt.is_empty() {
|
||||
self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, if_stmt, true, level,
|
||||
)
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
if !else_stmt.is_empty() {
|
||||
self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, else_stmt, true, level,
|
||||
)
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -2070,21 +2076,29 @@ impl Engine {
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
table
|
||||
.get(&hash)
|
||||
.map(|stmt| self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level))
|
||||
table.get(&hash).map(|StmtBlock { statements, .. }| {
|
||||
if !statements.is_empty() {
|
||||
self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, statements, true, level,
|
||||
)
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Non-hashable values never match any specific clause
|
||||
None
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
// Default match clause
|
||||
def_stmt.as_ref().map_or_else(
|
||||
|| Ok(Dynamic::UNIT),
|
||||
|def_stmt| {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
||||
},
|
||||
)
|
||||
let def_stmt = &def_stmt.statements;
|
||||
if !def_stmt.is_empty() {
|
||||
self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, def_stmt, true, level,
|
||||
)
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -2102,20 +2116,22 @@ impl Engine {
|
||||
true
|
||||
};
|
||||
|
||||
if condition {
|
||||
match self
|
||||
.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(err) => match *err {
|
||||
EvalAltResult::LoopBreak(false, _) => (),
|
||||
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||
_ => return Err(err),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
if !condition {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
if body.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(err) => match *err {
|
||||
EvalAltResult::LoopBreak(false, _) => (),
|
||||
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||
_ => return Err(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2124,14 +2140,17 @@ impl Engine {
|
||||
let body = &body.statements;
|
||||
|
||||
loop {
|
||||
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(err) => match *err {
|
||||
EvalAltResult::LoopBreak(false, _) => continue,
|
||||
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||
_ => return Err(err),
|
||||
},
|
||||
if !body.is_empty() {
|
||||
match self
|
||||
.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(err) => match *err {
|
||||
EvalAltResult::LoopBreak(false, _) => continue,
|
||||
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||
_ => return Err(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if self
|
||||
@ -2203,6 +2222,10 @@ impl Engine {
|
||||
|
||||
self.inc_operations(state, *pos)?;
|
||||
|
||||
if statements.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, statements, true, level,
|
||||
) {
|
||||
|
@ -493,6 +493,10 @@ impl Engine {
|
||||
|
||||
self.inc_operations(state, pos)?;
|
||||
|
||||
if fn_def.body.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
// Check for stack overflow
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
@ -539,10 +543,10 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Evaluate the function
|
||||
let stmt = &fn_def.body;
|
||||
let body = &fn_def.body.statements;
|
||||
|
||||
let result = self
|
||||
.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level)
|
||||
.eval_stmt_block(scope, mods, state, unified_lib, this_ptr, body, true, level)
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
EvalAltResult::Return(x, _) => Ok(x),
|
||||
@ -722,6 +726,10 @@ impl Engine {
|
||||
|
||||
let func = func.get_fn_def();
|
||||
|
||||
if func.body.is_empty() {
|
||||
return Ok((Dynamic::UNIT, false));
|
||||
}
|
||||
|
||||
let scope: &mut Scope = &mut Default::default();
|
||||
|
||||
// Move captured variables into scope
|
||||
@ -1391,22 +1399,27 @@ impl Engine {
|
||||
match func {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Some(f) if f.is_script() => {
|
||||
let args = args.as_mut();
|
||||
let new_scope = &mut Default::default();
|
||||
let fn_def = f.get_fn_def().clone();
|
||||
let fn_def = f.get_fn_def();
|
||||
|
||||
let mut source = module.id_raw().cloned();
|
||||
mem::swap(&mut state.source, &mut source);
|
||||
if fn_def.body.is_empty() {
|
||||
Ok(Dynamic::UNIT)
|
||||
} else {
|
||||
let args = args.as_mut();
|
||||
let new_scope = &mut Default::default();
|
||||
|
||||
let level = level + 1;
|
||||
let mut source = module.id_raw().cloned();
|
||||
mem::swap(&mut state.source, &mut source);
|
||||
|
||||
let result = self.call_script_fn(
|
||||
new_scope, mods, state, lib, &mut None, &fn_def, args, pos, level,
|
||||
);
|
||||
let level = level + 1;
|
||||
|
||||
state.source = source;
|
||||
let result = self.call_script_fn(
|
||||
new_scope, mods, state, lib, &mut None, fn_def, args, pos, level,
|
||||
);
|
||||
|
||||
result
|
||||
state.source = source;
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
Some(f) if f.is_plugin_fn() => f
|
||||
|
104
src/optimize.rs
104
src/optimize.rs
@ -1,6 +1,6 @@
|
||||
//! Module implementing the [`AST`] optimizer.
|
||||
|
||||
use crate::ast::{Expr, Ident, Stmt};
|
||||
use crate::ast::{Expr, Ident, Stmt, StmtBlock};
|
||||
use crate::dynamic::AccessMode;
|
||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||
use crate::fn_builtin::get_builtin_binary_op_fn;
|
||||
@ -362,29 +362,54 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||
|
||||
let table = &mut x.0;
|
||||
|
||||
if let Some(stmt) = table.get_mut(&hash) {
|
||||
optimize_stmt(stmt, state, true);
|
||||
*expr = Expr::Stmt(Box::new(vec![mem::take(stmt)].into()), *pos);
|
||||
} else if let Some(def_stmt) = x.1.as_mut() {
|
||||
optimize_stmt(def_stmt, state, true);
|
||||
*expr = Expr::Stmt(Box::new(vec![mem::take(def_stmt)].into()), *pos);
|
||||
let (statements, new_pos) = if let Some(block) = table.get_mut(&hash) {
|
||||
(
|
||||
optimize_stmt_block(
|
||||
mem::take(&mut block.statements).into_vec(),
|
||||
block.pos,
|
||||
state,
|
||||
true,
|
||||
)
|
||||
.into(),
|
||||
block.pos,
|
||||
)
|
||||
} else {
|
||||
*expr = Expr::Unit(*pos);
|
||||
}
|
||||
(
|
||||
optimize_stmt_block(
|
||||
mem::take(&mut x.1.statements).into_vec(),
|
||||
x.1.pos,
|
||||
state,
|
||||
true,
|
||||
)
|
||||
.into(),
|
||||
if x.1.pos.is_none() { *pos } else { x.1.pos },
|
||||
)
|
||||
};
|
||||
|
||||
*expr = Expr::Stmt(Box::new(StmtBlock {
|
||||
statements,
|
||||
pos: new_pos,
|
||||
}));
|
||||
}
|
||||
// switch
|
||||
Stmt::Switch(expr, x, _) => {
|
||||
optimize_expr(expr, state);
|
||||
x.0.values_mut()
|
||||
.for_each(|stmt| optimize_stmt(stmt, state, preserve_result));
|
||||
if let Some(def_stmt) = x.1.as_mut() {
|
||||
optimize_stmt(def_stmt, state, preserve_result);
|
||||
|
||||
match def_stmt {
|
||||
Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.1 = None,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
x.0.values_mut().for_each(|block| {
|
||||
block.statements = optimize_stmt_block(
|
||||
mem::take(&mut block.statements).into_vec(),
|
||||
block.pos,
|
||||
state,
|
||||
preserve_result,
|
||||
)
|
||||
.into()
|
||||
});
|
||||
x.1.statements = optimize_stmt_block(
|
||||
mem::take(&mut x.1.statements).into_vec(),
|
||||
x.1.pos,
|
||||
state,
|
||||
preserve_result,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
// while false { block } -> Noop
|
||||
@ -512,14 +537,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||
.into();
|
||||
}
|
||||
// {}
|
||||
Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => {
|
||||
Stmt::Expr(Expr::Stmt(x)) if x.statements.is_empty() => {
|
||||
state.set_dirty();
|
||||
*stmt = Stmt::Noop(*pos);
|
||||
*stmt = Stmt::Noop(x.pos);
|
||||
}
|
||||
// {...};
|
||||
Stmt::Expr(Expr::Stmt(x, pos)) => {
|
||||
Stmt::Expr(Expr::Stmt(x)) => {
|
||||
state.set_dirty();
|
||||
*stmt = Stmt::Block(mem::take(x).into_vec(), *pos);
|
||||
*stmt = Stmt::Block(mem::take(&mut x.statements).into_vec(), x.pos);
|
||||
}
|
||||
// expr;
|
||||
Stmt::Expr(expr) => optimize_expr(expr, state),
|
||||
@ -542,18 +567,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
||||
|
||||
match expr {
|
||||
// {}
|
||||
Expr::Stmt(x, pos) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(*pos) }
|
||||
Expr::Stmt(x) if x.statements.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.pos) }
|
||||
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
|
||||
Expr::Stmt(x, pos) => {
|
||||
let statements = optimize_stmt_block(mem::take(x).into_vec(), *pos, state, true);
|
||||
*expr = Expr::Stmt(Box::new(statements.into()), *pos);
|
||||
}
|
||||
Expr::Stmt(x) => x.statements = optimize_stmt_block(mem::take(&mut x.statements).into_vec(), x.pos, state, true).into(),
|
||||
// lhs.rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
||||
// map.string
|
||||
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
|
||||
let prop = &p.4.name;
|
||||
let prop = &p.2.name;
|
||||
// Map literal where everything is pure - promote the indexed item.
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
@ -902,30 +924,34 @@ pub fn optimize_into_ast(
|
||||
_functions
|
||||
.into_iter()
|
||||
.map(|mut fn_def| {
|
||||
let pos = fn_def.body.position();
|
||||
let pos = fn_def.body.pos;
|
||||
|
||||
// Optimize the function body
|
||||
let mut body = optimize_top_level(
|
||||
vec![fn_def.body],
|
||||
fn_def.body.statements.into_vec(),
|
||||
engine,
|
||||
&Scope::new(),
|
||||
&[&lib2],
|
||||
level,
|
||||
);
|
||||
|
||||
// {} -> Noop
|
||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||
match &mut body[..] {
|
||||
// { return val; } -> val
|
||||
Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _) => {
|
||||
Stmt::Expr(expr)
|
||||
[Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _)] => {
|
||||
body[0] = Stmt::Expr(mem::take(expr))
|
||||
}
|
||||
// { return; } -> ()
|
||||
Stmt::Return(crate::ast::ReturnType::Return, None, pos) => {
|
||||
Stmt::Expr(Expr::Unit(pos))
|
||||
[Stmt::Return(crate::ast::ReturnType::Return, None, _)] => {
|
||||
body.clear();
|
||||
}
|
||||
// All others
|
||||
stmt => stmt,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
fn_def.body = StmtBlock {
|
||||
statements: body.into(),
|
||||
pos,
|
||||
};
|
||||
|
||||
fn_def
|
||||
})
|
||||
.for_each(|fn_def| {
|
||||
|
@ -243,7 +243,11 @@ impl Expr {
|
||||
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
|
||||
let hash_set = calc_fn_hash(empty(), &setter, 2);
|
||||
|
||||
Self::Property(Box::new((getter, hash_get, setter, hash_set, ident.into())))
|
||||
Self::Property(Box::new((
|
||||
(getter, hash_get),
|
||||
(setter, hash_set),
|
||||
ident.into(),
|
||||
)))
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
@ -809,7 +813,7 @@ fn parse_switch(
|
||||
}
|
||||
}
|
||||
|
||||
let mut table = HashMap::new();
|
||||
let mut table = HashMap::<u64, StmtBlock>::new();
|
||||
let mut def_stmt = None;
|
||||
|
||||
loop {
|
||||
@ -869,10 +873,10 @@ fn parse_switch(
|
||||
let need_comma = !stmt.is_self_terminated();
|
||||
|
||||
def_stmt = if let Some(hash) = hash {
|
||||
table.insert(hash, stmt);
|
||||
table.insert(hash, stmt.into());
|
||||
None
|
||||
} else {
|
||||
Some(stmt)
|
||||
Some(stmt.into())
|
||||
};
|
||||
|
||||
match input.peek().unwrap() {
|
||||
@ -903,7 +907,10 @@ fn parse_switch(
|
||||
|
||||
Ok(Stmt::Switch(
|
||||
item,
|
||||
Box::new((final_table.into(), def_stmt)),
|
||||
Box::new((
|
||||
final_table.into(),
|
||||
def_stmt.unwrap_or_else(|| Stmt::Noop(Position::NONE).into()),
|
||||
)),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@ -954,7 +961,7 @@ fn parse_primary(
|
||||
// { - block statement as expression
|
||||
Token::LeftBrace if settings.allow_stmt_expr => {
|
||||
match parse_block(input, state, lib, settings.level_up())? {
|
||||
Stmt::Block(statements, pos) => Expr::Stmt(Box::new(statements.into()), pos),
|
||||
block @ Stmt::Block(_, _) => Expr::Stmt(Box::new(block.into())),
|
||||
stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt),
|
||||
}
|
||||
}
|
||||
@ -962,15 +969,14 @@ fn parse_primary(
|
||||
Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?,
|
||||
|
||||
// If statement is allowed to act as expressions
|
||||
Token::If if settings.allow_if_expr => Expr::Stmt(
|
||||
Box::new(vec![parse_if(input, state, lib, settings.level_up())?].into()),
|
||||
settings.pos,
|
||||
),
|
||||
Token::If if settings.allow_if_expr => Expr::Stmt(Box::new(
|
||||
parse_if(input, state, lib, settings.level_up())?.into(),
|
||||
)),
|
||||
// Switch statement is allowed to act as expressions
|
||||
Token::Switch if settings.allow_switch_expr => Expr::Stmt(
|
||||
Box::new(vec![parse_switch(input, state, lib, settings.level_up())?].into()),
|
||||
settings.pos,
|
||||
),
|
||||
Token::Switch if settings.allow_switch_expr => Expr::Stmt(Box::new(
|
||||
parse_switch(input, state, lib, settings.level_up())?.into(),
|
||||
)),
|
||||
|
||||
// | ...
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Token::Pipe | Token::Or if settings.allow_anonymous_fn => {
|
||||
@ -1506,7 +1512,7 @@ fn make_dot_expr(
|
||||
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
|
||||
let hash_set = calc_fn_hash(empty(), &setter, 2);
|
||||
|
||||
let rhs = Expr::Property(Box::new((getter, hash_get, setter, hash_set, ident)));
|
||||
let rhs = Expr::Property(Box::new(((getter, hash_get), (setter, hash_set), ident)));
|
||||
|
||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||
}
|
||||
@ -1861,8 +1867,8 @@ fn parse_custom_syntax(
|
||||
tokens.push(keyword);
|
||||
}
|
||||
MARKER_BLOCK => match parse_block(input, state, lib, settings)? {
|
||||
Stmt::Block(statements, pos) => {
|
||||
keywords.push(Expr::Stmt(Box::new(statements.into()), pos));
|
||||
block @ Stmt::Block(_, _) => {
|
||||
keywords.push(Expr::Stmt(Box::new(block.into())));
|
||||
let keyword = state.get_interned_string(MARKER_BLOCK);
|
||||
segments.push(keyword.clone());
|
||||
tokens.push(keyword);
|
||||
@ -2006,19 +2012,9 @@ fn parse_if(
|
||||
Stmt::Noop(Position::NONE)
|
||||
};
|
||||
|
||||
let else_body = match else_body {
|
||||
Stmt::If(_, _, pos) => {
|
||||
let mut statements: StaticVec<_> = Default::default();
|
||||
statements.push(else_body);
|
||||
StmtBlock { statements, pos }
|
||||
}
|
||||
Stmt::Block(_, _) | Stmt::Noop(_) => else_body.into(),
|
||||
_ => unreachable!("should either be if or a block, not {:?}", else_body),
|
||||
};
|
||||
|
||||
Ok(Stmt::If(
|
||||
guard,
|
||||
Box::new((if_body.into(), else_body)),
|
||||
Box::new((if_body.into(), else_body.into())),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@ -2741,7 +2737,8 @@ fn parse_fn(
|
||||
parse_block(input, state, lib, settings.level_up())?
|
||||
}
|
||||
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
|
||||
};
|
||||
}
|
||||
.into();
|
||||
|
||||
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
|
||||
|
||||
@ -2803,7 +2800,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
|
||||
let mut statements: StaticVec<_> = Default::default();
|
||||
statements.extend(externals.into_iter().map(Stmt::Share));
|
||||
statements.push(Stmt::Expr(expr));
|
||||
Expr::Stmt(Box::new(statements), pos)
|
||||
Expr::Stmt(Box::new(StmtBlock { statements, pos }))
|
||||
}
|
||||
|
||||
/// Parse an anonymous function definition.
|
||||
@ -2905,7 +2902,7 @@ fn parse_anon_fn(
|
||||
params,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
externals: Default::default(),
|
||||
body,
|
||||
body: body.into(),
|
||||
lib: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
mods: Default::default(),
|
||||
|
Loading…
Reference in New Issue
Block a user