Add Stmt::FnCall.

This commit is contained in:
Stephen Chung 2021-04-21 18:16:24 +08:00
parent cc546fcaab
commit fe37edd123
4 changed files with 87 additions and 20 deletions

View File

@ -918,6 +918,11 @@ pub enum Stmt {
Const(Expr, Box<Ident>, bool, Position),
/// expr op`=` expr
Assignment(Box<(Expr, Option<OpAssignment>, 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<FnCallExpr>, Position),
/// `{` stmt`;` ... `}`
Block(Vec<Stmt>, 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<NamespaceRef>,

View File

@ -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();

View File

@ -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();

View File

@ -72,7 +72,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
fn inc(x) { x + 1 }
let x = 0;
while x < 31 {
while x < 36 {
print(x);
x = inc(x);
}