Merge branch 'master' into plugins

This commit is contained in:
Stephen Chung
2020-07-28 11:08:57 +08:00
25 changed files with 551 additions and 358 deletions

View File

@@ -19,7 +19,7 @@ use crate::utils::StaticVec;
use crate::any::Variant;
#[cfg(not(feature = "no_function"))]
use crate::parser::{FnAccess, ScriptFnDef};
use crate::parser::ScriptFnDef;
#[cfg(not(feature = "no_module"))]
use crate::module::ModuleResolver;
@@ -228,11 +228,6 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
/// [INTERNALS] A type that holds all the current states of the Engine.
/// Exported under the `internals` feature only.
///
/// # Safety
///
/// This type uses some unsafe code, mainly for avoiding cloning of local variable names via
/// direct lifetime casting.
///
/// ## WARNING
///
/// This type is volatile and may change.
@@ -264,19 +259,15 @@ pub fn get_script_function_by_signature<'a>(
module: &'a Module,
name: &str,
params: usize,
public_only: bool,
pub_only: bool,
) -> Option<&'a ScriptFnDef> {
// Qualifiers (none) + function name + number of arguments.
let hash_script = calc_fn_hash(empty(), name, params, empty());
let func = module.get_fn(hash_script)?;
if !func.is_script() {
return None;
}
let fn_def = func.get_fn_def();
match fn_def.access {
FnAccess::Private if public_only => None,
FnAccess::Private | FnAccess::Public => Some(&fn_def),
let func = module.get_fn(hash_script, pub_only)?;
if func.is_script() {
Some(func.get_fn_def())
} else {
None
}
}
@@ -695,8 +686,8 @@ impl Engine {
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val];
self.exec_fn_call(
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None,
level,
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, false,
None, level,
)
.or_else(|err| match *err {
// If there is no index setter, no need to set it back because the indexer is read-only
@@ -719,8 +710,8 @@ impl Engine {
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val];
self.exec_fn_call(
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None,
level,
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, false,
None, level,
)?;
}
// Error
@@ -741,7 +732,12 @@ impl Engine {
match rhs {
// xxx.fn_name(arg_expr_list)
Expr::FnCall(x) if x.1.is_none() => {
self.make_method_call(state, lib, target, rhs, idx_val, level)
let ((name, native, pos), _, hash, _, def_val) = x.as_ref();
self.make_method_call(
state, lib, name, *hash, target, idx_val, *def_val, *native, false,
level,
)
.map_err(|err| err.new_position(*pos))
}
// xxx.module::fn_name(...) - syntax error
Expr::FnCall(_) => unreachable!(),
@@ -770,7 +766,8 @@ impl Engine {
let ((_, _, setter), pos) = x.as_ref();
let mut args = [target.as_mut(), _new_val.as_mut().unwrap()];
self.exec_fn_call(
state, lib, setter, true, 0, &mut args, is_ref, true, None, level,
state, lib, setter, true, 0, &mut args, is_ref, true, false, None,
level,
)
.map(|(v, _)| (v, true))
.map_err(|err| err.new_position(*pos))
@@ -780,7 +777,8 @@ impl Engine {
let ((_, getter, _), pos) = x.as_ref();
let mut args = [target.as_mut()];
self.exec_fn_call(
state, lib, getter, true, 0, &mut args, is_ref, true, None, level,
state, lib, getter, true, 0, &mut args, is_ref, true, false, None,
level,
)
.map(|(v, _)| (v, false))
.map_err(|err| err.new_position(*pos))
@@ -791,15 +789,19 @@ impl Engine {
let mut val = match sub_lhs {
Expr::Property(p) => {
let ((prop, _, _), _) = p.as_ref();
let ((prop, _, _), pos) = p.as_ref();
let index = prop.clone().into();
self.get_indexed_mut(state, lib, target, index, *pos, false, level)?
}
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
Expr::FnCall(x) if x.1.is_none() => {
let (val, _) = self.make_method_call(
state, lib, target, sub_lhs, idx_val, level,
)?;
let ((name, native, pos), _, hash, _, def_val) = x.as_ref();
let (val, _) = self
.make_method_call(
state, lib, name, *hash, target, idx_val, *def_val,
*native, false, level,
)
.map_err(|err| err.new_position(*pos))?;
val.into()
}
// {xxx:map}.module::fn_name(...) - syntax error
@@ -816,18 +818,18 @@ impl Engine {
}
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr
Expr::Index(x) | Expr::Dot(x) => {
let (sub_lhs, expr, pos) = x.as_ref();
let (sub_lhs, expr, _) = x.as_ref();
match sub_lhs {
// xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => {
let ((_, getter, setter), _) = p.as_ref();
let ((_, getter, setter), pos) = p.as_ref();
let arg_values = &mut [target.as_mut(), &mut Default::default()];
let args = &mut arg_values[..1];
let (mut val, updated) = self
.exec_fn_call(
state, lib, getter, true, 0, args, is_ref, true, None,
level,
state, lib, getter, true, 0, args, is_ref, true, false,
None, level,
)
.map_err(|err| err.new_position(*pos))?;
@@ -847,7 +849,7 @@ impl Engine {
arg_values[1] = val;
self.exec_fn_call(
state, lib, setter, true, 0, arg_values, is_ref, true,
None, level,
false, None, level,
)
.or_else(
|err| match *err {
@@ -864,9 +866,13 @@ impl Engine {
}
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(x) if x.1.is_none() => {
let (mut val, _) = self.make_method_call(
state, lib, target, sub_lhs, idx_val, level,
)?;
let ((name, native, pos), _, hash, _, def_val) = x.as_ref();
let (mut val, _) = self
.make_method_call(
state, lib, name, *hash, target, idx_val, *def_val,
*native, false, level,
)
.map_err(|err| err.new_position(*pos))?;
let val = &mut val;
let target = &mut val.into();
@@ -1132,7 +1138,7 @@ impl Engine {
let type_name = val.type_name();
let args = &mut [val, &mut _idx];
self.exec_fn_call(
state, _lib, FN_IDX_GET, true, 0, args, is_ref, true, None, _level,
state, _lib, FN_IDX_GET, true, 0, args, is_ref, true, false, None, _level,
)
.map(|(v, _)| v.into())
.map_err(|err| match *err {
@@ -1188,7 +1194,7 @@ impl Engine {
let (r, _) = self
.call_fn_raw(
&mut scope, mods, state, lib, op, hashes, args, false, false,
&mut scope, mods, state, lib, op, hashes, args, false, false, false,
def_value, level,
)
.map_err(|err| err.new_position(rhs.position()))?;
@@ -1289,8 +1295,8 @@ impl Engine {
if let Some(CallableFunction::Method(func)) = self
.global_module
.get_fn(hash_fn)
.or_else(|| self.packages.get_fn(hash_fn))
.get_fn(hash_fn, false)
.or_else(|| self.packages.get_fn(hash_fn, false))
{
// Overriding exact implementation
func(self, lib, &mut [lhs_ptr, &mut rhs_val])?;
@@ -1303,7 +1309,8 @@ impl Engine {
// Run function
let (value, _) = self
.exec_fn_call(
state, lib, op, true, hash, args, false, false, None, level,
state, lib, op, true, hash, args, false, false, false, None,
level,
)
.map_err(|err| err.new_position(*op_pos))?;
// Set value to LHS
@@ -1331,9 +1338,11 @@ impl Engine {
&mut self.eval_expr(scope, mods, state, lib, this_ptr, lhs_expr, level)?,
&mut rhs_val,
];
self.exec_fn_call(state, lib, op, true, hash, args, false, false, None, level)
.map(|(v, _)| v)
.map_err(|err| err.new_position(*op_pos))?
self.exec_fn_call(
state, lib, op, true, hash, args, false, false, false, None, level,
)
.map(|(v, _)| v)
.map_err(|err| err.new_position(*op_pos))?
});
match lhs_expr {
@@ -1403,7 +1412,7 @@ impl Engine {
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
self.make_function_call(
scope, mods, state, lib, this_ptr, name, args_expr, *def_val, *hash, *native,
level,
false, level,
)
.map_err(|err| err.new_position(*pos))
}
@@ -1481,6 +1490,12 @@ impl Engine {
}
/// Evaluate a statement
///
///
/// # Safety
///
/// This method uses some unsafe code, mainly for avoiding cloning of local variable names via
/// direct lifetime casting.
pub(crate) fn eval_stmt(
&self,
scope: &mut Scope,
@@ -1524,7 +1539,7 @@ impl Engine {
// If-else statement
Stmt::IfThenElse(x) => {
let (expr, if_block, else_block) = x.as_ref();
let (expr, if_block, else_block, _) = x.as_ref();
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool()
@@ -1542,7 +1557,7 @@ impl Engine {
// While loop
Stmt::While(x) => loop {
let (expr, body) = x.as_ref();
let (expr, body, _) = x.as_ref();
match self
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
@@ -1568,8 +1583,8 @@ impl Engine {
},
// Loop statement
Stmt::Loop(body) => loop {
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
Stmt::Loop(x) => loop {
match self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level) {
Ok(_) => (),
Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (),
@@ -1581,7 +1596,7 @@ impl Engine {
// For loop
Stmt::For(x) => {
let (name, expr, stmt) = x.as_ref();
let (name, expr, stmt, _) = x.as_ref();
let iter_type = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
let tid = iter_type.type_id();
@@ -1627,16 +1642,9 @@ impl Engine {
// Return value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
let expr = x.1.as_ref().unwrap();
Err(Box::new(EvalAltResult::Return(
self.eval_expr(
scope,
mods,
state,
lib,
this_ptr,
x.1.as_ref().unwrap(),
level,
)?,
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?,
(x.0).1,
)))
}
@@ -1648,15 +1656,8 @@ impl Engine {
// Throw value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => {
let val = self.eval_expr(
scope,
mods,
state,
lib,
this_ptr,
x.1.as_ref().unwrap(),
level,
)?;
let expr = x.1.as_ref().unwrap();
let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
Err(Box::new(EvalAltResult::ErrorRuntime(
val.take_string().unwrap_or_else(|_| "".into()),
(x.0).1,
@@ -1672,23 +1673,16 @@ impl Engine {
// Let statement
Stmt::Let(x) if x.1.is_some() => {
let ((var_name, _), expr) = x.as_ref();
let val = self.eval_expr(
scope,
mods,
state,
lib,
this_ptr,
expr.as_ref().unwrap(),
level,
)?;
let ((var_name, _), expr, _) = x.as_ref();
let expr = expr.as_ref().unwrap();
let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
Ok(Default::default())
}
Stmt::Let(x) => {
let ((var_name, _), _) = x.as_ref();
let ((var_name, _), _, _) = x.as_ref();
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
scope.push(var_name, ());
Ok(Default::default())
@@ -1696,7 +1690,7 @@ impl Engine {
// Const statement
Stmt::Const(x) if x.1.is_constant() => {
let ((var_name, _), expr) = x.as_ref();
let ((var_name, _), expr, _) = x.as_ref();
let val = self.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?;
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
@@ -1709,7 +1703,7 @@ impl Engine {
// Import statement
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => {
let (expr, (name, _pos)) = x.as_ref();
let (expr, (name, _pos), _) = x.as_ref();
// Guard against too many modules
#[cfg(not(feature = "unchecked"))]
@@ -1742,8 +1736,8 @@ impl Engine {
// Export statement
#[cfg(not(feature = "no_module"))]
Stmt::Export(list) => {
for ((id, id_pos), rename) in list.iter() {
Stmt::Export(x) => {
for ((id, id_pos), rename) in x.0.iter() {
// Mark scope variables as public
if let Some(index) = scope.get_index(id).map(|(i, _)| i) {
let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id);

View File

@@ -125,6 +125,7 @@ impl Engine {
args: &mut FnCallArgs,
is_ref: bool,
_is_method: bool,
pub_only: bool,
def_val: Option<bool>,
_level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
@@ -150,14 +151,14 @@ impl Engine {
// Then search packages
// NOTE: We skip script functions for global_module and packages, and native functions for lib
let func = if !native_only {
lib.get_fn(hash_script) //.or_else(|| lib.get_fn(hash_fn))
lib.get_fn(hash_script, pub_only) //.or_else(|| lib.get_fn(hash_fn, pub_only))
} else {
None
}
//.or_else(|| self.global_module.get_fn(hash_script))
.or_else(|| self.global_module.get_fn(hash_fn))
//.or_else(|| self.packages.get_fn(hash_script))
.or_else(|| self.packages.get_fn(hash_fn));
//.or_else(|| self.global_module.get_fn(hash_script, pub_only))
.or_else(|| self.global_module.get_fn(hash_fn, pub_only))
//.or_else(|| self.packages.get_fn(hash_script, pub_only))
.or_else(|| self.packages.get_fn(hash_fn, pub_only));
if let Some(func) = func {
#[cfg(not(feature = "no_function"))]
@@ -252,7 +253,11 @@ impl Engine {
// Getter function not found?
if let Some(prop) = extract_prop_from_getter(fn_name) {
return Err(Box::new(EvalAltResult::ErrorDotExpr(
format!("- property '{}' unknown or write-only", prop),
format!(
"Unknown property '{}' for {}, or it is write-only",
prop,
self.map_type_name(args[0].type_name())
),
Position::none(),
)));
}
@@ -260,7 +265,11 @@ impl Engine {
// Setter function not found?
if let Some(prop) = extract_prop_from_setter(fn_name) {
return Err(Box::new(EvalAltResult::ErrorDotExpr(
format!("- property '{}' unknown or read-only", prop),
format!(
"Unknown property '{}' for {}, or it is read-only",
prop,
self.map_type_name(args[0].type_name())
),
Position::none(),
)));
}
@@ -378,18 +387,18 @@ impl Engine {
}
// Has a system function an override?
fn has_override(&self, lib: &Module, hash_fn: u64, hash_script: u64) -> bool {
fn has_override(&self, lib: &Module, hash_fn: u64, hash_script: u64, pub_only: bool) -> bool {
// NOTE: We skip script functions for global_module and packages, and native functions for lib
// First check script-defined functions
lib.contains_fn(hash_script)
//|| lib.contains_fn(hash_fn)
// Then check registered functions
//|| self.global_module.contains_fn(hash_script)
|| self.global_module.contains_fn(hash_fn)
// Then check packages
//|| self.packages.contains_fn(hash_script)
|| self.packages.contains_fn(hash_fn)
lib.contains_fn(hash_script, pub_only)
//|| lib.contains_fn(hash_fn, pub_only)
// Then check registered functions
//|| self.global_module.contains_fn(hash_script, pub_only)
|| self.global_module.contains_fn(hash_fn, pub_only)
// Then check packages
//|| self.packages.contains_fn(hash_script, pub_only)
|| self.packages.contains_fn(hash_fn, pub_only)
}
/// Perform an actual function call, taking care of special functions
@@ -410,6 +419,7 @@ impl Engine {
args: &mut FnCallArgs,
is_ref: bool,
is_method: bool,
pub_only: bool,
def_val: Option<bool>,
level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
@@ -420,7 +430,9 @@ impl Engine {
match fn_name {
// type_of
KEYWORD_TYPE_OF if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1) => {
KEYWORD_TYPE_OF
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
{
Ok((
self.map_type_name(args[0].type_name()).to_string().into(),
false,
@@ -428,7 +440,9 @@ impl Engine {
}
// Fn
KEYWORD_FN_PTR if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1) => {
KEYWORD_FN_PTR
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
{
Err(Box::new(EvalAltResult::ErrorRuntime(
"'Fn' should not be called in method style. Try Fn(...);".into(),
Position::none(),
@@ -436,7 +450,9 @@ impl Engine {
}
// eval - reaching this point it must be a method-style call
KEYWORD_EVAL if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1) => {
KEYWORD_EVAL
if args.len() == 1 && !self.has_override(lib, hashes.0, hashes.1, pub_only) =>
{
Err(Box::new(EvalAltResult::ErrorRuntime(
"'eval' should not be called in method style. Try eval(...);".into(),
Position::none(),
@@ -449,7 +465,7 @@ impl Engine {
let mut mods = Imports::new();
self.call_fn_raw(
&mut scope, &mut mods, state, lib, fn_name, hashes, args, is_ref, is_method,
def_val, level,
pub_only, def_val, level,
)
}
}
@@ -499,28 +515,28 @@ impl Engine {
}
/// Call a dot method.
/// Position in `EvalAltResult` is `None` and must be set afterwards.
#[cfg(not(feature = "no_object"))]
pub(crate) fn make_method_call(
&self,
state: &mut State,
lib: &Module,
name: &str,
hash: u64,
target: &mut Target,
expr: &Expr,
idx_val: Dynamic,
def_val: Option<bool>,
native: bool,
pub_only: bool,
level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
let ((name, native, pos), _, hash, _, def_val) = match expr {
Expr::FnCall(x) => x.as_ref(),
_ => unreachable!(),
};
let is_ref = target.is_ref();
let is_value = target.is_value();
// Get a reference to the mutation target Dynamic
let obj = target.as_mut();
let mut idx = idx_val.cast::<StaticVec<Dynamic>>();
let mut _fn_name = name.as_ref();
let mut _fn_name = name;
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
// FnPtr call
@@ -539,7 +555,7 @@ impl Engine {
// Map it to name(args) in function-call style
self.exec_fn_call(
state, lib, fn_name, *native, hash, args, false, false, *def_val, level,
state, lib, fn_name, native, hash, args, false, false, pub_only, def_val, level,
)
} else if _fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
// FnPtr call on object
@@ -558,7 +574,7 @@ impl Engine {
// Map it to name(args) in function-call style
self.exec_fn_call(
state, lib, &fn_name, *native, hash, args, is_ref, true, *def_val, level,
state, lib, &fn_name, native, hash, args, is_ref, true, pub_only, def_val, level,
)
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
// Curry call
@@ -579,7 +595,7 @@ impl Engine {
} else {
#[cfg(not(feature = "no_object"))]
let redirected;
let mut _hash = *hash;
let mut _hash = hash;
// Check if it is a map method call in OOP style
#[cfg(not(feature = "no_object"))]
@@ -600,10 +616,9 @@ impl Engine {
let args = arg_values.as_mut();
self.exec_fn_call(
state, lib, _fn_name, *native, _hash, args, is_ref, true, *def_val, level,
state, lib, _fn_name, native, _hash, args, is_ref, true, pub_only, def_val, level,
)
}
.map_err(|err| err.new_position(*pos))?;
}?;
// Feed the changed temp value back
if updated && !is_ref && !is_value {
@@ -628,13 +643,14 @@ impl Engine {
def_val: Option<bool>,
mut hash: u64,
native: bool,
pub_only: bool,
level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> {
// Handle Fn()
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash) {
if !self.has_override(lib, hash_fn, hash, pub_only) {
// Fn - only in function call style
let expr = args_expr.get(0).unwrap();
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
@@ -685,7 +701,7 @@ impl Engine {
if name == KEYWORD_EVAL && args_expr.len() == 1 {
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, hash_fn, hash) {
if !self.has_override(lib, hash_fn, hash, pub_only) {
// eval - only in function call style
let prev_len = scope.len();
let expr = args_expr.get(0).unwrap();
@@ -710,7 +726,10 @@ impl Engine {
let mut curry: StaticVec<_> = Default::default();
let mut name = name;
if name == KEYWORD_FN_PTR_CALL && args_expr.len() >= 1 && !self.has_override(lib, 0, hash) {
if name == KEYWORD_FN_PTR_CALL
&& args_expr.len() >= 1
&& !self.has_override(lib, 0, hash, pub_only)
{
let expr = args_expr.get(0).unwrap();
let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
@@ -779,7 +798,7 @@ impl Engine {
let args = args.as_mut();
self.exec_fn_call(
state, lib, name, native, hash, args, is_ref, false, def_val, level,
state, lib, name, native, hash, args, is_ref, false, pub_only, def_val, level,
)
.map(|(v, _)| v)
}
@@ -845,8 +864,8 @@ impl Engine {
// First search in script-defined functions (can override built-in)
let func = match module.get_qualified_fn(hash_script) {
Err(err) if matches!(*err, EvalAltResult::ErrorFunctionNotFound(_, _)) => {
// Then search in Rust functions
// Then search in Rust functions
None => {
self.inc_operations(state)?;
// Qualified Rust functions are indexed in two steps:
@@ -865,7 +884,7 @@ impl Engine {
match func {
#[cfg(not(feature = "no_function"))]
Ok(f) if f.is_script() => {
Some(f) if f.is_script() => {
let args = args.as_mut();
let fn_def = f.get_fn_def();
let mut scope = Scope::new();
@@ -874,32 +893,25 @@ impl Engine {
&mut scope, &mut mods, state, lib, &mut None, name, fn_def, args, level,
)
}
Ok(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut(), Position::none()),
Ok(f) => f.get_native_fn()(self, lib, args.as_mut()),
Err(err) => match *err {
EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => {
Ok(def_val.unwrap().into())
}
EvalAltResult::ErrorFunctionNotFound(_, pos) => {
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
format!(
"{}{} ({})",
modules,
name,
args.iter()
.map(|a| if a.is::<ImmutableString>() {
"&str | ImmutableString | String"
} else {
self.map_type_name((*a).type_name())
})
.collect::<Vec<_>>()
.join(", ")
),
pos,
)))
}
_ => Err(err),
},
Some(f) => f.get_native_fn()(self, lib, args.as_mut()),
Some(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut(), Position::none()),
None if def_val.is_some() => Ok(def_val.unwrap().into()),
None => Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
format!(
"{}{} ({})",
modules,
name,
args.iter()
.map(|a| if a.is::<ImmutableString>() {
"&str | ImmutableString | String"
} else {
self.map_type_name((*a).type_name())
})
.collect::<Vec<_>>()
.join(", ")
),
Position::none(),
))),
}
}
}

View File

@@ -1,17 +1,19 @@
//! Module defining interfaces to native-Rust functions.
use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::Engine;
use crate::module::Module;
use crate::parser::FnAccess;
use crate::plugin::PluginFunction;
use crate::result::EvalAltResult;
use crate::token::{is_valid_identifier, Position};
use crate::utils::ImmutableString;
#[cfg(not(feature = "no_function"))]
use crate::{module::FuncReturn, parser::ScriptFnDef, scope::Scope, utils::StaticVec};
use crate::{module::FuncReturn, parser::ScriptFnDef, utils::StaticVec};
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, string::String, vec::Vec};
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, string::String, vec::Vec};
#[cfg(not(feature = "no_function"))]
use crate::stdlib::mem;
@@ -90,9 +92,7 @@ impl FnPtr {
/// Call the function pointer with curried arguments (if any).
///
/// The function must be a script-defined function. It cannot be a Rust function.
///
/// To call a Rust function, just call it directly in Rust!
/// If this function is a script-defined function, it must not be marked private.
///
/// ## WARNING
///
@@ -108,14 +108,39 @@ impl FnPtr {
this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>,
) -> FuncReturn<Dynamic> {
let args = self
let mut args_data = self
.1
.iter()
.cloned()
.chain(arg_values.as_mut().iter_mut().map(|v| mem::take(v)))
.collect::<StaticVec<_>>();
engine.call_fn_dynamic(&mut Scope::new(), lib, self.0.as_str(), this_ptr, args)
let has_this = this_ptr.is_some();
let args_len = args_data.len();
let mut args = args_data.iter_mut().collect::<StaticVec<_>>();
if let Some(obj) = this_ptr {
args.insert(0, obj);
}
let fn_name = self.0.as_str();
let hash_script = calc_fn_hash(empty(), fn_name, args_len, empty());
engine
.exec_fn_call(
&mut Default::default(),
lib.as_ref(),
fn_name,
false,
hash_script,
args.as_mut(),
has_this,
has_this,
true,
None,
0,
)
.map(|(v, _)| v)
}
}
@@ -272,6 +297,16 @@ impl CallableFunction {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Script(_) => false,
}
}
/// Get the access mode.
pub fn access(&self) -> FnAccess {
match self {
CallableFunction::Plugin(_) => FnAccess::Public,
CallableFunction::Pure(_)
| CallableFunction::Method(_)
| CallableFunction::Iterator(_) => FnAccess::Public,
CallableFunction::Script(f) => f.access,
}
}
/// Get a reference to a native Rust function.
///
/// # Panics

View File

@@ -355,10 +355,20 @@ impl Module {
///
/// let mut module = Module::new();
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn contains_fn(&self, hash_fn: u64) -> bool {
self.functions.contains_key(&hash_fn)
pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool {
if public_only {
self.functions
.get(&hash_fn)
.map(|(_, access, _, _)| match access {
FnAccess::Public => true,
FnAccess::Private => false,
})
.unwrap_or(false)
} else {
self.functions.contains_key(&hash_fn)
}
}
/// Set a Rust function into the module, returning a hash key.
@@ -422,7 +432,7 @@ impl Module {
/// let mut module = Module::new();
/// let hash = module.set_raw_fn("double_or_not",
/// // Pass parameter types via a slice with TypeId's
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>() ],
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
/// // Fixed closure signature
/// |engine, lib, args| {
/// // 'args' is guaranteed to be the right length and of the correct types
@@ -443,7 +453,7 @@ impl Module {
/// Ok(orig) // return Result<T, Box<EvalAltResult>>
/// });
///
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_raw_fn<T: Variant + Clone>(
&mut self,
@@ -468,7 +478,7 @@ impl Module {
///
/// let mut module = Module::new();
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_0<T: Variant + Clone>(
&mut self,
@@ -491,7 +501,7 @@ impl Module {
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_1<A: Variant + Clone, T: Variant + Clone>(
&mut self,
@@ -516,7 +526,7 @@ impl Module {
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
&mut self,
@@ -541,7 +551,7 @@ impl Module {
///
/// let mut module = Module::new();
/// let hash = module.set_getter_fn("value", |x: &mut i64| { Ok(*x) });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
#[cfg(not(feature = "no_object"))]
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
@@ -565,7 +575,7 @@ impl Module {
/// let hash = module.set_fn_2("calc", |x: i64, y: ImmutableString| {
/// Ok(x + y.len() as i64)
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
@@ -596,7 +606,7 @@ impl Module {
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: ImmutableString| {
/// *x += y.len() as i64; Ok(*x)
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
@@ -628,7 +638,7 @@ impl Module {
/// *x = y.len() as i64;
/// Ok(())
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
#[cfg(not(feature = "no_object"))]
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
@@ -653,7 +663,7 @@ impl Module {
/// let hash = module.set_indexer_get_fn(|x: &mut i64, y: ImmutableString| {
/// Ok(*x + y.len() as i64)
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))]
@@ -677,7 +687,7 @@ impl Module {
/// let hash = module.set_fn_3("calc", |x: i64, y: ImmutableString, z: i64| {
/// Ok(x + y.len() as i64 + z)
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_3<
A: Variant + Clone,
@@ -714,7 +724,7 @@ impl Module {
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: ImmutableString, z: i64| {
/// *x += y.len() as i64 + z; Ok(*x)
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_3_mut<
A: Variant + Clone,
@@ -752,7 +762,7 @@ impl Module {
/// *x = y.len() as i64 + value;
/// Ok(())
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))]
@@ -796,8 +806,8 @@ impl Module {
/// Ok(())
/// }
/// );
/// assert!(module.contains_fn(hash_get));
/// assert!(module.contains_fn(hash_set));
/// assert!(module.contains_fn(hash_get, true));
/// assert!(module.contains_fn(hash_set, true));
/// ```
#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))]
@@ -825,7 +835,7 @@ impl Module {
/// let hash = module.set_fn_4("calc", |x: i64, y: ImmutableString, z: i64, _w: ()| {
/// Ok(x + y.len() as i64 + z)
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_4<
A: Variant + Clone,
@@ -869,7 +879,7 @@ impl Module {
/// let hash = module.set_fn_4_mut("calc", |x: &mut i64, y: ImmutableString, z: i64, _w: ()| {
/// *x += y.len() as i64 + z; Ok(*x)
/// });
/// assert!(module.contains_fn(hash));
/// assert!(module.contains_fn(hash, true));
/// ```
pub fn set_fn_4_mut<
A: Variant + Clone,
@@ -903,8 +913,14 @@ impl Module {
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
/// It is also returned by the `set_fn_XXX` calls.
pub(crate) fn get_fn(&self, hash_fn: u64) -> Option<&Func> {
self.functions.get(&hash_fn).map(|(_, _, _, v)| v)
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&Func> {
self.functions
.get(&hash_fn)
.and_then(|(_, access, _, f)| match access {
_ if !public_only => Some(f),
FnAccess::Public => Some(f),
FnAccess::Private => None,
})
}
/// Get a modules-qualified function.
@@ -912,16 +928,8 @@ impl Module {
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash` and must match
/// the hash calculated by `index_all_sub_modules`.
pub(crate) fn get_qualified_fn(
&self,
hash_qualified_fn: u64,
) -> Result<&Func, Box<EvalAltResult>> {
self.all_functions.get(&hash_qualified_fn).ok_or_else(|| {
Box::new(EvalAltResult::ErrorFunctionNotFound(
String::new(),
Position::none(),
))
})
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&Func> {
self.all_functions.get(&hash_qualified_fn)
}
/// Merge another module into this module.

View File

@@ -141,6 +141,7 @@ fn call_fn_with_constant_arguments(
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
false,
false,
true,
None,
0,
)
@@ -184,6 +185,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
optimize_expr(expr, state),
optimize_stmt(x.1, state, true),
None,
x.3,
))),
},
// if expr { if_block } else { else_block }
@@ -200,6 +202,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Noop(_) => None, // Noop -> no else block
stmt => Some(stmt),
},
x.3,
))),
},
// while expr { block }
@@ -210,7 +213,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Noop(pos)
}
// while true { block } -> loop { block }
Expr::True(_) => Stmt::Loop(Box::new(optimize_stmt(x.1, state, false))),
Expr::True(_) => Stmt::Loop(Box::new((optimize_stmt(x.1, state, false), x.2))),
// while expr { block }
expr => match optimize_stmt(x.1, state, false) {
// while expr { break; } -> { expr; }
@@ -225,11 +228,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Block(Box::new((statements, pos)))
}
// while expr { block }
stmt => Stmt::While(Box::new((optimize_expr(expr, state), stmt))),
stmt => Stmt::While(Box::new((optimize_expr(expr, state), stmt, x.2))),
},
},
// loop { block }
Stmt::Loop(block) => match optimize_stmt(*block, state, false) {
Stmt::Loop(x) => match optimize_stmt(x.0, state, false) {
// loop { break; } -> Noop
Stmt::Break(pos) => {
// Only a single break statement
@@ -237,23 +240,26 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
Stmt::Noop(pos)
}
// loop { block }
stmt => Stmt::Loop(Box::new(stmt)),
stmt => Stmt::Loop(Box::new((stmt, x.1))),
},
// for id in expr { block }
Stmt::For(x) => Stmt::For(Box::new((
x.0,
optimize_expr(x.1, state),
optimize_stmt(x.2, state, false),
x.3,
))),
// let id = expr;
Stmt::Let(x) if x.1.is_some() => {
Stmt::Let(Box::new((x.0, Some(optimize_expr(x.1.unwrap(), state)))))
}
Stmt::Let(x) if x.1.is_some() => Stmt::Let(Box::new((
x.0,
Some(optimize_expr(x.1.unwrap(), state)),
x.2,
))),
// let id;
stmt @ Stmt::Let(_) => stmt,
// import expr as id;
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))),
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1, x.2))),
// { block }
Stmt::Block(x) => {
let orig_len = x.0.len(); // Original number of statements in the block, for change detection
@@ -266,7 +272,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
.map(|stmt| match stmt {
// Add constant into the state
Stmt::Const(v) => {
let ((name, pos), expr) = *v;
let ((name, pos), expr, _) = *v;
state.push_constant(&name, expr);
state.set_dirty();
Stmt::Noop(pos) // No need to keep constants
@@ -366,9 +372,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
// expr;
Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))),
// return expr;
Stmt::ReturnWithVal(x) if x.1.is_some() => {
Stmt::ReturnWithVal(Box::new((x.0, Some(optimize_expr(x.1.unwrap(), state)))))
}
Stmt::ReturnWithVal(x) if x.1.is_some() => Stmt::ReturnWithVal(Box::new((
x.0,
Some(optimize_expr(x.1.unwrap(), state)),
x.2,
))),
// All other statements - skip
stmt => stmt,
}
@@ -411,7 +419,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty();
let pos = m.1;
m.0.into_iter().find(|((name, _), _)| name.as_str() == prop.as_str())
.map(|(_, expr)| expr.set_position(pos))
.map(|(_, mut expr)| { expr.set_position(pos); expr })
.unwrap_or_else(|| Expr::Unit(pos))
}
// lhs.rhs
@@ -428,7 +436,9 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
// Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away.
state.set_dirty();
a.0.take(i.0 as usize).set_position(a.1)
let mut expr = a.0.take(i.0 as usize);
expr.set_position(a.1);
expr
}
// map[string]
(Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
@@ -437,7 +447,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty();
let pos = m.1;
m.0.into_iter().find(|((name, _), _)| *name == s.0)
.map(|(_, expr)| expr.set_position(pos))
.map(|(_, mut expr)| { expr.set_position(pos); expr })
.unwrap_or_else(|| Expr::Unit(pos))
}
// string[int]
@@ -624,7 +634,9 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty();
// Replace constant with value
state.find_constant(&name).unwrap().clone().set_position(pos)
let mut expr = state.find_constant(&name).unwrap().clone();
expr.set_position(pos);
expr
}
// Custom syntax
@@ -686,7 +698,7 @@ fn optimize(
match &stmt {
Stmt::Const(v) => {
// Load constants
let ((name, _), expr) = v.as_ref();
let ((name, _), expr, _) = v.as_ref();
state.push_constant(&name, expr.clone());
stmt // Keep it in the global scope
}

View File

@@ -61,14 +61,15 @@ impl PackagesCollection {
self.0.insert(0, package);
}
/// Does the specified function hash key exist in the `PackagesCollection`?
pub fn contains_fn(&self, hash: u64) -> bool {
self.0.iter().any(|p| p.contains_fn(hash))
#[allow(dead_code)]
pub fn contains_fn(&self, hash: u64, public_only: bool) -> bool {
self.0.iter().any(|p| p.contains_fn(hash, public_only))
}
/// Get specified function via its hash key.
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
pub fn get_fn(&self, hash: u64, public_only: bool) -> Option<&CallableFunction> {
self.0
.iter()
.map(|p| p.get_fn(hash))
.map(|p| p.get_fn(hash, public_only))
.find(|f| f.is_some())
.flatten()
}

View File

@@ -26,7 +26,6 @@ use crate::stdlib::{
fmt, format,
hash::{Hash, Hasher},
iter::empty,
mem,
num::NonZeroUsize,
ops::Add,
string::{String, ToString},
@@ -511,33 +510,38 @@ pub enum Stmt {
/// No-op.
Noop(Position),
/// if expr { stmt } else { stmt }
IfThenElse(Box<(Expr, Stmt, Option<Stmt>)>),
IfThenElse(Box<(Expr, Stmt, Option<Stmt>, Position)>),
/// while expr { stmt }
While(Box<(Expr, Stmt)>),
While(Box<(Expr, Stmt, Position)>),
/// loop { stmt }
Loop(Box<Stmt>),
Loop(Box<(Stmt, Position)>),
/// for id in expr { stmt }
For(Box<(String, Expr, Stmt)>),
For(Box<(String, Expr, Stmt, Position)>),
/// let id = expr
Let(Box<((String, Position), Option<Expr>)>),
Let(Box<((String, Position), Option<Expr>, Position)>),
/// const id = expr
Const(Box<((String, Position), Expr)>),
Const(Box<((String, Position), Expr, Position)>),
/// { stmt; ... }
Block(Box<(StaticVec<Stmt>, Position)>),
/// { stmt }
/// expr
Expr(Box<Expr>),
/// continue
Continue(Position),
/// break
Break(Position),
/// return/throw
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>)>),
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>, Position)>),
/// import expr as module
#[cfg(not(feature = "no_module"))]
Import(Box<(Expr, (String, Position))>),
Import(Box<(Expr, (String, Position), Position)>),
/// expr id as name, ...
#[cfg(not(feature = "no_module"))]
Export(Box<StaticVec<((String, Position), Option<(String, Position)>)>>),
Export(
Box<(
StaticVec<((String, Position), Option<(String, Position)>)>,
Position,
)>,
),
}
impl Default for Stmt {
@@ -555,19 +559,44 @@ impl Stmt {
Stmt::Const(x) => (x.0).1,
Stmt::ReturnWithVal(x) => (x.0).1,
Stmt::Block(x) => x.1,
Stmt::IfThenElse(x) => x.0.position(),
Stmt::IfThenElse(x) => x.3,
Stmt::Expr(x) => x.position(),
Stmt::While(x) => x.1.position(),
Stmt::Loop(x) => x.position(),
Stmt::For(x) => x.2.position(),
Stmt::While(x) => x.2,
Stmt::Loop(x) => x.1,
Stmt::For(x) => x.3,
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => (x.1).1,
Stmt::Import(x) => x.2,
#[cfg(not(feature = "no_module"))]
Stmt::Export(x) => (x.get(0).0).1,
Stmt::Export(x) => x.1,
}
}
/// Override the `Position` of this statement.
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Stmt::Noop(pos) | Stmt::Continue(pos) | Stmt::Break(pos) => *pos = new_pos,
Stmt::Let(x) => (x.0).1 = new_pos,
Stmt::Const(x) => (x.0).1 = new_pos,
Stmt::ReturnWithVal(x) => (x.0).1 = new_pos,
Stmt::Block(x) => x.1 = new_pos,
Stmt::IfThenElse(x) => x.3 = new_pos,
Stmt::Expr(x) => {
x.set_position(new_pos);
}
Stmt::While(x) => x.2 = new_pos,
Stmt::Loop(x) => x.1 = new_pos,
Stmt::For(x) => x.3 = new_pos,
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => x.2 = new_pos,
#[cfg(not(feature = "no_module"))]
Stmt::Export(x) => x.1 = new_pos,
}
self
}
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
pub fn is_self_terminated(&self) -> bool {
match self {
@@ -602,7 +631,7 @@ impl Stmt {
}
Stmt::IfThenElse(x) => x.1.is_pure(),
Stmt::While(x) => x.0.is_pure() && x.1.is_pure(),
Stmt::Loop(x) => x.is_pure(),
Stmt::Loop(x) => x.0.is_pure(),
Stmt::For(x) => x.1.is_pure() && x.2.is_pure(),
Stmt::Let(_) | Stmt::Const(_) => false,
Stmt::Block(x) => x.0.iter().all(Stmt::is_pure),
@@ -832,11 +861,10 @@ impl Expr {
}
/// Override the `Position` of the expression.
pub(crate) fn set_position(mut self, new_pos: Position) -> Self {
match &mut self {
Self::Expr(ref mut x) => {
let expr = mem::take(x);
*x = Box::new(expr.set_position(new_pos));
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Self::Expr(x) => {
x.set_position(new_pos);
}
#[cfg(not(feature = "no_float"))]
@@ -2314,7 +2342,7 @@ fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result
fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
match input.peek().unwrap() {
(Token::Equals, pos) => {
return Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos))
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos))
}
(Token::PlusAssign, pos)
| (Token::MinusAssign, pos)
@@ -2326,12 +2354,10 @@ fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
| (Token::PowerOfAssign, pos)
| (Token::AndAssign, pos)
| (Token::OrAssign, pos)
| (Token::XOrAssign, pos) => {
return Err(PERR::BadInput(
"Expecting a boolean expression, not an assignment".to_string(),
)
.into_err(*pos))
}
| (Token::XOrAssign, pos) => Err(PERR::BadInput(
"Expecting a boolean expression, not an assignment".to_string(),
)
.into_err(*pos)),
_ => Ok(()),
}
@@ -2345,7 +2371,8 @@ fn parse_if(
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
// if ...
settings.pos = eat_token(input, Token::If);
let token_pos = eat_token(input, Token::If);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@@ -2369,7 +2396,9 @@ fn parse_if(
None
};
Ok(Stmt::IfThenElse(Box::new((guard, if_body, else_body))))
Ok(Stmt::IfThenElse(Box::new((
guard, if_body, else_body, token_pos,
))))
}
/// Parse a while loop.
@@ -2380,7 +2409,8 @@ fn parse_while(
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
// while ...
settings.pos = eat_token(input, Token::While);
let token_pos = eat_token(input, Token::While);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@@ -2393,7 +2423,7 @@ fn parse_while(
settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?;
Ok(Stmt::While(Box::new((guard, body))))
Ok(Stmt::While(Box::new((guard, body, token_pos))))
}
/// Parse a loop statement.
@@ -2404,7 +2434,8 @@ fn parse_loop(
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
// loop ...
settings.pos = eat_token(input, Token::Loop);
let token_pos = eat_token(input, Token::Loop);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@@ -2413,7 +2444,7 @@ fn parse_loop(
settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?;
Ok(Stmt::Loop(Box::new(body)))
Ok(Stmt::Loop(Box::new((body, token_pos))))
}
/// Parse a for loop.
@@ -2424,7 +2455,8 @@ fn parse_for(
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
// for ...
settings.pos = eat_token(input, Token::For);
let token_pos = eat_token(input, Token::For);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@@ -2467,7 +2499,7 @@ fn parse_for(
state.stack.truncate(prev_stack_len);
Ok(Stmt::For(Box::new((name, expr, body))))
Ok(Stmt::For(Box::new((name, expr, body, token_pos))))
}
/// Parse a variable definition statement.
@@ -2479,7 +2511,8 @@ fn parse_let(
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
// let/const... (specified in `var_type`)
settings.pos = input.next().unwrap().1;
let token_pos = input.next().unwrap().1;
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@@ -2503,12 +2536,16 @@ fn parse_let(
// let name = expr
ScopeEntryType::Normal => {
state.stack.push((name.clone(), ScopeEntryType::Normal));
Ok(Stmt::Let(Box::new(((name, pos), Some(init_value)))))
Ok(Stmt::Let(Box::new((
(name, pos),
Some(init_value),
token_pos,
))))
}
// const name = { expr:constant }
ScopeEntryType::Constant if init_value.is_constant() => {
state.stack.push((name.clone(), ScopeEntryType::Constant));
Ok(Stmt::Const(Box::new(((name, pos), init_value))))
Ok(Stmt::Const(Box::new(((name, pos), init_value, token_pos))))
}
// const name = expr: error
ScopeEntryType::Constant => {
@@ -2520,11 +2557,15 @@ fn parse_let(
match var_type {
ScopeEntryType::Normal => {
state.stack.push((name.clone(), ScopeEntryType::Normal));
Ok(Stmt::Let(Box::new(((name, pos), None))))
Ok(Stmt::Let(Box::new(((name, pos), None, token_pos))))
}
ScopeEntryType::Constant => {
state.stack.push((name.clone(), ScopeEntryType::Constant));
Ok(Stmt::Const(Box::new(((name, pos), Expr::Unit(pos)))))
Ok(Stmt::Const(Box::new((
(name, pos),
Expr::Unit(pos),
token_pos,
))))
}
}
}
@@ -2539,7 +2580,8 @@ fn parse_import(
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
// import ...
settings.pos = eat_token(input, Token::Import);
let token_pos = eat_token(input, Token::Import);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@@ -2569,7 +2611,12 @@ fn parse_import(
};
state.modules.push(name.clone());
Ok(Stmt::Import(Box::new((expr, (name, settings.pos)))))
Ok(Stmt::Import(Box::new((
expr,
(name, settings.pos),
token_pos,
))))
}
/// Parse an export statement.
@@ -2580,7 +2627,8 @@ fn parse_export(
_lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
settings.pos = eat_token(input, Token::Export);
let token_pos = eat_token(input, Token::Export);
settings.pos = token_pos;
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(_state.max_expr_depth)?;
@@ -2640,7 +2688,7 @@ fn parse_export(
})
.map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?;
Ok(Stmt::Export(Box::new(exports)))
Ok(Stmt::Export(Box::new((exports, token_pos))))
}
/// Parse a statement block.
@@ -2674,10 +2722,9 @@ fn parse_block(
// Parse statements inside the block
settings.is_global = false;
let stmt = if let Some(s) = parse_stmt(input, state, lib, settings.level_up())? {
s
} else {
continue;
let stmt = match parse_stmt(input, state, lib, settings.level_up())? {
Some(s) => s,
None => continue,
};
// See if it needs a terminating semicolon
@@ -2828,22 +2875,32 @@ fn parse_stmt(
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)),
Token::Return | Token::Throw => {
let return_type = match input.next().unwrap() {
(Token::Return, _) => ReturnType::Return,
(Token::Throw, _) => ReturnType::Exception,
_ => unreachable!(),
};
let (return_type, token_pos) = input
.next()
.map(|(token, pos)| {
(
match token {
Token::Return => ReturnType::Return,
Token::Throw => ReturnType::Exception,
_ => unreachable!(),
},
pos,
)
})
.unwrap();
match input.peek().unwrap() {
// `return`/`throw` at <EOF>
(Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, *pos),
None,
token_pos,
))))),
// `return;` or `throw;`
(Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, settings.pos),
None,
token_pos,
))))),
// `return` or `throw` with expression
(_, _) => {
@@ -2853,6 +2910,7 @@ fn parse_stmt(
Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, pos),
Some(expr),
token_pos,
)))))
}
}
@@ -3137,10 +3195,9 @@ impl Engine {
pos: Position::none(),
};
let stmt = if let Some(s) = parse_stmt(input, &mut state, &mut functions, settings)? {
s
} else {
continue;
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {
Some(s) => s,
None => continue,
};
let need_semicolon = !stmt.is_self_terminated();

View File

@@ -182,7 +182,7 @@ impl fmt::Display for EvalAltResult {
| Self::ErrorVariableNotFound(s, _)
| Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?,
Self::ErrorDotExpr(s, _) if !s.is_empty() => write!(f, "{} {}", desc, s)?,
Self::ErrorDotExpr(s, _) if !s.is_empty() => write!(f, "{}", s)?,
Self::ErrorIndexingType(_, _)
| Self::ErrorNumericIndexExpr(_)

View File

@@ -495,8 +495,8 @@ impl Token {
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" | "public"
| "new" | "use" | "module" | "package" | "var" | "static" | "with" | "do" | "each"
| "then" | "goto" | "exit" | "switch" | "match" | "case" | "try" | "catch"
| "default" | "void" | "null" | "nil" | "spawn" | "go" | "async" | "await"
| "yield" => Reserved(syntax.into()),
| "default" | "void" | "null" | "nil" | "spawn" | "go" | "shared" | "sync"
| "async" | "await" | "yield" => Reserved(syntax.into()),
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS => Reserved(syntax.into()),