Refactor.
This commit is contained in:
parent
a7bfac21bd
commit
29159b359b
150
src/engine.rs
150
src/engine.rs
@ -890,6 +890,8 @@ impl Engine {
|
|||||||
match rhs {
|
match rhs {
|
||||||
// xxx.fn_name(arg_expr_list)
|
// xxx.fn_name(arg_expr_list)
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
|
let ((name, pos), modules, hash, args, def_val) = x.as_ref();
|
||||||
|
|
||||||
let mut args: Vec<_> = once(obj)
|
let mut args: Vec<_> = once(obj)
|
||||||
.chain(
|
.chain(
|
||||||
idx_val
|
idx_val
|
||||||
@ -898,10 +900,9 @@ impl Engine {
|
|||||||
.iter_mut(),
|
.iter_mut(),
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
let def_val = x.4.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, &x.0, x.2, &mut args, def_val, x.5, 0)
|
self.exec_fn_call(state, name, *hash, &mut args, def_val.as_ref(), *pos, 0)
|
||||||
.map(|v| (v, true))
|
.map(|v| (v, true))
|
||||||
}
|
}
|
||||||
// xxx.module::fn_name(...) - syntax error
|
// xxx.module::fn_name(...) - syntax error
|
||||||
@ -1034,17 +1035,18 @@ impl Engine {
|
|||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let index = if state.always_search { None } else { x.3 };
|
let ((name, pos), modules, hash, index) = x.as_ref();
|
||||||
let (target, typ) =
|
let index = if state.always_search { None } else { *index };
|
||||||
search_scope(scope, &x.0, x.1.as_ref().map(|m| (m, x.2)), index, x.4)?;
|
let mod_and_hash = modules.as_ref().map(|m| (m, *hash));
|
||||||
|
let (target, typ) = search_scope(scope, &name, mod_and_hash, index, *pos)?;
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
match typ {
|
match typ {
|
||||||
ScopeEntryType::Module => unreachable!(),
|
ScopeEntryType::Module => unreachable!(),
|
||||||
ScopeEntryType::Constant if new_val.is_some() => {
|
ScopeEntryType::Constant if new_val.is_some() => {
|
||||||
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||||
x.0.clone(),
|
name.clone(),
|
||||||
x.4,
|
*pos,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
||||||
@ -1290,9 +1292,10 @@ impl Engine {
|
|||||||
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
||||||
Expr::CharConstant(x) => Ok(x.0.into()),
|
Expr::CharConstant(x) => Ok(x.0.into()),
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let index = if state.always_search { None } else { x.3 };
|
let ((name, pos), modules, hash, index) = x.as_ref();
|
||||||
let mod_and_hash = x.1.as_ref().map(|m| (m, x.2));
|
let index = if state.always_search { None } else { *index };
|
||||||
let (val, _) = search_scope(scope, &x.0, mod_and_hash, index, x.4)?;
|
let mod_and_hash = modules.as_ref().map(|m| (m, *hash));
|
||||||
|
let (val, _) = search_scope(scope, name, mod_and_hash, index, *pos)?;
|
||||||
Ok(val.clone())
|
Ok(val.clone())
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!(),
|
Expr::Property(_) => unreachable!(),
|
||||||
@ -1308,12 +1311,14 @@ impl Engine {
|
|||||||
match &x.0 {
|
match &x.0 {
|
||||||
// name = rhs
|
// name = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let index = if state.always_search { None } else { x.3 };
|
let ((name, pos), modules, hash, index) = x.as_ref();
|
||||||
let mod_and_hash = x.1.as_ref().map(|m| (m, x.2));
|
let index = if state.always_search { None } else { *index };
|
||||||
let (value_ptr, typ) = search_scope(scope, &x.0, mod_and_hash, index, x.4)?;
|
let mod_and_hash = modules.as_ref().map(|m| (m, *hash));
|
||||||
|
let (value_ptr, typ) =
|
||||||
|
search_scope(scope, name, mod_and_hash, index, *pos)?;
|
||||||
match typ {
|
match typ {
|
||||||
ScopeEntryType::Constant => Err(Box::new(
|
ScopeEntryType::Constant => Err(Box::new(
|
||||||
EvalAltResult::ErrorAssignmentToConstant(x.0.clone(), x.4),
|
EvalAltResult::ErrorAssignmentToConstant(name.clone(), *pos),
|
||||||
)),
|
)),
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
*value_ptr = rhs_val;
|
*value_ptr = rhs_val;
|
||||||
@ -1375,7 +1380,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
|
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
|
||||||
x.0.iter()
|
x.0.iter()
|
||||||
.map(|(key, expr, _)| {
|
.map(|((key, _), expr)| {
|
||||||
self.eval_expr(scope, state, expr, level)
|
self.eval_expr(scope, state, expr, level)
|
||||||
.map(|val| (key.clone(), val))
|
.map(|val| (key.clone(), val))
|
||||||
})
|
})
|
||||||
@ -1384,8 +1389,10 @@ impl Engine {
|
|||||||
|
|
||||||
// Normal function call
|
// Normal function call
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let mut arg_values =
|
let ((name, pos), _, hash_fn_def, args_expr, def_val) = x.as_ref();
|
||||||
x.3.iter()
|
|
||||||
|
let mut arg_values = args_expr
|
||||||
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, state, expr, level))
|
.map(|expr| self.eval_expr(scope, state, expr, level))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
@ -1394,15 +1401,16 @@ impl Engine {
|
|||||||
let hash_fn_spec =
|
let hash_fn_spec =
|
||||||
calc_fn_hash(empty(), KEYWORD_EVAL, once(TypeId::of::<String>()));
|
calc_fn_hash(empty(), KEYWORD_EVAL, once(TypeId::of::<String>()));
|
||||||
|
|
||||||
if x.0 == KEYWORD_EVAL
|
if name == KEYWORD_EVAL
|
||||||
&& args.len() == 1
|
&& args.len() == 1
|
||||||
&& !self.has_override(state, hash_fn_spec, x.2)
|
&& !self.has_override(state, hash_fn_spec, *hash_fn_def)
|
||||||
{
|
{
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
|
|
||||||
// Evaluate the text string as a script
|
// Evaluate the text string as a script
|
||||||
let result = self.eval_script_expr(scope, state, args[0], x.3[0].position());
|
let result =
|
||||||
|
self.eval_script_expr(scope, state, args[0], args_expr[0].position());
|
||||||
|
|
||||||
if scope.len() != prev_len {
|
if scope.len() != prev_len {
|
||||||
// IMPORTANT! If the eval defines new variables in the current scope,
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||||
@ -1413,18 +1421,26 @@ impl Engine {
|
|||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
// Normal function call - except for eval (handled above)
|
// Normal function call - except for eval (handled above)
|
||||||
let def_value = x.4.as_deref();
|
self.exec_fn_call(
|
||||||
self.exec_fn_call(state, &x.0, x.2, &mut args, def_value, x.5, level)
|
state,
|
||||||
|
name,
|
||||||
|
*hash_fn_def,
|
||||||
|
&mut args,
|
||||||
|
def_val.as_ref(),
|
||||||
|
*pos,
|
||||||
|
level,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Module-qualified function call
|
// Module-qualified function call
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::FnCall(x) if x.1.is_some() => {
|
Expr::FnCall(x) if x.1.is_some() => {
|
||||||
let modules = x.1.as_ref().unwrap();
|
let ((name, pos), modules, hash_fn_def, args_expr, def_val) = x.as_ref();
|
||||||
|
let modules = modules.as_ref().unwrap();
|
||||||
|
|
||||||
let mut arg_values =
|
let mut arg_values = args_expr
|
||||||
x.3.iter()
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, state, expr, level))
|
.map(|expr| self.eval_expr(scope, state, expr, level))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
@ -1445,8 +1461,8 @@ 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(x.2) {
|
if let Some(fn_def) = module.get_qualified_scripted_fn(*hash_fn_def) {
|
||||||
self.call_script_fn(None, state, fn_def, &mut args, x.5, 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
|
||||||
|
|
||||||
@ -1455,13 +1471,13 @@ impl Engine {
|
|||||||
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
||||||
// 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 hash_fn_args = 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 = x.2 ^ hash2;
|
let hash = *hash_fn_def ^ hash_fn_args;
|
||||||
|
|
||||||
match module.get_qualified_fn(&x.0, hash, x.5) {
|
match module.get_qualified_fn(name, hash, *pos) {
|
||||||
Ok(func) => func(&mut args, x.5),
|
Ok(func) => func(&mut args, *pos),
|
||||||
Err(_) if x.4.is_some() => Ok(x.4.as_deref().unwrap().clone()),
|
Err(_) if def_val.is_some() => Ok(def_val.clone().unwrap()),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1469,35 +1485,41 @@ impl Engine {
|
|||||||
|
|
||||||
Expr::In(x) => self.eval_in_expr(scope, state, &x.0, &x.1, level),
|
Expr::In(x) => self.eval_in_expr(scope, state, &x.0, &x.1, level),
|
||||||
|
|
||||||
Expr::And(x) => Ok((self
|
Expr::And(x) => {
|
||||||
.eval_expr(scope, state, &x.0, level)?
|
let (lhs, rhs, _) = x.as_ref();
|
||||||
|
Ok((self
|
||||||
|
.eval_expr(scope, state, lhs, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), x.0.position())
|
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
|
||||||
})?
|
})?
|
||||||
&& // Short-circuit using &&
|
&& // Short-circuit using &&
|
||||||
self
|
self
|
||||||
.eval_expr(scope, state, &x.1, level)?
|
.eval_expr(scope, state, rhs, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), x.1.position())
|
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
|
||||||
})?)
|
})?)
|
||||||
.into()),
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
Expr::Or(x) => Ok((self
|
Expr::Or(x) => {
|
||||||
.eval_expr(scope, state, &x.0, level)?
|
let (lhs, rhs, _) = x.as_ref();
|
||||||
|
Ok((self
|
||||||
|
.eval_expr(scope, state, lhs, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), x.0.position())
|
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
|
||||||
})?
|
})?
|
||||||
|| // Short-circuit using ||
|
|| // Short-circuit using ||
|
||||||
self
|
self
|
||||||
.eval_expr(scope, state, &x.1, level)?
|
.eval_expr(scope, state, rhs, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), x.1.position())
|
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
|
||||||
})?)
|
})?)
|
||||||
.into()),
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
Expr::True(_) => Ok(true.into()),
|
Expr::True(_) => Ok(true.into()),
|
||||||
Expr::False(_) => Ok(false.into()),
|
Expr::False(_) => Ok(false.into()),
|
||||||
@ -1633,56 +1655,56 @@ impl Engine {
|
|||||||
Stmt::Break(pos) => Err(Box::new(EvalAltResult::ErrorLoopBreak(true, *pos))),
|
Stmt::Break(pos) => Err(Box::new(EvalAltResult::ErrorLoopBreak(true, *pos))),
|
||||||
|
|
||||||
// Return value
|
// Return value
|
||||||
Stmt::ReturnWithVal(x) if x.0.is_some() && x.1 == ReturnType::Return => {
|
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
|
||||||
Err(Box::new(EvalAltResult::Return(
|
Err(Box::new(EvalAltResult::Return(
|
||||||
self.eval_expr(scope, state, x.0.as_ref().unwrap(), level)?,
|
self.eval_expr(scope, state, x.1.as_ref().unwrap(), level)?,
|
||||||
x.2,
|
(x.0).1,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty return
|
// Empty return
|
||||||
Stmt::ReturnWithVal(x) if x.1 == ReturnType::Return => {
|
Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Return => {
|
||||||
Err(Box::new(EvalAltResult::Return(Default::default(), x.2)))
|
Err(Box::new(EvalAltResult::Return(Default::default(), (x.0).1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw value
|
// Throw value
|
||||||
Stmt::ReturnWithVal(x) if x.0.is_some() && x.1 == ReturnType::Exception => {
|
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => {
|
||||||
let val = self.eval_expr(scope, state, x.0.as_ref().unwrap(), level)?;
|
let val = self.eval_expr(scope, state, x.1.as_ref().unwrap(), level)?;
|
||||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
val.take_string().unwrap_or_else(|_| "".to_string()),
|
val.take_string().unwrap_or_else(|_| "".to_string()),
|
||||||
x.2,
|
(x.0).1,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty throw
|
// Empty throw
|
||||||
Stmt::ReturnWithVal(x) if x.1 == ReturnType::Exception => {
|
Stmt::ReturnWithVal(x) if (x.0).0 == ReturnType::Exception => {
|
||||||
Err(Box::new(EvalAltResult::ErrorRuntime("".into(), x.2)))
|
Err(Box::new(EvalAltResult::ErrorRuntime("".into(), (x.0).1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt::ReturnWithVal(_) => unreachable!(),
|
Stmt::ReturnWithVal(_) => unreachable!(),
|
||||||
|
|
||||||
// Let statement
|
// Let statement
|
||||||
Stmt::Let(x) if x.1.is_some() => {
|
Stmt::Let(x) if x.1.is_some() => {
|
||||||
let val = self.eval_expr(scope, state, x.1.as_ref().unwrap(), level)?;
|
let ((var_name, _), expr) = x.as_ref();
|
||||||
|
let val = self.eval_expr(scope, state, expr.as_ref().unwrap(), level)?;
|
||||||
// TODO - avoid copying variable name in inner block?
|
// TODO - avoid copying variable name in inner block?
|
||||||
let var_name = x.0.clone();
|
scope.push_dynamic_value(var_name.clone(), ScopeEntryType::Normal, val, false);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt::Let(x) => {
|
Stmt::Let(x) => {
|
||||||
|
let ((var_name, _), _) = x.as_ref();
|
||||||
// TODO - avoid copying variable name in inner block?
|
// TODO - avoid copying variable name in inner block?
|
||||||
let var_name = x.0.clone();
|
scope.push(var_name.clone(), ());
|
||||||
scope.push(var_name, ());
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Const statement
|
// Const statement
|
||||||
Stmt::Const(x) if x.1.is_constant() => {
|
Stmt::Const(x) if x.1.is_constant() => {
|
||||||
let val = self.eval_expr(scope, state, &x.1, level)?;
|
let ((var_name, _), expr) = x.as_ref();
|
||||||
|
let val = self.eval_expr(scope, state, &expr, level)?;
|
||||||
// TODO - avoid copying variable name in inner block?
|
// TODO - avoid copying variable name in inner block?
|
||||||
let var_name = x.0.clone();
|
scope.push_dynamic_value(var_name.clone(), ScopeEntryType::Constant, val, true);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1691,7 +1713,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Import statement
|
// Import statement
|
||||||
Stmt::Import(x) => {
|
Stmt::Import(x) => {
|
||||||
let (expr, name, _) = x.as_ref();
|
let (expr, (name, _)) = x.as_ref();
|
||||||
|
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
unreachable!();
|
unreachable!();
|
||||||
@ -1725,7 +1747,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Export statement
|
// Export statement
|
||||||
Stmt::Export(list) => {
|
Stmt::Export(list) => {
|
||||||
for (id, id_pos, rename) in list.as_ref() {
|
for ((id, id_pos), rename) in list.as_ref() {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
|
||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
|
@ -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), false);
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||||
/// 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 {
|
||||||
@ -289,7 +289,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), false);
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_0<K: Into<String>, T: Into<Dynamic>>(
|
pub fn set_fn_0<K: Into<String>, T: Into<Dynamic>>(
|
||||||
@ -297,7 +297,6 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
is_private: bool,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &mut FnCallArgs, pos| {
|
let f = move |_: &mut FnCallArgs, pos| {
|
||||||
func()
|
func()
|
||||||
@ -305,12 +304,7 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![];
|
let arg_types = vec![];
|
||||||
let access = if is_private {
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
FnAccess::Private
|
|
||||||
} else {
|
|
||||||
FnAccess::Public
|
|
||||||
};
|
|
||||||
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
||||||
@ -323,7 +317,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), false);
|
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_1<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_1<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
@ -331,7 +325,6 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
is_private: bool,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
func(mem::take(args[0]).cast::<A>())
|
func(mem::take(args[0]).cast::<A>())
|
||||||
@ -339,12 +332,7 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>()];
|
let arg_types = vec![TypeId::of::<A>()];
|
||||||
let access = if is_private {
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
FnAccess::Private
|
|
||||||
} else {
|
|
||||||
FnAccess::Public
|
|
||||||
};
|
|
||||||
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
||||||
@ -357,7 +345,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_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) }, false);
|
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_1_mut<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_1_mut<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
@ -365,7 +353,6 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
is_private: bool,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
func(args[0].downcast_mut::<A>().unwrap())
|
func(args[0].downcast_mut::<A>().unwrap())
|
||||||
@ -373,12 +360,7 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>()];
|
let arg_types = vec![TypeId::of::<A>()];
|
||||||
let access = if is_private {
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
FnAccess::Private
|
|
||||||
} else {
|
|
||||||
FnAccess::Public
|
|
||||||
};
|
|
||||||
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||||
@ -393,7 +375,7 @@ impl Module {
|
|||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
|
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
|
||||||
/// Ok(x + y.len() as i64)
|
/// Ok(x + y.len() as i64)
|
||||||
/// }, false);
|
/// });
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_2<K: Into<String>, A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_2<K: Into<String>, A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||||
@ -401,7 +383,6 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
is_private: bool,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let a = mem::take(args[0]).cast::<A>();
|
let a = mem::take(args[0]).cast::<A>();
|
||||||
@ -412,12 +393,7 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
let access = if is_private {
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
FnAccess::Private
|
|
||||||
} else {
|
|
||||||
FnAccess::Public
|
|
||||||
};
|
|
||||||
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
||||||
@ -431,7 +407,7 @@ impl Module {
|
|||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
|
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
|
||||||
/// *x += y.len() as i64; Ok(*x)
|
/// *x += y.len() as i64; Ok(*x)
|
||||||
/// }, false);
|
/// });
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_2_mut<
|
pub fn set_fn_2_mut<
|
||||||
@ -444,7 +420,6 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
is_private: bool,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let b = mem::take(args[1]).cast::<B>();
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
@ -455,12 +430,7 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
let access = if is_private {
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
FnAccess::Private
|
|
||||||
} else {
|
|
||||||
FnAccess::Public
|
|
||||||
};
|
|
||||||
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||||
@ -475,7 +445,7 @@ impl Module {
|
|||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
|
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
|
||||||
/// Ok(x + y.len() as i64 + z)
|
/// Ok(x + y.len() as i64 + z)
|
||||||
/// }, false);
|
/// });
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_3<
|
pub fn set_fn_3<
|
||||||
@ -489,7 +459,6 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
is_private: bool,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let a = mem::take(args[0]).cast::<A>();
|
let a = mem::take(args[0]).cast::<A>();
|
||||||
@ -501,12 +470,7 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
let access = if is_private {
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
FnAccess::Private
|
|
||||||
} else {
|
|
||||||
FnAccess::Public
|
|
||||||
};
|
|
||||||
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||||
@ -522,7 +486,7 @@ impl Module {
|
|||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
|
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
|
||||||
/// *x += y.len() as i64 + z; Ok(*x)
|
/// *x += y.len() as i64 + z; Ok(*x)
|
||||||
/// }, false);
|
/// });
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_3_mut<
|
pub fn set_fn_3_mut<
|
||||||
@ -536,7 +500,6 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
is_private: bool,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let b = mem::take(args[1]).cast::<B>();
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
@ -548,12 +511,7 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
let access = if is_private {
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
FnAccess::Private
|
|
||||||
} else {
|
|
||||||
FnAccess::Public
|
|
||||||
};
|
|
||||||
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a Rust function.
|
/// Get a Rust function.
|
||||||
@ -567,7 +525,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), false);
|
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||||
/// 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>> {
|
||||||
@ -694,16 +652,16 @@ impl Module {
|
|||||||
// 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 = calc_fn_hash(
|
let hash_fn_def = calc_fn_hash(
|
||||||
qualifiers.iter().map(|v| *v),
|
qualifiers.iter().map(|v| *v),
|
||||||
fn_name,
|
fn_name,
|
||||||
repeat(EMPTY_TYPE_ID()).take(params.len()),
|
repeat(EMPTY_TYPE_ID()).take(params.len()),
|
||||||
);
|
);
|
||||||
// 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(), "", params.iter().cloned());
|
let hash_fn_args = calc_fn_hash(empty(), "", params.iter().cloned());
|
||||||
// 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 = hash_fn_def ^ hash_fn_args;
|
||||||
|
|
||||||
functions.push((hash, func.clone()));
|
functions.push((hash, func.clone()));
|
||||||
}
|
}
|
||||||
|
@ -220,15 +220,13 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
optimize_stmt(x.2, state, false),
|
optimize_stmt(x.2, state, false),
|
||||||
))),
|
))),
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Let(x) if x.1.is_some() => Stmt::Let(Box::new((
|
Stmt::Let(x) if x.1.is_some() => {
|
||||||
x.0,
|
Stmt::Let(Box::new((x.0, Some(optimize_expr(x.1.unwrap(), state)))))
|
||||||
Some(optimize_expr(x.1.unwrap(), state)),
|
}
|
||||||
x.2,
|
|
||||||
))),
|
|
||||||
// let id;
|
// let id;
|
||||||
stmt @ Stmt::Let(_) => stmt,
|
stmt @ Stmt::Let(_) => stmt,
|
||||||
// import expr as id;
|
// import expr as id;
|
||||||
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1, x.2))),
|
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))),
|
||||||
// { block }
|
// { block }
|
||||||
Stmt::Block(x) => {
|
Stmt::Block(x) => {
|
||||||
let orig_len = x.0.len(); // Original number of statements in the block, for change detection
|
let orig_len = x.0.len(); // Original number of statements in the block, for change detection
|
||||||
@ -241,9 +239,10 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
.map(|stmt| match stmt {
|
.map(|stmt| match stmt {
|
||||||
// Add constant into the state
|
// Add constant into the state
|
||||||
Stmt::Const(v) => {
|
Stmt::Const(v) => {
|
||||||
state.push_constant(&v.0, v.1);
|
let ((name, pos), expr) = *v;
|
||||||
|
state.push_constant(&name, expr);
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Stmt::Noop(v.2) // No need to keep constants
|
Stmt::Noop(pos) // No need to keep constants
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(stmt, state, preserve_result),
|
_ => optimize_stmt(stmt, state, preserve_result),
|
||||||
@ -336,11 +335,9 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
// expr;
|
// expr;
|
||||||
Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))),
|
Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))),
|
||||||
// return expr;
|
// return expr;
|
||||||
Stmt::ReturnWithVal(x) if x.0.is_some() => Stmt::ReturnWithVal(Box::new((
|
Stmt::ReturnWithVal(x) if x.1.is_some() => {
|
||||||
Some(optimize_expr(x.0.unwrap(), state)),
|
Stmt::ReturnWithVal(Box::new((x.0, Some(optimize_expr(x.1.unwrap(), state)))))
|
||||||
x.1,
|
}
|
||||||
x.2,
|
|
||||||
))),
|
|
||||||
// All other statements - skip
|
// All other statements - skip
|
||||||
stmt => stmt,
|
stmt => stmt,
|
||||||
}
|
}
|
||||||
@ -394,13 +391,13 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x) => match (x.0, x.1) {
|
Expr::Dot(x) => match (x.0, x.1) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x, _)| x.is_pure()) => {
|
(Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.into_iter().find(|(name, _, _)| name == &p.0)
|
m.0.into_iter().find(|((name, _), _)| name == &p.0)
|
||||||
.map(|(_, expr, _)| expr.set_position(pos))
|
.map(|(_, expr)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
@ -420,13 +417,13 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
a.0.remove(i.0 as usize).set_position(a.1)
|
a.0.remove(i.0 as usize).set_position(a.1)
|
||||||
}
|
}
|
||||||
// map[string]
|
// map[string]
|
||||||
(Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x, _)| x.is_pure()) => {
|
(Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.into_iter().find(|(name, _, _)| name == &s.0)
|
m.0.into_iter().find(|((name, _), _)| name == &s.0)
|
||||||
.map(|(_, expr, _)| expr.set_position(pos))
|
.map(|(_, expr)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// string[int]
|
// string[int]
|
||||||
@ -448,7 +445,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(m) => Expr::Map(Box::new((m.0
|
Expr::Map(m) => Expr::Map(Box::new((m.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(key, expr, pos)| (key, optimize_expr(expr, state), pos))
|
.map(|((key, pos), expr)| ((key, pos), optimize_expr(expr, state)))
|
||||||
.collect(), m.1))),
|
.collect(), m.1))),
|
||||||
// lhs in rhs
|
// lhs in rhs
|
||||||
Expr::In(x) => match (x.0, x.1) {
|
Expr::In(x) => match (x.0, x.1) {
|
||||||
@ -465,7 +462,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
// "xxx" in #{...}
|
// "xxx" in #{...}
|
||||||
(Expr::StringConstant(a), Expr::Map(b)) => {
|
(Expr::StringConstant(a), Expr::Map(b)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if b.0.iter().find(|(name, _, _)| name == &a.0).is_some() {
|
if b.0.iter().find(|((name, _), _)| name == &a.0).is_some() {
|
||||||
Expr::True(a.1)
|
Expr::True(a.1)
|
||||||
} else {
|
} else {
|
||||||
Expr::False(a.1)
|
Expr::False(a.1)
|
||||||
@ -476,7 +473,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let ch = a.0.to_string();
|
let ch = a.0.to_string();
|
||||||
|
|
||||||
if b.0.iter().find(|(name, _, _)| name == &ch).is_some() {
|
if b.0.iter().find(|((name, _), _)| name == &ch).is_some() {
|
||||||
Expr::True(a.1)
|
Expr::True(a.1)
|
||||||
} else {
|
} else {
|
||||||
Expr::False(a.1)
|
Expr::False(a.1)
|
||||||
@ -527,7 +524,7 @@ 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(mut x) if DONT_EVAL_KEYWORDS.contains(&x.0.as_ref().as_ref())=> {
|
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref())=> {
|
||||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
}
|
}
|
||||||
@ -538,25 +535,27 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
&& state.optimization_level == OptimizationLevel::Full // full optimizations
|
&& state.optimization_level == OptimizationLevel::Full // full optimizations
|
||||||
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
||||||
=> {
|
=> {
|
||||||
|
let ((name, pos), _, _, args, def_value) = x.as_mut();
|
||||||
|
|
||||||
// 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 == x.0 && *len == x.3.len()).is_some() {
|
if state.fn_lib.iter().find(|(id, len)| *id == name && *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
|
||||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
return Expr::FnCall(x);
|
return Expr::FnCall(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut arg_values: Vec<_> = x.3.iter().map(Expr::get_constant_value).collect();
|
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||||
let mut call_args: Vec<_> = arg_values.iter_mut().collect();
|
let mut call_args: Vec<_> = arg_values.iter_mut().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
|
||||||
let arg_for_type_of = if x.0 == KEYWORD_TYPE_OF && call_args.len() == 1 {
|
let arg_for_type_of = if name == KEYWORD_TYPE_OF && call_args.len() == 1 {
|
||||||
state.engine.map_type_name(call_args[0].type_name())
|
state.engine.map_type_name(call_args[0].type_name())
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
call_fn(&state.engine.packages, &state.engine.base_package, &x.0, &mut call_args, x.5).ok()
|
call_fn(&state.engine.packages, &state.engine.base_package, name, &mut call_args, *pos).ok()
|
||||||
.and_then(|result|
|
.and_then(|result|
|
||||||
result.or_else(|| {
|
result.or_else(|| {
|
||||||
if !arg_for_type_of.is_empty() {
|
if !arg_for_type_of.is_empty() {
|
||||||
@ -564,9 +563,9 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
Some(arg_for_type_of.to_string().into())
|
Some(arg_for_type_of.to_string().into())
|
||||||
} else {
|
} else {
|
||||||
// Otherwise use the default value, if any
|
// Otherwise use the default value, if any
|
||||||
x.4.clone().map(|v| *v)
|
def_value.clone()
|
||||||
}
|
}
|
||||||
}).and_then(|result| map_dynamic_to_expr(result, x.5))
|
}).and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
.map(|expr| {
|
.map(|expr| {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
expr
|
expr
|
||||||
@ -585,11 +584,12 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.0) => {
|
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&(x.0).0) => {
|
||||||
|
let (name, pos) = x.0;
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
state.find_constant(&x.0).expect("should find constant in scope!").clone().set_position(x.4)
|
state.find_constant(&name).expect("should find constant in scope!").clone().set_position(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other expressions - skip
|
// All other expressions - skip
|
||||||
@ -643,9 +643,10 @@ fn optimize<'a>(
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, stmt)| {
|
.map(|(i, stmt)| {
|
||||||
match &stmt {
|
match &stmt {
|
||||||
Stmt::Const(x) => {
|
Stmt::Const(v) => {
|
||||||
// Load constants
|
// Load constants
|
||||||
state.push_constant(&x.0, x.1.clone());
|
let ((name, _), expr) = v.as_ref();
|
||||||
|
state.push_constant(&name, expr.clone());
|
||||||
stmt // Keep it in the global scope
|
stmt // Keep it in the global scope
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -718,12 +719,16 @@ pub fn optimize_into_ast(
|
|||||||
// {} -> Noop
|
// {} -> Noop
|
||||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||||
// { return val; } -> val
|
// { return val; } -> val
|
||||||
Stmt::ReturnWithVal(x) if x.0.is_some() && x.1 == ReturnType::Return => {
|
Stmt::ReturnWithVal(x)
|
||||||
Stmt::Expr(Box::new(x.0.unwrap()))
|
if x.1.is_some() && (x.0).0 == ReturnType::Return =>
|
||||||
|
{
|
||||||
|
Stmt::Expr(Box::new(x.1.unwrap()))
|
||||||
}
|
}
|
||||||
// { return; } -> ()
|
// { return; } -> ()
|
||||||
Stmt::ReturnWithVal(x) if x.0.is_none() && x.1 == ReturnType::Return => {
|
Stmt::ReturnWithVal(x)
|
||||||
Stmt::Expr(Box::new(Expr::Unit(x.2)))
|
if x.1.is_none() && (x.0).0 == ReturnType::Return =>
|
||||||
|
{
|
||||||
|
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
|
||||||
}
|
}
|
||||||
// All others
|
// All others
|
||||||
stmt => stmt,
|
stmt => stmt,
|
||||||
|
254
src/parser.rs
254
src/parser.rs
@ -280,9 +280,9 @@ pub enum Stmt {
|
|||||||
/// for id in expr { stmt }
|
/// for id in expr { stmt }
|
||||||
For(Box<(String, Expr, Stmt)>),
|
For(Box<(String, Expr, Stmt)>),
|
||||||
/// let id = expr
|
/// let id = expr
|
||||||
Let(Box<(String, Option<Expr>, Position)>),
|
Let(Box<((String, Position), Option<Expr>)>),
|
||||||
/// const id = expr
|
/// const id = expr
|
||||||
Const(Box<(String, Expr, Position)>),
|
Const(Box<((String, Position), Expr)>),
|
||||||
/// { stmt; ... }
|
/// { stmt; ... }
|
||||||
Block(Box<(Vec<Stmt>, Position)>),
|
Block(Box<(Vec<Stmt>, Position)>),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
@ -292,11 +292,11 @@ pub enum Stmt {
|
|||||||
/// break
|
/// break
|
||||||
Break(Position),
|
Break(Position),
|
||||||
/// return/throw
|
/// return/throw
|
||||||
ReturnWithVal(Box<(Option<Expr>, ReturnType, Position)>),
|
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>)>),
|
||||||
/// import expr as module
|
/// import expr as module
|
||||||
Import(Box<(Expr, String, Position)>),
|
Import(Box<(Expr, (String, Position))>),
|
||||||
/// expr id as name, ...
|
/// expr id as name, ...
|
||||||
Export(Box<Vec<(String, Position, Option<(String, Position)>)>>),
|
Export(Box<Vec<((String, Position), Option<(String, Position)>)>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
@ -304,17 +304,17 @@ impl Stmt {
|
|||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
Stmt::Noop(pos) | Stmt::Continue(pos) | Stmt::Break(pos) => *pos,
|
Stmt::Noop(pos) | Stmt::Continue(pos) | Stmt::Break(pos) => *pos,
|
||||||
Stmt::Let(x) => x.2,
|
Stmt::Let(x) => (x.0).1,
|
||||||
Stmt::Const(x) => x.2,
|
Stmt::Const(x) => (x.0).1,
|
||||||
Stmt::ReturnWithVal(x) => x.2,
|
Stmt::ReturnWithVal(x) => (x.0).1,
|
||||||
Stmt::Block(x) => x.1,
|
Stmt::Block(x) => x.1,
|
||||||
Stmt::IfThenElse(x) => x.0.position(),
|
Stmt::IfThenElse(x) => x.0.position(),
|
||||||
Stmt::Expr(x) => x.position(),
|
Stmt::Expr(x) => x.position(),
|
||||||
Stmt::While(x) => x.1.position(),
|
Stmt::While(x) => x.1.position(),
|
||||||
Stmt::Loop(x) => x.position(),
|
Stmt::Loop(x) => x.position(),
|
||||||
Stmt::For(x) => x.2.position(),
|
Stmt::For(x) => x.2.position(),
|
||||||
Stmt::Import(x) => x.2,
|
Stmt::Import(x) => (x.1).1,
|
||||||
Stmt::Export(x) => x.get(0).unwrap().1,
|
Stmt::Export(x) => (x.get(0).unwrap().0).1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,23 +379,22 @@ pub enum Expr {
|
|||||||
CharConstant(Box<(char, Position)>),
|
CharConstant(Box<(char, Position)>),
|
||||||
/// String constant.
|
/// String constant.
|
||||||
StringConstant(Box<(String, Position)>),
|
StringConstant(Box<(String, Position)>),
|
||||||
/// Variable access - (variable name, optional modules, hash, optional index, position)
|
/// Variable access - ((variable name, position), optional modules, hash, optional index)
|
||||||
Variable(Box<(String, MRef, u64, Option<NonZeroUsize>, Position)>),
|
Variable(Box<((String, Position), MRef, u64, Option<NonZeroUsize>)>),
|
||||||
/// Property access.
|
/// Property access.
|
||||||
Property(Box<(String, Position)>),
|
Property(Box<(String, Position)>),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Stmt(Box<(Stmt, Position)>),
|
Stmt(Box<(Stmt, Position)>),
|
||||||
/// func(expr, ... ) - (function name, optional modules, hash, arguments, optional default value, position)
|
/// func(expr, ... ) - ((function name, position), optional modules, hash, arguments, optional default value)
|
||||||
/// 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<(
|
Box<(
|
||||||
Cow<'static, str>,
|
(Cow<'static, str>, Position),
|
||||||
MRef,
|
MRef,
|
||||||
u64,
|
u64,
|
||||||
Vec<Expr>,
|
Vec<Expr>,
|
||||||
Option<Box<Dynamic>>,
|
Option<Dynamic>,
|
||||||
Position,
|
|
||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
/// expr = expr
|
/// expr = expr
|
||||||
@ -407,7 +406,7 @@ pub enum Expr {
|
|||||||
/// [ expr, ... ]
|
/// [ expr, ... ]
|
||||||
Array(Box<(Vec<Expr>, Position)>),
|
Array(Box<(Vec<Expr>, Position)>),
|
||||||
/// #{ name:expr, ... }
|
/// #{ name:expr, ... }
|
||||||
Map(Box<(Vec<(String, Expr, Position)>, Position)>),
|
Map(Box<(Vec<((String, Position), Expr)>, Position)>),
|
||||||
/// lhs in rhs
|
/// lhs in rhs
|
||||||
In(Box<(Expr, Expr, Position)>),
|
In(Box<(Expr, Expr, Position)>),
|
||||||
/// lhs && rhs
|
/// lhs && rhs
|
||||||
@ -445,10 +444,10 @@ impl Expr {
|
|||||||
))),
|
))),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::Map(x) if x.0.iter().all(|(_, v, _)| v.is_constant()) => {
|
Self::Map(x) if x.0.iter().all(|(_, v)| v.is_constant()) => {
|
||||||
Dynamic(Union::Map(Box::new(
|
Dynamic(Union::Map(Box::new(
|
||||||
x.0.iter()
|
x.0.iter()
|
||||||
.map(|(k, v, _)| (k.clone(), v.get_constant_value()))
|
.map(|((k, _), v)| (k.clone(), v.get_constant_value()))
|
||||||
.collect::<HashMap<_, _>>(),
|
.collect::<HashMap<_, _>>(),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
@ -493,8 +492,8 @@ impl Expr {
|
|||||||
Self::Map(x) => x.1,
|
Self::Map(x) => x.1,
|
||||||
Self::Property(x) => x.1,
|
Self::Property(x) => x.1,
|
||||||
Self::Stmt(x) => x.1,
|
Self::Stmt(x) => x.1,
|
||||||
Self::Variable(x) => x.4,
|
Self::Variable(x) => (x.0).1,
|
||||||
Self::FnCall(x) => x.5,
|
Self::FnCall(x) => (x.0).1,
|
||||||
|
|
||||||
Self::And(x) | Self::Or(x) | Self::In(x) => x.2,
|
Self::And(x) | Self::Or(x) | Self::In(x) => x.2,
|
||||||
|
|
||||||
@ -515,10 +514,10 @@ impl Expr {
|
|||||||
Self::StringConstant(x) => x.1 = new_pos,
|
Self::StringConstant(x) => x.1 = new_pos,
|
||||||
Self::Array(x) => x.1 = new_pos,
|
Self::Array(x) => x.1 = new_pos,
|
||||||
Self::Map(x) => x.1 = new_pos,
|
Self::Map(x) => x.1 = new_pos,
|
||||||
Self::Variable(x) => x.4 = new_pos,
|
Self::Variable(x) => (x.0).1 = new_pos,
|
||||||
Self::Property(x) => x.1 = new_pos,
|
Self::Property(x) => x.1 = new_pos,
|
||||||
Self::Stmt(x) => x.1 = new_pos,
|
Self::Stmt(x) => x.1 = new_pos,
|
||||||
Self::FnCall(x) => x.5 = new_pos,
|
Self::FnCall(x) => (x.0).1 = new_pos,
|
||||||
Self::And(x) => x.2 = new_pos,
|
Self::And(x) => x.2 = new_pos,
|
||||||
Self::Or(x) => x.2 = new_pos,
|
Self::Or(x) => x.2 = new_pos,
|
||||||
Self::In(x) => x.2 = new_pos,
|
Self::In(x) => x.2 = new_pos,
|
||||||
@ -541,7 +540,8 @@ impl Expr {
|
|||||||
Self::Array(x) => x.0.iter().all(Self::is_pure),
|
Self::Array(x) => x.0.iter().all(Self::is_pure),
|
||||||
|
|
||||||
Self::Index(x) | Self::And(x) | Self::Or(x) | Self::In(x) => {
|
Self::Index(x) | Self::And(x) | Self::Or(x) | Self::In(x) => {
|
||||||
x.0.is_pure() && x.1.is_pure()
|
let (lhs, rhs, _) = x.as_ref();
|
||||||
|
lhs.is_pure() && rhs.is_pure()
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Stmt(x) => x.0.is_pure(),
|
Self::Stmt(x) => x.0.is_pure(),
|
||||||
@ -569,7 +569,7 @@ impl Expr {
|
|||||||
Self::Array(x) => x.0.iter().all(Self::is_constant),
|
Self::Array(x) => x.0.iter().all(Self::is_constant),
|
||||||
|
|
||||||
// An map literal is constant if all items are constant
|
// An map literal is constant if all items are constant
|
||||||
Self::Map(x) => x.0.iter().map(|(_, expr, _)| expr).all(Self::is_constant),
|
Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant),
|
||||||
|
|
||||||
// Check in expression
|
// Check in expression
|
||||||
Self::In(x) => match (&x.0, &x.1) {
|
Self::In(x) => match (&x.0, &x.1) {
|
||||||
@ -626,7 +626,10 @@ 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(x) if x.1.is_none() => Self::Property(Box::new((x.0.clone(), x.4))),
|
Self::Variable(x) if x.1.is_none() => {
|
||||||
|
let (name, pos) = x.0;
|
||||||
|
Self::Property(Box::new((name.clone(), pos)))
|
||||||
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -713,34 +716,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 = {
|
let hash_fn_def = {
|
||||||
if let Some(modules) = modules.as_mut() {
|
if let Some(modules) = modules.as_mut() {
|
||||||
modules.set_index(stack.find_module(&modules.get(0).0));
|
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 + no parameters.
|
// i.e. qualifiers + function name + no parameters.
|
||||||
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.
|
||||||
|
calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty())
|
||||||
hash1
|
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), &id, empty())
|
calc_fn_hash(empty(), &id, empty())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Qualifiers (none) + function name + no parameters.
|
// Qualifiers (none) + function name + no parameters.
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
let hash1 = calc_fn_hash(empty(), &id, empty());
|
let hash_fn_def = calc_fn_hash(empty(), &id, empty());
|
||||||
|
|
||||||
return Ok(Expr::FnCall(Box::new((
|
return Ok(Expr::FnCall(Box::new((
|
||||||
id.into(),
|
(id.into(), begin),
|
||||||
modules,
|
modules,
|
||||||
hash1,
|
hash_fn_def,
|
||||||
args,
|
args,
|
||||||
None,
|
None,
|
||||||
begin,
|
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
// id...
|
// id...
|
||||||
@ -756,38 +756,36 @@ 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 = {
|
let hash_fn_def = {
|
||||||
if let Some(modules) = modules.as_mut() {
|
if let Some(modules) = modules.as_mut() {
|
||||||
modules.set_index(stack.find_module(&modules.get(0).0));
|
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).
|
||||||
let hash1 = calc_fn_hash(
|
|
||||||
modules.iter().map(|(m, _)| m.as_str()),
|
|
||||||
&id,
|
|
||||||
repeat(EMPTY_TYPE_ID()).take(args.len()),
|
|
||||||
);
|
|
||||||
// 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.
|
||||||
|
calc_fn_hash(
|
||||||
hash1
|
modules.iter().map(|(m, _)| m.as_str()),
|
||||||
|
&id,
|
||||||
|
repeat(EMPTY_TYPE_ID()).take(args.len()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()))
|
calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Qualifiers (none) + function name + dummy parameter types (one for each parameter).
|
// Qualifiers (none) + function name + dummy parameter types (one for each parameter).
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
let hash1 = calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()));
|
let hash_fn_def =
|
||||||
|
calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()));
|
||||||
|
|
||||||
return Ok(Expr::FnCall(Box::new((
|
return Ok(Expr::FnCall(Box::new((
|
||||||
id.into(),
|
(id.into(), begin),
|
||||||
modules,
|
modules,
|
||||||
hash1,
|
hash_fn_def,
|
||||||
args,
|
args,
|
||||||
None,
|
None,
|
||||||
begin,
|
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
// id(...args,
|
// id(...args,
|
||||||
@ -1094,7 +1092,7 @@ fn parse_map_literal<'a>(
|
|||||||
|
|
||||||
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
let expr = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
map.push((name, expr, pos));
|
map.push(((name, pos), expr));
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
@ -1127,11 +1125,11 @@ fn parse_map_literal<'a>(
|
|||||||
// Check for duplicating properties
|
// Check for duplicating properties
|
||||||
map.iter()
|
map.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.try_for_each(|(i, (k1, _, _))| {
|
.try_for_each(|(i, ((k1, _), _))| {
|
||||||
map.iter()
|
map.iter()
|
||||||
.skip(i + 1)
|
.skip(i + 1)
|
||||||
.find(|(k2, _, _)| k2 == k1)
|
.find(|((k2, _), _)| k2 == k1)
|
||||||
.map_or_else(|| Ok(()), |(k2, _, pos)| Err((k2, *pos)))
|
.map_or_else(|| Ok(()), |((k2, pos), _)| Err((k2, *pos)))
|
||||||
})
|
})
|
||||||
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
||||||
|
|
||||||
@ -1163,7 +1161,7 @@ fn parse_primary<'a>(
|
|||||||
Token::StringConst(s) => Expr::StringConstant(Box::new((s, pos))),
|
Token::StringConst(s) => Expr::StringConstant(Box::new((s, pos))),
|
||||||
Token::Identifier(s) => {
|
Token::Identifier(s) => {
|
||||||
let index = stack.find(&s);
|
let index = stack.find(&s);
|
||||||
Expr::Variable(Box::new((s, None, 0, index, pos)))
|
Expr::Variable(Box::new(((s, pos), None, 0, index)))
|
||||||
}
|
}
|
||||||
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"))]
|
||||||
@ -1191,24 +1189,27 @@ fn parse_primary<'a>(
|
|||||||
root_expr = match (root_expr, token) {
|
root_expr = match (root_expr, token) {
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(x), Token::LeftParen) => {
|
(Expr::Variable(x), Token::LeftParen) => {
|
||||||
parse_call_expr(input, stack, x.0, x.1, x.4, allow_stmt_expr)?
|
let ((name, pos), modules, _, _) = *x;
|
||||||
|
parse_call_expr(input, stack, name, modules, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
(Expr::Property(x), Token::LeftParen) => {
|
(Expr::Property(x), Token::LeftParen) => {
|
||||||
parse_call_expr(input, stack, x.0, None, x.1, allow_stmt_expr)?
|
let (name, pos) = *x;
|
||||||
|
parse_call_expr(input, stack, name, None, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
// module access
|
// module access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(Expr::Variable(mut x), Token::DoubleColon) => match input.next().unwrap() {
|
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
||||||
(Token::Identifier(id2), pos2) => {
|
(Token::Identifier(id2), pos2) => {
|
||||||
if let Some(ref mut modules) = x.1 {
|
let ((name, pos), mut modules, _, index) = *x;
|
||||||
modules.push((x.0, x.4));
|
if let Some(ref mut modules) = modules {
|
||||||
|
modules.push((name, pos));
|
||||||
} else {
|
} else {
|
||||||
let mut m: ModuleRef = Default::default();
|
let mut m: ModuleRef = Default::default();
|
||||||
m.push((x.0, x.4));
|
m.push((name, pos));
|
||||||
x.1 = Some(Box::new(m));
|
modules = Some(Box::new(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Variable(Box::new((id2, x.1, 0, x.3, pos2)))
|
Expr::Variable(Box::new(((id2, pos2), modules, 0, index)))
|
||||||
}
|
}
|
||||||
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
||||||
},
|
},
|
||||||
@ -1226,10 +1227,11 @@ fn parse_primary<'a>(
|
|||||||
// 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(x) if x.1.is_some() => {
|
Expr::Variable(x) if x.1.is_some() => {
|
||||||
let modules = x.1.as_mut().unwrap();
|
let ((name, _), modules, hash, _) = x.as_mut();
|
||||||
|
let modules = modules.as_mut().unwrap();
|
||||||
|
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
x.2 = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), &x.0, empty());
|
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, empty());
|
||||||
modules.set_index(stack.find_module(&modules.get(0).0));
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -1292,12 +1294,11 @@ fn parse_unary<'a>(
|
|||||||
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
|
|
||||||
Ok(Expr::FnCall(Box::new((
|
Ok(Expr::FnCall(Box::new((
|
||||||
op.into(),
|
(op.into(), pos),
|
||||||
None,
|
None,
|
||||||
hash,
|
hash,
|
||||||
vec![e],
|
vec![e],
|
||||||
None,
|
None,
|
||||||
pos,
|
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1310,16 +1311,17 @@ 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 expr = vec![parse_primary(input, stack, allow_stmt_expr)?];
|
||||||
|
|
||||||
let op = "!";
|
let op = "!";
|
||||||
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
|
|
||||||
Ok(Expr::FnCall(Box::new((
|
Ok(Expr::FnCall(Box::new((
|
||||||
op.into(),
|
(op.into(), pos),
|
||||||
None,
|
None,
|
||||||
hash,
|
hash,
|
||||||
vec![parse_primary(input, stack, allow_stmt_expr)?],
|
expr,
|
||||||
Some(Box::new(false.into())), // NOT operator, when operating on invalid operand, defaults to false
|
Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
|
||||||
pos,
|
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
// <EOF>
|
// <EOF>
|
||||||
@ -1338,11 +1340,12 @@ fn make_assignment_stmt<'a>(
|
|||||||
match &lhs {
|
match &lhs {
|
||||||
Expr::Variable(x) if x.3.is_none() => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
|
Expr::Variable(x) if x.3.is_none() => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
match stack[(stack.len() - x.3.unwrap().get())].1 {
|
let ((name, name_pos), _, _, index) = x.as_ref();
|
||||||
|
match stack[(stack.len() - index.unwrap().get())].1 {
|
||||||
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
|
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => {
|
||||||
Err(PERR::AssignmentToConstant(x.0.to_string()).into_err(x.4))
|
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos))
|
||||||
}
|
}
|
||||||
ScopeEntryType::Module => unreachable!(),
|
ScopeEntryType::Module => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -1350,11 +1353,12 @@ fn make_assignment_stmt<'a>(
|
|||||||
Expr::Index(x) | Expr::Dot(x) => match &x.0 {
|
Expr::Index(x) | Expr::Dot(x) => match &x.0 {
|
||||||
Expr::Variable(x) if x.3.is_none() => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
|
Expr::Variable(x) if x.3.is_none() => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
match stack[(stack.len() - x.3.unwrap().get())].1 {
|
let ((name, name_pos), _, _, index) = x.as_ref();
|
||||||
|
match stack[(stack.len() - index.unwrap().get())].1 {
|
||||||
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
|
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => {
|
||||||
Err(PERR::AssignmentToConstant(x.0.to_string()).into_err(x.4))
|
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos))
|
||||||
}
|
}
|
||||||
ScopeEntryType::Module => unreachable!(),
|
ScopeEntryType::Module => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -1403,7 +1407,7 @@ 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 hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(args.len()));
|
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, args, None, pos)));
|
let rhs_expr = Expr::FnCall(Box::new(((op.into(), pos), None, hash, args, None)));
|
||||||
make_assignment_stmt(stack, lhs, rhs_expr, pos)
|
make_assignment_stmt(stack, lhs, rhs_expr, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1423,15 +1427,13 @@ fn make_dot_expr(
|
|||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
||||||
let lhs = if is_index { lhs.into_property() } else { lhs };
|
let lhs = if is_index { lhs.into_property() } else { lhs };
|
||||||
Expr::Dot(Box::new((
|
let rhs = Expr::Property(Box::new(x.0));
|
||||||
lhs,
|
Expr::Dot(Box::new((lhs, rhs, op_pos)))
|
||||||
Expr::Property(Box::new((x.0, x.4))),
|
|
||||||
op_pos,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
(lhs, Expr::Property(x)) => {
|
(lhs, Expr::Property(x)) => {
|
||||||
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, Expr::Property(x), op_pos)))
|
let rhs = Expr::Property(x);
|
||||||
|
Expr::Dot(Box::new((lhs, rhs, op_pos)))
|
||||||
}
|
}
|
||||||
// lhs.module::id - syntax error
|
// lhs.module::id - syntax error
|
||||||
(_, Expr::Variable(x)) if x.1.is_some() => {
|
(_, Expr::Variable(x)) if x.1.is_some() => {
|
||||||
@ -1441,17 +1443,31 @@ fn make_dot_expr(
|
|||||||
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get(0).1));
|
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get(0).1));
|
||||||
}
|
}
|
||||||
// lhs.dot_lhs.dot_rhs
|
// lhs.dot_lhs.dot_rhs
|
||||||
(lhs, Expr::Dot(x)) => Expr::Dot(Box::new((
|
(lhs, Expr::Dot(x)) => {
|
||||||
|
let (dot_lhs, dot_rhs, pos) = *x;
|
||||||
|
Expr::Dot(Box::new((
|
||||||
lhs,
|
lhs,
|
||||||
Expr::Dot(Box::new((x.0.into_property(), x.1.into_property(), x.2))),
|
Expr::Dot(Box::new((
|
||||||
op_pos,
|
dot_lhs.into_property(),
|
||||||
|
dot_rhs.into_property(),
|
||||||
|
pos,
|
||||||
))),
|
))),
|
||||||
|
op_pos,
|
||||||
|
)))
|
||||||
|
}
|
||||||
// lhs.idx_lhs[idx_rhs]
|
// lhs.idx_lhs[idx_rhs]
|
||||||
(lhs, Expr::Index(x)) => Expr::Dot(Box::new((
|
(lhs, Expr::Index(x)) => {
|
||||||
|
let (idx_lhs, idx_rhs, pos) = *x;
|
||||||
|
Expr::Dot(Box::new((
|
||||||
lhs,
|
lhs,
|
||||||
Expr::Index(Box::new((x.0.into_property(), x.1.into_property(), x.2))),
|
Expr::Index(Box::new((
|
||||||
op_pos,
|
idx_lhs.into_property(),
|
||||||
|
idx_rhs.into_property(),
|
||||||
|
pos,
|
||||||
))),
|
))),
|
||||||
|
op_pos,
|
||||||
|
)))
|
||||||
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
(lhs, rhs) => Expr::Dot(Box::new((lhs, rhs.into_property(), op_pos))),
|
(lhs, rhs) => Expr::Dot(Box::new((lhs, rhs.into_property(), op_pos))),
|
||||||
})
|
})
|
||||||
@ -1664,32 +1680,32 @@ fn parse_binary_op<'a>(
|
|||||||
rhs
|
rhs
|
||||||
};
|
};
|
||||||
|
|
||||||
let cmp_default = Some(Box::new(false.into()));
|
let cmp_def = Some(false.into());
|
||||||
let op = op_token.syntax();
|
let op = op_token.syntax();
|
||||||
let hash = calc_fn_hash(empty(), &op, repeat(EMPTY_TYPE_ID()).take(2));
|
let hash = calc_fn_hash(empty(), &op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
let mut args = vec![current_lhs, rhs];
|
let mut args = vec![current_lhs, rhs];
|
||||||
|
|
||||||
current_lhs = match op_token {
|
current_lhs = match op_token {
|
||||||
Token::Plus => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::Plus => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
Token::Minus => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::Minus => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
Token::Multiply => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::Multiply => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
Token::Divide => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::Divide => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
|
|
||||||
Token::LeftShift => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::LeftShift => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
Token::RightShift => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::RightShift => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
Token::Modulo => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::Modulo => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
Token::PowerOf => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::PowerOf => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
|
|
||||||
// Comparison operators default to false when passed invalid operands
|
// Comparison operators default to false when passed invalid operands
|
||||||
Token::EqualsTo => Expr::FnCall(Box::new((op, None, hash, args, cmp_default, pos))),
|
Token::EqualsTo => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
|
||||||
Token::NotEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, cmp_default, pos))),
|
Token::NotEqualsTo => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
|
||||||
Token::LessThan => Expr::FnCall(Box::new((op, None, hash, args, cmp_default, pos))),
|
Token::LessThan => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
|
||||||
Token::LessThanEqualsTo => {
|
Token::LessThanEqualsTo => {
|
||||||
Expr::FnCall(Box::new((op, None, hash, args, cmp_default, pos)))
|
Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def)))
|
||||||
}
|
}
|
||||||
Token::GreaterThan => Expr::FnCall(Box::new((op, None, hash, args, cmp_default, pos))),
|
Token::GreaterThan => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
|
||||||
Token::GreaterThanEqualsTo => {
|
Token::GreaterThanEqualsTo => {
|
||||||
Expr::FnCall(Box::new((op, None, hash, args, cmp_default, pos)))
|
Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def)))
|
||||||
}
|
}
|
||||||
|
|
||||||
Token::Or => {
|
Token::Or => {
|
||||||
@ -1702,9 +1718,9 @@ fn parse_binary_op<'a>(
|
|||||||
let current_lhs = args.pop().unwrap();
|
let current_lhs = args.pop().unwrap();
|
||||||
Expr::And(Box::new((current_lhs, rhs, pos)))
|
Expr::And(Box::new((current_lhs, rhs, pos)))
|
||||||
}
|
}
|
||||||
Token::Ampersand => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::Ampersand => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
Token::Pipe => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::Pipe => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
Token::XOr => Expr::FnCall(Box::new((op, None, hash, args, None, pos))),
|
Token::XOr => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
|
|
||||||
Token::In => {
|
Token::In => {
|
||||||
let rhs = args.pop().unwrap();
|
let rhs = args.pop().unwrap();
|
||||||
@ -1916,12 +1932,12 @@ fn parse_let<'a>(
|
|||||||
// let name = expr
|
// let name = expr
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
stack.push((name.clone(), ScopeEntryType::Normal));
|
stack.push((name.clone(), ScopeEntryType::Normal));
|
||||||
Ok(Stmt::Let(Box::new((name, Some(init_value), pos))))
|
Ok(Stmt::Let(Box::new(((name, pos), Some(init_value)))))
|
||||||
}
|
}
|
||||||
// const name = { expr:constant }
|
// const name = { expr:constant }
|
||||||
ScopeEntryType::Constant if init_value.is_constant() => {
|
ScopeEntryType::Constant if init_value.is_constant() => {
|
||||||
stack.push((name.clone(), ScopeEntryType::Constant));
|
stack.push((name.clone(), ScopeEntryType::Constant));
|
||||||
Ok(Stmt::Const(Box::new((name, init_value, pos))))
|
Ok(Stmt::Const(Box::new(((name, pos), init_value))))
|
||||||
}
|
}
|
||||||
// const name = expr - error
|
// const name = expr - error
|
||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => {
|
||||||
@ -1935,11 +1951,11 @@ fn parse_let<'a>(
|
|||||||
match var_type {
|
match var_type {
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
stack.push((name.clone(), ScopeEntryType::Normal));
|
stack.push((name.clone(), ScopeEntryType::Normal));
|
||||||
Ok(Stmt::Let(Box::new((name, None, pos))))
|
Ok(Stmt::Let(Box::new(((name, pos), None))))
|
||||||
}
|
}
|
||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => {
|
||||||
stack.push((name.clone(), ScopeEntryType::Constant));
|
stack.push((name.clone(), ScopeEntryType::Constant));
|
||||||
Ok(Stmt::Const(Box::new((name, Expr::Unit(pos), pos))))
|
Ok(Stmt::Const(Box::new(((name, pos), Expr::Unit(pos)))))
|
||||||
}
|
}
|
||||||
// Variable cannot be a module
|
// Variable cannot be a module
|
||||||
ScopeEntryType::Module => unreachable!(),
|
ScopeEntryType::Module => unreachable!(),
|
||||||
@ -1978,7 +1994,7 @@ fn parse_import<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
stack.push((name.clone(), ScopeEntryType::Module));
|
stack.push((name.clone(), ScopeEntryType::Module));
|
||||||
Ok(Stmt::Import(Box::new((expr, name, pos))))
|
Ok(Stmt::Import(Box::new((expr, (name, pos)))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an export statement.
|
/// Parse an export statement.
|
||||||
@ -2005,7 +2021,7 @@ fn parse_export<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Box
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.push((id, id_pos, rename));
|
exports.push(((id, id_pos), rename));
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
@ -2026,14 +2042,14 @@ fn parse_export<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Box
|
|||||||
exports
|
exports
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.try_for_each(|(i, (p1, _, _))| {
|
.try_for_each(|(i, ((id1, _), _))| {
|
||||||
exports
|
exports
|
||||||
.iter()
|
.iter()
|
||||||
.skip(i + 1)
|
.skip(i + 1)
|
||||||
.find(|(p2, _, _)| p2 == p1)
|
.find(|((id2, _), _)| id2 == id1)
|
||||||
.map_or_else(|| Ok(()), |(p2, pos, _)| Err((p2, *pos)))
|
.map_or_else(|| Ok(()), |((id2, pos), _)| Err((id2, *pos)))
|
||||||
})
|
})
|
||||||
.map_err(|(p, pos)| PERR::DuplicatedExport(p.to_string()).into_err(pos))?;
|
.map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?;
|
||||||
|
|
||||||
Ok(Stmt::Export(Box::new(exports)))
|
Ok(Stmt::Export(Box::new(exports)))
|
||||||
}
|
}
|
||||||
@ -2165,10 +2181,10 @@ fn parse_stmt<'a>(
|
|||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
// `return`/`throw` at <EOF>
|
// `return`/`throw` at <EOF>
|
||||||
(Token::EOF, pos) => Ok(Stmt::ReturnWithVal(Box::new((None, return_type, *pos)))),
|
(Token::EOF, pos) => Ok(Stmt::ReturnWithVal(Box::new(((return_type, *pos), None)))),
|
||||||
// `return;` or `throw;`
|
// `return;` or `throw;`
|
||||||
(Token::SemiColon, _) => {
|
(Token::SemiColon, _) => {
|
||||||
Ok(Stmt::ReturnWithVal(Box::new((None, return_type, pos))))
|
Ok(Stmt::ReturnWithVal(Box::new(((return_type, pos), None))))
|
||||||
}
|
}
|
||||||
// `return` or `throw` with expression
|
// `return` or `throw` with expression
|
||||||
(_, _) => {
|
(_, _) => {
|
||||||
@ -2176,9 +2192,8 @@ fn parse_stmt<'a>(
|
|||||||
let pos = expr.position();
|
let pos = expr.position();
|
||||||
|
|
||||||
Ok(Stmt::ReturnWithVal(Box::new((
|
Ok(Stmt::ReturnWithVal(Box::new((
|
||||||
|
(return_type, pos),
|
||||||
Some(expr),
|
Some(expr),
|
||||||
return_type,
|
|
||||||
pos,
|
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2448,13 +2463,14 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
|||||||
Union::Map(map) => {
|
Union::Map(map) => {
|
||||||
let items: Vec<_> = map
|
let items: Vec<_> = map
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (k, map_dynamic_to_expr(v, pos), pos))
|
.map(|(k, v)| ((k, pos), map_dynamic_to_expr(v, pos)))
|
||||||
.collect();
|
.collect();
|
||||||
if items.iter().all(|(_, expr, _)| expr.is_some()) {
|
|
||||||
|
if items.iter().all(|(_, expr)| expr.is_some()) {
|
||||||
Some(Expr::Map(Box::new((
|
Some(Expr::Map(Box::new((
|
||||||
items
|
items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, expr, pos)| (k, expr.unwrap(), pos))
|
.map(|((k, pos), expr)| ((k, pos), expr.unwrap()))
|
||||||
.collect(),
|
.collect(),
|
||||||
pos,
|
pos,
|
||||||
))))
|
))))
|
||||||
|
@ -18,12 +18,7 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let mut sub_module2 = Module::new();
|
let mut sub_module2 = Module::new();
|
||||||
sub_module2.set_var("answer", 41 as INT);
|
sub_module2.set_var("answer", 41 as INT);
|
||||||
let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1), false);
|
let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
|
||||||
let hash_hidden = sub_module2.set_fn_0(
|
|
||||||
"hidden",
|
|
||||||
|| Err("shouldn't see me!".into()) as Result<(), Box<EvalAltResult>>,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
sub_module.set_sub_module("universe", sub_module2);
|
sub_module.set_sub_module("universe", sub_module2);
|
||||||
module.set_sub_module("life", sub_module);
|
module.set_sub_module("life", sub_module);
|
||||||
@ -36,7 +31,6 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(m2.contains_var("answer"));
|
assert!(m2.contains_var("answer"));
|
||||||
assert!(m2.contains_fn(hash_inc));
|
assert!(m2.contains_fn(hash_inc));
|
||||||
assert!(m2.contains_fn(hash_hidden));
|
|
||||||
|
|
||||||
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
||||||
|
|
||||||
@ -59,11 +53,6 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
)?,
|
)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
assert!(matches!(
|
|
||||||
*engine.eval_expression_with_scope::<()>(&mut scope, "question::life::universe::hidden()")
|
|
||||||
.expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "hidden"
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user