Encapsulate into FlowControl type.
This commit is contained in:
parent
80ccd75514
commit
cb09393dba
@ -22,8 +22,8 @@ pub use namespace_none::Namespace;
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use script_fn::{ScriptFnDef, ScriptFnMetadata};
|
pub use script_fn::{ScriptFnDef, ScriptFnMetadata};
|
||||||
pub use stmt::{
|
pub use stmt::{
|
||||||
CaseBlocksList, ConditionalExpr, OpAssignment, RangeCase, Stmt, StmtBlock, StmtBlockContainer,
|
CaseBlocksList, ConditionalExpr, FlowControl, OpAssignment, RangeCase, Stmt, StmtBlock,
|
||||||
SwitchCasesCollection, TryCatchBlock,
|
StmtBlockContainer, SwitchCasesCollection,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// _(internals)_ Placeholder for a script-defined function.
|
/// _(internals)_ Placeholder for a script-defined function.
|
||||||
|
@ -292,18 +292,6 @@ pub struct SwitchCasesCollection {
|
|||||||
pub def_case: Option<usize>,
|
pub def_case: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ A `try-catch` block.
|
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
#[derive(Debug, Clone, Hash)]
|
|
||||||
pub struct TryCatchBlock {
|
|
||||||
/// `try` block.
|
|
||||||
pub try_block: StmtBlock,
|
|
||||||
/// `catch` variable, if any.
|
|
||||||
pub catch_var: Ident,
|
|
||||||
/// `catch` block.
|
|
||||||
pub catch_block: StmtBlock,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Number of items to keep inline for [`StmtBlockContainer`].
|
/// Number of items to keep inline for [`StmtBlockContainer`].
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
const STMT_BLOCK_INLINE_SIZE: usize = 8;
|
const STMT_BLOCK_INLINE_SIZE: usize = 8;
|
||||||
@ -532,6 +520,22 @@ impl Extend<Stmt> for StmtBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// _(internals)_ A flow control block containing:
|
||||||
|
/// * an expression,
|
||||||
|
/// * a statements body
|
||||||
|
/// * an alternate statements body
|
||||||
|
///
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub struct FlowControl {
|
||||||
|
/// Flow control expression.
|
||||||
|
pub expr: Expr,
|
||||||
|
/// Main body.
|
||||||
|
pub body: StmtBlock,
|
||||||
|
/// Branch body.
|
||||||
|
pub branch: StmtBlock,
|
||||||
|
}
|
||||||
|
|
||||||
/// _(internals)_ A statement.
|
/// _(internals)_ A statement.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
@ -540,7 +544,7 @@ pub enum Stmt {
|
|||||||
/// No-op.
|
/// No-op.
|
||||||
Noop(Position),
|
Noop(Position),
|
||||||
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
||||||
If(Box<(Expr, StmtBlock, StmtBlock)>, Position),
|
If(Box<FlowControl>, Position),
|
||||||
/// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}`
|
/// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}`
|
||||||
///
|
///
|
||||||
/// ### Data Structure
|
/// ### Data Structure
|
||||||
@ -552,16 +556,16 @@ pub enum Stmt {
|
|||||||
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
|
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
|
||||||
///
|
///
|
||||||
/// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement.
|
/// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement.
|
||||||
While(Box<(Expr, StmtBlock)>, Position),
|
While(Box<FlowControl>, Position),
|
||||||
/// `do` `{` stmt `}` `while`|`until` expr
|
/// `do` `{` stmt `}` `while`|`until` expr
|
||||||
///
|
///
|
||||||
/// ### Flags
|
/// ### Flags
|
||||||
///
|
///
|
||||||
/// * [`NONE`][ASTFlags::NONE] = `while`
|
/// * [`NONE`][ASTFlags::NONE] = `while`
|
||||||
/// * [`NEGATED`][ASTFlags::NEGATED] = `until`
|
/// * [`NEGATED`][ASTFlags::NEGATED] = `until`
|
||||||
Do(Box<(Expr, StmtBlock)>, ASTFlags, Position),
|
Do(Box<FlowControl>, ASTFlags, Position),
|
||||||
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
|
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
|
||||||
For(Box<(Ident, Ident, Expr, StmtBlock)>, Position),
|
For(Box<(Ident, Ident, FlowControl)>, Position),
|
||||||
/// \[`export`\] `let`|`const` id `=` expr
|
/// \[`export`\] `let`|`const` id `=` expr
|
||||||
///
|
///
|
||||||
/// ### Flags
|
/// ### Flags
|
||||||
@ -579,7 +583,7 @@ pub enum Stmt {
|
|||||||
/// `{` stmt`;` ... `}`
|
/// `{` stmt`;` ... `}`
|
||||||
Block(Box<StmtBlock>),
|
Block(Box<StmtBlock>),
|
||||||
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
||||||
TryCatch(Box<TryCatchBlock>, Position),
|
TryCatch(Box<FlowControl>, Position),
|
||||||
/// [expression][Expr]
|
/// [expression][Expr]
|
||||||
Expr(Box<Expr>),
|
Expr(Box<Expr>),
|
||||||
/// `continue`/`break` expr
|
/// `continue`/`break` expr
|
||||||
@ -815,7 +819,9 @@ impl Stmt {
|
|||||||
Self::Noop(..) => true,
|
Self::Noop(..) => true,
|
||||||
Self::Expr(expr) => expr.is_pure(),
|
Self::Expr(expr) => expr.is_pure(),
|
||||||
Self::If(x, ..) => {
|
Self::If(x, ..) => {
|
||||||
x.0.is_pure() && x.1.iter().all(Self::is_pure) && x.2.iter().all(Self::is_pure)
|
x.expr.is_pure()
|
||||||
|
&& x.body.iter().all(Self::is_pure)
|
||||||
|
&& x.branch.iter().all(Self::is_pure)
|
||||||
}
|
}
|
||||||
Self::Switch(x, ..) => {
|
Self::Switch(x, ..) => {
|
||||||
let (expr, sw) = &**x;
|
let (expr, sw) = &**x;
|
||||||
@ -833,10 +839,10 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Loops that exit can be pure because it can never be infinite.
|
// Loops that exit can be pure because it can never be infinite.
|
||||||
Self::While(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => true,
|
Self::While(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => true,
|
||||||
Self::Do(x, options, ..) if matches!(x.0, Expr::BoolConstant(..)) => match x.0 {
|
Self::Do(x, options, ..) if matches!(x.expr, Expr::BoolConstant(..)) => match x.expr {
|
||||||
Expr::BoolConstant(cond, ..) if cond == options.contains(ASTFlags::NEGATED) => {
|
Expr::BoolConstant(cond, ..) if cond == options.contains(ASTFlags::NEGATED) => {
|
||||||
x.1.iter().all(Self::is_pure)
|
x.body.iter().all(Self::is_pure)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
@ -846,13 +852,15 @@ impl Stmt {
|
|||||||
|
|
||||||
// For loops can be pure because if the iterable is pure, it is finite,
|
// For loops can be pure because if the iterable is pure, it is finite,
|
||||||
// so infinite loops can never occur.
|
// so infinite loops can never occur.
|
||||||
Self::For(x, ..) => x.2.is_pure() && x.3.iter().all(Self::is_pure),
|
Self::For(x, ..) => x.2.expr.is_pure() && x.2.body.iter().all(Self::is_pure),
|
||||||
|
|
||||||
Self::Var(..) | Self::Assignment(..) | Self::FnCall(..) => false,
|
Self::Var(..) | Self::Assignment(..) | Self::FnCall(..) => false,
|
||||||
Self::Block(block, ..) => block.iter().all(Self::is_pure),
|
Self::Block(block, ..) => block.iter().all(Self::is_pure),
|
||||||
Self::BreakLoop(..) | Self::Return(..) => false,
|
Self::BreakLoop(..) | Self::Return(..) => false,
|
||||||
Self::TryCatch(x, ..) => {
|
Self::TryCatch(x, ..) => {
|
||||||
x.try_block.iter().all(Self::is_pure) && x.catch_block.iter().all(Self::is_pure)
|
x.expr.is_pure()
|
||||||
|
&& x.body.iter().all(Self::is_pure)
|
||||||
|
&& x.branch.iter().all(Self::is_pure)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -947,15 +955,15 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::If(x, ..) => {
|
Self::If(x, ..) => {
|
||||||
if !x.0.walk(path, on_node) {
|
if !x.expr.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for s in &x.1 {
|
for s in &x.body {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for s in &x.2 {
|
for s in &x.branch {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -996,20 +1004,20 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::While(x, ..) | Self::Do(x, ..) => {
|
Self::While(x, ..) | Self::Do(x, ..) => {
|
||||||
if !x.0.walk(path, on_node) {
|
if !x.expr.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for s in x.1.statements() {
|
for s in x.body.statements() {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::For(x, ..) => {
|
Self::For(x, ..) => {
|
||||||
if !x.2.walk(path, on_node) {
|
if !x.2.expr.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for s in &x.3 {
|
for s in &x.2.body {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1038,12 +1046,12 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::TryCatch(x, ..) => {
|
Self::TryCatch(x, ..) => {
|
||||||
for s in &x.try_block {
|
for s in &x.body {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for s in &x.catch_block {
|
for s in &x.branch {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
||||||
use crate::api::events::VarDefInfo;
|
use crate::api::events::VarDefInfo;
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
ASTFlags, BinaryExpr, Expr, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock,
|
ASTFlags, BinaryExpr, Expr, FlowControl, OpAssignment, Stmt, SwitchCasesCollection,
|
||||||
};
|
};
|
||||||
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
|
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
|
||||||
use crate::types::dynamic::{AccessMode, Union};
|
use crate::types::dynamic::{AccessMode, Union};
|
||||||
@ -310,7 +310,11 @@ impl Engine {
|
|||||||
|
|
||||||
// If statement
|
// If statement
|
||||||
Stmt::If(x, ..) => {
|
Stmt::If(x, ..) => {
|
||||||
let (expr, if_block, else_block) = &**x;
|
let FlowControl {
|
||||||
|
expr,
|
||||||
|
body: if_block,
|
||||||
|
branch: else_block,
|
||||||
|
} = &**x;
|
||||||
|
|
||||||
let guard_val = self
|
let guard_val = self
|
||||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||||
@ -402,8 +406,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Loop
|
// Loop
|
||||||
Stmt::While(x, ..) if matches!(x.0, Expr::Unit(..) | Expr::BoolConstant(true, ..)) => {
|
Stmt::While(x, ..)
|
||||||
let (.., body) = &**x;
|
if matches!(x.expr, Expr::Unit(..) | Expr::BoolConstant(true, ..)) =>
|
||||||
|
{
|
||||||
|
let FlowControl { body, .. } = &**x;
|
||||||
|
|
||||||
if body.is_empty() {
|
if body.is_empty() {
|
||||||
loop {
|
loop {
|
||||||
@ -427,7 +433,7 @@ impl Engine {
|
|||||||
|
|
||||||
// While loop
|
// While loop
|
||||||
Stmt::While(x, ..) => {
|
Stmt::While(x, ..) => {
|
||||||
let (expr, body) = &**x;
|
let FlowControl { expr, body, .. } = &**x;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let condition = self
|
let condition = self
|
||||||
@ -458,7 +464,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Do loop
|
// Do loop
|
||||||
Stmt::Do(x, options, ..) => {
|
Stmt::Do(x, options, ..) => {
|
||||||
let (expr, body) = &**x;
|
let FlowControl { expr, body, .. } = &**x;
|
||||||
let is_while = !options.contains(ASTFlags::NEGATED);
|
let is_while = !options.contains(ASTFlags::NEGATED);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -488,7 +494,7 @@ impl Engine {
|
|||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(x, ..) => {
|
Stmt::For(x, ..) => {
|
||||||
let (var_name, counter, expr, statements) = &**x;
|
let (var_name, counter, FlowControl { expr, body, .. }) = &**x;
|
||||||
|
|
||||||
let iter_obj = self
|
let iter_obj = self
|
||||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||||
@ -566,15 +572,15 @@ impl Engine {
|
|||||||
*scope.get_mut_by_index(index).write_lock().unwrap() = value;
|
*scope.get_mut_by_index(index).write_lock().unwrap() = value;
|
||||||
|
|
||||||
// Run block
|
// Run block
|
||||||
self.track_operation(global, statements.position())?;
|
self.track_operation(global, body.position())?;
|
||||||
|
|
||||||
if statements.is_empty() {
|
if body.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_ptr = this_ptr.as_deref_mut();
|
let this_ptr = this_ptr.as_deref_mut();
|
||||||
|
|
||||||
match self.eval_stmt_block(global, caches, scope, this_ptr, statements, true) {
|
match self.eval_stmt_block(global, caches, scope, this_ptr, body, true) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
ERR::LoopBreak(false, ..) => (),
|
ERR::LoopBreak(false, ..) => (),
|
||||||
@ -605,10 +611,10 @@ impl Engine {
|
|||||||
|
|
||||||
// Try/Catch statement
|
// Try/Catch statement
|
||||||
Stmt::TryCatch(x, ..) => {
|
Stmt::TryCatch(x, ..) => {
|
||||||
let TryCatchBlock {
|
let FlowControl {
|
||||||
try_block,
|
body: try_block,
|
||||||
catch_var,
|
expr: catch_var,
|
||||||
catch_block,
|
branch: catch_block,
|
||||||
} = &**x;
|
} = &**x;
|
||||||
|
|
||||||
match self.eval_stmt_block(
|
match self.eval_stmt_block(
|
||||||
@ -659,10 +665,10 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Restore scope at end of block
|
// Restore scope at end of block
|
||||||
auto_restore! { scope if !catch_var.is_empty() => rewind; let orig_scope_len = scope.len(); }
|
auto_restore! { scope if !catch_var.is_unit() => rewind; let orig_scope_len = scope.len(); }
|
||||||
|
|
||||||
if !catch_var.is_empty() {
|
if let Expr::Variable(x, ..) = catch_var {
|
||||||
scope.push(catch_var.name.clone(), err_value);
|
scope.push(x.3.clone(), err_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_ptr = this_ptr.as_deref_mut();
|
let this_ptr = this_ptr.as_deref_mut();
|
||||||
|
@ -336,8 +336,8 @@ pub use parser::ParseState;
|
|||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub use ast::{
|
pub use ast::{
|
||||||
ASTFlags, ASTNode, BinaryExpr, ConditionalExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
ASTFlags, ASTNode, BinaryExpr, ConditionalExpr, Expr, FlowControl, FnCallExpr, FnCallHashes,
|
||||||
OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, SwitchCasesCollection, TryCatchBlock,
|
Ident, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, SwitchCasesCollection,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -12,8 +12,8 @@ use crate::module::ModuleFlags;
|
|||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, Identifier, ImmutableString, Position,
|
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FlowControl, FnPtr, Identifier,
|
||||||
Scope, StaticVec, AST,
|
ImmutableString, Position, Scope, StaticVec, AST,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -430,8 +430,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if expr {}
|
// if expr {}
|
||||||
Stmt::If(x, ..) if x.1.is_empty() && x.2.is_empty() => {
|
Stmt::If(x, ..) if x.body.is_empty() && x.branch.is_empty() => {
|
||||||
let condition = &mut x.0;
|
let condition = &mut x.expr;
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
let pos = condition.start_position();
|
let pos = condition.start_position();
|
||||||
@ -452,8 +452,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// if false { if_block } -> Noop
|
// if false { if_block } -> Noop
|
||||||
Stmt::If(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) && x.2.is_empty() => {
|
Stmt::If(x, ..)
|
||||||
if let Expr::BoolConstant(false, pos) = x.0 {
|
if matches!(x.expr, Expr::BoolConstant(false, ..)) && x.branch.is_empty() =>
|
||||||
|
{
|
||||||
|
if let Expr::BoolConstant(false, pos) = x.expr {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(pos);
|
*stmt = Stmt::Noop(pos);
|
||||||
} else {
|
} else {
|
||||||
@ -461,33 +463,31 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if false { if_block } else { else_block } -> else_block
|
// if false { if_block } else { else_block } -> else_block
|
||||||
Stmt::If(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => {
|
Stmt::If(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt =
|
let body = mem::take(&mut *x.branch);
|
||||||
match optimize_stmt_block(mem::take(&mut *x.2), state, preserve_result, true, false)
|
*stmt = match optimize_stmt_block(body, state, preserve_result, true, false) {
|
||||||
{
|
statements if statements.is_empty() => Stmt::Noop(x.branch.position()),
|
||||||
statements if statements.is_empty() => Stmt::Noop(x.2.position()),
|
statements => (statements, x.branch.span()).into(),
|
||||||
statements => (statements, x.2.span()).into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if true { if_block } else { else_block } -> if_block
|
// if true { if_block } else { else_block } -> if_block
|
||||||
Stmt::If(x, ..) if matches!(x.0, Expr::BoolConstant(true, ..)) => {
|
Stmt::If(x, ..) if matches!(x.expr, Expr::BoolConstant(true, ..)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt =
|
let body = mem::take(&mut *x.body);
|
||||||
match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false)
|
*stmt = match optimize_stmt_block(body, state, preserve_result, true, false) {
|
||||||
{
|
statements if statements.is_empty() => Stmt::Noop(x.body.position()),
|
||||||
statements if statements.is_empty() => Stmt::Noop(x.1.position()),
|
statements => (statements, x.body.span()).into(),
|
||||||
statements => (statements, x.1.span()).into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if expr { if_block } else { else_block }
|
// if expr { if_block } else { else_block }
|
||||||
Stmt::If(x, ..) => {
|
Stmt::If(x, ..) => {
|
||||||
let (condition, body, other) = &mut **x;
|
let FlowControl { expr, body, branch } = &mut **x;
|
||||||
optimize_expr(condition, state, false);
|
optimize_expr(expr, state, false);
|
||||||
**body =
|
let statements = mem::take(&mut **body);
|
||||||
optimize_stmt_block(mem::take(&mut **body), state, preserve_result, true, false);
|
**body = optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||||
**other =
|
let statements = mem::take(&mut **branch);
|
||||||
optimize_stmt_block(mem::take(&mut **other), state, preserve_result, true, false);
|
**branch = optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch const { ... }
|
// switch const { ... }
|
||||||
@ -524,7 +524,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
|
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||||
optimize_expr(&mut b.condition, state, false);
|
optimize_expr(&mut b.condition, state, false);
|
||||||
|
|
||||||
let else_stmt = match def_case {
|
let branch = match def_case {
|
||||||
Some(index) => {
|
Some(index) => {
|
||||||
let mut def_stmt =
|
let mut def_stmt =
|
||||||
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||||
@ -533,14 +533,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
_ => StmtBlock::NONE,
|
_ => StmtBlock::NONE,
|
||||||
};
|
};
|
||||||
|
let body = Stmt::Expr(mem::take(&mut b.expr).into()).into();
|
||||||
|
let expr = mem::take(&mut b.condition);
|
||||||
|
|
||||||
*stmt = Stmt::If(
|
*stmt = Stmt::If(
|
||||||
(
|
FlowControl { expr, body, branch }.into(),
|
||||||
mem::take(&mut b.condition),
|
|
||||||
Stmt::Expr(mem::take(&mut b.expr).into()).into(),
|
|
||||||
else_stmt,
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
match_expr.start_position(),
|
match_expr.start_position(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -585,12 +582,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
optimize_stmt(&mut statements, state, true);
|
optimize_stmt(&mut statements, state, true);
|
||||||
*stmt = statements;
|
*stmt = statements;
|
||||||
} else {
|
} else {
|
||||||
let mut condition = mem::take(&mut range_block.condition);
|
let mut expr = mem::take(&mut range_block.condition);
|
||||||
|
|
||||||
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
|
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||||
optimize_expr(&mut condition, state, false);
|
optimize_expr(&mut expr, state, false);
|
||||||
|
|
||||||
let else_stmt = match def_case {
|
let branch = match def_case {
|
||||||
Some(index) => {
|
Some(index) => {
|
||||||
let mut def_stmt =
|
let mut def_stmt =
|
||||||
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||||
@ -600,12 +597,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
_ => StmtBlock::NONE,
|
_ => StmtBlock::NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let if_stmt =
|
let body =
|
||||||
Stmt::Expr(mem::take(&mut expressions[r.index()].expr).into())
|
Stmt::Expr(mem::take(&mut expressions[r.index()].expr).into())
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
*stmt = Stmt::If(
|
*stmt = Stmt::If(
|
||||||
(condition, if_stmt, else_stmt).into(),
|
FlowControl { expr, body, branch }.into(),
|
||||||
match_expr.start_position(),
|
match_expr.start_position(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -738,7 +735,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// while false { block } -> Noop
|
// while false { block } -> Noop
|
||||||
Stmt::While(x, ..) if matches!(x.0, Expr::BoolConstant(false, ..)) => match x.0 {
|
Stmt::While(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => match x.expr {
|
||||||
Expr::BoolConstant(false, pos) => {
|
Expr::BoolConstant(false, pos) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(pos);
|
*stmt = Stmt::Noop(pos);
|
||||||
@ -747,22 +744,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
},
|
},
|
||||||
// while expr { block }
|
// while expr { block }
|
||||||
Stmt::While(x, ..) => {
|
Stmt::While(x, ..) => {
|
||||||
let (condition, body) = &mut **x;
|
let FlowControl { expr, body, .. } = &mut **x;
|
||||||
optimize_expr(condition, state, false);
|
optimize_expr(expr, state, false);
|
||||||
if let Expr::BoolConstant(true, pos) = condition {
|
if let Expr::BoolConstant(true, pos) = expr {
|
||||||
*condition = Expr::Unit(*pos);
|
*expr = Expr::Unit(*pos);
|
||||||
}
|
}
|
||||||
**body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
|
**body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
|
||||||
}
|
}
|
||||||
// do { block } while|until expr
|
// do { block } while|until expr
|
||||||
Stmt::Do(x, ..) => {
|
Stmt::Do(x, ..) => {
|
||||||
optimize_expr(&mut x.0, state, false);
|
optimize_expr(&mut x.expr, state, false);
|
||||||
*x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false);
|
*x.body = optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false);
|
||||||
}
|
}
|
||||||
// for id in expr { block }
|
// for id in expr { block }
|
||||||
Stmt::For(x, ..) => {
|
Stmt::For(x, ..) => {
|
||||||
optimize_expr(&mut x.2, state, false);
|
optimize_expr(&mut x.2.expr, state, false);
|
||||||
*x.3 = optimize_stmt_block(mem::take(&mut *x.3), state, false, true, false);
|
*x.2.body = optimize_stmt_block(mem::take(&mut *x.2.body), state, false, true, false);
|
||||||
}
|
}
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Var(x, options, ..) if !options.contains(ASTFlags::CONSTANT) => {
|
Stmt::Var(x, options, ..) if !options.contains(ASTFlags::CONSTANT) => {
|
||||||
@ -791,21 +788,19 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// try { pure try_block } catch ( var ) { catch_block } -> try_block
|
// try { pure try_block } catch ( var ) { catch_block } -> try_block
|
||||||
Stmt::TryCatch(x, ..) if x.try_block.iter().all(Stmt::is_pure) => {
|
Stmt::TryCatch(x, ..) if x.body.iter().all(Stmt::is_pure) => {
|
||||||
// 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();
|
||||||
*stmt = (
|
*stmt = (
|
||||||
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false),
|
optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false),
|
||||||
x.try_block.span(),
|
x.body.span(),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
// try { try_block } catch ( var ) { catch_block }
|
// try { try_block } catch ( var ) { catch_block }
|
||||||
Stmt::TryCatch(x, ..) => {
|
Stmt::TryCatch(x, ..) => {
|
||||||
*x.try_block =
|
*x.body = optimize_stmt_block(mem::take(&mut *x.body), state, false, true, false);
|
||||||
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false);
|
*x.branch = optimize_stmt_block(mem::take(&mut *x.branch), state, false, true, false);
|
||||||
*x.catch_block =
|
|
||||||
optimize_stmt_block(mem::take(&mut *x.catch_block), state, false, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// expr(stmt)
|
// expr(stmt)
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
use crate::api::events::VarDefInfo;
|
use crate::api::events::VarDefInfo;
|
||||||
use crate::api::options::LangOptions;
|
use crate::api::options::LangOptions;
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
ASTFlags, BinaryExpr, CaseBlocksList, ConditionalExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
ASTFlags, BinaryExpr, CaseBlocksList, ConditionalExpr, Expr, FlowControl, FnCallExpr,
|
||||||
Namespace, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, StmtBlockContainer,
|
FnCallHashes, Ident, Namespace, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock,
|
||||||
SwitchCasesCollection, TryCatchBlock,
|
StmtBlockContainer, SwitchCasesCollection,
|
||||||
};
|
};
|
||||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||||
use crate::eval::{Caches, GlobalRuntimeState};
|
use crate::eval::{Caches, GlobalRuntimeState};
|
||||||
@ -2649,14 +2649,14 @@ impl Engine {
|
|||||||
|
|
||||||
// if guard { if_body }
|
// if guard { if_body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = self
|
let expr = self
|
||||||
.parse_expr(input, state, lib, settings)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
let if_body = self.parse_block(input, state, lib, settings)?;
|
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||||
|
|
||||||
// if guard { if_body } else ...
|
// if guard { if_body } else ...
|
||||||
let else_body = if match_token(input, Token::Else).0 {
|
let branch = if match_token(input, Token::Else).0 {
|
||||||
if let (Token::If, ..) = input.peek().expect(NEVER_ENDS) {
|
if let (Token::If, ..) = input.peek().expect(NEVER_ENDS) {
|
||||||
// if guard { if_body } else if ...
|
// if guard { if_body } else if ...
|
||||||
self.parse_if(input, state, lib, settings)?
|
self.parse_if(input, state, lib, settings)?
|
||||||
@ -2666,10 +2666,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Stmt::Noop(Position::NONE)
|
Stmt::Noop(Position::NONE)
|
||||||
};
|
}
|
||||||
|
.into();
|
||||||
|
|
||||||
Ok(Stmt::If(
|
Ok(Stmt::If(
|
||||||
(guard, if_body.into(), else_body.into()).into(),
|
FlowControl { expr, body, branch }.into(),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -2685,7 +2686,7 @@ impl Engine {
|
|||||||
let mut settings = settings.level_up()?;
|
let mut settings = settings.level_up()?;
|
||||||
|
|
||||||
// while|loops ...
|
// while|loops ...
|
||||||
let (guard, token_pos) = match input.next().expect(NEVER_ENDS) {
|
let (expr, token_pos) = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::While, pos) => {
|
(Token::While, pos) => {
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let expr = self
|
let expr = self
|
||||||
@ -2700,9 +2701,13 @@ impl Engine {
|
|||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
|
|
||||||
let body = self.parse_block(input, state, lib, settings)?;
|
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||||
|
let branch = StmtBlock::NONE;
|
||||||
|
|
||||||
Ok(Stmt::While((guard, body.into()).into(), settings.pos))
|
Ok(Stmt::While(
|
||||||
|
FlowControl { expr, body, branch }.into(),
|
||||||
|
settings.pos,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a do loop.
|
/// Parse a do loop.
|
||||||
@ -2722,7 +2727,7 @@ impl Engine {
|
|||||||
|
|
||||||
// do { body } [while|until] guard
|
// do { body } [while|until] guard
|
||||||
|
|
||||||
let body = self.parse_block(input, state, lib, settings)?;
|
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||||
|
|
||||||
let negated = match input.next().expect(NEVER_ENDS) {
|
let negated = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::While, ..) => ASTFlags::NONE,
|
(Token::While, ..) => ASTFlags::NONE,
|
||||||
@ -2740,12 +2745,18 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = self
|
let expr = self
|
||||||
.parse_expr(input, state, lib, settings)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
|
|
||||||
Ok(Stmt::Do((guard, body.into()).into(), negated, settings.pos))
|
let branch = StmtBlock::NONE;
|
||||||
|
|
||||||
|
Ok(Stmt::Do(
|
||||||
|
FlowControl { expr, body, branch }.into(),
|
||||||
|
negated,
|
||||||
|
settings.pos,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a for loop.
|
/// Parse a for loop.
|
||||||
@ -2837,12 +2848,14 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
let body = self.parse_block(input, state, lib, settings)?;
|
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||||
|
|
||||||
state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
|
state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
|
||||||
|
|
||||||
|
let branch = StmtBlock::NONE;
|
||||||
|
|
||||||
Ok(Stmt::For(
|
Ok(Stmt::For(
|
||||||
Box::new((loop_var, counter_var, expr, body.into())),
|
Box::new((loop_var, counter_var, FlowControl { expr, body, branch })),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -3477,7 +3490,7 @@ impl Engine {
|
|||||||
settings.pos = eat_token(input, Token::Try);
|
settings.pos = eat_token(input, Token::Try);
|
||||||
|
|
||||||
// try { try_block }
|
// try { try_block }
|
||||||
let try_block = self.parse_block(input, state, lib, settings)?;
|
let body = self.parse_block(input, state, lib, settings)?.into();
|
||||||
|
|
||||||
// try { try_block } catch
|
// try { try_block } catch
|
||||||
let (matched, catch_pos) = match_token(input, Token::Catch);
|
let (matched, catch_pos) = match_token(input, Token::Catch);
|
||||||
@ -3516,20 +3529,23 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// try { try_block } catch ( var ) { catch_block }
|
// try { try_block } catch ( var ) { catch_block }
|
||||||
let catch_block = self.parse_block(input, state, lib, settings)?;
|
let branch = self.parse_block(input, state, lib, settings)?.into();
|
||||||
|
|
||||||
if !catch_var.is_empty() {
|
let expr = if !catch_var.is_empty() {
|
||||||
// Remove the error variable from the stack
|
// Remove the error variable from the stack
|
||||||
state.stack.as_deref_mut().unwrap().pop();
|
state.stack.as_deref_mut().unwrap().pop();
|
||||||
}
|
|
||||||
|
Expr::Variable(
|
||||||
|
(None, Namespace::default(), 0, catch_var.name).into(),
|
||||||
|
None,
|
||||||
|
catch_var.pos,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Expr::Unit(catch_var.pos)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Stmt::TryCatch(
|
Ok(Stmt::TryCatch(
|
||||||
TryCatchBlock {
|
FlowControl { body, expr, branch }.into(),
|
||||||
try_block: try_block.into(),
|
|
||||||
catch_var,
|
|
||||||
catch_block: catch_block.into(),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user