Optimize layout.

This commit is contained in:
Stephen Chung 2021-03-10 22:12:48 +08:00
parent 874b3fc843
commit 728ed81173
5 changed files with 226 additions and 159 deletions

View File

@ -46,7 +46,7 @@ pub enum FnAccess {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ScriptFnDef { pub struct ScriptFnDef {
/// Function body. /// Function body.
pub body: Stmt, pub body: StmtBlock,
/// Encapsulated running environment, if any. /// Encapsulated running environment, if any.
pub lib: Option<Shared<Module>>, pub lib: Option<Shared<Module>>,
/// Encapsulated imported modules. /// Encapsulated imported modules.
@ -692,7 +692,7 @@ impl AST {
.chain({ .chain({
#[cfg(not(feature = "no_function"))] #[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")] #[cfg(feature = "no_function")]
{ {
@ -862,8 +862,8 @@ pub enum Stmt {
Switch( Switch(
Expr, Expr,
Box<( Box<(
HashableHashMap<u64, Stmt, StraightHasherBuilder>, HashableHashMap<u64, StmtBlock, StraightHasherBuilder>,
Option<Stmt>, StmtBlock,
)>, )>,
Position, Position,
), ),
@ -914,6 +914,7 @@ impl Default for Stmt {
} }
impl From<Stmt> for StmtBlock { impl From<Stmt> for StmtBlock {
#[inline(always)]
fn from(stmt: Stmt) -> Self { fn from(stmt: Stmt) -> Self {
match stmt { match stmt {
Stmt::Block(block, pos) => Self { Stmt::Block(block, pos) => Self {
@ -924,7 +925,11 @@ impl From<Stmt> for StmtBlock {
statements: Default::default(), statements: Default::default(),
pos, 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, _) => { Self::Switch(expr, x, _) => {
expr.is_pure() expr.is_pure()
&& x.0.values().all(Stmt::is_pure) && x.0
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) .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, _, _) => { Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
condition.is_pure() && block.statements.iter().all(Stmt::is_pure) condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
@ -1083,10 +1091,10 @@ impl Stmt {
} }
Self::Switch(e, x, _) => { Self::Switch(e, x, _) => {
e.walk(path, on_node); e.walk(path, on_node);
x.0.values().for_each(|s| s.walk(path, on_node)); x.0.values()
if let Some(ref s) = x.1 { .flat_map(|block| block.statements.iter())
s.walk(path, on_node); .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, _, _) => { Self::While(e, s, _) | Self::Do(s, e, _, _) => {
e.walk(path, on_node); e.walk(path, on_node);
@ -1384,10 +1392,10 @@ pub enum Expr {
Unit(Position), Unit(Position),
/// Variable access - (optional index, optional (hash, modules), variable name) /// Variable access - (optional index, optional (hash, modules), variable name)
Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>), Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>),
/// Property access - (getter, hash, setter, hash, prop) /// Property access - ((getter, hash), (setter, hash), prop)
Property(Box<(ImmutableString, u64, ImmutableString, u64, Ident)>), Property(Box<((ImmutableString, u64), (ImmutableString, u64), Ident)>),
/// { [statement][Stmt] ... } /// { [statement][Stmt] ... }
Stmt(Box<StaticVec<Stmt>>, Position), Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)` /// func `(` expr `,` ... `)`
FnCall(Box<FnCallExpr>, Position), FnCall(Box<FnCallExpr>, Position),
/// lhs `.` rhs /// lhs `.` rhs
@ -1478,8 +1486,8 @@ impl Expr {
Self::FnPointer(_, pos) => *pos, Self::FnPointer(_, pos) => *pos,
Self::Array(_, pos) => *pos, Self::Array(_, pos) => *pos,
Self::Map(_, pos) => *pos, Self::Map(_, pos) => *pos,
Self::Property(x) => (x.4).pos, Self::Property(x) => (x.2).pos,
Self::Stmt(_, pos) => *pos, Self::Stmt(x) => x.pos,
Self::Variable(x) => (x.2).pos, Self::Variable(x) => (x.2).pos,
Self::FnCall(_, pos) => *pos, Self::FnCall(_, pos) => *pos,
@ -1508,8 +1516,8 @@ impl Expr {
Self::Array(_, pos) => *pos = new_pos, Self::Array(_, pos) => *pos = new_pos,
Self::Map(_, pos) => *pos = new_pos, Self::Map(_, pos) => *pos = new_pos,
Self::Variable(x) => (x.2).pos = new_pos, Self::Variable(x) => (x.2).pos = new_pos,
Self::Property(x) => (x.4).pos = new_pos, Self::Property(x) => (x.2).pos = new_pos,
Self::Stmt(_, pos) => *pos = new_pos, Self::Stmt(x) => x.pos = new_pos,
Self::FnCall(_, pos) => *pos = new_pos, Self::FnCall(_, pos) => *pos = new_pos,
Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos, Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos,
Self::Unit(pos) => *pos = new_pos, Self::Unit(pos) => *pos = new_pos,
@ -1533,7 +1541,7 @@ impl Expr {
x.lhs.is_pure() && x.rhs.is_pure() 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, Self::Variable(_) => true,
@ -1596,7 +1604,7 @@ impl Expr {
Self::StringConstant(_, _) Self::StringConstant(_, _)
| Self::FnCall(_, _) | Self::FnCall(_, _)
| Self::Stmt(_, _) | Self::Stmt(_)
| Self::Dot(_, _) | Self::Dot(_, _)
| Self::Index(_, _) | Self::Index(_, _)
| Self::Array(_, _) | Self::Array(_, _)
@ -1632,7 +1640,7 @@ impl Expr {
on_node(path); on_node(path);
match self { 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::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::Map(x, _) => x.iter().for_each(|(_, e)| e.walk(path, on_node)),
Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => { Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => {

View File

@ -1181,7 +1181,7 @@ impl Engine {
} }
// {xxx:map}.id op= ??? // {xxx:map}.id op= ???
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => { 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 index = name.clone().into();
let val = self.get_indexed_mut( let val = self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, true, is_ref, false, level, mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
@ -1194,7 +1194,7 @@ impl Engine {
} }
// {xxx:map}.id // {xxx:map}.id
Expr::Property(x) if target_val.is::<Map>() => { 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 index = name.clone().into();
let val = self.get_indexed_mut( let val = self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, false, is_ref, false, level, mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
@ -1204,7 +1204,7 @@ impl Engine {
} }
// xxx.id = ??? // xxx.id = ???
Expr::Property(x) if new_val.is_some() => { 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 hash = FnHash::from_native(*hash_set);
let mut new_val = new_val; let mut new_val = new_val;
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0]; let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
@ -1216,7 +1216,7 @@ impl Engine {
} }
// xxx.id // xxx.id
Expr::Property(x) => { 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 hash = FnHash::from_native(*hash_get);
let mut args = [target_val]; let mut args = [target_val];
self.exec_fn_call( 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>() => { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::<Map>() => {
let mut val = match &x.lhs { let mut val = match &x.lhs {
Expr::Property(p) => { Expr::Property(p) => {
let Ident { name, pos, .. } = &p.4; let Ident { name, pos, .. } = &p.2;
let index = name.clone().into(); let index = name.clone().into();
self.get_indexed_mut( self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, false, is_ref, true, mods, state, lib, target_val, index, *pos, false, is_ref, true,
@ -1264,7 +1264,7 @@ impl Engine {
match &x.lhs { match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr // xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => { Expr::Property(p) => {
let (getter, hash_get, setter, hash_set, Ident { pos, .. }) = let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
p.as_ref(); p.as_ref();
let hash_get = FnHash::from_native(*hash_get); let hash_get = FnHash::from_native(*hash_get);
let hash_set = FnHash::from_native(*hash_set); let hash_set = FnHash::from_native(*hash_set);
@ -1456,7 +1456,7 @@ impl Engine {
} }
Expr::Property(x) if parent_chain_type == ChainType::Dot => { 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"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
@ -1466,7 +1466,7 @@ impl Engine {
// Evaluate in left-to-right order // Evaluate in left-to-right order
let lhs_val = match lhs { let lhs_val = match lhs {
Expr::Property(x) if parent_chain_type == ChainType::Dot => { 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::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
Expr::FnCall(x, _) Expr::FnCall(x, _)
@ -1655,16 +1655,11 @@ impl Engine {
.map(|(val, _)| val.take_or_clone()), .map(|(val, _)| val.take_or_clone()),
// Statement block // Statement block
Expr::Stmt(x, _) => self.eval_stmt_block( Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
scope, Expr::Stmt(x) => {
mods, let statements = &x.statements;
state, self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level)
lib, }
this_ptr,
x.as_ref().as_ref(),
true,
level,
),
// lhs[idx_expr] // lhs[idx_expr]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -1804,6 +1799,10 @@ impl Engine {
restore_prev_state: bool, restore_prev_state: bool,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
if statements.is_empty() {
return Ok(Dynamic::UNIT);
}
let mut _extra_fn_resolution_cache = false; let mut _extra_fn_resolution_cache = false;
let prev_always_search = state.always_search; let prev_always_search = state.always_search;
let prev_scope_len = scope.len(); let prev_scope_len = scope.len();
@ -2025,6 +2024,7 @@ impl Engine {
} }
// Block scope // Block scope
Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT),
Stmt::Block(statements, _) => { Stmt::Block(statements, _) => {
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, true, level) 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())) .map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))
.and_then(|guard_val| { .and_then(|guard_val| {
if guard_val { if guard_val {
self.eval_stmt_block( if !if_stmt.is_empty() {
scope, mods, state, lib, this_ptr, if_stmt, true, level, self.eval_stmt_block(
) scope, mods, state, lib, this_ptr, if_stmt, true, level,
} else if !else_stmt.is_empty() { )
self.eval_stmt_block( } else {
scope, mods, state, lib, this_ptr, else_stmt, true, level, Ok(Dynamic::UNIT)
) }
} else { } 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); value.hash(hasher);
let hash = hasher.finish(); let hash = hasher.finish();
table table.get(&hash).map(|StmtBlock { statements, .. }| {
.get(&hash) if !statements.is_empty() {
.map(|stmt| self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)) self.eval_stmt_block(
scope, mods, state, lib, this_ptr, statements, true, level,
)
} else {
Ok(Dynamic::UNIT)
}
})
} else { } else {
// Non-hashable values never match any specific clause // Non-hashable values never match any specific clause
None None
} }
.unwrap_or_else(|| { .unwrap_or_else(|| {
// Default match clause // Default match clause
def_stmt.as_ref().map_or_else( let def_stmt = &def_stmt.statements;
|| Ok(Dynamic::UNIT), if !def_stmt.is_empty() {
|def_stmt| { self.eval_stmt_block(
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level) scope, mods, state, lib, this_ptr, def_stmt, true, level,
}, )
) } else {
Ok(Dynamic::UNIT)
}
}) })
} }
@ -2102,20 +2116,22 @@ impl Engine {
true true
}; };
if condition { 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 {
return Ok(Dynamic::UNIT); 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; let body = &body.statements;
loop { loop {
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) if !body.is_empty() {
{ match self
Ok(_) => (), .eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
Err(err) => match *err { {
EvalAltResult::LoopBreak(false, _) => continue, Ok(_) => (),
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), Err(err) => match *err {
_ => return Err(err), EvalAltResult::LoopBreak(false, _) => continue,
}, EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
_ => return Err(err),
},
}
} }
if self if self
@ -2203,6 +2222,10 @@ impl Engine {
self.inc_operations(state, *pos)?; self.inc_operations(state, *pos)?;
if statements.is_empty() {
continue;
}
match self.eval_stmt_block( match self.eval_stmt_block(
scope, mods, state, lib, this_ptr, statements, true, level, scope, mods, state, lib, this_ptr, statements, true, level,
) { ) {

View File

@ -493,6 +493,10 @@ impl Engine {
self.inc_operations(state, pos)?; self.inc_operations(state, pos)?;
if fn_def.body.is_empty() {
return Ok(Dynamic::UNIT);
}
// Check for stack overflow // Check for stack overflow
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -539,10 +543,10 @@ impl Engine {
} }
// Evaluate the function // Evaluate the function
let stmt = &fn_def.body; let body = &fn_def.body.statements;
let result = self 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 { .or_else(|err| match *err {
// Convert return statement to return value // Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x), EvalAltResult::Return(x, _) => Ok(x),
@ -722,6 +726,10 @@ impl Engine {
let func = func.get_fn_def(); let func = func.get_fn_def();
if func.body.is_empty() {
return Ok((Dynamic::UNIT, false));
}
let scope: &mut Scope = &mut Default::default(); let scope: &mut Scope = &mut Default::default();
// Move captured variables into scope // Move captured variables into scope
@ -1391,22 +1399,27 @@ impl Engine {
match func { match func {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Some(f) if f.is_script() => { Some(f) if f.is_script() => {
let args = args.as_mut(); let fn_def = f.get_fn_def();
let new_scope = &mut Default::default();
let fn_def = f.get_fn_def().clone();
let mut source = module.id_raw().cloned(); if fn_def.body.is_empty() {
mem::swap(&mut state.source, &mut source); 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( let level = level + 1;
new_scope, mods, state, lib, &mut None, &fn_def, args, pos, level,
);
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 Some(f) if f.is_plugin_fn() => f

View File

@ -1,6 +1,6 @@
//! Module implementing the [`AST`] optimizer. //! Module implementing the [`AST`] optimizer.
use crate::ast::{Expr, Ident, Stmt}; use crate::ast::{Expr, Ident, Stmt, StmtBlock};
use crate::dynamic::AccessMode; use crate::dynamic::AccessMode;
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::fn_builtin::get_builtin_binary_op_fn; 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; let table = &mut x.0;
if let Some(stmt) = table.get_mut(&hash) { let (statements, new_pos) = if let Some(block) = table.get_mut(&hash) {
optimize_stmt(stmt, state, true); (
*expr = Expr::Stmt(Box::new(vec![mem::take(stmt)].into()), *pos); optimize_stmt_block(
} else if let Some(def_stmt) = x.1.as_mut() { mem::take(&mut block.statements).into_vec(),
optimize_stmt(def_stmt, state, true); block.pos,
*expr = Expr::Stmt(Box::new(vec![mem::take(def_stmt)].into()), *pos); state,
true,
)
.into(),
block.pos,
)
} else { } 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 // switch
Stmt::Switch(expr, x, _) => { Stmt::Switch(expr, x, _) => {
optimize_expr(expr, state); optimize_expr(expr, state);
x.0.values_mut() x.0.values_mut().for_each(|block| {
.for_each(|stmt| optimize_stmt(stmt, state, preserve_result)); block.statements = optimize_stmt_block(
if let Some(def_stmt) = x.1.as_mut() { mem::take(&mut block.statements).into_vec(),
optimize_stmt(def_stmt, state, preserve_result); block.pos,
state,
match def_stmt { preserve_result,
Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.1 = None, )
_ => (), .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 // while false { block } -> Noop
@ -512,14 +537,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
.into(); .into();
} }
// {} // {}
Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => { Stmt::Expr(Expr::Stmt(x)) if x.statements.is_empty() => {
state.set_dirty(); 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(); 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; // expr;
Stmt::Expr(expr) => optimize_expr(expr, state), Stmt::Expr(expr) => optimize_expr(expr, state),
@ -542,18 +567,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
match expr { 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 // { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
Expr::Stmt(x, pos) => { Expr::Stmt(x) => x.statements = optimize_stmt_block(mem::take(&mut x.statements).into_vec(), x.pos, state, true).into(),
let statements = optimize_stmt_block(mem::take(x).into_vec(), *pos, state, true);
*expr = Expr::Stmt(Box::new(statements.into()), *pos);
}
// lhs.rhs // lhs.rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) { Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
// map.string // map.string
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => { (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. // 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();
@ -902,30 +924,34 @@ pub fn optimize_into_ast(
_functions _functions
.into_iter() .into_iter()
.map(|mut fn_def| { .map(|mut fn_def| {
let pos = fn_def.body.position(); let pos = fn_def.body.pos;
// Optimize the function body // Optimize the function body
let mut body = optimize_top_level( let mut body = optimize_top_level(
vec![fn_def.body], fn_def.body.statements.into_vec(),
engine, engine,
&Scope::new(), &Scope::new(),
&[&lib2], &[&lib2],
level, level,
); );
// {} -> Noop match &mut body[..] {
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
// { return val; } -> val // { return val; } -> val
Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _) => { [Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _)] => {
Stmt::Expr(expr) body[0] = Stmt::Expr(mem::take(expr))
} }
// { return; } -> () // { return; } -> ()
Stmt::Return(crate::ast::ReturnType::Return, None, pos) => { [Stmt::Return(crate::ast::ReturnType::Return, None, _)] => {
Stmt::Expr(Expr::Unit(pos)) body.clear();
} }
// All others _ => (),
stmt => stmt, }
fn_def.body = StmtBlock {
statements: body.into(),
pos,
}; };
fn_def fn_def
}) })
.for_each(|fn_def| { .for_each(|fn_def| {

View File

@ -243,7 +243,11 @@ impl Expr {
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name)); let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
let hash_set = calc_fn_hash(empty(), &setter, 2); 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, _ => 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; let mut def_stmt = None;
loop { loop {
@ -869,10 +873,10 @@ fn parse_switch(
let need_comma = !stmt.is_self_terminated(); let need_comma = !stmt.is_self_terminated();
def_stmt = if let Some(hash) = hash { def_stmt = if let Some(hash) = hash {
table.insert(hash, stmt); table.insert(hash, stmt.into());
None None
} else { } else {
Some(stmt) Some(stmt.into())
}; };
match input.peek().unwrap() { match input.peek().unwrap() {
@ -903,7 +907,10 @@ fn parse_switch(
Ok(Stmt::Switch( Ok(Stmt::Switch(
item, 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, settings.pos,
)) ))
} }
@ -954,7 +961,7 @@ fn parse_primary(
// { - block statement as expression // { - block statement as expression
Token::LeftBrace if settings.allow_stmt_expr => { Token::LeftBrace if settings.allow_stmt_expr => {
match parse_block(input, state, lib, settings.level_up())? { 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), 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())?, Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?,
// If statement is allowed to act as expressions // If statement is allowed to act as expressions
Token::If if settings.allow_if_expr => Expr::Stmt( Token::If if settings.allow_if_expr => Expr::Stmt(Box::new(
Box::new(vec![parse_if(input, state, lib, settings.level_up())?].into()), parse_if(input, state, lib, settings.level_up())?.into(),
settings.pos, )),
),
// Switch statement is allowed to act as expressions // Switch statement is allowed to act as expressions
Token::Switch if settings.allow_switch_expr => Expr::Stmt( Token::Switch if settings.allow_switch_expr => Expr::Stmt(Box::new(
Box::new(vec![parse_switch(input, state, lib, settings.level_up())?].into()), parse_switch(input, state, lib, settings.level_up())?.into(),
settings.pos, )),
),
// | ... // | ...
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Token::Pipe | Token::Or if settings.allow_anonymous_fn => { 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 setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
let hash_set = calc_fn_hash(empty(), &setter, 2); 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) Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
} }
@ -1861,8 +1867,8 @@ fn parse_custom_syntax(
tokens.push(keyword); tokens.push(keyword);
} }
MARKER_BLOCK => match parse_block(input, state, lib, settings)? { MARKER_BLOCK => match parse_block(input, state, lib, settings)? {
Stmt::Block(statements, pos) => { block @ Stmt::Block(_, _) => {
keywords.push(Expr::Stmt(Box::new(statements.into()), pos)); keywords.push(Expr::Stmt(Box::new(block.into())));
let keyword = state.get_interned_string(MARKER_BLOCK); let keyword = state.get_interned_string(MARKER_BLOCK);
segments.push(keyword.clone()); segments.push(keyword.clone());
tokens.push(keyword); tokens.push(keyword);
@ -2006,19 +2012,9 @@ fn parse_if(
Stmt::Noop(Position::NONE) 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( Ok(Stmt::If(
guard, guard,
Box::new((if_body.into(), else_body)), Box::new((if_body.into(), else_body.into())),
settings.pos, settings.pos,
)) ))
} }
@ -2741,7 +2737,8 @@ fn parse_fn(
parse_block(input, state, lib, settings.level_up())? parse_block(input, state, lib, settings.level_up())?
} }
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)), (_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
}; }
.into();
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect(); 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(); let mut statements: StaticVec<_> = Default::default();
statements.extend(externals.into_iter().map(Stmt::Share)); statements.extend(externals.into_iter().map(Stmt::Share));
statements.push(Stmt::Expr(expr)); statements.push(Stmt::Expr(expr));
Expr::Stmt(Box::new(statements), pos) Expr::Stmt(Box::new(StmtBlock { statements, pos }))
} }
/// Parse an anonymous function definition. /// Parse an anonymous function definition.
@ -2905,7 +2902,7 @@ fn parse_anon_fn(
params, params,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
externals: Default::default(), externals: Default::default(),
body, body: body.into(),
lib: None, lib: None,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
mods: Default::default(), mods: Default::default(),