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

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

View File

@@ -3,7 +3,11 @@
use crate::{Identifier, Position};
#[cfg(feature = "no_std")]
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].
/// 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 {
/// 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)]
pub fn as_str(&self) -> &str {
self.name.as_str()

View File

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

151
src/ast/namespace.rs Normal file
View File

@@ -0,0 +1,151 @@
//! Namespace reference type.
#![cfg(not(feature = "no_module"))]
use crate::ast::Ident;
use crate::tokenizer::Token;
use crate::{Position, StaticVec};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
fmt,
num::NonZeroUsize,
ops::{Deref, DerefMut},
};
/// _(internals)_ A chain of [module][crate::Module] names to namespace-qualify a variable or function call.
/// Exported under the `internals` feature only.
///
/// Not available under `no_module`.
///
/// A [`u64`] offset to the current [stack of imported modules][crate::GlobalRuntimeState] is
/// cached for quick search purposes.
///
/// A [`StaticVec`] is used because the vast majority of namespace-qualified access contains only
/// one level, and it is wasteful to always allocate a [`Vec`] with one element.
#[derive(Clone, Eq, PartialEq, Default, Hash)]
pub struct Namespace {
path: StaticVec<Ident>,
index: Option<NonZeroUsize>,
}
impl fmt::Debug for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
return f.write_str("NONE");
}
if let Some(index) = self.index {
write!(f, "{} -> ", index)?;
}
f.write_str(
&self
.path
.iter()
.map(|m| m.as_str())
.collect::<StaticVec<_>>()
.join(Token::DoubleColon.literal_syntax()),
)
}
}
impl fmt::Display for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
return Ok(());
}
f.write_str(
&self
.path
.iter()
.map(|m| m.as_str())
.collect::<StaticVec<_>>()
.join(Token::DoubleColon.literal_syntax()),
)
}
}
impl Deref for Namespace {
type Target = StaticVec<Ident>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.path
}
}
impl DerefMut for Namespace {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.path
}
}
impl From<Vec<Ident>> for Namespace {
#[inline(always)]
fn from(mut path: Vec<Ident>) -> Self {
path.shrink_to_fit();
Self {
index: None,
path: path.into(),
}
}
}
impl From<StaticVec<Ident>> for Namespace {
#[inline(always)]
fn from(mut path: StaticVec<Ident>) -> Self {
path.shrink_to_fit();
Self { index: None, path }
}
}
impl Namespace {
/// Constant for no namespace.
pub const NONE: Self = Self {
index: None,
path: StaticVec::new_const(),
};
/// Create a new [`Namespace`].
#[inline(always)]
#[must_use]
pub fn new(root: impl Into<Ident>) -> Self {
let mut path = StaticVec::new_const();
path.push(root.into());
Self { index: None, path }
}
/// Get the [`Scope`][crate::Scope] index offset.
#[inline(always)]
#[must_use]
pub(crate) const fn index(&self) -> Option<NonZeroUsize> {
self.index
}
/// Set the [`Scope`][crate::Scope] index offset.
#[inline(always)]
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
self.index = index
}
/// Get the [position][Position] of this [`Namespace`].
///
/// # Panics
///
/// Panics if the path is empty.
#[inline(always)]
#[must_use]
pub fn position(&self) -> Position {
self.path[0].pos
}
/// Get the first path segment of this [`Namespace`].
///
/// # Panics
///
/// Panics if the path is empty.
#[inline(always)]
#[must_use]
pub fn root(&self) -> &str {
&self.path[0].name
}
}

View File

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