Expr::Stmt takes a statements block.
This commit is contained in:
parent
32f41c69bd
commit
2168fd5361
@ -908,7 +908,7 @@ pub enum Expr {
|
||||
/// Property access - (getter, setter), prop
|
||||
Property(Box<((String, String), IdentX)>),
|
||||
/// { stmt }
|
||||
Stmt(Box<Stmt>, Position),
|
||||
Stmt(Box<StaticVec<Stmt>>, Position),
|
||||
/// Wrapped expression - should not be optimized away.
|
||||
Expr(Box<Expr>),
|
||||
/// func(expr, ... )
|
||||
@ -1092,7 +1092,7 @@ impl Expr {
|
||||
x.lhs.is_pure() && x.rhs.is_pure()
|
||||
}
|
||||
|
||||
Self::Stmt(x, _) => x.is_pure(),
|
||||
Self::Stmt(x, _) => x.iter().all(Stmt::is_pure),
|
||||
|
||||
Self::Variable(_) => true,
|
||||
|
||||
|
@ -1579,7 +1579,9 @@ impl Engine {
|
||||
Expr::Property(_) => unreachable!(),
|
||||
|
||||
// Statement block
|
||||
Expr::Stmt(x, _) => self.eval_stmt(scope, mods, state, lib, this_ptr, x, level),
|
||||
Expr::Stmt(x, _) => {
|
||||
self.eval_statements(scope, mods, state, lib, this_ptr, x.as_ref(), level)
|
||||
}
|
||||
|
||||
// lhs[idx_expr]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -1706,6 +1708,37 @@ impl Engine {
|
||||
.map_err(|err| err.fill_position(expr.position()))
|
||||
}
|
||||
|
||||
pub(crate) fn eval_statements<'a>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
mods: &mut Imports,
|
||||
state: &mut State,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
statements: impl IntoIterator<Item = &'a Stmt>,
|
||||
level: usize,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let prev_scope_len = scope.len();
|
||||
let prev_mods_len = mods.len();
|
||||
state.scope_level += 1;
|
||||
|
||||
let result = statements
|
||||
.into_iter()
|
||||
.try_fold(Default::default(), |_, stmt| {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
||||
});
|
||||
|
||||
scope.rewind(prev_scope_len);
|
||||
mods.truncate(prev_mods_len);
|
||||
state.scope_level -= 1;
|
||||
|
||||
// The impact of an eval statement goes away at the end of a block
|
||||
// because any new variables introduced will go out of scope
|
||||
state.always_search = false;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Evaluate a statement
|
||||
///
|
||||
///
|
||||
@ -1886,23 +1919,7 @@ impl Engine {
|
||||
|
||||
// Block scope
|
||||
Stmt::Block(statements, _) => {
|
||||
let prev_scope_len = scope.len();
|
||||
let prev_mods_len = mods.len();
|
||||
state.scope_level += 1;
|
||||
|
||||
let result = statements.iter().try_fold(Default::default(), |_, stmt| {
|
||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
||||
});
|
||||
|
||||
scope.rewind(prev_scope_len);
|
||||
mods.truncate(prev_mods_len);
|
||||
state.scope_level -= 1;
|
||||
|
||||
// The impact of an eval statement goes away at the end of a block
|
||||
// because any new variables introduced will go out of scope
|
||||
state.always_search = false;
|
||||
|
||||
result
|
||||
self.eval_statements(scope, mods, state, lib, this_ptr, statements, level)
|
||||
}
|
||||
|
||||
// If-else statement
|
||||
|
@ -1405,7 +1405,7 @@ impl Engine {
|
||||
mods: &mut Imports,
|
||||
ast: &'a AST,
|
||||
) -> Result<(Dynamic, u64), Box<EvalAltResult>> {
|
||||
self.eval_statements(scope, mods, ast.statements(), &[ast.lib()])
|
||||
self.eval_statements_raw(scope, mods, ast.statements(), &[ast.lib()])
|
||||
}
|
||||
|
||||
/// Evaluate a file, but throw away the result and only return error (if any).
|
||||
@ -1467,7 +1467,7 @@ impl Engine {
|
||||
ast: &AST,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
let mut mods = Default::default();
|
||||
self.eval_statements(scope, &mut mods, ast.statements(), &[ast.lib()])
|
||||
self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()])
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
|
@ -604,9 +604,10 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate a list of statements.
|
||||
/// Evaluate a list of statements with an empty state and no `this` pointer.
|
||||
/// This is commonly used to evaluate a list of statements in an `AST` or a script function body.
|
||||
#[inline]
|
||||
pub(crate) fn eval_statements<'a>(
|
||||
pub(crate) fn eval_statements_raw<'a>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
mods: &mut Imports,
|
||||
@ -667,7 +668,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Evaluate the AST
|
||||
let (result, operations) = self.eval_statements(scope, mods, ast.statements(), lib)?;
|
||||
let (result, operations) = self.eval_statements_raw(scope, mods, ast.statements(), lib)?;
|
||||
|
||||
state.operations += operations;
|
||||
self.inc_operations(state)?;
|
||||
|
@ -409,9 +409,14 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
||||
)))
|
||||
}
|
||||
// expr;
|
||||
Stmt::Expr(Expr::Stmt(x, _)) if matches!(*x, Stmt::Expr(_)) => {
|
||||
Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => {
|
||||
state.set_dirty();
|
||||
optimize_stmt(*x, state, preserve_result)
|
||||
Stmt::Noop(pos)
|
||||
}
|
||||
// expr;
|
||||
Stmt::Expr(Expr::Stmt(mut x, _)) if x.len() == 1 => {
|
||||
state.set_dirty();
|
||||
optimize_stmt(x.remove(0), state, preserve_result)
|
||||
}
|
||||
// expr;
|
||||
Stmt::Expr(expr) => Stmt::Expr(optimize_expr(expr, state)),
|
||||
@ -438,8 +443,13 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
match expr {
|
||||
// expr - do not promote because there is a reason it is wrapped in an `Expr::Expr`
|
||||
Expr::Expr(x) => Expr::Expr(Box::new(optimize_expr(*x, state))),
|
||||
// {}
|
||||
Expr::Stmt(x, pos) if x.is_empty() => {
|
||||
state.set_dirty();
|
||||
Expr::Unit(pos)
|
||||
}
|
||||
// { stmt }
|
||||
Expr::Stmt(x, pos) => match *x {
|
||||
Expr::Stmt(mut x, pos) if x.len() == 1 => match x.remove(0) {
|
||||
// {} -> ()
|
||||
Stmt::Noop(_) => {
|
||||
state.set_dirty();
|
||||
@ -451,8 +461,12 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
optimize_expr(expr, state)
|
||||
}
|
||||
// { stmt }
|
||||
stmt => Expr::Stmt(Box::new(optimize_stmt(stmt, state, true)), pos),
|
||||
},
|
||||
stmt => Expr::Stmt(Box::new(vec![optimize_stmt(stmt, state, true)].into()), pos)
|
||||
}
|
||||
// { stmt; ... }
|
||||
Expr::Stmt(x, pos) => Expr::Stmt(Box::new(
|
||||
x.into_iter().map(|stmt| optimize_stmt(stmt, state, true)).collect(),
|
||||
), pos),
|
||||
|
||||
// lhs.rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
|
@ -765,8 +765,10 @@ fn parse_primary(
|
||||
let (token, _) = match token {
|
||||
// { - block statement as expression
|
||||
Token::LeftBrace if settings.allow_stmt_expr => {
|
||||
return parse_block(input, state, lib, settings.level_up())
|
||||
.map(|block| Expr::Stmt(Box::new(block), settings.pos))
|
||||
return parse_block(input, state, lib, settings.level_up()).map(|block| match block {
|
||||
Stmt::Block(statements, pos) => Expr::Stmt(Box::new(statements.into()), pos),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
||||
_ => input.next().unwrap(),
|
||||
@ -962,10 +964,11 @@ fn parse_unary(
|
||||
|
||||
match token {
|
||||
// If statement is allowed to act as expressions
|
||||
Token::If if settings.allow_if_expr => Ok(Expr::Stmt(
|
||||
Box::new(parse_if(input, state, lib, settings.level_up())?),
|
||||
settings.pos,
|
||||
)),
|
||||
Token::If if settings.allow_if_expr => {
|
||||
let mut block: StaticVec<_> = Default::default();
|
||||
block.push(parse_if(input, state, lib, settings.level_up())?);
|
||||
Ok(Expr::Stmt(Box::new(block), settings.pos))
|
||||
}
|
||||
// -expr
|
||||
Token::UnaryMinus => {
|
||||
let pos = eat_token(input, Token::UnaryMinus);
|
||||
@ -1657,12 +1660,13 @@ fn parse_custom_syntax(
|
||||
exprs.push(parse_expr(input, state, lib, settings)?);
|
||||
segments.push(MARKER_EXPR.into());
|
||||
}
|
||||
MARKER_BLOCK => {
|
||||
let stmt = parse_block(input, state, lib, settings)?;
|
||||
let pos = stmt.position();
|
||||
exprs.push(Expr::Stmt(Box::new(stmt), pos));
|
||||
segments.push(MARKER_BLOCK.into());
|
||||
}
|
||||
MARKER_BLOCK => match parse_block(input, state, lib, settings)? {
|
||||
Stmt::Block(statements, pos) => {
|
||||
exprs.push(Expr::Stmt(Box::new(statements.into()), pos));
|
||||
segments.push(MARKER_BLOCK.into());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
s => match input.next().unwrap() {
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(t, _) if t.syntax().as_ref() == s => {
|
||||
@ -2525,12 +2529,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
{
|
||||
// Statement block
|
||||
let mut statements: Vec<_> = Default::default();
|
||||
let mut statements: StaticVec<_> = Default::default();
|
||||
// Insert `Share` statements
|
||||
statements.extend(externals.into_iter().map(|x| Stmt::Share(Box::new(x))));
|
||||
// Final expression
|
||||
statements.push(Stmt::Expr(expr));
|
||||
Expr::Stmt(Box::new(Stmt::Block(statements, pos)), pos)
|
||||
Expr::Stmt(Box::new(statements), pos)
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_closure")]
|
||||
|
Loading…
Reference in New Issue
Block a user