Eagerly evaluate built-in operators for OptimizationLevel::Simple.

This commit is contained in:
Stephen Chung
2020-10-05 10:27:31 +08:00
parent b91a073596
commit 0d0affd5e9
11 changed files with 182 additions and 104 deletions

View File

@@ -5,6 +5,7 @@ use crate::calc_fn_hash;
use crate::engine::{
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
};
use crate::fn_call::run_builtin_binary_op;
use crate::fn_native::FnPtr;
use crate::module::Module;
use crate::parser::{map_dynamic_to_expr, Expr, ScriptFnDef, Stmt, AST};
@@ -568,58 +569,75 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
}
}
// Call built-in functions
Expr::FnCall(mut x)
if x.1.is_none() // Non-qualified
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
&& x.3.len() == 2 // binary call
&& x.3.iter().all(Expr::is_constant) // all arguments are constants
=> {
let ((name, _, _, pos), _, _, args, _) = x.as_mut();
let arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect();
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
// Search for overloaded operators (can override built-in).
if !state.engine.has_override_by_name_and_arguments(state.lib, name, arg_types.as_ref(), false) {
if let Some(expr) = run_builtin_binary_op(name, &arg_values[0], &arg_values[1])
.ok().flatten()
.and_then(|result| map_dynamic_to_expr(result, *pos))
{
state.set_dirty();
return expr;
}
}
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x)
}
// Eagerly call functions
Expr::FnCall(mut x)
if x.1.is_none() // Non-qualified
&& state.optimization_level == OptimizationLevel::Full // full optimizations
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
&& x.3.iter().all(Expr::is_constant) // all arguments are constants
=> {
let ((name, _, _, pos), _, _, args, def_value) = x.as_mut();
// First search in functions lib (can override built-in)
// Cater for both normal function call style and method call style (one additional arguments)
let has_script_fn = cfg!(not(feature = "no_function")) && state.lib.iter_fn().find(|(_, _, _, _,f)| {
if !f.is_script() { return false; }
let fn_def = f.get_fn_def();
fn_def.name == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len())
}).is_some();
// First search for script-defined functions (can override built-in)
let has_script_fn = cfg!(not(feature = "no_function"))
&& state.lib.get_script_fn(name, args.len(), false).is_some();
if has_script_fn {
// A script-defined function overrides the built-in function - do not make the call
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
return Expr::FnCall(x);
if !has_script_fn {
let mut arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect();
// Save the typename of the first argument if it is `type_of()`
// This is to avoid `call_args` being passed into the closure
let arg_for_type_of = if name == KEYWORD_TYPE_OF && arg_values.len() == 1 {
state.engine.map_type_name(arg_values[0].type_name())
} else {
""
};
if let Some(expr) = call_fn_with_constant_arguments(&state, name, arg_values.as_mut())
.or_else(|| {
if !arg_for_type_of.is_empty() {
// Handle `type_of()`
Some(arg_for_type_of.to_string().into())
} else {
// Otherwise use the default value, if any
def_value.map(|v| v.into())
}
})
.and_then(|result| map_dynamic_to_expr(result, *pos))
{
state.set_dirty();
return expr;
}
}
let mut arg_values: StaticVec<_> = args.iter().map(Expr::get_constant_value).collect();
// Save the typename of the first argument if it is `type_of()`
// This is to avoid `call_args` being passed into the closure
let arg_for_type_of = if name == KEYWORD_TYPE_OF && arg_values.len() == 1 {
state.engine.map_type_name(arg_values[0].type_name())
} else {
""
};
call_fn_with_constant_arguments(&state, name, arg_values.as_mut())
.or_else(|| {
if !arg_for_type_of.is_empty() {
// Handle `type_of()`
Some(arg_for_type_of.to_string().into())
} else {
// Otherwise use the default value, if any
def_value.map(|v| v.into())
}
})
.and_then(|result| map_dynamic_to_expr(result, *pos))
.map(|expr| {
state.set_dirty();
expr
})
.unwrap_or_else(|| {
// Optimize function call arguments
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x)
})
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
Expr::FnCall(x)
}
// id(args ..) -> optimize function call arguments