Pack Stmt structure.

This commit is contained in:
Stephen Chung 2020-10-27 18:18:19 +08:00
parent 01663a6581
commit 93b5df6b3c
3 changed files with 290 additions and 302 deletions

View File

@ -1809,12 +1809,12 @@ impl Engine {
Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level), Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level),
// Block scope // Block scope
Stmt::Block(x) => { Stmt::Block(statements, _) => {
let prev_scope_len = scope.len(); let prev_scope_len = scope.len();
let prev_mods_len = mods.len(); let prev_mods_len = mods.len();
state.scope_level += 1; state.scope_level += 1;
let result = x.0.iter().try_fold(Default::default(), |_, stmt| { let result = statements.iter().try_fold(Default::default(), |_, stmt| {
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
}); });
@ -1830,27 +1830,22 @@ impl Engine {
} }
// If-else statement // If-else statement
Stmt::IfThenElse(x) => { Stmt::IfThenElse(expr, if_block, else_block, _) => self
let (expr, if_block, else_block, _) = x.as_ref(); .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool()
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))
.as_bool() .and_then(|guard_val| {
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position())) if guard_val {
.and_then(|guard_val| { self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level)
if guard_val { } else if let Some(stmt) = else_block {
self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level) self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
} else if let Some(stmt) = else_block { } else {
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) Ok(Default::default())
} else { }
Ok(Default::default()) }),
}
})
}
// While loop // While loop
Stmt::While(x) => loop { Stmt::While(expr, body, _) => loop {
let (expr, body, _) = x.as_ref();
match self match self
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
@ -1873,8 +1868,8 @@ impl Engine {
}, },
// Loop statement // Loop statement
Stmt::Loop(x) => loop { Stmt::Loop(block, _) => loop {
match self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level) { match self.eval_stmt(scope, mods, state, lib, this_ptr, block, level) {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::LoopBreak(false, _) => (), EvalAltResult::LoopBreak(false, _) => (),
@ -1885,8 +1880,7 @@ impl Engine {
}, },
// For loop // For loop
Stmt::For(x) => { Stmt::For(name, expr, stmt, _) => {
let (name, expr, stmt, _) = x.as_ref();
let iter_obj = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let iter_obj = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
let iter_type = iter_obj.type_id(); let iter_type = iter_obj.type_id();
@ -1929,7 +1923,7 @@ impl Engine {
scope.rewind(scope.len() - 1); scope.rewind(scope.len() - 1);
Ok(Default::default()) Ok(Default::default())
} else { } else {
EvalAltResult::ErrorFor(x.1.position()).into() EvalAltResult::ErrorFor(expr.position()).into()
} }
} }
@ -1941,10 +1935,10 @@ impl Engine {
// Try/Catch statement // Try/Catch statement
Stmt::TryCatch(x) => { Stmt::TryCatch(x) => {
let ((body, _), var_def, (catch_body, _)) = x.as_ref(); let ((try_body, _), var_def, (catch_body, _)) = x.as_ref();
let result = self let result = self
.eval_stmt(scope, mods, state, lib, this_ptr, body, level) .eval_stmt(scope, mods, state, lib, this_ptr, try_body, level)
.map(|_| ().into()); .map(|_| ().into());
match result { match result {
@ -1992,40 +1986,33 @@ impl Engine {
} }
// Return value // Return value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => { Stmt::ReturnWithVal((ReturnType::Return, pos), Some(expr), _) => EvalAltResult::Return(
let expr = x.1.as_ref().unwrap(); self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?,
EvalAltResult::Return( *pos,
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, )
(x.0).1, .into(),
)
.into()
}
// Empty return // Empty return
Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Return => { Stmt::ReturnWithVal((ReturnType::Return, pos), None, _) => {
EvalAltResult::Return(Default::default(), (x.0).1).into() EvalAltResult::Return(Default::default(), *pos).into()
} }
// Throw value // Throw value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => { Stmt::ReturnWithVal((ReturnType::Exception, pos), Some(expr), _) => {
let expr = x.1.as_ref().unwrap();
let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
EvalAltResult::ErrorRuntime(val, (x.0).1).into() EvalAltResult::ErrorRuntime(val, *pos).into()
} }
// Empty throw // Empty throw
Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Exception => { Stmt::ReturnWithVal((ReturnType::Exception, pos), None, _) => {
EvalAltResult::ErrorRuntime(().into(), (x.0).1).into() EvalAltResult::ErrorRuntime(().into(), *pos).into()
} }
Stmt::ReturnWithVal(_) => unreachable!(),
// Let/const statement // Let/const statement
Stmt::Let(x) | Stmt::Const(x) => { Stmt::Let(var_def, expr, _) | Stmt::Const(var_def, expr, _) => {
let ((var_name, _), expr, _) = x.as_ref();
let entry_type = match stmt { let entry_type = match stmt {
Stmt::Let(_) => ScopeEntryType::Normal, Stmt::Let(_, _, _) => ScopeEntryType::Normal,
Stmt::Const(_) => ScopeEntryType::Constant, Stmt::Const(_, _, _) => ScopeEntryType::Constant,
_ => unreachable!(), _ => unreachable!(),
}; };
@ -2035,16 +2022,14 @@ impl Engine {
} else { } else {
().into() ().into()
}; };
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state); let var_name = unsafe_cast_var_name_to_lifetime(&var_def.0, &state);
scope.push_dynamic_value(var_name, entry_type, val, false); scope.push_dynamic_value(var_name, entry_type, val, false);
Ok(Default::default()) Ok(Default::default())
} }
// Import statement // Import statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x) => { Stmt::Import(expr, alias, _pos) => {
let (expr, alias, _pos) = x.as_ref();
// Guard against too many modules // Guard against too many modules
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if state.modules >= self.max_modules() { if state.modules >= self.max_modules() {
@ -2079,8 +2064,8 @@ impl Engine {
// Export statement // Export statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Export(x) => { Stmt::Export(list, _) => {
for ((id, id_pos), rename) in x.0.iter() { for ((id, id_pos), rename) in list.iter() {
// Mark scope variables as public // Mark scope variables as public
if let Some(index) = scope.get_index(id).map(|(i, _)| i) { if let Some(index) = scope.get_index(id).map(|(i, _)| i) {
let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id); let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id);
@ -2094,9 +2079,7 @@ impl Engine {
// Share statement // Share statement
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => { Stmt::Share(var_name, _) => {
let (var_name, _) = x.as_ref();
match scope.get_index(var_name) { match scope.get_index(var_name) {
Some((index, ScopeEntryType::Normal)) => { Some((index, ScopeEntryType::Normal)) => {
let (val, _) = scope.get_mut(index); let (val, _) = scope.get_mut(index);

View File

@ -163,87 +163,87 @@ fn call_fn_with_constant_arguments(
/// Optimize a statement. /// Optimize a statement.
fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
match stmt { match stmt {
// if false { if_block } -> Noop
Stmt::IfThenElse(Expr::False(pos), _, None, _) => {
state.set_dirty();
Stmt::Noop(pos)
}
// if true { if_block } -> if_block
Stmt::IfThenElse(Expr::True(_), if_block, None, _) => optimize_stmt(*if_block, state, true),
// if expr { Noop } // if expr { Noop }
Stmt::IfThenElse(x) if matches!(x.1, Stmt::Noop(_)) && x.2.is_none() => { Stmt::IfThenElse(condition, if_block, None, _)
if matches!(if_block.as_ref(), Stmt::Noop(_)) =>
{
state.set_dirty(); state.set_dirty();
let pos = x.0.position(); let pos = condition.position();
let expr = optimize_expr(x.0, state); let expr = optimize_expr(condition, state);
if preserve_result { if preserve_result {
// -> { expr, Noop } // -> { expr, Noop }
let mut statements = StaticVec::new(); let mut statements = Vec::new();
statements.push(Stmt::Expr(Box::new(expr))); statements.push(Stmt::Expr(expr));
statements.push(x.1); statements.push(*if_block);
Stmt::Block(Box::new((statements, pos))) Stmt::Block(statements, pos)
} else { } else {
// -> expr // -> expr
Stmt::Expr(Box::new(expr)) Stmt::Expr(expr)
} }
} }
// if expr { if_block } // if expr { if_block }
Stmt::IfThenElse(x) if x.2.is_none() => match x.0 { Stmt::IfThenElse(condition, if_block, None, pos) => Stmt::IfThenElse(
// if false { if_block } -> Noop optimize_expr(condition, state),
Expr::False(pos) => { Box::new(optimize_stmt(*if_block, state, true)),
state.set_dirty(); None,
Stmt::Noop(pos) pos,
} ),
// if true { if_block } -> if_block // if false { if_block } else { else_block } -> else_block
Expr::True(_) => optimize_stmt(x.1, state, true), Stmt::IfThenElse(Expr::False(_), _, Some(else_block), _) => {
// if expr { if_block } optimize_stmt(*else_block, state, true)
expr => Stmt::IfThenElse(Box::new(( }
optimize_expr(expr, state), // if true { if_block } else { else_block } -> if_block
optimize_stmt(x.1, state, true), Stmt::IfThenElse(Expr::True(_), if_block, _, _) => optimize_stmt(*if_block, state, true),
None,
x.3,
))),
},
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
Stmt::IfThenElse(x) if x.2.is_some() => match x.0 { Stmt::IfThenElse(condition, if_block, Some(else_block), pos) => Stmt::IfThenElse(
// if false { if_block } else { else_block } -> else_block optimize_expr(condition, state),
Expr::False(_) => optimize_stmt(x.2.unwrap(), state, true), Box::new(optimize_stmt(*if_block, state, true)),
// if true { if_block } else { else_block } -> if_block match optimize_stmt(*else_block, state, true) {
Expr::True(_) => optimize_stmt(x.1, state, true), Stmt::Noop(_) => None, // Noop -> no else block
// if expr { if_block } else { else_block } stmt => Some(Box::new(stmt)),
expr => Stmt::IfThenElse(Box::new(( },
optimize_expr(expr, state), pos,
optimize_stmt(x.1, state, true), ),
match optimize_stmt(x.2.unwrap(), state, true) {
Stmt::Noop(_) => None, // Noop -> no else block // while false { block } -> Noop
stmt => Some(stmt), Stmt::While(Expr::False(pos), _, _) => {
}, state.set_dirty();
x.3, Stmt::Noop(pos)
))), }
}, // while true { block } -> loop { block }
Stmt::While(Expr::True(_), block, pos) => {
Stmt::Loop(Box::new(optimize_stmt(*block, state, false)), pos)
}
// while expr { block } // while expr { block }
Stmt::While(x) => match x.0 { Stmt::While(condition, block, pos) => {
// while false { block } -> Noop match optimize_stmt(*block, state, false) {
Expr::False(pos) => {
state.set_dirty();
Stmt::Noop(pos)
}
// while true { block } -> loop { block }
Expr::True(_) => Stmt::Loop(Box::new((optimize_stmt(x.1, state, false), x.2))),
// while expr { block }
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
state.set_dirty(); state.set_dirty();
let mut statements = StaticVec::new(); let mut statements = Vec::new();
statements.push(Stmt::Expr(Box::new(optimize_expr(expr, state)))); statements.push(Stmt::Expr(optimize_expr(condition, state)));
if preserve_result { if preserve_result {
statements.push(Stmt::Noop(pos)) statements.push(Stmt::Noop(pos))
} }
Stmt::Block(Box::new((statements, pos))) Stmt::Block(statements, pos)
} }
// while expr { block } // while expr { block }
stmt => Stmt::While(Box::new((optimize_expr(expr, state), stmt, x.2))), stmt => Stmt::While(optimize_expr(condition, state), Box::new(stmt), pos),
}, }
}, }
// loop { block } // loop { block }
Stmt::Loop(x) => match optimize_stmt(x.0, state, false) { Stmt::Loop(block, pos) => match optimize_stmt(*block, state, false) {
// loop { break; } -> Noop // loop { break; } -> Noop
Stmt::Break(pos) => { Stmt::Break(pos) => {
// Only a single break statement // Only a single break statement
@ -251,59 +251,50 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Noop(pos) Stmt::Noop(pos)
} }
// loop { block } // loop { block }
stmt => Stmt::Loop(Box::new((stmt, x.1))), stmt => Stmt::Loop(Box::new(stmt), pos),
}, },
// for id in expr { block } // for id in expr { block }
Stmt::For(x) => Stmt::For(Box::new(( Stmt::For(var_name, iterable, block, pos) => Stmt::For(
x.0, var_name,
optimize_expr(x.1, state), optimize_expr(iterable, state),
optimize_stmt(x.2, state, false), Box::new(optimize_stmt(*block, state, false)),
x.3, pos,
))), ),
// let id = expr; // let id = expr;
Stmt::Let(x) if x.1.is_some() => Stmt::Let(Box::new(( Stmt::Let(name, Some(expr), pos) => Stmt::Let(name, Some(optimize_expr(expr, state)), pos),
x.0,
Some(optimize_expr(x.1.unwrap(), state)),
x.2,
))),
// let id; // let id;
stmt @ Stmt::Let(_) => stmt, stmt @ Stmt::Let(_, None, _) => stmt,
// import expr as var; // import expr as var;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1, x.2))), Stmt::Import(expr, alias, pos) => Stmt::Import(optimize_expr(expr, state), alias, pos),
// { block } // { block }
Stmt::Block(x) => { Stmt::Block(statements, pos) => {
let orig_len = x.0.len(); // Original number of statements in the block, for change detection let orig_len = statements.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<_> = let mut result: Vec<_> = statements
x.0.into_iter() .into_iter()
.map(|stmt| match stmt { .map(|stmt| match stmt {
// Add constant literals into the state // Add constant literals into the state
Stmt::Const(mut v) => { Stmt::Const(name, Some(expr), pos) if expr.is_literal() => {
if let Some(expr) = v.1 { state.set_dirty();
let expr = optimize_expr(expr, state); state.push_constant(&name.0, expr);
Stmt::Noop(pos) // No need to keep constants
if expr.is_literal() { }
state.set_dirty(); Stmt::Const(name, Some(expr), pos) if expr.is_literal() => {
state.push_constant(&(v.0).0, expr); let expr = optimize_expr(expr, state);
Stmt::Noop(pos) // No need to keep constants Stmt::Const(name, Some(expr), pos)
} else { }
v.1 = Some(expr); Stmt::Const(name, None, pos) => {
Stmt::Const(v) state.set_dirty();
} state.push_constant(&name.0, Expr::Unit(name.1));
} else { Stmt::Noop(pos) // No need to keep constants
state.set_dirty(); }
state.push_constant(&(v.0).0, Expr::Unit((v.0).1)); // Optimize the statement
Stmt::Noop(pos) // No need to keep constants stmt => optimize_stmt(stmt, state, preserve_result),
} })
} .collect();
// Optimize the statement
_ => optimize_stmt(stmt, state, preserve_result),
})
.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 };
@ -321,9 +312,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
while let Some(expr) = result.pop() { while let Some(expr) = result.pop() {
match expr { match expr {
Stmt::Let(x) => removed = x.1.as_ref().map(Expr::is_pure).unwrap_or(true), Stmt::Let(_, expr, _) => {
removed = expr.as_ref().map(Expr::is_pure).unwrap_or(true)
}
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x) => removed = x.0.is_pure(), Stmt::Import(expr, _, _) => removed = expr.is_pure(),
_ => { _ => {
result.push(expr); result.push(expr);
break; break;
@ -341,7 +334,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
.into_iter() .into_iter()
.rev() .rev()
.enumerate() .enumerate()
.map(|(i, s)| optimize_stmt(s, state, i == 0)) .map(|(i, stmt)| optimize_stmt(stmt, state, i == 0))
.rev() .rev()
.collect(); .collect();
} }
@ -355,7 +348,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
} }
match stmt { match stmt {
Stmt::ReturnWithVal(_) | Stmt::Break(_) => dead_code = true, Stmt::ReturnWithVal(_, _, _) | Stmt::Break(_) => dead_code = true,
_ => (), _ => (),
} }
@ -370,23 +363,23 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
// Pop the stack and remove all the local constants // Pop the stack and remove all the local constants
state.restore_constants(orig_constants_len); state.restore_constants(orig_constants_len);
match result[..] { match &result[..] {
// No statements in block - change to No-op // No statements in block - change to No-op
[] => { [] => {
state.set_dirty(); state.set_dirty();
Stmt::Noop(pos) Stmt::Noop(pos)
} }
// Only one let statement - leave it alone // Only one let statement - leave it alone
[Stmt::Let(_)] => Stmt::Block(Box::new((result.into(), pos))), [x] if matches!(x, Stmt::Let(_, _, _)) => Stmt::Block(result, pos),
// Only one import statement - leave it alone // Only one import statement - leave it alone
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
[Stmt::Import(_)] => Stmt::Block(Box::new((result.into(), pos))), [x] if matches!(x, Stmt::Import(_, _, _)) => Stmt::Block(result, pos),
// Only one statement - promote // Only one statement - promote
[_] => { [_] => {
state.set_dirty(); state.set_dirty();
result.remove(0) result.remove(0)
} }
_ => Stmt::Block(Box::new((result.into(), pos))), _ => Stmt::Block(result, pos),
} }
} }
// try { block } catch ( var ) { block } // try { block } catch ( var ) { block }
@ -394,25 +387,26 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
// If try block is pure, there will never be any exceptions // If try block is pure, there will never be any exceptions
state.set_dirty(); state.set_dirty();
let pos = (x.0).0.position(); let pos = (x.0).0.position();
let mut statements: StaticVec<_> = Default::default(); let mut statements: Vec<_> = Default::default();
statements.push(optimize_stmt((x.0).0, state, preserve_result)); statements.push(optimize_stmt((x.0).0, state, preserve_result));
statements.push(Stmt::Noop(pos)); statements.push(Stmt::Noop(pos));
Stmt::Block(Box::new((statements, pos))) Stmt::Block(statements, pos)
} }
// try { block } catch ( var ) { block } // try { block } catch ( var ) { block }
Stmt::TryCatch(x) => Stmt::TryCatch(Box::new(( Stmt::TryCatch(x) => {
(optimize_stmt((x.0).0, state, false), (x.0).1), let ((try_block, try_pos), var_name, (catch_block, catch_pos)) = *x;
x.1, Stmt::TryCatch(Box::new((
(optimize_stmt((x.2).0, state, false), (x.2).1), (optimize_stmt(try_block, state, false), try_pos),
))), var_name,
(optimize_stmt(catch_block, state, false), catch_pos),
)))
}
// expr; // expr;
Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))), Stmt::Expr(expr) => Stmt::Expr(optimize_expr(expr, state)),
// return expr; // return expr;
Stmt::ReturnWithVal(x) if x.1.is_some() => Stmt::ReturnWithVal(Box::new(( Stmt::ReturnWithVal(ret, Some(expr), pos) => {
x.0, Stmt::ReturnWithVal(ret, Some(optimize_expr(expr, state)), pos)
Some(optimize_expr(x.1.unwrap(), state)), }
x.2,
))),
// All other statements - skip // All other statements - skip
stmt => stmt, stmt => stmt,
} }
@ -442,7 +436,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
// ( expr ) -> expr // ( expr ) -> expr
Stmt::Expr(expr) => { Stmt::Expr(expr) => {
state.set_dirty(); state.set_dirty();
*expr expr
} }
// ( stmt ) // ( stmt )
stmt => Expr::Stmt(Box::new((stmt, x.1))), stmt => Expr::Stmt(Box::new((stmt, x.1))),
@ -748,35 +742,35 @@ fn optimize(
.enumerate() .enumerate()
.map(|(i, stmt)| { .map(|(i, stmt)| {
match stmt { match stmt {
Stmt::Const(mut v) => { Stmt::Const(var_def, Some(expr), pos) => {
// Load constants // Load constants
if let Some(expr) = v.1 { let expr = optimize_expr(expr, &mut state);
let expr = optimize_expr(expr, &mut state);
if expr.is_literal() { if expr.is_literal() {
state.push_constant(&(v.0).0, expr.clone()); state.push_constant(&var_def.0, expr.clone());
}
v.1 = if expr.is_unit() {
state.set_dirty();
None
} else {
Some(expr)
};
} else {
state.push_constant(&(v.0).0, Expr::Unit((v.0).1));
} }
// Keep it in the global scope // Keep it in the global scope
Stmt::Const(v) if expr.is_unit() {
state.set_dirty();
Stmt::Const(var_def, None, pos)
} else {
Stmt::Const(var_def, Some(expr), pos)
}
}
Stmt::Const(ref var_def, None, _) => {
state.push_constant(&var_def.0, Expr::Unit(var_def.1));
// Keep it in the global scope
stmt
} }
_ => { _ => {
// 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(_) => true, Stmt::Let(_, _, _) => true,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(_) => true, Stmt::Import(_, _, _) => true,
_ => i == num_statements - 1, _ => i == num_statements - 1,
}; };
optimize_stmt(stmt, &mut state, keep) optimize_stmt(stmt, &mut state, keep)
@ -859,16 +853,12 @@ pub fn optimize_into_ast(
// {} -> Noop // {} -> Noop
fn_def.body = 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(x) Stmt::ReturnWithVal((ReturnType::Return, _), Some(expr), _) => {
if x.1.is_some() && (x.0).0 == ReturnType::Return => Stmt::Expr(expr)
{
Stmt::Expr(Box::new(x.1.unwrap()))
} }
// { return; } -> () // { return; } -> ()
Stmt::ReturnWithVal(x) Stmt::ReturnWithVal((ReturnType::Return, pos), None, _) => {
if x.1.is_none() && (x.0).0 == ReturnType::Return => Stmt::Expr(Expr::Unit(pos))
{
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
} }
// All others // All others
stmt => stmt, stmt => stmt,

View File

@ -742,19 +742,19 @@ pub enum Stmt {
/// No-op. /// No-op.
Noop(Position), Noop(Position),
/// if expr { stmt } else { stmt } /// if expr { stmt } else { stmt }
IfThenElse(Box<(Expr, Stmt, Option<Stmt>, Position)>), IfThenElse(Expr, Box<Stmt>, Option<Box<Stmt>>, Position),
/// while expr { stmt } /// while expr { stmt }
While(Box<(Expr, Stmt, Position)>), While(Expr, Box<Stmt>, Position),
/// loop { stmt } /// loop { stmt }
Loop(Box<(Stmt, Position)>), Loop(Box<Stmt>, Position),
/// for id in expr { stmt } /// for id in expr { stmt }
For(Box<(String, Expr, Stmt, Position)>), For(Box<String>, Expr, Box<Stmt>, Position),
/// let id = expr /// let id = expr
Let(Box<((String, Position), Option<Expr>, Position)>), Let(Box<(String, Position)>, Option<Expr>, Position),
/// const id = expr /// const id = expr
Const(Box<((String, Position), Option<Expr>, Position)>), Const(Box<(String, Position)>, Option<Expr>, Position),
/// { stmt; ... } /// { stmt; ... }
Block(Box<(StaticVec<Stmt>, Position)>), Block(Vec<Stmt>, Position),
/// try { stmt; ... } catch ( var ) { stmt; ... } /// try { stmt; ... } catch ( var ) { stmt; ... }
TryCatch( TryCatch(
Box<( Box<(
@ -764,27 +764,25 @@ pub enum Stmt {
)>, )>,
), ),
/// expr /// expr
Expr(Box<Expr>), Expr(Expr),
/// continue /// continue
Continue(Position), Continue(Position),
/// break /// break
Break(Position), Break(Position),
/// return/throw /// return/throw
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>, Position)>), ReturnWithVal((ReturnType, Position), Option<Expr>, Position),
/// import expr as var /// import expr as var
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Import(Box<(Expr, Option<(ImmutableString, Position)>, Position)>), Import(Expr, Option<(ImmutableString, Position)>, Position),
/// export var as var, ... /// export var as var, ...
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Export( Export(
Box<( Vec<((String, Position), Option<(String, Position)>)>,
StaticVec<((String, Position), Option<(String, Position)>)>, Position,
Position,
)>,
), ),
/// Convert a variable to shared. /// Convert a variable to shared.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Share(Box<(String, Position)>), Share(String, Position),
} }
impl Default for Stmt { impl Default for Stmt {
@ -806,52 +804,58 @@ impl Stmt {
/// Get the `Position` of this statement. /// Get the `Position` of this statement.
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
Self::Noop(pos) | Self::Continue(pos) | Self::Break(pos) => *pos, Self::Noop(pos)
Self::Let(x) => (x.0).1, | Self::Continue(pos)
Self::Const(x) => (x.0).1, | Self::Break(pos)
Self::Block(x) => x.1, | Self::Block(_, pos)
Self::IfThenElse(x) => x.3, | Self::IfThenElse(_, _, _, pos)
Self::Expr(x) => x.position(), | Self::While(_, _, pos)
Self::While(x) => x.2, | Self::Loop(_, pos)
Self::Loop(x) => x.1, | Self::For(_, _, _, pos)
Self::For(x) => x.3, | Self::ReturnWithVal((_, pos), _, _) => *pos,
Self::ReturnWithVal(x) => (x.0).1,
Self::Let(x, _, _) | Self::Const(x, _, _) => x.1,
Self::TryCatch(x) => (x.0).1, Self::TryCatch(x) => (x.0).1,
Self::Expr(x) => x.position(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(x) => x.2, Self::Import(_, _, pos) => *pos,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Export(x) => x.1, Self::Export(_, pos) => *pos,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(x) => x.1, Self::Share(_, pos) => *pos,
} }
} }
/// Override the `Position` of this statement. /// Override the `Position` of this statement.
pub fn set_position(&mut self, new_pos: Position) -> &mut Self { pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self { match self {
Self::Noop(pos) | Self::Continue(pos) | Self::Break(pos) => *pos = new_pos, Self::Noop(pos)
Self::Let(x) => (x.0).1 = new_pos, | Self::Continue(pos)
Self::Const(x) => (x.0).1 = new_pos, | Self::Break(pos)
Self::Block(x) => x.1 = new_pos, | Self::Block(_, pos)
Self::IfThenElse(x) => x.3 = new_pos, | Self::IfThenElse(_, _, _, pos)
| Self::While(_, _, pos)
| Self::Loop(_, pos)
| Self::For(_, _, _, pos)
| Self::ReturnWithVal((_, pos), _, _) => *pos = new_pos,
Self::Let(x, _, _) | Self::Const(x, _, _) => x.1 = new_pos,
Self::TryCatch(x) => (x.0).1 = new_pos,
Self::Expr(x) => { Self::Expr(x) => {
x.set_position(new_pos); x.set_position(new_pos);
} }
Self::While(x) => x.2 = new_pos,
Self::Loop(x) => x.1 = new_pos,
Self::For(x) => x.3 = new_pos,
Self::ReturnWithVal(x) => (x.0).1 = new_pos,
Self::TryCatch(x) => (x.0).1 = new_pos,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(x) => x.2 = new_pos, Self::Import(_, _, pos) => *pos = new_pos,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Export(x) => x.1 = new_pos, Self::Export(_, pos) => *pos = new_pos,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(x) => x.1 = new_pos, Self::Share(_, pos) => *pos = new_pos,
} }
self self
@ -860,28 +864,28 @@ impl Stmt {
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)? /// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
pub fn is_self_terminated(&self) -> bool { pub fn is_self_terminated(&self) -> bool {
match self { match self {
Self::IfThenElse(_) Self::IfThenElse(_, _, _, _)
| Self::While(_) | Self::While(_, _, _)
| Self::Loop(_) | Self::Loop(_, _)
| Self::For(_) | Self::For(_, _, _, _)
| Self::Block(_) | Self::Block(_, _)
| Self::TryCatch(_) => true, | Self::TryCatch(_) => true,
// A No-op requires a semicolon in order to know it is an empty statement! // A No-op requires a semicolon in order to know it is an empty statement!
Self::Noop(_) => false, Self::Noop(_) => false,
Self::Let(_) Self::Let(_, _, _)
| Self::Const(_) | Self::Const(_, _, _)
| Self::Expr(_) | Self::Expr(_)
| Self::Continue(_) | Self::Continue(_)
| Self::Break(_) | Self::Break(_)
| Self::ReturnWithVal(_) => false, | Self::ReturnWithVal(_, _, _) => false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_) | Self::Export(_) => false, Self::Import(_, _, _) | Self::Export(_, _) => false,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(_) => false, Self::Share(_, _) => false,
} }
} }
@ -890,25 +894,27 @@ impl Stmt {
match self { match self {
Self::Noop(_) => true, Self::Noop(_) => true,
Self::Expr(expr) => expr.is_pure(), Self::Expr(expr) => expr.is_pure(),
Self::IfThenElse(x) if x.2.is_some() => { Self::IfThenElse(condition, if_block, Some(else_block), _) => {
x.0.is_pure() && x.1.is_pure() && x.2.as_ref().unwrap().is_pure() condition.is_pure() && if_block.is_pure() && else_block.is_pure()
} }
Self::IfThenElse(x) => x.1.is_pure(), Self::IfThenElse(condition, if_block, None, _) => {
Self::While(x) => x.0.is_pure() && x.1.is_pure(), condition.is_pure() && if_block.is_pure()
Self::Loop(x) => x.0.is_pure(), }
Self::For(x) => x.1.is_pure() && x.2.is_pure(), Self::While(condition, block, _) => condition.is_pure() && block.is_pure(),
Self::Let(_) | Self::Const(_) => false, Self::Loop(block, _) => block.is_pure(),
Self::Block(x) => x.0.iter().all(Self::is_pure), Self::For(_, iterable, block, _) => iterable.is_pure() && block.is_pure(),
Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_) => false, Self::Let(_, _, _) | Self::Const(_, _, _) => false,
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_, _, _) => false,
Self::TryCatch(x) => (x.0).0.is_pure() && (x.2).0.is_pure(), Self::TryCatch(x) => (x.0).0.is_pure() && (x.2).0.is_pure(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_) => false, Self::Import(_, _, _) => false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Export(_) => false, Self::Export(_, _) => false,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(_) => false, Self::Share(_, _) => false,
} }
} }
} }
@ -2859,24 +2865,22 @@ fn parse_if(
ensure_not_statement_expr(input, "a boolean")?; ensure_not_statement_expr(input, "a boolean")?;
let guard = parse_expr(input, state, lib, settings.level_up())?; let guard = parse_expr(input, state, lib, settings.level_up())?;
ensure_not_assignment(input)?; ensure_not_assignment(input)?;
let if_body = parse_block(input, state, lib, settings.level_up())?; let if_body = Box::new(parse_block(input, state, lib, settings.level_up())?);
// if guard { if_body } else ... // if guard { if_body } else ...
let else_body = if match_token(input, Token::Else).0 { let else_body = if match_token(input, Token::Else).0 {
Some(if let (Token::If, _) = input.peek().unwrap() { Some(Box::new(if let (Token::If, _) = input.peek().unwrap() {
// if guard { if_body } else if ... // if guard { if_body } else if ...
parse_if(input, state, lib, settings.level_up())? parse_if(input, state, lib, settings.level_up())?
} else { } else {
// if guard { if_body } else { else-body } // if guard { if_body } else { else-body }
parse_block(input, state, lib, settings.level_up())? parse_block(input, state, lib, settings.level_up())?
}) }))
} else { } else {
None None
}; };
Ok(Stmt::IfThenElse(Box::new(( Ok(Stmt::IfThenElse(guard, if_body, else_body, token_pos))
guard, if_body, else_body, token_pos,
))))
} }
/// Parse a while loop. /// Parse a while loop.
@ -2899,9 +2903,9 @@ fn parse_while(
ensure_not_assignment(input)?; ensure_not_assignment(input)?;
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?; let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
Ok(Stmt::While(Box::new((guard, body, token_pos)))) Ok(Stmt::While(guard, body, token_pos))
} }
/// Parse a loop statement. /// Parse a loop statement.
@ -2920,9 +2924,9 @@ fn parse_loop(
// loop { body } // loop { body }
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?; let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
Ok(Stmt::Loop(Box::new((body, token_pos)))) Ok(Stmt::Loop(body, token_pos))
} }
/// Parse a for loop. /// Parse a for loop.
@ -2973,11 +2977,11 @@ fn parse_for(
state.stack.push((name.clone(), ScopeEntryType::Normal)); state.stack.push((name.clone(), ScopeEntryType::Normal));
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?; let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
state.stack.truncate(prev_stack_len); state.stack.truncate(prev_stack_len);
Ok(Stmt::For(Box::new((name, expr, body, token_pos)))) Ok(Stmt::For(Box::new(name), expr, body, token_pos))
} }
/// Parse a variable definition statement. /// Parse a variable definition statement.
@ -3017,12 +3021,12 @@ fn parse_let(
// let name = expr // let name = expr
ScopeEntryType::Normal => { ScopeEntryType::Normal => {
state.stack.push((name.clone(), ScopeEntryType::Normal)); state.stack.push((name.clone(), ScopeEntryType::Normal));
Ok(Stmt::Let(Box::new(((name, pos), init_value, token_pos)))) Ok(Stmt::Let(Box::new((name, pos)), init_value, token_pos))
} }
// const name = { expr:constant } // const name = { expr:constant }
ScopeEntryType::Constant => { ScopeEntryType::Constant => {
state.stack.push((name.clone(), ScopeEntryType::Constant)); state.stack.push((name.clone(), ScopeEntryType::Constant));
Ok(Stmt::Const(Box::new(((name, pos), init_value, token_pos)))) Ok(Stmt::Const(Box::new((name, pos)), init_value, token_pos))
} }
} }
} }
@ -3047,7 +3051,7 @@ fn parse_import(
// import expr as ... // import expr as ...
if !match_token(input, Token::As).0 { if !match_token(input, Token::As).0 {
return Ok(Stmt::Import(Box::new((expr, None, token_pos)))); return Ok(Stmt::Import(expr, None, token_pos));
} }
// import expr as name ... // import expr as name ...
@ -3062,11 +3066,11 @@ fn parse_import(
state.modules.push(name.clone()); state.modules.push(name.clone());
Ok(Stmt::Import(Box::new(( Ok(Stmt::Import(
expr, expr,
Some((name.into(), settings.pos)), Some((name.into(), settings.pos)),
token_pos, token_pos,
)))) ))
} }
/// Parse an export statement. /// Parse an export statement.
@ -3083,7 +3087,7 @@ fn parse_export(
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(_state.max_expr_depth)?; settings.ensure_level_within_max_limit(_state.max_expr_depth)?;
let mut exports = StaticVec::new(); let mut exports = Vec::new();
loop { loop {
let (id, id_pos) = match input.next().unwrap() { let (id, id_pos) = match input.next().unwrap() {
@ -3138,7 +3142,7 @@ fn parse_export(
}) })
.map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?; .map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?;
Ok(Stmt::Export(Box::new((exports, token_pos)))) Ok(Stmt::Export(exports, token_pos))
} }
/// Parse a statement block. /// Parse a statement block.
@ -3164,7 +3168,7 @@ fn parse_block(
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut statements = StaticVec::new(); let mut statements = Vec::new();
let prev_stack_len = state.stack.len(); let prev_stack_len = state.stack.len();
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -3217,7 +3221,7 @@ fn parse_block(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
state.modules.truncate(prev_mods_len); state.modules.truncate(prev_mods_len);
Ok(Stmt::Block(Box::new((statements, settings.pos)))) Ok(Stmt::Block(statements, settings.pos))
} }
/// Parse an expression as a statement. /// Parse an expression as a statement.
@ -3234,7 +3238,7 @@ fn parse_expr_stmt(
let expr = parse_expr(input, state, lib, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
let expr = parse_op_assignment_stmt(input, state, lib, expr, settings.level_up())?; let expr = parse_op_assignment_stmt(input, state, lib, expr, settings.level_up())?;
Ok(Stmt::Expr(Box::new(expr))) Ok(Stmt::Expr(expr))
} }
/// Parse a single statement. /// Parse a single statement.
@ -3345,26 +3349,26 @@ fn parse_stmt(
match input.peek().unwrap() { match input.peek().unwrap() {
// `return`/`throw` at <EOF> // `return`/`throw` at <EOF>
(Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal(Box::new(( (Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal(
(return_type, token_pos), (return_type, token_pos),
None, None,
*pos, *pos,
))))), ))),
// `return;` or `throw;` // `return;` or `throw;`
(Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal(Box::new(( (Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal(
(return_type, token_pos), (return_type, token_pos),
None, None,
settings.pos, settings.pos,
))))), ))),
// `return` or `throw` with expression // `return` or `throw` with expression
(_, _) => { (_, _) => {
let expr = parse_expr(input, state, lib, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
let pos = expr.position(); let pos = expr.position();
Ok(Some(Stmt::ReturnWithVal(Box::new(( Ok(Some(Stmt::ReturnWithVal(
(return_type, token_pos), (return_type, token_pos),
Some(expr), Some(expr),
pos, pos,
))))) )))
} }
} }
} }
@ -3598,12 +3602,16 @@ fn make_curry_from_externals(
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
{ {
// Statement block // Statement block
let mut statements: StaticVec<_> = Default::default(); let mut statements: Vec<_> = Default::default();
// Insert `Share` statements // Insert `Share` statements
statements.extend(externals.into_iter().map(|x| Stmt::Share(Box::new(x)))); statements.extend(
externals
.into_iter()
.map(|(var_name, pos)| Stmt::Share(var_name, pos)),
);
// Final expression // Final expression
statements.push(Stmt::Expr(Box::new(expr))); statements.push(Stmt::Expr(expr));
Expr::Stmt(Box::new((Stmt::Block(Box::new((statements, pos))), pos))) Expr::Stmt(Box::new((Stmt::Block(statements, pos), pos)))
} }
#[cfg(feature = "no_closure")] #[cfg(feature = "no_closure")]
@ -3781,7 +3789,7 @@ impl Engine {
} }
} }
let expr = vec![Stmt::Expr(Box::new(expr))]; let expr = vec![Stmt::Expr(expr)];
Ok( Ok(
// Optimize AST // Optimize AST
@ -3924,3 +3932,10 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
_ => None, _ => None,
} }
} }
#[test]
fn test() {
println!("{}", std::mem::size_of::<Position>());
println!("{}", std::mem::size_of::<Expr>());
println!("{}", std::mem::size_of::<Stmt>());
}