Refine data structures

This commit is contained in:
Stephen Chung 2022-03-05 17:57:23 +08:00
parent e06c2b2abb
commit 8bda8c64df
15 changed files with 235 additions and 219 deletions

View File

@ -72,8 +72,8 @@ impl Expression<'_> {
pub fn get_string_value(&self) -> Option<&str> { pub fn get_string_value(&self) -> Option<&str> {
match self.0 { match self.0 {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Expr::Variable(.., x) if x.1.is_some() => None, Expr::Variable(x, ..) if !x.1.is_empty() => None,
Expr::Variable(.., x) => Some(x.2.as_str()), Expr::Variable(x, ..) => Some(x.3.as_str()),
Expr::StringConstant(x, ..) => Some(x.as_str()), Expr::StringConstant(x, ..) => Some(x.as_str()),
_ => None, _ => None,
} }
@ -102,8 +102,8 @@ impl Expression<'_> {
Expr::CharConstant(x, ..) => reify!(*x => Option<T>), Expr::CharConstant(x, ..) => reify!(*x => Option<T>),
Expr::StringConstant(x, ..) => reify!(x.clone() => Option<T>), Expr::StringConstant(x, ..) => reify!(x.clone() => Option<T>),
Expr::Variable(.., x) => { Expr::Variable(x, ..) => {
let x: ImmutableString = x.2.clone().into(); let x: ImmutableString = x.3.clone().into();
reify!(x => Option<T>) reify!(x => Option<T>)
} }
Expr::BoolConstant(x, ..) => reify!(*x => Option<T>), Expr::BoolConstant(x, ..) => reify!(*x => Option<T>),

View File

@ -169,7 +169,7 @@ impl FnCallHashes {
pub struct FnCallExpr { pub struct FnCallExpr {
/// Namespace of the function, if any. /// Namespace of the function, if any.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub namespace: Option<crate::module::Namespace>, pub namespace: super::Namespace,
/// Function name. /// Function name.
pub name: Identifier, pub name: Identifier,
/// Pre-calculated hashes. /// Pre-calculated hashes.
@ -186,8 +186,8 @@ impl fmt::Debug for FnCallExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ff = f.debug_struct("FnCallExpr"); let mut ff = f.debug_struct("FnCallExpr");
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if let Some(ref ns) = self.namespace { if !self.namespace.is_empty() {
ff.field("namespace", ns); ff.field("namespace", &self.namespace);
} }
if self.capture_parent_scope { if self.capture_parent_scope {
ff.field("capture_parent_scope", &self.capture_parent_scope); ff.field("capture_parent_scope", &self.capture_parent_scope);
@ -206,9 +206,9 @@ impl FnCallExpr {
/// Always `false` under `no_module`. /// Always `false` under `no_module`.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_qualified(&self) -> bool { pub fn is_qualified(&self) -> bool {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
return self.namespace.is_some(); return !self.namespace.is_empty();
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
return false; return false;
} }
@ -372,21 +372,17 @@ pub enum Expr {
), ),
/// () /// ()
Unit(Position), Unit(Position),
/// Variable access - optional short index, position, (optional index, optional (hash, modules), variable name) /// Variable access - (optional long index, namespace, namespace hash, variable name), optional short index, position
/// ///
/// The short index is [`u8`] which is used when the index is <= 255, which should be the vast /// The short index is [`u8`] which is used when the index is <= 255, which should be the vast
/// majority of cases (unless there are more than 255 variables defined!). /// majority of cases (unless there are more than 255 variables defined!).
/// This is to avoid reading a pointer redirection during each variable access. /// This is to avoid reading a pointer redirection during each variable access.
Variable( Variable(
Option<NonZeroU8>,
Position,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Box<( Box<(Option<NonZeroUsize>, super::Namespace, u64, Identifier)>,
Option<NonZeroUsize>, Option<NonZeroU8>,
Option<(crate::module::Namespace, u64)>,
Identifier,
)>,
#[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, (), Identifier)>, #[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, (), Identifier)>,
Position,
), ),
/// Property access - ((getter, hash), (setter, hash), prop) /// Property access - ((getter, hash), (setter, hash), prop)
Property( Property(
@ -451,18 +447,18 @@ impl fmt::Debug for Expr {
.entries(x.0.iter().map(|(k, v)| (k, v))) .entries(x.0.iter().map(|(k, v)| (k, v)))
.finish() .finish()
} }
Self::Variable(i, _, x) => { Self::Variable(x, i, ..) => {
f.write_str("Variable(")?; f.write_str("Variable(")?;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if let Some((ref namespace, ..)) = x.1 { if !x.1.is_empty() {
write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())?; write!(f, "{}{}", x.1, Token::DoubleColon.literal_syntax())?;
let pos = namespace.position(); let pos = x.1.position();
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {:?}", pos); display_pos = format!(" @ {:?}", pos);
} }
} }
f.write_str(&x.2)?; f.write_str(&x.3)?;
if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) { if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
write!(f, " #{}", n)?; write!(f, " #{}", n)?;
} }
@ -621,7 +617,7 @@ impl Expr {
Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall( Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
FnCallExpr { FnCallExpr {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace: None, namespace: super::Namespace::NONE,
name: KEYWORD_FN_PTR.into(), name: KEYWORD_FN_PTR.into(),
hashes: calc_fn_hash(f.fn_name(), 1).into(), hashes: calc_fn_hash(f.fn_name(), 1).into(),
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
@ -640,12 +636,12 @@ impl Expr {
/// `non_qualified` is ignored under `no_module`. /// `non_qualified` is ignored under `no_module`.
#[inline] #[inline]
#[must_use] #[must_use]
pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool { pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool {
let _non_qualified = non_qualified; let _non_qualified = non_qualified;
match self { match self {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Variable(.., x) if _non_qualified && x.1.is_some() => false, Self::Variable(x, ..) if _non_qualified && !x.1.is_empty() => false,
Self::Variable(..) => true, Self::Variable(..) => true,
_ => false, _ => false,
} }
@ -660,8 +656,8 @@ impl Expr {
match self { match self {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Variable(.., x) if _non_qualified && x.1.is_some() => None, Self::Variable(x, ..) if _non_qualified && !x.1.is_empty() => None,
Self::Variable(.., x) => Some(x.2.as_str()), Self::Variable(x, ..) => Some(x.3.as_str()),
_ => None, _ => None,
} }
} }
@ -681,7 +677,7 @@ impl Expr {
| Self::StringConstant(.., pos) | Self::StringConstant(.., pos)
| Self::Array(.., pos) | Self::Array(.., pos)
| Self::Map(.., pos) | Self::Map(.., pos)
| Self::Variable(.., pos, _) | Self::Variable(.., pos)
| Self::And(.., pos) | Self::And(.., pos)
| Self::Or(.., pos) | Self::Or(.., pos)
| Self::Index(.., pos) | Self::Index(.., pos)
@ -702,9 +698,9 @@ impl Expr {
pub fn start_position(&self) -> Position { pub fn start_position(&self) -> Position {
match self { match self {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Variable(.., x) => { Self::Variable(x, ..) => {
if let Some((ref namespace, ..)) = x.1 { if !x.1.is_empty() {
namespace.position() x.1.position()
} else { } else {
self.position() self.position()
} }
@ -735,7 +731,7 @@ impl Expr {
| Self::Or(.., pos) | Self::Or(.., pos)
| Self::Dot(.., pos) | Self::Dot(.., pos)
| Self::Index(.., pos) | Self::Index(.., pos)
| Self::Variable(.., pos, _) | Self::Variable(.., pos)
| Self::FnCall(.., pos) | Self::FnCall(.., pos)
| Self::MethodCall(.., pos) | Self::MethodCall(.., pos)
| Self::Custom(.., pos) | Self::Custom(.., pos)

View File

@ -3,7 +3,11 @@
use crate::{Identifier, Position}; use crate::{Identifier, Position};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{fmt, hash::Hash}; use std::{
fmt,
hash::Hash,
ops::{Deref, DerefMut},
};
/// _(internals)_ An identifier containing a name and a [position][Position]. /// _(internals)_ An identifier containing a name and a [position][Position].
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
@ -29,7 +33,30 @@ impl AsRef<str> for Ident {
} }
} }
impl Deref for Ident {
type Target = Identifier;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.name
}
}
impl DerefMut for Ident {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.name
}
}
impl Ident { impl Ident {
/// An empty [`Ident`].
pub const EMPTY: Self = Self {
name: Identifier::new_const(),
pos: Position::NONE,
};
/// Get the name of the identifier as a string slice.
#[inline(always)] #[inline(always)]
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
self.name.as_str() self.name.as_str()

View File

@ -4,6 +4,7 @@ pub mod ast;
pub mod expr; pub mod expr;
pub mod flags; pub mod flags;
pub mod ident; pub mod ident;
pub mod namespace;
pub mod script_fn; pub mod script_fn;
pub mod stmt; pub mod stmt;
@ -12,6 +13,8 @@ pub use expr::{BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes};
pub use flags::{ASTFlags, FnAccess}; pub use flags::{ASTFlags, FnAccess};
pub use ident::Ident; pub use ident::Ident;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub use namespace::Namespace;
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use script_fn::EncapsulatedEnviron; pub use script_fn::EncapsulatedEnviron;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]

View File

@ -30,6 +30,10 @@ pub struct Namespace {
impl fmt::Debug for Namespace { impl fmt::Debug for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
return f.write_str("NONE");
}
if let Some(index) = self.index { if let Some(index) = self.index {
write!(f, "{} -> ", index)?; write!(f, "{} -> ", index)?;
} }
@ -38,7 +42,7 @@ impl fmt::Debug for Namespace {
&self &self
.path .path
.iter() .iter()
.map(|Ident { name, .. }| name.as_str()) .map(|m| m.as_str())
.collect::<StaticVec<_>>() .collect::<StaticVec<_>>()
.join(Token::DoubleColon.literal_syntax()), .join(Token::DoubleColon.literal_syntax()),
) )
@ -47,11 +51,15 @@ impl fmt::Debug for Namespace {
impl fmt::Display for Namespace { impl fmt::Display for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
return Ok(());
}
f.write_str( f.write_str(
&self &self
.path .path
.iter() .iter()
.map(|Ident { name, .. }| name.as_str()) .map(|m| m.as_str())
.collect::<StaticVec<_>>() .collect::<StaticVec<_>>()
.join(Token::DoubleColon.literal_syntax()), .join(Token::DoubleColon.literal_syntax()),
) )
@ -94,14 +102,20 @@ impl From<StaticVec<Ident>> for Namespace {
} }
impl Namespace { impl Namespace {
/// Constant for no namespace.
pub const NONE: Self = Self {
index: None,
path: StaticVec::new_const(),
};
/// Create a new [`Namespace`]. /// Create a new [`Namespace`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn new() -> Self { pub fn new(root: impl Into<Ident>) -> Self {
Self { let mut path = StaticVec::new_const();
index: None, path.push(root.into());
path: StaticVec::new_const(),
} Self { index: None, path }
} }
/// Get the [`Scope`][crate::Scope] index offset. /// Get the [`Scope`][crate::Scope] index offset.
#[inline(always)] #[inline(always)]

View File

@ -127,7 +127,7 @@ pub struct TryCatchBlock {
/// `try` block. /// `try` block.
pub try_block: StmtBlock, pub try_block: StmtBlock,
/// `catch` variable, if any. /// `catch` variable, if any.
pub catch_var: Option<Ident>, pub catch_var: Ident,
/// `catch` block. /// `catch` block.
pub catch_block: StmtBlock, pub catch_block: StmtBlock,
} }
@ -346,7 +346,7 @@ pub enum Stmt {
/// * [`NEGATED`][ASTFlags::NEGATED] = `until` /// * [`NEGATED`][ASTFlags::NEGATED] = `until`
Do(Box<(Expr, StmtBlock)>, ASTFlags, Position), Do(Box<(Expr, StmtBlock)>, ASTFlags, Position),
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}` /// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
For(Box<(Ident, Option<Ident>, Expr, StmtBlock)>, Position), For(Box<(Ident, Ident, Expr, StmtBlock)>, Position),
/// \[`export`\] `let`|`const` id `=` expr /// \[`export`\] `let`|`const` id `=` expr
/// ///
/// ### Flags /// ### Flags
@ -385,7 +385,7 @@ pub enum Stmt {
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Import(Box<(Expr, Option<Ident>)>, Position), Import(Box<(Expr, Ident)>, Position),
/// `export` var `as` alias /// `export` var `as` alias
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.

View File

@ -620,7 +620,7 @@ impl Engine {
match lhs { match lhs {
// id.??? or id[???] // id.??? or id[???]
Expr::Variable(.., var_pos, x) => { Expr::Variable(x, .., var_pos) => {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?; self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?;
@ -631,7 +631,7 @@ impl Engine {
self.search_namespace(scope, global, state, lib, this_ptr, lhs, level)?; self.search_namespace(scope, global, state, lib, this_ptr, lhs, level)?;
let obj_ptr = &mut target; let obj_ptr = &mut target;
let root = (x.2.as_str(), *var_pos); let root = (x.3.as_str(), *var_pos);
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
global, state, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values, global, state, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values,

View File

@ -18,8 +18,10 @@ impl Engine {
&self, &self,
global: &GlobalRuntimeState, global: &GlobalRuntimeState,
state: &mut EvalState, state: &mut EvalState,
namespace: &crate::module::Namespace, namespace: &crate::ast::Namespace,
) -> Option<crate::Shared<Module>> { ) -> Option<crate::Shared<Module>> {
assert!(!namespace.is_empty());
let root = namespace.root(); let root = namespace.root();
// Qualified - check if the root module is directly indexed // Qualified - check if the root module is directly indexed
@ -53,23 +55,23 @@ impl Engine {
level: usize, level: usize,
) -> RhaiResultOf<(Target<'s>, Position)> { ) -> RhaiResultOf<(Target<'s>, Position)> {
match expr { match expr {
Expr::Variable(Some(_), ..) => { Expr::Variable(_, Some(_), _) => {
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
} }
Expr::Variable(None, _var_pos, v) => match v.as_ref() { Expr::Variable(v, None, _var_pos) => match v.as_ref() {
// Normal variable access // Normal variable access
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(_, None, _) => { (_, ns, ..) if ns.is_empty() => {
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
} }
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
(_, (), _) => { (_, (), ..) => {
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
} }
// Qualified variable access // Qualified variable access
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(_, Some((namespace, hash_var)), var_name) => { (_, namespace, hash_var, var_name) => {
// foo:bar::baz::VARIABLE // foo:bar::baz::VARIABLE
if let Some(module) = self.search_imports(global, state, namespace) { if let Some(module) = self.search_imports(global, state, namespace) {
return if let Some(mut target) = module.get_qualified_var(*hash_var) { return if let Some(mut target) = module.get_qualified_var(*hash_var) {
@ -139,7 +141,7 @@ impl Engine {
let (index, var_pos) = match expr { let (index, var_pos) = match expr {
// Check if the variable is `this` // Check if the variable is `this`
Expr::Variable(None, pos, v) if v.0.is_none() && v.2 == KEYWORD_THIS => { Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => {
return if let Some(val) = this_ptr { return if let Some(val) = this_ptr {
Ok(((*val).into(), *pos)) Ok(((*val).into(), *pos))
} else { } else {
@ -147,8 +149,8 @@ impl Engine {
} }
} }
_ if state.always_search_scope => (0, expr.start_position()), _ if state.always_search_scope => (0, expr.start_position()),
Expr::Variable(Some(i), pos, ..) => (i.get() as usize, *pos), Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos),
Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), Expr::Variable(v, None, pos) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
_ => unreachable!("Expr::Variable expected but gets {:?}", expr), _ => unreachable!("Expr::Variable expected but gets {:?}", expr),
}; };
@ -216,7 +218,7 @@ impl Engine {
} = expr; } = expr;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if let Some(namespace) = namespace.as_ref() { if !namespace.is_empty() {
// Qualified function call // Qualified function call
let hash = hashes.native; let hash = hashes.native;
@ -280,14 +282,14 @@ impl Engine {
// Then variable access. // Then variable access.
// We shouldn't do this for too many variants because, soon or later, the added comparisons // We shouldn't do this for too many variants because, soon or later, the added comparisons
// will cost more than the mis-predicted `match` branch. // will cost more than the mis-predicted `match` branch.
if let Expr::Variable(index, var_pos, x) = expr { if let Expr::Variable(x, index, var_pos) = expr {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?; self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, expr.position())?; self.inc_operations(&mut global.num_operations, expr.position())?;
return if index.is_none() && x.0.is_none() && x.2 == KEYWORD_THIS { return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
this_ptr this_ptr
.as_deref() .as_deref()
.cloned() .cloned()
@ -396,8 +398,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
let mut sizes = (0, 0, 0); let mut sizes = (0, 0, 0);
for (crate::ast::Ident { name, .. }, value_expr) in x.0.iter() { for (key, value_expr) in x.0.iter() {
let key = name.as_str();
let value = match self let value = match self
.eval_expr(scope, global, state, lib, this_ptr, value_expr, level) .eval_expr(scope, global, state, lib, this_ptr, value_expr, level)
{ {
@ -411,7 +412,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
let delta = Self::calc_data_sizes(&value, true); let delta = Self::calc_data_sizes(&value, true);
*map.get_mut(key).unwrap() = value; *map.get_mut(key.as_str()).unwrap() = value;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() { if self.has_data_size_limit() {

View File

@ -569,7 +569,7 @@ impl Engine {
// For loop // For loop
Stmt::For(x, ..) => { Stmt::For(x, ..) => {
let (Ident { name: var_name, .. }, counter, expr, statements) = x.as_ref(); let (var_name, counter, expr, statements) = x.as_ref();
let iter_result = self let iter_result = self
.eval_expr(scope, global, state, lib, this_ptr, expr, level) .eval_expr(scope, global, state, lib, this_ptr, expr, level)
@ -600,14 +600,14 @@ impl Engine {
if let Some(func) = func { if let Some(func) = func {
// Add the loop variables // Add the loop variables
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();
let counter_index = if let Some(counter) = counter { let counter_index = if !counter.is_empty() {
scope.push(counter.name.clone(), 0 as INT); scope.push(counter.name.clone(), 0 as INT);
scope.len() - 1 scope.len() - 1
} else { } else {
usize::MAX usize::MAX
}; };
scope.push(var_name.clone(), ()); scope.push(var_name.name.clone(), ());
let index = scope.len() - 1; let index = scope.len() - 1;
let mut loop_result = Ok(Dynamic::UNIT); let mut loop_result = Ok(Dynamic::UNIT);
@ -619,7 +619,7 @@ impl Engine {
if x > INT::MAX as usize { if x > INT::MAX as usize {
loop_result = Err(ERR::ErrorArithmetic( loop_result = Err(ERR::ErrorArithmetic(
format!("for-loop counter overflow: {}", x), format!("for-loop counter overflow: {}", x),
counter.as_ref().expect("`Some`").pos, counter.pos,
) )
.into()); .into());
break; break;
@ -707,7 +707,10 @@ impl Engine {
Stmt::TryCatch(x, ..) => { Stmt::TryCatch(x, ..) => {
let TryCatchBlock { let TryCatchBlock {
try_block, try_block,
catch_var, catch_var:
Ident {
name: catch_var, ..
},
catch_block, catch_block,
} = x.as_ref(); } = x.as_ref();
@ -757,9 +760,9 @@ impl Engine {
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();
catch_var if !catch_var.is_empty() {
.as_ref() scope.push(catch_var.clone(), err_value);
.map(|Ident { name, .. }| scope.push(name.clone(), err_value)); }
let result = self.eval_stmt_block( let result = self.eval_stmt_block(
scope, scope,
@ -808,12 +811,12 @@ impl Engine {
Stmt::Return(None, .., pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()), Stmt::Return(None, .., pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()),
// Let/const statement - shadowing disallowed // Let/const statement - shadowing disallowed
Stmt::Var(x, .., pos) if !self.allow_shadowing() && scope.contains(&x.0.name) => { Stmt::Var(x, .., pos) if !self.allow_shadowing() && scope.contains(&x.0) => {
Err(ERR::ErrorVariableExists(x.0.name.to_string(), *pos).into()) Err(ERR::ErrorVariableExists(x.0.to_string(), *pos).into())
} }
// Let/const statement // Let/const statement
Stmt::Var(x, options, pos) => { Stmt::Var(x, options, pos) => {
let (Ident { name: var_name, .. }, expr, index) = x.as_ref(); let (var_name, expr, index) = x.as_ref();
let access = if options.contains(ASTFlags::CONSTANT) { let access = if options.contains(ASTFlags::CONSTANT) {
AccessMode::ReadOnly AccessMode::ReadOnly
@ -879,7 +882,7 @@ impl Engine {
)); ));
} }
crate::func::locked_write(global.constants.as_ref().unwrap()) crate::func::locked_write(global.constants.as_ref().unwrap())
.insert(var_name.clone(), value.clone()); .insert(var_name.name.clone(), value.clone());
} }
if export { if export {
@ -897,12 +900,12 @@ impl Engine {
value.set_access_mode(access); value.set_access_mode(access);
*scope.get_mut_by_index(scope.len() - index.get()) = value; *scope.get_mut_by_index(scope.len() - index.get()) = value;
} else { } else {
scope.push_entry(var_name.clone(), access, value); scope.push_entry(var_name.name.clone(), access, value);
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if let Some(alias) = _alias { if let Some(alias) = _alias {
scope.add_entry_alias(scope.len() - 1, alias.clone()); scope.add_entry_alias(scope.len() - 1, alias.name.clone());
} }
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
@ -958,14 +961,14 @@ impl Engine {
}); });
if let Ok(module) = module_result { if let Ok(module) = module_result {
if let Some(name) = export.as_ref().map(|x| x.name.clone()) { if !export.is_empty() {
if !module.is_indexed() { if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed // Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::func::native::shared_take_or_clone(module); let mut m = crate::func::native::shared_take_or_clone(module);
module.build_index(); m.build_index();
global.push_import(name, module); global.push_import(export.name.clone(), m);
} else { } else {
global.push_import(name, module); global.push_import(export.name.clone(), module);
} }
} }
@ -983,7 +986,7 @@ impl Engine {
// Export statement // Export statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Export(x, ..) => { Stmt::Export(x, ..) => {
let (Ident { name, pos, .. }, Ident { name: alias, .. }) = x.as_ref(); let (Ident { name, pos, .. }, alias) = x.as_ref();
// Mark scope variables as public // Mark scope variables as public
if let Some((index, ..)) = scope.get_index(name) { if let Some((index, ..)) = scope.get_index(name) {
scope.add_entry_alias( scope.add_entry_alias(

View File

@ -151,14 +151,14 @@ impl Engine {
#[must_use] #[must_use]
fn gen_call_signature( fn gen_call_signature(
&self, &self,
#[cfg(not(feature = "no_module"))] namespace: Option<&crate::module::Namespace>, #[cfg(not(feature = "no_module"))] namespace: &crate::ast::Namespace,
fn_name: &str, fn_name: &str,
args: &[&mut Dynamic], args: &[&mut Dynamic],
) -> String { ) -> String {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let (ns, sep) = ( let (ns, sep) = (
namespace.map_or_else(|| String::new(), |ns| ns.to_string()), namespace.to_string(),
if namespace.is_some() { if !namespace.is_empty() {
crate::tokenizer::Token::DoubleColon.literal_syntax() crate::tokenizer::Token::DoubleColon.literal_syntax()
} else { } else {
"" ""
@ -576,7 +576,7 @@ impl Engine {
_ => Err(ERR::ErrorFunctionNotFound( _ => Err(ERR::ErrorFunctionNotFound(
self.gen_call_signature( self.gen_call_signature(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
None, &crate::ast::Namespace::NONE,
name, name,
args, args,
), ),
@ -1277,7 +1277,7 @@ impl Engine {
state: &mut EvalState, state: &mut EvalState,
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
namespace: &crate::module::Namespace, namespace: &crate::ast::Namespace,
fn_name: &str, fn_name: &str,
args_expr: &[Expr], args_expr: &[Expr],
hash: u64, hash: u64,
@ -1406,7 +1406,7 @@ impl Engine {
Some(f) => unreachable!("unknown function type: {:?}", f), Some(f) => unreachable!("unknown function type: {:?}", f),
None => Err(ERR::ErrorFunctionNotFound( None => Err(ERR::ErrorFunctionNotFound(
self.gen_call_signature(Some(namespace), fn_name, &args), self.gen_call_signature(namespace, fn_name, &args),
pos, pos,
) )
.into()), .into()),

View File

@ -264,6 +264,10 @@ pub use ast::{
FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock, FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock,
}; };
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))]
pub use ast::Namespace;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -279,10 +283,6 @@ pub use eval::{EvalState, GlobalRuntimeState};
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub use func::call::{FnResolutionCache, FnResolutionCacheEntry}; pub use func::call::{FnResolutionCache, FnResolutionCacheEntry};
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))]
pub use module::Namespace;
/// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a /// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a
/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored. /// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
/// ///

View File

@ -2039,14 +2039,9 @@ impl Module {
} }
} }
mod namespace;
/// Module containing all built-in [module resolvers][ModuleResolver]. /// Module containing all built-in [module resolvers][ModuleResolver].
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub mod resolvers; pub mod resolvers;
#[cfg(not(feature = "no_module"))]
pub use namespace::Namespace;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub use resolvers::ModuleResolver; pub use resolvers::ModuleResolver;

View File

@ -259,7 +259,7 @@ fn optimize_stmt_block(
if x.1.is_constant() { if x.1.is_constant() {
state.push_var( state.push_var(
x.0.name.as_str(), x.0.as_str(),
AccessMode::ReadOnly, AccessMode::ReadOnly,
x.1.get_literal_value(), x.1.get_literal_value(),
); );
@ -267,7 +267,7 @@ fn optimize_stmt_block(
} else { } else {
// Add variables into the state // Add variables into the state
optimize_expr(&mut x.1, state, false); optimize_expr(&mut x.1, state, false);
state.push_var(x.0.name.as_str(), AccessMode::ReadWrite, None); state.push_var(x.0.as_str(), AccessMode::ReadWrite, None);
} }
} }
// Optimize the statement // Optimize the statement
@ -895,7 +895,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
// Map literal where everything is pure - promote the indexed item. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
*expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name == prop) *expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.as_str() == 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));
} }
@ -935,7 +935,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
// Map literal where everything is pure - promote the indexed item. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
*expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name.as_str() == s.as_str()) *expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.as_str() == s.as_str())
.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));
} }
@ -1068,7 +1068,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
} }
// Do not call some special keywords // Do not call some special keywords
Expr::FnCall(x, ..) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => { Expr::FnCall(x, ..) if DONT_EVAL_KEYWORDS.contains(&x.name.as_str()) => {
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
} }
@ -1077,7 +1077,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
if !x.is_qualified() // Non-qualified if !x.is_qualified() // Non-qualified
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations && state.optimization_level == OptimizationLevel::Simple // simple optimizations
&& x.args.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted //&& !is_valid_identifier(x.chars()) // cannot be scripted
=> { => {
let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>(); let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>();
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
@ -1096,14 +1096,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
} }
// Overloaded operators can override built-in. // Overloaded operators can override built-in.
_ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, arg_types.as_ref()) => { _ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, arg_types.as_ref()) => {
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1])
.and_then(|f| { .and_then(|f| {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let lib = state.lib; let lib = state.lib;
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
let lib = &[]; let lib = &[];
let context = (state.engine, x.name.as_str(), lib).into(); let context = (state.engine, &x.name, lib).into();
let (first, second) = arg_values.split_first_mut().unwrap(); let (first, second) = arg_values.split_first_mut().unwrap();
(f)(context, &mut [ first, &mut second[0] ]).ok() (f)(context, &mut [ first, &mut second[0] ]).ok()
}) { }) {
@ -1123,10 +1123,10 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
Expr::DynamicConstant(..) | Expr::Unit(..) Expr::DynamicConstant(..) | Expr::Unit(..)
| Expr::StringConstant(..) | Expr::CharConstant(..) | Expr::StringConstant(..) | Expr::CharConstant(..)
| Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (), | Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Expr:: FloatConstant(..) => (), Expr:: FloatConstant(..) => (),
_ => if let Some(value) = arg.get_literal_value() { _ => if let Some(value) = arg.get_literal_value() {
state.set_dirty(); state.set_dirty();
*arg = Expr::DynamicConstant(value.into(), arg.start_position()); *arg = Expr::DynamicConstant(value.into(), arg.start_position());
@ -1189,10 +1189,10 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
// constant-name // constant-name
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Expr::Variable(.., x) if x.1.is_some() => (), Expr::Variable(x, ..) if !x.1.is_empty() => (),
Expr::Variable(.., pos, x) if state.find_constant(&x.2).is_some() => { Expr::Variable(x, .., pos) if state.find_constant(&x.3).is_some() => {
// Replace constant with value // Replace constant with value
*expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos); *expr = Expr::from_dynamic(state.find_constant(&x.3).unwrap().clone(), *pos);
state.set_dirty(); state.set_dirty();
} }

View File

@ -92,7 +92,7 @@ fn collect_fn_metadata(
// Create a metadata record for a function. // Create a metadata record for a function.
fn make_metadata( fn make_metadata(
dict: &BTreeSet<Identifier>, dict: &BTreeSet<Identifier>,
#[cfg(not(feature = "no_module"))] namespace: Option<Identifier>, #[cfg(not(feature = "no_module"))] namespace: Identifier,
func: &ScriptFnDef, func: &ScriptFnDef,
) -> Map { ) -> Map {
const DICT: &str = "key exists"; const DICT: &str = "key exists";
@ -100,8 +100,8 @@ fn collect_fn_metadata(
let mut map = Map::new(); let mut map = Map::new();
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if let Some(ns) = namespace { if !namespace.is_empty() {
map.insert(dict.get("namespace").expect(DICT).clone(), ns.into()); map.insert(dict.get("namespace").expect(DICT).clone(), namespace.into());
} }
map.insert( map.insert(
dict.get("name").expect(DICT).clone(), dict.get("name").expect(DICT).clone(),
@ -157,7 +157,7 @@ fn collect_fn_metadata(
make_metadata( make_metadata(
&dict, &dict,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
None, Identifier::new_const(),
f, f,
) )
.into(), .into(),
@ -174,7 +174,7 @@ fn collect_fn_metadata(
make_metadata( make_metadata(
&dict, &dict,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
None, Identifier::new_const(),
f, f,
) )
.into(), .into(),
@ -192,7 +192,7 @@ fn collect_fn_metadata(
make_metadata( make_metadata(
&dict, &dict,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
None, Identifier::new_const(),
f, f,
) )
.into(), .into(),
@ -219,9 +219,7 @@ fn collect_fn_metadata(
module module
.iter_script_fn() .iter_script_fn()
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f)) .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
.for_each(|(.., f)| { .for_each(|(.., f)| list.push(make_metadata(dict, namespace.clone(), f).into()));
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
});
for (ns, m) in module.iter_sub_modules() { for (ns, m) in module.iter_sub_modules() {
let ns = format!( let ns = format!(
"{}{}{}", "{}{}{}",

View File

@ -135,7 +135,7 @@ impl<'e> ParseState<'e> {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
if self.allow_capture { if self.allow_capture {
if index == 0 && !self.external_vars.iter().any(|v| v.name == name) { if index == 0 && !self.external_vars.iter().any(|v| v.as_str() == name) {
self.external_vars.push(crate::ast::Ident { self.external_vars.push(crate::ast::Ident {
name: name.into(), name: name.into(),
pos: _pos, pos: _pos,
@ -266,9 +266,9 @@ impl Expr {
fn into_property(self, state: &mut ParseState) -> Self { fn into_property(self, state: &mut ParseState) -> Self {
match self { match self {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Variable(.., x) if x.1.is_some() => unreachable!("qualified property"), Self::Variable(x, ..) if !x.1.is_empty() => unreachable!("qualified property"),
Self::Variable(.., pos, x) => { Self::Variable(x, .., pos) => {
let ident = x.2; let ident = x.3;
let getter = state.get_identifier(crate::engine::FN_GET, &ident); let getter = state.get_identifier(crate::engine::FN_GET, &ident);
let hash_get = calc_fn_hash(&getter, 1); let hash_get = calc_fn_hash(&getter, 1);
let setter = state.get_identifier(crate::engine::FN_SET, &ident); let setter = state.get_identifier(crate::engine::FN_SET, &ident);
@ -456,7 +456,7 @@ impl Engine {
lib: &mut FnLib, lib: &mut FnLib,
id: Identifier, id: Identifier,
capture_parent_scope: bool, capture_parent_scope: bool,
#[cfg(not(feature = "no_module"))] namespace: Option<crate::module::Namespace>, #[cfg(not(feature = "no_module"))] namespace: crate::ast::Namespace,
settings: ParseSettings, settings: ParseSettings,
) -> ParseResult<Expr> { ) -> ParseResult<Expr> {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -484,7 +484,7 @@ impl Engine {
eat_token(input, Token::RightParen); eat_token(input, Token::RightParen);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let hash = if let Some(namespace) = namespace.as_mut() { let hash = if !namespace.is_empty() {
let index = state.find_module(namespace.root()); let index = state.find_module(namespace.root());
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -499,7 +499,7 @@ impl Engine {
namespace.set_index(index); namespace.set_index(index);
crate::calc_qualified_fn_hash(namespace.iter().map(|m| m.name.as_str()), &id, 0) crate::calc_qualified_fn_hash(namespace.iter().map(|m| m.as_str()), &id, 0)
} else { } else {
calc_fn_hash(&id, 0) calc_fn_hash(&id, 0)
}; };
@ -544,7 +544,7 @@ impl Engine {
eat_token(input, Token::RightParen); eat_token(input, Token::RightParen);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let hash = if let Some(namespace) = namespace.as_mut() { let hash = if !namespace.is_empty() {
let index = state.find_module(namespace.root()); let index = state.find_module(namespace.root());
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -560,7 +560,7 @@ impl Engine {
namespace.set_index(index); namespace.set_index(index);
crate::calc_qualified_fn_hash( crate::calc_qualified_fn_hash(
namespace.iter().map(|m| m.name.as_str()), namespace.iter().map(|m| m.as_str()),
&id, &id,
args.len(), args.len(),
) )
@ -900,7 +900,7 @@ impl Engine {
let (name, pos) = match input.next().expect(NEVER_ENDS) { let (name, pos) = match input.next().expect(NEVER_ENDS) {
(Token::Identifier(s), pos) | (Token::StringConstant(s), pos) => { (Token::Identifier(s), pos) | (Token::StringConstant(s), pos) => {
if map.iter().any(|(p, ..)| p.name == &*s) { if map.iter().any(|(p, ..)| **p == s) {
return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos)); return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos));
} }
(s, pos) (s, pos)
@ -1387,9 +1387,9 @@ impl Engine {
// Identifier // Identifier
Token::Identifier(..) => { Token::Identifier(..) => {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let none = None; let ns = crate::ast::Namespace::NONE;
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
let none = (); let ns = ();
let s = match input.next().expect(NEVER_ENDS) { let s = match input.next().expect(NEVER_ENDS) {
(Token::Identifier(s), ..) => s, (Token::Identifier(s), ..) => s,
@ -1405,9 +1405,9 @@ impl Engine {
state.allow_capture = true; state.allow_capture = true;
} }
Expr::Variable( Expr::Variable(
(None, ns, 0, state.get_identifier("", s)).into(),
None, None,
settings.pos, settings.pos,
(None, none, state.get_identifier("", s)).into(),
) )
} }
// Namespace qualification // Namespace qualification
@ -1419,9 +1419,9 @@ impl Engine {
state.allow_capture = true; state.allow_capture = true;
} }
Expr::Variable( Expr::Variable(
(None, ns, 0, state.get_identifier("", s)).into(),
None, None,
settings.pos, settings.pos,
(None, none, state.get_identifier("", s)).into(),
) )
} }
// Normal variable access // Normal variable access
@ -1442,9 +1442,9 @@ impl Engine {
} }
}); });
Expr::Variable( Expr::Variable(
(index, ns, 0, state.get_identifier("", s)).into(),
short_index, short_index,
settings.pos, settings.pos,
(index, none, state.get_identifier("", s)).into(),
) )
} }
} }
@ -1453,9 +1453,9 @@ impl Engine {
// Reserved keyword or symbol // Reserved keyword or symbol
Token::Reserved(..) => { Token::Reserved(..) => {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let none = None; let ns = crate::ast::Namespace::NONE;
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
let none = (); let ns = ();
let s = match input.next().expect(NEVER_ENDS) { let s = match input.next().expect(NEVER_ENDS) {
(Token::Reserved(s), ..) => s, (Token::Reserved(s), ..) => s,
@ -1465,16 +1465,16 @@ impl Engine {
match input.peek().expect(NEVER_ENDS).0 { match input.peek().expect(NEVER_ENDS).0 {
// Function call is allowed to have reserved keyword // Function call is allowed to have reserved keyword
Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable( Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable(
(None, ns, 0, state.get_identifier("", s)).into(),
None, None,
settings.pos, settings.pos,
(None, none, state.get_identifier("", s)).into(),
), ),
// Access to `this` as a variable is OK within a function scope // Access to `this` as a variable is OK within a function scope
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
_ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable( _ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable(
(None, ns, 0, state.get_identifier("", s)).into(),
None, None,
settings.pos, settings.pos,
(None, none, state.get_identifier("", s)).into(),
), ),
// Cannot access to `this` as a variable not in a function scope // Cannot access to `this` as a variable not in a function scope
_ if &*s == KEYWORD_THIS => { _ if &*s == KEYWORD_THIS => {
@ -1527,7 +1527,7 @@ impl Engine {
lhs = match (lhs, tail_token) { lhs = match (lhs, tail_token) {
// Qualified function call with ! // Qualified function call with !
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(Expr::Variable(.., x), Token::Bang) if x.1.is_some() => { (Expr::Variable(x, ..), Token::Bang) if !x.1.is_empty() => {
return if !match_token(input, Token::LeftParen).0 { return if !match_token(input, Token::LeftParen).0 {
Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string()) Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string())
.into_err(tail_pos)) .into_err(tail_pos))
@ -1540,7 +1540,7 @@ impl Engine {
}; };
} }
// Function call with ! // Function call with !
(Expr::Variable(.., pos, x), Token::Bang) => { (Expr::Variable(x, .., pos), Token::Bang) => {
match match_token(input, Token::LeftParen) { match match_token(input, Token::LeftParen) {
(false, pos) => { (false, pos) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
@ -1552,10 +1552,8 @@ impl Engine {
_ => (), _ => (),
} }
let (.., _ns, name) = *x; let (.., _ns, _, name) = *x;
settings.pos = pos; settings.pos = pos;
#[cfg(not(feature = "no_module"))]
let _ns = _ns.map(|(ns, ..)| ns);
self.parse_fn_call( self.parse_fn_call(
input, input,
state, state,
@ -1568,10 +1566,8 @@ impl Engine {
)? )?
} }
// Function call // Function call
(Expr::Variable(.., pos, x), Token::LeftParen) => { (Expr::Variable(x, .., pos), Token::LeftParen) => {
let (.., _ns, name) = *x; let (.., _ns, _, name) = *x;
#[cfg(not(feature = "no_module"))]
let _ns = _ns.map(|(ns, ..)| ns);
settings.pos = pos; settings.pos = pos;
self.parse_fn_call( self.parse_fn_call(
input, input,
@ -1586,23 +1582,17 @@ impl Engine {
} }
// module access // module access
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(Expr::Variable(.., pos, x), Token::DoubleColon) => { (Expr::Variable(x, .., pos), Token::DoubleColon) => {
let (id2, pos2) = parse_var_name(input)?; let (id2, pos2) = parse_var_name(input)?;
let (.., mut namespace, name) = *x; let (.., mut namespace, _, name) = *x;
let var_name_def = Ident { name, pos }; let var_name_def = Ident { name, pos };
if let Some((ref mut namespace, ..)) = namespace { namespace.push(var_name_def);
namespace.push(var_name_def);
} else {
let mut ns = crate::module::Namespace::new();
ns.push(var_name_def);
namespace = Some((ns, 42));
}
Expr::Variable( Expr::Variable(
(None, namespace, 0, state.get_identifier("", id2)).into(),
None, None,
pos2, pos2,
(None, namespace, state.get_identifier("", id2)).into(),
) )
} }
// Indexing // Indexing
@ -1641,33 +1631,35 @@ impl Engine {
// Cache the hash key for namespace-qualified variables // Cache the hash key for namespace-qualified variables
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let namespaced_variable = match lhs { let namespaced_variable = match lhs {
Expr::Variable(.., ref mut x) if x.1.is_some() => Some(x.as_mut()), Expr::Variable(ref mut x, ..) if !x.1.is_empty() => Some(x.as_mut()),
Expr::Index(ref mut x, ..) | Expr::Dot(ref mut x, ..) => match x.lhs { Expr::Index(ref mut x, ..) | Expr::Dot(ref mut x, ..) => match x.lhs {
Expr::Variable(.., ref mut x) if x.1.is_some() => Some(x.as_mut()), Expr::Variable(ref mut x, ..) if !x.1.is_empty() => Some(x.as_mut()),
_ => None, _ => None,
}, },
_ => None, _ => None,
}; };
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if let Some((.., Some((namespace, hash)), name)) = namespaced_variable { if let Some((.., namespace, hash, name)) = namespaced_variable {
*hash = crate::calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name); if !namespace.is_empty() {
*hash = crate::calc_qualified_var_hash(namespace.iter().map(|v| v.as_str()), name);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
let index = state.find_module(namespace.root()); let index = state.find_module(namespace.root());
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let relax = settings.is_function_scope; let relax = settings.is_function_scope;
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
let relax = false; let relax = false;
if !relax && settings.options.strict_var && index.is_none() { if !relax && settings.options.strict_var && index.is_none() {
return Err(PERR::ModuleUndefined(namespace.root().to_string()) return Err(PERR::ModuleUndefined(namespace.root().to_string())
.into_err(namespace.position())); .into_err(namespace.position()));
}
namespace.set_index(index);
} }
namespace.set_index(index);
} }
} }
@ -1826,13 +1818,13 @@ impl Engine {
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.start_position())) Err(PERR::AssignmentToConstant("".into()).into_err(lhs.start_position()))
} }
// var (non-indexed) = rhs // var (non-indexed) = rhs
Expr::Variable(None, _, ref x) if x.0.is_none() => Ok(Stmt::Assignment( Expr::Variable(ref x, None, _) if x.0.is_none() => Ok(Stmt::Assignment(
(op_info, (lhs, rhs).into()).into(), (op_info, (lhs, rhs).into()).into(),
op_pos, op_pos,
)), )),
// var (indexed) = rhs // var (indexed) = rhs
Expr::Variable(i, var_pos, ref x) => { Expr::Variable(ref x, i, var_pos) => {
let (index, _, name) = x.as_ref(); let (index, .., name) = x.as_ref();
let index = i.map_or_else( let index = i.map_or_else(
|| index.expect("either long or short index is `None`").get(), || index.expect("either long or short index is `None`").get(),
|n| n.get() as usize, |n| n.get() as usize,
@ -1937,8 +1929,8 @@ impl Engine {
} }
// lhs.module::id - syntax error // lhs.module::id - syntax error
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(.., Expr::Variable(.., x)) if x.1.is_some() => { (.., Expr::Variable(x, ..)) if !x.1.is_empty() => {
Err(PERR::PropertyExpected.into_err(x.1.expect("`Some`").0.position())) Err(PERR::PropertyExpected.into_err(x.1.position()))
} }
// lhs.id // lhs.id
(lhs, var_expr @ Expr::Variable(..)) => { (lhs, var_expr @ Expr::Variable(..)) => {
@ -1958,7 +1950,7 @@ impl Engine {
// lhs.nnn::func(...) - syntax error // lhs.nnn::func(...) - syntax error
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(.., Expr::FnCall(func, ..)) if func.is_qualified() => { (.., Expr::FnCall(func, ..)) if func.is_qualified() => {
Err(PERR::PropertyExpected.into_err(func.namespace.expect("`Some`").position())) Err(PERR::PropertyExpected.into_err(func.namespace.position()))
} }
// lhs.Fn() or lhs.eval() // lhs.Fn() or lhs.eval()
(.., Expr::FnCall(func, func_pos)) (.., Expr::FnCall(func, func_pos))
@ -2006,14 +1998,13 @@ impl Engine {
match x.lhs { match x.lhs {
// lhs.module::id.dot_rhs or lhs.module::id[idx_rhs] - syntax error // lhs.module::id.dot_rhs or lhs.module::id[idx_rhs] - syntax error
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Expr::Variable(.., x) if x.1.is_some() => { Expr::Variable(x, ..) if !x.1.is_empty() => {
Err(PERR::PropertyExpected.into_err(x.1.expect("`Some`").0.position())) Err(PERR::PropertyExpected.into_err(x.1.position()))
} }
// lhs.module::func().dot_rhs or lhs.module::func()[idx_rhs] - syntax error // lhs.module::func().dot_rhs or lhs.module::func()[idx_rhs] - syntax error
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Expr::FnCall(func, ..) if func.is_qualified() => { Expr::FnCall(func, ..) if func.is_qualified() => {
Err(PERR::PropertyExpected Err(PERR::PropertyExpected.into_err(func.namespace.position()))
.into_err(func.namespace.expect("`Some`").position()))
} }
// lhs.id.dot_rhs or lhs.id[idx_rhs] // lhs.id.dot_rhs or lhs.id[idx_rhs]
Expr::Variable(..) | Expr::Property(..) => { Expr::Variable(..) | Expr::Property(..) => {
@ -2294,21 +2285,15 @@ impl Engine {
CUSTOM_SYNTAX_MARKER_IDENT => { CUSTOM_SYNTAX_MARKER_IDENT => {
let (name, pos) = parse_var_name(input)?; let (name, pos) = parse_var_name(input)?;
let name = state.get_identifier("", name); let name = state.get_identifier("", name);
#[cfg(not(feature = "no_module"))]
let ns = crate::ast::Namespace::NONE;
#[cfg(feature = "no_module")]
let ns = ();
segments.push(name.clone().into()); segments.push(name.clone().into());
tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_IDENT)); tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_IDENT));
inputs.push(Expr::Variable( inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos));
None,
pos,
(
None,
#[cfg(not(feature = "no_module"))]
None,
#[cfg(feature = "no_module")]
(),
name,
)
.into(),
));
} }
CUSTOM_SYNTAX_MARKER_SYMBOL => { CUSTOM_SYNTAX_MARKER_SYMBOL => {
let (symbol, pos) = parse_symbol(input)?; let (symbol, pos) = parse_symbol(input)?;
@ -2611,11 +2596,11 @@ impl Engine {
) )
.into_err(pos)); .into_err(pos));
} }
(name, name_pos, Some(counter_name), Some(counter_pos)) (name, name_pos, counter_name, counter_pos)
} else { } else {
// name // name
let (name, name_pos) = parse_var_name(input)?; let (name, name_pos) = parse_var_name(input)?;
(name, name_pos, None, None) (name, name_pos, Identifier::new_const(), Position::NONE)
}; };
// for name in ... // for name in ...
@ -2639,12 +2624,13 @@ impl Engine {
let prev_stack_len = state.stack.len(); let prev_stack_len = state.stack.len();
let counter_var = counter_name.map(|name| { if !counter_name.is_empty() {
let name = state.get_identifier("", name);
let pos = counter_pos.expect("`Some`");
state.stack.push(name.clone(), ()); state.stack.push(name.clone(), ());
Ident { name, pos } }
}); let counter_var = Ident {
name: state.get_identifier("", counter_name),
pos: counter_pos,
};
let loop_var = state.get_identifier("", name); let loop_var = state.get_identifier("", name);
state.stack.push(loop_var.clone(), ()); state.stack.push(loop_var.clone(), ());
@ -2786,7 +2772,7 @@ impl Engine {
// import expr as ... // import expr as ...
if !match_token(input, Token::As).0 { if !match_token(input, Token::As).0 {
return Ok(Stmt::Import((expr, None).into(), settings.pos)); return Ok(Stmt::Import((expr, Ident::EMPTY).into(), settings.pos));
} }
// import expr as name ... // import expr as name ...
@ -2795,7 +2781,7 @@ impl Engine {
state.imports.push(name.clone()); state.imports.push(name.clone());
Ok(Stmt::Import( Ok(Stmt::Import(
(expr, Some(Ident { name, pos })).into(), (expr, Ident { name, pos }).into(),
settings.pos, settings.pos,
)) ))
} }
@ -3229,15 +3215,15 @@ impl Engine {
let name = state.get_identifier("", name); let name = state.get_identifier("", name);
state.stack.push(name.clone(), ()); state.stack.push(name.clone(), ());
Some(Ident { name, pos }) Ident { name, pos }
} else { } else {
None Ident::EMPTY
}; };
// try { try_block } catch ( var ) { catch_block } // try { try_block } catch ( var ) { catch_block }
let catch_block = self.parse_block(input, state, lib, settings.level_up())?; let catch_block = self.parse_block(input, state, lib, settings.level_up())?;
if catch_var.is_some() { if !catch_var.is_empty() {
// Remove the error variable from the stack // Remove the error variable from the stack
state.stack.rewind(state.stack.len() - 1); state.stack.rewind(state.stack.len() - 1);
} }
@ -3382,19 +3368,12 @@ impl Engine {
.iter() .iter()
.cloned() .cloned()
.map(|crate::ast::Ident { name, pos }| { .map(|crate::ast::Ident { name, pos }| {
Expr::Variable( #[cfg(not(feature = "no_module"))]
None, let ns = crate::ast::Namespace::NONE;
pos, #[cfg(feature = "no_module")]
( let ns = ();
None,
#[cfg(not(feature = "no_module"))] Expr::Variable((None, ns, 0, name).into(), None, pos)
None,
#[cfg(feature = "no_module")]
(),
name,
)
.into(),
)
}), }),
); );