Pre-calculate function call hashes.
This commit is contained in:
parent
0d20137d6c
commit
79f39bd702
127
src/engine.rs
127
src/engine.rs
@ -159,15 +159,11 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Does a certain script-defined function exist in the `State`?
|
/// Does a certain script-defined function exist in the `State`?
|
||||||
pub fn has_function(&self, name: &str, params: usize) -> bool {
|
pub fn has_function(&self, hash: u64) -> bool {
|
||||||
// Qualifiers (none) + function name + placeholders (one for each parameter).
|
|
||||||
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
|
|
||||||
self.fn_lib.contains_key(&hash)
|
self.fn_lib.contains_key(&hash)
|
||||||
}
|
}
|
||||||
/// Get a script-defined function definition from the `State`.
|
/// Get a script-defined function definition from the `State`.
|
||||||
pub fn get_function(&self, name: &str, params: usize) -> Option<&FnDef> {
|
pub fn get_function(&self, hash: u64) -> Option<&FnDef> {
|
||||||
// Qualifiers (none) + function name + placeholders (one for each parameter).
|
|
||||||
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
|
|
||||||
self.fn_lib.get(&hash).map(|f| f.as_ref())
|
self.fn_lib.get(&hash).map(|f| f.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -442,14 +438,14 @@ fn default_print(s: &str) {
|
|||||||
fn search_scope<'a>(
|
fn search_scope<'a>(
|
||||||
scope: &'a mut Scope,
|
scope: &'a mut Scope,
|
||||||
name: &str,
|
name: &str,
|
||||||
#[cfg(not(feature = "no_module"))] modules: &Option<Box<ModuleRef>>,
|
#[cfg(not(feature = "no_module"))] modules: Option<(&Box<ModuleRef>, u64)>,
|
||||||
#[cfg(feature = "no_module")] _: &Option<ModuleRef>,
|
#[cfg(feature = "no_module")] _: Option<(&ModuleRef, u64)>,
|
||||||
index: Option<NonZeroUsize>,
|
index: Option<NonZeroUsize>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
if let Some(modules) = modules {
|
if let Some((modules, hash)) = modules {
|
||||||
let (id, root_pos) = modules.get(0);
|
let (id, root_pos) = modules.get(0);
|
||||||
|
|
||||||
let module = if let Some(index) = modules.index() {
|
let module = if let Some(index) = modules.index() {
|
||||||
@ -468,7 +464,7 @@ fn search_scope<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return Ok((
|
return Ok((
|
||||||
module.get_qualified_var_mut(name, modules.key(), pos)?,
|
module.get_qualified_var_mut(name, hash, pos)?,
|
||||||
// Module variables are constant
|
// Module variables are constant
|
||||||
ScopeEntryType::Constant,
|
ScopeEntryType::Constant,
|
||||||
));
|
));
|
||||||
@ -574,6 +570,8 @@ impl Engine {
|
|||||||
scope: Option<&mut Scope>,
|
scope: Option<&mut Scope>,
|
||||||
state: &State,
|
state: &State,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
|
hash_fn_spec: u64,
|
||||||
|
hash_fn_def: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
@ -585,18 +583,17 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if let Some(fn_def) = state.get_function(fn_name, args.len()) {
|
if hash_fn_def > 0 {
|
||||||
return self.call_script_fn(scope, state, fn_def, args, pos, level);
|
if let Some(fn_def) = state.get_function(hash_fn_def) {
|
||||||
|
return self.call_script_fn(scope, state, fn_def, args, pos, level);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search built-in's and external functions
|
// Search built-in's and external functions
|
||||||
// Qualifiers (none) + function name + argument `TypeId`'s.
|
|
||||||
let fn_spec = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id()));
|
|
||||||
|
|
||||||
if let Some(func) = self
|
if let Some(func) = self
|
||||||
.base_package
|
.base_package
|
||||||
.get_function(fn_spec)
|
.get_function(hash_fn_spec)
|
||||||
.or_else(|| self.packages.get_function(fn_spec))
|
.or_else(|| self.packages.get_function(hash_fn_spec))
|
||||||
{
|
{
|
||||||
// Run external function
|
// Run external function
|
||||||
let result = func(args, pos)?;
|
let result = func(args, pos)?;
|
||||||
@ -739,16 +736,13 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Has a system function an override?
|
// Has a system function an override?
|
||||||
fn has_override(&self, state: &State, name: &str) -> bool {
|
fn has_override(&self, state: &State, hash_fn_spec: u64, hash_fn_def: u64) -> bool {
|
||||||
// Qualifiers (none) + function name + argument `TypeId`'s.
|
|
||||||
let hash = calc_fn_hash(empty(), name, once(TypeId::of::<String>()));
|
|
||||||
|
|
||||||
// First check registered functions
|
// First check registered functions
|
||||||
self.base_package.contains_function(hash)
|
self.base_package.contains_function(hash_fn_spec)
|
||||||
// Then check packages
|
// Then check packages
|
||||||
|| self.packages.contains_function(hash)
|
|| self.packages.contains_function(hash_fn_spec)
|
||||||
// Then check script-defined functions
|
// Then check script-defined functions
|
||||||
|| state.has_function(name, 1)
|
|| state.has_function(hash_fn_def)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform an actual function call, taking care of special functions
|
// Perform an actual function call, taking care of special functions
|
||||||
@ -762,26 +756,45 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
state: &State,
|
state: &State,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
|
hash_fn_def: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
// Qualifiers (none) + function name + argument `TypeId`'s.
|
||||||
|
let hash_fn_spec = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
match fn_name {
|
match fn_name {
|
||||||
// type_of
|
// type_of
|
||||||
KEYWORD_TYPE_OF if args.len() == 1 && !self.has_override(state, KEYWORD_TYPE_OF) => {
|
KEYWORD_TYPE_OF
|
||||||
|
if args.len() == 1 && !self.has_override(state, hash_fn_spec, hash_fn_def) =>
|
||||||
|
{
|
||||||
Ok(self.map_type_name(args[0].type_name()).to_string().into())
|
Ok(self.map_type_name(args[0].type_name()).to_string().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// eval - reaching this point it must be a method-style call
|
// eval - reaching this point it must be a method-style call
|
||||||
KEYWORD_EVAL if args.len() == 1 && !self.has_override(state, KEYWORD_EVAL) => {
|
KEYWORD_EVAL
|
||||||
|
if args.len() == 1 && !self.has_override(state, hash_fn_spec, hash_fn_def) =>
|
||||||
|
{
|
||||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
"'eval' should not be called in method style. Try eval(...);".into(),
|
"'eval' should not be called in method style. Try eval(...);".into(),
|
||||||
pos,
|
pos,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal method call
|
// Normal method call
|
||||||
_ => self.call_fn_raw(None, state, fn_name, args, def_val, pos, level),
|
_ => self.call_fn_raw(
|
||||||
|
None,
|
||||||
|
state,
|
||||||
|
fn_name,
|
||||||
|
hash_fn_spec,
|
||||||
|
hash_fn_def,
|
||||||
|
args,
|
||||||
|
def_val,
|
||||||
|
pos,
|
||||||
|
level,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -869,17 +882,17 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
match rhs {
|
match rhs {
|
||||||
// xxx.fn_name(arg_expr_list)
|
// xxx.fn_name(arg_expr_list)
|
||||||
Expr::FnCall(fn_name, None,_, def_val, pos) => {
|
Expr::FnCall(fn_name, None, hash, _, def_val, pos) => {
|
||||||
let mut args: Vec<_> = once(obj)
|
let mut args: Vec<_> = once(obj)
|
||||||
.chain(idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap().iter_mut())
|
.chain(idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap().iter_mut())
|
||||||
.collect();
|
.collect();
|
||||||
let def_val = def_val.as_deref();
|
let def_val = def_val.as_deref();
|
||||||
// A function call is assumed to have side effects, so the value is changed
|
// A function call is assumed to have side effects, so the value is changed
|
||||||
// TODO - Remove assumption of side effects by checking whether the first parameter is &mut
|
// TODO - Remove assumption of side effects by checking whether the first parameter is &mut
|
||||||
self.exec_fn_call(state, fn_name, &mut args, def_val, *pos, 0).map(|v| (v, true))
|
self.exec_fn_call(state, fn_name, *hash, &mut args, def_val, *pos, 0).map(|v| (v, true))
|
||||||
}
|
}
|
||||||
// xxx.module::fn_name(...) - syntax error
|
// xxx.module::fn_name(...) - syntax error
|
||||||
Expr::FnCall(_,_,_,_,_) => unreachable!(),
|
Expr::FnCall(_, _, _, _, _, _) => unreachable!(),
|
||||||
// {xxx:map}.id = ???
|
// {xxx:map}.id = ???
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => {
|
Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => {
|
||||||
@ -899,13 +912,13 @@ impl Engine {
|
|||||||
Expr::Property(id, pos) if new_val.is_some() => {
|
Expr::Property(id, pos) if new_val.is_some() => {
|
||||||
let fn_name = make_setter(id);
|
let fn_name = make_setter(id);
|
||||||
let mut args = [obj, new_val.as_mut().unwrap()];
|
let mut args = [obj, new_val.as_mut().unwrap()];
|
||||||
self.exec_fn_call(state, &fn_name, &mut args, None, *pos, 0).map(|v| (v, true))
|
self.exec_fn_call(state, &fn_name, 0, &mut args, None, *pos, 0).map(|v| (v, true))
|
||||||
}
|
}
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let fn_name = make_getter(id);
|
let fn_name = make_getter(id);
|
||||||
let mut args = [obj];
|
let mut args = [obj];
|
||||||
self.exec_fn_call(state, &fn_name, &mut args, None, *pos, 0).map(|v| (v, false))
|
self.exec_fn_call(state, &fn_name, 0, &mut args, None, *pos, 0).map(|v| (v, false))
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
// {xxx:map}.idx_lhs[idx_expr]
|
// {xxx:map}.idx_lhs[idx_expr]
|
||||||
@ -936,7 +949,7 @@ impl Engine {
|
|||||||
|
|
||||||
let indexed_val = &mut (if let Expr::Property(id, pos) = dot_lhs.as_ref() {
|
let indexed_val = &mut (if let Expr::Property(id, pos) = dot_lhs.as_ref() {
|
||||||
let fn_name = make_getter(id);
|
let fn_name = make_getter(id);
|
||||||
self.exec_fn_call(state, &fn_name, &mut args[..1], None, *pos, 0)?
|
self.exec_fn_call(state, &fn_name, 0, &mut args[..1], None, *pos, 0)?
|
||||||
} else {
|
} else {
|
||||||
// Syntax error
|
// Syntax error
|
||||||
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
||||||
@ -954,7 +967,7 @@ impl Engine {
|
|||||||
let fn_name = make_setter(id);
|
let fn_name = make_setter(id);
|
||||||
// Re-use args because the first &mut parameter will not be consumed
|
// Re-use args because the first &mut parameter will not be consumed
|
||||||
args[1] = indexed_val;
|
args[1] = indexed_val;
|
||||||
self.exec_fn_call(state, &fn_name, &mut args, None, *pos, 0).or_else(|err| match *err {
|
self.exec_fn_call(state, &fn_name, 0, &mut args, None, *pos, 0).or_else(|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because the property is read-only
|
// If there is no setter, no need to feed it back because the property is read-only
|
||||||
EvalAltResult::ErrorDotExpr(_,_) => Ok(Default::default()),
|
EvalAltResult::ErrorDotExpr(_,_) => Ok(Default::default()),
|
||||||
err => Err(Box::new(err))
|
err => Err(Box::new(err))
|
||||||
@ -991,9 +1004,10 @@ impl Engine {
|
|||||||
|
|
||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(id, modules, index, pos) => {
|
Expr::Variable(id, modules, hash, index, pos) => {
|
||||||
let index = if state.always_search { None } else { *index };
|
let index = if state.always_search { None } else { *index };
|
||||||
let (target, typ) = search_scope(scope, id, modules, index, *pos)?;
|
let (target, typ) =
|
||||||
|
search_scope(scope, id, modules.as_ref().map(|m| (m, *hash)), index, *pos)?;
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
match typ {
|
match typ {
|
||||||
@ -1046,7 +1060,7 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::FnCall(_, None, arg_exprs, _, _) => {
|
Expr::FnCall(_, None, _, arg_exprs, _, _) => {
|
||||||
let mut arg_values = StaticVec::<Dynamic>::new();
|
let mut arg_values = StaticVec::<Dynamic>::new();
|
||||||
|
|
||||||
for arg_expr in arg_exprs.iter() {
|
for arg_expr in arg_exprs.iter() {
|
||||||
@ -1055,7 +1069,7 @@ impl Engine {
|
|||||||
|
|
||||||
idx_values.push(Dynamic::from(arg_values));
|
idx_values.push(Dynamic::from(arg_values));
|
||||||
}
|
}
|
||||||
Expr::FnCall(_, _, _, _, _) => unreachable!(),
|
Expr::FnCall(_, _, _, _, _, _) => unreachable!(),
|
||||||
Expr::Property(_, _) => idx_values.push(()), // Store a placeholder - no need to copy the property name
|
Expr::Property(_, _) => idx_values.push(()), // Store a placeholder - no need to copy the property name
|
||||||
Expr::Index(lhs, rhs, _) | Expr::Dot(lhs, rhs, _) => {
|
Expr::Index(lhs, rhs, _) | Expr::Dot(lhs, rhs, _) => {
|
||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
@ -1158,7 +1172,7 @@ impl Engine {
|
|||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let args = &mut [val, &mut idx];
|
let args = &mut [val, &mut idx];
|
||||||
self.exec_fn_call(state, FUNC_INDEXER, args, None, op_pos, 0)
|
self.exec_fn_call(state, FUNC_INDEXER, 0, args, None, op_pos, 0)
|
||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Box::new(EvalAltResult::ErrorIndexingType(
|
Box::new(EvalAltResult::ErrorIndexingType(
|
||||||
@ -1196,9 +1210,16 @@ impl Engine {
|
|||||||
let args = &mut [&mut lhs_value.clone(), &mut value.clone()];
|
let args = &mut [&mut lhs_value.clone(), &mut value.clone()];
|
||||||
let def_value = Some(&def_value);
|
let def_value = Some(&def_value);
|
||||||
let pos = rhs.position();
|
let pos = rhs.position();
|
||||||
|
let op = "==";
|
||||||
|
|
||||||
|
// Qualifiers (none) + function name + argument `TypeId`'s.
|
||||||
|
let fn_spec = calc_fn_hash(empty(), op, args.iter().map(|a| a.type_id()));
|
||||||
|
let fn_def = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.call_fn_raw(None, state, "==", args, def_value, pos, level)?
|
.call_fn_raw(
|
||||||
|
None, state, op, fn_spec, fn_def, args, def_value, pos, level,
|
||||||
|
)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1239,9 +1260,10 @@ impl Engine {
|
|||||||
Expr::FloatConstant(f, _) => Ok((*f).into()),
|
Expr::FloatConstant(f, _) => Ok((*f).into()),
|
||||||
Expr::StringConstant(s, _) => Ok(s.to_string().into()),
|
Expr::StringConstant(s, _) => Ok(s.to_string().into()),
|
||||||
Expr::CharConstant(c, _) => Ok((*c).into()),
|
Expr::CharConstant(c, _) => Ok((*c).into()),
|
||||||
Expr::Variable(id, modules, index, pos) => {
|
Expr::Variable(id, modules, hash, index, pos) => {
|
||||||
let index = if state.always_search { None } else { *index };
|
let index = if state.always_search { None } else { *index };
|
||||||
let (val, _) = search_scope(scope, id, modules, index, *pos)?;
|
let mod_and_hash = modules.as_ref().map(|m| (m, *hash));
|
||||||
|
let (val, _) = search_scope(scope, id, mod_and_hash, index, *pos)?;
|
||||||
Ok(val.clone())
|
Ok(val.clone())
|
||||||
}
|
}
|
||||||
Expr::Property(_, _) => unreachable!(),
|
Expr::Property(_, _) => unreachable!(),
|
||||||
@ -1255,9 +1277,10 @@ impl Engine {
|
|||||||
|
|
||||||
match lhs.as_ref() {
|
match lhs.as_ref() {
|
||||||
// name = rhs
|
// name = rhs
|
||||||
Expr::Variable(id, modules, index, pos) => {
|
Expr::Variable(id, modules, hash, index, pos) => {
|
||||||
let index = if state.always_search { None } else { *index };
|
let index = if state.always_search { None } else { *index };
|
||||||
let (value_ptr, typ) = search_scope(scope, id, modules, index, *pos)?;
|
let mod_and_hash = modules.as_ref().map(|m| (m, *hash));
|
||||||
|
let (value_ptr, typ) = search_scope(scope, id, mod_and_hash, index, *pos)?;
|
||||||
match typ {
|
match typ {
|
||||||
ScopeEntryType::Constant => Err(Box::new(
|
ScopeEntryType::Constant => Err(Box::new(
|
||||||
EvalAltResult::ErrorAssignmentToConstant(id.to_string(), *pos),
|
EvalAltResult::ErrorAssignmentToConstant(id.to_string(), *pos),
|
||||||
@ -1332,7 +1355,7 @@ impl Engine {
|
|||||||
)))),
|
)))),
|
||||||
|
|
||||||
// Normal function call
|
// Normal function call
|
||||||
Expr::FnCall(fn_name, None, arg_exprs, def_val, pos) => {
|
Expr::FnCall(fn_name, None, hash, arg_exprs, def_val, pos) => {
|
||||||
let mut arg_values = arg_exprs
|
let mut arg_values = arg_exprs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, state, expr, level))
|
.map(|expr| self.eval_expr(scope, state, expr, level))
|
||||||
@ -1340,9 +1363,12 @@ impl Engine {
|
|||||||
|
|
||||||
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
|
let hash_fn_spec =
|
||||||
|
calc_fn_hash(empty(), KEYWORD_EVAL, once(TypeId::of::<String>()));
|
||||||
|
|
||||||
if fn_name.as_ref() == KEYWORD_EVAL
|
if fn_name.as_ref() == KEYWORD_EVAL
|
||||||
&& args.len() == 1
|
&& args.len() == 1
|
||||||
&& !self.has_override(state, KEYWORD_EVAL)
|
&& !self.has_override(state, hash_fn_spec, *hash)
|
||||||
{
|
{
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
@ -1361,13 +1387,13 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
// Normal function call - except for eval (handled above)
|
// Normal function call - except for eval (handled above)
|
||||||
let def_value = def_val.as_deref();
|
let def_value = def_val.as_deref();
|
||||||
self.exec_fn_call(state, fn_name, &mut args, def_value, *pos, level)
|
self.exec_fn_call(state, fn_name, *hash, &mut args, def_value, *pos, level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Module-qualified function call
|
// Module-qualified function call
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::FnCall(fn_name, Some(modules), arg_exprs, def_val, pos) => {
|
Expr::FnCall(fn_name, Some(modules), hash1, arg_exprs, def_val, pos) => {
|
||||||
let modules = modules.as_ref();
|
let modules = modules.as_ref();
|
||||||
|
|
||||||
let mut arg_values = arg_exprs
|
let mut arg_values = arg_exprs
|
||||||
@ -1392,7 +1418,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if let Some(fn_def) = module.get_qualified_scripted_fn(modules.key()) {
|
if let Some(fn_def) = module.get_qualified_scripted_fn(*hash1) {
|
||||||
self.call_script_fn(None, state, fn_def, &mut args, *pos, level)
|
self.call_script_fn(None, state, fn_def, &mut args, *pos, level)
|
||||||
} else {
|
} else {
|
||||||
// Then search in Rust functions
|
// Then search in Rust functions
|
||||||
@ -1400,12 +1426,11 @@ impl Engine {
|
|||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
||||||
let hash1 = modules.key();
|
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name, and
|
// 2) Calculate a second hash with no qualifiers, empty function name, and
|
||||||
// the actual list of parameter `TypeId`'.s
|
// the actual list of parameter `TypeId`'.s
|
||||||
let hash2 = calc_fn_hash(empty(), "", args.iter().map(|a| a.type_id()));
|
let hash2 = calc_fn_hash(empty(), "", args.iter().map(|a| a.type_id()));
|
||||||
// 3) The final hash is the XOR of the two hashes.
|
// 3) The final hash is the XOR of the two hashes.
|
||||||
let hash = hash1 ^ hash2;
|
let hash = *hash1 ^ hash2;
|
||||||
|
|
||||||
match module.get_qualified_fn(fn_name, hash, *pos) {
|
match module.get_qualified_fn(fn_name, hash, *pos) {
|
||||||
Ok(func) => func(&mut args, *pos),
|
Ok(func) => func(&mut args, *pos),
|
||||||
|
@ -250,7 +250,7 @@ impl Module {
|
|||||||
/// use rhai::Module;
|
/// use rhai::Module;
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64), false);
|
||||||
/// assert!(module.contains_fn(hash));
|
/// assert!(module.contains_fn(hash));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn contains_fn(&self, hash: u64) -> bool {
|
pub fn contains_fn(&self, hash: u64) -> bool {
|
||||||
@ -567,7 +567,7 @@ impl Module {
|
|||||||
/// use rhai::Module;
|
/// use rhai::Module;
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1), false);
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
|
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
|
||||||
@ -894,18 +894,14 @@ mod file {
|
|||||||
/// A `StaticVec` is used because most module-level access contains only one level,
|
/// A `StaticVec` is used because most module-level access contains only one level,
|
||||||
/// and it is wasteful to always allocate a `Vec` with one element.
|
/// and it is wasteful to always allocate a `Vec` with one element.
|
||||||
#[derive(Clone, Hash, Default)]
|
#[derive(Clone, Hash, Default)]
|
||||||
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>, u64);
|
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
||||||
|
|
||||||
impl fmt::Debug for ModuleRef {
|
impl fmt::Debug for ModuleRef {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.0, f)?;
|
fmt::Debug::fmt(&self.0, f)?;
|
||||||
|
|
||||||
if self.2 > 0 {
|
if let Some(index) = self.1 {
|
||||||
if let Some(index) = self.1 {
|
write!(f, " -> {}", index)
|
||||||
write!(f, " -> {},{:0>16x}", index, self.2)
|
|
||||||
} else {
|
|
||||||
write!(f, " -> {:0>16x}", self.2)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -937,17 +933,11 @@ impl fmt::Display for ModuleRef {
|
|||||||
|
|
||||||
impl From<StaticVec<(String, Position)>> for ModuleRef {
|
impl From<StaticVec<(String, Position)>> for ModuleRef {
|
||||||
fn from(modules: StaticVec<(String, Position)>) -> Self {
|
fn from(modules: StaticVec<(String, Position)>) -> Self {
|
||||||
Self(modules, None, 0)
|
Self(modules, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleRef {
|
impl ModuleRef {
|
||||||
pub(crate) fn key(&self) -> u64 {
|
|
||||||
self.2
|
|
||||||
}
|
|
||||||
pub(crate) fn set_key(&mut self, key: u64) {
|
|
||||||
self.2 = key
|
|
||||||
}
|
|
||||||
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
|
@ -367,12 +367,17 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
//id = id2 = expr2
|
//id = id2 = expr2
|
||||||
Expr::Assignment(id2, expr2, pos2) => match (*id, *id2) {
|
Expr::Assignment(id2, expr2, pos2) => match (*id, *id2) {
|
||||||
// var = var = expr2 -> var = expr2
|
// var = var = expr2 -> var = expr2
|
||||||
(Expr::Variable(var, None, sp, _), Expr::Variable(var2, None, sp2, _))
|
(Expr::Variable(var, None, index, sp, _), Expr::Variable(var2, None, _, sp2, _))
|
||||||
if var == var2 && sp == sp2 =>
|
if var == var2 && sp == sp2 =>
|
||||||
{
|
{
|
||||||
// Assignment to the same variable - fold
|
// Assignment to the same variable - fold
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Expr::Assignment(Box::new(Expr::Variable(var, None, sp, pos)), Box::new(optimize_expr(*expr2, state)), pos)
|
|
||||||
|
Expr::Assignment(
|
||||||
|
Box::new(Expr::Variable(var, None, index, sp, pos)),
|
||||||
|
Box::new(optimize_expr(*expr2, state))
|
||||||
|
, pos
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// id1 = id2 = expr2
|
// id1 = id2 = expr2
|
||||||
(id1, id2) => Expr::Assignment(
|
(id1, id2) => Expr::Assignment(
|
||||||
@ -544,18 +549,18 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FnCall(id, None, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref().as_ref())=>
|
Expr::FnCall(id, None, index, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref().as_ref())=>
|
||||||
Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
Expr::FnCall(id, None, index, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
Expr::FnCall(id, None, args, def_value, pos)
|
Expr::FnCall(id, None, index, args, def_value, pos)
|
||||||
if state.optimization_level == OptimizationLevel::Full // full optimizations
|
if state.optimization_level == OptimizationLevel::Full // full optimizations
|
||||||
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
||||||
=> {
|
=> {
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if state.fn_lib.iter().find(|(name, len)| name == id.as_ref() && *len == args.len()).is_some() {
|
if state.fn_lib.iter().find(|(name, len)| name == id.as_ref() && *len == args.len()).is_some() {
|
||||||
// A script-defined function overrides the built-in function - do not make the call
|
// A script-defined function overrides the built-in function - do not make the call
|
||||||
return Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos);
|
return Expr::FnCall(id, None, index, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
@ -586,16 +591,16 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
})
|
})
|
||||||
).unwrap_or_else(||
|
).unwrap_or_else(||
|
||||||
// Optimize function call arguments
|
// Optimize function call arguments
|
||||||
Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos)
|
Expr::FnCall(id, None, index, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(id, modules, args, def_value, pos) =>
|
Expr::FnCall(id, modules, index, args, def_value, pos) =>
|
||||||
Expr::FnCall(id, modules, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
Expr::FnCall(id, modules, index, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(name, None, _, pos) if state.contains_constant(&name) => {
|
Expr::Variable(name, None, _, _, pos) if state.contains_constant(&name) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
|
127
src/parser.rs
127
src/parser.rs
@ -382,6 +382,7 @@ pub enum Expr {
|
|||||||
Box<String>,
|
Box<String>,
|
||||||
#[cfg(not(feature = "no_module"))] Option<Box<ModuleRef>>,
|
#[cfg(not(feature = "no_module"))] Option<Box<ModuleRef>>,
|
||||||
#[cfg(feature = "no_module")] Option<ModuleRef>,
|
#[cfg(feature = "no_module")] Option<ModuleRef>,
|
||||||
|
u64,
|
||||||
Option<NonZeroUsize>,
|
Option<NonZeroUsize>,
|
||||||
Position,
|
Position,
|
||||||
),
|
),
|
||||||
@ -389,13 +390,14 @@ pub enum Expr {
|
|||||||
Property(String, Position),
|
Property(String, Position),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Stmt(Box<Stmt>, Position),
|
Stmt(Box<Stmt>, Position),
|
||||||
/// func(expr, ... ) - (function name, optional modules, arguments, optional default value, position)
|
/// func(expr, ... ) - (function name, optional modules, hash, arguments, optional default value, position)
|
||||||
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
|
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
|
||||||
/// and the function names are predictable, so no need to allocate a new `String`.
|
/// and the function names are predictable, so no need to allocate a new `String`.
|
||||||
FnCall(
|
FnCall(
|
||||||
Box<Cow<'static, str>>,
|
Box<Cow<'static, str>>,
|
||||||
#[cfg(not(feature = "no_module"))] Option<Box<ModuleRef>>,
|
#[cfg(not(feature = "no_module"))] Option<Box<ModuleRef>>,
|
||||||
#[cfg(feature = "no_module")] Option<ModuleRef>,
|
#[cfg(feature = "no_module")] Option<ModuleRef>,
|
||||||
|
u64,
|
||||||
Box<Vec<Expr>>,
|
Box<Vec<Expr>>,
|
||||||
Option<Box<Dynamic>>,
|
Option<Box<Dynamic>>,
|
||||||
Position,
|
Position,
|
||||||
@ -499,10 +501,10 @@ impl Expr {
|
|||||||
| Self::StringConstant(_, pos)
|
| Self::StringConstant(_, pos)
|
||||||
| Self::Array(_, pos)
|
| Self::Array(_, pos)
|
||||||
| Self::Map(_, pos)
|
| Self::Map(_, pos)
|
||||||
| Self::Variable(_, _, _, pos)
|
| Self::Variable(_, _, _, _, pos)
|
||||||
| Self::Property(_, pos)
|
| Self::Property(_, pos)
|
||||||
| Self::Stmt(_, pos)
|
| Self::Stmt(_, pos)
|
||||||
| Self::FnCall(_, _, _, _, pos)
|
| Self::FnCall(_, _, _, _, _, pos)
|
||||||
| Self::And(_, _, pos)
|
| Self::And(_, _, pos)
|
||||||
| Self::Or(_, _, pos)
|
| Self::Or(_, _, pos)
|
||||||
| Self::In(_, _, pos)
|
| Self::In(_, _, pos)
|
||||||
@ -527,10 +529,10 @@ impl Expr {
|
|||||||
| Self::StringConstant(_, pos)
|
| Self::StringConstant(_, pos)
|
||||||
| Self::Array(_, pos)
|
| Self::Array(_, pos)
|
||||||
| Self::Map(_, pos)
|
| Self::Map(_, pos)
|
||||||
| Self::Variable(_, _, _, pos)
|
| Self::Variable(_, _, _, _, pos)
|
||||||
| Self::Property(_, pos)
|
| Self::Property(_, pos)
|
||||||
| Self::Stmt(_, pos)
|
| Self::Stmt(_, pos)
|
||||||
| Self::FnCall(_, _, _, _, pos)
|
| Self::FnCall(_, _, _, _, _, pos)
|
||||||
| Self::And(_, _, pos)
|
| Self::And(_, _, pos)
|
||||||
| Self::Or(_, _, pos)
|
| Self::Or(_, _, pos)
|
||||||
| Self::In(_, _, pos)
|
| Self::In(_, _, pos)
|
||||||
@ -558,7 +560,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::Stmt(stmt, _) => stmt.is_pure(),
|
Self::Stmt(stmt, _) => stmt.is_pure(),
|
||||||
|
|
||||||
Self::Variable(_, _, _, _) => true,
|
Self::Variable(_, _, _, _, _) => true,
|
||||||
|
|
||||||
expr => expr.is_constant(),
|
expr => expr.is_constant(),
|
||||||
}
|
}
|
||||||
@ -611,7 +613,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::StringConstant(_, _)
|
Self::StringConstant(_, _)
|
||||||
| Self::Stmt(_, _)
|
| Self::Stmt(_, _)
|
||||||
| Self::FnCall(_, _, _, _, _)
|
| Self::FnCall(_, _, _, _, _, _)
|
||||||
| Self::Assignment(_, _, _)
|
| Self::Assignment(_, _, _)
|
||||||
| Self::Dot(_, _, _)
|
| Self::Dot(_, _, _)
|
||||||
| Self::Index(_, _, _)
|
| Self::Index(_, _, _)
|
||||||
@ -621,7 +623,7 @@ impl Expr {
|
|||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::Variable(_, _, _, _) => match token {
|
Self::Variable(_, _, _, _, _) => match token {
|
||||||
Token::LeftBracket | Token::LeftParen => true,
|
Token::LeftBracket | Token::LeftParen => true,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Token::DoubleColon => true,
|
Token::DoubleColon => true,
|
||||||
@ -638,7 +640,7 @@ impl Expr {
|
|||||||
/// Convert a `Variable` into a `Property`. All other variants are untouched.
|
/// Convert a `Variable` into a `Property`. All other variants are untouched.
|
||||||
pub(crate) fn into_property(self) -> Self {
|
pub(crate) fn into_property(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Variable(id, None, _, pos) => Self::Property(*id, pos),
|
Self::Variable(id, None, _, _, pos) => Self::Property(*id, pos),
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -725,24 +727,31 @@ fn parse_call_expr<'a>(
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
let hash1 = {
|
||||||
if let Some(modules) = modules.as_mut() {
|
if let Some(modules) = modules.as_mut() {
|
||||||
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
// i.e. qualifiers + function name + no parameters.
|
||||||
let hash1 = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
|
let hash1 = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name, and
|
// 2) Calculate a second hash with no qualifiers, empty function name, and
|
||||||
// the actual list of parameter `TypeId`'.s
|
// the actual list of parameter `TypeId`'.s
|
||||||
// 3) The final hash is the XOR of the two hashes.
|
// 3) The final hash is the XOR of the two hashes.
|
||||||
|
|
||||||
// Cache the first hash
|
hash1
|
||||||
modules.set_key(hash1);
|
} else {
|
||||||
modules.set_index(stack.find_module(&modules.get(0).0));
|
calc_fn_hash(empty(), &id, empty())
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
// Qualifiers (none) + function name + no parameters.
|
||||||
|
#[cfg(feature = "no_module")]
|
||||||
|
let hash1 = calc_fn_hash(empty(), &id, empty());
|
||||||
|
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
Box::new(id.into()),
|
Box::new(id.into()),
|
||||||
modules,
|
modules,
|
||||||
|
hash1,
|
||||||
Box::new(args),
|
Box::new(args),
|
||||||
None,
|
None,
|
||||||
begin,
|
begin,
|
||||||
@ -761,8 +770,10 @@ fn parse_call_expr<'a>(
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
let hash1 = {
|
||||||
if let Some(modules) = modules.as_mut() {
|
if let Some(modules) = modules.as_mut() {
|
||||||
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
||||||
@ -775,14 +786,19 @@ fn parse_call_expr<'a>(
|
|||||||
// the actual list of parameter `TypeId`'.s
|
// the actual list of parameter `TypeId`'.s
|
||||||
// 3) The final hash is the XOR of the two hashes.
|
// 3) The final hash is the XOR of the two hashes.
|
||||||
|
|
||||||
// Cache the first hash
|
hash1
|
||||||
modules.set_key(hash1);
|
} else {
|
||||||
modules.set_index(stack.find_module(&modules.get(0).0));
|
calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()))
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
// Qualifiers (none) + function name + dummy parameter types (one for each parameter).
|
||||||
|
#[cfg(feature = "no_module")]
|
||||||
|
let hash1 = calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()));
|
||||||
|
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
Box::new(id.into()),
|
Box::new(id.into()),
|
||||||
modules,
|
modules,
|
||||||
|
hash1,
|
||||||
Box::new(args),
|
Box::new(args),
|
||||||
None,
|
None,
|
||||||
begin,
|
begin,
|
||||||
@ -1139,7 +1155,7 @@ fn parse_primary<'a>(
|
|||||||
Token::StringConst(s) => Expr::StringConstant(s, pos),
|
Token::StringConst(s) => Expr::StringConstant(s, pos),
|
||||||
Token::Identifier(s) => {
|
Token::Identifier(s) => {
|
||||||
let index = stack.find(&s);
|
let index = stack.find(&s);
|
||||||
Expr::Variable(Box::new(s), None, index, pos)
|
Expr::Variable(Box::new(s), None, 0, index, pos)
|
||||||
}
|
}
|
||||||
Token::LeftParen => parse_paren_expr(input, stack, pos, allow_stmt_expr)?,
|
Token::LeftParen => parse_paren_expr(input, stack, pos, allow_stmt_expr)?,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1166,7 +1182,7 @@ fn parse_primary<'a>(
|
|||||||
|
|
||||||
root_expr = match (root_expr, token) {
|
root_expr = match (root_expr, token) {
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(id, modules, _, pos), Token::LeftParen) => {
|
(Expr::Variable(id, modules, _, _, pos), Token::LeftParen) => {
|
||||||
parse_call_expr(input, stack, *id, modules, pos, allow_stmt_expr)?
|
parse_call_expr(input, stack, *id, modules, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
(Expr::Property(id, pos), Token::LeftParen) => {
|
(Expr::Property(id, pos), Token::LeftParen) => {
|
||||||
@ -1174,7 +1190,7 @@ fn parse_primary<'a>(
|
|||||||
}
|
}
|
||||||
// module access
|
// module access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(Expr::Variable(id, mut modules, index, pos), Token::DoubleColon) => {
|
(Expr::Variable(id, mut modules, _, index, pos), Token::DoubleColon) => {
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Identifier(id2), pos2) => {
|
(Token::Identifier(id2), pos2) => {
|
||||||
if let Some(ref mut modules) = modules {
|
if let Some(ref mut modules) = modules {
|
||||||
@ -1185,7 +1201,7 @@ fn parse_primary<'a>(
|
|||||||
modules = Some(Box::new(m));
|
modules = Some(Box::new(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Variable(Box::new(id2), modules, index, pos2)
|
Expr::Variable(Box::new(id2), modules, 0, index, pos2)
|
||||||
}
|
}
|
||||||
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
||||||
}
|
}
|
||||||
@ -1203,10 +1219,9 @@ fn parse_primary<'a>(
|
|||||||
match &mut root_expr {
|
match &mut root_expr {
|
||||||
// Cache the hash key for module-qualified variables
|
// Cache the hash key for module-qualified variables
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::Variable(id, Some(modules), _, _) => {
|
Expr::Variable(id, Some(modules), hash, _, _) => {
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
|
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
|
||||||
modules.set_key(hash);
|
|
||||||
modules.set_index(stack.find_module(&modules.get(0).0));
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -1259,13 +1274,19 @@ fn parse_unary<'a>(
|
|||||||
Expr::FloatConstant(f, pos) => Ok(Expr::FloatConstant(-f, pos)),
|
Expr::FloatConstant(f, pos) => Ok(Expr::FloatConstant(-f, pos)),
|
||||||
|
|
||||||
// Call negative function
|
// Call negative function
|
||||||
e => Ok(Expr::FnCall(
|
e => {
|
||||||
Box::new("-".into()),
|
let op = "-";
|
||||||
None,
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
Box::new(vec![e]),
|
|
||||||
None,
|
Ok(Expr::FnCall(
|
||||||
pos,
|
Box::new(op.into()),
|
||||||
)),
|
None,
|
||||||
|
hash,
|
||||||
|
Box::new(vec![e]),
|
||||||
|
None,
|
||||||
|
pos,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// +expr
|
// +expr
|
||||||
@ -1276,9 +1297,13 @@ fn parse_unary<'a>(
|
|||||||
// !expr
|
// !expr
|
||||||
(Token::Bang, _) => {
|
(Token::Bang, _) => {
|
||||||
let pos = eat_token(input, Token::Bang);
|
let pos = eat_token(input, Token::Bang);
|
||||||
|
let op = "!";
|
||||||
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
|
|
||||||
Ok(Expr::FnCall(
|
Ok(Expr::FnCall(
|
||||||
Box::new("!".into()),
|
Box::new(op.into()),
|
||||||
None,
|
None,
|
||||||
|
hash,
|
||||||
Box::new(vec![parse_primary(input, stack, allow_stmt_expr)?]),
|
Box::new(vec![parse_primary(input, stack, allow_stmt_expr)?]),
|
||||||
Some(Box::new(false.into())), // NOT operator, when operating on invalid operand, defaults to false
|
Some(Box::new(false.into())), // NOT operator, when operating on invalid operand, defaults to false
|
||||||
pos,
|
pos,
|
||||||
@ -1298,8 +1323,8 @@ fn make_assignment_stmt<'a>(
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
match &lhs {
|
match &lhs {
|
||||||
Expr::Variable(_, _, None, _) => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
Expr::Variable(_, _, _, None, _) => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
||||||
Expr::Variable(name, _, Some(index), var_pos) => {
|
Expr::Variable(name, _, _, Some(index), var_pos) => {
|
||||||
match stack[(stack.len() - index.get())].1 {
|
match stack[(stack.len() - index.get())].1 {
|
||||||
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
@ -1310,10 +1335,10 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Index(idx_lhs, _, _) | Expr::Dot(idx_lhs, _, _) => match idx_lhs.as_ref() {
|
Expr::Index(idx_lhs, _, _) | Expr::Dot(idx_lhs, _, _) => match idx_lhs.as_ref() {
|
||||||
Expr::Variable(_, _, None, _) => {
|
Expr::Variable(_, _, _, None, _) => {
|
||||||
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
||||||
}
|
}
|
||||||
Expr::Variable(name, _, Some(index), var_pos) => {
|
Expr::Variable(name, _, _, Some(index), var_pos) => {
|
||||||
match stack[(stack.len() - index.get())].1 {
|
match stack[(stack.len() - index.get())].1 {
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
||||||
@ -1368,7 +1393,8 @@ fn parse_op_assignment_stmt<'a>(
|
|||||||
|
|
||||||
// lhs op= rhs -> lhs = op(lhs, rhs)
|
// lhs op= rhs -> lhs = op(lhs, rhs)
|
||||||
let args = vec![lhs_copy, rhs];
|
let args = vec![lhs_copy, rhs];
|
||||||
let rhs_expr = Expr::FnCall(Box::new(op.into()), None, Box::new(args), None, pos);
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(args.len()));
|
||||||
|
let rhs_expr = Expr::FnCall(Box::new(op.into()), None, hash, Box::new(args), None, pos);
|
||||||
make_assignment_stmt(stack, lhs, rhs_expr, pos)
|
make_assignment_stmt(stack, lhs, rhs_expr, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1388,12 +1414,12 @@ fn make_dot_expr(
|
|||||||
idx_pos,
|
idx_pos,
|
||||||
),
|
),
|
||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, rhs @ Expr::Variable(_, None, _, _)) | (lhs, rhs @ Expr::Property(_, _)) => {
|
(lhs, rhs @ Expr::Variable(_, None, _, _, _)) | (lhs, rhs @ Expr::Property(_, _)) => {
|
||||||
let lhs = if is_index { lhs.into_property() } else { lhs };
|
let lhs = if is_index { lhs.into_property() } else { lhs };
|
||||||
Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos)
|
Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos)
|
||||||
}
|
}
|
||||||
// lhs.module::id - syntax error
|
// lhs.module::id - syntax error
|
||||||
(_, Expr::Variable(_, Some(modules), _, _)) => {
|
(_, Expr::Variable(_, Some(modules), _, _, _)) => {
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
unreachable!();
|
unreachable!();
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1609,6 +1635,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::Plus => Expr::FnCall(
|
Token::Plus => Expr::FnCall(
|
||||||
Box::new("+".into()),
|
Box::new("+".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "+", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1616,6 +1643,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::Minus => Expr::FnCall(
|
Token::Minus => Expr::FnCall(
|
||||||
Box::new("-".into()),
|
Box::new("-".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "-", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1623,6 +1651,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::Multiply => Expr::FnCall(
|
Token::Multiply => Expr::FnCall(
|
||||||
Box::new("*".into()),
|
Box::new("*".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "*", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1630,6 +1659,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::Divide => Expr::FnCall(
|
Token::Divide => Expr::FnCall(
|
||||||
Box::new("/".into()),
|
Box::new("/".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "/", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1638,6 +1668,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::LeftShift => Expr::FnCall(
|
Token::LeftShift => Expr::FnCall(
|
||||||
Box::new("<<".into()),
|
Box::new("<<".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "<<", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1645,6 +1676,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::RightShift => Expr::FnCall(
|
Token::RightShift => Expr::FnCall(
|
||||||
Box::new(">>".into()),
|
Box::new(">>".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), ">>", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1652,6 +1684,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::Modulo => Expr::FnCall(
|
Token::Modulo => Expr::FnCall(
|
||||||
Box::new("%".into()),
|
Box::new("%".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "%", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1659,6 +1692,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::PowerOf => Expr::FnCall(
|
Token::PowerOf => Expr::FnCall(
|
||||||
Box::new("~".into()),
|
Box::new("~".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "~", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1668,6 +1702,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::EqualsTo => Expr::FnCall(
|
Token::EqualsTo => Expr::FnCall(
|
||||||
Box::new("==".into()),
|
Box::new("==".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "==", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
@ -1675,6 +1710,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::NotEqualsTo => Expr::FnCall(
|
Token::NotEqualsTo => Expr::FnCall(
|
||||||
Box::new("!=".into()),
|
Box::new("!=".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "!=", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
@ -1682,6 +1718,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::LessThan => Expr::FnCall(
|
Token::LessThan => Expr::FnCall(
|
||||||
Box::new("<".into()),
|
Box::new("<".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "<", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
@ -1689,6 +1726,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::LessThanEqualsTo => Expr::FnCall(
|
Token::LessThanEqualsTo => Expr::FnCall(
|
||||||
Box::new("<=".into()),
|
Box::new("<=".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "<=", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
@ -1696,6 +1734,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::GreaterThan => Expr::FnCall(
|
Token::GreaterThan => Expr::FnCall(
|
||||||
Box::new(">".into()),
|
Box::new(">".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), ">", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
@ -1703,6 +1742,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::GreaterThanEqualsTo => Expr::FnCall(
|
Token::GreaterThanEqualsTo => Expr::FnCall(
|
||||||
Box::new(">=".into()),
|
Box::new(">=".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), ">=", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
cmp_default,
|
cmp_default,
|
||||||
pos,
|
pos,
|
||||||
@ -1713,6 +1753,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::Ampersand => Expr::FnCall(
|
Token::Ampersand => Expr::FnCall(
|
||||||
Box::new("&".into()),
|
Box::new("&".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "&", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1720,6 +1761,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::Pipe => Expr::FnCall(
|
Token::Pipe => Expr::FnCall(
|
||||||
Box::new("|".into()),
|
Box::new("|".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "|", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
@ -1727,6 +1769,7 @@ fn parse_binary_op<'a>(
|
|||||||
Token::XOr => Expr::FnCall(
|
Token::XOr => Expr::FnCall(
|
||||||
Box::new("^".into()),
|
Box::new("^".into()),
|
||||||
None,
|
None,
|
||||||
|
calc_fn_hash(empty(), "^", repeat(EMPTY_TYPE_ID()).take(2)),
|
||||||
Box::new(vec![current_lhs, rhs]),
|
Box::new(vec![current_lhs, rhs]),
|
||||||
None,
|
None,
|
||||||
pos,
|
pos,
|
||||||
|
Loading…
Reference in New Issue
Block a user