Unify all functions into CallableFunction type, remove FunctionsLib.

This commit is contained in:
Stephen Chung
2020-06-05 15:14:42 +08:00
parent e21d25a0c5
commit 56eb659d5a
6 changed files with 316 additions and 253 deletions

View File

@@ -1,10 +1,8 @@
use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::{
Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
KEYWORD_TYPE_OF,
};
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
use crate::engine::{Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::module::Module;
use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST};
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::utils::StaticVec;
@@ -54,14 +52,14 @@ struct State<'a> {
/// An `Engine` instance for eager function evaluation.
engine: &'a Engine,
/// Library of script-defined functions.
lib: &'a FunctionsLib,
lib: &'a Module,
/// Optimization level.
optimization_level: OptimizationLevel,
}
impl<'a> State<'a> {
/// Create a new State.
pub fn new(engine: &'a Engine, lib: &'a FunctionsLib, level: OptimizationLevel) -> Self {
pub fn new(engine: &'a Engine, lib: &'a Module, level: OptimizationLevel) -> Self {
Self {
changed: false,
constants: vec![],
@@ -547,14 +545,15 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
&& state.optimization_level == OptimizationLevel::Full // full optimizations
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
=> {
let ((name, native_only, pos), _, _, args, def_value) = x.as_mut();
let ((name, _, pos), _, _, args, def_value) = x.as_mut();
// First search in script-defined functions (can override built-in)
// First search in functions lib (can override built-in)
// Cater for both normal function call style and method call style (one additional arguments)
if !*native_only && state.lib.values().find(|f|
&f.name == name
&& (args.len()..=args.len() + 1).contains(&f.params.len())
).is_some() {
if 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() {
// 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);
@@ -616,7 +615,7 @@ fn optimize(
statements: Vec<Stmt>,
engine: &Engine,
scope: &Scope,
lib: &FunctionsLib,
lib: &Module,
level: OptimizationLevel,
) -> Vec<Stmt> {
// If optimization level is None then skip optimizing
@@ -703,7 +702,7 @@ pub fn optimize_into_ast(
engine: &Engine,
scope: &Scope,
statements: Vec<Stmt>,
functions: Vec<FnDef>,
functions: Vec<ScriptFnDef>,
level: OptimizationLevel,
) -> AST {
#[cfg(feature = "no_optimize")]
@@ -711,37 +710,65 @@ pub fn optimize_into_ast(
#[cfg(not(feature = "no_function"))]
let lib = {
let mut module = Module::new();
if !level.is_none() {
let lib = FunctionsLib::from_iter(functions.iter().cloned());
// We only need the script library's signatures for optimization purposes
let mut lib2 = Module::new();
FunctionsLib::from_iter(functions.into_iter().map(|mut fn_def| {
let pos = fn_def.body.position();
// Optimize the function body
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &lib, level);
// {} -> Noop
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
// { return val; } -> val
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
Stmt::Expr(Box::new(x.1.unwrap()))
functions
.iter()
.map(|fn_def| {
ScriptFnDef {
name: fn_def.name.clone(),
access: fn_def.access,
body: Default::default(),
params: fn_def.params.clone(),
pos: fn_def.pos,
}
// { return; } -> ()
Stmt::ReturnWithVal(x) if x.1.is_none() && (x.0).0 == ReturnType::Return => {
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
}
// All others
stmt => stmt,
};
fn_def
}))
.into()
})
.for_each(|fn_def| lib2.set_script_fn(fn_def));
functions
.into_iter()
.map(|mut fn_def| {
let pos = fn_def.body.position();
// Optimize the function body
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &lib2, level);
// {} -> Noop
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
// { return val; } -> val
Stmt::ReturnWithVal(x)
if x.1.is_some() && (x.0).0 == ReturnType::Return =>
{
Stmt::Expr(Box::new(x.1.unwrap()))
}
// { return; } -> ()
Stmt::ReturnWithVal(x)
if x.1.is_none() && (x.0).0 == ReturnType::Return =>
{
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
}
// All others
stmt => stmt,
};
fn_def.into()
})
.for_each(|fn_def| module.set_script_fn(fn_def));
} else {
FunctionsLib::from_iter(functions.into_iter())
functions
.into_iter()
.for_each(|fn_def| module.set_script_fn(fn_def));
}
module
};
#[cfg(feature = "no_function")]
let lib: FunctionsLib = Default::default();
let lib = Default::default();
AST::new(
match level {