Code cleanup and refactor.
This commit is contained in:
parent
29a397b216
commit
0c85f0c796
@ -43,7 +43,7 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
[features]
|
||||
default = ["std", "ahash/runtime-rng"] # ahash/runtime-rng trumps ahash/compile-time-rng
|
||||
std = ["ahash/std", "num-traits/std", "smartstring/std"]
|
||||
unchecked = [] # unchecked arithmetic
|
||||
unchecked = [] # disable safety checks
|
||||
sync = [] # restrict to only types that implement Send + Sync
|
||||
no_position = [] # do not track position in the parser
|
||||
no_optimize = [] # no script optimizer
|
||||
|
@ -4,7 +4,7 @@
|
||||
use crate::ast::Expr;
|
||||
use crate::func::SendSync;
|
||||
use crate::parser::ParseResult;
|
||||
use crate::tokenizer::{is_valid_identifier, Token};
|
||||
use crate::tokenizer::{is_valid_identifier, Token, NO_TOKEN};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
reify, Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position,
|
||||
@ -166,6 +166,7 @@ impl Deref for Expression<'_> {
|
||||
type Target = Expr;
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
@ -230,11 +231,11 @@ impl Engine {
|
||||
continue;
|
||||
}
|
||||
|
||||
let token = Token::lookup_symbol_from_syntax(s).or_else(|| {
|
||||
let token = Token::lookup_symbol_from_syntax(s).unwrap_or_else(|| {
|
||||
if Token::is_reserved_keyword(s) {
|
||||
Some(Token::Reserved(Box::new(s.into())))
|
||||
Token::Reserved(Box::new(s.into()))
|
||||
} else {
|
||||
None
|
||||
NO_TOKEN
|
||||
}
|
||||
});
|
||||
|
||||
@ -255,16 +256,16 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||
// Standard or reserved keyword/symbol not in first position
|
||||
_ if !segments.is_empty() && token.is_some() => {
|
||||
_ if !segments.is_empty() && token != NO_TOKEN => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if (self
|
||||
.disabled_symbols
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(s))
|
||||
|| token.map_or(false, |v| v.is_reserved()))
|
||||
|| token.is_reserved())
|
||||
&& !self
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains_key(s))
|
||||
{
|
||||
self.custom_keywords
|
||||
@ -275,10 +276,10 @@ impl Engine {
|
||||
}
|
||||
// Standard keyword in first position but not disabled
|
||||
_ if segments.is_empty()
|
||||
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
||||
&& token.is_standard_keyword()
|
||||
&& !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(s)) =>
|
||||
{
|
||||
return Err(LexError::ImproperSymbol(
|
||||
@ -295,12 +296,12 @@ impl Engine {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(s))
|
||||
|| (token.map_or(false, |v| v.is_reserved())
|
||||
|| (token.is_reserved()
|
||||
&& !self
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains_key(s)))
|
||||
{
|
||||
self.custom_keywords
|
||||
|
@ -461,7 +461,7 @@ impl Module {
|
||||
|| def
|
||||
.engine
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
|
||||
|
||||
f.write_definition(writer, def, operator)?;
|
||||
|
@ -64,8 +64,8 @@ impl Engine {
|
||||
|
||||
let (stream, tokenizer_control) = self.lex_raw(
|
||||
&scripts,
|
||||
if has_null {
|
||||
Some(&|token, _, _| {
|
||||
Some(if has_null {
|
||||
&|token, _, _| {
|
||||
match token {
|
||||
// `null` => `()`
|
||||
Token::Reserved(s) if &*s == "null" => Token::Unit,
|
||||
@ -86,9 +86,9 @@ impl Engine {
|
||||
// All others
|
||||
_ => token,
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Some(&|token, _, _| {
|
||||
&|token, _, _| {
|
||||
match token {
|
||||
Token::Reserved(s) if &*s == "null" => Token::LexError(
|
||||
LexError::ImproperSymbol("null".to_string(), String::new()).into(),
|
||||
@ -97,24 +97,21 @@ impl Engine {
|
||||
Token::LeftBrace => Token::MapStart,
|
||||
// Disallowed syntax
|
||||
t @ (Token::Unit | Token::MapStart) => Token::LexError(
|
||||
LexError::ImproperSymbol(
|
||||
t.literal_syntax().to_string(),
|
||||
"Invalid JSON syntax".to_string(),
|
||||
)
|
||||
LexError::ImproperSymbol(t.literal_syntax().to_string(), String::new())
|
||||
.into(),
|
||||
),
|
||||
Token::InterpolatedString(..) => Token::LexError(
|
||||
LexError::ImproperSymbol(
|
||||
"interpolated string".to_string(),
|
||||
"Invalid JSON syntax".to_string(),
|
||||
String::new(),
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
// All others
|
||||
_ => token,
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let ast = {
|
||||
|
70
src/api/limits_unchecked.rs
Normal file
70
src/api/limits_unchecked.rs
Normal file
@ -0,0 +1,70 @@
|
||||
#![cfg(feature = "unchecked")]
|
||||
|
||||
use crate::Engine;
|
||||
|
||||
impl Engine {
|
||||
/// The maximum levels of function calls allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`] under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_call_levels(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The maximum number of operations allowed for a script to run (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_operations(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
/// The maximum number of imported [modules][crate::Module] allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`] under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_modules(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The depth limit for expressions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The depth limit for expressions in functions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_function_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_string_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_array_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_map_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
@ -19,9 +19,21 @@ pub mod options;
|
||||
pub mod optimize;
|
||||
|
||||
pub mod limits;
|
||||
pub mod limits_unchecked;
|
||||
|
||||
pub mod events;
|
||||
|
||||
pub mod type_names;
|
||||
|
||||
pub mod custom_syntax;
|
||||
|
||||
pub mod build_type;
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
pub mod definitions;
|
||||
|
||||
pub mod deprecated;
|
||||
|
||||
use crate::{Dynamic, Engine, Identifier};
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -156,7 +168,7 @@ impl Engine {
|
||||
Some(token) if token.is_standard_keyword() => {
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved keyword"));
|
||||
@ -166,7 +178,7 @@ impl Engine {
|
||||
Some(token) if token.is_standard_symbol() => {
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved operator"));
|
||||
@ -176,7 +188,7 @@ impl Engine {
|
||||
Some(token)
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax())) =>
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved symbol"))
|
||||
@ -212,82 +224,3 @@ impl Engine {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
impl Engine {
|
||||
/// The maximum levels of function calls allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`] under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_call_levels(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The maximum number of operations allowed for a script to run (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_operations(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
/// The maximum number of imported [modules][crate::Module] allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`] under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_modules(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The depth limit for expressions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The depth limit for expressions in functions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_function_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_string_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_array_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_map_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub mod type_names;
|
||||
|
||||
pub mod custom_syntax;
|
||||
|
||||
pub mod build_type;
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
pub mod definitions;
|
||||
|
||||
pub mod deprecated;
|
||||
|
@ -57,7 +57,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
ast.shared_lib()
|
||||
.iter_fn()
|
||||
.map(|f| f.func.get_script_fn_def().expect("`ScriptFnDef").clone())
|
||||
.map(|f| f.func.get_script_fn_def().cloned().expect("`ScriptFnDef"))
|
||||
.collect(),
|
||||
optimization_level,
|
||||
);
|
||||
|
@ -1,9 +1,9 @@
|
||||
//! Module defining script expressions.
|
||||
|
||||
use super::{ASTFlags, ASTNode, Ident, Stmt, StmtBlock};
|
||||
use super::{ASTFlags, ASTNode, Ident, Namespace, Stmt, StmtBlock};
|
||||
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||
use crate::func::hashing::ALT_ZERO_HASH;
|
||||
use crate::tokenizer::Token;
|
||||
use crate::tokenizer::{Token, NO_TOKEN};
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec,
|
||||
@ -197,8 +197,7 @@ impl FnCallHashes {
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct FnCallExpr {
|
||||
/// Namespace of the function, if any.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub namespace: super::Namespace,
|
||||
pub namespace: Namespace,
|
||||
/// Function name.
|
||||
pub name: ImmutableString,
|
||||
/// Pre-calculated hashes.
|
||||
@ -208,7 +207,7 @@ pub struct FnCallExpr {
|
||||
/// Does this function call capture the parent scope?
|
||||
pub capture_parent_scope: bool,
|
||||
/// Is this function call a native operator?
|
||||
/// Otherwise set to [`Token::NonToken`].
|
||||
/// Otherwise set to [`Token::NONE`].
|
||||
pub op_token: Token,
|
||||
}
|
||||
|
||||
@ -217,14 +216,13 @@ impl fmt::Debug for FnCallExpr {
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut ff = f.debug_struct("FnCallExpr");
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if !self.namespace.is_empty() {
|
||||
ff.field("namespace", &self.namespace);
|
||||
}
|
||||
ff.field("hash", &self.hashes)
|
||||
.field("name", &self.name)
|
||||
.field("args", &self.args);
|
||||
if self.op_token != Token::NonToken {
|
||||
if self.op_token != NO_TOKEN {
|
||||
ff.field("op_token", &self.op_token);
|
||||
}
|
||||
if self.capture_parent_scope {
|
||||
@ -241,10 +239,7 @@ impl FnCallExpr {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_qualified(&self) -> bool {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
return !self.namespace.is_empty();
|
||||
#[cfg(feature = "no_module")]
|
||||
return false;
|
||||
!self.namespace.is_empty()
|
||||
}
|
||||
/// Convert this into an [`Expr::FnCall`].
|
||||
#[inline(always)]
|
||||
@ -304,9 +299,7 @@ pub enum Expr {
|
||||
/// majority of cases (unless there are more than 255 variables defined!).
|
||||
/// This is to avoid reading a pointer redirection during each variable access.
|
||||
Variable(
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Box<(Option<NonZeroUsize>, super::Namespace, u64, ImmutableString)>,
|
||||
#[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, [(); 0], u64, ImmutableString)>,
|
||||
Box<(Option<NonZeroUsize>, Namespace, u64, ImmutableString)>,
|
||||
Option<NonZeroU8>,
|
||||
Position,
|
||||
),
|
||||
@ -584,13 +577,12 @@ impl Expr {
|
||||
|
||||
Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
|
||||
FnCallExpr {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace: super::Namespace::NONE,
|
||||
namespace: Namespace::NONE,
|
||||
name: KEYWORD_FN_PTR.into(),
|
||||
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||
capture_parent_scope: false,
|
||||
op_token: Token::NonToken,
|
||||
op_token: NO_TOKEN,
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
|
@ -5,6 +5,7 @@ pub mod expr;
|
||||
pub mod flags;
|
||||
pub mod ident;
|
||||
pub mod namespace;
|
||||
pub mod namespace_none;
|
||||
pub mod script_fn;
|
||||
pub mod stmt;
|
||||
|
||||
@ -16,6 +17,8 @@ pub use flags::{ASTFlags, FnAccess};
|
||||
pub use ident::Ident;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub use namespace::Namespace;
|
||||
#[cfg(feature = "no_module")]
|
||||
pub use namespace_none::Namespace;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use script_fn::EncapsulatedEnviron;
|
||||
|
22
src/ast/namespace_none.rs
Normal file
22
src/ast/namespace_none.rs
Normal file
@ -0,0 +1,22 @@
|
||||
//! Namespace reference type.
|
||||
#![cfg(feature = "no_module")]
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// _(internals)_ A chain of [module][crate::Module] names to namespace-qualify a variable or function call.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
|
||||
pub struct Namespace;
|
||||
|
||||
impl Namespace {
|
||||
/// Constant for no namespace.
|
||||
pub const NONE: Self = Self;
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ use std::{fmt, hash::Hash};
|
||||
///
|
||||
/// Not available under `no_module` or `no_function`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncapsulatedEnviron {
|
||||
/// Functions defined within the same [`AST`][crate::AST].
|
||||
@ -35,7 +34,6 @@ pub struct ScriptFnDef {
|
||||
pub body: StmtBlock,
|
||||
/// Encapsulated AST environment, if any.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub environ: Option<crate::Shared<EncapsulatedEnviron>>,
|
||||
/// Function name.
|
||||
pub name: ImmutableString,
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
use super::{ASTFlags, ASTNode, BinaryExpr, Expr, FnCallExpr, Ident};
|
||||
use crate::engine::KEYWORD_EVAL;
|
||||
use crate::tokenizer::{Span, Token};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::Span;
|
||||
use crate::{calc_fn_hash, Position, StaticVec, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
@ -5,7 +5,7 @@ use super::{Caches, GlobalRuntimeState, Target};
|
||||
use crate::ast::{ASTFlags, Expr, OpAssignment};
|
||||
use crate::config::hashing::SusLock;
|
||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::tokenizer::NO_TOKEN;
|
||||
use crate::types::{dynamic::Union, RestoreOnDrop};
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
||||
@ -72,16 +72,10 @@ impl Engine {
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
FN_IDX_GET,
|
||||
Token::NonToken,
|
||||
hash_idx().0,
|
||||
&mut [target, idx],
|
||||
true,
|
||||
pos,
|
||||
)
|
||||
let hash = hash_idx().0;
|
||||
let args = &mut [target, idx];
|
||||
|
||||
self.exec_native_fn_call(global, caches, FN_IDX_GET, NO_TOKEN, hash, args, true, pos)
|
||||
.map(|(r, ..)| r)
|
||||
}
|
||||
|
||||
@ -101,15 +95,11 @@ impl Engine {
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
|
||||
let hash = hash_idx().1;
|
||||
let args = &mut [target, idx, new_val];
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
FN_IDX_SET,
|
||||
Token::NonToken,
|
||||
hash_idx().1,
|
||||
&mut [target, idx, new_val],
|
||||
is_ref_mut,
|
||||
pos,
|
||||
global, caches, FN_IDX_SET, NO_TOKEN, hash, args, is_ref_mut, pos,
|
||||
)
|
||||
}
|
||||
|
||||
@ -766,15 +756,10 @@ impl Engine {
|
||||
|
||||
if op_info.is_op_assignment() {
|
||||
let args = &mut [target.as_mut()];
|
||||
|
||||
let (mut orig_val, ..) = self
|
||||
.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
getter,
|
||||
Token::NonToken,
|
||||
*hash_get,
|
||||
args,
|
||||
is_ref_mut,
|
||||
global, caches, getter, NO_TOKEN, *hash_get, args, is_ref_mut,
|
||||
*pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
@ -807,15 +792,9 @@ impl Engine {
|
||||
}
|
||||
|
||||
let args = &mut [target.as_mut(), &mut new_val];
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
setter,
|
||||
Token::NonToken,
|
||||
*hash_set,
|
||||
args,
|
||||
is_ref_mut,
|
||||
*pos,
|
||||
global, caches, setter, NO_TOKEN, *hash_set, args, is_ref_mut, *pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
@ -840,15 +819,9 @@ impl Engine {
|
||||
|
||||
let ((getter, hash_get), _, name) = &**x;
|
||||
let args = &mut [target.as_mut()];
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
getter,
|
||||
Token::NonToken,
|
||||
*hash_get,
|
||||
args,
|
||||
is_ref_mut,
|
||||
*pos,
|
||||
global, caches, getter, NO_TOKEN, *hash_get, args, is_ref_mut, *pos,
|
||||
)
|
||||
.map_or_else(
|
||||
|err| match *err {
|
||||
@ -938,20 +911,13 @@ impl Engine {
|
||||
self.run_debugger(global, caches, scope, this_ptr, _node)?;
|
||||
|
||||
let ((getter, hash_get), (setter, hash_set), name) = &**p;
|
||||
let mut arg_values = [target.as_mut(), &mut Dynamic::UNIT.clone()];
|
||||
let args = &mut arg_values[..1];
|
||||
let args = &mut [target.as_mut()];
|
||||
|
||||
// Assume getters are always pure
|
||||
let (mut val, ..) = self
|
||||
.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
getter,
|
||||
Token::NonToken,
|
||||
*hash_get,
|
||||
args,
|
||||
is_ref_mut,
|
||||
pos,
|
||||
global, caches, getter, NO_TOKEN, *hash_get, args,
|
||||
is_ref_mut, pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
@ -981,19 +947,13 @@ impl Engine {
|
||||
// Feed the value back via a setter just in case it has been updated
|
||||
if may_be_changed {
|
||||
// Re-use args because the first &mut parameter will not be consumed
|
||||
let mut arg_values = [target.as_mut(), val.as_mut()];
|
||||
let args = &mut arg_values;
|
||||
let args = &mut [target.as_mut(), val.as_mut()];
|
||||
|
||||
// The return value is thrown away and not used.
|
||||
let _ = self
|
||||
.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
setter,
|
||||
Token::NonToken,
|
||||
*hash_set,
|
||||
args,
|
||||
is_ref_mut,
|
||||
pos,
|
||||
global, caches, setter, NO_TOKEN, *hash_set, args,
|
||||
is_ref_mut, pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
|
@ -508,11 +508,10 @@ impl Engine {
|
||||
node: ASTNode<'a>,
|
||||
event: DebuggerEvent,
|
||||
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
||||
if let Some(ref x) = self.debugger_interface {
|
||||
let src = global.source_raw().cloned();
|
||||
let src = src.as_ref().map(|s| s.as_str());
|
||||
let context = crate::EvalContext::new(self, global, caches, scope, this_ptr);
|
||||
|
||||
if let Some(ref x) = self.debugger_interface {
|
||||
let context = EvalContext::new(self, global, caches, scope, this_ptr);
|
||||
let (.., ref on_debugger) = **x;
|
||||
|
||||
let command = on_debugger(context, event, node, src, node.position())?;
|
||||
|
@ -7,7 +7,6 @@ use std::prelude::v1::*;
|
||||
|
||||
/// Context of a script evaluation process.
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
/// The current [`Engine`].
|
||||
engine: &'a Engine,
|
||||
|
@ -6,13 +6,26 @@ use crate::ast::{
|
||||
ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock,
|
||||
};
|
||||
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::types::dynamic::{AccessMode, Union};
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, ERR, INT};
|
||||
use std::hash::{Hash, Hasher};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
impl Dynamic {
|
||||
/// If the value is a string, intern it.
|
||||
#[inline(always)]
|
||||
fn intern_string(self, engine: &Engine) -> Self {
|
||||
match self.0 {
|
||||
Union::Str(..) => engine
|
||||
.get_interned_string(self.into_immutable_string().expect("`ImmutableString`"))
|
||||
.into(),
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
/// Evaluate a statements block.
|
||||
pub(crate) fn eval_stmt_block(
|
||||
@ -169,14 +182,13 @@ impl Engine {
|
||||
self.check_data_size(&*args[0], root.position())?;
|
||||
} else {
|
||||
// Normal assignment
|
||||
|
||||
// If value is a string, intern it
|
||||
if new_val.is_string() {
|
||||
let value = new_val.into_immutable_string().expect("`ImmutableString`");
|
||||
new_val = self.get_interned_string(value).into();
|
||||
match target {
|
||||
// Lock it again just in case it is shared
|
||||
Target::RefMut(_) | Target::TempValue(_) => {
|
||||
*target.write_lock::<Dynamic>().unwrap() = new_val
|
||||
}
|
||||
_ => **target = new_val,
|
||||
}
|
||||
|
||||
*target.write_lock::<Dynamic>().unwrap() = new_val;
|
||||
}
|
||||
|
||||
target.propagate_changed_value(op_info.pos)
|
||||
@ -224,14 +236,13 @@ impl Engine {
|
||||
|
||||
let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
||||
|
||||
let is_temp_result = !target.is_ref();
|
||||
let var_name = x.3.as_str();
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
// Also handle case where target is a `Dynamic` shared value
|
||||
// (returned by a variable resolver, for example)
|
||||
let is_temp_result = !target.is_ref() && !target.is_shared();
|
||||
#[cfg(feature = "no_closure")]
|
||||
let is_temp_result = !target.is_ref();
|
||||
let is_temp_result = is_temp_result && !target.is_shared();
|
||||
|
||||
// Cannot assign to temp result from expression
|
||||
if is_temp_result {
|
||||
@ -251,19 +262,16 @@ impl Engine {
|
||||
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
{
|
||||
let mut rhs_val = self
|
||||
let rhs_val = self
|
||||
.eval_expr(global, caches, scope, this_ptr, rhs)?
|
||||
.flatten();
|
||||
|
||||
// If value is a string, intern it
|
||||
if rhs_val.is_string() {
|
||||
let value = rhs_val.into_immutable_string().expect("`ImmutableString`");
|
||||
rhs_val = self.get_interned_string(value).into();
|
||||
}
|
||||
.flatten()
|
||||
.intern_string(self);
|
||||
|
||||
let _new_val = Some((rhs_val, op_info));
|
||||
|
||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||
// Must be either `var[index] op= val` or `var.prop op= val`.
|
||||
// The return value of any op-assignment (should be `()`) is thrown away and not used.
|
||||
let _ =
|
||||
match lhs {
|
||||
// name op= rhs (handled above)
|
||||
Expr::Variable(..) => {
|
||||
@ -271,14 +279,12 @@ impl Engine {
|
||||
}
|
||||
// idx_lhs[idx_expr] op= rhs
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(..) => {
|
||||
self.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val)
|
||||
}
|
||||
Expr::Index(..) => self
|
||||
.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val),
|
||||
// dot_lhs.dot_rhs op= rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(..) => {
|
||||
self.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val)
|
||||
}
|
||||
Expr::Dot(..) => self
|
||||
.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val),
|
||||
_ => unreachable!("cannot assign to expression: {:?}", lhs),
|
||||
}?;
|
||||
|
||||
@ -723,7 +729,8 @@ impl Engine {
|
||||
// Evaluate initial value
|
||||
let mut value = self
|
||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||
.flatten();
|
||||
.flatten()
|
||||
.intern_string(self);
|
||||
|
||||
let _alias = if !rewind_scope {
|
||||
// Put global constants into global module
|
||||
|
@ -89,9 +89,9 @@ pub enum Target<'a> {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
SharedValue {
|
||||
/// Lock guard to the shared [`Dynamic`].
|
||||
source: crate::types::dynamic::DynamicWriteLock<'a, Dynamic>,
|
||||
/// Copy of the value.
|
||||
value: Dynamic,
|
||||
guard: crate::types::dynamic::DynamicWriteLock<'a, Dynamic>,
|
||||
/// Copy of the shared value.
|
||||
shared_value: Dynamic,
|
||||
},
|
||||
/// The target is a temporary [`Dynamic`] value (i.e. its mutation can cause no side effects).
|
||||
TempValue(Dynamic),
|
||||
@ -178,21 +178,21 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
}
|
||||
/// Is the [`Target`] a shared value?
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_shared(&self) -> bool {
|
||||
match self {
|
||||
Self::RefMut(r) => r.is_shared(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
return match self {
|
||||
Self::RefMut(r) => r.is_shared(),
|
||||
Self::SharedValue { .. } => true,
|
||||
Self::TempValue(value) => value.is_shared(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { .. }
|
||||
| Self::BitField { .. }
|
||||
| Self::BlobByte { .. }
|
||||
| Self::StringChar { .. } => false,
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "no_closure")]
|
||||
return false;
|
||||
}
|
||||
/// Get the value of the [`Target`] as a [`Dynamic`], cloning a referenced value if necessary.
|
||||
#[inline]
|
||||
@ -200,7 +200,7 @@ impl<'a> Target<'a> {
|
||||
match self {
|
||||
Self::RefMut(r) => r.clone(), // Referenced value is cloned
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { value, .. } => value, // Original shared value is simply taken
|
||||
Self::SharedValue { shared_value, .. } => shared_value, // Original shared value is simply taken
|
||||
Self::TempValue(value) => value, // Owned value is simply taken
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { value, .. } => value, // boolean is taken
|
||||
@ -227,7 +227,7 @@ impl<'a> Target<'a> {
|
||||
match self {
|
||||
Self::RefMut(r) => Self::TempValue(r.clone()),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { value, .. } => Self::TempValue(value),
|
||||
Self::SharedValue { shared_value, .. } => Self::TempValue(shared_value),
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
@ -239,7 +239,7 @@ impl<'a> Target<'a> {
|
||||
match self {
|
||||
Self::RefMut(r) => r,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { source, .. } => source,
|
||||
Self::SharedValue { guard, .. } => guard,
|
||||
Self::TempValue(value) => value,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { source, .. } => source,
|
||||
@ -365,9 +365,12 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
if value.is_shared() {
|
||||
// Cloning is cheap for a shared value
|
||||
let val = value.clone();
|
||||
let source = value.write_lock::<Dynamic>().expect("`Dynamic`");
|
||||
return Self::SharedValue { source, value: val };
|
||||
let shared_value = value.clone();
|
||||
let guard = value.write_lock::<Dynamic>().expect("`Dynamic`");
|
||||
return Self::SharedValue {
|
||||
guard,
|
||||
shared_value,
|
||||
};
|
||||
}
|
||||
|
||||
Self::RefMut(value)
|
||||
@ -382,7 +385,7 @@ impl Deref for Target<'_> {
|
||||
match self {
|
||||
Self::RefMut(r) => r,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { source, .. } => source,
|
||||
Self::SharedValue { guard, .. } => guard,
|
||||
Self::TempValue(ref value) => value,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { ref value, .. }
|
||||
@ -415,7 +418,7 @@ impl DerefMut for Target<'_> {
|
||||
match self {
|
||||
Self::RefMut(r) => r,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { source, .. } => &mut *source,
|
||||
Self::SharedValue { guard, .. } => &mut *guard,
|
||||
Self::TempValue(ref mut value) => value,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { ref mut value, .. }
|
||||
|
@ -8,7 +8,7 @@ use crate::engine::{
|
||||
KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||
};
|
||||
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
|
||||
use crate::tokenizer::{is_valid_function_name, Token};
|
||||
use crate::tokenizer::{is_valid_function_name, Token, NO_TOKEN};
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
|
||||
@ -272,7 +272,7 @@ impl Engine {
|
||||
|
||||
// Try to find a built-in version
|
||||
let builtin = args.and_then(|args| match op_token {
|
||||
Token::NonToken => None,
|
||||
Token::NONE => None,
|
||||
token if token.is_op_assignment() => {
|
||||
let (first_arg, rest_args) = args.split_first().unwrap();
|
||||
|
||||
@ -639,15 +639,7 @@ impl Engine {
|
||||
let local_entry = &mut None;
|
||||
|
||||
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
||||
.resolve_fn(
|
||||
global,
|
||||
caches,
|
||||
local_entry,
|
||||
Token::NonToken,
|
||||
hash,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.resolve_fn(global, caches, local_entry, NO_TOKEN, hash, None, false)
|
||||
.cloned()
|
||||
{
|
||||
// Script function call
|
||||
@ -791,7 +783,7 @@ impl Engine {
|
||||
caches,
|
||||
None,
|
||||
fn_name,
|
||||
Token::NonToken,
|
||||
NO_TOKEN,
|
||||
new_hash,
|
||||
&mut args,
|
||||
false,
|
||||
@ -845,7 +837,7 @@ impl Engine {
|
||||
caches,
|
||||
None,
|
||||
&fn_name,
|
||||
Token::NonToken,
|
||||
NO_TOKEN,
|
||||
new_hash,
|
||||
&mut args,
|
||||
is_ref_mut,
|
||||
@ -944,7 +936,7 @@ impl Engine {
|
||||
caches,
|
||||
None,
|
||||
fn_name,
|
||||
Token::NonToken,
|
||||
NO_TOKEN,
|
||||
hash,
|
||||
&mut args,
|
||||
is_ref_mut,
|
||||
@ -986,7 +978,7 @@ impl Engine {
|
||||
let redirected; // Handle call() - Redirect function call
|
||||
|
||||
match name {
|
||||
_ if op_token != Token::NonToken => (),
|
||||
_ if op_token != NO_TOKEN => (),
|
||||
|
||||
// Handle call()
|
||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||
@ -1217,12 +1209,7 @@ impl Engine {
|
||||
|
||||
self.track_operation(global, first_expr.position())?;
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let target_is_shared = target.is_shared();
|
||||
#[cfg(feature = "no_closure")]
|
||||
let target_is_shared = false;
|
||||
|
||||
if target_is_shared || target.is_temp_value() {
|
||||
if target.is_shared() || target.is_temp_value() {
|
||||
arg_values.insert(0, target.take_or_clone().flatten());
|
||||
} else {
|
||||
// Turn it into a method call only if the object is not shared and not a simple value
|
||||
@ -1500,7 +1487,7 @@ impl Engine {
|
||||
let op_token = op_token.clone();
|
||||
|
||||
// Short-circuit native binary operator call if under Fast Operators mode
|
||||
if op_token != Token::NonToken && self.fast_operators() && args.len() == 2 {
|
||||
if op_token != NO_TOKEN && self.fast_operators() && args.len() == 2 {
|
||||
let mut lhs = self
|
||||
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
||||
.0
|
||||
|
@ -4,7 +4,7 @@ use super::call::FnCallArgs;
|
||||
use crate::ast::FnCallHashes;
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::plugin::PluginFunction;
|
||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState, NO_TOKEN};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
calc_fn_hash, reify, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult,
|
||||
@ -424,7 +424,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
let caches = &mut Caches::new();
|
||||
|
||||
let fn_name = fn_name.as_ref();
|
||||
let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(Token::NonToken);
|
||||
let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(NO_TOKEN);
|
||||
let args_len = args.len();
|
||||
|
||||
global.level += 1;
|
||||
|
@ -207,9 +207,9 @@ pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
|
||||
pub use eval::EvalContext;
|
||||
pub use func::{NativeCallContext, RegisterNativeFunction};
|
||||
pub use module::{FnNamespace, Module};
|
||||
pub use tokenizer::Position;
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
pub use types::Instant;
|
||||
pub use types::Position;
|
||||
pub use types::{
|
||||
Dynamic, EvalAltResult, FnPtr, ImmutableString, LexError, ParseError, ParseErrorType, Scope,
|
||||
};
|
||||
@ -311,12 +311,12 @@ pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant}
|
||||
pub use types::FloatWrapper;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use types::StringsInterner;
|
||||
pub use types::{Span, StringsInterner};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use tokenizer::{
|
||||
get_next_token, is_valid_function_name, is_valid_identifier, parse_string_literal, InputStream,
|
||||
MultiInputsStream, Span, Token, TokenIterator, TokenizeState, TokenizerControl,
|
||||
MultiInputsStream, Token, TokenIterator, TokenizeState, TokenizerControl,
|
||||
TokenizerControlBlock,
|
||||
};
|
||||
|
||||
|
@ -69,14 +69,15 @@ impl<'a> OptimizerState<'a> {
|
||||
#[inline(always)]
|
||||
pub fn new(
|
||||
engine: &'a Engine,
|
||||
#[cfg(not(feature = "no_function"))] lib: &'a [crate::SharedModule],
|
||||
lib: &'a [crate::SharedModule],
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> Self {
|
||||
let mut _global = GlobalRuntimeState::new(engine);
|
||||
let _lib = lib;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
_global.lib = lib.iter().cloned().collect();
|
||||
_global.lib = _lib.iter().cloned().collect();
|
||||
}
|
||||
|
||||
Self {
|
||||
@ -986,7 +987,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
// ``
|
||||
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
||||
state.set_dirty();
|
||||
*expr = Expr::StringConstant(state.engine.get_interned_string(""), *pos);
|
||||
*expr = Expr::StringConstant(state.engine.const_empty_string(), *pos);
|
||||
}
|
||||
// `... ${const} ...`
|
||||
Expr::InterpolatedString(..) if expr.is_constant() => {
|
||||
@ -1117,7 +1118,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
return;
|
||||
}
|
||||
// Overloaded operators can override built-in.
|
||||
_ if x.args.len() == 2 && x.op_token != Token::NonToken && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => {
|
||||
_ if x.args.len() == 2 && x.op_token != Token::NONE && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => {
|
||||
if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone(), &arg_values[0], &arg_values[1])
|
||||
.and_then(|f| {
|
||||
let context = (state.engine, x.name.as_str(),None, &state.global, *pos).into();
|
||||
@ -1265,7 +1266,7 @@ impl Engine {
|
||||
&self,
|
||||
statements: StmtBlockContainer,
|
||||
scope: &Scope,
|
||||
#[cfg(not(feature = "no_function"))] lib: &[crate::SharedModule],
|
||||
lib: &[crate::SharedModule],
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> StmtBlockContainer {
|
||||
let mut statements = statements;
|
||||
@ -1277,12 +1278,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Set up the state
|
||||
let mut state = OptimizerState::new(
|
||||
self,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib,
|
||||
optimization_level,
|
||||
);
|
||||
let mut state = OptimizerState::new(self, lib, optimization_level);
|
||||
|
||||
// Add constants from global modules
|
||||
for (name, value) in self.global_modules.iter().rev().flat_map(|m| m.iter_var()) {
|
||||
@ -1355,19 +1351,17 @@ impl Engine {
|
||||
|
||||
module.into()
|
||||
};
|
||||
#[cfg(feature = "no_function")]
|
||||
let lib: crate::Shared<_> = crate::Module::new().into();
|
||||
|
||||
statements.shrink_to_fit();
|
||||
|
||||
AST::new(
|
||||
match optimization_level {
|
||||
OptimizationLevel::None => statements,
|
||||
OptimizationLevel::Simple | OptimizationLevel::Full => self.optimize_top_level(
|
||||
statements,
|
||||
scope,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
&[lib.clone()],
|
||||
optimization_level,
|
||||
),
|
||||
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
||||
self.optimize_top_level(statements, scope, &[lib.clone()], optimization_level)
|
||||
}
|
||||
},
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib,
|
||||
|
137
src/parser.rs
137
src/parser.rs
@ -4,7 +4,7 @@ use crate::api::events::VarDefInfo;
|
||||
use crate::api::options::LangOptions;
|
||||
use crate::ast::{
|
||||
ASTFlags, BinaryExpr, CaseBlocksList, ConditionalExpr, Expr, FnCallExpr, FnCallHashes, Ident,
|
||||
OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, StmtBlockContainer,
|
||||
Namespace, OpAssignment, RangeCase, ScriptFnDef, Stmt, StmtBlock, StmtBlockContainer,
|
||||
SwitchCasesCollection, TryCatchBlock,
|
||||
};
|
||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||
@ -12,14 +12,14 @@ use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::func::{hashing::get_hasher, StraightHashMap};
|
||||
use crate::tokenizer::{
|
||||
is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream,
|
||||
TokenizerControl,
|
||||
TokenizerControl, NO_TOKEN,
|
||||
};
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::types::StringsInterner;
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ExclusiveRange, FnArgsVec,
|
||||
Identifier, ImmutableString, InclusiveRange, LexError, OptimizationLevel, ParseError, Position,
|
||||
Scope, Shared, SmartString, StaticVec, AST, INT, PERR,
|
||||
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ExclusiveRange, Identifier,
|
||||
ImmutableString, InclusiveRange, LexError, OptimizationLevel, ParseError, Position, Scope,
|
||||
Shared, SmartString, StaticVec, AST, INT, PERR,
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -63,7 +63,7 @@ pub struct ParseState<'e, 's> {
|
||||
pub block_stack_len: usize,
|
||||
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
pub external_vars: Option<Box<FnArgsVec<Ident>>>,
|
||||
pub external_vars: Option<Box<crate::FnArgsVec<Ident>>>,
|
||||
/// An indicator that disables variable capturing into externals one single time
|
||||
/// up until the nearest consumed Identifier token.
|
||||
/// If set to false the next call to [`access_var`][ParseState::access_var] will not capture the variable.
|
||||
@ -557,7 +557,7 @@ impl Engine {
|
||||
id: ImmutableString,
|
||||
no_args: bool,
|
||||
capture_parent_scope: bool,
|
||||
#[cfg(not(feature = "no_module"))] namespace: crate::ast::Namespace,
|
||||
namespace: Namespace,
|
||||
settings: ParseSettings,
|
||||
) -> ParseResult<Expr> {
|
||||
let (token, token_pos) = if no_args {
|
||||
@ -566,8 +566,7 @@ impl Engine {
|
||||
input.peek().expect(NEVER_ENDS)
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let mut namespace = namespace;
|
||||
let mut _namespace = namespace;
|
||||
let mut args = StaticVec::new_const();
|
||||
|
||||
match token {
|
||||
@ -588,17 +587,16 @@ impl Engine {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let hash = if namespace.is_empty() {
|
||||
let hash = if _namespace.is_empty() {
|
||||
calc_fn_hash(None, &id, 0)
|
||||
} else {
|
||||
let root = namespace.root();
|
||||
let root = _namespace.root();
|
||||
let index = state.find_module(root);
|
||||
let is_global = false;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let is_global = root == crate::engine::KEYWORD_GLOBAL;
|
||||
#[cfg(any(feature = "no_function", feature = "no_module"))]
|
||||
let is_global = false;
|
||||
let is_global = is_global || root == crate::engine::KEYWORD_GLOBAL;
|
||||
|
||||
if settings.has_option(LangOptions::STRICT_VAR)
|
||||
&& index.is_none()
|
||||
@ -615,13 +613,13 @@ impl Engine {
|
||||
.map_or(false, |m| m.contains_key(root))
|
||||
{
|
||||
return Err(
|
||||
PERR::ModuleUndefined(root.into()).into_err(namespace.position())
|
||||
PERR::ModuleUndefined(root.into()).into_err(_namespace.position())
|
||||
);
|
||||
}
|
||||
|
||||
namespace.set_index(index);
|
||||
_namespace.set_index(index);
|
||||
|
||||
crate::calc_fn_hash(namespace.iter().map(Ident::as_str), &id, 0)
|
||||
calc_fn_hash(_namespace.iter().map(Ident::as_str), &id, 0)
|
||||
};
|
||||
#[cfg(feature = "no_module")]
|
||||
let hash = calc_fn_hash(None, &id, 0);
|
||||
@ -637,9 +635,8 @@ impl Engine {
|
||||
return Ok(FnCallExpr {
|
||||
name: state.get_interned_string(id),
|
||||
capture_parent_scope,
|
||||
op_token: Token::NonToken,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace,
|
||||
op_token: NO_TOKEN,
|
||||
namespace: _namespace,
|
||||
hashes,
|
||||
args,
|
||||
}
|
||||
@ -664,10 +661,10 @@ impl Engine {
|
||||
eat_token(input, Token::RightParen);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let hash = if namespace.is_empty() {
|
||||
let hash = if _namespace.is_empty() {
|
||||
calc_fn_hash(None, &id, args.len())
|
||||
} else {
|
||||
let root = namespace.root();
|
||||
let root = _namespace.root();
|
||||
let index = state.find_module(root);
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
@ -691,13 +688,13 @@ impl Engine {
|
||||
.map_or(false, |m| m.contains_key(root))
|
||||
{
|
||||
return Err(
|
||||
PERR::ModuleUndefined(root.into()).into_err(namespace.position())
|
||||
PERR::ModuleUndefined(root.into()).into_err(_namespace.position())
|
||||
);
|
||||
}
|
||||
|
||||
namespace.set_index(index);
|
||||
_namespace.set_index(index);
|
||||
|
||||
crate::calc_fn_hash(namespace.iter().map(Ident::as_str), &id, args.len())
|
||||
calc_fn_hash(_namespace.iter().map(Ident::as_str), &id, args.len())
|
||||
};
|
||||
#[cfg(feature = "no_module")]
|
||||
let hash = calc_fn_hash(None, &id, args.len());
|
||||
@ -713,9 +710,8 @@ impl Engine {
|
||||
return Ok(FnCallExpr {
|
||||
name: state.get_interned_string(id),
|
||||
capture_parent_scope,
|
||||
op_token: Token::NonToken,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace,
|
||||
op_token: NO_TOKEN,
|
||||
namespace: _namespace,
|
||||
hashes,
|
||||
args,
|
||||
}
|
||||
@ -1592,10 +1588,7 @@ impl Engine {
|
||||
|
||||
// Identifier
|
||||
Token::Identifier(..) => {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let ns = crate::ast::Namespace::NONE;
|
||||
#[cfg(feature = "no_module")]
|
||||
let ns = [];
|
||||
let ns = Namespace::NONE;
|
||||
|
||||
let s = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), ..) => s,
|
||||
@ -1657,10 +1650,7 @@ impl Engine {
|
||||
|
||||
// Reserved keyword or symbol
|
||||
Token::Reserved(..) => {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let ns = crate::ast::Namespace::NONE;
|
||||
#[cfg(feature = "no_module")]
|
||||
let ns = [];
|
||||
let ns = Namespace::NONE;
|
||||
|
||||
let s = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Reserved(s), ..) => s,
|
||||
@ -1763,35 +1753,18 @@ impl Engine {
|
||||
|
||||
let no_args = input.next().expect(NEVER_ENDS).0 == Token::Unit;
|
||||
|
||||
let (.., _ns, _, name) = *x;
|
||||
let (.., ns, _, name) = *x;
|
||||
settings.pos = pos;
|
||||
self.parse_fn_call(
|
||||
input,
|
||||
state,
|
||||
lib,
|
||||
name,
|
||||
no_args,
|
||||
true,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
_ns,
|
||||
settings.level_up()?,
|
||||
)?
|
||||
let settings = settings.level_up()?;
|
||||
self.parse_fn_call(input, state, lib, name, no_args, true, ns, settings)?
|
||||
}
|
||||
// Function call
|
||||
(Expr::Variable(x, .., pos), t @ (Token::LeftParen | Token::Unit)) => {
|
||||
let (.., _ns, _, name) = *x;
|
||||
let (.., ns, _, name) = *x;
|
||||
let no_args = t == Token::Unit;
|
||||
settings.pos = pos;
|
||||
self.parse_fn_call(
|
||||
input,
|
||||
state,
|
||||
lib,
|
||||
name,
|
||||
t == Token::Unit,
|
||||
false,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
_ns,
|
||||
settings.level_up()?,
|
||||
)?
|
||||
let settings = settings.level_up()?;
|
||||
self.parse_fn_call(input, state, lib, name, no_args, false, ns, settings)?
|
||||
}
|
||||
// module access
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@ -1959,8 +1932,7 @@ impl Engine {
|
||||
args.shrink_to_fit();
|
||||
|
||||
Ok(FnCallExpr {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace: Default::default(),
|
||||
namespace: Namespace::NONE,
|
||||
name: state.get_interned_string("-"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
|
||||
args,
|
||||
@ -1988,8 +1960,7 @@ impl Engine {
|
||||
args.shrink_to_fit();
|
||||
|
||||
Ok(FnCallExpr {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace: Default::default(),
|
||||
namespace: Namespace::NONE,
|
||||
name: state.get_interned_string("+"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
|
||||
args,
|
||||
@ -2010,8 +1981,7 @@ impl Engine {
|
||||
args.shrink_to_fit();
|
||||
|
||||
Ok(FnCallExpr {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace: Default::default(),
|
||||
namespace: Namespace::NONE,
|
||||
name: state.get_interned_string("!"),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
|
||||
args,
|
||||
@ -2062,7 +2032,7 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
let op_info = if op != Token::NonToken {
|
||||
let op_info = if op != NO_TOKEN {
|
||||
OpAssignment::new_op_assignment_from_token(op, op_pos)
|
||||
} else {
|
||||
OpAssignment::new_assignment(op_pos)
|
||||
@ -2143,7 +2113,7 @@ impl Engine {
|
||||
) -> ParseResult<Stmt> {
|
||||
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
||||
// var = ...
|
||||
(Token::Equals, ..) => (Token::NonToken, eat_token(input, Token::Equals)),
|
||||
(Token::Equals, ..) => (NO_TOKEN, eat_token(input, Token::Equals)),
|
||||
// var op= ...
|
||||
(token, ..) if token.is_op_assignment() => {
|
||||
input.next().map(|(op, pos)| (op, pos)).expect(NEVER_ENDS)
|
||||
@ -2389,7 +2359,7 @@ impl Engine {
|
||||
let hash = calc_fn_hash(None, &op, 2);
|
||||
let is_valid_script_function = is_valid_function_name(&op);
|
||||
let operator_token = if is_valid_script_function {
|
||||
Token::NonToken
|
||||
NO_TOKEN
|
||||
} else {
|
||||
op_token.clone()
|
||||
};
|
||||
@ -2400,8 +2370,7 @@ impl Engine {
|
||||
args.shrink_to_fit();
|
||||
|
||||
let mut op_base = FnCallExpr {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace: Default::default(),
|
||||
namespace: Namespace::NONE,
|
||||
name: state.get_interned_string(&op),
|
||||
hashes: FnCallHashes::from_native(hash),
|
||||
args,
|
||||
@ -2539,10 +2508,7 @@ impl Engine {
|
||||
let (name, pos) = parse_var_name(input)?;
|
||||
let name = state.get_interned_string(name);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let ns = crate::ast::Namespace::NONE;
|
||||
#[cfg(feature = "no_module")]
|
||||
let ns = [];
|
||||
let ns = Namespace::NONE;
|
||||
|
||||
segments.push(name.clone());
|
||||
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_IDENT));
|
||||
@ -3632,7 +3598,7 @@ impl Engine {
|
||||
}
|
||||
.into();
|
||||
|
||||
let mut params: FnArgsVec<_> = params.into_iter().map(|(p, ..)| p).collect();
|
||||
let mut params: crate::FnArgsVec<_> = params.into_iter().map(|(p, ..)| p).collect();
|
||||
params.shrink_to_fit();
|
||||
|
||||
Ok(ScriptFnDef {
|
||||
@ -3660,7 +3626,7 @@ impl Engine {
|
||||
parent: &mut ParseState,
|
||||
lib: &FnLib,
|
||||
fn_expr: Expr,
|
||||
externals: FnArgsVec<Ident>,
|
||||
externals: crate::FnArgsVec<Ident>,
|
||||
pos: Position,
|
||||
) -> Expr {
|
||||
// If there are no captured variables, no need to curry
|
||||
@ -3684,8 +3650,7 @@ impl Engine {
|
||||
}));
|
||||
|
||||
let expr = FnCallExpr {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace: Default::default(),
|
||||
namespace: Namespace::NONE,
|
||||
name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY),
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash(
|
||||
None,
|
||||
@ -3693,7 +3658,7 @@ impl Engine {
|
||||
num_externals + 1,
|
||||
)),
|
||||
args,
|
||||
op_token: Token::NonToken,
|
||||
op_token: NO_TOKEN,
|
||||
capture_parent_scope: false,
|
||||
}
|
||||
.into_fn_call_expr(pos);
|
||||
@ -3708,7 +3673,7 @@ impl Engine {
|
||||
let (index, _) = parent.access_var(&name, lib, pos);
|
||||
(name, index, pos)
|
||||
})
|
||||
.collect::<FnArgsVec<_>>()
|
||||
.collect::<crate::FnArgsVec<_>>()
|
||||
.into(),
|
||||
));
|
||||
statements.push(Stmt::Expr(expr.into()));
|
||||
@ -3779,20 +3744,20 @@ impl Engine {
|
||||
// so extract them into a list.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let (mut params, externals) = if let Some(ref external_vars) = state.external_vars {
|
||||
let externals: FnArgsVec<_> = external_vars.iter().cloned().collect();
|
||||
let externals: crate::FnArgsVec<_> = external_vars.iter().cloned().collect();
|
||||
|
||||
let mut params = FnArgsVec::with_capacity(params_list.len() + externals.len());
|
||||
let mut params = crate::FnArgsVec::with_capacity(params_list.len() + externals.len());
|
||||
params.extend(externals.iter().map(|Ident { name, .. }| name.clone()));
|
||||
|
||||
(params, externals)
|
||||
} else {
|
||||
(
|
||||
FnArgsVec::with_capacity(params_list.len()),
|
||||
FnArgsVec::new_const(),
|
||||
crate::FnArgsVec::with_capacity(params_list.len()),
|
||||
crate::FnArgsVec::new_const(),
|
||||
)
|
||||
};
|
||||
#[cfg(feature = "no_closure")]
|
||||
let mut params = FnArgsVec::with_capacity(params_list.len());
|
||||
let mut params = crate::FnArgsVec::with_capacity(params_list.len());
|
||||
|
||||
params.append(&mut params_list);
|
||||
|
||||
|
329
src/tokenizer.rs
329
src/tokenizer.rs
@ -5,7 +5,7 @@ use crate::engine::{
|
||||
KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
||||
};
|
||||
use crate::func::native::OnParseTokenCallback;
|
||||
use crate::{Engine, Identifier, LexError, SmartString, StaticVec, INT, UNSIGNED_INT};
|
||||
use crate::{Engine, Identifier, LexError, Position, SmartString, StaticVec, INT, UNSIGNED_INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
@ -13,7 +13,6 @@ use std::{
|
||||
char, fmt,
|
||||
iter::{FusedIterator, Peekable},
|
||||
num::NonZeroUsize,
|
||||
ops::{Add, AddAssign},
|
||||
rc::Rc,
|
||||
str::{Chars, FromStr},
|
||||
};
|
||||
@ -50,326 +49,12 @@ type LERR = LexError;
|
||||
/// Separator character for numbers.
|
||||
const NUMBER_SEPARATOR: char = '_';
|
||||
|
||||
/// No token.
|
||||
pub const NO_TOKEN: Token = Token::NONE;
|
||||
|
||||
/// A stream of tokens.
|
||||
pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
|
||||
|
||||
/// A location (line number + character position) in the input script.
|
||||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// In order to keep footprint small, both line number and character position have 16-bit resolution,
|
||||
/// meaning they go up to a maximum of 65,535 lines and 65,535 characters per line.
|
||||
///
|
||||
/// Advancing beyond the maximum line length or maximum number of lines is not an error but has no effect.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Position {
|
||||
/// Line number: 0 = none
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
line: u16,
|
||||
/// Character position: 0 = BOL
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pos: u16,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// A [`Position`] representing no position.
|
||||
pub const NONE: Self = Self {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
line: 0,
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pos: 0,
|
||||
};
|
||||
/// A [`Position`] representing the first position.
|
||||
pub const START: Self = Self {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
line: 1,
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pos: 0,
|
||||
};
|
||||
|
||||
/// Create a new [`Position`].
|
||||
///
|
||||
/// `line` must not be zero.
|
||||
///
|
||||
/// If `position` is zero, then it is at the beginning of a line.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `line` is zero.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(line: u16, position: u16) -> Self {
|
||||
assert!(line != 0, "line cannot be zero");
|
||||
|
||||
let _pos = position;
|
||||
|
||||
Self {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
line,
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pos: _pos,
|
||||
}
|
||||
}
|
||||
/// Get the line number (1-based), or [`None`] if there is no position.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn line(self) -> Option<usize> {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return if self.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(self.line as usize)
|
||||
};
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
return None;
|
||||
}
|
||||
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn position(self) -> Option<usize> {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return if self.is_none() || self.pos == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.pos as usize)
|
||||
};
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
return None;
|
||||
}
|
||||
/// Advance by one character position.
|
||||
#[inline]
|
||||
pub(crate) fn advance(&mut self) {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
{
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.pos < u16::MAX {
|
||||
self.pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Go backwards by one character position.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if already at beginning of a line - cannot rewind to a previous line.
|
||||
#[inline]
|
||||
pub(crate) fn rewind(&mut self) {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
{
|
||||
assert!(!self.is_none(), "cannot rewind Position::none");
|
||||
assert!(self.pos > 0, "cannot rewind at position 0");
|
||||
self.pos -= 1;
|
||||
}
|
||||
}
|
||||
/// Advance to the next line.
|
||||
#[inline]
|
||||
pub(crate) fn new_line(&mut self) {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
{
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.line < u16::MAX {
|
||||
self.line += 1;
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Is this [`Position`] at the beginning of a line?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_beginning_of_line(self) -> bool {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return self.pos == 0 && !self.is_none();
|
||||
#[cfg(feature = "no_position")]
|
||||
return false;
|
||||
}
|
||||
/// Is there no [`Position`]?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(self) -> bool {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return self.line == 0 && self.pos == 0;
|
||||
#[cfg(feature = "no_position")]
|
||||
return true;
|
||||
}
|
||||
/// Returns an fallback [`Position`] if it is [`NONE`][Position::NONE]?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn or_else(self, pos: Self) -> Self {
|
||||
if self.is_none() {
|
||||
pos
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
/// Print this [`Position`] for debug purposes.
|
||||
#[inline]
|
||||
pub(crate) fn debug_print(self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !self.is_none() {
|
||||
write!(_f, " @ {:?}", self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::START
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
write!(f, "none")?;
|
||||
} else {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
write!(f, "line {}, position {}", self.line, self.pos)?;
|
||||
#[cfg(feature = "no_position")]
|
||||
unreachable!("no position");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Position {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
f.write_str("none")
|
||||
} else {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
if self.is_beginning_of_line() {
|
||||
write!(f, "{}", self.line)
|
||||
} else {
|
||||
write!(f, "{}:{}", self.line, self.pos)
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
unreachable!("no position");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Position {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_none() {
|
||||
self
|
||||
} else {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return Self {
|
||||
line: self.line + rhs.line - 1,
|
||||
pos: if rhs.is_beginning_of_line() {
|
||||
self.pos
|
||||
} else {
|
||||
self.pos + rhs.pos - 1
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "no_position")]
|
||||
unreachable!("no position");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Position {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A span consisting of a starting and an ending [positions][Position].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Span {
|
||||
/// Starting [position][Position].
|
||||
start: Position,
|
||||
/// Ending [position][Position].
|
||||
end: Position,
|
||||
}
|
||||
|
||||
impl Default for Span {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::NONE
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Empty [`Span`].
|
||||
pub const NONE: Self = Self::new(Position::NONE, Position::NONE);
|
||||
|
||||
/// Create a new [`Span`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(start: Position, end: Position) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
/// Is this [`Span`] non-existent?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(&self) -> bool {
|
||||
self.start.is_none() && self.end.is_none()
|
||||
}
|
||||
/// Get the [`Span`]'s starting [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn start(&self) -> Position {
|
||||
self.start
|
||||
}
|
||||
/// Get the [`Span`]'s ending [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn end(&self) -> Position {
|
||||
self.end
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let _f = f;
|
||||
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
match (self.start(), self.end()) {
|
||||
(Position::NONE, Position::NONE) => write!(_f, "{:?}", Position::NONE),
|
||||
(Position::NONE, end) => write!(_f, "..{:?}", end),
|
||||
(start, Position::NONE) => write!(_f, "{:?}", start),
|
||||
(start, end) if start.line() != end.line() => {
|
||||
write!(_f, "{:?}-{:?}", start, end)
|
||||
}
|
||||
(start, end) => write!(
|
||||
_f,
|
||||
"{}:{}-{}",
|
||||
start.line().unwrap(),
|
||||
start.position().unwrap_or(0),
|
||||
end.position().unwrap_or(0)
|
||||
),
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A Rhai language token.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
@ -587,7 +272,7 @@ pub enum Token {
|
||||
/// Used as a placeholder for the end of input.
|
||||
EOF,
|
||||
/// Placeholder to indicate the lack of a token.
|
||||
NonToken,
|
||||
NONE,
|
||||
}
|
||||
|
||||
impl fmt::Display for Token {
|
||||
@ -613,7 +298,7 @@ impl fmt::Display for Token {
|
||||
Comment(s) => f.write_str(s),
|
||||
|
||||
EOF => f.write_str("{EOF}"),
|
||||
NonToken => f.write_str("{NONE}"),
|
||||
NONE => f.write_str("{NONE}"),
|
||||
|
||||
token => f.write_str(token.literal_syntax()),
|
||||
}
|
||||
@ -642,7 +327,7 @@ impl Token {
|
||||
Custom(..) => false,
|
||||
LexError(..) | Comment(..) => false,
|
||||
|
||||
EOF | NonToken => false,
|
||||
EOF | NONE => false,
|
||||
|
||||
_ => true,
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ pub mod fn_ptr;
|
||||
pub mod immutable_string;
|
||||
pub mod interner;
|
||||
pub mod parse_error;
|
||||
pub mod position;
|
||||
pub mod position_none;
|
||||
pub mod restore;
|
||||
pub mod scope;
|
||||
pub mod variant;
|
||||
@ -25,6 +27,12 @@ pub use fn_ptr::FnPtr;
|
||||
pub use immutable_string::ImmutableString;
|
||||
pub use interner::StringsInterner;
|
||||
pub use parse_error::{LexError, ParseError, ParseErrorType};
|
||||
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pub use position::{Position, Span};
|
||||
#[cfg(feature = "no_position")]
|
||||
pub use position_none::{Position, Span};
|
||||
|
||||
pub use restore::RestoreOnDrop;
|
||||
pub use scope::Scope;
|
||||
pub use variant::Variant;
|
||||
|
269
src/types/position.rs
Normal file
269
src/types/position.rs
Normal file
@ -0,0 +1,269 @@
|
||||
#![cfg(not(feature = "no_position"))]
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
fmt,
|
||||
ops::{Add, AddAssign},
|
||||
};
|
||||
|
||||
/// A location (line number + character position) in the input script.
|
||||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// In order to keep footprint small, both line number and character position have 16-bit resolution,
|
||||
/// meaning they go up to a maximum of 65,535 lines and 65,535 characters per line.
|
||||
///
|
||||
/// Advancing beyond the maximum line length or maximum number of lines is not an error but has no effect.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Position {
|
||||
/// Line number: 0 = none
|
||||
line: u16,
|
||||
/// Character position: 0 = BOL
|
||||
pos: u16,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// A [`Position`] representing no position.
|
||||
pub const NONE: Self = Self { line: 0, pos: 0 };
|
||||
/// A [`Position`] representing the first position.
|
||||
pub const START: Self = Self { line: 1, pos: 0 };
|
||||
|
||||
/// Create a new [`Position`].
|
||||
///
|
||||
/// `line` must not be zero.
|
||||
///
|
||||
/// If `position` is zero, then it is at the beginning of a line.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `line` is zero.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(line: u16, position: u16) -> Self {
|
||||
assert!(line != 0, "line cannot be zero");
|
||||
|
||||
let _pos = position;
|
||||
|
||||
Self { line, pos: _pos }
|
||||
}
|
||||
/// Get the line number (1-based), or [`None`] if there is no position.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn line(self) -> Option<usize> {
|
||||
if self.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(self.line as usize)
|
||||
}
|
||||
}
|
||||
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn position(self) -> Option<usize> {
|
||||
if self.is_none() || self.pos == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.pos as usize)
|
||||
}
|
||||
}
|
||||
/// Advance by one character position.
|
||||
#[inline]
|
||||
pub(crate) fn advance(&mut self) {
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.pos < u16::MAX {
|
||||
self.pos += 1;
|
||||
}
|
||||
}
|
||||
/// Go backwards by one character position.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if already at beginning of a line - cannot rewind to a previous line.
|
||||
#[inline]
|
||||
pub(crate) fn rewind(&mut self) {
|
||||
assert!(!self.is_none(), "cannot rewind Position::none");
|
||||
assert!(self.pos > 0, "cannot rewind at position 0");
|
||||
self.pos -= 1;
|
||||
}
|
||||
/// Advance to the next line.
|
||||
#[inline]
|
||||
pub(crate) fn new_line(&mut self) {
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.line < u16::MAX {
|
||||
self.line += 1;
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
/// Is this [`Position`] at the beginning of a line?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_beginning_of_line(self) -> bool {
|
||||
self.pos == 0 && !self.is_none()
|
||||
}
|
||||
/// Is there no [`Position`]?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(self) -> bool {
|
||||
self.line == 0 && self.pos == 0
|
||||
}
|
||||
/// Returns an fallback [`Position`] if it is [`NONE`][Position::NONE]?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn or_else(self, pos: Self) -> Self {
|
||||
if self.is_none() {
|
||||
pos
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
/// Print this [`Position`] for debug purposes.
|
||||
#[inline]
|
||||
pub(crate) fn debug_print(self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !self.is_none() {
|
||||
write!(_f, " @ {:?}", self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::START
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
write!(f, "none")
|
||||
} else {
|
||||
write!(f, "line {}, position {}", self.line, self.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Position {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
f.write_str("none")
|
||||
} else {
|
||||
if self.is_beginning_of_line() {
|
||||
write!(f, "{}", self.line)
|
||||
} else {
|
||||
write!(f, "{}:{}", self.line, self.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Position {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_none() {
|
||||
self
|
||||
} else {
|
||||
Self {
|
||||
line: self.line + rhs.line - 1,
|
||||
pos: if rhs.is_beginning_of_line() {
|
||||
self.pos
|
||||
} else {
|
||||
self.pos + rhs.pos - 1
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Position {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A span consisting of a starting and an ending [positions][Position].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Span {
|
||||
/// Starting [position][Position].
|
||||
start: Position,
|
||||
/// Ending [position][Position].
|
||||
end: Position,
|
||||
}
|
||||
|
||||
impl Default for Span {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::NONE
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Empty [`Span`].
|
||||
pub const NONE: Self = Self::new(Position::NONE, Position::NONE);
|
||||
|
||||
/// Create a new [`Span`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(start: Position, end: Position) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
/// Is this [`Span`] non-existent?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(&self) -> bool {
|
||||
self.start.is_none() && self.end.is_none()
|
||||
}
|
||||
/// Get the [`Span`]'s starting [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn start(&self) -> Position {
|
||||
self.start
|
||||
}
|
||||
/// Get the [`Span`]'s ending [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn end(&self) -> Position {
|
||||
self.end
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let _f = f;
|
||||
|
||||
match (self.start(), self.end()) {
|
||||
(Position::NONE, Position::NONE) => write!(_f, "{:?}", Position::NONE),
|
||||
(Position::NONE, end) => write!(_f, "..{:?}", end),
|
||||
(start, Position::NONE) => write!(_f, "{:?}", start),
|
||||
(start, end) if start.line() != end.line() => {
|
||||
write!(_f, "{:?}-{:?}", start, end)
|
||||
}
|
||||
(start, end) => write!(
|
||||
_f,
|
||||
"{}:{}-{}",
|
||||
start.line().unwrap(),
|
||||
start.position().unwrap_or(0),
|
||||
end.position().unwrap_or(0)
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
165
src/types/position_none.rs
Normal file
165
src/types/position_none.rs
Normal file
@ -0,0 +1,165 @@
|
||||
#![cfg(feature = "no_position")]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
fmt,
|
||||
ops::{Add, AddAssign},
|
||||
};
|
||||
|
||||
/// A location (line number + character position) in the input script.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Position;
|
||||
|
||||
impl Position {
|
||||
/// A [`Position`] representing no position.
|
||||
pub const NONE: Self = Self;
|
||||
/// A [`Position`] representing the first position.
|
||||
pub const START: Self = Self;
|
||||
|
||||
/// Create a new [`Position`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(line: u16, position: u16) -> Self {
|
||||
Self
|
||||
}
|
||||
/// Get the line number (1-based), or [`None`] if there is no position.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn line(self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn position(self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
/// Advance by one character position.
|
||||
#[inline(always)]
|
||||
pub(crate) fn advance(&mut self) {}
|
||||
/// Go backwards by one character position.
|
||||
#[inline(always)]
|
||||
pub(crate) fn rewind(&mut self) {}
|
||||
/// Advance to the next line.
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_line(&mut self) {}
|
||||
/// Is this [`Position`] at the beginning of a line?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_beginning_of_line(self) -> bool {
|
||||
false
|
||||
}
|
||||
/// Is there no [`Position`]?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_none(self) -> bool {
|
||||
true
|
||||
}
|
||||
/// Returns an fallback [`Position`] if it is [`NONE`][Position::NONE]?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn or_else(self, pos: Self) -> Self {
|
||||
pos
|
||||
}
|
||||
/// Print this [`Position`] for debug purposes.
|
||||
#[inline(always)]
|
||||
pub(crate) fn debug_print(self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "none")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Position {
|
||||
#[cold]
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("none")
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Position {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Position {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: Self) {}
|
||||
}
|
||||
|
||||
/// _(internals)_ A span consisting of a starting and an ending [positions][Position].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Span;
|
||||
|
||||
impl Default for Span {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Empty [`Span`].
|
||||
pub const NONE: Self = Self;
|
||||
|
||||
/// Create a new [`Span`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(start: Position, end: Position) -> Self {
|
||||
Self
|
||||
}
|
||||
/// Is this [`Span`] non-existent?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_none(&self) -> bool {
|
||||
true
|
||||
}
|
||||
/// Get the [`Span`]'s starting [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn start(&self) -> Position {
|
||||
Position
|
||||
}
|
||||
/// Get the [`Span`]'s ending [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn end(&self) -> Position {
|
||||
Position
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let f = f;
|
||||
write!(f, "{:?}", Position)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
#[cold]
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user