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