Make file structures consistent.
This commit is contained in:
parent
aa2e04bd25
commit
42a14ab4cc
19
src/ast.rs
19
src/ast.rs
@ -1,10 +1,10 @@
|
||||
//! Module defining the AST (abstract syntax tree).
|
||||
|
||||
use crate::calc_fn_hash;
|
||||
use crate::dynamic::Union;
|
||||
use crate::fn_native::shared_make_mut;
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::token::Token;
|
||||
use crate::utils::calc_fn_hash;
|
||||
use crate::{
|
||||
Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT,
|
||||
};
|
||||
@ -1399,6 +1399,15 @@ pub struct CustomExpr {
|
||||
pub tokens: StaticVec<Identifier>,
|
||||
}
|
||||
|
||||
impl CustomExpr {
|
||||
/// Convert this into a [`Expr::Custom`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_custom_syntax_expr(self, pos: Position) -> Expr {
|
||||
Expr::Custom(self.into(), pos)
|
||||
}
|
||||
}
|
||||
|
||||
/// _(INTERNALS)_ A binary expression.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
@ -1564,6 +1573,12 @@ impl FnCallExpr {
|
||||
pub fn is_qualified(&self) -> bool {
|
||||
self.namespace.is_some()
|
||||
}
|
||||
/// Convert this into a [`FnCall`][Expr::FnCall].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_fn_call_expr(self, pos: Position) -> Expr {
|
||||
Expr::FnCall(self.into(), pos)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that wraps a floating-point number and implements [`Hash`].
|
||||
@ -1730,7 +1745,7 @@ pub enum Expr {
|
||||
Position,
|
||||
Box<(
|
||||
Option<NonZeroUsize>,
|
||||
Option<(u64, NamespaceRef)>,
|
||||
Option<(NamespaceRef, u64)>,
|
||||
Identifier,
|
||||
)>,
|
||||
),
|
||||
|
@ -1,7 +1,9 @@
|
||||
//! Main module defining the script evaluation [`Engine`].
|
||||
|
||||
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt};
|
||||
use crate::custom_syntax::CustomSyntax;
|
||||
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||
use crate::fn_hash::get_hasher;
|
||||
use crate::fn_native::{
|
||||
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnVarCallback,
|
||||
};
|
||||
@ -9,9 +11,7 @@ use crate::module::NamespaceRef;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::packages::{Package, StandardPackage};
|
||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||
use crate::syntax::CustomSyntax;
|
||||
use crate::token::Token;
|
||||
use crate::utils::get_hasher;
|
||||
use crate::{
|
||||
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
|
||||
Shared, StaticVec, INT,
|
||||
@ -1102,17 +1102,17 @@ impl Engine {
|
||||
// Normal variable access
|
||||
(_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
|
||||
// Qualified variable
|
||||
(_, Some((hash_var, modules)), var_name) => {
|
||||
let module = self.search_imports(mods, state, modules).ok_or_else(|| {
|
||||
(_, Some((namespace, hash_var)), var_name) => {
|
||||
let module = self.search_imports(mods, state, namespace).ok_or_else(|| {
|
||||
EvalAltResult::ErrorModuleNotFound(
|
||||
modules[0].name.to_string(),
|
||||
modules[0].pos,
|
||||
namespace[0].name.to_string(),
|
||||
namespace[0].pos,
|
||||
)
|
||||
})?;
|
||||
let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
|
||||
match *err {
|
||||
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
|
||||
*err_name = format!("{}{}", modules, var_name);
|
||||
*err_name = format!("{}{}", namespace, var_name);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use crate::engine::{EvalContext, Imports, State};
|
||||
use crate::fn_native::{FnCallArgs, SendSync};
|
||||
use crate::fn_register::RegisterNativeFunction;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::parser::ParseState;
|
||||
use crate::parse::ParseState;
|
||||
use crate::{
|
||||
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module,
|
||||
NativeCallContext, ParseError, Position, RhaiResult, Shared, AST,
|
||||
|
113
src/fn_hash.rs
Normal file
113
src/fn_hash.rs
Normal file
@ -0,0 +1,113 @@
|
||||
//! Module containing utilities to hash functions and function calls.
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
hash::{BuildHasher, Hash, Hasher},
|
||||
iter::empty,
|
||||
};
|
||||
|
||||
/// A hasher that only takes one single [`u64`] and returns it as a hash key.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when hashing any data type other than a [`u64`].
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct StraightHasher(u64);
|
||||
|
||||
impl Hasher for StraightHasher {
|
||||
#[inline(always)]
|
||||
fn finish(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
#[inline(always)]
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values");
|
||||
|
||||
let mut key = [0_u8; 8];
|
||||
key.copy_from_slice(bytes);
|
||||
|
||||
self.0 = u64::from_ne_bytes(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// A hash builder for `StraightHasher`.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||
pub struct StraightHasherBuilder;
|
||||
|
||||
impl BuildHasher for StraightHasherBuilder {
|
||||
type Hasher = StraightHasher;
|
||||
|
||||
#[inline(always)]
|
||||
fn build_hasher(&self) -> Self::Hasher {
|
||||
StraightHasher(42)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an instance of the default hasher.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get_hasher() -> ahash::AHasher {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Calculate a [`u64`] hash key from a namespace-qualified function name
|
||||
/// and the number of parameters, but no parameter types.
|
||||
///
|
||||
/// Module names are passed in via `&str` references from an iterator.
|
||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn calc_qualified_fn_hash<'a>(
|
||||
modules: impl Iterator<Item = &'a str>,
|
||||
fn_name: impl AsRef<str>,
|
||||
num: usize,
|
||||
) -> u64 {
|
||||
let s = &mut get_hasher();
|
||||
|
||||
// We always skip the first module
|
||||
let mut len = 0;
|
||||
modules
|
||||
.inspect(|_| len += 1)
|
||||
.skip(1)
|
||||
.for_each(|m| m.hash(s));
|
||||
len.hash(s);
|
||||
fn_name.as_ref().hash(s);
|
||||
num.hash(s);
|
||||
s.finish()
|
||||
}
|
||||
|
||||
/// Calculate a [`u64`] hash key from a non-namespace-qualified function name
|
||||
/// and the number of parameters, but no parameter types.
|
||||
///
|
||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
|
||||
calc_qualified_fn_hash(empty(), fn_name, num)
|
||||
}
|
||||
|
||||
/// Calculate a [`u64`] hash key from a list of parameter types.
|
||||
///
|
||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
||||
let s = &mut get_hasher();
|
||||
let mut len = 0;
|
||||
params.inspect(|_| len += 1).for_each(|t| t.hash(s));
|
||||
len.hash(s);
|
||||
s.finish()
|
||||
}
|
||||
|
||||
/// Combine two [`u64`] hashes by taking the XOR of them.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
|
||||
a ^ b
|
||||
}
|
@ -1,124 +1,19 @@
|
||||
//! Module containing various utility types and functions.
|
||||
//! The `ImmutableString` type.
|
||||
|
||||
use crate::fn_native::{shared_make_mut, shared_take};
|
||||
use crate::{Identifier, Shared, SmartString};
|
||||
use crate::{Shared, SmartString};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
borrow::Borrow,
|
||||
cmp::Ordering,
|
||||
fmt,
|
||||
hash::{BuildHasher, Hash, Hasher},
|
||||
iter::{empty, FromIterator},
|
||||
hash::Hash,
|
||||
iter::FromIterator,
|
||||
ops::{Add, AddAssign, Deref, Sub, SubAssign},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
/// A hasher that only takes one single [`u64`] and returns it as a hash key.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when hashing any data type other than a [`u64`].
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct StraightHasher(u64);
|
||||
|
||||
impl Hasher for StraightHasher {
|
||||
#[inline(always)]
|
||||
fn finish(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
#[inline(always)]
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values");
|
||||
|
||||
let mut key = [0_u8; 8];
|
||||
key.copy_from_slice(bytes);
|
||||
|
||||
self.0 = u64::from_ne_bytes(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// A hash builder for `StraightHasher`.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||
pub struct StraightHasherBuilder;
|
||||
|
||||
impl BuildHasher for StraightHasherBuilder {
|
||||
type Hasher = StraightHasher;
|
||||
|
||||
#[inline(always)]
|
||||
fn build_hasher(&self) -> Self::Hasher {
|
||||
StraightHasher(42)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an instance of the default hasher.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get_hasher() -> ahash::AHasher {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Calculate a [`u64`] hash key from a namespace-qualified function name
|
||||
/// and the number of parameters, but no parameter types.
|
||||
///
|
||||
/// Module names are passed in via `&str` references from an iterator.
|
||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn calc_qualified_fn_hash<'a>(
|
||||
modules: impl Iterator<Item = &'a str>,
|
||||
fn_name: impl AsRef<str>,
|
||||
num: usize,
|
||||
) -> u64 {
|
||||
let s = &mut get_hasher();
|
||||
|
||||
// We always skip the first module
|
||||
let mut len = 0;
|
||||
modules
|
||||
.inspect(|_| len += 1)
|
||||
.skip(1)
|
||||
.for_each(|m| m.hash(s));
|
||||
len.hash(s);
|
||||
fn_name.as_ref().hash(s);
|
||||
num.hash(s);
|
||||
s.finish()
|
||||
}
|
||||
|
||||
/// Calculate a [`u64`] hash key from a non-namespace-qualified function name
|
||||
/// and the number of parameters, but no parameter types.
|
||||
///
|
||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
|
||||
calc_qualified_fn_hash(empty(), fn_name, num)
|
||||
}
|
||||
|
||||
/// Calculate a [`u64`] hash key from a list of parameter types.
|
||||
///
|
||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
||||
let s = &mut get_hasher();
|
||||
let mut len = 0;
|
||||
params.inspect(|_| len += 1).for_each(|t| t.hash(s));
|
||||
len.hash(s);
|
||||
s.finish()
|
||||
}
|
||||
|
||||
/// Combine two [`u64`] hashes by taking the XOR of them.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
|
||||
a ^ b
|
||||
}
|
||||
|
||||
/// The system immutable string type.
|
||||
///
|
||||
/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>`
|
||||
@ -628,33 +523,3 @@ impl ImmutableString {
|
||||
shared_make_mut(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A factory of identifiers from text strings.
|
||||
///
|
||||
/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`],
|
||||
/// this just returns one because most identifiers in Rhai are short and ASCII-based.
|
||||
///
|
||||
/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a
|
||||
/// collection of strings and returns shared instances, only creating a new string when it is not
|
||||
/// yet interned.
|
||||
#[derive(Debug, Clone, Default, Hash)]
|
||||
pub struct IdentifierBuilder(
|
||||
#[cfg(feature = "no_smartstring")] std::collections::BTreeSet<Identifier>,
|
||||
);
|
||||
|
||||
impl IdentifierBuilder {
|
||||
/// Get an identifier from a text string.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
return text.as_ref().into();
|
||||
|
||||
#[cfg(feature = "no_smartstring")]
|
||||
return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
|
||||
let s: Identifier = text.into();
|
||||
self.0.insert(s.clone());
|
||||
s
|
||||
});
|
||||
}
|
||||
}
|
23
src/lib.rs
23
src/lib.rs
@ -70,28 +70,29 @@ use std::prelude::v1::*;
|
||||
// Internal modules
|
||||
|
||||
mod ast;
|
||||
mod custom_syntax;
|
||||
mod dynamic;
|
||||
mod engine;
|
||||
mod engine_api;
|
||||
mod engine_settings;
|
||||
mod error;
|
||||
mod error_parsing;
|
||||
mod fn_args;
|
||||
mod fn_builtin;
|
||||
mod fn_call;
|
||||
mod fn_func;
|
||||
mod fn_hash;
|
||||
mod fn_native;
|
||||
mod fn_register;
|
||||
mod immutable_string;
|
||||
mod module;
|
||||
mod optimize;
|
||||
pub mod packages;
|
||||
mod parse_error;
|
||||
mod parser;
|
||||
mod parse;
|
||||
pub mod plugin;
|
||||
mod result;
|
||||
mod scope;
|
||||
mod syntax;
|
||||
mod token;
|
||||
mod r#unsafe;
|
||||
mod utils;
|
||||
|
||||
type RhaiResult = Result<Dynamic, Box<EvalAltResult>>;
|
||||
|
||||
@ -126,17 +127,17 @@ pub type FLOAT = f64;
|
||||
pub type FLOAT = f32;
|
||||
|
||||
pub use ast::{FnAccess, AST};
|
||||
pub use custom_syntax::Expression;
|
||||
pub use dynamic::Dynamic;
|
||||
pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS};
|
||||
pub use error::EvalAltResult;
|
||||
pub use error_parsing::{LexError, ParseError, ParseErrorType};
|
||||
pub use fn_native::{FnPtr, NativeCallContext};
|
||||
pub use fn_register::RegisterNativeFunction;
|
||||
pub use immutable_string::ImmutableString;
|
||||
pub use module::{FnNamespace, Module};
|
||||
pub use parse_error::{LexError, ParseError, ParseErrorType};
|
||||
pub use result::EvalAltResult;
|
||||
pub use scope::Scope;
|
||||
pub use syntax::Expression;
|
||||
pub use token::Position;
|
||||
pub use utils::ImmutableString;
|
||||
|
||||
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
||||
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
||||
@ -169,7 +170,9 @@ pub use fn_native::Shared;
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
use fn_native::Locked;
|
||||
|
||||
pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes};
|
||||
pub(crate) use fn_hash::{
|
||||
calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes,
|
||||
};
|
||||
|
||||
pub use rhai_codegen::*;
|
||||
|
||||
|
@ -4,8 +4,8 @@ use crate::ast::{FnAccess, Ident};
|
||||
use crate::dynamic::Variant;
|
||||
use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync};
|
||||
use crate::fn_register::RegisterNativeFunction;
|
||||
use crate::parse::IdentifierBuilder;
|
||||
use crate::token::Token;
|
||||
use crate::utils::IdentifierBuilder;
|
||||
use crate::{
|
||||
calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult,
|
||||
Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec,
|
||||
|
@ -4,8 +4,8 @@ use crate::ast::{Expr, OpAssignment, Stmt};
|
||||
use crate::dynamic::AccessMode;
|
||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||
use crate::fn_builtin::get_builtin_binary_op_fn;
|
||||
use crate::fn_hash::get_hasher;
|
||||
use crate::token::Token;
|
||||
use crate::utils::get_hasher;
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString,
|
||||
Module, Position, Scope, StaticVec, AST,
|
||||
@ -729,7 +729,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
||||
// Array literal where everything is pure - promote the indexed item.
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
let mut result = a.remove(*i as usize);
|
||||
let mut result = mem::take(&mut a[*i as usize]);
|
||||
result.set_position(*pos);
|
||||
*expr = result;
|
||||
}
|
||||
@ -740,7 +740,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
||||
// Array literal where everything is pure - promote the indexed item.
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
let mut result = a.remove(a.len() - i.abs() as usize);
|
||||
let index = a.len() - i.abs() as usize;
|
||||
let mut result = mem::take(&mut a[index]);
|
||||
result.set_position(*pos);
|
||||
*expr = result;
|
||||
}
|
||||
@ -797,9 +798,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
||||
}
|
||||
// `... ${ ... } ...`
|
||||
Expr::InterpolatedString(x) => {
|
||||
x.iter_mut().for_each(|expr| optimize_expr(expr, state, false));
|
||||
|
||||
let mut n= 0;
|
||||
let mut n = 0;
|
||||
|
||||
// Merge consecutive strings
|
||||
while n < x.len()-1 {
|
||||
|
@ -9,7 +9,7 @@ use std::prelude::v1::*;
|
||||
use crate::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::error::EvalAltResult;
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
|
@ -4,19 +4,18 @@ use crate::ast::{
|
||||
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType,
|
||||
ScriptFnDef, Stmt, StmtBlock,
|
||||
};
|
||||
use crate::custom_syntax::{
|
||||
CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING,
|
||||
};
|
||||
use crate::dynamic::{AccessMode, Union};
|
||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::optimize::optimize_into_ast;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::syntax::{
|
||||
CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING,
|
||||
};
|
||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||
|
||||
use crate::fn_hash::get_hasher;
|
||||
use crate::token::{
|
||||
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
|
||||
};
|
||||
use crate::utils::{get_hasher, IdentifierBuilder};
|
||||
use crate::{
|
||||
calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError,
|
||||
ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
||||
@ -30,7 +29,7 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::{syntax::MARKER_FLOAT, FLOAT};
|
||||
use crate::{custom_syntax::MARKER_FLOAT, FLOAT};
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
use crate::FnAccess;
|
||||
@ -41,6 +40,36 @@ type FunctionsLib = BTreeMap<u64, Shared<ScriptFnDef>>;
|
||||
|
||||
const NEVER_ENDS: &str = "never fails because `TokenStream` never ends";
|
||||
|
||||
/// A factory of identifiers from text strings.
|
||||
///
|
||||
/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`],
|
||||
/// this just returns a copy because most identifiers in Rhai are short and ASCII-based.
|
||||
///
|
||||
/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a
|
||||
/// collection of strings and returns shared instances, only creating a new string when it is not
|
||||
/// yet interned.
|
||||
#[derive(Debug, Clone, Default, Hash)]
|
||||
pub struct IdentifierBuilder(
|
||||
#[cfg(feature = "no_smartstring")] std::collections::BTreeSet<Identifier>,
|
||||
);
|
||||
|
||||
impl IdentifierBuilder {
|
||||
/// Get an identifier from a text string.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
return text.into();
|
||||
|
||||
#[cfg(feature = "no_smartstring")]
|
||||
return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
|
||||
let s: Identifier = text.into();
|
||||
self.0.insert(s.clone());
|
||||
s
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that encapsulates the current state of the parser.
|
||||
#[derive(Debug)]
|
||||
pub struct ParseState<'e> {
|
||||
@ -372,17 +401,15 @@ fn parse_fn_call(
|
||||
|
||||
args.shrink_to_fit();
|
||||
|
||||
return Ok(Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: state.get_identifier(id),
|
||||
capture,
|
||||
namespace,
|
||||
hashes,
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
settings.pos,
|
||||
));
|
||||
return Ok(FnCallExpr {
|
||||
name: state.get_identifier(id),
|
||||
capture,
|
||||
namespace,
|
||||
hashes,
|
||||
args,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(settings.pos));
|
||||
}
|
||||
// id...
|
||||
_ => (),
|
||||
@ -424,17 +451,15 @@ fn parse_fn_call(
|
||||
|
||||
args.shrink_to_fit();
|
||||
|
||||
return Ok(Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: state.get_identifier(id),
|
||||
capture,
|
||||
namespace,
|
||||
hashes,
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
settings.pos,
|
||||
));
|
||||
return Ok(FnCallExpr {
|
||||
name: state.get_identifier(id),
|
||||
capture,
|
||||
namespace,
|
||||
hashes,
|
||||
args,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(settings.pos));
|
||||
}
|
||||
// id(...args,
|
||||
(Token::Comma, _) => {
|
||||
@ -692,7 +717,7 @@ fn parse_array_literal(
|
||||
|
||||
arr.shrink_to_fit();
|
||||
|
||||
Ok(Expr::Array(Box::new(arr), settings.pos))
|
||||
Ok(Expr::Array(arr.into(), settings.pos))
|
||||
}
|
||||
|
||||
/// Parse a map literal.
|
||||
@ -1101,7 +1126,7 @@ fn parse_primary(
|
||||
}
|
||||
|
||||
segments.shrink_to_fit();
|
||||
Expr::InterpolatedString(Box::new(segments))
|
||||
Expr::InterpolatedString(segments.into())
|
||||
}
|
||||
|
||||
// Array literal
|
||||
@ -1245,14 +1270,14 @@ fn parse_primary(
|
||||
|
||||
let (_, namespace, name) = *x;
|
||||
settings.pos = var_pos;
|
||||
let ns = namespace.map(|(_, ns)| ns);
|
||||
let ns = namespace.map(|(ns, _)| ns);
|
||||
parse_fn_call(input, state, lib, name, true, ns, settings.level_up())?
|
||||
}
|
||||
// Function call
|
||||
(Expr::Variable(_, var_pos, x), Token::LeftParen) => {
|
||||
let (_, namespace, name) = *x;
|
||||
settings.pos = var_pos;
|
||||
let ns = namespace.map(|(_, ns)| ns);
|
||||
let ns = namespace.map(|(ns, _)| ns);
|
||||
parse_fn_call(input, state, lib, name, false, ns, settings.level_up())?
|
||||
}
|
||||
// module access
|
||||
@ -1264,12 +1289,12 @@ fn parse_primary(
|
||||
pos: var_pos,
|
||||
};
|
||||
|
||||
if let Some((_, ref mut namespace)) = namespace {
|
||||
if let Some((ref mut namespace, _)) = namespace {
|
||||
namespace.push(var_name_def);
|
||||
} else {
|
||||
let mut ns: NamespaceRef = Default::default();
|
||||
ns.push(var_name_def);
|
||||
namespace = Some((42, ns));
|
||||
namespace = Some((ns, 42));
|
||||
}
|
||||
|
||||
Expr::Variable(
|
||||
@ -1322,7 +1347,7 @@ fn parse_primary(
|
||||
_ => None,
|
||||
}
|
||||
.map(|x| match x {
|
||||
(_, Some((hash, namespace)), name) => {
|
||||
(_, Some((namespace, hash)), name) => {
|
||||
*hash = calc_qualified_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@ -1376,15 +1401,13 @@ fn parse_unary(
|
||||
args.push(expr);
|
||||
args.shrink_to_fit();
|
||||
|
||||
Ok(Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: state.get_identifier("-"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
pos,
|
||||
))
|
||||
Ok(FnCallExpr {
|
||||
name: state.get_identifier("-"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1403,15 +1426,13 @@ fn parse_unary(
|
||||
args.push(expr);
|
||||
args.shrink_to_fit();
|
||||
|
||||
Ok(Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: state.get_identifier("+"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
pos,
|
||||
))
|
||||
Ok(FnCallExpr {
|
||||
name: state.get_identifier("+"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1422,15 +1443,13 @@ fn parse_unary(
|
||||
args.push(parse_unary(input, state, lib, settings.level_up())?);
|
||||
args.shrink_to_fit();
|
||||
|
||||
Ok(Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: state.get_identifier("!"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
pos,
|
||||
))
|
||||
Ok(FnCallExpr {
|
||||
name: state.get_identifier("!"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
|
||||
args,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
}
|
||||
// <EOF>
|
||||
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
||||
@ -1587,7 +1606,7 @@ fn make_dot_expr(
|
||||
// lhs.module::id - syntax error
|
||||
(_, Expr::Variable(_, _, x)) if x.1.is_some() => {
|
||||
return Err(PERR::PropertyExpected
|
||||
.into_err(x.1.expect("never fails because the namespace is `Some`").1[0].pos))
|
||||
.into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos))
|
||||
}
|
||||
// lhs.prop
|
||||
(lhs, prop @ Expr::Property(_)) => {
|
||||
@ -1772,19 +1791,17 @@ fn parse_binary_op(
|
||||
| Token::PowerOf
|
||||
| Token::Ampersand
|
||||
| Token::Pipe
|
||||
| Token::XOr => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos),
|
||||
| Token::XOr => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
||||
|
||||
// '!=' defaults to true when passed invalid operands
|
||||
Token::NotEqualsTo => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos),
|
||||
Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
||||
|
||||
// Comparison operators default to false when passed invalid operands
|
||||
Token::EqualsTo
|
||||
| Token::LessThan
|
||||
| Token::LessThanEqualsTo
|
||||
| Token::GreaterThan
|
||||
| Token::GreaterThanEqualsTo => {
|
||||
Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos)
|
||||
}
|
||||
| Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
||||
|
||||
Token::Or => {
|
||||
let rhs = args
|
||||
@ -1823,16 +1840,13 @@ fn parse_binary_op(
|
||||
args.shrink_to_fit();
|
||||
|
||||
// Convert into a call to `contains`
|
||||
let hash = calc_fn_hash(OP_CONTAINS, 2);
|
||||
Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
hashes: FnCallHashes::from_script(hash),
|
||||
args,
|
||||
name: state.get_identifier(OP_CONTAINS),
|
||||
..op_base
|
||||
}),
|
||||
pos,
|
||||
)
|
||||
FnCallExpr {
|
||||
hashes: FnCallHashes::from_script(calc_fn_hash(OP_CONTAINS, 2)),
|
||||
args,
|
||||
name: state.get_identifier(OP_CONTAINS),
|
||||
..op_base
|
||||
}
|
||||
.into_fn_call_expr(pos)
|
||||
}
|
||||
|
||||
Token::Custom(s)
|
||||
@ -1844,18 +1858,16 @@ fn parse_binary_op(
|
||||
{
|
||||
let hash = calc_fn_hash(&s, 2);
|
||||
|
||||
Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
hashes: if is_valid_identifier(s.chars()) {
|
||||
FnCallHashes::from_script(hash)
|
||||
} else {
|
||||
FnCallHashes::from_native(hash)
|
||||
},
|
||||
args,
|
||||
..op_base
|
||||
}),
|
||||
pos,
|
||||
)
|
||||
FnCallExpr {
|
||||
hashes: if is_valid_identifier(s.chars()) {
|
||||
FnCallHashes::from_script(hash)
|
||||
} else {
|
||||
FnCallHashes::from_native(hash)
|
||||
},
|
||||
args,
|
||||
..op_base
|
||||
}
|
||||
.into_fn_call_expr(pos)
|
||||
}
|
||||
|
||||
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)),
|
||||
@ -1999,14 +2011,12 @@ fn parse_custom_syntax(
|
||||
keywords.shrink_to_fit();
|
||||
tokens.shrink_to_fit();
|
||||
|
||||
Ok(Expr::Custom(
|
||||
Box::new(CustomExpr {
|
||||
keywords,
|
||||
tokens,
|
||||
scope_changed: syntax.scope_changed,
|
||||
}),
|
||||
pos,
|
||||
))
|
||||
Ok(CustomExpr {
|
||||
keywords,
|
||||
tokens,
|
||||
scope_changed: syntax.scope_changed,
|
||||
}
|
||||
.into_custom_syntax_expr(pos))
|
||||
}
|
||||
|
||||
/// Parse an expression.
|
||||
@ -2942,36 +2952,31 @@ fn make_curry_from_externals(
|
||||
}
|
||||
|
||||
let num_externals = externals.len();
|
||||
let mut args: StaticVec<_> = Default::default();
|
||||
let mut args = StaticVec::with_capacity(externals.len() + 1);
|
||||
|
||||
args.push(fn_expr);
|
||||
|
||||
externals.iter().for_each(|x| {
|
||||
args.push(Expr::Variable(
|
||||
None,
|
||||
Position::NONE,
|
||||
Box::new((None, None, x.clone())),
|
||||
));
|
||||
});
|
||||
|
||||
args.shrink_to_fit();
|
||||
|
||||
let expr = Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash(
|
||||
crate::engine::KEYWORD_FN_PTR_CURRY,
|
||||
num_externals + 1,
|
||||
)),
|
||||
args,
|
||||
..Default::default()
|
||||
}),
|
||||
pos,
|
||||
args.extend(
|
||||
externals
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|x| Expr::Variable(None, Position::NONE, Box::new((None, None, x)))),
|
||||
);
|
||||
|
||||
let expr = FnCallExpr {
|
||||
name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash(
|
||||
crate::engine::KEYWORD_FN_PTR_CURRY,
|
||||
num_externals + 1,
|
||||
)),
|
||||
args,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos);
|
||||
|
||||
// Convert the entire expression into a statement block, then insert the relevant
|
||||
// [`Share`][Stmt::Share] statements.
|
||||
let mut statements: StaticVec<_> = Default::default();
|
||||
let mut statements = StaticVec::with_capacity(externals.len() + 1);
|
||||
statements.extend(externals.into_iter().map(Stmt::Share));
|
||||
statements.push(Stmt::Expr(expr));
|
||||
Expr::Stmt(Box::new(StmtBlock::new(statements, pos)))
|
Loading…
Reference in New Issue
Block a user