Make file structures consistent.

This commit is contained in:
Stephen Chung 2021-06-16 18:36:33 +08:00
parent aa2e04bd25
commit 42a14ab4cc
13 changed files with 289 additions and 289 deletions

View File

@ -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,
)>,
),

View File

@ -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);
}
_ => (),
}

View File

@ -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
View 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
}

View File

@ -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
});
}
}

View File

@ -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::*;

View File

@ -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,

View File

@ -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,8 +798,6 @@ 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;
// Merge consecutive strings

View File

@ -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"))]

View File

@ -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 {
return Ok(FnCallExpr {
name: state.get_identifier(id),
capture,
namespace,
hashes,
args,
..Default::default()
}),
settings.pos,
));
}
.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 {
return Ok(FnCallExpr {
name: state.get_identifier(id),
capture,
namespace,
hashes,
args,
..Default::default()
}),
settings.pos,
));
}
.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 {
Ok(FnCallExpr {
name: state.get_identifier("-"),
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
args,
..Default::default()
}),
pos,
))
}
.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 {
Ok(FnCallExpr {
name: state.get_identifier("+"),
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
args,
..Default::default()
}),
pos,
))
}
.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 {
Ok(FnCallExpr {
name: state.get_identifier("!"),
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
args,
..Default::default()
}),
pos,
))
}
.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),
FnCallExpr {
hashes: FnCallHashes::from_script(calc_fn_hash(OP_CONTAINS, 2)),
args,
name: state.get_identifier(OP_CONTAINS),
..op_base
}),
pos,
)
}
.into_fn_call_expr(pos)
}
Token::Custom(s)
@ -1844,8 +1858,7 @@ fn parse_binary_op(
{
let hash = calc_fn_hash(&s, 2);
Expr::FnCall(
Box::new(FnCallExpr {
FnCallExpr {
hashes: if is_valid_identifier(s.chars()) {
FnCallHashes::from_script(hash)
} else {
@ -1853,9 +1866,8 @@ fn parse_binary_op(
},
args,
..op_base
}),
pos,
)
}
.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 {
Ok(CustomExpr {
keywords,
tokens,
scope_changed: syntax.scope_changed,
}),
pos,
))
}
.into_custom_syntax_expr(pos))
}
/// Parse an expression.
@ -2942,22 +2952,18 @@ 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.extend(
externals
.iter()
.cloned()
.map(|x| Expr::Variable(None, Position::NONE, Box::new((None, None, x)))),
);
args.shrink_to_fit();
let expr = Expr::FnCall(
Box::new(FnCallExpr {
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,
@ -2965,13 +2971,12 @@ fn make_curry_from_externals(
)),
args,
..Default::default()
}),
pos,
);
}
.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)))