Extract Identifier type.

This commit is contained in:
Stephen Chung 2021-03-29 11:36:02 +08:00
parent 241f5abe10
commit e5249cc1ae
16 changed files with 170 additions and 133 deletions

View File

@ -34,6 +34,7 @@ Breaking changes
* The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate. * The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate.
* The shebang `#!` is now a reserved symbol. * The shebang `#!` is now a reserved symbol.
* Shebangs at the very beginning of script files are skipped when loading them. * Shebangs at the very beginning of script files are skipped when loading them.
* [`smartstring`](https://crates.io/crates/smartstring) is used for identifiers.
Enhancements Enhancements
------------ ------------

View File

@ -17,6 +17,7 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"]
[dependencies] [dependencies]
smallvec = { version = "1.6", default-features = false, features = ["union"] } smallvec = { version = "1.6", default-features = false, features = ["union"] }
smartstring = { version = "0.2.6" }
ahash = { version = "0.7", default-features = false } ahash = { version = "0.7", default-features = false }
num-traits = { version = "0.2", default_features = false } num-traits = { version = "0.2", default_features = false }
rhai_codegen = { version = "0.3.4", path = "codegen", features = ["metadata"] } rhai_codegen = { version = "0.3.4", path = "codegen", features = ["metadata"] }

View File

@ -35,7 +35,7 @@ Standard features
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, [integers](https://rhai.rs/book/language/numbers.html), [floating-point numbers](https://rhai.rs/book/language/numbers.html) (including [`Decimal`](https://crates.io/crates/rust_decimal)), [strings](https://rhai.rs/book/language/strings-chars.html), [Unicode characters](https://rhai.rs/book/language/strings-chars.html), [arrays](https://rhai.rs/book/language/arrays.html) and [object maps](https://rhai.rs/book/language/object-maps.html). * Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, [integers](https://rhai.rs/book/language/numbers.html), [floating-point numbers](https://rhai.rs/book/language/numbers.html) (including [`Decimal`](https://crates.io/crates/rust_decimal)), [strings](https://rhai.rs/book/language/strings-chars.html), [Unicode characters](https://rhai.rs/book/language/strings-chars.html), [arrays](https://rhai.rs/book/language/arrays.html) and [object maps](https://rhai.rs/book/language/object-maps.html).
* Easily [call a script-defined function](https://rhai.rs/book/engine/call-fn.html) from Rust. * Easily [call a script-defined function](https://rhai.rs/book/engine/call-fn.html) from Rust.
* Relatively little `unsafe` code (yes there are some for performance reasons). * Relatively little `unsafe` code (yes there are some for performance reasons).
* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec), [`num-traits`](https://crates.io/crates/num-traits) and [`ahash`](https://crates.io/crates/ahash)). * Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec), [`num-traits`](https://crates.io/crates/num-traits), [`ahash`](https://crates.io/crates/ahash)) and [`smartstring`](https://crates.io/crates/smartstring)).
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature). * Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
* Compile once to [AST](https://rhai.rs/book/engine/compile.html) form for repeated evaluations. * Compile once to [AST](https://rhai.rs/book/engine/compile.html) form for repeated evaluations.
* Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts). * Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts).

View File

@ -16,7 +16,8 @@ use crate::stdlib::{
}; };
use crate::token::Token; use crate::token::Token;
use crate::{ use crate::{
Dynamic, FnNamespace, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT, Dynamic, FnNamespace, FnPtr, Identifier, ImmutableString, Module, Position, Shared, StaticVec,
INT,
}; };
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -50,14 +51,14 @@ pub struct ScriptFnDef {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub mods: crate::engine::Imports, pub mods: crate::engine::Imports,
/// Function name. /// Function name.
pub name: ImmutableString, pub name: Identifier,
/// Function access mode. /// Function access mode.
pub access: FnAccess, pub access: FnAccess,
/// Names of function parameters. /// Names of function parameters.
pub params: StaticVec<ImmutableString>, pub params: StaticVec<Identifier>,
/// Access to external variables. /// Access to external variables.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
pub externals: BTreeSet<ImmutableString>, pub externals: BTreeSet<Identifier>,
/// Function doc-comments (if any). /// Function doc-comments (if any).
pub comments: StaticVec<String>, pub comments: StaticVec<String>,
} }
@ -144,7 +145,7 @@ impl<'a> Into<ScriptFnMetadata<'a>> for &'a ScriptFnDef {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AST { pub struct AST {
/// Source of the [`AST`]. /// Source of the [`AST`].
source: Option<ImmutableString>, source: Option<Identifier>,
/// Global statements. /// Global statements.
body: StmtBlock, body: StmtBlock,
/// Script-defined functions. /// Script-defined functions.
@ -190,7 +191,7 @@ impl AST {
pub fn new_with_source( pub fn new_with_source(
statements: impl IntoIterator<Item = Stmt>, statements: impl IntoIterator<Item = Stmt>,
functions: impl Into<Shared<Module>>, functions: impl Into<Shared<Module>>,
source: impl Into<ImmutableString>, source: impl Into<Identifier>,
) -> Self { ) -> Self {
Self { Self {
source: Some(source.into()), source: Some(source.into()),
@ -210,12 +211,12 @@ impl AST {
} }
/// Clone the source, if any. /// Clone the source, if any.
#[inline(always)] #[inline(always)]
pub(crate) fn clone_source(&self) -> Option<ImmutableString> { pub(crate) fn clone_source(&self) -> Option<Identifier> {
self.source.clone() self.source.clone()
} }
/// Set the source. /// Set the source.
#[inline(always)] #[inline(always)]
pub fn set_source(&mut self, source: impl Into<ImmutableString>) -> &mut Self { pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
self.source = Some(source.into()); self.source = Some(source.into());
if let Some(module) = Shared::get_mut(&mut self.functions) { if let Some(module) = Shared::get_mut(&mut self.functions) {
@ -756,7 +757,7 @@ impl AsRef<Module> for AST {
} }
} }
/// _(INTERNALS)_ An identifier containing an [immutable string][ImmutableString] 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.
/// ///
/// # Volatile Data Structure /// # Volatile Data Structure
@ -765,7 +766,7 @@ impl AsRef<Module> for AST {
#[derive(Clone, Eq, PartialEq, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
pub struct Ident { pub struct Ident {
/// Identifier name. /// Identifier name.
pub name: ImmutableString, pub name: Identifier,
/// Declaration position. /// Declaration position.
pub pos: Position, pub pos: Position,
} }
@ -869,11 +870,11 @@ pub enum Stmt {
/// `do` `{` stmt `}` `while`|`until` expr /// `do` `{` stmt `}` `while`|`until` expr
Do(Box<StmtBlock>, Expr, bool, Position), Do(Box<StmtBlock>, Expr, bool, Position),
/// `for` id `in` expr `{` stmt `}` /// `for` id `in` expr `{` stmt `}`
For(Expr, Box<(String, StmtBlock)>, Position), For(Expr, Box<(Identifier, StmtBlock)>, Position),
/// \[`export`\] `let` id `=` expr /// \[`export`\] `let` id `=` expr
Let(Expr, Ident, bool, Position), Let(Expr, Box<Ident>, bool, Position),
/// \[`export`\] `const` id `=` expr /// \[`export`\] `const` id `=` expr
Const(Expr, Ident, bool, Position), Const(Expr, Box<Ident>, bool, Position),
/// expr op`=` expr /// expr op`=` expr
Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position), Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position),
/// `{` stmt`;` ... `}` /// `{` stmt`;` ... `}`
@ -894,7 +895,7 @@ pub enum Stmt {
Return(ReturnType, Option<Expr>, Position), Return(ReturnType, Option<Expr>, Position),
/// `import` expr `as` var /// `import` expr `as` var
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Import(Expr, Option<Ident>, Position), Import(Expr, Option<Box<Ident>>, Position),
/// `export` var `as` var `,` ... /// `export` var `as` var `,` ...
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Export(Vec<(Ident, Option<Ident>)>, Position), Export(Vec<(Ident, Option<Ident>)>, Position),
@ -1256,7 +1257,7 @@ pub struct CustomExpr {
/// List of keywords. /// List of keywords.
pub keywords: StaticVec<Expr>, pub keywords: StaticVec<Expr>,
/// List of tokens actually parsed. /// List of tokens actually parsed.
pub tokens: Vec<ImmutableString>, pub tokens: Vec<Identifier>,
/// Delta number of variables in the scope. /// Delta number of variables in the scope.
pub scope_delta: isize, pub scope_delta: isize,
} }
@ -1400,11 +1401,11 @@ pub struct FnCallExpr {
/// List of function call argument expressions. /// List of function call argument expressions.
pub args: StaticVec<Expr>, pub args: StaticVec<Expr>,
/// List of function call arguments that are constants. /// List of function call arguments that are constants.
pub constant_args: StaticVec<(Dynamic, Position)>, pub constant_args: smallvec::SmallVec<[(Dynamic, Position); 2]>,
/// Namespace of the function, if any. Boxed because it occurs rarely. /// Namespace of the function, if any. Boxed because it occurs rarely.
pub namespace: Option<NamespaceRef>, pub namespace: Option<NamespaceRef>,
/// Function name. /// Function name.
pub name: ImmutableString, pub name: Identifier,
} }
impl FnCallExpr { impl FnCallExpr {
@ -1554,7 +1555,7 @@ pub enum Expr {
/// Variable access - (optional index, optional (hash, modules), variable name) /// Variable access - (optional index, optional (hash, modules), variable name)
Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>), Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>),
/// Property access - ((getter, hash), (setter, hash), prop) /// Property access - ((getter, hash), (setter, hash), prop)
Property(Box<((ImmutableString, u64), (ImmutableString, u64), Ident)>), Property(Box<((Identifier, u64), (Identifier, u64), Ident)>),
/// { [statement][Stmt] ... } /// { [statement][Stmt] ... }
Stmt(Box<StmtBlock>), Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)` /// func `(` expr `,` ... `)`
@ -1609,7 +1610,7 @@ impl Expr {
Self::Map(x, _) if self.is_constant() => { Self::Map(x, _) if self.is_constant() => {
let mut map = x.1.clone(); let mut map = x.1.clone();
x.0.iter().for_each(|(k, v)| { x.0.iter().for_each(|(k, v)| {
*map.get_mut(&k.name).unwrap() = v.get_constant_value().unwrap() *map.get_mut(k.name.as_str()).unwrap() = v.get_constant_value().unwrap()
}); });
Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly)) Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly))
} }

View File

@ -1667,6 +1667,18 @@ impl<S: Into<ImmutableString>> From<S> for Dynamic {
Self(Union::Str(value.into(), AccessMode::ReadWrite)) Self(Union::Str(value.into(), AccessMode::ReadWrite))
} }
} }
impl From<&ImmutableString> for Dynamic {
#[inline(always)]
fn from(value: &ImmutableString) -> Self {
value.clone().into()
}
}
impl<C: smartstring::SmartStringMode> From<&smartstring::SmartString<C>> for Dynamic {
#[inline(always)]
fn from(value: &smartstring::SmartString<C>) -> Self {
value.to_string().into()
}
}
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
impl<T: Variant + Clone> From<crate::stdlib::vec::Vec<T>> for Dynamic { impl<T: Variant + Clone> From<crate::stdlib::vec::Vec<T>> for Dynamic {
#[inline(always)] #[inline(always)]

View File

@ -25,8 +25,8 @@ use crate::stdlib::{
use crate::syntax::CustomSyntax; use crate::syntax::CustomSyntax;
use crate::utils::get_hasher; use crate::utils::get_hasher;
use crate::{ use crate::{
Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position, RhaiResult, Scope, Shared, Dynamic, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, Position, RhaiResult,
StaticVec, Scope, Shared, StaticVec,
}; };
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -50,7 +50,7 @@ pub type Precedence = NonZeroU8;
// the module name will live beyond the AST of the eval script text. // the module name will live beyond the AST of the eval script text.
// The best we can do is a shared reference. // The best we can do is a shared reference.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Imports(StaticVec<ImmutableString>, StaticVec<Shared<Module>>); pub struct Imports(StaticVec<Identifier>, StaticVec<Shared<Module>>);
impl Imports { impl Imports {
/// Get the length of this stack of imported [modules][Module]. /// Get the length of this stack of imported [modules][Module].
@ -79,7 +79,7 @@ impl Imports {
} }
/// Push an imported [modules][Module] onto the stack. /// Push an imported [modules][Module] onto the stack.
#[inline(always)] #[inline(always)]
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) { pub fn push(&mut self, name: impl Into<Identifier>, module: impl Into<Shared<Module>>) {
self.0.push(name.into()); self.0.push(name.into());
self.1.push(module.into()); self.1.push(module.into());
} }
@ -102,18 +102,18 @@ impl Imports {
/// Get an iterator to this stack of imported [modules][Module] in reverse order. /// Get an iterator to this stack of imported [modules][Module] in reverse order.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> { pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
self.0.iter().rev().zip(self.1.iter().rev()) self.0.iter().rev().zip(self.1.iter().rev())
} }
/// Get an iterator to this stack of imported [modules][Module] in forward order. /// Get an iterator to this stack of imported [modules][Module] in forward order.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> { pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
self.0.iter().zip(self.1.iter()) self.0.iter().zip(self.1.iter())
} }
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order. /// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
#[inline(always)] #[inline(always)]
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> { pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
self.0.into_iter().rev().zip(self.1.into_iter().rev()) self.0.into_iter().rev().zip(self.1.into_iter().rev())
} }
/// Does the specified function hash key exist in this stack of imported [modules][Module]? /// Does the specified function hash key exist in this stack of imported [modules][Module]?
@ -124,7 +124,7 @@ impl Imports {
} }
/// Get specified function via its hash key. /// Get specified function via its hash key.
#[inline(always)] #[inline(always)]
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&ImmutableString>)> { pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> {
self.1 self.1
.iter() .iter()
.rev() .rev()
@ -491,7 +491,7 @@ pub struct FnResolutionCacheEntry {
/// Function. /// Function.
pub func: CallableFunction, pub func: CallableFunction,
/// Optional source. /// Optional source.
pub source: Option<ImmutableString>, pub source: Option<Identifier>,
} }
/// A function resolution cache. /// A function resolution cache.
@ -506,7 +506,7 @@ pub type FnResolutionCache = BTreeMap<u64, Option<FnResolutionCacheEntry>>;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct State { pub struct State {
/// Source of the current context. /// Source of the current context.
pub source: Option<ImmutableString>, pub source: Option<Identifier>,
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup. /// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
/// In some situation, e.g. after running an `eval` statement, subsequent offsets become mis-aligned. /// In some situation, e.g. after running an `eval` statement, subsequent offsets become mis-aligned.
/// When that happens, this flag is turned on to force a scope lookup by name. /// When that happens, this flag is turned on to force a scope lookup by name.
@ -703,7 +703,7 @@ pub struct Engine {
/// A collection of all modules loaded into the global namespace of the Engine. /// A collection of all modules loaded into the global namespace of the Engine.
pub(crate) global_modules: StaticVec<Shared<Module>>, pub(crate) global_modules: StaticVec<Shared<Module>>,
/// A collection of all sub-modules directly loaded into the Engine. /// A collection of all sub-modules directly loaded into the Engine.
pub(crate) global_sub_modules: BTreeMap<ImmutableString, Shared<Module>>, pub(crate) global_sub_modules: BTreeMap<Identifier, Shared<Module>>,
/// A module resolution service. /// A module resolution service.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -717,7 +717,7 @@ pub struct Engine {
/// A map containing custom keywords and precedence to recognize. /// A map containing custom keywords and precedence to recognize.
pub(crate) custom_keywords: BTreeMap<String, Option<Precedence>>, pub(crate) custom_keywords: BTreeMap<String, Option<Precedence>>,
/// Custom syntax. /// Custom syntax.
pub(crate) custom_syntax: BTreeMap<ImmutableString, CustomSyntax>, pub(crate) custom_syntax: BTreeMap<Identifier, CustomSyntax>,
/// Callback closure for resolving variable access. /// Callback closure for resolving variable access.
pub(crate) resolve_var: Option<OnVarCallback>, pub(crate) resolve_var: Option<OnVarCallback>,
@ -1183,7 +1183,7 @@ impl Engine {
// {xxx:map}.id op= ??? // {xxx:map}.id op= ???
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => { Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
let Ident { name, pos, .. } = &x.2; let Ident { name, pos, .. } = &x.2;
let index = name.clone().into(); let index = name.into();
let val = self.get_indexed_mut( let val = self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, true, is_ref, false, level, mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
)?; )?;
@ -1196,7 +1196,7 @@ impl Engine {
// {xxx:map}.id // {xxx:map}.id
Expr::Property(x) if target_val.is::<Map>() => { Expr::Property(x) if target_val.is::<Map>() => {
let Ident { name, pos, .. } = &x.2; let Ident { name, pos, .. } = &x.2;
let index = name.clone().into(); let index = name.into();
let val = self.get_indexed_mut( let val = self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, false, is_ref, false, level, mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
)?; )?;
@ -1231,7 +1231,7 @@ impl Engine {
let mut val = match &x.lhs { let mut val = match &x.lhs {
Expr::Property(p) => { Expr::Property(p) => {
let Ident { name, pos, .. } = &p.2; let Ident { name, pos, .. } = &p.2;
let index = name.clone().into(); let index = name.into();
self.get_indexed_mut( self.get_indexed_mut(
mods, state, lib, target_val, index, *pos, false, is_ref, true, mods, state, lib, target_val, index, *pos, false, is_ref, true,
level, level,
@ -1696,7 +1696,7 @@ impl Engine {
Expr::Map(x, _) => { Expr::Map(x, _) => {
let mut map = x.1.clone(); let mut map = x.1.clone();
for (Ident { name: key, .. }, expr) in &x.0 { for (Ident { name: key, .. }, expr) in &x.0 {
*map.get_mut(key).unwrap() = self *map.get_mut(key.as_str()).unwrap() = self
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.flatten(); .flatten();
} }
@ -2209,7 +2209,7 @@ impl Engine {
if let Some(func) = func { if let Some(func) = func {
// Add the loop variable // Add the loop variable
let var_name: Cow<'_, str> = if state.is_global() { let var_name: Cow<'_, str> = if state.is_global() {
name.clone().into() name.to_string().into()
} else { } else {
unsafe_cast_var_name_to_lifetime(name).into() unsafe_cast_var_name_to_lifetime(name).into()
}; };
@ -2300,7 +2300,7 @@ impl Engine {
err_map.insert("message".into(), err.to_string().into()); err_map.insert("message".into(), err.to_string().into());
if let Some(ref source) = state.source { if let Some(ref source) = state.source {
err_map.insert("source".into(), source.clone().into()); err_map.insert("source".into(), source.into());
} }
if err_pos.is_none() { if err_pos.is_none() {
@ -2382,8 +2382,8 @@ impl Engine {
} }
// Let/const statement // Let/const statement
Stmt::Let(expr, Ident { name, .. }, export, _) Stmt::Let(expr, x, export, _) | Stmt::Const(expr, x, export, _) => {
| Stmt::Const(expr, Ident { name, .. }, export, _) => { let name = &x.name;
let entry_type = match stmt { let entry_type = match stmt {
Stmt::Let(_, _, _, _) => AccessMode::ReadWrite, Stmt::Let(_, _, _, _) => AccessMode::ReadWrite,
Stmt::Const(_, _, _, _) => AccessMode::ReadOnly, Stmt::Const(_, _, _, _) => AccessMode::ReadOnly,

View File

@ -12,7 +12,7 @@ use crate::stdlib::{
string::String, string::String,
}; };
use crate::{ use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module, scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module,
NativeCallContext, ParseError, Position, RhaiResult, Shared, AST, NativeCallContext, ParseError, Position, RhaiResult, Shared, AST,
}; };
@ -53,7 +53,7 @@ impl Engine {
#[inline] #[inline]
pub fn register_fn<N, A, F>(&mut self, name: N, func: F) -> &mut Self pub fn register_fn<N, A, F>(&mut self, name: N, func: F) -> &mut Self
where where
N: AsRef<str> + Into<ImmutableString>, N: AsRef<str> + Into<Identifier>,
F: RegisterNativeFunction<A, ()>, F: RegisterNativeFunction<A, ()>,
{ {
let param_types = F::param_types(); let param_types = F::param_types();
@ -113,7 +113,7 @@ impl Engine {
#[inline] #[inline]
pub fn register_result_fn<N, A, F, R>(&mut self, name: N, func: F) -> &mut Self pub fn register_result_fn<N, A, F, R>(&mut self, name: N, func: F) -> &mut Self
where where
N: AsRef<str> + Into<ImmutableString>, N: AsRef<str> + Into<Identifier>,
F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>, F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>,
{ {
let param_types = F::param_types(); let param_types = F::param_types();
@ -170,7 +170,7 @@ impl Engine {
+ 'static, + 'static,
) -> &mut Self ) -> &mut Self
where where
N: AsRef<str> + Into<ImmutableString>, N: AsRef<str> + Into<Identifier>,
T: Variant + Clone, T: Variant + Clone,
{ {
self.global_namespace.set_raw_fn( self.global_namespace.set_raw_fn(
@ -901,12 +901,12 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub fn register_static_module( pub fn register_static_module(
&mut self, &mut self,
name: impl AsRef<str> + Into<ImmutableString>, name: impl AsRef<str> + Into<Identifier>,
module: Shared<Module>, module: Shared<Module>,
) -> &mut Self { ) -> &mut Self {
fn register_static_module_raw( fn register_static_module_raw(
root: &mut crate::stdlib::collections::BTreeMap<crate::ImmutableString, Shared<Module>>, root: &mut crate::stdlib::collections::BTreeMap<Identifier, Shared<Module>>,
name: impl AsRef<str> + Into<ImmutableString>, name: impl AsRef<str> + Into<Identifier>,
module: Shared<Module>, module: Shared<Module>,
) { ) {
let separator = crate::token::Token::DoubleColon.syntax(); let separator = crate::token::Token::DoubleColon.syntax();
@ -952,7 +952,7 @@ impl Engine {
#[deprecated(since = "0.19.9", note = "use `register_static_module` instead")] #[deprecated(since = "0.19.9", note = "use `register_static_module` instead")]
pub fn register_module( pub fn register_module(
&mut self, &mut self,
name: impl AsRef<str> + Into<ImmutableString>, name: impl AsRef<str> + Into<Identifier>,
module: impl Into<Shared<Module>>, module: impl Into<Shared<Module>>,
) -> &mut Self { ) -> &mut Self {
self.register_static_module(name, module.into()) self.register_static_module(name, module.into())
@ -1045,14 +1045,14 @@ impl Engine {
fn collect_imports( fn collect_imports(
ast: &AST, ast: &AST,
resolver: &StaticModuleResolver, resolver: &StaticModuleResolver,
imports: &mut BTreeSet<ImmutableString>, imports: &mut BTreeSet<Identifier>,
) { ) {
ast.walk(&mut |path| match path.last().unwrap() { ast.walk(&mut |path| match path.last().unwrap() {
// Collect all `import` statements with a string constant path // Collect all `import` statements with a string constant path
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _)) ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
if !resolver.contains_path(s) && !imports.contains(s) => if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>
{ {
imports.insert(s.clone()); imports.insert(s.clone().into());
true true
} }
_ => true, _ => true,

View File

@ -13,8 +13,8 @@ use crate::stdlib::{
}; };
use crate::token::is_valid_identifier; use crate::token::is_valid_identifier;
use crate::{ use crate::{
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, Position, calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, Identifier, ImmutableString, Module,
RhaiResult, StaticVec, Position, RhaiResult, StaticVec,
}; };
/// Trait that maps to `Send + Sync` only under the `sync` feature. /// Trait that maps to `Send + Sync` only under the `sync` feature.
@ -144,9 +144,7 @@ impl<'a> NativeCallContext<'a> {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub(crate) fn iter_imports_raw( pub(crate) fn iter_imports_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
&self,
) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
self.mods.iter().flat_map(|&m| m.iter_raw()) self.mods.iter().flat_map(|&m| m.iter_raw())
} }
/// _(INTERNALS)_ The current set of modules imported via `import` statements. /// _(INTERNALS)_ The current set of modules imported via `import` statements.

View File

@ -292,6 +292,8 @@ type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub type StaticVec<T> = smallvec::SmallVec<[T; 4]>; pub type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
pub type Identifier = smartstring::SmartString<smartstring::Compact>;
// Compiler guards against mutually-exclusive feature flags // Compiler guards against mutually-exclusive feature flags
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]

View File

@ -18,8 +18,8 @@ use crate::stdlib::{
use crate::token::Token; use crate::token::Token;
use crate::utils::StringInterner; use crate::utils::StringInterner;
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ImmutableString, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, Identifier,
NativeCallContext, Position, Shared, StaticVec, ImmutableString, NativeCallContext, Position, Shared, StaticVec,
}; };
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -55,14 +55,14 @@ pub struct FuncInfo {
/// Function access mode. /// Function access mode.
pub access: FnAccess, pub access: FnAccess,
/// Function name. /// Function name.
pub name: ImmutableString, pub name: Identifier,
/// Number of parameters. /// Number of parameters.
pub params: usize, pub params: usize,
/// Parameter types (if applicable). /// Parameter types (if applicable).
pub param_types: StaticVec<TypeId>, pub param_types: StaticVec<TypeId>,
/// Parameter names (if available). /// Parameter names (if available).
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
pub param_names: StaticVec<ImmutableString>, pub param_names: StaticVec<Identifier>,
} }
impl FuncInfo { impl FuncInfo {
@ -128,11 +128,11 @@ fn calc_native_fn_hash<'a>(
#[derive(Clone)] #[derive(Clone)]
pub struct Module { pub struct Module {
/// ID identifying the module. /// ID identifying the module.
id: Option<ImmutableString>, id: Option<Identifier>,
/// Sub-modules. /// Sub-modules.
modules: BTreeMap<ImmutableString, Shared<Module>>, modules: BTreeMap<Identifier, Shared<Module>>,
/// [`Module`] variables. /// [`Module`] variables.
variables: BTreeMap<ImmutableString, Dynamic>, variables: BTreeMap<Identifier, Dynamic>,
/// Flattened collection of all [`Module`] variables, including those in sub-modules. /// Flattened collection of all [`Module`] variables, including those in sub-modules.
all_variables: BTreeMap<u64, Dynamic>, all_variables: BTreeMap<u64, Dynamic>,
/// External Rust functions. /// External Rust functions.
@ -288,7 +288,7 @@ impl Module {
self.id_raw().map(|s| s.as_str()) self.id_raw().map(|s| s.as_str())
} }
/// Get the ID of the [`Module`] as an [`ImmutableString`], if any. /// Get the ID of the [`Module`] as an [`Identifier`], if any.
/// ///
/// # Example /// # Example
/// ///
@ -300,7 +300,7 @@ impl Module {
/// assert_eq!(module.id_raw().map(|s| s.as_str()), Some("hello")); /// assert_eq!(module.id_raw().map(|s| s.as_str()), Some("hello"));
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn id_raw(&self) -> Option<&ImmutableString> { pub fn id_raw(&self) -> Option<&Identifier> {
self.id.as_ref() self.id.as_ref()
} }
@ -316,7 +316,7 @@ impl Module {
/// assert_eq!(module.id(), Some("hello")); /// assert_eq!(module.id(), Some("hello"));
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_id<S: Into<ImmutableString>>(&mut self, id: Option<S>) { pub fn set_id<S: Into<Identifier>>(&mut self, id: Option<S>) {
self.id = id.map(|s| s.into()); self.id = id.map(|s| s.into());
} }
@ -440,7 +440,7 @@ impl Module {
#[inline(always)] #[inline(always)]
pub fn set_var( pub fn set_var(
&mut self, &mut self,
name: impl Into<ImmutableString>, name: impl Into<Identifier>,
value: impl Variant + Clone, value: impl Variant + Clone,
) -> &mut Self { ) -> &mut Self {
self.variables.insert(name.into(), Dynamic::from(value)); self.variables.insert(name.into(), Dynamic::from(value));
@ -514,7 +514,7 @@ impl Module {
/// Thus the [`Module`] is automatically set to be non-indexed. /// Thus the [`Module`] is automatically set to be non-indexed.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap<ImmutableString, Shared<Module>> { pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> {
// We must assume that the user has changed the sub-modules // We must assume that the user has changed the sub-modules
// (otherwise why take a mutable reference?) // (otherwise why take a mutable reference?)
self.all_functions.clear(); self.all_functions.clear();
@ -577,7 +577,7 @@ impl Module {
#[inline(always)] #[inline(always)]
pub fn set_sub_module( pub fn set_sub_module(
&mut self, &mut self,
name: impl Into<ImmutableString>, name: impl Into<Identifier>,
sub_module: impl Into<Shared<Module>>, sub_module: impl Into<Shared<Module>>,
) -> &mut Self { ) -> &mut Self {
self.modules.insert(name.into(), sub_module.into()); self.modules.insert(name.into(), sub_module.into());
@ -672,7 +672,7 @@ impl Module {
#[inline] #[inline]
pub fn set_fn( pub fn set_fn(
&mut self, &mut self,
name: impl AsRef<str> + Into<ImmutableString>, name: impl AsRef<str> + Into<Identifier>,
namespace: FnNamespace, namespace: FnNamespace,
access: FnAccess, access: FnAccess,
_arg_names: Option<&[&str]>, _arg_names: Option<&[&str]>,
@ -793,7 +793,7 @@ impl Module {
func: F, func: F,
) -> u64 ) -> u64
where where
N: AsRef<str> + Into<ImmutableString>, N: AsRef<str> + Into<Identifier>,
T: Variant + Clone, T: Variant + Clone,
F: Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>> F: Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync + SendSync
@ -838,7 +838,7 @@ impl Module {
#[inline(always)] #[inline(always)]
pub fn set_native_fn<ARGS, N, T, F>(&mut self, name: N, func: F) -> u64 pub fn set_native_fn<ARGS, N, T, F>(&mut self, name: N, func: F) -> u64
where where
N: AsRef<str> + Into<ImmutableString>, N: AsRef<str> + Into<Identifier>,
T: Variant + Clone, T: Variant + Clone,
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>, F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
{ {

View File

@ -1,6 +1,6 @@
//! Module implementing the [`AST`] optimizer. //! Module implementing the [`AST`] optimizer.
use crate::ast::{Expr, Ident, Stmt, StmtBlock}; use crate::ast::{Expr, Stmt, StmtBlock};
use crate::dynamic::AccessMode; use crate::dynamic::AccessMode;
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::fn_builtin::get_builtin_binary_op_fn; use crate::fn_builtin::get_builtin_binary_op_fn;
@ -214,17 +214,17 @@ fn optimize_stmt_block(
statements.iter_mut().for_each(|stmt| { statements.iter_mut().for_each(|stmt| {
match stmt { match stmt {
// Add constant literals into the state // Add constant literals into the state
Stmt::Const(value_expr, Ident { name, .. }, _, _) => { Stmt::Const(value_expr, x, _, _) => {
optimize_expr(value_expr, state); optimize_expr(value_expr, state);
if value_expr.is_constant() { if value_expr.is_constant() {
state.push_var(name, AccessMode::ReadOnly, value_expr.clone()); state.push_var(&x.name, AccessMode::ReadOnly, value_expr.clone());
} }
} }
// Add variables into the state // Add variables into the state
Stmt::Let(value_expr, Ident { name, pos, .. }, _, _) => { Stmt::Let(value_expr, x, _, _) => {
optimize_expr(value_expr, state); optimize_expr(value_expr, state);
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(*pos)); state.push_var(&x.name, AccessMode::ReadWrite, Expr::Unit(x.pos));
} }
// Optimize the statement // Optimize the statement
_ => optimize_stmt(stmt, state, preserve_result), _ => optimize_stmt(stmt, state, preserve_result),
@ -649,7 +649,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// 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 == *s) *expr = mem::take(&mut m.0).into_iter().find(|(x, _)| x.name.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));
} }

View File

@ -1,5 +1,5 @@
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, FnPtr, ImmutableString, NativeCallContext}; use crate::{def_package, FnPtr, Identifier, ImmutableString, NativeCallContext};
def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, { def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions);
@ -38,18 +38,21 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
// Create a metadata record for a function. // Create a metadata record for a function.
fn make_metadata( fn make_metadata(
dict: &BTreeSet<ImmutableString>, dict: &BTreeSet<Identifier>,
namespace: Option<ImmutableString>, namespace: Option<Identifier>,
f: &ScriptFnDef, f: &ScriptFnDef,
) -> Map { ) -> Map {
let mut map = Map::new(); let mut map = Map::new();
if let Some(ns) = namespace { if let Some(ns) = namespace {
map.insert(dict.get("namespace").unwrap().clone(), ns.into()); map.insert(dict.get("namespace").unwrap().clone().into(), ns.into());
} }
map.insert(dict.get("name").unwrap().clone(), f.name.clone().into());
map.insert( map.insert(
dict.get("access").unwrap().clone(), dict.get("name").unwrap().clone().into(),
f.name.clone().into(),
);
map.insert(
dict.get("access").unwrap().clone().into(),
match f.access { match f.access {
FnAccess::Public => dict.get("public").unwrap().clone(), FnAccess::Public => dict.get("public").unwrap().clone(),
FnAccess::Private => dict.get("private").unwrap().clone(), FnAccess::Private => dict.get("private").unwrap().clone(),
@ -57,11 +60,11 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
.into(), .into(),
); );
map.insert( map.insert(
dict.get("is_anonymous").unwrap().clone(), dict.get("is_anonymous").unwrap().clone().into(),
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(), f.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
); );
map.insert( map.insert(
dict.get("params").unwrap().clone(), dict.get("params").unwrap().clone().into(),
f.params f.params
.iter() .iter()
.cloned() .cloned()
@ -74,7 +77,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
} }
// Intern strings // Intern strings
let dict: BTreeSet<ImmutableString> = [ let dict: BTreeSet<Identifier> = [
"namespace", "namespace",
"name", "name",
"access", "access",
@ -98,8 +101,8 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
// Recursively scan modules for script-defined functions. // Recursively scan modules for script-defined functions.
fn scan_module( fn scan_module(
list: &mut Array, list: &mut Array,
dict: &BTreeSet<ImmutableString>, dict: &BTreeSet<Identifier>,
namespace: ImmutableString, namespace: Identifier,
module: &Module, module: &Module,
) { ) {
module.iter_script_fn().for_each(|(_, _, _, _, f)| { module.iter_script_fn().for_each(|(_, _, _, _, f)| {

View File

@ -24,8 +24,8 @@ use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream}; use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
use crate::utils::{get_hasher, StringInterner}; use crate::utils::{get_hasher, StringInterner};
use crate::{ use crate::{
calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position, calc_fn_hash, Dynamic, Engine, Identifier, ImmutableString, LexError, ParseError,
Scope, Shared, StaticVec, AST, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
}; };
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -46,12 +46,12 @@ struct ParseState<'e> {
/// Interned strings. /// Interned strings.
interned_strings: StringInterner, interned_strings: StringInterner,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope. /// Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(ImmutableString, AccessMode)>, stack: Vec<(Identifier, AccessMode)>,
/// Size of the local variables stack upon entry of the current block scope. /// Size of the local variables stack upon entry of the current block scope.
entry_stack_len: usize, entry_stack_len: usize,
/// Tracks a list of external variables (variables that are not explicitly declared in the scope). /// Tracks a list of external variables (variables that are not explicitly declared in the scope).
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
external_vars: BTreeMap<ImmutableString, Position>, external_vars: BTreeMap<Identifier, Position>,
/// An indicator that disables variable capturing into externals one single time /// An indicator that disables variable capturing into externals one single time
/// up until the nearest consumed Identifier token. /// up until the nearest consumed Identifier token.
/// If set to false the next call to `access_var` will not capture the variable. /// If set to false the next call to `access_var` will not capture the variable.
@ -60,7 +60,7 @@ struct ParseState<'e> {
allow_capture: bool, allow_capture: bool,
/// Encapsulates a local stack with imported [module][crate::Module] names. /// Encapsulates a local stack with imported [module][crate::Module] names.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
modules: StaticVec<ImmutableString>, modules: StaticVec<Identifier>,
/// Maximum levels of expression nesting. /// Maximum levels of expression nesting.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
max_expr_depth: Option<NonZeroUsize>, max_expr_depth: Option<NonZeroUsize>,
@ -166,10 +166,7 @@ impl<'e> ParseState<'e> {
/// Get an interned string, creating one if it is not yet interned. /// Get an interned string, creating one if it is not yet interned.
#[inline(always)] #[inline(always)]
pub fn get_interned_string( pub fn get_interned_string(&mut self, text: impl AsRef<str>) -> Identifier {
&mut self,
text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString {
self.interned_strings.get(text) self.interned_strings.get(text)
} }
} }
@ -310,7 +307,7 @@ fn parse_fn_call(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
id: ImmutableString, id: Identifier,
capture: bool, capture: bool,
mut namespace: Option<NamespaceRef>, mut namespace: Option<NamespaceRef>,
settings: ParseSettings, settings: ParseSettings,
@ -668,6 +665,8 @@ fn parse_array_literal(
}; };
} }
arr.shrink_to_fit();
Ok(Expr::Array(Box::new(arr), settings.pos)) Ok(Expr::Array(Box::new(arr), settings.pos))
} }
@ -707,7 +706,7 @@ fn parse_map_literal(
let (name, pos) = match input.next().unwrap() { let (name, pos) = match input.next().unwrap() {
(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.name == s) {
return Err(PERR::DuplicatedProperty(s).into_err(pos)); return Err(PERR::DuplicatedProperty(s).into_err(pos));
} }
(s, pos) (s, pos)
@ -757,7 +756,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())?;
let name = state.get_interned_string(name); let name = state.get_interned_string(name);
template.insert(name.clone(), Default::default()); template.insert(name.clone().into(), Default::default());
map.push((Ident { name, pos }, expr)); map.push((Ident { name, pos }, expr));
match input.peek().unwrap() { match input.peek().unwrap() {
@ -782,6 +781,8 @@ fn parse_map_literal(
} }
} }
map.shrink_to_fit();
Ok(Expr::Map(Box::new((map, template)), settings.pos)) Ok(Expr::Map(Box::new((map, template)), settings.pos))
} }
@ -935,7 +936,7 @@ fn parse_primary(
Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos), Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos),
Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos),
Token::StringConstant(s) => { Token::StringConstant(s) => {
Expr::StringConstant(state.get_interned_string(s), settings.pos) Expr::StringConstant(state.get_interned_string(s).into(), settings.pos)
} }
Token::True => Expr::BoolConstant(true, settings.pos), Token::True => Expr::BoolConstant(true, settings.pos),
Token::False => Expr::BoolConstant(false, settings.pos), Token::False => Expr::BoolConstant(false, settings.pos),
@ -1830,7 +1831,7 @@ fn parse_custom_syntax(
MARKER_IDENT => match input.next().unwrap() { MARKER_IDENT => match input.next().unwrap() {
(Token::Identifier(s), pos) => { (Token::Identifier(s), pos) => {
let name = state.get_interned_string(s); let name = state.get_interned_string(s);
segments.push(name.clone()); segments.push(name.clone().into());
tokens.push(state.get_interned_string(MARKER_IDENT)); tokens.push(state.get_interned_string(MARKER_IDENT));
let var_name_def = Ident { name, pos }; let var_name_def = Ident { name, pos };
keywords.push(Expr::Variable(Box::new((None, None, var_name_def)))); keywords.push(Expr::Variable(Box::new((None, None, var_name_def))));
@ -1843,14 +1844,14 @@ fn parse_custom_syntax(
MARKER_EXPR => { MARKER_EXPR => {
keywords.push(parse_expr(input, state, lib, settings)?); keywords.push(parse_expr(input, state, lib, settings)?);
let keyword = state.get_interned_string(MARKER_EXPR); let keyword = state.get_interned_string(MARKER_EXPR);
segments.push(keyword.clone()); segments.push(keyword.clone().into());
tokens.push(keyword); tokens.push(keyword);
} }
MARKER_BLOCK => match parse_block(input, state, lib, settings)? { MARKER_BLOCK => match parse_block(input, state, lib, settings)? {
block @ Stmt::Block(_, _) => { block @ Stmt::Block(_, _) => {
keywords.push(Expr::Stmt(Box::new(block.into()))); keywords.push(Expr::Stmt(Box::new(block.into())));
let keyword = state.get_interned_string(MARKER_BLOCK); let keyword = state.get_interned_string(MARKER_BLOCK);
segments.push(keyword.clone()); segments.push(keyword.clone().into());
tokens.push(keyword); tokens.push(keyword);
} }
stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt), stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt),
@ -1859,7 +1860,7 @@ fn parse_custom_syntax(
(Token::LexError(err), pos) => return Err(err.into_err(pos)), (Token::LexError(err), pos) => return Err(err.into_err(pos)),
(t, _) if t.syntax().as_ref() == s => { (t, _) if t.syntax().as_ref() == s => {
segments.push(required_token.clone()); segments.push(required_token.clone());
tokens.push(required_token.clone()); tokens.push(required_token.clone().into());
} }
(_, pos) => { (_, pos) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
@ -1901,7 +1902,7 @@ fn parse_expr(
match token { match token {
Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => { Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => {
match state.engine.custom_syntax.get_key_value(key) { match state.engine.custom_syntax.get_key_value(key.as_str()) {
Some((key, syntax)) => { Some((key, syntax)) => {
input.next().unwrap(); input.next().unwrap();
return parse_custom_syntax( return parse_custom_syntax(
@ -2119,16 +2120,20 @@ fn parse_for(
ensure_not_statement_expr(input, "a boolean")?; ensure_not_statement_expr(input, "a boolean")?;
let expr = parse_expr(input, state, lib, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
let loop_var = state.get_interned_string(name.clone()); let loop_var = state.get_interned_string(name);
let prev_stack_len = state.stack.len(); let prev_stack_len = state.stack.len();
state.stack.push((loop_var, AccessMode::ReadWrite)); state.stack.push((loop_var.clone(), AccessMode::ReadWrite));
settings.is_breakable = true; settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?; let body = parse_block(input, state, lib, settings.level_up())?;
state.stack.truncate(prev_stack_len); state.stack.truncate(prev_stack_len);
Ok(Stmt::For(expr, Box::new((name, body.into())), settings.pos)) Ok(Stmt::For(
expr,
Box::new((loop_var, body.into())),
settings.pos,
))
} }
/// Parse a variable definition statement. /// Parse a variable definition statement.
@ -2174,9 +2179,9 @@ fn parse_let(
match var_type { match var_type {
// let name = expr // let name = expr
AccessMode::ReadWrite => Ok(Stmt::Let(expr, var_def, export, settings.pos)), AccessMode::ReadWrite => Ok(Stmt::Let(expr, var_def.into(), export, settings.pos)),
// const name = { expr:constant } // const name = { expr:constant }
AccessMode::ReadOnly => Ok(Stmt::Const(expr, var_def, export, settings.pos)), AccessMode::ReadOnly => Ok(Stmt::Const(expr, var_def.into(), export, settings.pos)),
} }
} }
@ -2217,10 +2222,13 @@ fn parse_import(
Ok(Stmt::Import( Ok(Stmt::Import(
expr, expr,
Some(Ident { Some(
Ident {
name, name,
pos: name_pos, pos: name_pos,
}), }
.into(),
),
settings.pos, settings.pos,
)) ))
} }
@ -2511,7 +2519,7 @@ fn parse_stmt(
if lib.contains_key(&hash) { if lib.contains_key(&hash) {
return Err(PERR::FnDuplicatedDefinition( return Err(PERR::FnDuplicatedDefinition(
func.name.into_owned(), func.name.to_string(),
func.params.len(), func.params.len(),
) )
.into_err(pos)); .into_err(pos));
@ -2904,7 +2912,7 @@ fn parse_anon_fn(
comments: Default::default(), comments: Default::default(),
}; };
let expr = Expr::FnPointer(fn_name, settings.pos); let expr = Expr::FnPointer(fn_name.into(), settings.pos);
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
let expr = make_curry_from_externals(state, expr, externals, settings.pos); let expr = make_curry_from_externals(state, expr, externals, settings.pos);

View File

@ -2,7 +2,7 @@
use crate::dynamic::{AccessMode, Variant}; use crate::dynamic::{AccessMode, Variant};
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec::Vec}; use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec::Vec};
use crate::{Dynamic, ImmutableString, StaticVec}; use crate::{Dynamic, Identifier, StaticVec};
/// Keep a number of entries inline (since [`Dynamic`] is usually small enough). /// Keep a number of entries inline (since [`Dynamic`] is usually small enough).
const SCOPE_SIZE: usize = 16; const SCOPE_SIZE: usize = 16;
@ -54,7 +54,7 @@ pub struct Scope<'a> {
/// Current value of the entry. /// Current value of the entry.
values: smallvec::SmallVec<[Dynamic; SCOPE_SIZE]>, values: smallvec::SmallVec<[Dynamic; SCOPE_SIZE]>,
/// (Name, aliases) of the entry. /// (Name, aliases) of the entry.
names: Vec<(Cow<'a, str>, Option<Box<StaticVec<ImmutableString>>>)>, names: Vec<(Cow<'a, str>, Option<Box<StaticVec<Identifier>>>)>,
} }
impl Default for Scope<'_> { impl Default for Scope<'_> {
@ -414,7 +414,7 @@ impl<'a> Scope<'a> {
pub(crate) fn add_entry_alias( pub(crate) fn add_entry_alias(
&mut self, &mut self,
index: usize, index: usize,
alias: impl Into<ImmutableString> + PartialEq<ImmutableString>, alias: impl Into<Identifier> + PartialEq<Identifier>,
) -> &mut Self { ) -> &mut Self {
let entry = self.names.get_mut(index).expect("invalid index in Scope"); let entry = self.names.get_mut(index).expect("invalid index in Scope");
if entry.1.is_none() { if entry.1.is_none() {
@ -449,7 +449,7 @@ impl<'a> Scope<'a> {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn into_iter( pub(crate) fn into_iter(
self, self,
) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<ImmutableString>)> { ) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<Identifier>)> {
self.names self.names
.into_iter() .into_iter()
.zip(self.values.into_iter()) .zip(self.values.into_iter())

View File

@ -6,7 +6,8 @@ use crate::fn_native::SendSync;
use crate::stdlib::{boxed::Box, format, string::ToString}; use crate::stdlib::{boxed::Box, format, string::ToString};
use crate::token::{is_valid_identifier, Token}; use crate::token::{is_valid_identifier, Token};
use crate::{ use crate::{
Engine, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared, StaticVec, Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared,
StaticVec,
}; };
pub const MARKER_EXPR: &str = "$expr$"; pub const MARKER_EXPR: &str = "$expr$";
@ -103,7 +104,7 @@ impl Engine {
/// ///
/// If `new_vars` is negative, then it is expected that only the top `new_vars` variables in the /// If `new_vars` is negative, then it is expected that only the top `new_vars` variables in the
/// current [`Scope`][crate::Scope] will be _popped_. Do not randomly remove variables. /// current [`Scope`][crate::Scope] will be _popped_. Do not randomly remove variables.
pub fn register_custom_syntax<S: AsRef<str> + Into<ImmutableString>>( pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
&mut self, &mut self,
keywords: &[S], keywords: &[S],
new_vars: isize, new_vars: isize,
@ -221,7 +222,7 @@ impl Engine {
/// Otherwise, custom keywords won't be recognized. /// Otherwise, custom keywords won't be recognized.
pub fn register_custom_syntax_raw( pub fn register_custom_syntax_raw(
&mut self, &mut self,
key: impl Into<ImmutableString>, key: impl Into<Identifier>,
parse: impl Fn(&[ImmutableString], &str) -> Result<Option<ImmutableString>, ParseError> parse: impl Fn(&[ImmutableString], &str) -> Result<Option<ImmutableString>, ParseError>
+ SendSync + SendSync
+ 'static, + 'static,

View File

@ -15,7 +15,7 @@ use crate::stdlib::{
str::FromStr, str::FromStr,
string::{String, ToString}, string::{String, ToString},
}; };
use crate::Shared; use crate::{Identifier, Shared};
/// A hasher that only takes one single [`u64`] and returns it as a hash key. /// A hasher that only takes one single [`u64`] and returns it as a hash key.
/// ///
@ -590,6 +590,20 @@ impl PartialOrd<ImmutableString> for String {
} }
} }
impl From<ImmutableString> for Identifier {
#[inline(always)]
fn from(value: ImmutableString) -> Self {
value.into_owned().into()
}
}
impl From<Identifier> for ImmutableString {
#[inline(always)]
fn from(value: Identifier) -> Self {
value.to_string().into()
}
}
impl ImmutableString { impl ImmutableString {
/// Consume the [`ImmutableString`] and convert it into a [`String`]. /// Consume the [`ImmutableString`] and convert it into a [`String`].
/// If there are other references to the same string, a cloned copy is returned. /// If there are other references to the same string, a cloned copy is returned.
@ -608,16 +622,12 @@ impl ImmutableString {
/// A collection of interned strings. /// A collection of interned strings.
#[derive(Debug, Clone, Default, Hash)] #[derive(Debug, Clone, Default, Hash)]
pub struct StringInterner(BTreeSet<ImmutableString>); pub struct StringInterner(BTreeSet<Identifier>);
impl StringInterner { impl StringInterner {
/// Get an interned string, creating one if it is not yet interned. /// Get an interned string, creating one if it is not yet interned.
#[inline(always)] #[inline(always)]
pub fn get(&mut self, text: impl AsRef<str> + Into<ImmutableString>) -> ImmutableString { pub fn get(&mut self, text: impl AsRef<str>) -> Identifier {
self.0.get(text.as_ref()).cloned().unwrap_or_else(|| { text.as_ref().into()
let s = text.into();
self.0.insert(s.clone());
s
})
} }
} }