Simplify constant function call arguments.
This commit is contained in:
parent
bed5256e2e
commit
2c21928f67
45
src/ast.rs
45
src/ast.rs
@ -1,5 +1,6 @@
|
|||||||
//! Module defining the AST (abstract syntax tree).
|
//! Module defining the AST (abstract syntax tree).
|
||||||
|
|
||||||
|
use crate::dynamic::Union;
|
||||||
use crate::fn_native::shared_make_mut;
|
use crate::fn_native::shared_make_mut;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
@ -1519,7 +1520,7 @@ pub struct FnCallExpr {
|
|||||||
/// List of function call argument expressions.
|
/// List of function call argument expressions.
|
||||||
pub args: StaticVec<Expr>,
|
pub args: StaticVec<Expr>,
|
||||||
/// List of function call arguments that are constants.
|
/// List of function call arguments that are constants.
|
||||||
pub literal_args: smallvec::SmallVec<[(Dynamic, Position); 2]>,
|
pub constants: smallvec::SmallVec<[Dynamic; 2]>,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
@ -1532,16 +1533,6 @@ impl FnCallExpr {
|
|||||||
pub fn is_qualified(&self) -> bool {
|
pub fn is_qualified(&self) -> bool {
|
||||||
self.namespace.is_some()
|
self.namespace.is_some()
|
||||||
}
|
}
|
||||||
/// Are there no arguments to this function call?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_args_empty(&self) -> bool {
|
|
||||||
self.args.is_empty() && self.literal_args.is_empty()
|
|
||||||
}
|
|
||||||
/// Get the number of arguments to this function call.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn args_count(&self) -> usize {
|
|
||||||
self.args.len() + self.literal_args.len()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that wraps a floating-point number and implements [`Hash`].
|
/// A type that wraps a floating-point number and implements [`Hash`].
|
||||||
@ -1718,6 +1709,8 @@ pub enum Expr {
|
|||||||
(ImmutableString, Position),
|
(ImmutableString, Position),
|
||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
|
/// Stack slot
|
||||||
|
Stack(usize, Position),
|
||||||
/// { [statement][Stmt] ... }
|
/// { [statement][Stmt] ... }
|
||||||
Stmt(Box<StmtBlock>),
|
Stmt(Box<StmtBlock>),
|
||||||
/// func `(` expr `,` ... `)`
|
/// func `(` expr `,` ... `)`
|
||||||
@ -1783,6 +1776,7 @@ impl fmt::Debug for Expr {
|
|||||||
f.write_str(")")
|
f.write_str(")")
|
||||||
}
|
}
|
||||||
Self::Property(x) => write!(f, "Property({})", (x.2).0),
|
Self::Property(x) => write!(f, "Property({})", (x.2).0),
|
||||||
|
Self::Stack(x, _) => write!(f, "StackSlot({})", x),
|
||||||
Self::Stmt(x) => {
|
Self::Stmt(x) => {
|
||||||
f.write_str("ExprStmtBlock")?;
|
f.write_str("ExprStmtBlock")?;
|
||||||
f.debug_list().entries(x.0.iter()).finish()
|
f.debug_list().entries(x.0.iter()).finish()
|
||||||
@ -1793,8 +1787,8 @@ impl fmt::Debug for Expr {
|
|||||||
ff.field("name", &x.name)
|
ff.field("name", &x.name)
|
||||||
.field("hash", &x.hashes)
|
.field("hash", &x.hashes)
|
||||||
.field("args", &x.args);
|
.field("args", &x.args);
|
||||||
if !x.literal_args.is_empty() {
|
if !x.constants.is_empty() {
|
||||||
ff.field("literal_args", &x.literal_args);
|
ff.field("constants", &x.constants);
|
||||||
}
|
}
|
||||||
if x.capture {
|
if x.capture {
|
||||||
ff.field("capture", &x.capture);
|
ff.field("capture", &x.capture);
|
||||||
@ -1865,6 +1859,22 @@ impl Expr {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/// Create an [`Expr`] from a [`Dynamic`] value.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_dynamic(value: Dynamic, pos: Position) -> Self {
|
||||||
|
match value.0 {
|
||||||
|
Union::Unit(_, _, _) => Self::Unit(pos),
|
||||||
|
Union::Bool(b, _, _) => Self::BoolConstant(b, pos),
|
||||||
|
Union::Str(s, _, _) => Self::StringConstant(s, pos),
|
||||||
|
Union::Char(c, _, _) => Self::CharConstant(c, pos),
|
||||||
|
Union::Int(i, _, _) => Self::IntegerConstant(i, pos),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Union::Float(f, _, _) => Self::FloatConstant(f, pos),
|
||||||
|
|
||||||
|
_ => Self::DynamicConstant(Box::new(value), pos),
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Is the expression a simple variable access?
|
/// Is the expression a simple variable access?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool {
|
pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool {
|
||||||
@ -1897,6 +1907,7 @@ impl Expr {
|
|||||||
| Self::Array(_, pos)
|
| Self::Array(_, pos)
|
||||||
| Self::Map(_, pos)
|
| Self::Map(_, pos)
|
||||||
| Self::Variable(_, pos, _)
|
| Self::Variable(_, pos, _)
|
||||||
|
| Self::Stack(_, pos)
|
||||||
| Self::FnCall(_, pos)
|
| Self::FnCall(_, pos)
|
||||||
| Self::Custom(_, pos) => *pos,
|
| Self::Custom(_, pos) => *pos,
|
||||||
|
|
||||||
@ -1935,6 +1946,7 @@ impl Expr {
|
|||||||
| Self::Dot(_, pos)
|
| Self::Dot(_, pos)
|
||||||
| Self::Index(_, pos)
|
| Self::Index(_, pos)
|
||||||
| Self::Variable(_, pos, _)
|
| Self::Variable(_, pos, _)
|
||||||
|
| Self::Stack(_, pos)
|
||||||
| Self::FnCall(_, pos)
|
| Self::FnCall(_, pos)
|
||||||
| Self::Custom(_, pos) => *pos = new_pos,
|
| Self::Custom(_, pos) => *pos = new_pos,
|
||||||
|
|
||||||
@ -1964,7 +1976,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::Stmt(x) => x.0.iter().all(Stmt::is_pure),
|
Self::Stmt(x) => x.0.iter().all(Stmt::is_pure),
|
||||||
|
|
||||||
Self::Variable(_, _, _) => true,
|
Self::Variable(_, _, _) | Self::Stack(_, _) => true,
|
||||||
|
|
||||||
_ => self.is_constant(),
|
_ => self.is_constant(),
|
||||||
}
|
}
|
||||||
@ -1989,7 +2001,8 @@ impl Expr {
|
|||||||
| Self::IntegerConstant(_, _)
|
| Self::IntegerConstant(_, _)
|
||||||
| Self::CharConstant(_, _)
|
| Self::CharConstant(_, _)
|
||||||
| Self::StringConstant(_, _)
|
| Self::StringConstant(_, _)
|
||||||
| Self::Unit(_) => true,
|
| Self::Unit(_)
|
||||||
|
| Self::Stack(_, _) => true,
|
||||||
|
|
||||||
Self::InterpolatedString(x) | Self::Array(x, _) => x.iter().all(Self::is_constant),
|
Self::InterpolatedString(x) | Self::Array(x, _) => x.iter().all(Self::is_constant),
|
||||||
|
|
||||||
@ -2049,6 +2062,8 @@ impl Expr {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Self::Custom(_, _) => false,
|
Self::Custom(_, _) => false,
|
||||||
|
|
||||||
|
Self::Stack(_, _) => unreachable!("Expr::Stack should not occur naturally"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Recursively walk this expression.
|
/// Recursively walk this expression.
|
||||||
|
@ -1670,19 +1670,18 @@ impl Engine {
|
|||||||
let arg_values = x
|
let arg_values = x
|
||||||
.args
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg_expr| {
|
.map(|arg_expr| match arg_expr {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
Expr::Stack(slot, _) => Ok(x.constants[*slot].clone()),
|
||||||
.map(Dynamic::flatten)
|
_ => self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
||||||
|
.map(Dynamic::flatten),
|
||||||
})
|
})
|
||||||
.chain(x.literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
|
||||||
.collect::<Result<StaticVec<_>, _>>()?;
|
.collect::<Result<StaticVec<_>, _>>()?;
|
||||||
|
|
||||||
let pos = x
|
let pos = x
|
||||||
.args
|
.args
|
||||||
.iter()
|
.get(0)
|
||||||
.map(|arg_expr| arg_expr.position())
|
.map(|arg_expr| arg_expr.position())
|
||||||
.chain(x.literal_args.iter().map(|(_, pos)| *pos))
|
|
||||||
.next()
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
idx_values.push((arg_values, pos).into());
|
idx_values.push((arg_values, pos).into());
|
||||||
@ -1716,19 +1715,18 @@ impl Engine {
|
|||||||
let arg_values = x
|
let arg_values = x
|
||||||
.args
|
.args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg_expr| {
|
.map(|arg_expr| match arg_expr {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
Expr::Stack(slot, _) => Ok(x.constants[*slot].clone()),
|
||||||
.map(Dynamic::flatten)
|
_ => self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
||||||
|
.map(Dynamic::flatten),
|
||||||
})
|
})
|
||||||
.chain(x.literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
|
||||||
.collect::<Result<StaticVec<_>, _>>()?;
|
.collect::<Result<StaticVec<_>, _>>()?;
|
||||||
|
|
||||||
let pos = x
|
let pos = x
|
||||||
.args
|
.args
|
||||||
.iter()
|
.get(0)
|
||||||
.map(|arg_expr| arg_expr.position())
|
.map(|arg_expr| arg_expr.position())
|
||||||
.chain(x.literal_args.iter().map(|(_, pos)| *pos))
|
|
||||||
.next()
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
(arg_values, pos).into()
|
(arg_values, pos).into()
|
||||||
@ -2055,7 +2053,7 @@ impl Engine {
|
|||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
literal_args: c_args,
|
constants,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace
|
let namespace = namespace
|
||||||
@ -2063,8 +2061,8 @@ impl Engine {
|
|||||||
.expect("never fails because function call is qualified");
|
.expect("never fails because function call is qualified");
|
||||||
let hash = hashes.native_hash();
|
let hash = hashes.native_hash();
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash,
|
||||||
level,
|
*pos, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2075,12 +2073,12 @@ impl Engine {
|
|||||||
capture,
|
capture,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
literal_args: c_args,
|
constants,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, mods, state, lib, this_ptr, name, args, c_args, *hashes, *pos, *capture,
|
scope, mods, state, lib, this_ptr, name, args, constants, *hashes, *pos,
|
||||||
level,
|
*capture, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2643,7 +2641,7 @@ impl Engine {
|
|||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
literal_args: c_args,
|
constants,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace
|
let namespace = namespace
|
||||||
@ -2651,8 +2649,8 @@ impl Engine {
|
|||||||
.expect("never fails because function call is qualified");
|
.expect("never fails because function call is qualified");
|
||||||
let hash = hashes.native_hash();
|
let hash = hashes.native_hash();
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash,
|
||||||
level,
|
*pos, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2663,12 +2661,12 @@ impl Engine {
|
|||||||
capture,
|
capture,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
literal_args: c_args,
|
constants,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, mods, state, lib, this_ptr, name, args, c_args, *hashes, *pos, *capture,
|
scope, mods, state, lib, this_ptr, name, args, constants, *hashes, *pos,
|
||||||
level,
|
*capture, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
155
src/fn_call.rs
155
src/fn_call.rs
@ -1039,6 +1039,27 @@ impl Engine {
|
|||||||
Ok((result, updated))
|
Ok((result, updated))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_arg_value(
|
||||||
|
&self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
mods: &mut Imports,
|
||||||
|
state: &mut State,
|
||||||
|
lib: &[&Module],
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
|
level: usize,
|
||||||
|
args_expr: &[Expr],
|
||||||
|
constants: &[Dynamic],
|
||||||
|
index: usize,
|
||||||
|
) -> Result<(Dynamic, Position), Box<EvalAltResult>> {
|
||||||
|
match args_expr[index] {
|
||||||
|
Expr::Stack(slot, pos) => Ok((constants[slot].clone(), pos)),
|
||||||
|
ref arg => self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
|
.map(|v| (v, arg.position())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Call a function in normal function-call style.
|
/// Call a function in normal function-call style.
|
||||||
pub(crate) fn make_function_call(
|
pub(crate) fn make_function_call(
|
||||||
&self,
|
&self,
|
||||||
@ -1049,7 +1070,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
literal_args: &[(Dynamic, Position)],
|
constants: &[Dynamic],
|
||||||
mut hashes: FnCallHashes,
|
mut hashes: FnCallHashes,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
@ -1058,20 +1079,15 @@ impl Engine {
|
|||||||
// Handle call() - Redirect function call
|
// Handle call() - Redirect function call
|
||||||
let redirected;
|
let redirected;
|
||||||
let mut args_expr = args_expr;
|
let mut args_expr = args_expr;
|
||||||
let mut literal_args = literal_args;
|
let mut total_args = args_expr.len();
|
||||||
let mut total_args = args_expr.len() + literal_args.len();
|
|
||||||
let mut curry = StaticVec::new();
|
let mut curry = StaticVec::new();
|
||||||
let mut name = fn_name;
|
let mut name = fn_name;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
let (arg, arg_pos) = self.get_arg_value(
|
||||||
|| Ok(literal_args[0].clone()),
|
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||||
|arg| {
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
|
||||||
.map(|v| (v, arg.position()))
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !arg.is::<FnPtr>() {
|
if !arg.is::<FnPtr>() {
|
||||||
@ -1089,11 +1105,7 @@ impl Engine {
|
|||||||
name = &redirected;
|
name = &redirected;
|
||||||
|
|
||||||
// Skip the first argument
|
// Skip the first argument
|
||||||
if !args_expr.is_empty() {
|
|
||||||
args_expr = &args_expr[1..];
|
args_expr = &args_expr[1..];
|
||||||
} else {
|
|
||||||
literal_args = &literal_args[1..];
|
|
||||||
}
|
|
||||||
total_args -= 1;
|
total_args -= 1;
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
@ -1106,12 +1118,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
KEYWORD_FN_PTR if total_args == 1 => {
|
KEYWORD_FN_PTR if total_args == 1 => {
|
||||||
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
let (arg, arg_pos) = self.get_arg_value(
|
||||||
|| Ok(literal_args[0].clone()),
|
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||||
|arg| {
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
|
||||||
.map(|v| (v, arg.position()))
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
@ -1125,12 +1133,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle curry()
|
// Handle curry()
|
||||||
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
||||||
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
let (arg, arg_pos) = self.get_arg_value(
|
||||||
|| Ok(literal_args[0].clone()),
|
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||||
|arg| {
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
|
||||||
.map(|v| (v, arg.position()))
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !arg.is::<FnPtr>() {
|
if !arg.is::<FnPtr>() {
|
||||||
@ -1143,15 +1147,12 @@ impl Engine {
|
|||||||
let (name, mut fn_curry) = arg.cast::<FnPtr>().take_data();
|
let (name, mut fn_curry) = arg.cast::<FnPtr>().take_data();
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
if !args_expr.is_empty() {
|
args_expr.iter().skip(1).try_for_each(|expr| match expr {
|
||||||
args_expr.iter().skip(1).try_for_each(|expr| {
|
Expr::Stack(slot, _) => Ok(fn_curry.push(constants[*slot].clone())),
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
_ => self
|
||||||
.map(|value| fn_curry.push(value))
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
|
.map(|value| fn_curry.push(value)),
|
||||||
})?;
|
})?;
|
||||||
fn_curry.extend(literal_args.iter().map(|(v, _)| v.clone()));
|
|
||||||
} else {
|
|
||||||
fn_curry.extend(literal_args.iter().skip(1).map(|(v, _)| v.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
||||||
}
|
}
|
||||||
@ -1159,9 +1160,8 @@ impl Engine {
|
|||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
||||||
let arg = args_expr.get(0).map_or_else(
|
let (arg, _) = self.get_arg_value(
|
||||||
|| Ok(literal_args[0].0.clone()),
|
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||||
|arg| self.eval_expr(scope, mods, state, lib, this_ptr, arg, level),
|
|
||||||
)?;
|
)?;
|
||||||
return Ok(arg.is_shared().into());
|
return Ok(arg.is_shared().into());
|
||||||
}
|
}
|
||||||
@ -1169,27 +1169,17 @@ impl Engine {
|
|||||||
// Handle is_def_fn()
|
// Handle is_def_fn()
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
||||||
let (arg, arg_pos) = if !args_expr.is_empty() {
|
let (arg, arg_pos) = self.get_arg_value(
|
||||||
(
|
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?,
|
)?;
|
||||||
args_expr[0].position(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
literal_args[0].clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let fn_name = arg
|
let fn_name = arg
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|err| self.make_type_mismatch_err::<ImmutableString>(err, arg_pos))?;
|
.map_err(|err| self.make_type_mismatch_err::<ImmutableString>(err, arg_pos))?;
|
||||||
|
|
||||||
let (arg, arg_pos) = if args_expr.len() > 1 {
|
let (arg, arg_pos) = self.get_arg_value(
|
||||||
(
|
scope, mods, state, lib, this_ptr, level, args_expr, constants, 1,
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?,
|
)?;
|
||||||
args_expr[1].position(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
literal_args[if args_expr.is_empty() { 1 } else { 0 }].clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let num_params = arg
|
let num_params = arg
|
||||||
.as_int()
|
.as_int()
|
||||||
@ -1206,12 +1196,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
||||||
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
let (arg, arg_pos) = self.get_arg_value(
|
||||||
|| Ok(literal_args[0].clone()),
|
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||||
|arg| {
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
|
||||||
.map(|v| (v, arg.position()))
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
let var_name = arg
|
let var_name = arg
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
@ -1223,12 +1209,8 @@ impl Engine {
|
|||||||
KEYWORD_EVAL if total_args == 1 => {
|
KEYWORD_EVAL if total_args == 1 => {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let (script, script_pos) = args_expr.get(0).map_or_else(
|
let (script, script_pos) = self.get_arg_value(
|
||||||
|| Ok(literal_args[0].clone()),
|
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||||
|script_expr| {
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)
|
|
||||||
.map(|v| (v, script_expr.position()))
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
let script = script.take_immutable_string().map_err(|typ| {
|
let script = script.take_immutable_string().map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
||||||
@ -1276,7 +1258,7 @@ impl Engine {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if args_expr.is_empty() && literal_args.is_empty() && curry.is_empty() {
|
if args_expr.is_empty() && curry.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
args = Default::default();
|
args = Default::default();
|
||||||
} else {
|
} else {
|
||||||
@ -1288,11 +1270,12 @@ impl Engine {
|
|||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|expr| {
|
.map(|expr| match expr {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
Expr::Stack(slot, _) => Ok(constants[*slot].clone()),
|
||||||
.map(Dynamic::flatten)
|
_ => self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
|
.map(Dynamic::flatten),
|
||||||
})
|
})
|
||||||
.chain(literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let (mut target, _pos) =
|
let (mut target, _pos) =
|
||||||
@ -1323,11 +1306,12 @@ impl Engine {
|
|||||||
// func(..., ...)
|
// func(..., ...)
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| {
|
.map(|expr| match expr {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
Expr::Stack(slot, _) => Ok(constants[*slot].clone()),
|
||||||
.map(Dynamic::flatten)
|
_ => self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
|
.map(Dynamic::flatten),
|
||||||
})
|
})
|
||||||
.chain(literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
||||||
@ -1351,7 +1335,7 @@ impl Engine {
|
|||||||
namespace: &NamespaceRef,
|
namespace: &NamespaceRef,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
literal_args: &[(Dynamic, Position)],
|
constants: &[Dynamic],
|
||||||
hash: u64,
|
hash: u64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -1360,7 +1344,7 @@ impl Engine {
|
|||||||
let mut first_arg_value = None;
|
let mut first_arg_value = None;
|
||||||
let mut args: StaticVec<_>;
|
let mut args: StaticVec<_>;
|
||||||
|
|
||||||
if args_expr.is_empty() && literal_args.is_empty() {
|
if args_expr.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
args = Default::default();
|
args = Default::default();
|
||||||
} else {
|
} else {
|
||||||
@ -1375,13 +1359,15 @@ impl Engine {
|
|||||||
.map(|(i, expr)| {
|
.map(|(i, expr)| {
|
||||||
// Skip the first argument
|
// Skip the first argument
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
Ok(Default::default())
|
return Ok(Default::default());
|
||||||
} else {
|
}
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
match expr {
|
||||||
.map(Dynamic::flatten)
|
Expr::Stack(slot, _) => Ok(constants[*slot].clone()),
|
||||||
|
_ => self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
|
.map(Dynamic::flatten),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.chain(literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
@ -1412,11 +1398,12 @@ impl Engine {
|
|||||||
// func(..., ...) or func(mod::x, ...)
|
// func(..., ...) or func(mod::x, ...)
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| {
|
.map(|expr| match expr {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
Expr::Stack(slot, _) => Ok(constants[*slot].clone()),
|
||||||
.map(Dynamic::flatten)
|
_ => self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
|
.map(Dynamic::flatten),
|
||||||
})
|
})
|
||||||
.chain(literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
args = arg_values.iter_mut().collect();
|
args = arg_values.iter_mut().collect();
|
||||||
|
@ -369,7 +369,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
&& x.0.is_variable_access(true)
|
&& x.0.is_variable_access(true)
|
||||||
&& matches!(&x.2, Expr::FnCall(x2, _)
|
&& matches!(&x.2, Expr::FnCall(x2, _)
|
||||||
if Token::lookup_from_syntax(&x2.name).map(|t| t.has_op_assignment()).unwrap_or(false)
|
if Token::lookup_from_syntax(&x2.name).map(|t| t.has_op_assignment()).unwrap_or(false)
|
||||||
&& x2.args_count() == 2 && x2.args.len() >= 1
|
&& x2.args.len() == 2
|
||||||
&& x2.args[0].get_variable_name(true) == x.0.get_variable_name(true)
|
&& x2.args[0].get_variable_name(true) == x.0.get_variable_name(true)
|
||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
@ -379,12 +379,15 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
let op = Token::lookup_from_syntax(&x2.name).unwrap();
|
let op = Token::lookup_from_syntax(&x2.name).unwrap();
|
||||||
let op_assignment = op.make_op_assignment().unwrap();
|
let op_assignment = op.make_op_assignment().unwrap();
|
||||||
x.1 = Some(OpAssignment::new(op_assignment));
|
x.1 = Some(OpAssignment::new(op_assignment));
|
||||||
x.2 = if x2.args.len() > 1 {
|
|
||||||
mem::take(&mut x2.args[1])
|
let value = mem::take(&mut x2.args[1]);
|
||||||
|
|
||||||
|
if let Expr::Stack(slot, pos) = value {
|
||||||
|
let value = mem::take(x2.constants.get_mut(slot).unwrap());
|
||||||
|
x.2 = Expr::from_dynamic(value, pos);
|
||||||
} else {
|
} else {
|
||||||
let (value, pos) = mem::take(&mut x2.literal_args[0]);
|
x.2 = value;
|
||||||
Expr::DynamicConstant(Box::new(value), pos)
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -905,15 +908,24 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
Expr::FnCall(x, pos)
|
Expr::FnCall(x, pos)
|
||||||
if !x.is_qualified() // Non-qualified
|
if !x.is_qualified() // Non-qualified
|
||||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||||
&& x.args_count() == 1
|
&& x.args.len() == 1
|
||||||
&& x.literal_args.len() == 1
|
&& x.args[0].is_constant()
|
||||||
&& x.literal_args[0].0.is::<ImmutableString>()
|
|
||||||
&& x.name == KEYWORD_FN_PTR
|
&& x.name == KEYWORD_FN_PTR
|
||||||
=> {
|
=> {
|
||||||
|
let fn_name = match x.args[0] {
|
||||||
|
Expr::Stack(slot, _) => Some(x.constants[slot].clone()),
|
||||||
|
Expr::StringConstant(ref s, _) => Some(s.clone().into()),
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(fn_name) = fn_name {
|
||||||
|
if fn_name.is::<ImmutableString>() {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let fn_ptr = FnPtr::new_unchecked(mem::take(&mut x.literal_args[0].0).as_str_ref().unwrap().into(), Default::default());
|
let fn_ptr = FnPtr::new_unchecked(fn_name.as_str_ref().unwrap().into(), Default::default());
|
||||||
*expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), *pos);
|
*expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), *pos);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FnCall(x, _) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => {
|
Expr::FnCall(x, _) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => {
|
||||||
@ -924,13 +936,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
Expr::FnCall(x, pos)
|
Expr::FnCall(x, pos)
|
||||||
if !x.is_qualified() // Non-qualified
|
if !x.is_qualified() // Non-qualified
|
||||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||||
&& x.args_count() == 2 // binary call
|
&& x.args.len() == 2 // binary call
|
||||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||||
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
||||||
=> {
|
=> {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap())
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e {
|
||||||
.chain(x.literal_args.iter().map(|(v, _)| v).cloned())
|
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
||||||
.collect();
|
_ => e.get_constant_value().unwrap()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
|
||||||
@ -953,15 +966,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
|
|
||||||
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
||||||
|
|
||||||
// Move constant arguments to the right
|
// Move constant arguments
|
||||||
while x.args.last().map(Expr::is_constant).unwrap_or(false) {
|
for arg in x.args.iter_mut() {
|
||||||
let arg = x.args.pop().unwrap();
|
if let Some(value) = arg.get_constant_value() {
|
||||||
let arg_pos = arg.position();
|
state.set_dirty();
|
||||||
x.literal_args.insert(0, (arg.get_constant_value().unwrap(), arg_pos));
|
x.constants.push(value);
|
||||||
|
*arg = Expr::Stack(x.constants.len()-1, arg.position());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x.args.shrink_to_fit();
|
|
||||||
x.literal_args.shrink_to_fit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
@ -972,14 +984,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
=> {
|
=> {
|
||||||
// First search for script-defined functions (can override built-in)
|
// First search for script-defined functions (can override built-in)
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.args_count()).is_some());
|
let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.args.len()).is_some());
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let has_script_fn = false;
|
let has_script_fn = false;
|
||||||
|
|
||||||
if !has_script_fn {
|
if !has_script_fn {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap())
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e {
|
||||||
.chain(x.literal_args.iter().map(|(v, _)| v).cloned())
|
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
||||||
.collect();
|
_ => e.get_constant_value().unwrap()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
// Save the typename of the first argument if it is `type_of()`
|
// Save the typename of the first argument if it is `type_of()`
|
||||||
// This is to avoid `call_args` being passed into the closure
|
// This is to avoid `call_args` being passed into the closure
|
||||||
@ -990,15 +1003,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mut result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values)
|
if let Some(mut result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values)
|
||||||
.or_else(|| {
|
.or_else(|| if !arg_for_type_of.is_empty() {
|
||||||
if !arg_for_type_of.is_empty() {
|
|
||||||
// Handle `type_of()`
|
// Handle `type_of()`
|
||||||
Some(arg_for_type_of.to_string().into())
|
Some(arg_for_type_of.to_string().into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}).map(Expr::from)
|
||||||
})
|
|
||||||
.map(Expr::from)
|
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
result.set_position(*pos);
|
result.set_position(*pos);
|
||||||
@ -1011,19 +1021,16 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(x, _) => {
|
Expr::FnCall(x, _) => for arg in x.args.iter_mut() {
|
||||||
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
optimize_expr(arg, state, false);
|
||||||
|
|
||||||
// Move constant arguments to the right
|
// Move constant arguments
|
||||||
while x.args.last().map(Expr::is_constant).unwrap_or(false) {
|
if let Some(value) = arg.get_constant_value() {
|
||||||
let arg = x.args.pop().unwrap();
|
state.set_dirty();
|
||||||
let arg_pos = arg.position();
|
x.constants.push(value);
|
||||||
x.literal_args.insert(0, (arg.get_constant_value().unwrap(), arg_pos));
|
*arg = Expr::Stack(x.constants.len()-1, arg.position());
|
||||||
}
|
|
||||||
|
|
||||||
x.args.shrink_to_fit();
|
|
||||||
x.literal_args.shrink_to_fit();
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
|
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
|
||||||
|
@ -1598,8 +1598,8 @@ fn make_dot_expr(
|
|||||||
Expr::FnCall(mut func, func_pos) => {
|
Expr::FnCall(mut func, func_pos) => {
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
func.hashes = FnCallHashes::from_script_and_native(
|
func.hashes = FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(&func.name, func.args_count()),
|
calc_fn_hash(&func.name, func.args.len()),
|
||||||
calc_fn_hash(&func.name, func.args_count() + 1),
|
calc_fn_hash(&func.name, func.args.len() + 1),
|
||||||
);
|
);
|
||||||
|
|
||||||
let rhs = Expr::Dot(
|
let rhs = Expr::Dot(
|
||||||
@ -1630,7 +1630,7 @@ fn make_dot_expr(
|
|||||||
}
|
}
|
||||||
// lhs.Fn() or lhs.eval()
|
// lhs.Fn() or lhs.eval()
|
||||||
(_, Expr::FnCall(x, pos))
|
(_, Expr::FnCall(x, pos))
|
||||||
if x.is_args_empty()
|
if x.args.is_empty()
|
||||||
&& [crate::engine::KEYWORD_FN_PTR, crate::engine::KEYWORD_EVAL]
|
&& [crate::engine::KEYWORD_FN_PTR, crate::engine::KEYWORD_EVAL]
|
||||||
.contains(&x.name.as_ref()) =>
|
.contains(&x.name.as_ref()) =>
|
||||||
{
|
{
|
||||||
@ -1654,8 +1654,8 @@ fn make_dot_expr(
|
|||||||
(lhs, Expr::FnCall(mut func, func_pos)) => {
|
(lhs, Expr::FnCall(mut func, func_pos)) => {
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
func.hashes = FnCallHashes::from_script_and_native(
|
func.hashes = FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(&func.name, func.args_count()),
|
calc_fn_hash(&func.name, func.args.len()),
|
||||||
calc_fn_hash(&func.name, func.args_count() + 1),
|
calc_fn_hash(&func.name, func.args.len() + 1),
|
||||||
);
|
);
|
||||||
let rhs = Expr::FnCall(func, func_pos);
|
let rhs = Expr::FnCall(func, func_pos);
|
||||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user