Pack Stmt structure.
This commit is contained in:
parent
01663a6581
commit
93b5df6b3c
@ -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);
|
||||||
|
286
src/optimize.rs
286
src/optimize.rs
@ -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,
|
||||||
|
207
src/parser.rs
207
src/parser.rs
@ -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>());
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user