Add Ident/IdentX to AST.
This commit is contained in:
parent
00b1051732
commit
f56c3fe9cb
@ -24,6 +24,11 @@ New features
|
|||||||
* Low-level API for custom syntax allowing more flexibility in designing the syntax.
|
* Low-level API for custom syntax allowing more flexibility in designing the syntax.
|
||||||
* `Module::fill_with` to poly-fill a module with another.
|
* `Module::fill_with` to poly-fill a module with another.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* AST data structures are optimized to maximize cache friendliness. This may have speed impacts on large, complex scripts (benchmarks wanted!).
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.3
|
Version 0.19.3
|
||||||
==============
|
==============
|
||||||
|
@ -6,7 +6,7 @@ use crate::fn_native::{Callback, FnPtr, OnVarCallback};
|
|||||||
use crate::module::{Module, ModuleRef};
|
use crate::module::{Module, ModuleRef};
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
||||||
use crate::parser::{BinaryExpr, Expr, ReturnType, Stmt};
|
use crate::parser::{BinaryExpr, Expr, Ident, ReturnType, Stmt};
|
||||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
@ -33,6 +33,7 @@ use crate::any::DynamicWriteLock;
|
|||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::type_name,
|
any::type_name,
|
||||||
|
borrow::Cow,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt, format,
|
fmt, format,
|
||||||
@ -393,6 +394,11 @@ impl State {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
/// Is the state currently at global (root) level?
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_global(&self) -> bool {
|
||||||
|
self.scope_level == 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A type containing all the limits imposed by the `Engine`.
|
/// _[INTERNALS]_ A type containing all the limits imposed by the `Engine`.
|
||||||
@ -764,7 +770,7 @@ impl Engine {
|
|||||||
match expr {
|
match expr {
|
||||||
Expr::Variable(v) => match v.as_ref() {
|
Expr::Variable(v) => match v.as_ref() {
|
||||||
// Qualified variable
|
// Qualified variable
|
||||||
((name, pos), Some(modules), hash_var, _) => {
|
(Ident { name, pos }, Some(modules), hash_var, _) => {
|
||||||
let module = search_imports_mut(mods, state, modules)?;
|
let module = search_imports_mut(mods, state, modules)?;
|
||||||
let target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| {
|
let target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| {
|
||||||
match *err {
|
match *err {
|
||||||
@ -796,7 +802,7 @@ impl Engine {
|
|||||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||||
expr: &'a Expr,
|
expr: &'a Expr,
|
||||||
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
||||||
let ((name, pos), _, _, index) = match expr {
|
let (Ident { name, pos }, _, _, index) = match expr {
|
||||||
Expr::Variable(v) => v.as_ref(),
|
Expr::Variable(v) => v.as_ref(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
@ -1187,7 +1193,10 @@ impl Engine {
|
|||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (var_name, var_pos) = &x.0;
|
let Ident {
|
||||||
|
name: var_name,
|
||||||
|
pos: var_pos,
|
||||||
|
} = &x.0;
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(*var_pos))?;
|
.map_err(|err| err.fill_position(*var_pos))?;
|
||||||
@ -1491,14 +1500,16 @@ impl Engine {
|
|||||||
Expr::IntegerConstant(x) => Ok(x.0.into()),
|
Expr::IntegerConstant(x) => Ok(x.0.into()),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Expr::FloatConstant(x) => Ok(x.0.into()),
|
Expr::FloatConstant(x) => Ok(x.0.into()),
|
||||||
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
Expr::StringConstant(x) => Ok(x.name.to_string().into()),
|
||||||
Expr::CharConstant(x) => Ok(x.0.into()),
|
Expr::CharConstant(x) => Ok(x.0.into()),
|
||||||
Expr::FnPointer(x) => Ok(FnPtr::new_unchecked(x.0.clone(), Default::default()).into()),
|
Expr::FnPointer(x) => {
|
||||||
Expr::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into())
|
||||||
|
}
|
||||||
|
Expr::Variable(x) if (x.0).name == KEYWORD_THIS => {
|
||||||
if let Some(val) = this_ptr {
|
if let Some(val) = this_ptr {
|
||||||
Ok(val.clone())
|
Ok(val.clone())
|
||||||
} else {
|
} else {
|
||||||
EvalAltResult::ErrorUnboundThis((x.0).1).into()
|
EvalAltResult::ErrorUnboundThis((x.0).pos).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Variable(_) => {
|
Expr::Variable(_) => {
|
||||||
@ -1533,9 +1544,9 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
|
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
|
||||||
x.0.iter()
|
x.0.iter()
|
||||||
.map(|((key, _), expr)| {
|
.map(|(key, expr)| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(|val| (key.clone(), val))
|
.map(|val| (key.name.clone(), val))
|
||||||
})
|
})
|
||||||
.collect::<Result<HashMap<_, _>, _>>()?,
|
.collect::<Result<HashMap<_, _>, _>>()?,
|
||||||
)))),
|
)))),
|
||||||
@ -1885,7 +1896,11 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(func) = func {
|
if let Some(func) = func {
|
||||||
// Add the loop variable
|
// Add the loop variable
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(name, &state);
|
let var_name: Cow<'_, str> = if state.is_global() {
|
||||||
|
name.clone().into()
|
||||||
|
} else {
|
||||||
|
unsafe_cast_var_name_to_lifetime(name).into()
|
||||||
|
};
|
||||||
scope.push(var_name, ());
|
scope.push(var_name, ());
|
||||||
let index = scope.len() - 1;
|
let index = scope.len() - 1;
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
@ -1929,7 +1944,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Try/Catch statement
|
// Try/Catch statement
|
||||||
Stmt::TryCatch(x) => {
|
Stmt::TryCatch(x) => {
|
||||||
let ((try_body, _), var_def, (catch_body, _)) = x.as_ref();
|
let (try_body, var_def, catch_body, _) = x.as_ref();
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt(scope, mods, state, lib, this_ptr, try_body, level)
|
.eval_stmt(scope, mods, state, lib, this_ptr, try_body, level)
|
||||||
@ -1951,8 +1966,12 @@ impl Engine {
|
|||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
|
|
||||||
if let Some((var_name, _)) = var_def {
|
if let Some(Ident { name, .. }) = var_def {
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
let var_name: Cow<'_, str> = if state.is_global() {
|
||||||
|
name.clone().into()
|
||||||
|
} else {
|
||||||
|
unsafe_cast_var_name_to_lifetime(name).into()
|
||||||
|
};
|
||||||
scope.push(var_name, value);
|
scope.push(var_name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2016,7 +2035,11 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
().into()
|
().into()
|
||||||
};
|
};
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(&var_def.0, &state);
|
let var_name: Cow<'_, str> = if state.is_global() {
|
||||||
|
var_def.name.clone().into()
|
||||||
|
} else {
|
||||||
|
unsafe_cast_var_name_to_lifetime(&var_def.name).into()
|
||||||
|
};
|
||||||
scope.push_dynamic_value(var_name, entry_type, val, false);
|
scope.push_dynamic_value(var_name, entry_type, val, false);
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
@ -2039,7 +2062,7 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(name_def) = alias {
|
if let Some(name_def) = alias {
|
||||||
module.index_all_sub_modules();
|
module.index_all_sub_modules();
|
||||||
mods.push((name_def.0.clone(), module));
|
mods.push((name_def.name.clone(), module));
|
||||||
}
|
}
|
||||||
|
|
||||||
state.modules += 1;
|
state.modules += 1;
|
||||||
@ -2059,13 +2082,13 @@ impl Engine {
|
|||||||
// Export statement
|
// Export statement
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Export(list, _) => {
|
Stmt::Export(list, _) => {
|
||||||
for ((id, id_pos), rename) in list.iter() {
|
for (Ident { name, pos: id_pos }, rename) in list.iter() {
|
||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
if let Some(index) = scope.get_index(id).map(|(i, _)| i) {
|
if let Some(index) = scope.get_index(name).map(|(i, _)| i) {
|
||||||
let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id);
|
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name);
|
||||||
scope.set_entry_alias(index, alias.clone());
|
scope.set_entry_alias(index, alias.clone());
|
||||||
} else {
|
} else {
|
||||||
return EvalAltResult::ErrorVariableNotFound(id.into(), *id_pos).into();
|
return EvalAltResult::ErrorVariableNotFound(name.into(), *id_pos).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
@ -2073,7 +2096,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Share statement
|
// Share statement
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Stmt::Share(var_name, _) => {
|
Stmt::Share(Ident { name: var_name, .. }) => {
|
||||||
match scope.get_index(var_name) {
|
match scope.get_index(var_name) {
|
||||||
Some((index, ScopeEntryType::Normal)) => {
|
Some((index, ScopeEntryType::Normal)) => {
|
||||||
let (val, _) = scope.get_mut(index);
|
let (val, _) = scope.get_mut(index);
|
||||||
|
@ -41,6 +41,7 @@ use crate::scope::Entry as ScopeEntry;
|
|||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
|
borrow::Cow,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
format,
|
format,
|
||||||
@ -376,7 +377,7 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.zip(args.iter_mut().map(|v| mem::take(*v)))
|
.zip(args.iter_mut().map(|v| mem::take(*v)))
|
||||||
.map(|(name, value)| {
|
.map(|(name, value)| {
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(name.as_str(), state);
|
let var_name: Cow<'_, str> = unsafe_cast_var_name_to_lifetime(name).into();
|
||||||
(var_name, ScopeEntryType::Normal, value)
|
(var_name, ScopeEntryType::Normal, value)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -149,7 +149,9 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
|
|||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated(note = "this type is volatile and may change")]
|
#[deprecated(note = "this type is volatile and may change")]
|
||||||
pub use parser::{CustomExpr, Expr, FloatWrapper, ReturnType, ScriptFnDef, Stmt};
|
pub use parser::{
|
||||||
|
BinaryExpr, CustomExpr, Expr, FloatWrapper, Ident, IdentX, ReturnType, ScriptFnDef, Stmt,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated(note = "this type is volatile and may change")]
|
#[deprecated(note = "this type is volatile and may change")]
|
||||||
|
@ -7,7 +7,7 @@ use crate::engine::{
|
|||||||
};
|
};
|
||||||
use crate::fn_call::run_builtin_binary_op;
|
use crate::fn_call::run_builtin_binary_op;
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::parser::{map_dynamic_to_expr, BinaryExpr, Expr, ScriptFnDef, Stmt, AST};
|
use crate::parser::{map_dynamic_to_expr, BinaryExpr, CustomExpr, Expr, ScriptFnDef, Stmt, AST};
|
||||||
use crate::scope::{Entry as ScopeEntry, Scope};
|
use crate::scope::{Entry as ScopeEntry, Scope};
|
||||||
use crate::token::{is_valid_identifier, Position};
|
use crate::token::{is_valid_identifier, Position};
|
||||||
use crate::{calc_fn_hash, StaticVec};
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
@ -15,9 +15,6 @@ use crate::{calc_fn_hash, StaticVec};
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::parser::ReturnType;
|
use crate::parser::ReturnType;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
|
||||||
use crate::parser::CustomExpr;
|
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
iter::empty,
|
iter::empty,
|
||||||
@ -283,18 +280,18 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|stmt| match stmt {
|
.map(|stmt| match stmt {
|
||||||
// Add constant literals into the state
|
// Add constant literals into the state
|
||||||
Stmt::Const(name, Some(expr), pos) if expr.is_literal() => {
|
Stmt::Const(var_def, Some(expr), pos) if expr.is_literal() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
state.push_constant(&name.0, expr);
|
state.push_constant(&var_def.name, expr);
|
||||||
Stmt::Noop(pos) // No need to keep constants
|
Stmt::Noop(pos) // No need to keep constants
|
||||||
}
|
}
|
||||||
Stmt::Const(name, Some(expr), pos) if expr.is_literal() => {
|
Stmt::Const(var_def, Some(expr), pos) if expr.is_literal() => {
|
||||||
let expr = optimize_expr(expr, state);
|
let expr = optimize_expr(expr, state);
|
||||||
Stmt::Const(name, Some(expr), pos)
|
Stmt::Const(var_def, Some(expr), pos)
|
||||||
}
|
}
|
||||||
Stmt::Const(name, None, pos) => {
|
Stmt::Const(var_def, None, pos) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
state.push_constant(&name.0, Expr::Unit(name.1));
|
state.push_constant(&var_def.name, Expr::Unit(var_def.pos));
|
||||||
Stmt::Noop(pos) // No need to keep constants
|
Stmt::Noop(pos) // No need to keep constants
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
@ -389,22 +386,25 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// try { block } catch ( var ) { block }
|
// try { block } catch ( var ) { block }
|
||||||
Stmt::TryCatch(x) if (x.0).0.is_pure() => {
|
Stmt::TryCatch(x) if x.0.is_pure() => {
|
||||||
// If try block is pure, there will never be any exceptions
|
// If try block is pure, there will never be any exceptions
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = (x.0).0.position();
|
let pos = x.0.position();
|
||||||
let mut statements: Vec<_> = Default::default();
|
let mut statements = match optimize_stmt(x.0, state, preserve_result) {
|
||||||
statements.push(optimize_stmt((x.0).0, state, preserve_result));
|
Stmt::Block(statements, _) => statements,
|
||||||
|
stmt => vec![stmt],
|
||||||
|
};
|
||||||
statements.push(Stmt::Noop(pos));
|
statements.push(Stmt::Noop(pos));
|
||||||
Stmt::Block(statements, pos)
|
Stmt::Block(statements, pos)
|
||||||
}
|
}
|
||||||
// try { block } catch ( var ) { block }
|
// try { block } catch ( var ) { block }
|
||||||
Stmt::TryCatch(x) => {
|
Stmt::TryCatch(x) => {
|
||||||
let ((try_block, try_pos), var_name, (catch_block, catch_pos)) = *x;
|
let (try_block, var_name, catch_block, pos) = *x;
|
||||||
Stmt::TryCatch(Box::new((
|
Stmt::TryCatch(Box::new((
|
||||||
(optimize_stmt(try_block, state, false), try_pos),
|
optimize_stmt(try_block, state, false),
|
||||||
var_name,
|
var_name,
|
||||||
(optimize_stmt(catch_block, state, false), catch_pos),
|
optimize_stmt(catch_block, state, false),
|
||||||
|
pos,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
// expr;
|
// expr;
|
||||||
@ -463,7 +463,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.into_iter().find(|((name, _), _)| name == prop)
|
m.0.into_iter().find(|(x, _)| &x.name == prop)
|
||||||
.map(|(_, mut expr)| { expr.set_position(pos); expr })
|
.map(|(_, mut expr)| { expr.set_position(pos); expr })
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
@ -495,15 +495,15 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.into_iter().find(|((name, _), _)| *name == s.0)
|
m.0.into_iter().find(|(x, _)| x.name == s.name)
|
||||||
.map(|(_, mut expr)| { expr.set_position(pos); expr })
|
.map(|(_, mut expr)| { expr.set_position(pos); expr })
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// string[int]
|
// string[int]
|
||||||
(Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.0.chars().count() => {
|
(Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.name.chars().count() => {
|
||||||
// String literal indexing - get the character
|
// String literal indexing - get the character
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Expr::CharConstant(Box::new((s.0.chars().nth(i.0 as usize).unwrap(), s.1)))
|
Expr::CharConstant(Box::new((s.name.chars().nth(i.0 as usize).unwrap(), s.pos)))
|
||||||
}
|
}
|
||||||
// lhs[rhs]
|
// lhs[rhs]
|
||||||
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr {
|
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr {
|
||||||
@ -520,27 +520,27 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(m) => Expr::Map(Box::new((m.0
|
Expr::Map(m) => Expr::Map(Box::new((m.0
|
||||||
.into_iter().map(|((key, pos), expr)| ((key, pos), optimize_expr(expr, state)))
|
.into_iter().map(|(key, expr)| (key, optimize_expr(expr, state)))
|
||||||
.collect(), m.1))),
|
.collect(), m.1))),
|
||||||
// lhs in rhs
|
// lhs in rhs
|
||||||
Expr::In(x) => match (x.lhs, x.rhs) {
|
Expr::In(x) => match (x.lhs, x.rhs) {
|
||||||
// "xxx" in "xxxxx"
|
// "xxx" in "xxxxx"
|
||||||
(Expr::StringConstant(a), Expr::StringConstant(b)) => {
|
(Expr::StringConstant(a), Expr::StringConstant(b)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if b.0.contains(a.0.as_str()) { Expr::True(a.1) } else { Expr::False(a.1) }
|
if b.name.contains(a.name.as_str()) { Expr::True(a.pos) } else { Expr::False(a.pos) }
|
||||||
}
|
}
|
||||||
// 'x' in "xxxxx"
|
// 'x' in "xxxxx"
|
||||||
(Expr::CharConstant(a), Expr::StringConstant(b)) => {
|
(Expr::CharConstant(a), Expr::StringConstant(b)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if b.0.contains(a.0) { Expr::True(a.1) } else { Expr::False(a.1) }
|
if b.name.contains(a.0) { Expr::True(a.1) } else { Expr::False(a.1) }
|
||||||
}
|
}
|
||||||
// "xxx" in #{...}
|
// "xxx" in #{...}
|
||||||
(Expr::StringConstant(a), Expr::Map(b)) => {
|
(Expr::StringConstant(a), Expr::Map(b)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if b.0.iter().find(|((name, _), _)| *name == a.0).is_some() {
|
if b.0.iter().find(|(x, _)| x.name == a.name).is_some() {
|
||||||
Expr::True(a.1)
|
Expr::True(a.pos)
|
||||||
} else {
|
} else {
|
||||||
Expr::False(a.1)
|
Expr::False(a.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 'x' in #{...}
|
// 'x' in #{...}
|
||||||
@ -548,7 +548,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let ch = a.0.to_string();
|
let ch = a.0.to_string();
|
||||||
|
|
||||||
if b.0.iter().find(|((name, _), _)| name == &ch).is_some() {
|
if b.0.iter().find(|(x, _)| x.name == &ch).is_some() {
|
||||||
Expr::True(a.1)
|
Expr::True(a.1)
|
||||||
} else {
|
} else {
|
||||||
Expr::False(a.1)
|
Expr::False(a.1)
|
||||||
@ -697,24 +697,20 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&(x.0).0) => {
|
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.0.name) => {
|
||||||
let (name, pos) = x.0;
|
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
let mut expr = state.find_constant(&name).unwrap().clone();
|
let mut expr = state.find_constant(&x.0.name).unwrap().clone();
|
||||||
expr.set_position(pos);
|
expr.set_position(x.0.pos);
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom syntax
|
// Custom syntax
|
||||||
#[cfg(feature = "internals")]
|
Expr::Custom(x) => Expr::Custom(Box::new(CustomExpr {
|
||||||
Expr::Custom(x) => Expr::Custom(Box::new((
|
keywords: x.keywords.into_iter().map(|expr| optimize_expr(expr, state)).collect(),
|
||||||
CustomExpr(
|
..*x
|
||||||
(x.0).0.into_iter().map(|expr| optimize_expr(expr, state)).collect(),
|
})),
|
||||||
(x.0).1),
|
|
||||||
x.1
|
|
||||||
))),
|
|
||||||
|
|
||||||
// All other expressions - skip
|
// All other expressions - skip
|
||||||
expr => expr,
|
expr => expr,
|
||||||
@ -776,7 +772,7 @@ fn optimize(
|
|||||||
let expr = optimize_expr(expr, &mut state);
|
let expr = optimize_expr(expr, &mut state);
|
||||||
|
|
||||||
if expr.is_literal() {
|
if expr.is_literal() {
|
||||||
state.push_constant(&var_def.0, expr.clone());
|
state.push_constant(&var_def.name, expr.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep it in the global scope
|
// Keep it in the global scope
|
||||||
@ -788,7 +784,7 @@ fn optimize(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Const(ref var_def, None, _) => {
|
Stmt::Const(ref var_def, None, _) => {
|
||||||
state.push_constant(&var_def.0, Expr::Unit(var_def.1));
|
state.push_constant(&var_def.name, Expr::Unit(var_def.pos));
|
||||||
|
|
||||||
// Keep it in the global scope
|
// Keep it in the global scope
|
||||||
stmt
|
stmt
|
||||||
|
240
src/parser.rs
240
src/parser.rs
@ -733,6 +733,37 @@ impl ParseSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An identifier containing a string name and a position.
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub struct Ident {
|
||||||
|
pub name: String,
|
||||||
|
pub pos: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ident {
|
||||||
|
/// Create a new `Identifier`.
|
||||||
|
pub fn new(name: String, pos: Position) -> Self {
|
||||||
|
Self { name, pos }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An identifier containing an immutable name and a position.
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub struct IdentX {
|
||||||
|
pub name: ImmutableString,
|
||||||
|
pub pos: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdentX {
|
||||||
|
/// Create a new `Identifier`.
|
||||||
|
pub fn new(name: impl Into<ImmutableString>, pos: Position) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A Rhai statement.
|
/// _[INTERNALS]_ A Rhai statement.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
@ -751,21 +782,15 @@ pub enum Stmt {
|
|||||||
/// for id in expr { stmt }
|
/// for id in expr { stmt }
|
||||||
For(Expr, Box<(String, Stmt)>, Position),
|
For(Expr, Box<(String, Stmt)>, Position),
|
||||||
/// let id = expr
|
/// let id = expr
|
||||||
Let(Box<(String, Position)>, Option<Expr>, Position),
|
Let(Box<Ident>, Option<Expr>, Position),
|
||||||
/// const id = expr
|
/// const id = expr
|
||||||
Const(Box<(String, Position)>, Option<Expr>, Position),
|
Const(Box<Ident>, Option<Expr>, Position),
|
||||||
/// expr op= expr
|
/// expr op= expr
|
||||||
Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position),
|
Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position),
|
||||||
/// { stmt; ... }
|
/// { stmt; ... }
|
||||||
Block(Vec<Stmt>, Position),
|
Block(Vec<Stmt>, Position),
|
||||||
/// try { stmt; ... } catch ( var ) { stmt; ... }
|
/// try { stmt; ... } catch ( var ) { stmt; ... }
|
||||||
TryCatch(
|
TryCatch(Box<(Stmt, Option<Ident>, Stmt, (Position, Position))>),
|
||||||
Box<(
|
|
||||||
(Stmt, Position),
|
|
||||||
Option<(String, Position)>,
|
|
||||||
(Stmt, Position),
|
|
||||||
)>,
|
|
||||||
),
|
|
||||||
/// expr
|
/// expr
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
/// continue
|
/// continue
|
||||||
@ -776,16 +801,13 @@ pub enum Stmt {
|
|||||||
ReturnWithVal((ReturnType, Position), Option<Expr>, Position),
|
ReturnWithVal((ReturnType, Position), Option<Expr>, Position),
|
||||||
/// import expr as var
|
/// import expr as var
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Import(Expr, Option<Box<(ImmutableString, Position)>>, Position),
|
Import(Expr, Option<Box<IdentX>>, Position),
|
||||||
/// export var as var, ...
|
/// export var as var, ...
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Export(
|
Export(Vec<(Ident, Option<Ident>)>, Position),
|
||||||
Vec<((String, Position), Option<(String, Position)>)>,
|
|
||||||
Position,
|
|
||||||
),
|
|
||||||
/// Convert a variable to shared.
|
/// Convert a variable to shared.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Share(String, Position),
|
Share(Ident),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Stmt {
|
impl Default for Stmt {
|
||||||
@ -818,8 +840,8 @@ impl Stmt {
|
|||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, pos)
|
||||||
| Self::ReturnWithVal((_, pos), _, _) => *pos,
|
| Self::ReturnWithVal((_, pos), _, _) => *pos,
|
||||||
|
|
||||||
Self::Let(x, _, _) | Self::Const(x, _, _) => x.1,
|
Self::Let(x, _, _) | Self::Const(x, _, _) => x.pos,
|
||||||
Self::TryCatch(x) => (x.0).1,
|
Self::TryCatch(x) => (x.3).0,
|
||||||
|
|
||||||
Self::Expr(x) => x.position(),
|
Self::Expr(x) => x.position(),
|
||||||
|
|
||||||
@ -829,7 +851,7 @@ impl Stmt {
|
|||||||
Self::Export(_, pos) => *pos,
|
Self::Export(_, pos) => *pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(_, pos) => *pos,
|
Self::Share(Ident { pos, .. }) => *pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,8 +869,8 @@ impl Stmt {
|
|||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, pos)
|
||||||
| Self::ReturnWithVal((_, pos), _, _) => *pos = new_pos,
|
| Self::ReturnWithVal((_, pos), _, _) => *pos = new_pos,
|
||||||
|
|
||||||
Self::Let(x, _, _) | Self::Const(x, _, _) => x.1 = new_pos,
|
Self::Let(x, _, _) | Self::Const(x, _, _) => x.pos = new_pos,
|
||||||
Self::TryCatch(x) => (x.0).1 = new_pos,
|
Self::TryCatch(x) => (x.3).0 = new_pos,
|
||||||
|
|
||||||
Self::Expr(x) => {
|
Self::Expr(x) => {
|
||||||
x.set_position(new_pos);
|
x.set_position(new_pos);
|
||||||
@ -860,7 +882,7 @@ impl Stmt {
|
|||||||
Self::Export(_, pos) => *pos = new_pos,
|
Self::Export(_, pos) => *pos = new_pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(_, pos) => *pos = new_pos,
|
Self::Share(Ident { pos, .. }) => *pos = new_pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -891,7 +913,7 @@ impl Stmt {
|
|||||||
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(_, _) => false,
|
Self::Share(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,7 +932,7 @@ impl Stmt {
|
|||||||
Self::Let(_, _, _) | Self::Const(_, _, _) | Self::Assignment(_, _) => false,
|
Self::Let(_, _, _) | Self::Const(_, _, _) | Self::Assignment(_, _) => false,
|
||||||
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
||||||
Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_, _, _) => false,
|
Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_, _, _) => false,
|
||||||
Self::TryCatch(x) => (x.0).0.is_pure() && (x.2).0.is_pure(),
|
Self::TryCatch(x) => x.0.is_pure() && x.2.is_pure(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Self::Import(_, _, _) => false,
|
Self::Import(_, _, _) => false,
|
||||||
@ -918,7 +940,7 @@ impl Stmt {
|
|||||||
Self::Export(_, _) => false,
|
Self::Export(_, _) => false,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(_, _) => false,
|
Self::Share(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -931,9 +953,9 @@ impl Stmt {
|
|||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CustomExpr {
|
pub struct CustomExpr {
|
||||||
keywords: StaticVec<Expr>,
|
pub(crate) keywords: StaticVec<Expr>,
|
||||||
func: Shared<FnCustomSyntaxEval>,
|
pub(crate) func: Shared<FnCustomSyntaxEval>,
|
||||||
pos: Position,
|
pub(crate) pos: Position,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for CustomExpr {
|
impl fmt::Debug for CustomExpr {
|
||||||
@ -990,6 +1012,7 @@ impl Hash for FloatWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A binary expression structure.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub struct BinaryExpr {
|
pub struct BinaryExpr {
|
||||||
pub lhs: Expr,
|
pub lhs: Expr,
|
||||||
@ -1016,18 +1039,11 @@ pub enum Expr {
|
|||||||
/// Character constant.
|
/// Character constant.
|
||||||
CharConstant(Box<(char, Position)>),
|
CharConstant(Box<(char, Position)>),
|
||||||
/// String constant.
|
/// String constant.
|
||||||
StringConstant(Box<(ImmutableString, Position)>),
|
StringConstant(Box<IdentX>),
|
||||||
/// FnPtr constant.
|
/// FnPtr constant.
|
||||||
FnPointer(Box<(ImmutableString, Position)>),
|
FnPointer(Box<IdentX>),
|
||||||
/// Variable access - ((variable name, position), optional modules, hash, optional index)
|
/// Variable access - ((variable name, position), optional modules, hash, optional index)
|
||||||
Variable(
|
Variable(Box<(Ident, Option<Box<ModuleRef>>, u64, Option<NonZeroUsize>)>),
|
||||||
Box<(
|
|
||||||
(String, Position),
|
|
||||||
Option<Box<ModuleRef>>,
|
|
||||||
u64,
|
|
||||||
Option<NonZeroUsize>,
|
|
||||||
)>,
|
|
||||||
),
|
|
||||||
/// Property access.
|
/// Property access.
|
||||||
Property(Box<((ImmutableString, String, String), Position)>),
|
Property(Box<((ImmutableString, String, String), Position)>),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
@ -1053,7 +1069,7 @@ pub enum Expr {
|
|||||||
/// [ expr, ... ]
|
/// [ expr, ... ]
|
||||||
Array(Box<(StaticVec<Expr>, Position)>),
|
Array(Box<(StaticVec<Expr>, Position)>),
|
||||||
/// #{ name:expr, ... }
|
/// #{ name:expr, ... }
|
||||||
Map(Box<(StaticVec<((ImmutableString, Position), Expr)>, Position)>),
|
Map(Box<(StaticVec<(IdentX, Expr)>, Position)>),
|
||||||
/// lhs in rhs
|
/// lhs in rhs
|
||||||
In(Box<BinaryExpr>),
|
In(Box<BinaryExpr>),
|
||||||
/// lhs && rhs
|
/// lhs && rhs
|
||||||
@ -1117,9 +1133,9 @@ impl Expr {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Self::FloatConstant(x) => x.0.into(),
|
Self::FloatConstant(x) => x.0.into(),
|
||||||
Self::CharConstant(x) => x.0.into(),
|
Self::CharConstant(x) => x.0.into(),
|
||||||
Self::StringConstant(x) => x.0.clone().into(),
|
Self::StringConstant(x) => x.name.clone().into(),
|
||||||
Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
|
Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
|
||||||
x.0.clone(),
|
x.name.clone(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
)))),
|
)))),
|
||||||
Self::True(_) => true.into(),
|
Self::True(_) => true.into(),
|
||||||
@ -1137,7 +1153,7 @@ impl Expr {
|
|||||||
Self::Map(x) if x.0.iter().all(|(_, v)| v.is_constant()) => {
|
Self::Map(x) if x.0.iter().all(|(_, v)| v.is_constant()) => {
|
||||||
Dynamic(Union::Map(Box::new(
|
Dynamic(Union::Map(Box::new(
|
||||||
x.0.iter()
|
x.0.iter()
|
||||||
.map(|((k, _), v)| (k.clone(), v.get_constant_value().unwrap()))
|
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap()))
|
||||||
.collect(),
|
.collect(),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
@ -1149,7 +1165,7 @@ impl Expr {
|
|||||||
/// Is the expression a simple variable access?
|
/// Is the expression a simple variable access?
|
||||||
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
|
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.0).0.as_str()),
|
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.0).name.as_str()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1164,13 +1180,13 @@ impl Expr {
|
|||||||
|
|
||||||
Self::IntegerConstant(x) => x.1,
|
Self::IntegerConstant(x) => x.1,
|
||||||
Self::CharConstant(x) => x.1,
|
Self::CharConstant(x) => x.1,
|
||||||
Self::StringConstant(x) => x.1,
|
Self::StringConstant(x) => x.pos,
|
||||||
Self::FnPointer(x) => x.1,
|
Self::FnPointer(x) => x.pos,
|
||||||
Self::Array(x) => x.1,
|
Self::Array(x) => x.1,
|
||||||
Self::Map(x) => x.1,
|
Self::Map(x) => x.1,
|
||||||
Self::Property(x) => x.1,
|
Self::Property(x) => x.1,
|
||||||
Self::Stmt(x) => x.1,
|
Self::Stmt(x) => x.1,
|
||||||
Self::Variable(x) => (x.0).1,
|
Self::Variable(x) => (x.0).pos,
|
||||||
Self::FnCall(x) => (x.0).3,
|
Self::FnCall(x) => (x.0).3,
|
||||||
|
|
||||||
Self::And(x) | Self::Or(x) | Self::In(x) => x.pos,
|
Self::And(x) | Self::Or(x) | Self::In(x) => x.pos,
|
||||||
@ -1195,11 +1211,11 @@ impl Expr {
|
|||||||
|
|
||||||
Self::IntegerConstant(x) => x.1 = new_pos,
|
Self::IntegerConstant(x) => x.1 = new_pos,
|
||||||
Self::CharConstant(x) => x.1 = new_pos,
|
Self::CharConstant(x) => x.1 = new_pos,
|
||||||
Self::StringConstant(x) => x.1 = new_pos,
|
Self::StringConstant(x) => x.pos = new_pos,
|
||||||
Self::FnPointer(x) => x.1 = new_pos,
|
Self::FnPointer(x) => x.pos = new_pos,
|
||||||
Self::Array(x) => x.1 = new_pos,
|
Self::Array(x) => x.1 = new_pos,
|
||||||
Self::Map(x) => x.1 = new_pos,
|
Self::Map(x) => x.1 = new_pos,
|
||||||
Self::Variable(x) => (x.0).1 = new_pos,
|
Self::Variable(x) => (x.0).pos = new_pos,
|
||||||
Self::Property(x) => x.1 = new_pos,
|
Self::Property(x) => x.1 = new_pos,
|
||||||
Self::Stmt(x) => x.1 = new_pos,
|
Self::Stmt(x) => x.1 = new_pos,
|
||||||
Self::FnCall(x) => (x.0).3 = new_pos,
|
Self::FnCall(x) => (x.0).3 = new_pos,
|
||||||
@ -1364,7 +1380,7 @@ impl Expr {
|
|||||||
pub(crate) fn into_property(self) -> Self {
|
pub(crate) fn into_property(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Variable(x) if x.1.is_none() => {
|
Self::Variable(x) if x.1.is_none() => {
|
||||||
let (name, pos) = x.0;
|
let Ident { name, pos } = x.0;
|
||||||
let getter = make_getter(&name);
|
let getter = make_getter(&name);
|
||||||
let setter = make_setter(&name);
|
let setter = make_setter(&name);
|
||||||
Self::Property(Box::new(((name.into(), getter, setter), pos)))
|
Self::Property(Box::new(((name.into(), getter, setter), pos)))
|
||||||
@ -1622,7 +1638,7 @@ fn parse_index_chain(
|
|||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
"Array or string expects numeric index, not a string".into(),
|
"Array or string expects numeric index, not a string".into(),
|
||||||
)
|
)
|
||||||
.into_err(x.1))
|
.into_err(x.pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -1872,7 +1888,7 @@ fn parse_map_literal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
map.push(((Into::<ImmutableString>::into(name), pos), expr));
|
map.push((IdentX::new(name, pos), expr));
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
@ -1899,11 +1915,11 @@ fn parse_map_literal(
|
|||||||
// Check for duplicating properties
|
// Check for duplicating properties
|
||||||
map.iter()
|
map.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.try_for_each(|(i, ((k1, _), _))| {
|
.try_for_each(|(i, (IdentX { name: k1, .. }, _))| {
|
||||||
map.iter()
|
map.iter()
|
||||||
.skip(i + 1)
|
.skip(i + 1)
|
||||||
.find(|((k2, _), _)| k2 == k1)
|
.find(|(IdentX { name: k2, .. }, _)| k2 == k1)
|
||||||
.map_or_else(|| Ok(()), |((k2, pos), _)| Err((k2, *pos)))
|
.map_or_else(|| Ok(()), |(IdentX { name: k2, pos }, _)| Err((k2, *pos)))
|
||||||
})
|
})
|
||||||
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
||||||
|
|
||||||
@ -1940,7 +1956,7 @@ fn parse_primary(
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Token::FloatConstant(x) => Expr::FloatConstant(Box::new(FloatWrapper(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::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))),
|
||||||
Token::StringConstant(s) => Expr::StringConstant(Box::new((s.into(), settings.pos))),
|
Token::StringConstant(s) => Expr::StringConstant(Box::new(IdentX::new(s, settings.pos))),
|
||||||
|
|
||||||
// Function call
|
// Function call
|
||||||
Token::Identifier(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
|
Token::Identifier(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
|
||||||
@ -1949,7 +1965,7 @@ fn parse_primary(
|
|||||||
{
|
{
|
||||||
state.allow_capture = true;
|
state.allow_capture = true;
|
||||||
}
|
}
|
||||||
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None)))
|
||||||
}
|
}
|
||||||
// Module qualification
|
// Module qualification
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1959,18 +1975,18 @@ fn parse_primary(
|
|||||||
{
|
{
|
||||||
state.allow_capture = true;
|
state.allow_capture = true;
|
||||||
}
|
}
|
||||||
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None)))
|
||||||
}
|
}
|
||||||
// Normal variable access
|
// Normal variable access
|
||||||
Token::Identifier(s) => {
|
Token::Identifier(s) => {
|
||||||
let index = state.access_var(&s, settings.pos);
|
let index = state.access_var(&s, settings.pos);
|
||||||
Expr::Variable(Box::new(((s, settings.pos), None, 0, index)))
|
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, index)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function call is allowed to have reserved keyword
|
// Function call is allowed to have reserved keyword
|
||||||
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
|
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
|
||||||
if is_keyword_function(&s) {
|
if is_keyword_function(&s) {
|
||||||
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None)))
|
||||||
} else {
|
} else {
|
||||||
return Err(PERR::Reserved(s).into_err(settings.pos));
|
return Err(PERR::Reserved(s).into_err(settings.pos));
|
||||||
}
|
}
|
||||||
@ -1984,7 +2000,7 @@ fn parse_primary(
|
|||||||
.into_err(settings.pos),
|
.into_err(settings.pos),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new((Ident::new(s, settings.pos), None, 0, None)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2040,13 +2056,13 @@ fn parse_primary(
|
|||||||
.into_err(pos));
|
.into_err(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ((name, pos), modules, _, _) = *x;
|
let (Ident { name, pos }, modules, _, _) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
|
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
|
||||||
}
|
}
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(x), Token::LeftParen) => {
|
(Expr::Variable(x), Token::LeftParen) => {
|
||||||
let ((name, pos), modules, _, _) = *x;
|
let (Ident { name, pos }, modules, _, _) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
|
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
|
||||||
}
|
}
|
||||||
@ -2054,7 +2070,7 @@ fn parse_primary(
|
|||||||
// module access
|
// module access
|
||||||
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
||||||
(Token::Identifier(id2), pos2) => {
|
(Token::Identifier(id2), pos2) => {
|
||||||
let ((name, pos), mut modules, _, index) = *x;
|
let (Ident { name, pos }, mut modules, _, index) = *x;
|
||||||
|
|
||||||
if let Some(ref mut modules) = modules {
|
if let Some(ref mut modules) = modules {
|
||||||
modules.push((name, pos));
|
modules.push((name, pos));
|
||||||
@ -2064,7 +2080,7 @@ fn parse_primary(
|
|||||||
modules = Some(Box::new(m));
|
modules = Some(Box::new(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Variable(Box::new(((id2, pos2), modules, 0, index)))
|
Expr::Variable(Box::new((Ident::new(id2, pos2), modules, 0, index)))
|
||||||
}
|
}
|
||||||
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
|
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
|
||||||
return Err(PERR::Reserved(id2).into_err(pos2));
|
return Err(PERR::Reserved(id2).into_err(pos2));
|
||||||
@ -2088,7 +2104,7 @@ fn parse_primary(
|
|||||||
match &mut root_expr {
|
match &mut root_expr {
|
||||||
// Cache the hash key for module-qualified variables
|
// Cache the hash key for module-qualified variables
|
||||||
Expr::Variable(x) if x.1.is_some() => {
|
Expr::Variable(x) if x.1.is_some() => {
|
||||||
let ((name, _), modules, hash, _) = x.as_mut();
|
let (Ident { name, .. }, modules, hash, _) = x.as_mut();
|
||||||
let modules = modules.as_mut().unwrap();
|
let modules = modules.as_mut().unwrap();
|
||||||
|
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
@ -2249,7 +2265,15 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
// var (indexed) = rhs
|
// var (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let ((name, name_pos), _, _, index) = x.as_ref();
|
let (
|
||||||
|
Ident {
|
||||||
|
name,
|
||||||
|
pos: name_pos,
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
index,
|
||||||
|
) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
||||||
@ -2268,7 +2292,15 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let ((name, name_pos), _, _, index) = x.as_ref();
|
let (
|
||||||
|
Ident {
|
||||||
|
name,
|
||||||
|
pos: name_pos,
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
index,
|
||||||
|
) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
||||||
@ -2344,7 +2376,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
}
|
}
|
||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
||||||
let (name, pos) = x.0;
|
let Ident { name, pos } = x.0;
|
||||||
|
|
||||||
let getter = make_getter(&name);
|
let getter = make_getter(&name);
|
||||||
let setter = make_setter(&name);
|
let setter = make_setter(&name);
|
||||||
@ -2758,7 +2790,12 @@ fn parse_custom_syntax(
|
|||||||
MARKER_IDENT => match input.next().unwrap() {
|
MARKER_IDENT => match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => {
|
(Token::Identifier(s), pos) => {
|
||||||
segments.push(s.clone());
|
segments.push(s.clone());
|
||||||
exprs.push(Expr::Variable(Box::new(((s, pos), None, 0, None))));
|
exprs.push(Expr::Variable(Box::new((
|
||||||
|
Ident::new(s, pos),
|
||||||
|
None,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
))));
|
||||||
}
|
}
|
||||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
return Err(PERR::Reserved(s).into_err(pos));
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
@ -3051,12 +3088,20 @@ fn parse_let(
|
|||||||
// let name = expr
|
// let name = expr
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
state.stack.push((name.clone(), ScopeEntryType::Normal));
|
state.stack.push((name.clone(), ScopeEntryType::Normal));
|
||||||
Ok(Stmt::Let(Box::new((name, pos)), init_value, token_pos))
|
Ok(Stmt::Let(
|
||||||
|
Box::new(Ident::new(name, pos)),
|
||||||
|
init_value,
|
||||||
|
token_pos,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
// const name = { expr:constant }
|
// const name = { expr:constant }
|
||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => {
|
||||||
state.stack.push((name.clone(), ScopeEntryType::Constant));
|
state.stack.push((name.clone(), ScopeEntryType::Constant));
|
||||||
Ok(Stmt::Const(Box::new((name, pos)), init_value, token_pos))
|
Ok(Stmt::Const(
|
||||||
|
Box::new(Ident::new(name, pos)),
|
||||||
|
init_value,
|
||||||
|
token_pos,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3098,7 +3143,7 @@ fn parse_import(
|
|||||||
|
|
||||||
Ok(Stmt::Import(
|
Ok(Stmt::Import(
|
||||||
expr,
|
expr,
|
||||||
Some(Box::new((name.into(), settings.pos))),
|
Some(Box::new(IdentX::new(name, settings.pos))),
|
||||||
token_pos,
|
token_pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -3131,7 +3176,7 @@ fn parse_export(
|
|||||||
|
|
||||||
let rename = if match_token(input, Token::As).0 {
|
let rename = if match_token(input, Token::As).0 {
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => Some((s.clone(), pos)),
|
(Token::Identifier(s), pos) => Some(Ident::new(s.clone(), pos)),
|
||||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
return Err(PERR::Reserved(s).into_err(pos));
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
}
|
}
|
||||||
@ -3142,7 +3187,7 @@ fn parse_export(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.push(((id, id_pos), rename));
|
exports.push((Ident::new(id, id_pos), rename));
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
@ -3163,12 +3208,12 @@ fn parse_export(
|
|||||||
exports
|
exports
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.try_for_each(|(i, ((id1, _), _))| {
|
.try_for_each(|(i, (Ident { name: id1, .. }, _))| {
|
||||||
exports
|
exports
|
||||||
.iter()
|
.iter()
|
||||||
.skip(i + 1)
|
.skip(i + 1)
|
||||||
.find(|((id2, _), _)| id2 == id1)
|
.find(|(Ident { name: id2, .. }, _)| id2 == id1)
|
||||||
.map_or_else(|| Ok(()), |((id2, pos), _)| Err((id2, *pos)))
|
.map_or_else(|| Ok(()), |(Ident { name: id2, pos }, _)| Err((id2, *pos)))
|
||||||
})
|
})
|
||||||
.map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?;
|
.map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?;
|
||||||
|
|
||||||
@ -3451,7 +3496,7 @@ fn parse_try_catch(
|
|||||||
// try { body } catch (
|
// try { body } catch (
|
||||||
let var_def = if match_token(input, Token::LeftParen).0 {
|
let var_def = if match_token(input, Token::LeftParen).0 {
|
||||||
let id = match input.next().unwrap() {
|
let id = match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => (s, pos),
|
(Token::Identifier(s), pos) => Ident::new(s, pos),
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3474,9 +3519,10 @@ fn parse_try_catch(
|
|||||||
let catch_body = parse_block(input, state, lib, settings.level_up())?;
|
let catch_body = parse_block(input, state, lib, settings.level_up())?;
|
||||||
|
|
||||||
Ok(Stmt::TryCatch(Box::new((
|
Ok(Stmt::TryCatch(Box::new((
|
||||||
(body, token_pos),
|
body,
|
||||||
var_def,
|
var_def,
|
||||||
(catch_body, catch_pos),
|
catch_body,
|
||||||
|
(token_pos, catch_pos),
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3588,11 +3634,7 @@ fn parse_fn(
|
|||||||
|
|
||||||
/// Creates a curried expression from a list of external variables
|
/// Creates a curried expression from a list of external variables
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
fn make_curry_from_externals(
|
fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Position) -> Expr {
|
||||||
fn_expr: Expr,
|
|
||||||
externals: StaticVec<(String, Position)>,
|
|
||||||
pos: Position,
|
|
||||||
) -> Expr {
|
|
||||||
if externals.is_empty() {
|
if externals.is_empty() {
|
||||||
return fn_expr;
|
return fn_expr;
|
||||||
}
|
}
|
||||||
@ -3603,13 +3645,8 @@ fn make_curry_from_externals(
|
|||||||
args.push(fn_expr);
|
args.push(fn_expr);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals.iter().for_each(|(var_name, pos)| {
|
externals.iter().for_each(|x| {
|
||||||
args.push(Expr::Variable(Box::new((
|
args.push(Expr::Variable(Box::new((x.clone(), None, 0, None))));
|
||||||
(var_name.into(), *pos),
|
|
||||||
None,
|
|
||||||
0,
|
|
||||||
None,
|
|
||||||
))));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -3634,11 +3671,7 @@ fn make_curry_from_externals(
|
|||||||
// Statement block
|
// Statement block
|
||||||
let mut statements: Vec<_> = Default::default();
|
let mut statements: Vec<_> = Default::default();
|
||||||
// Insert `Share` statements
|
// Insert `Share` statements
|
||||||
statements.extend(
|
statements.extend(externals.into_iter().map(Stmt::Share));
|
||||||
externals
|
|
||||||
.into_iter()
|
|
||||||
.map(|(var_name, pos)| Stmt::Share(var_name, pos)),
|
|
||||||
);
|
|
||||||
// Final expression
|
// Final expression
|
||||||
statements.push(Stmt::Expr(expr));
|
statements.push(Stmt::Expr(expr));
|
||||||
Expr::Stmt(Box::new((Stmt::Block(statements, pos), pos)))
|
Expr::Stmt(Box::new((Stmt::Block(statements, pos), pos)))
|
||||||
@ -3723,7 +3756,7 @@ fn parse_anon_fn(
|
|||||||
state
|
state
|
||||||
.externals
|
.externals
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, &v)| (k.clone(), v))
|
.map(|(k, &v)| Ident::new(k.clone(), v))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -3733,8 +3766,7 @@ fn parse_anon_fn(
|
|||||||
let params: StaticVec<_> = if cfg!(not(feature = "no_closure")) {
|
let params: StaticVec<_> = if cfg!(not(feature = "no_closure")) {
|
||||||
externals
|
externals
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, _)| k)
|
.map(|k| k.name.clone())
|
||||||
.cloned()
|
|
||||||
.chain(params.into_iter().map(|(v, _)| v))
|
.chain(params.into_iter().map(|(v, _)| v))
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
@ -3767,7 +3799,7 @@ fn parse_anon_fn(
|
|||||||
lib: None,
|
lib: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let expr = Expr::FnPointer(Box::new((fn_name, settings.pos)));
|
let expr = Expr::FnPointer(Box::new(IdentX::new(fn_name, settings.pos)));
|
||||||
|
|
||||||
let expr = if cfg!(not(feature = "no_closure")) {
|
let expr = if cfg!(not(feature = "no_closure")) {
|
||||||
make_curry_from_externals(expr, externals, settings.pos)
|
make_curry_from_externals(expr, externals, settings.pos)
|
||||||
@ -3920,7 +3952,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
|||||||
Union::Unit(_) => Some(Expr::Unit(pos)),
|
Union::Unit(_) => Some(Expr::Unit(pos)),
|
||||||
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))),
|
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))),
|
||||||
Union::Char(value) => Some(Expr::CharConstant(Box::new((value, pos)))),
|
Union::Char(value) => Some(Expr::CharConstant(Box::new((value, pos)))),
|
||||||
Union::Str(value) => Some(Expr::StringConstant(Box::new((value, pos)))),
|
Union::Str(value) => Some(Expr::StringConstant(Box::new(IdentX::new(value, pos)))),
|
||||||
Union::Bool(true) => Some(Expr::True(pos)),
|
Union::Bool(true) => Some(Expr::True(pos)),
|
||||||
Union::Bool(false) => Some(Expr::False(pos)),
|
Union::Bool(false) => Some(Expr::False(pos)),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -3943,14 +3975,14 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
|||||||
Union::Map(map) => {
|
Union::Map(map) => {
|
||||||
let items: Vec<_> = map
|
let items: Vec<_> = map
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| ((k, pos), map_dynamic_to_expr(v, pos)))
|
.map(|(k, v)| (IdentX::new(k, pos), map_dynamic_to_expr(v, pos)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if items.iter().all(|(_, expr)| expr.is_some()) {
|
if items.iter().all(|(_, expr)| expr.is_some()) {
|
||||||
Some(Expr::Map(Box::new((
|
Some(Expr::Map(Box::new((
|
||||||
items
|
items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|((k, pos), expr)| ((k, pos), expr.unwrap()))
|
.map(|(k, expr)| (k, expr.unwrap()))
|
||||||
.collect(),
|
.collect(),
|
||||||
pos,
|
pos,
|
||||||
))))
|
))))
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
//! A helper module containing unsafe utility functions.
|
//! A helper module containing unsafe utility functions.
|
||||||
|
|
||||||
use crate::any::Variant;
|
use crate::any::Variant;
|
||||||
use crate::engine::State;
|
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
borrow::Cow,
|
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
mem, ptr,
|
mem, ptr,
|
||||||
string::ToString,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Cast a type into another type.
|
/// Cast a type into another type.
|
||||||
@ -46,7 +43,7 @@ pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, B
|
|||||||
|
|
||||||
/// # DANGEROUS!!!
|
/// # DANGEROUS!!!
|
||||||
///
|
///
|
||||||
/// A dangerous function that blindly casts a `&str` from one lifetime to a `Cow<str>` of
|
/// A dangerous function that blindly casts a `&str` from one lifetime to a `&str` of
|
||||||
/// another lifetime. This is mainly used to let us push a block-local variable into the
|
/// another lifetime. This is mainly used to let us push a block-local variable into the
|
||||||
/// current `Scope` without cloning the variable name. Doing this is safe because all local
|
/// current `Scope` without cloning the variable name. Doing this is safe because all local
|
||||||
/// variables in the `Scope` are cleared out before existing the block.
|
/// variables in the `Scope` are cleared out before existing the block.
|
||||||
@ -54,15 +51,8 @@ pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, B
|
|||||||
/// Force-casting a local variable's lifetime to the current `Scope`'s larger lifetime saves
|
/// Force-casting a local variable's lifetime to the current `Scope`'s larger lifetime saves
|
||||||
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str {
|
||||||
// If not at global level, we can force-cast
|
|
||||||
if state.scope_level > 0 {
|
|
||||||
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
||||||
// this is safe because all local variables are cleared at the end of the block
|
// this is safe because all local variables are cleared at the end of the block
|
||||||
unsafe { mem::transmute::<_, &'s str>(name) }.into()
|
unsafe { mem::transmute(name) }
|
||||||
} else {
|
|
||||||
// The variable is introduced at global (top) level and may persist after the script run.
|
|
||||||
// Therefore, clone the variable name.
|
|
||||||
name.to_string().into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user