From fe37edd1239ee08154e8893415416af0f1090f03 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 21 Apr 2021 18:16:24 +0800 Subject: [PATCH] Add Stmt::FnCall. --- src/ast.rs | 28 ++++++++++++++++--- src/engine.rs | 66 ++++++++++++++++++++++++++++++++++----------- src/optimize.rs | 11 ++++++++ tests/operations.rs | 2 +- 4 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index d12c8d14..ddaba81a 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -918,6 +918,11 @@ pub enum Stmt { Const(Expr, Box, bool, Position), /// expr op`=` expr Assignment(Box<(Expr, Option, Expr)>, Position), + /// func `(` expr `,` ... `)` + /// + /// Note - this is a duplicate of [`Expr::FnCall`] to cover the very common pattern of a single + /// function call forming one statement. + FnCall(Box, Position), /// `{` stmt`;` ... `}` Block(Vec, Position), /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` @@ -983,6 +988,7 @@ impl Stmt { | Self::Break(pos) | Self::Block(_, pos) | Self::Assignment(_, pos) + | Self::FnCall(_, pos) | Self::If(_, _, pos) | Self::Switch(_, _, pos) | Self::While(_, _, pos) @@ -1012,6 +1018,7 @@ impl Stmt { | Self::Break(pos) | Self::Block(_, pos) | Self::Assignment(_, pos) + | Self::FnCall(_, pos) | Self::If(_, _, pos) | Self::Switch(_, _, pos) | Self::While(_, _, pos) @@ -1040,7 +1047,11 @@ impl Stmt { /// Does this statement return a value? pub fn returns_value(&self) -> bool { match self { - Self::If(_, _, _) | Self::Switch(_, _, _) | Self::Block(_, _) | Self::Expr(_) => true, + Self::If(_, _, _) + | Self::Switch(_, _, _) + | Self::Block(_, _) + | Self::Expr(_) + | Self::FnCall(_, _) => true, Self::Noop(_) | Self::While(_, _, _) @@ -1078,6 +1089,7 @@ impl Stmt { Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) + | Self::FnCall(_, _) | Self::Expr(_) | Self::Do(_, _, _, _) | Self::Continue(_) @@ -1115,7 +1127,10 @@ impl Stmt { condition.is_pure() && block.0.iter().all(Stmt::is_pure) } Self::For(iterable, x, _) => iterable.is_pure() && (x.1).0.iter().all(Stmt::is_pure), - Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false, + Self::Let(_, _, _, _) + | Self::Const(_, _, _, _) + | Self::Assignment(_, _) + | Self::FnCall(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, Self::TryCatch(x, _, _) => { @@ -1244,6 +1259,13 @@ impl Stmt { return false; } } + Self::FnCall(x, _) => { + for s in &x.args { + if !s.walk(path, on_node) { + return false; + } + } + } Self::Block(x, _) => { for s in x { if !s.walk(path, on_node) { @@ -1441,7 +1463,7 @@ impl FnCallHashes { /// # Volatile Data Structure /// /// This type is volatile and may change. -#[derive(Clone, Default, Hash)] +#[derive(Debug, Clone, Default, Hash)] pub struct FnCallExpr { /// Namespace of the function, if any. pub namespace: Option, diff --git a/src/engine.rs b/src/engine.rs index cbd14c47..2f5cf085 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1830,22 +1830,6 @@ impl Engine { Ok(map.into()) } - // Normal function call - Expr::FnCall(x, pos) if !x.is_qualified() => { - let FnCallExpr { - name, - capture, - hashes, - args, - constant_args: c_args, - .. - } = x.as_ref(); - self.make_function_call( - scope, mods, state, lib, this_ptr, name, args, c_args, *hashes, *pos, *capture, - level, - ) - } - // Namespace-qualified function call Expr::FnCall(x, pos) if x.is_qualified() => { let FnCallExpr { @@ -1864,6 +1848,22 @@ impl Engine { ) } + // Normal function call + Expr::FnCall(x, pos) => { + let FnCallExpr { + name, + capture, + hashes, + args, + constant_args: c_args, + .. + } = x.as_ref(); + self.make_function_call( + scope, mods, state, lib, this_ptr, name, args, c_args, *hashes, *pos, *capture, + level, + ) + } + Expr::And(x, _) => { Ok((self .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? @@ -2390,6 +2390,40 @@ impl Engine { // Break statement Stmt::Break(pos) => EvalAltResult::LoopBreak(true, *pos).into(), + // Namespace-qualified function call + Stmt::FnCall(x, pos) if x.is_qualified() => { + let FnCallExpr { + name, + namespace, + hashes, + args, + constant_args: c_args, + .. + } = x.as_ref(); + let namespace = namespace.as_ref(); + let hash = hashes.native_hash(); + self.make_qualified_function_call( + scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos, + level, + ) + } + + // Normal function call + Stmt::FnCall(x, pos) => { + let FnCallExpr { + name, + capture, + hashes, + args, + constant_args: c_args, + .. + } = x.as_ref(); + self.make_function_call( + scope, mods, state, lib, this_ptr, name, args, c_args, *hashes, *pos, *capture, + level, + ) + } + // Try/Catch statement Stmt::TryCatch(x, _, _) => { let (try_stmt, err_var, catch_stmt) = x.as_ref(); diff --git a/src/optimize.rs b/src/optimize.rs index 3ddde12c..e9b36d27 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -628,6 +628,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { let catch_block = mem::take(x.2.statements()).into_vec(); *x.2.statements() = optimize_stmt_block(catch_block, state, false, true, false).into(); } + // func(...) + Stmt::Expr(expr @ Expr::FnCall(_, _)) => { + optimize_expr(expr, state); + match expr { + Expr::FnCall(x, pos) => { + state.set_dirty(); + *stmt = Stmt::FnCall(mem::take(x), *pos); + } + _ => (), + } + } // {} Stmt::Expr(Expr::Stmt(x)) if x.is_empty() => { state.set_dirty(); diff --git a/tests/operations.rs b/tests/operations.rs index 9ebf5fcf..d35813c2 100644 --- a/tests/operations.rs +++ b/tests/operations.rs @@ -72,7 +72,7 @@ fn test_max_operations_functions() -> Result<(), Box> { fn inc(x) { x + 1 } let x = 0; - while x < 31 { + while x < 36 { print(x); x = inc(x); }