Optimize type_of.

This commit is contained in:
Stephen Chung 2020-03-17 10:27:43 +08:00
parent 705fbd0c1b
commit ad2601972a

View File

@ -1,7 +1,9 @@
#![cfg(not(feature = "no_optimize"))] #![cfg(not(feature = "no_optimize"))]
use crate::any::Dynamic; use crate::any::{Any, Dynamic};
use crate::engine::{Engine, FnCallArgs, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_PRINT}; use crate::engine::{
Engine, FnCallArgs, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_PRINT, KEYWORD_TYPE_OF,
};
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, Stmt, AST}; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, Stmt, AST};
use crate::scope::{Scope, ScopeEntry, VariableType}; use crate::scope::{Scope, ScopeEntry, VariableType};
@ -223,6 +225,8 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
} }
fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
const SKIP_FUNC_KEYWORDS: [&str; 3] = [KEYWORD_PRINT, KEYWORD_DEBUG, KEYWORD_DUMP_AST];
match expr { match expr {
Expr::Stmt(stmt, pos) => match optimize_stmt(*stmt, state, true) { Expr::Stmt(stmt, pos) => match optimize_stmt(*stmt, state, true) {
Stmt::Noop(_) => { Stmt::Noop(_) => {
@ -343,41 +347,46 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
), ),
}, },
// Do not optimize anything within `dump_ast` // Do not optimize anything within built-in function keywords
Expr::FunctionCall(id, args, def_value, pos) if id == KEYWORD_DUMP_AST => { Expr::FunctionCall(id, args, def_value, pos) if SKIP_FUNC_KEYWORDS.contains(&id.as_str())=>
Expr::FunctionCall(id, args, def_value, pos) Expr::FunctionCall(id, args, def_value, pos),
}
// Actually call function to optimize it // Actually call function to optimize it
Expr::FunctionCall(id, args, def_value, pos) Expr::FunctionCall(id, args, def_value, pos)
if id != KEYWORD_DEBUG // not debug if state.engine.map(|eng| eng.optimization_level == OptimizationLevel::Full).unwrap_or(false) // full optimizations
&& id != KEYWORD_PRINT // not print
&& state.engine.map(|eng| eng.optimization_level == OptimizationLevel::Full).unwrap_or(false) // full optimizations
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants && args.iter().all(|expr| expr.is_constant()) // all arguments are constants
=> => {
{
let engine = state.engine.expect("engine should be Some"); let engine = state.engine.expect("engine should be Some");
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect(); let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
let call_args: FnCallArgs = arg_values.iter_mut().map(Dynamic::as_mut).collect(); let call_args: FnCallArgs = arg_values.iter_mut().map(Dynamic::as_mut).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 id == KEYWORD_TYPE_OF && call_args.len() == 1 {
engine.map_type_name(call_args[0].type_name())
} else {
""
};
engine.call_ext_fn_raw(&id, call_args, pos).ok().map(|r| engine.call_ext_fn_raw(&id, call_args, pos).ok().map(|r|
r.or(def_value.clone()).and_then(|result| map_dynamic_to_expr(result, pos).0) r.or_else(|| {
if !arg_for_type_of.is_empty() {
// Handle `type_of()`
Some(arg_for_type_of.to_string().into_dynamic())
} else {
// Otherwise use the default value, if any
def_value.clone()
}
}).and_then(|result| map_dynamic_to_expr(result, pos).0)
.map(|expr| { .map(|expr| {
state.set_dirty(); state.set_dirty();
expr expr
})).flatten() })
.unwrap_or_else(|| Expr::FunctionCall(id, args, def_value, pos)) ).flatten().unwrap_or_else(|| Expr::FunctionCall(id, args, def_value, pos))
} }
// Optimize the function call arguments // Optimize the function call arguments
Expr::FunctionCall(id, args, def_value, pos) => { Expr::FunctionCall(id, args, def_value, pos) =>
let orig_len = args.len(); Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos),
let args: Vec<_> = args.into_iter().map(|a| optimize_expr(a, state)).collect();
if orig_len != args.len() {
state.set_dirty();
}
Expr::FunctionCall(id, args, def_value, pos)
}
Expr::Variable(ref name, _) if state.contains_constant(name) => { Expr::Variable(ref name, _) if state.contains_constant(name) => {
state.set_dirty(); state.set_dirty();