Merge branch 'master' into plugins
This commit is contained in:
14
src/any.rs
14
src/any.rs
@@ -243,12 +243,15 @@ impl fmt::Display for Dynamic {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(value) => write!(f, "#{:?}", value),
|
||||
Union::Map(value) => {
|
||||
f.write_str("#")?;
|
||||
fmt::Debug::fmt(value, f)
|
||||
}
|
||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
||||
Union::Variant(_) => write!(f, "?"),
|
||||
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,12 +269,15 @@ impl fmt::Debug for Dynamic {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(value) => write!(f, "#{:?}", value),
|
||||
Union::Map(value) => {
|
||||
f.write_str("#")?;
|
||||
fmt::Debug::fmt(value, f)
|
||||
}
|
||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
||||
Union::Variant(_) => write!(f, "<dynamic>"),
|
||||
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -87,6 +87,7 @@ pub const FN_GET: &str = "get$";
|
||||
pub const FN_SET: &str = "set$";
|
||||
pub const FN_IDX_GET: &str = "index$get$";
|
||||
pub const FN_IDX_SET: &str = "index$set$";
|
||||
pub const FN_ANONYMOUS: &str = "anon$";
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub const MARKER_EXPR: &str = "$expr$";
|
||||
@@ -187,7 +188,7 @@ impl Target<'_> {
|
||||
}
|
||||
}
|
||||
/// Update the value of the `Target`.
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
pub fn set_value(&mut self, new_val: Dynamic) -> Result<(), Box<EvalAltResult>> {
|
||||
match self {
|
||||
Self::Ref(r) => **r = new_val,
|
||||
@@ -418,12 +419,9 @@ pub fn make_getter(id: &str) -> String {
|
||||
fn extract_prop_from_getter(fn_name: &str) -> Option<&str> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if fn_name.starts_with(FN_GET) {
|
||||
Some(&fn_name[FN_GET.len()..])
|
||||
} else {
|
||||
None
|
||||
return Some(&fn_name[FN_GET.len()..]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_object")]
|
||||
None
|
||||
}
|
||||
|
||||
@@ -436,12 +434,9 @@ pub fn make_setter(id: &str) -> String {
|
||||
fn extract_prop_from_setter(fn_name: &str) -> Option<&str> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if fn_name.starts_with(FN_SET) {
|
||||
Some(&fn_name[FN_SET.len()..])
|
||||
} else {
|
||||
None
|
||||
return Some(&fn_name[FN_SET.len()..]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_object")]
|
||||
None
|
||||
}
|
||||
|
||||
@@ -453,7 +448,7 @@ fn default_print(s: &str) {
|
||||
}
|
||||
|
||||
/// Search for a module within an imports stack.
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
fn search_imports<'s>(
|
||||
mods: &'s Imports,
|
||||
state: &mut State,
|
||||
@@ -486,7 +481,7 @@ fn search_imports<'s>(
|
||||
}
|
||||
|
||||
/// Search for a module within an imports stack.
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
fn search_imports_mut<'s>(
|
||||
mods: &'s mut Imports,
|
||||
state: &mut State,
|
||||
@@ -636,7 +631,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Universal method for calling functions either registered with the `Engine` or written in Rhai.
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
@@ -654,7 +649,7 @@ impl Engine {
|
||||
args: &mut FnCallArgs,
|
||||
is_ref: bool,
|
||||
is_method: bool,
|
||||
def_val: Option<&Dynamic>,
|
||||
def_val: Option<bool>,
|
||||
level: usize,
|
||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||
self.inc_operations(state)?;
|
||||
@@ -709,7 +704,7 @@ impl Engine {
|
||||
/// This function restores the first argument that was replaced by `normalize_first_arg_of_method_call`.
|
||||
fn restore_first_arg<'a>(old_this_ptr: Option<&'a mut Dynamic>, args: &mut FnCallArgs<'a>) {
|
||||
if let Some(this_pointer) = old_this_ptr {
|
||||
mem::replace(args.get_mut(0).unwrap(), this_pointer);
|
||||
args[0] = this_pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,7 +810,7 @@ impl Engine {
|
||||
|
||||
// Return default value (if any)
|
||||
if let Some(val) = def_val {
|
||||
return Ok((val.clone(), false));
|
||||
return Ok((val.into(), false));
|
||||
}
|
||||
|
||||
// Getter function not found?
|
||||
@@ -877,7 +872,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Call a script-defined function.
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
@@ -959,7 +954,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Perform an actual function call, taking care of special functions
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
@@ -976,7 +971,7 @@ impl Engine {
|
||||
args: &mut FnCallArgs,
|
||||
is_ref: bool,
|
||||
is_method: bool,
|
||||
def_val: Option<&Dynamic>,
|
||||
def_val: Option<bool>,
|
||||
level: usize,
|
||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||
@@ -1022,7 +1017,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Evaluate a text string as a script - used primarily for 'eval'.
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
fn eval_script_expr(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@@ -1081,7 +1076,6 @@ impl Engine {
|
||||
|
||||
let is_ref = target.is_ref();
|
||||
let is_value = target.is_value();
|
||||
let def_val = def_val.as_ref();
|
||||
|
||||
// Get a reference to the mutation target Dynamic
|
||||
let obj = target.as_mut();
|
||||
@@ -1100,7 +1094,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, *def_val, level,
|
||||
)
|
||||
} else if fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
||||
// FnPtr call on object
|
||||
@@ -1120,7 +1114,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, *def_val, level,
|
||||
)
|
||||
} else {
|
||||
let redirected: Option<ImmutableString>;
|
||||
@@ -1146,7 +1140,7 @@ 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, *def_val, level,
|
||||
)
|
||||
}
|
||||
.map_err(|err| err.new_position(*pos))?;
|
||||
@@ -1161,7 +1155,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Chain-evaluate a dot/index chain.
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
fn eval_dot_index_chain_helper(
|
||||
&self,
|
||||
state: &mut State,
|
||||
@@ -1210,14 +1204,14 @@ impl Engine {
|
||||
}
|
||||
// xxx[rhs] = new_val
|
||||
_ if new_val.is_some() => {
|
||||
let mut new_val = new_val.unwrap();
|
||||
let mut idx_val2 = idx_val.clone();
|
||||
|
||||
match self.get_indexed_mut(state, lib, target, idx_val, pos, true, level) {
|
||||
// Indexed value is an owned value - the only possibility is an indexer
|
||||
// Try to call an index setter
|
||||
Ok(obj_ptr) if obj_ptr.is_value() => {
|
||||
let args =
|
||||
&mut [target.as_mut(), &mut idx_val2, &mut new_val.unwrap()];
|
||||
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,
|
||||
@@ -1236,17 +1230,13 @@ impl Engine {
|
||||
// Indexed value is a reference - update directly
|
||||
Ok(ref mut obj_ptr) => {
|
||||
obj_ptr
|
||||
.set_value(new_val.unwrap())
|
||||
.set_value(new_val)
|
||||
.map_err(|err| err.new_position(rhs.position()))?;
|
||||
}
|
||||
Err(err) => match *err {
|
||||
// No index getter - try to call an index setter
|
||||
EvalAltResult::ErrorIndexingType(_, _) => {
|
||||
let args = &mut [
|
||||
target.as_mut(),
|
||||
&mut idx_val2,
|
||||
&mut new_val.unwrap(),
|
||||
];
|
||||
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,
|
||||
@@ -1694,13 +1684,12 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Dynamic(Union::Array(mut rhs_value)) => {
|
||||
let op = "==";
|
||||
let def_value = false.into();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Call the `==` operator to compare each value
|
||||
for value in rhs_value.iter_mut() {
|
||||
let def_value = Some(false);
|
||||
let args = &mut [&mut lhs_value.clone(), value];
|
||||
let def_value = Some(&def_value);
|
||||
|
||||
let hashes = (
|
||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||
@@ -1782,6 +1771,7 @@ impl Engine {
|
||||
Expr::FloatConstant(x) => Ok(x.0.into()),
|
||||
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
||||
Expr::CharConstant(x) => Ok(x.0.into()),
|
||||
Expr::FnPointer(x) => Ok(FnPtr::new_unchecked(x.0.clone()).into()),
|
||||
Expr::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
||||
if let Some(ref val) = this_ptr {
|
||||
Ok((*val).clone())
|
||||
@@ -1941,7 +1931,6 @@ impl Engine {
|
||||
// Normal function call
|
||||
Expr::FnCall(x) if x.1.is_none() => {
|
||||
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
|
||||
let def_val = def_val.as_ref();
|
||||
|
||||
// Handle Fn()
|
||||
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
|
||||
@@ -2072,7 +2061,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, *def_val, level,
|
||||
)
|
||||
.map(|(v, _)| v)
|
||||
.map_err(|err| err.new_position(*pos))
|
||||
@@ -2167,7 +2156,7 @@ impl Engine {
|
||||
.map_err(|err| err.new_position(*pos)),
|
||||
Err(err) => match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => {
|
||||
Ok(def_val.clone().unwrap())
|
||||
Ok(def_val.unwrap().into())
|
||||
}
|
||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
||||
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
||||
@@ -2634,7 +2623,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Check if the number of operations stay within limit.
|
||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||
fn inc_operations(&self, state: &mut State) -> Result<(), Box<EvalAltResult>> {
|
||||
state.operations += 1;
|
||||
|
||||
|
@@ -206,6 +206,9 @@ impl fmt::Display for ParseErrorType {
|
||||
|
||||
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s),
|
||||
|
||||
Self::FnMissingBody(s) if s.is_empty() => {
|
||||
f.write_str("Expecting body statement block for anonymous function")
|
||||
}
|
||||
Self::FnMissingBody(s) => {
|
||||
write!(f, "Expecting body statement block for function '{}'", s)
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ use crate::stdlib::{
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(feature = "sync")]
|
||||
use crate::stdlib::sync::RwLock;
|
||||
|
||||
/// Return type of module-level Rust function.
|
||||
@@ -1098,7 +1099,7 @@ impl Module {
|
||||
///
|
||||
/// A `StaticVec` is used because most module-level access contains only one level,
|
||||
/// and it is wasteful to always allocate a `Vec` with one element.
|
||||
#[derive(Clone, Eq, PartialEq, Default)]
|
||||
#[derive(Clone, Eq, PartialEq, Default, Hash)]
|
||||
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
||||
|
||||
impl fmt::Debug for ModuleRef {
|
||||
|
@@ -1,9 +1,12 @@
|
||||
use crate::any::Dynamic;
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{Engine, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||
use crate::engine::{
|
||||
Engine, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||
};
|
||||
use crate::module::Module;
|
||||
use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST};
|
||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::is_valid_identifier;
|
||||
use crate::utils::StaticVec;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
@@ -397,7 +400,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
let pos = m.1;
|
||||
m.0.into_iter().find(|((name, _), _)| name.as_str() == prop)
|
||||
m.0.into_iter().find(|((name, _), _)| name.as_str() == prop.as_str())
|
||||
.map(|(_, expr)| expr.set_position(pos))
|
||||
.unwrap_or_else(|| Expr::Unit(pos))
|
||||
}
|
||||
@@ -528,6 +531,19 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
Expr::FnCall(x)
|
||||
}
|
||||
|
||||
// Fn("...")
|
||||
Expr::FnCall(x)
|
||||
if x.1.is_none()
|
||||
&& (x.0).0 == KEYWORD_FN_PTR
|
||||
&& x.3.len() == 1
|
||||
&& matches!(x.3[0], Expr::StringConstant(_))
|
||||
=> {
|
||||
match &x.3[0] {
|
||||
Expr::StringConstant(s) if is_valid_identifier(s.0.chars()) => Expr::FnPointer(s.clone()),
|
||||
_ => Expr::FnCall(x)
|
||||
}
|
||||
}
|
||||
|
||||
// Eagerly call functions
|
||||
Expr::FnCall(mut x)
|
||||
if x.1.is_none() // Non-qualified
|
||||
@@ -571,7 +587,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
Some(arg_for_type_of.to_string().into())
|
||||
} else {
|
||||
// Otherwise use the default value, if any
|
||||
def_value.clone()
|
||||
def_value.map(|v| v.into())
|
||||
}
|
||||
})
|
||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||
|
@@ -7,6 +7,10 @@ use crate::token::Position;
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(feature = "no_std")]
|
||||
use num_traits::*;
|
||||
|
||||
use num_traits::{
|
||||
identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl,
|
||||
CheckedShr, CheckedSub,
|
||||
|
@@ -6,6 +6,10 @@ use crate::token::Position;
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(feature = "no_std")]
|
||||
use num_traits::*;
|
||||
|
||||
use crate::stdlib::{boxed::Box, format, i32, i64};
|
||||
|
||||
#[cfg(feature = "only_i32")]
|
||||
|
230
src/parser.rs
230
src/parser.rs
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::any::{Dynamic, Union};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{make_getter, make_setter, Engine, KEYWORD_THIS};
|
||||
use crate::engine::{make_getter, make_setter, Engine, FN_ANONYMOUS, KEYWORD_THIS};
|
||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||
use crate::module::{Module, ModuleRef};
|
||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||
@@ -25,7 +25,7 @@ use crate::stdlib::{
|
||||
char,
|
||||
collections::HashMap,
|
||||
fmt, format,
|
||||
hash::Hash,
|
||||
hash::{Hash, Hasher},
|
||||
iter::empty,
|
||||
mem,
|
||||
num::NonZeroUsize,
|
||||
@@ -35,6 +35,12 @@ use crate::stdlib::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
use crate::stdlib::collections::hash_map::DefaultHasher;
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use ahash::AHasher;
|
||||
|
||||
/// The system integer type.
|
||||
///
|
||||
/// If the `only_i32` feature is enabled, this will be `i32` instead.
|
||||
@@ -598,21 +604,35 @@ impl Hash for CustomExpr {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[derive(Debug, PartialEq, PartialOrd, Clone)]
|
||||
pub struct FloatWrapper(pub FLOAT, pub Position);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl Hash for FloatWrapper {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write(&self.0.to_le_bytes());
|
||||
self.1.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// An expression.
|
||||
///
|
||||
/// Each variant is at most one pointer in size (for speed),
|
||||
/// with everything being allocated together in one single tuple.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub enum Expr {
|
||||
/// Integer constant.
|
||||
IntegerConstant(Box<(INT, Position)>),
|
||||
/// Floating-point constant.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
FloatConstant(Box<(FLOAT, Position)>),
|
||||
FloatConstant(Box<FloatWrapper>),
|
||||
/// Character constant.
|
||||
CharConstant(Box<(char, Position)>),
|
||||
/// String constant.
|
||||
StringConstant(Box<(ImmutableString, Position)>),
|
||||
/// FnPtr constant.
|
||||
FnPointer(Box<(ImmutableString, Position)>),
|
||||
/// Variable access - ((variable name, position), optional modules, hash, optional index)
|
||||
Variable(
|
||||
Box<(
|
||||
@@ -623,7 +643,7 @@ pub enum Expr {
|
||||
)>,
|
||||
),
|
||||
/// Property access.
|
||||
Property(Box<((String, String, String), Position)>),
|
||||
Property(Box<((ImmutableString, String, String), Position)>),
|
||||
/// { stmt }
|
||||
Stmt(Box<(Stmt, Position)>),
|
||||
/// Wrapped expression - should not be optimized away.
|
||||
@@ -637,7 +657,7 @@ pub enum Expr {
|
||||
Option<Box<ModuleRef>>,
|
||||
u64,
|
||||
StaticVec<Expr>,
|
||||
Option<Dynamic>,
|
||||
Option<bool>,
|
||||
)>,
|
||||
),
|
||||
/// expr op= expr
|
||||
@@ -673,18 +693,6 @@ impl Default for Expr {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Expr {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::FloatConstant(x) => {
|
||||
state.write(&x.0.to_le_bytes());
|
||||
x.1.hash(state);
|
||||
}
|
||||
_ => self.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
/// Get the `Dynamic` value of a constant expression.
|
||||
///
|
||||
@@ -758,6 +766,7 @@ impl Expr {
|
||||
Self::IntegerConstant(x) => x.1,
|
||||
Self::CharConstant(x) => x.1,
|
||||
Self::StringConstant(x) => x.1,
|
||||
Self::FnPointer(x) => x.1,
|
||||
Self::Array(x) => x.1,
|
||||
Self::Map(x) => x.1,
|
||||
Self::Property(x) => x.1,
|
||||
@@ -791,6 +800,7 @@ impl Expr {
|
||||
Self::IntegerConstant(x) => x.1 = new_pos,
|
||||
Self::CharConstant(x) => x.1 = new_pos,
|
||||
Self::StringConstant(x) => x.1 = new_pos,
|
||||
Self::FnPointer(x) => x.1 = new_pos,
|
||||
Self::Array(x) => x.1 = new_pos,
|
||||
Self::Map(x) => x.1 = new_pos,
|
||||
Self::Variable(x) => (x.0).1 = new_pos,
|
||||
@@ -847,6 +857,7 @@ impl Expr {
|
||||
Self::IntegerConstant(_)
|
||||
| Self::CharConstant(_)
|
||||
| Self::StringConstant(_)
|
||||
| Self::FnPointer(_)
|
||||
| Self::True(_)
|
||||
| Self::False(_)
|
||||
| Self::Unit(_) => true,
|
||||
@@ -878,6 +889,7 @@ impl Expr {
|
||||
|
||||
Self::IntegerConstant(_)
|
||||
| Self::CharConstant(_)
|
||||
| Self::FnPointer(_)
|
||||
| Self::In(_)
|
||||
| Self::And(_)
|
||||
| Self::Or(_)
|
||||
@@ -925,7 +937,7 @@ impl Expr {
|
||||
let (name, pos) = x.0;
|
||||
let getter = make_getter(&name);
|
||||
let setter = make_setter(&name);
|
||||
Self::Property(Box::new(((name.clone(), getter, setter), pos)))
|
||||
Self::Property(Box::new(((name.into(), getter, setter), pos)))
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
@@ -1476,7 +1488,7 @@ fn parse_primary(
|
||||
let mut root_expr = match token {
|
||||
Token::IntegerConstant(x) => Expr::IntegerConstant(Box::new((x, settings.pos))),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Token::FloatConstant(x) => Expr::FloatConstant(Box::new((x, settings.pos))),
|
||||
Token::FloatConstant(x) => Expr::FloatConstant(Box::new(FloatWrapper(x, settings.pos))),
|
||||
Token::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))),
|
||||
Token::StringConstant(s) => Expr::StringConstant(Box::new((s.into(), settings.pos))),
|
||||
Token::Identifier(s) => {
|
||||
@@ -1616,7 +1628,10 @@ fn parse_unary(
|
||||
.map(|i| Expr::IntegerConstant(Box::new((i, pos))))
|
||||
.or_else(|| {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Some(Expr::FloatConstant(Box::new((-(x.0 as FLOAT), pos))));
|
||||
return Some(Expr::FloatConstant(Box::new(FloatWrapper(
|
||||
-(x.0 as FLOAT),
|
||||
pos,
|
||||
))));
|
||||
#[cfg(feature = "no_float")]
|
||||
return None;
|
||||
})
|
||||
@@ -1625,7 +1640,9 @@ fn parse_unary(
|
||||
|
||||
// Negative float
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Expr::FloatConstant(x) => Ok(Expr::FloatConstant(Box::new((-x.0, x.1)))),
|
||||
Expr::FloatConstant(x) => {
|
||||
Ok(Expr::FloatConstant(Box::new(FloatWrapper(-x.0, x.1))))
|
||||
}
|
||||
|
||||
// Call negative function
|
||||
expr => {
|
||||
@@ -1664,9 +1681,38 @@ fn parse_unary(
|
||||
None,
|
||||
hash,
|
||||
args,
|
||||
Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
|
||||
Some(false), // NOT operator, when operating on invalid operand, defaults to false
|
||||
))))
|
||||
}
|
||||
// | ...
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Token::Pipe | Token::Or => {
|
||||
let mut state = ParseState::new(
|
||||
state.engine,
|
||||
state.max_function_expr_depth,
|
||||
state.max_function_expr_depth,
|
||||
);
|
||||
|
||||
let settings = ParseSettings {
|
||||
allow_if_expr: true,
|
||||
allow_stmt_expr: true,
|
||||
allow_anonymous_fn: true,
|
||||
is_global: false,
|
||||
is_function_scope: true,
|
||||
is_breakable: false,
|
||||
level: 0,
|
||||
pos: *token_pos,
|
||||
};
|
||||
|
||||
let (expr, func) = parse_anon_fn(input, &mut state, lib, settings)?;
|
||||
|
||||
// Qualifiers (none) + function name + number of arguments.
|
||||
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
|
||||
|
||||
lib.insert(hash, func);
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
// <EOF>
|
||||
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
||||
// All other tokens
|
||||
@@ -1788,7 +1834,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
||||
|
||||
let getter = make_getter(&name);
|
||||
let setter = make_setter(&name);
|
||||
let rhs = Expr::Property(Box::new(((name, getter, setter), pos)));
|
||||
let rhs = Expr::Property(Box::new(((name.into(), getter, setter), pos)));
|
||||
|
||||
Expr::Dot(Box::new((lhs, rhs, op_pos)))
|
||||
}
|
||||
@@ -2018,7 +2064,7 @@ fn parse_binary_op(
|
||||
settings.pos = pos;
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
let cmp_def = Some(false.into());
|
||||
let cmp_def = Some(false);
|
||||
let op = op_token.syntax();
|
||||
let hash = calc_fn_hash(empty(), &op, 2, empty());
|
||||
let op = (op, true, pos);
|
||||
@@ -2041,7 +2087,7 @@ fn parse_binary_op(
|
||||
| Token::XOr => Expr::FnCall(Box::new((op, None, hash, args, None))),
|
||||
|
||||
// '!=' defaults to true when passed invalid operands
|
||||
Token::NotEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, Some(true.into())))),
|
||||
Token::NotEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, Some(true)))),
|
||||
|
||||
// Comparison operators default to false when passed invalid operands
|
||||
Token::EqualsTo
|
||||
@@ -2151,9 +2197,6 @@ fn parse_expr(
|
||||
exprs.push(Expr::Stmt(Box::new((stmt, pos))))
|
||||
}
|
||||
s => match input.peek().unwrap() {
|
||||
(Token::Custom(custom), _) if custom == s => {
|
||||
input.next().unwrap();
|
||||
}
|
||||
(t, _) if t.syntax().as_ref() == s => {
|
||||
input.next().unwrap();
|
||||
}
|
||||
@@ -2765,32 +2808,28 @@ fn parse_fn(
|
||||
let mut params = Vec::new();
|
||||
|
||||
if !match_token(input, Token::RightParen)? {
|
||||
let end_err = format!("to close the parameters list of function '{}'", name);
|
||||
let sep_err = format!("to separate the parameters of function '{}'", name);
|
||||
|
||||
loop {
|
||||
match input.peek().unwrap() {
|
||||
(Token::RightParen, _) => (),
|
||||
_ => match input.next().unwrap() {
|
||||
(Token::Identifier(s), pos) => {
|
||||
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
||||
params.push((s, pos))
|
||||
}
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
return Err(
|
||||
PERR::MissingToken(Token::RightParen.into(), end_err).into_err(pos)
|
||||
)
|
||||
}
|
||||
},
|
||||
match input.next().unwrap() {
|
||||
(Token::RightParen, _) => break,
|
||||
(Token::Identifier(s), pos) => {
|
||||
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
||||
params.push((s, pos))
|
||||
}
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
return Err(PERR::MissingToken(
|
||||
Token::RightParen.into(),
|
||||
format!("to close the parameters list of function '{}'", name),
|
||||
)
|
||||
.into_err(pos))
|
||||
}
|
||||
}
|
||||
|
||||
match input.next().unwrap() {
|
||||
(Token::RightParen, _) => break,
|
||||
(Token::Comma, _) => (),
|
||||
(Token::Identifier(_), pos) => {
|
||||
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
|
||||
}
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
|
||||
@@ -2834,6 +2873,101 @@ fn parse_fn(
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse an anonymous function definition.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
fn parse_anon_fn(
|
||||
input: &mut TokenStream,
|
||||
state: &mut ParseState,
|
||||
lib: &mut FunctionsLib,
|
||||
mut settings: ParseSettings,
|
||||
) -> Result<(Expr, ScriptFnDef), ParseError> {
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
let mut params = Vec::new();
|
||||
|
||||
if input.next().unwrap().0 != Token::Or {
|
||||
if !match_token(input, Token::Pipe)? {
|
||||
loop {
|
||||
match input.next().unwrap() {
|
||||
(Token::Pipe, _) => break,
|
||||
(Token::Identifier(s), pos) => {
|
||||
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
||||
params.push((s, pos))
|
||||
}
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
return Err(PERR::MissingToken(
|
||||
Token::Pipe.into(),
|
||||
"to close the parameters list of anonymous function".into(),
|
||||
)
|
||||
.into_err(pos))
|
||||
}
|
||||
}
|
||||
|
||||
match input.next().unwrap() {
|
||||
(Token::Pipe, _) => break,
|
||||
(Token::Comma, _) => (),
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
return Err(PERR::MissingToken(
|
||||
Token::Comma.into(),
|
||||
"to separate the parameters of anonymous function".into(),
|
||||
)
|
||||
.into_err(pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for duplicating parameters
|
||||
params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.try_for_each(|(i, (p1, _))| {
|
||||
params
|
||||
.iter()
|
||||
.skip(i + 1)
|
||||
.find(|(p2, _)| p2 == p1)
|
||||
.map_or_else(|| Ok(()), |(p2, pos)| Err((p2, *pos)))
|
||||
})
|
||||
.map_err(|(p, pos)| PERR::FnDuplicatedParam("".to_string(), p.to_string()).into_err(pos))?;
|
||||
|
||||
// Parse function body
|
||||
settings.is_breakable = false;
|
||||
let pos = input.peek().unwrap().1;
|
||||
let body = parse_stmt(input, state, lib, settings.level_up())
|
||||
.map(|stmt| stmt.unwrap_or_else(|| Stmt::Noop(pos)))?;
|
||||
|
||||
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
|
||||
|
||||
// Calculate hash
|
||||
#[cfg(feature = "no_std")]
|
||||
let mut s: AHasher = Default::default();
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
let mut s = DefaultHasher::new();
|
||||
|
||||
s.write_usize(params.len());
|
||||
params.iter().for_each(|a| a.hash(&mut s));
|
||||
body.hash(&mut s);
|
||||
let hash = s.finish();
|
||||
|
||||
// Create unique function name
|
||||
let fn_name = format!("{}{}", FN_ANONYMOUS, hash);
|
||||
|
||||
let script = ScriptFnDef {
|
||||
name: fn_name.clone(),
|
||||
access: FnAccess::Public,
|
||||
params,
|
||||
body,
|
||||
pos: settings.pos,
|
||||
};
|
||||
|
||||
let expr = Expr::FnPointer(Box::new((fn_name.into(), settings.pos)));
|
||||
|
||||
Ok((expr, script))
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub(crate) fn parse_global_expr(
|
||||
&self,
|
||||
@@ -2954,7 +3088,7 @@ impl Engine {
|
||||
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
||||
match value.0 {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(value) => Some(Expr::FloatConstant(Box::new((value, pos)))),
|
||||
Union::Float(value) => Some(Expr::FloatConstant(Box::new(FloatWrapper(value, pos)))),
|
||||
|
||||
Union::Unit(_) => Some(Expr::Unit(pos)),
|
||||
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))),
|
||||
|
@@ -2,7 +2,7 @@ use crate::engine::Engine;
|
||||
use crate::module::ModuleResolver;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::packages::PackageLibrary;
|
||||
use crate::token::is_valid_identifier;
|
||||
use crate::token::{is_valid_identifier, Token};
|
||||
|
||||
use crate::stdlib::{boxed::Box, format, string::String};
|
||||
|
||||
@@ -183,8 +183,7 @@ impl Engine {
|
||||
/// engine.disable_symbol("if"); // disable the 'if' keyword
|
||||
///
|
||||
/// engine.compile("let x = if true { 42 } else { 0 };")?;
|
||||
/// // ^ 'if' is parsed as a variable name
|
||||
/// // ^ missing ';' after statement end
|
||||
/// // ^ 'if' is rejected as a reserved keyword
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
@@ -252,6 +251,24 @@ impl Engine {
|
||||
return Err(format!("not a valid identifier: '{}'", keyword).into());
|
||||
}
|
||||
|
||||
match Token::lookup_from_syntax(keyword) {
|
||||
// Standard identifiers, reserved keywords and custom keywords are OK
|
||||
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
|
||||
// Disabled keywords are also OK
|
||||
Some(token)
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.map(|d| d.contains(token.syntax().as_ref()))
|
||||
.unwrap_or(false) =>
|
||||
{
|
||||
()
|
||||
}
|
||||
// Active standard keywords cannot be made custom
|
||||
Some(_) => return Err(format!("'{}' is a reserved keyword", keyword).into()),
|
||||
}
|
||||
|
||||
// Add to custom keywords
|
||||
if self.custom_keywords.is_none() {
|
||||
self.custom_keywords = Some(Default::default());
|
||||
}
|
||||
|
@@ -88,11 +88,16 @@ impl Engine {
|
||||
MARKER_EXPR | MARKER_BLOCK | MARKER_IDENT if !segments.is_empty() => s.to_string(),
|
||||
// Standard symbols not in first position
|
||||
s if !segments.is_empty() && Token::lookup_from_syntax(s).is_some() => {
|
||||
// Make it a custom keyword/operator if it is a disabled standard keyword/operator
|
||||
// or a reserved keyword/operator.
|
||||
if self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.map(|d| d.contains(s))
|
||||
.unwrap_or(false)
|
||||
|| Token::lookup_from_syntax(s)
|
||||
.map(|token| token.is_reserved())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// If symbol is disabled, make it a custom keyword
|
||||
if self.custom_keywords.is_none() {
|
||||
|
86
src/token.rs
86
src/token.rs
@@ -1334,73 +1334,81 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
|
||||
self.engine.disabled_symbols.as_ref(),
|
||||
self.engine.custom_keywords.as_ref(),
|
||||
) {
|
||||
// {EOF}
|
||||
(None, _, _) => None,
|
||||
(Some((Token::Reserved(s), pos)), None, None) => return Some((match s.as_str() {
|
||||
"===" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'===' is not a valid operator. This is not JavaScript! Should it be '=='?"
|
||||
.to_string(),
|
||||
// Reserved keyword/symbol
|
||||
(Some((Token::Reserved(s), pos)), disabled, custom) => Some((match
|
||||
(s.as_str(), custom.map(|c| c.contains_key(&s)).unwrap_or(false))
|
||||
{
|
||||
("===", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'===' is not a valid operator. This is not JavaScript! Should it be '=='?".to_string(),
|
||||
))),
|
||||
"!==" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'!==' is not a valid operator. This is not JavaScript! Should it be '!='?"
|
||||
.to_string(),
|
||||
("!==", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'!==' is not a valid operator. This is not JavaScript! Should it be '!='?".to_string(),
|
||||
))),
|
||||
"->" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'->' is not a valid symbol. This is not C or C++!".to_string(),
|
||||
))),
|
||||
"<-" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
("->", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'->' is not a valid symbol. This is not C or C++!".to_string()))),
|
||||
("<-", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'<-' is not a valid symbol. This is not Go! Should it be '<='?".to_string(),
|
||||
))),
|
||||
"=>" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'=>' is not a valid symbol. This is not Rust! Should it be '>='?"
|
||||
.to_string(),
|
||||
("=>", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'=>' is not a valid symbol. This is not Rust! Should it be '>='?".to_string(),
|
||||
))),
|
||||
":=" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"':=' is not a valid assignment operator. This is not Go! Should it be simply '='?"
|
||||
.to_string(),
|
||||
(":=", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"':=' is not a valid assignment operator. This is not Go! Should it be simply '='?".to_string(),
|
||||
))),
|
||||
"::<" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'::<>' is not a valid symbol. This is not Rust! Should it be '::'?"
|
||||
.to_string(),
|
||||
("::<", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'::<>' is not a valid symbol. This is not Rust! Should it be '::'?".to_string(),
|
||||
))),
|
||||
"(*" | "*)" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?"
|
||||
.to_string(),
|
||||
("(*", false) | ("*)", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(),
|
||||
))),
|
||||
"#" => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'#' is not a valid symbol. Should it be '#{'?"
|
||||
.to_string(),
|
||||
("#", false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
"'#' is not a valid symbol. Should it be '#{'?".to_string(),
|
||||
))),
|
||||
token if !is_valid_identifier(token.chars()) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
format!("'{}' is a reserved symbol.", token)
|
||||
// Reserved keyword/operator that is custom.
|
||||
(_, true) => Token::Custom(s),
|
||||
// Reserved operator that is not custom.
|
||||
(token, false) if !is_valid_identifier(token.chars()) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
format!("'{}' is a reserved symbol", token)
|
||||
))),
|
||||
_ => Token::Reserved(s)
|
||||
// Reserved keyword that is not custom and disabled.
|
||||
(token, false) if disabled.map(|d| d.contains(token)).unwrap_or(false) => Token::LexError(Box::new(LERR::ImproperSymbol(
|
||||
format!("reserved symbol '{}' is disabled", token)
|
||||
))),
|
||||
// Reserved keyword/operator that is not custom.
|
||||
(_, false) => Token::Reserved(s),
|
||||
}, pos)),
|
||||
(r @ Some(_), None, None) => r,
|
||||
// Custom keyword
|
||||
(Some((Token::Identifier(s), pos)), _, Some(custom)) if custom.contains_key(&s) => {
|
||||
// Convert custom keywords
|
||||
Some((Token::Custom(s), pos))
|
||||
}
|
||||
(Some((token, pos)), _, Some(custom))
|
||||
if (token.is_keyword() || token.is_operator() || token.is_reserved())
|
||||
&& custom.contains_key(token.syntax().as_ref()) =>
|
||||
// Custom standard keyword - must be disabled
|
||||
(Some((token, pos)), Some(disabled), Some(custom))
|
||||
if token.is_keyword() && custom.contains_key(token.syntax().as_ref()) =>
|
||||
{
|
||||
// Convert into custom keywords
|
||||
Some((Token::Custom(token.syntax().into()), pos))
|
||||
if disabled.contains(token.syntax().as_ref()) {
|
||||
// Disabled standard keyword
|
||||
Some((Token::Custom(token.syntax().into()), pos))
|
||||
} else {
|
||||
// Active standard keyword - should never be a custom keyword!
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
// Disabled operator
|
||||
(Some((token, pos)), Some(disabled), _)
|
||||
if token.is_operator() && disabled.contains(token.syntax().as_ref()) =>
|
||||
{
|
||||
// Convert disallowed operators into lex errors
|
||||
Some((
|
||||
Token::LexError(Box::new(LexError::UnexpectedInput(token.syntax().into()))),
|
||||
pos,
|
||||
))
|
||||
}
|
||||
// Disabled standard keyword
|
||||
(Some((token, pos)), Some(disabled), _)
|
||||
if token.is_keyword() && disabled.contains(token.syntax().as_ref()) =>
|
||||
{
|
||||
// Convert disallowed keywords into identifiers
|
||||
Some((Token::Identifier(token.syntax().into()), pos))
|
||||
Some((Token::Reserved(token.syntax().into()), pos))
|
||||
}
|
||||
(r, _, _) => r,
|
||||
}
|
||||
|
Reference in New Issue
Block a user