Merge branch 'master' into plugins
This commit is contained in:
158
src/engine.rs
158
src/engine.rs
@@ -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);
|
||||
|
||||
144
src/fn_call.rs
144
src/fn_call.rs
@@ -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(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
177
src/parser.rs
177
src/parser.rs
@@ -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();
|
||||
|
||||
@@ -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(_)
|
||||
|
||||
@@ -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()),
|
||||
|
||||
Reference in New Issue
Block a user