rhai/src/parser.rs

3773 lines
126 KiB
Rust
Raw Normal View History

2020-03-08 12:54:02 +01:00
//! Main module defining the lexer and parser.
2020-04-12 17:00:06 +02:00
use crate::any::{Dynamic, Union};
use crate::engine::{
Engine, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT,
};
2020-03-04 15:00:01 +01:00
use crate::error::{LexError, ParseError, ParseErrorType};
2020-08-03 17:12:42 +02:00
use crate::fn_native::{FnPtr, Shared};
use crate::module::{Module, ModuleRef};
use crate::optimize::{optimize_into_ast, OptimizationLevel};
2020-04-10 06:16:39 +02:00
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::syntax::FnCustomSyntaxEval;
2020-07-26 16:25:30 +02:00
use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream};
2020-10-10 07:41:55 +02:00
use crate::utils::StraightHasherBuilder;
use crate::{calc_fn_hash, StaticVec};
2020-05-08 08:50:48 +02:00
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_object"))]
use crate::engine::{make_getter, make_setter, Map};
#[cfg(not(feature = "no_function"))]
use crate::engine::{FN_ANONYMOUS, KEYWORD_FN_PTR_CURRY};
2020-07-26 09:53:22 +02:00
2020-03-17 19:26:11 +01:00
use crate::stdlib::{
any::TypeId,
2020-03-17 19:26:11 +01:00
borrow::Cow,
boxed::Box,
2020-03-29 17:53:35 +02:00
char,
2020-07-31 16:30:23 +02:00
collections::HashMap,
fmt, format,
2020-07-19 03:10:22 +02:00
hash::{Hash, Hasher},
2020-06-11 17:08:00 +02:00
iter::empty,
2020-04-29 10:11:54 +02:00
num::NonZeroUsize,
ops::{Add, AddAssign},
2020-03-17 19:26:11 +01:00
string::{String, ToString},
vec,
2020-03-17 19:26:11 +01:00
vec::Vec,
2020-03-12 05:40:28 +01:00
};
2016-02-29 22:43:45 +01:00
2020-07-19 11:14:55 +02:00
#[cfg(not(feature = "no_std"))]
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_function"))]
2020-07-19 11:14:55 +02:00
use crate::stdlib::collections::hash_map::DefaultHasher;
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-31 16:30:23 +02:00
use crate::stdlib::collections::HashSet;
2020-07-19 11:14:55 +02:00
#[cfg(feature = "no_std")]
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_function"))]
2020-07-19 11:14:55 +02:00
use ahash::AHasher;
2020-03-11 04:03:18 +01:00
/// The system integer type.
///
/// If the `only_i32` feature is enabled, this will be `i32` instead.
#[cfg(not(feature = "only_i32"))]
pub type INT = i64;
2020-03-18 03:36:50 +01:00
/// The system integer type.
2020-03-11 04:03:18 +01:00
///
/// If the `only_i32` feature is not enabled, this will be `i64` instead.
#[cfg(feature = "only_i32")]
pub type INT = i32;
2020-03-18 03:36:50 +01:00
/// The system floating-point type.
2020-04-03 13:42:01 +02:00
///
/// Not available under the `no_float` feature.
#[cfg(not(feature = "no_float"))]
pub type FLOAT = f64;
2020-03-01 06:30:22 +01:00
type PERR = ParseErrorType;
2020-05-26 08:14:03 +02:00
pub use crate::utils::ImmutableString;
type FunctionsLib = HashMap<u64, ScriptFnDef, StraightHasherBuilder>;
2020-03-04 15:00:01 +01:00
/// Compiled AST (abstract syntax tree) of a Rhai script.
2020-04-03 13:42:01 +02:00
///
2020-07-06 04:02:54 +02:00
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
2020-05-05 09:00:10 +02:00
#[derive(Debug, Clone, Default)]
pub struct AST(
2020-05-05 09:00:10 +02:00
/// Global statements.
Vec<Stmt>,
2020-05-19 16:25:57 +02:00
/// Script-defined functions.
Module,
);
2020-03-27 04:50:24 +01:00
impl AST {
2020-07-06 04:02:54 +02:00
/// Create a new `AST`.
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn new(statements: Vec<Stmt>, lib: Module) -> Self {
Self(statements, lib)
2020-05-05 09:00:10 +02:00
}
/// Get the statements.
2020-06-23 04:43:24 +02:00
#[cfg(not(feature = "internals"))]
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-06 07:01:57 +02:00
pub(crate) fn statements(&self) -> &[Stmt] {
2020-05-05 09:00:10 +02:00
&self.0
}
2020-10-03 12:49:11 +02:00
/// _[INTERNALS]_ Get the statements.
/// Exported under the `internals` feature only.
2020-06-23 04:43:24 +02:00
#[cfg(feature = "internals")]
2020-06-25 05:07:46 +02:00
#[deprecated(note = "this method is volatile and may change")]
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-06 07:01:57 +02:00
pub fn statements(&self) -> &[Stmt] {
2020-06-23 04:43:24 +02:00
&self.0
}
2020-05-05 09:00:10 +02:00
/// Get a mutable reference to the statements.
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-05-05 09:00:10 +02:00
pub(crate) fn statements_mut(&mut self) -> &mut Vec<Stmt> {
&mut self.0
}
2020-06-23 04:43:24 +02:00
/// Get the internal `Module` containing all script-defined functions.
#[cfg(not(feature = "internals"))]
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub(crate) fn lib(&self) -> &Module {
2020-06-23 04:43:24 +02:00
&self.1
}
2020-10-03 12:49:11 +02:00
/// _[INTERNALS]_ Get the internal `Module` containing all script-defined functions.
/// Exported under the `internals` feature only.
2020-06-23 04:43:24 +02:00
#[cfg(feature = "internals")]
2020-06-25 05:07:46 +02:00
#[deprecated(note = "this method is volatile and may change")]
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-06-23 04:43:24 +02:00
pub fn lib(&self) -> &Module {
2020-05-19 16:25:57 +02:00
&self.1
}
2020-07-06 04:02:54 +02:00
/// Clone the `AST`'s functions into a new `AST`.
/// No statements are cloned.
///
/// This operation is cheap because functions are shared.
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-06 04:02:54 +02:00
pub fn clone_functions_only(&self) -> Self {
self.clone_functions_only_filtered(|_, _, _| true)
}
/// Clone the `AST`'s functions into a new `AST` based on a filter predicate.
/// No statements are cloned.
///
/// This operation is cheap because functions are shared.
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-06 04:02:54 +02:00
pub fn clone_functions_only_filtered(
&self,
2020-09-24 16:50:28 +02:00
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
2020-07-06 04:02:54 +02:00
) -> Self {
let mut functions: Module = Default::default();
2020-09-24 16:50:28 +02:00
functions.merge_filtered(&self.1, &mut filter);
2020-07-06 04:02:54 +02:00
Self(Default::default(), functions)
}
/// Clone the `AST`'s script statements into a new `AST`.
/// No functions are cloned.
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-06 04:02:54 +02:00
pub fn clone_statements_only(&self) -> Self {
Self(self.0.clone(), Default::default())
}
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
2020-03-27 04:50:24 +01:00
/// is returned.
///
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
2020-07-06 04:02:54 +02:00
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
/// Of course, if the first `AST` uses a `return` statement at the end, then
/// the second `AST` will essentially be dead code.
2020-03-27 04:50:24 +01:00
///
2020-07-06 04:02:54 +02:00
/// All script-defined functions in the second `AST` overwrite similarly-named functions
/// in the first `AST` with the same number of parameters.
2020-03-27 04:50:24 +01:00
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
2020-04-01 04:21:49 +02:00
/// # #[cfg(not(feature = "no_function"))]
/// # {
2020-03-27 04:50:24 +01:00
/// use rhai::Engine;
///
/// let engine = Engine::new();
2020-03-27 04:50:24 +01:00
///
/// let ast1 = engine.compile(r#"
/// fn foo(x) { 42 + x }
/// foo(1)
/// "#)?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { "hello" + n }
/// foo("!")
/// "#)?;
2020-03-27 04:50:24 +01:00
///
/// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1'
2020-03-27 04:50:24 +01:00
///
/// // Notice that using the '+' operator also works:
/// // let ast = &ast1 + &ast2;
2020-03-27 04:50:24 +01:00
///
/// // 'ast' is essentially:
/// //
/// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten
/// // foo(1) // <- notice this will be "hello1" instead of 43,
/// // // but it is no longer the return value
2020-04-01 04:21:49 +02:00
/// // foo("!") // returns "hello!"
2020-03-27 04:50:24 +01:00
///
/// // Evaluate it
2020-04-01 04:21:49 +02:00
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "hello!");
/// # }
2020-03-27 04:50:24 +01:00
/// # Ok(())
/// # }
/// ```
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn merge(&self, other: &Self) -> Self {
self.merge_filtered(other, |_, _, _| true)
}
/// Combine one `AST` with another. The second `AST` is consumed.
///
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
/// Of course, if the first `AST` uses a `return` statement at the end, then
/// the second `AST` will essentially be dead code.
///
/// All script-defined functions in the second `AST` overwrite similarly-named functions
/// in the first `AST` with the same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let mut ast1 = engine.compile(r#"
/// fn foo(x) { 42 + x }
/// foo(1)
/// "#)?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { "hello" + n }
/// foo("!")
/// "#)?;
///
/// ast1.combine(ast2); // Combine 'ast2' into 'ast1'
///
/// // Notice that using the '+=' operator also works:
/// // ast1 += ast2;
///
/// // 'ast1' is essentially:
/// //
/// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten
/// // foo(1) // <- notice this will be "hello1" instead of 43,
/// // // but it is no longer the return value
/// // foo("!") // returns "hello!"
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "hello!");
/// # }
/// # Ok(())
/// # }
/// ```
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn combine(&mut self, other: Self) -> &mut Self {
self.combine_filtered(other, |_, _, _| true)
}
2020-07-06 04:02:54 +02:00
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
/// is returned.
///
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
2020-07-06 04:02:54 +02:00
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
/// Of course, if the first `AST` uses a `return` statement at the end, then
/// the second `AST` will essentially be dead code.
///
2020-07-06 04:02:54 +02:00
/// All script-defined functions in the second `AST` are first selected based on a filter
/// predicate, then overwrite similarly-named functions in the first `AST` with the
/// same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let ast1 = engine.compile(r#"
/// fn foo(x) { 42 + x }
/// foo(1)
/// "#)?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { "hello" + n }
/// fn error() { 0 }
/// foo("!")
/// "#)?;
///
/// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1'
/// let ast = ast1.merge_filtered(&ast2, |_, name, params| name == "error" && params == 0);
///
/// // 'ast' is essentially:
/// //
/// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten
/// // // because 'ast2::foo' is filtered away
/// // foo(1) // <- notice this will be 43 instead of "hello1",
/// // // but it is no longer the return value
/// // fn error() { 0 } // <- this function passes the filter and is merged
/// // foo("!") // <- returns "42!"
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "42!");
/// # }
/// # Ok(())
/// # }
/// ```
2020-10-08 16:25:50 +02:00
#[inline]
pub fn merge_filtered(
&self,
other: &Self,
2020-09-24 16:50:28 +02:00
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
) -> Self {
2020-04-05 11:44:48 +02:00
let Self(statements, functions) = self;
2020-04-05 11:44:48 +02:00
let ast = match (statements.is_empty(), other.0.is_empty()) {
(false, false) => {
2020-04-05 11:44:48 +02:00
let mut statements = statements.clone();
statements.extend(other.0.iter().cloned());
statements
}
2020-04-05 11:44:48 +02:00
(false, true) => statements.clone(),
(true, false) => other.0.clone(),
(true, true) => vec![],
};
2020-03-27 04:50:24 +01:00
let mut functions = functions.clone();
2020-09-24 16:50:28 +02:00
functions.merge_filtered(&other.1, &mut filter);
Self::new(ast, functions)
}
2020-03-27 04:50:24 +01:00
/// Combine one `AST` with another. The second `AST` is consumed.
///
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
/// Of course, if the first `AST` uses a `return` statement at the end, then
/// the second `AST` will essentially be dead code.
///
/// All script-defined functions in the second `AST` are first selected based on a filter
/// predicate, then overwrite similarly-named functions in the first `AST` with the
/// same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let mut ast1 = engine.compile(r#"
/// fn foo(x) { 42 + x }
/// foo(1)
/// "#)?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { "hello" + n }
/// fn error() { 0 }
/// foo("!")
/// "#)?;
///
/// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1'
/// ast1.combine_filtered(ast2, |_, name, params| name == "error" && params == 0);
///
/// // 'ast1' is essentially:
/// //
/// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten
/// // // because 'ast2::foo' is filtered away
/// // foo(1) // <- notice this will be 43 instead of "hello1",
/// // // but it is no longer the return value
/// // fn error() { 0 } // <- this function passes the filter and is merged
/// // foo("!") // <- returns "42!"
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "42!");
/// # }
/// # Ok(())
/// # }
/// ```
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn combine_filtered(
&mut self,
other: Self,
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
) -> &mut Self {
let Self(ref mut statements, ref mut functions) = self;
if !other.0.is_empty() {
statements.extend(other.0.into_iter());
}
functions.merge_filtered(&other.1, &mut filter);
self
}
/// Filter out the functions, retaining only some based on a filter predicate.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let mut ast = engine.compile(r#"
/// fn foo(n) { n + 1 }
/// fn bar() { print("hello"); }
/// "#)?;
///
/// // Remove all functions except 'foo(_)'
/// ast.retain_functions(|_, name, params| name == "foo" && params == 1);
/// # }
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_function"))]
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-09-24 16:50:28 +02:00
pub fn retain_functions(&mut self, filter: impl FnMut(FnAccess, &str, usize) -> bool) {
self.1.retain_functions(filter);
}
/// Iterate through all functions
#[cfg(not(feature = "no_function"))]
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-10-02 17:14:33 +02:00
pub fn iter_functions<'a>(
&'a self,
) -> impl Iterator<Item = (FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a {
self.1.iter_script_fn()
}
2020-07-06 04:02:54 +02:00
/// Clear all function definitions in the `AST`.
#[cfg(not(feature = "no_function"))]
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn clear_functions(&mut self) {
2020-05-19 16:25:57 +02:00
self.1 = Default::default();
}
2020-03-27 04:50:24 +01:00
2020-07-06 04:02:54 +02:00
/// Clear all statements in the `AST`, leaving only function definitions.
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn clear_statements(&mut self) {
self.0 = vec![];
2020-03-27 04:50:24 +01:00
}
}
impl<A: AsRef<AST>> Add<A> for &AST {
type Output = AST;
2020-03-27 04:50:24 +01:00
2020-10-08 16:25:50 +02:00
#[inline(always)]
fn add(self, rhs: A) -> Self::Output {
self.merge(rhs.as_ref())
2020-03-27 04:50:24 +01:00
}
}
impl<A: Into<AST>> AddAssign<A> for AST {
2020-10-08 16:25:50 +02:00
#[inline(always)]
fn add_assign(&mut self, rhs: A) {
self.combine(rhs.into());
}
}
2020-07-06 07:01:57 +02:00
impl AsRef<[Stmt]> for AST {
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-06 07:01:57 +02:00
fn as_ref(&self) -> &[Stmt] {
self.statements()
}
}
impl AsRef<Module> for AST {
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-06 07:01:57 +02:00
fn as_ref(&self) -> &Module {
self.lib()
}
}
/// A type representing the access mode of a scripted function.
2020-05-12 12:48:25 +02:00
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnAccess {
/// Public function.
Public,
2020-10-01 17:31:27 +02:00
/// Private function.
Private,
}
impl fmt::Display for FnAccess {
2020-10-08 16:25:50 +02:00
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Private => write!(f, "private"),
Self::Public => write!(f, "public"),
}
}
}
2020-10-01 17:31:27 +02:00
impl FnAccess {
/// Is this access mode private?
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-10-01 17:31:27 +02:00
pub fn is_private(self) -> bool {
match self {
Self::Public => false,
Self::Private => true,
}
}
/// Is this access mode public?
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-10-01 17:31:27 +02:00
pub fn is_public(self) -> bool {
match self {
Self::Public => true,
Self::Private => false,
}
}
}
2020-10-03 12:49:11 +02:00
/// _[INTERNALS]_ A type containing information on a scripted function.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone)]
pub struct ScriptFnDef {
2020-03-18 03:36:50 +01:00
/// Function name.
pub name: ImmutableString,
/// Function access mode.
pub access: FnAccess,
2020-03-18 03:36:50 +01:00
/// Names of function parameters.
pub params: StaticVec<String>,
2020-07-30 07:28:06 +02:00
/// Access to external variables.
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
pub externals: HashSet<String>,
2020-03-18 03:36:50 +01:00
/// Function body.
pub body: Stmt,
2020-03-18 03:36:50 +01:00
/// Position of the function definition.
pub pos: Position,
/// Encapsulated running environment, if any.
pub lib: Option<Shared<Module>>,
}
impl fmt::Display for ScriptFnDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{}({})",
match self.access {
FnAccess::Public => "",
FnAccess::Private => "private ",
},
self.name,
self.params
.iter()
.map(|s| s.as_str())
.collect::<Vec<_>>()
.join(",")
)
}
}
2020-10-03 12:49:11 +02:00
/// _[INTERNALS]_ A type encapsulating the mode of a `return`/`throw` statement.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
2020-03-11 16:43:04 +01:00
pub enum ReturnType {
2020-03-18 03:36:50 +01:00
/// `return` statement.
2020-03-11 16:43:04 +01:00
Return,
2020-03-18 03:36:50 +01:00
/// `throw` statement.
2020-03-11 16:43:04 +01:00
Exception,
}
2020-07-05 11:41:45 +02:00
#[derive(Clone)]
struct ParseState<'e> {
/// Reference to the scripting `Engine`.
engine: &'e Engine,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
2020-07-05 11:41:45 +02:00
stack: Vec<(String, ScopeEntryType)>,
2020-07-29 16:43:50 +02:00
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-29 17:34:48 +02:00
externals: HashMap<String, Position>,
/// An indicator that disables variable capturing into externals one single time
/// up until the nearest consumed Identifier token.
2020-08-03 06:10:20 +02:00
/// If set to false the next call to `access_var` will not capture the variable.
/// All consequent calls to `access_var` will not be affected
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
allow_capture: bool,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
2020-07-05 11:41:45 +02:00
modules: Vec<String>,
/// Maximum levels of expression nesting.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
2020-07-05 11:41:45 +02:00
max_expr_depth: usize,
/// Maximum levels of expression nesting in functions.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
max_function_expr_depth: usize,
}
2020-04-28 17:05:03 +02:00
2020-07-05 11:41:45 +02:00
impl<'e> ParseState<'e> {
/// Create a new `ParseState`.
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-26 09:53:22 +02:00
pub fn new(
engine: &'e Engine,
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
#[cfg(not(feature = "unchecked"))] max_function_expr_depth: usize,
) -> Self {
Self {
2020-07-05 11:41:45 +02:00
engine,
2020-07-29 16:43:50 +02:00
#[cfg(not(feature = "unchecked"))]
max_expr_depth,
#[cfg(not(feature = "unchecked"))]
max_function_expr_depth,
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-29 16:43:50 +02:00
externals: Default::default(),
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
allow_capture: true,
2020-07-05 11:41:45 +02:00
stack: Default::default(),
modules: Default::default(),
}
2020-04-28 17:05:03 +02:00
}
2020-07-29 16:43:50 +02:00
/// Find explicitly declared variable by name in the `ParseState`, searching in reverse order.
///
2020-07-29 16:43:50 +02:00
/// If the variable is not present in the scope adds it to the list of external variables
///
2020-04-29 10:11:54 +02:00
/// The return value is the offset to be deducted from `Stack::len`,
/// i.e. the top element of the `ParseState` is offset 1.
/// Return `None` when the variable name is not found in the `stack`.
2020-10-08 16:25:50 +02:00
#[inline]
2020-07-30 07:28:06 +02:00
fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
2020-07-29 16:43:50 +02:00
let index = self
.stack
2020-04-28 17:05:03 +02:00
.iter()
.rev()
.enumerate()
.find(|(_, (n, _))| *n == name)
.and_then(|(i, _)| NonZeroUsize::new(i + 1));
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
if self.allow_capture {
if index.is_none() && !self.externals.contains_key(name) {
self.externals.insert(name.to_string(), _pos);
}
} else {
2020-08-03 06:10:20 +02:00
self.allow_capture = true
}
2020-07-29 16:43:50 +02:00
index
2020-05-04 13:36:58 +02:00
}
/// Find a module by name in the `ParseState`, searching in reverse.
2020-05-04 13:36:58 +02:00
/// The return value is the offset to be deducted from `Stack::len`,
/// i.e. the top element of the `ParseState` is offset 1.
/// Return `None` when the variable name is not found in the `ParseState`.
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
self.modules
2020-05-04 13:36:58 +02:00
.iter()
.rev()
.enumerate()
.find(|(_, n)| *n == name)
2020-04-29 10:11:54 +02:00
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
2020-04-28 17:05:03 +02:00
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
/// A type that encapsulates all the settings for a particular parsing function.
struct ParseSettings {
/// Current position.
pos: Position,
/// Is the construct being parsed located at global level?
is_global: bool,
2020-07-16 06:09:31 +02:00
/// Is the construct being parsed located at function definition level?
is_function_scope: bool,
/// Is the current position inside a loop?
is_breakable: bool,
/// Is anonymous function allowed?
allow_anonymous_fn: bool,
/// Is if-expression allowed?
allow_if_expr: bool,
/// Is statement-expression allowed?
allow_stmt_expr: bool,
/// Current expression nesting level.
level: usize,
}
impl ParseSettings {
/// Create a new `ParseSettings` with one higher expression level.
2020-10-08 16:25:50 +02:00
#[inline(always)]
pub fn level_up(&self) -> Self {
Self {
level: self.level + 1,
..*self
}
}
/// Make sure that the current level of expression nesting is within the maximum limit.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
2020-10-08 16:25:50 +02:00
#[inline]
pub fn ensure_level_within_max_limit(&self, limit: usize) -> Result<(), ParseError> {
2020-06-14 08:25:47 +02:00
if limit == 0 {
Ok(())
} else if self.level > limit {
Err(PERR::ExprTooDeep.into_err(self.pos))
} else {
Ok(())
}
}
}
2020-10-03 12:49:11 +02:00
/// _[INTERNALS]_ A Rhai statement.
/// Exported under the `internals` feature only.
2020-05-09 18:19:13 +02:00
///
/// Each variant is at most one pointer in size (for speed),
/// with everything being allocated together in one single tuple.
#[derive(Debug, Clone, Hash)]
pub enum Stmt {
2020-03-18 03:36:50 +01:00
/// No-op.
2020-03-09 14:57:07 +01:00
Noop(Position),
2020-03-18 03:36:50 +01:00
/// if expr { stmt } else { stmt }
IfThenElse(Box<(Expr, Stmt, Option<Stmt>, Position)>),
2020-03-18 03:36:50 +01:00
/// while expr { stmt }
While(Box<(Expr, Stmt, Position)>),
2020-03-18 03:36:50 +01:00
/// loop { stmt }
Loop(Box<(Stmt, Position)>),
2020-03-18 03:36:50 +01:00
/// for id in expr { stmt }
For(Box<(String, Expr, Stmt, Position)>),
2020-03-18 03:36:50 +01:00
/// let id = expr
Let(Box<((String, Position), Option<Expr>, Position)>),
2020-03-18 03:36:50 +01:00
/// const id = expr
2020-10-09 05:15:25 +02:00
Const(Box<((String, Position), Option<Expr>, Position)>),
2020-03-18 03:36:50 +01:00
/// { stmt; ... }
Block(Box<(StaticVec<Stmt>, Position)>),
/// expr
Expr(Box<Expr>),
2020-04-01 10:22:18 +02:00
/// continue
Continue(Position),
2020-03-18 03:36:50 +01:00
/// break
Break(Position),
2020-05-04 11:43:54 +02:00
/// return/throw
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>, Position)>),
2020-05-04 13:36:58 +02:00
/// import expr as module
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
2020-10-02 12:52:18 +02:00
Import(Box<(Expr, Option<(ImmutableString, Position)>, Position)>),
2020-05-08 10:49:24 +02:00
/// expr id as name, ...
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
Export(
Box<(
StaticVec<((String, Position), Option<(String, Position)>)>,
Position,
)>,
),
2020-08-03 06:10:20 +02:00
/// Convert a variable to shared.
#[cfg(not(feature = "no_closure"))]
Share(Box<(String, Position)>),
}
impl Default for Stmt {
2020-10-08 16:25:50 +02:00
#[inline(always)]
fn default() -> Self {
Self::Noop(Default::default())
}
}
2016-02-29 22:43:45 +01:00
2020-03-09 14:57:07 +01:00
impl Stmt {
2020-03-18 03:36:50 +01:00
/// Get the `Position` of this statement.
2020-03-17 10:33:37 +01:00
pub fn position(&self) -> Position {
match self {
Stmt::Noop(pos) | Stmt::Continue(pos) | Stmt::Break(pos) => *pos,
2020-05-09 18:19:13 +02:00
Stmt::Let(x) => (x.0).1,
Stmt::Const(x) => (x.0).1,
Stmt::ReturnWithVal(x) => (x.0).1,
Stmt::Block(x) => x.1,
Stmt::IfThenElse(x) => x.3,
Stmt::Expr(x) => x.position(),
Stmt::While(x) => x.2,
Stmt::Loop(x) => x.1,
Stmt::For(x) => x.3,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => x.2,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
Stmt::Export(x) => x.1,
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => x.1,
2020-03-17 10:33:37 +01:00
}
}
/// Override the `Position` of this statement.
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Stmt::Noop(pos) | Stmt::Continue(pos) | Stmt::Break(pos) => *pos = new_pos,
Stmt::Let(x) => (x.0).1 = new_pos,
Stmt::Const(x) => (x.0).1 = new_pos,
Stmt::ReturnWithVal(x) => (x.0).1 = new_pos,
Stmt::Block(x) => x.1 = new_pos,
Stmt::IfThenElse(x) => x.3 = new_pos,
Stmt::Expr(x) => {
x.set_position(new_pos);
}
Stmt::While(x) => x.2 = new_pos,
Stmt::Loop(x) => x.1 = new_pos,
Stmt::For(x) => x.3 = new_pos,
#[cfg(not(feature = "no_module"))]
Stmt::Import(x) => x.2 = new_pos,
#[cfg(not(feature = "no_module"))]
Stmt::Export(x) => x.1 = new_pos,
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => x.1 = new_pos,
}
self
}
2020-03-18 03:36:50 +01:00
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
pub fn is_self_terminated(&self) -> bool {
match self {
Stmt::IfThenElse(_)
| Stmt::While(_)
| Stmt::Loop(_)
| Stmt::For(_)
| Stmt::Block(_) => true,
2020-03-18 11:41:18 +01:00
// A No-op requires a semicolon in order to know it is an empty statement!
Stmt::Noop(_) => false,
Stmt::Let(_)
| Stmt::Const(_)
| Stmt::Expr(_)
2020-04-01 10:22:18 +02:00
| Stmt::Continue(_)
| Stmt::Break(_)
| Stmt::ReturnWithVal(_) => false,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
Stmt::Import(_) | Stmt::Export(_) => false,
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
Stmt::Share(_) => false,
}
}
2020-03-18 03:36:50 +01:00
/// Is this statement _pure_?
2020-03-17 10:33:37 +01:00
pub fn is_pure(&self) -> bool {
2020-03-11 16:43:04 +01:00
match self {
2020-03-17 10:33:37 +01:00
Stmt::Noop(_) => true,
Stmt::Expr(expr) => expr.is_pure(),
Stmt::IfThenElse(x) if x.2.is_some() => {
x.0.is_pure() && x.1.is_pure() && x.2.as_ref().unwrap().is_pure()
2020-03-17 10:33:37 +01:00
}
Stmt::IfThenElse(x) => x.1.is_pure(),
Stmt::While(x) => x.0.is_pure() && x.1.is_pure(),
Stmt::Loop(x) => x.0.is_pure(),
Stmt::For(x) => x.1.is_pure() && x.2.is_pure(),
Stmt::Let(_) | Stmt::Const(_) => false,
Stmt::Block(x) => x.0.iter().all(Stmt::is_pure),
Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_) => false,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
Stmt::Import(_) => false,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_module"))]
2020-05-08 10:49:24 +02:00
Stmt::Export(_) => false,
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
Stmt::Share(_) => false,
2020-03-11 16:43:04 +01:00
}
}
2020-03-09 14:57:07 +01:00
}
2020-10-03 12:49:11 +02:00
/// _[INTERNALS]_ A type wrapping a custom syntax definition.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
2020-07-09 13:54:28 +02:00
#[derive(Clone)]
pub struct CustomExpr(pub StaticVec<Expr>, pub Shared<FnCustomSyntaxEval>);
impl fmt::Debug for CustomExpr {
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-09 13:54:28 +02:00
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl Hash for CustomExpr {
2020-10-08 16:25:50 +02:00
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
2020-10-03 12:49:11 +02:00
/// _[INTERNALS]_ A type wrapping a floating-point number.
/// Exported under the `internals` feature only.
///
/// This type is mainly used to provide a standard `Hash` implementation
/// to floating-point numbers, allowing `Expr` to derive `Hash` automatically.
///
/// ## WARNING
///
/// This type is volatile and may change.
2020-07-19 11:14:55 +02:00
#[cfg(not(feature = "no_float"))]
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct FloatWrapper(pub FLOAT, pub Position);
2020-07-20 14:23:35 +02:00
#[cfg(not(feature = "no_float"))]
2020-07-19 11:14:55 +02:00
impl Hash for FloatWrapper {
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-07-19 11:14:55 +02:00
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.0.to_le_bytes());
self.1.hash(state);
}
}
2020-10-03 12:49:11 +02:00
/// _[INTERNALS]_ An expression sub-tree.
/// Exported under the `internals` feature only.
2020-05-09 18:19:13 +02:00
///
/// Each variant is at most one pointer in size (for speed),
/// with everything being allocated together in one single tuple.
///
/// ## WARNING
///
/// This type is volatile and may change.
2020-07-19 11:14:55 +02:00
#[derive(Debug, Clone, Hash)]
pub enum Expr {
2020-03-18 03:36:50 +01:00
/// Integer constant.
IntegerConstant(Box<(INT, Position)>),
2020-03-18 03:36:50 +01:00
/// Floating-point constant.
#[cfg(not(feature = "no_float"))]
2020-07-19 11:14:55 +02:00
FloatConstant(Box<FloatWrapper>),
2020-03-18 03:36:50 +01:00
/// Character constant.
CharConstant(Box<(char, Position)>),
2020-03-18 03:36:50 +01:00
/// String constant.
2020-05-25 07:44:28 +02:00
StringConstant(Box<(ImmutableString, Position)>),
2020-07-19 11:14:55 +02:00
/// FnPtr constant.
FnPointer(Box<(ImmutableString, Position)>),
2020-05-09 18:19:13 +02:00
/// Variable access - ((variable name, position), optional modules, hash, optional index)
Variable(
Box<(
(String, Position),
Option<Box<ModuleRef>>,
u64,
Option<NonZeroUsize>,
)>,
),
2020-03-18 03:36:50 +01:00
/// Property access.
Property(Box<((ImmutableString, String, String), Position)>),
2020-03-18 03:36:50 +01:00
/// { stmt }
Stmt(Box<(Stmt, Position)>),
2020-05-30 04:27:48 +02:00
/// Wrapped expression - should not be optimized away.
Expr(Box<Expr>),
2020-07-30 12:18:28 +02:00
/// func(expr, ... ) - ((function name, native_only, capture, position), optional modules, hash, arguments, optional default value)
2020-04-28 13:39:36 +02:00
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
/// and the function names are predictable, so no need to allocate a new `String`.
FnCall(
Box<(
2020-07-30 12:18:28 +02:00
(Cow<'static, str>, bool, bool, Position),
Option<Box<ModuleRef>>,
u64,
StaticVec<Expr>,
Option<bool>, // Default value is `bool` in order for `Expr` to be `Hash`.
)>,
2020-05-03 19:19:01 +02:00
),
2020-05-25 14:14:31 +02:00
/// expr op= expr
Assignment(Box<(Expr, Cow<'static, str>, Expr, Position)>),
2020-03-18 03:36:50 +01:00
/// lhs.rhs
Dot(Box<(Expr, Expr, Position)>),
2020-03-18 03:36:50 +01:00
/// expr[expr]
Index(Box<(Expr, Expr, Position)>),
2020-03-18 03:36:50 +01:00
/// [ expr, ... ]
Array(Box<(StaticVec<Expr>, Position)>),
2020-03-30 11:40:26 +02:00
/// #{ name:expr, ... }
Map(Box<(StaticVec<((ImmutableString, Position), Expr)>, Position)>),
2020-04-06 11:47:34 +02:00
/// lhs in rhs
In(Box<(Expr, Expr, Position)>),
2020-03-18 03:36:50 +01:00
/// lhs && rhs
And(Box<(Expr, Expr, Position)>),
2020-03-18 03:36:50 +01:00
/// lhs || rhs
Or(Box<(Expr, Expr, Position)>),
2020-03-18 03:36:50 +01:00
/// true
True(Position),
2020-03-18 03:36:50 +01:00
/// false
False(Position),
2020-03-18 03:36:50 +01:00
/// ()
Unit(Position),
2020-07-09 13:54:28 +02:00
/// Custom syntax
Custom(Box<(CustomExpr, Position)>),
}
impl Default for Expr {
2020-10-08 16:25:50 +02:00
#[inline(always)]
fn default() -> Self {
Self::Unit(Default::default())
}
}
impl Expr {
/// Get the type of an expression.
2020-03-18 03:36:50 +01:00
///
/// Returns `None` if the expression's result type is not constant.
pub fn get_type_id(&self) -> Option<TypeId> {
Some(match self {
Self::Expr(x) => return x.get_type_id(),
Self::IntegerConstant(_) => TypeId::of::<INT>(),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_) => TypeId::of::<FLOAT>(),
Self::CharConstant(_) => TypeId::of::<char>(),
Self::StringConstant(_) => TypeId::of::<ImmutableString>(),
Self::FnPointer(_) => TypeId::of::<FnPtr>(),
Self::True(_) | Self::False(_) | Self::In(_) | Self::And(_) | Self::Or(_) => {
TypeId::of::<bool>()
}
Self::Unit(_) => TypeId::of::<()>(),
#[cfg(not(feature = "no_index"))]
Self::Array(_) => TypeId::of::<Array>(),
#[cfg(not(feature = "no_object"))]
Self::Map(_) => TypeId::of::<Map>(),
_ => return None,
})
}
/// Get the `Dynamic` value of a constant expression.
2020-03-18 03:36:50 +01:00
///
/// Returns `None` if the expression is not constant.
pub fn get_constant_value(&self) -> Option<Dynamic> {
Some(match self {
Self::Expr(x) => return x.get_constant_value(),
2020-05-30 04:27:48 +02:00
Self::IntegerConstant(x) => x.0.into(),
2020-04-12 17:00:06 +02:00
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(x) => x.0.into(),
Self::CharConstant(x) => x.0.into(),
Self::StringConstant(x) => x.0.clone().into(),
2020-08-03 17:12:42 +02:00
Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
x.0.clone(),
Default::default(),
)))),
Self::True(_) => true.into(),
Self::False(_) => false.into(),
Self::Unit(_) => ().into(),
2020-04-12 17:00:06 +02:00
#[cfg(not(feature = "no_index"))]
Self::Array(x) if x.0.iter().all(Self::is_constant) => Dynamic(Union::Array(Box::new(
x.0.iter()
.map(|v| v.get_constant_value().unwrap())
.collect(),
))),
2020-04-12 17:00:06 +02:00
#[cfg(not(feature = "no_object"))]
2020-05-09 18:19:13 +02:00
Self::Map(x) if x.0.iter().all(|(_, v)| v.is_constant()) => {
2020-04-12 17:00:06 +02:00
Dynamic(Union::Map(Box::new(
x.0.iter()
.map(|((k, _), v)| (k.clone(), v.get_constant_value().unwrap()))
.collect(),
2020-04-12 17:00:06 +02:00
)))
}
2020-03-29 17:53:35 +02:00
_ => return None,
})
2020-03-13 11:12:41 +01:00
}
/// Is the expression a simple variable access?
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
match self {
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.0).0.as_str()),
_ => None,
}
}
2020-03-18 03:36:50 +01:00
/// Get the `Position` of the expression.
pub fn position(&self) -> Position {
match self {
2020-05-30 04:27:48 +02:00
Self::Expr(x) => x.position(),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(x) => x.1,
Self::IntegerConstant(x) => x.1,
Self::CharConstant(x) => x.1,
Self::StringConstant(x) => x.1,
2020-07-19 11:14:55 +02:00
Self::FnPointer(x) => x.1,
Self::Array(x) => x.1,
Self::Map(x) => x.1,
Self::Property(x) => x.1,
Self::Stmt(x) => x.1,
2020-05-09 18:19:13 +02:00
Self::Variable(x) => (x.0).1,
2020-07-30 12:18:28 +02:00
Self::FnCall(x) => (x.0).3,
2020-05-25 14:14:31 +02:00
Self::Assignment(x) => x.0.position(),
Self::And(x) | Self::Or(x) | Self::In(x) => x.2,
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos,
2020-05-25 14:14:31 +02:00
Self::Dot(x) | Self::Index(x) => x.0.position(),
2020-07-09 13:54:28 +02:00
Self::Custom(x) => x.1,
}
}
2020-03-10 04:22:41 +01:00
/// Override the `Position` of the expression.
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Self::Expr(x) => {
x.set_position(new_pos);
2020-05-30 04:27:48 +02:00
}
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(x) => x.1 = new_pos,
Self::IntegerConstant(x) => x.1 = new_pos,
Self::CharConstant(x) => x.1 = new_pos,
Self::StringConstant(x) => x.1 = new_pos,
2020-07-19 11:14:55 +02:00
Self::FnPointer(x) => x.1 = new_pos,
Self::Array(x) => x.1 = new_pos,
Self::Map(x) => x.1 = new_pos,
2020-05-09 18:19:13 +02:00
Self::Variable(x) => (x.0).1 = new_pos,
Self::Property(x) => x.1 = new_pos,
Self::Stmt(x) => x.1 = new_pos,
2020-07-30 12:18:28 +02:00
Self::FnCall(x) => (x.0).3 = new_pos,
Self::And(x) => x.2 = new_pos,
Self::Or(x) => x.2 = new_pos,
Self::In(x) => x.2 = new_pos,
Self::True(pos) => *pos = new_pos,
Self::False(pos) => *pos = new_pos,
Self::Unit(pos) => *pos = new_pos,
2020-05-25 14:14:31 +02:00
Self::Assignment(x) => x.3 = new_pos,
Self::Dot(x) => x.2 = new_pos,
Self::Index(x) => x.2 = new_pos,
2020-07-09 13:54:28 +02:00
Self::Custom(x) => x.1 = new_pos,
}
self
}
2020-03-18 03:36:50 +01:00
/// Is the expression pure?
2020-03-11 16:43:04 +01:00
///
/// A pure expression has no side effects.
pub fn is_pure(&self) -> bool {
2020-03-10 04:22:41 +01:00
match self {
2020-05-30 04:27:48 +02:00
Self::Expr(x) => x.is_pure(),
Self::Array(x) => x.0.iter().all(Self::is_pure),
Self::Index(x) | Self::And(x) | Self::Or(x) | Self::In(x) => {
2020-05-09 18:19:13 +02:00
let (lhs, rhs, _) = x.as_ref();
lhs.is_pure() && rhs.is_pure()
2020-04-06 11:47:34 +02:00
}
Self::Stmt(x) => x.0.is_pure(),
2020-03-17 10:33:37 +01:00
Self::Variable(_) => true,
2020-06-14 08:25:47 +02:00
_ => self.is_constant(),
2020-03-12 05:40:28 +01:00
}
}
2020-10-09 05:15:25 +02:00
/// Is the expression the unit `()` literal?
#[inline(always)]
pub fn is_unit(&self) -> bool {
match self {
Self::Unit(_) => true,
_ => false,
}
}
/// Is the expression a simple constant literal?
pub fn is_literal(&self) -> bool {
match self {
Self::Expr(x) => x.is_literal(),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_) => true,
Self::IntegerConstant(_)
| Self::CharConstant(_)
| Self::StringConstant(_)
| Self::FnPointer(_)
| Self::True(_)
| Self::False(_)
| Self::Unit(_) => true,
// An array literal is literal if all items are literals
Self::Array(x) => x.0.iter().all(Self::is_literal),
// An map literal is literal if all items are literals
Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_literal),
// Check in expression
Self::In(x) => match (&x.0, &x.1) {
(Self::StringConstant(_), Self::StringConstant(_))
| (Self::CharConstant(_), Self::StringConstant(_)) => true,
_ => false,
},
_ => false,
}
}
2020-03-18 03:36:50 +01:00
/// Is the expression a constant?
2020-03-12 05:40:28 +01:00
pub fn is_constant(&self) -> bool {
match self {
2020-05-30 04:27:48 +02:00
Self::Expr(x) => x.is_constant(),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_) => true,
Self::IntegerConstant(_)
| Self::CharConstant(_)
| Self::StringConstant(_)
2020-07-19 11:14:55 +02:00
| Self::FnPointer(_)
2020-04-06 11:47:34 +02:00
| Self::True(_)
| Self::False(_)
| Self::Unit(_) => true,
2020-03-10 04:22:41 +01:00
2020-03-18 03:36:50 +01:00
// An array literal is constant if all items are constant
Self::Array(x) => x.0.iter().all(Self::is_constant),
2020-04-06 11:47:34 +02:00
// An map literal is constant if all items are constant
2020-05-09 18:19:13 +02:00
Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant),
2020-04-06 11:47:34 +02:00
// Check in expression
Self::In(x) => match (&x.0, &x.1) {
(Self::StringConstant(_), Self::StringConstant(_))
| (Self::CharConstant(_), Self::StringConstant(_)) => true,
2020-04-06 11:47:34 +02:00
_ => false,
},
_ => false,
}
}
2020-04-17 13:00:52 +02:00
/// Is a particular token allowed as a postfix operator to this expression?
pub fn is_valid_postfix(&self, token: &Token) -> bool {
match self {
2020-05-30 04:27:48 +02:00
Self::Expr(x) => x.is_valid_postfix(token),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_) => false,
Self::IntegerConstant(_)
| Self::CharConstant(_)
2020-07-19 11:14:55 +02:00
| Self::FnPointer(_)
| Self::In(_)
| Self::And(_)
| Self::Or(_)
2020-04-26 12:04:07 +02:00
| Self::True(_)
| Self::False(_)
| Self::Unit(_)
| Self::Assignment(_) => false,
2020-04-26 12:04:07 +02:00
Self::StringConstant(_)
| Self::Stmt(_)
| Self::FnCall(_)
| Self::Dot(_)
| Self::Index(_)
| Self::Array(_)
| Self::Map(_) => match token {
#[cfg(not(feature = "no_index"))]
2020-04-17 13:00:52 +02:00
Token::LeftBracket => true,
_ => false,
},
Self::Variable(_) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true,
2020-07-30 12:18:28 +02:00
Token::Bang => true,
2020-05-04 11:43:54 +02:00
Token::DoubleColon => true,
_ => false,
},
Self::Property(_) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true,
2020-04-17 13:00:52 +02:00
_ => false,
},
2020-07-09 13:54:28 +02:00
Self::Custom(_) => false,
2020-04-17 13:00:52 +02:00
}
}
2020-04-26 12:04:07 +02:00
2020-04-26 13:37:32 +02:00
/// Convert a `Variable` into a `Property`. All other variants are untouched.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_object"))]
2020-10-08 16:25:50 +02:00
#[inline]
2020-04-26 12:04:07 +02:00
pub(crate) fn into_property(self) -> Self {
match self {
2020-05-09 18:19:13 +02:00
Self::Variable(x) if x.1.is_none() => {
let (name, pos) = x.0;
let getter = make_getter(&name);
let setter = make_setter(&name);
Self::Property(Box::new(((name.into(), getter, setter), pos)))
2020-05-09 18:19:13 +02:00
}
2020-04-26 12:04:07 +02:00
_ => self,
}
}
}
2016-02-29 22:43:45 +01:00
/// Consume a particular token, checking that it is the expected one.
2020-06-11 12:13:33 +02:00
fn eat_token(input: &mut TokenStream, token: Token) -> Position {
let (t, pos) = input.next().unwrap();
if t != token {
2020-06-16 16:14:46 +02:00
unreachable!(
"expecting {} (found {}) at {}",
token.syntax(),
t.syntax(),
pos
);
}
pos
}
/// Match a particular token, consuming it if matched.
2020-06-11 12:13:33 +02:00
fn match_token(input: &mut TokenStream, token: Token) -> Result<bool, ParseError> {
let (t, _) = input.peek().unwrap();
if *t == token {
eat_token(input, token);
Ok(true)
} else {
Ok(false)
}
}
2020-03-18 03:36:50 +01:00
/// Parse ( expr )
2020-06-11 12:13:33 +02:00
fn parse_paren_expr(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
settings: ParseSettings,
) -> Result<Expr, ParseError> {
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
if match_token(input, Token::RightParen)? {
return Ok(Expr::Unit(settings.pos));
}
let expr = parse_expr(input, state, lib, settings.level_up())?;
2016-02-29 22:43:45 +01:00
match input.next().unwrap() {
2020-03-18 03:36:50 +01:00
// ( xxx )
(Token::RightParen, _) => Ok(expr),
// ( <error>
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
2020-03-18 03:36:50 +01:00
// ( xxx ???
(_, pos) => Err(PERR::MissingToken(
2020-05-04 13:36:58 +02:00
Token::RightParen.into(),
"for a matching ( in this expression".into(),
)
2020-06-11 17:08:00 +02:00
.into_err(pos)),
2016-02-29 22:43:45 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Parse a function call.
2020-07-26 16:25:30 +02:00
fn parse_fn_call(
2020-06-11 12:13:33 +02:00
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
2020-04-28 17:05:03 +02:00
id: String,
2020-07-30 12:18:28 +02:00
capture: bool,
2020-05-23 18:29:06 +02:00
mut modules: Option<Box<ModuleRef>>,
2020-06-11 17:08:00 +02:00
settings: ParseSettings,
) -> Result<Expr, ParseError> {
2020-06-29 17:55:28 +02:00
let (token, token_pos) = input.peek().unwrap();
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut args = StaticVec::new();
2017-10-28 05:30:12 +02:00
match token {
2020-06-29 17:55:28 +02:00
// id( <EOF>
Token::EOF => {
2020-04-06 06:29:01 +02:00
return Err(PERR::MissingToken(
2020-05-04 13:36:58 +02:00
Token::RightParen.into(),
2020-04-06 06:29:01 +02:00
format!("to close the arguments list of this function call '{}'", id),
)
2020-06-29 17:55:28 +02:00
.into_err(*token_pos))
2020-04-06 06:29:01 +02:00
}
2020-06-29 17:55:28 +02:00
// id( <error>
Token::LexError(err) => return Err(err.into_err(*token_pos)),
2020-04-06 06:29:01 +02:00
// id()
Token::RightParen => {
eat_token(input, Token::RightParen);
2020-06-11 12:13:33 +02:00
let hash_script = if let Some(modules) = modules.as_mut() {
2020-07-29 10:10:06 +02:00
modules.set_index(state.find_module(&modules[0].0));
// Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + number of arguments.
// 2) Calculate a second hash with no qualifiers, empty function name,
// zero number of arguments, and the actual list of argument `TypeId`'s.
// 3) The final hash is the XOR of the two hashes.
let qualifiers = modules.iter().map(|(m, _)| m.as_str());
calc_fn_hash(qualifiers, &id, 0, empty())
} else {
// Qualifiers (none) + function name + no parameters.
calc_fn_hash(empty(), &id, 0, empty())
2020-05-09 10:15:50 +02:00
};
return Ok(Expr::FnCall(Box::new((
2020-07-30 12:18:28 +02:00
(id.into(), false, capture, settings.pos),
2020-05-04 13:36:58 +02:00
modules,
2020-06-11 12:13:33 +02:00
hash_script,
args,
None,
))));
2020-04-06 06:29:01 +02:00
}
// id...
_ => (),
2016-02-29 22:43:45 +01:00
}
let settings = settings.level_up();
2016-02-29 22:43:45 +01:00
loop {
2020-06-16 16:14:46 +02:00
match input.peek().unwrap() {
// id(...args, ) - handle trailing comma
(Token::RightParen, _) => (),
_ => args.push(parse_expr(input, state, lib, settings)?),
2020-06-16 16:14:46 +02:00
}
2016-02-29 22:43:45 +01:00
match input.peek().unwrap() {
// id(...args)
(Token::RightParen, _) => {
eat_token(input, Token::RightParen);
2020-05-04 11:43:54 +02:00
2020-06-11 12:13:33 +02:00
let hash_script = if let Some(modules) = modules.as_mut() {
2020-07-29 10:10:06 +02:00
modules.set_index(state.find_module(&modules[0].0));
// Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + number of arguments.
// 2) Calculate a second hash with no qualifiers, empty function name,
// zero number of arguments, and the actual list of argument `TypeId`'s.
// 3) The final hash is the XOR of the two hashes.
let qualifiers = modules.iter().map(|(m, _)| m.as_str());
calc_fn_hash(qualifiers, &id, args.len(), empty())
} else {
// Qualifiers (none) + function name + number of arguments.
calc_fn_hash(empty(), &id, args.len(), empty())
2020-05-09 10:15:50 +02:00
};
return Ok(Expr::FnCall(Box::new((
2020-07-30 12:18:28 +02:00
(id.into(), false, capture, settings.pos),
2020-05-04 13:36:58 +02:00
modules,
2020-06-11 12:13:33 +02:00
hash_script,
args,
None,
))));
}
// id(...args,
(Token::Comma, _) => {
eat_token(input, Token::Comma);
}
// id(...args <EOF>
(Token::EOF, pos) => {
2020-04-06 06:29:01 +02:00
return Err(PERR::MissingToken(
2020-05-04 13:36:58 +02:00
Token::RightParen.into(),
2020-04-06 06:29:01 +02:00
format!("to close the arguments list of this function call '{}'", id),
)
.into_err(*pos))
2020-04-06 06:29:01 +02:00
}
// id(...args <error>
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
// id(...args ???
(_, pos) => {
return Err(PERR::MissingToken(
2020-05-04 13:36:58 +02:00
Token::Comma.into(),
format!("to separate the arguments to function call '{}'", id),
)
2020-03-24 09:46:47 +01:00
.into_err(*pos))
}
2016-02-29 22:43:45 +01:00
}
}
}
2020-04-26 13:37:32 +02:00
/// Parse an indexing chain.
/// Indexing binds to the right, so this call parses all possible levels of indexing following in the input.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_index"))]
2020-06-11 12:13:33 +02:00
fn parse_index_chain(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
2020-04-28 17:05:03 +02:00
lhs: Expr,
2020-06-11 17:08:00 +02:00
mut settings: ParseSettings,
) -> Result<Expr, ParseError> {
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let idx_expr = parse_expr(input, state, lib, settings.level_up())?;
2020-03-09 03:41:17 +01:00
2020-03-29 17:53:35 +02:00
// Check type of indexing - must be integer or string
2020-03-09 03:41:17 +01:00
match &idx_expr {
2020-03-18 03:36:50 +01:00
// lhs[int]
Expr::IntegerConstant(x) if x.0 < 0 => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(format!(
"Array access expects non-negative index: {} < 0",
x.0
2020-03-09 03:41:17 +01:00
))
.into_err(x.1))
2020-03-09 03:41:17 +01:00
}
Expr::IntegerConstant(x) => match lhs {
Expr::Array(_) | Expr::StringConstant(_) => (),
2020-03-29 17:53:35 +02:00
Expr::Map(_) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MalformedIndexExpr(
"Object map access expects string index, not a number".into(),
)
.into_err(x.1))
2020-03-29 17:53:35 +02:00
}
#[cfg(not(feature = "no_float"))]
2020-05-25 14:14:31 +02:00
Expr::FloatConstant(_) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(lhs.position()))
}
2020-05-25 14:14:31 +02:00
Expr::CharConstant(_)
| Expr::Assignment(_)
| Expr::And(_)
| Expr::Or(_)
| Expr::In(_)
| Expr::True(_)
| Expr::False(_)
| Expr::Unit(_) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(lhs.position()))
2020-03-29 17:53:35 +02:00
}
_ => (),
},
// lhs[string]
Expr::StringConstant(x) => match lhs {
Expr::Map(_) => (),
2020-03-29 17:53:35 +02:00
Expr::Array(_) | Expr::StringConstant(_) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MalformedIndexExpr(
"Array or string expects numeric index, not a string".into(),
)
.into_err(x.1))
2020-03-29 17:53:35 +02:00
}
#[cfg(not(feature = "no_float"))]
2020-05-25 14:14:31 +02:00
Expr::FloatConstant(_) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(lhs.position()))
}
2020-05-25 14:14:31 +02:00
Expr::CharConstant(_)
| Expr::Assignment(_)
| Expr::And(_)
| Expr::Or(_)
| Expr::In(_)
| Expr::True(_)
| Expr::False(_)
| Expr::Unit(_) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(lhs.position()))
2020-03-29 17:53:35 +02:00
}
_ => (),
},
2020-03-18 03:36:50 +01:00
// lhs[float]
#[cfg(not(feature = "no_float"))]
2020-05-25 14:14:31 +02:00
x @ Expr::FloatConstant(_) => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a float".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-03-09 03:41:17 +01:00
}
2020-03-18 03:36:50 +01:00
// lhs[char]
2020-05-25 14:14:31 +02:00
x @ Expr::CharConstant(_) => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a character".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
}
// lhs[??? = ??? ]
2020-05-25 14:14:31 +02:00
x @ Expr::Assignment(_) => {
return Err(PERR::MalformedIndexExpr(
2020-05-25 14:14:31 +02:00
"Array access expects integer index, not an assignment".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-03-09 03:41:17 +01:00
}
// lhs[()]
2020-05-25 14:14:31 +02:00
x @ Expr::Unit(_) => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not ()".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-03-09 03:41:17 +01:00
}
// lhs[??? && ???], lhs[??? || ???], lhs[??? in ???]
2020-05-25 14:14:31 +02:00
x @ Expr::And(_) | x @ Expr::Or(_) | x @ Expr::In(_) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
}
// lhs[true], lhs[false]
2020-05-25 14:14:31 +02:00
x @ Expr::True(_) | x @ Expr::False(_) => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-03-09 03:41:17 +01:00
}
2020-03-18 03:36:50 +01:00
// All other expressions
2020-03-09 03:41:17 +01:00
_ => (),
}
// Check if there is a closing bracket
match input.peek().unwrap() {
(Token::RightBracket, _) => {
eat_token(input, Token::RightBracket);
2020-04-26 12:04:07 +02:00
2020-04-26 13:37:32 +02:00
// Any more indexing following?
2020-04-26 12:04:07 +02:00
match input.peek().unwrap() {
2020-04-26 13:37:32 +02:00
// If another indexing level, right-bind it
2020-04-26 12:04:07 +02:00
(Token::LeftBracket, _) => {
2020-06-11 17:08:00 +02:00
let prev_pos = settings.pos;
settings.pos = eat_token(input, Token::LeftBracket);
2020-04-26 13:37:32 +02:00
// Recursively parse the indexing chain, right-binding each
let idx_expr =
parse_index_chain(input, state, lib, idx_expr, settings.level_up())?;
2020-04-26 13:37:32 +02:00
// Indexing binds to right
2020-06-11 17:08:00 +02:00
Ok(Expr::Index(Box::new((lhs, idx_expr, prev_pos))))
2020-04-26 12:04:07 +02:00
}
2020-04-26 13:37:32 +02:00
// Otherwise terminate the indexing chain
2020-05-30 04:27:48 +02:00
_ => {
match idx_expr {
// Terminate with an `Expr::Expr` wrapper to prevent the last index expression
// inside brackets to be mis-parsed as another level of indexing, or a
// dot expression/function call to be mis-parsed as following the indexing chain.
Expr::Index(_) | Expr::Dot(_) | Expr::FnCall(_) => Ok(Expr::Index(
Box::new((lhs, Expr::Expr(Box::new(idx_expr)), settings.pos)),
2020-05-30 04:27:48 +02:00
)),
_ => Ok(Expr::Index(Box::new((lhs, idx_expr, settings.pos)))),
2020-05-30 04:27:48 +02:00
}
}
2020-04-26 12:04:07 +02:00
}
}
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
(_, pos) => Err(PERR::MissingToken(
2020-05-04 13:36:58 +02:00
Token::RightBracket.into(),
"for a matching [ in this index expression".into(),
)
.into_err(*pos)),
2020-03-09 03:41:17 +01:00
}
2016-03-26 18:46:28 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse an array literal.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_index"))]
2020-06-11 12:13:33 +02:00
fn parse_array_literal(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
settings: ParseSettings,
) -> Result<Expr, ParseError> {
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut arr = StaticVec::new();
2016-03-26 18:46:28 +01:00
2020-06-16 16:14:46 +02:00
while !input.peek().unwrap().0.is_eof() {
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
if state.engine.limits.max_array_size > 0 && arr.len() >= state.engine.limits.max_array_size
{
2020-06-16 16:14:46 +02:00
return Err(PERR::LiteralTooLarge(
"Size of array literal".to_string(),
2020-07-26 09:53:22 +02:00
state.engine.limits.max_array_size,
2020-06-16 16:14:46 +02:00
)
.into_err(input.peek().unwrap().1));
}
2020-06-16 16:14:46 +02:00
match input.peek().unwrap() {
(Token::RightBracket, _) => {
eat_token(input, Token::RightBracket);
break;
}
_ => {
let expr = parse_expr(input, state, lib, settings.level_up())?;
2020-06-16 16:14:46 +02:00
arr.push(expr);
}
}
2020-06-16 16:14:46 +02:00
match input.peek().unwrap() {
(Token::Comma, _) => {
eat_token(input, Token::Comma);
}
(Token::RightBracket, _) => (),
(Token::EOF, pos) => {
return Err(PERR::MissingToken(
Token::RightBracket.into(),
"to end this array literal".into(),
)
.into_err(*pos))
}
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
(_, pos) => {
return Err(PERR::MissingToken(
Token::Comma.into(),
"to separate the items of this array literal".into(),
)
.into_err(*pos))
}
};
2016-03-26 18:46:28 +01:00
}
Ok(Expr::Array(Box::new((arr, settings.pos))))
2016-03-26 18:46:28 +01:00
}
2020-03-29 17:53:35 +02:00
/// Parse a map literal.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_object"))]
2020-06-11 12:13:33 +02:00
fn parse_map_literal(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
settings: ParseSettings,
) -> Result<Expr, ParseError> {
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut map = StaticVec::new();
2020-03-29 17:53:35 +02:00
2020-06-16 16:14:46 +02:00
while !input.peek().unwrap().0.is_eof() {
const MISSING_RBRACE: &str = "to end this object map literal";
2020-06-16 16:14:46 +02:00
match input.peek().unwrap() {
(Token::RightBrace, _) => {
eat_token(input, Token::RightBrace);
break;
}
_ => (),
}
2020-06-16 16:14:46 +02:00
let (name, pos) = match input.next().unwrap() {
(Token::Identifier(s), pos) => (s, pos),
(Token::StringConstant(s), pos) => (s, pos),
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) if map.is_empty() => {
return Err(
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
.into_err(pos),
);
}
(Token::EOF, pos) => {
return Err(
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
.into_err(pos),
);
}
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
};
2020-03-29 17:53:35 +02:00
match input.next().unwrap() {
(Token::Colon, _) => (),
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => {
return Err(PERR::MissingToken(
Token::Colon.into(),
format!(
"to follow the property '{}' in this object map literal",
name
),
)
.into_err(pos))
2020-06-14 08:25:47 +02:00
}
};
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
if state.engine.limits.max_map_size > 0 && map.len() >= state.engine.limits.max_map_size {
return Err(PERR::LiteralTooLarge(
"Number of properties in object map literal".to_string(),
2020-07-26 09:53:22 +02:00
state.engine.limits.max_map_size,
)
.into_err(input.peek().unwrap().1));
2020-06-16 16:14:46 +02:00
}
2020-03-29 17:53:35 +02:00
let expr = parse_expr(input, state, lib, settings.level_up())?;
map.push(((Into::<ImmutableString>::into(name), pos), expr));
2020-06-16 16:14:46 +02:00
match input.peek().unwrap() {
(Token::Comma, _) => {
eat_token(input, Token::Comma);
}
(Token::RightBrace, _) => (),
(Token::Identifier(_), pos) => {
return Err(PERR::MissingToken(
Token::Comma.into(),
"to separate the items of this object map literal".into(),
)
.into_err(*pos))
}
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
(_, pos) => {
return Err(
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
.into_err(*pos),
)
2020-03-29 17:53:35 +02:00
}
}
}
// Check for duplicating properties
map.iter()
.enumerate()
2020-05-09 18:19:13 +02:00
.try_for_each(|(i, ((k1, _), _))| {
2020-03-29 17:53:35 +02:00
map.iter()
.skip(i + 1)
2020-05-09 18:19:13 +02:00
.find(|((k2, _), _)| k2 == k1)
.map_or_else(|| Ok(()), |((k2, pos), _)| Err((k2, *pos)))
2020-03-29 17:53:35 +02:00
})
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
Ok(Expr::Map(Box::new((map, settings.pos))))
2020-03-29 17:53:35 +02:00
}
2020-03-18 03:36:50 +01:00
/// Parse a primary expression.
2020-06-11 12:13:33 +02:00
fn parse_primary(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Expr, ParseError> {
2020-06-29 17:55:28 +02:00
let (token, token_pos) = input.peek().unwrap();
settings.pos = *token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let (token, _) = match token {
// { - block statement as expression
Token::LeftBrace if settings.allow_stmt_expr => {
return parse_block(input, state, lib, settings.level_up())
.map(|block| Expr::Stmt(Box::new((block, settings.pos))))
2020-03-07 03:39:00 +01:00
}
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)),
_ => input.next().unwrap(),
};
2020-03-05 13:28:03 +01:00
2020-07-30 12:18:28 +02:00
let (next_token, _) = input.peek().unwrap();
let mut root_expr = match token {
Token::IntegerConstant(x) => Expr::IntegerConstant(Box::new((x, settings.pos))),
2020-04-17 14:08:41 +02:00
#[cfg(not(feature = "no_float"))]
2020-07-19 11:14:55 +02:00
Token::FloatConstant(x) => Expr::FloatConstant(Box::new(FloatWrapper(x, settings.pos))),
Token::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))),
2020-07-05 09:23:51 +02:00
Token::StringConstant(s) => Expr::StringConstant(Box::new((s.into(), settings.pos))),
2020-08-08 16:59:05 +02:00
// Function call
Token::Identifier(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
// Once the identifier consumed we must enable next variables capturing
2020-08-09 16:12:50 +02:00
#[cfg(not(feature = "no_closure"))]
{
2020-08-09 13:42:33 +02:00
state.allow_capture = true;
}
2020-08-08 16:59:05 +02:00
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
}
2020-08-22 16:44:24 +02:00
// Module qualification
#[cfg(not(feature = "no_module"))]
Token::Identifier(s) if *next_token == Token::DoubleColon => {
// Once the identifier consumed we must enable next variables capturing
#[cfg(not(feature = "no_closure"))]
{
state.allow_capture = true;
}
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
}
2020-08-08 16:59:05 +02:00
// Normal variable access
2020-04-28 17:05:03 +02:00
Token::Identifier(s) => {
2020-07-29 16:43:50 +02:00
let index = state.access_var(&s, settings.pos);
Expr::Variable(Box::new(((s, settings.pos), None, 0, index)))
2020-04-28 17:05:03 +02:00
}
2020-08-22 16:44:24 +02:00
2020-07-16 06:09:31 +02:00
// Function call is allowed to have reserved keyword
2020-07-30 12:18:28 +02:00
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
2020-07-26 16:25:30 +02:00
if is_keyword_function(&s) {
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
} else {
return Err(PERR::Reserved(s).into_err(settings.pos));
}
2020-07-16 06:09:31 +02:00
}
2020-08-22 16:44:24 +02:00
2020-07-16 06:09:31 +02:00
// Access to `this` as a variable is OK
2020-07-30 12:18:28 +02:00
Token::Reserved(s) if s == KEYWORD_THIS && *next_token != Token::LeftParen => {
2020-07-16 06:09:31 +02:00
if !settings.is_function_scope {
return Err(
PERR::BadInput(format!("'{}' can only be used in functions", s))
.into_err(settings.pos),
);
} else {
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
}
}
2020-08-22 16:44:24 +02:00
Token::Reserved(s) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(settings.pos));
}
2020-08-22 16:44:24 +02:00
Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?,
2020-03-17 10:33:37 +01:00
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?,
2020-03-29 17:53:35 +02:00
#[cfg(not(feature = "no_object"))]
Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?,
Token::True => Expr::True(settings.pos),
Token::False => Expr::False(settings.pos),
2020-06-14 10:56:36 +02:00
Token::LexError(err) => return Err(err.into_err(settings.pos)),
2020-08-22 16:44:24 +02:00
2020-06-14 08:25:47 +02:00
_ => {
return Err(
PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(settings.pos)
);
2020-04-17 13:00:52 +02:00
}
};
// Tail processing all possible postfix operators
loop {
let (token, _) = input.peek().unwrap();
2020-04-17 13:00:52 +02:00
if !root_expr.is_valid_postfix(token) {
break;
2020-03-24 09:46:47 +01:00
}
2020-03-05 13:28:03 +01:00
2020-05-04 11:43:54 +02:00
let (token, token_pos) = input.next().unwrap();
2020-06-11 17:08:00 +02:00
settings.pos = token_pos;
2020-04-10 06:16:39 +02:00
2020-04-17 13:00:52 +02:00
root_expr = match (root_expr, token) {
2020-10-12 10:59:59 +02:00
// Qualified function call with !
#[cfg(not(feature = "no_closure"))]
(Expr::Variable(x), Token::Bang) if x.1.is_some() => {
return Err(if !match_token(input, Token::LeftParen)? {
LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos)
} else {
PERR::BadInput("'!' cannot be used to call module functions".to_string())
.into_err(token_pos)
});
}
// Function call with !
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-30 12:18:28 +02:00
(Expr::Variable(x), Token::Bang) => {
if !match_token(input, Token::LeftParen)? {
return Err(PERR::MissingToken(
Token::LeftParen.syntax().into(),
"to start arguments list of function call".into(),
)
.into_err(input.peek().unwrap().1));
}
let ((name, pos), modules, _, _) = *x;
settings.pos = pos;
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
}
2020-04-17 13:00:52 +02:00
// Function call
(Expr::Variable(x), Token::LeftParen) => {
2020-05-09 18:19:13 +02:00
let ((name, pos), modules, _, _) = *x;
2020-06-11 17:08:00 +02:00
settings.pos = pos;
2020-07-30 12:18:28 +02:00
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
}
2020-05-12 04:20:29 +02:00
(Expr::Property(_), _) => unreachable!(),
2020-05-04 17:07:42 +02:00
// module access
2020-05-09 18:19:13 +02:00
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
(Token::Identifier(id2), pos2) => {
2020-05-09 18:19:13 +02:00
let ((name, pos), mut modules, _, index) = *x;
2020-06-11 17:08:00 +02:00
2020-05-09 18:19:13 +02:00
if let Some(ref mut modules) = modules {
modules.push((name, pos));
} else {
let mut m: ModuleRef = Default::default();
2020-05-09 18:19:13 +02:00
m.push((name, pos));
modules = Some(Box::new(m));
2020-05-04 11:43:54 +02:00
}
2020-05-09 18:19:13 +02:00
Expr::Variable(Box::new(((id2, pos2), modules, 0, index)))
2020-05-04 11:43:54 +02:00
}
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
return Err(PERR::Reserved(id2).into_err(pos2));
}
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
},
2020-04-17 13:00:52 +02:00
// Indexing
#[cfg(not(feature = "no_index"))]
2020-04-28 17:05:03 +02:00
(expr, Token::LeftBracket) => {
parse_index_chain(input, state, lib, expr, settings.level_up())?
2020-04-28 17:05:03 +02:00
}
2020-04-17 13:00:52 +02:00
// Unknown postfix operator
2020-06-16 16:14:46 +02:00
(expr, token) => unreachable!(
"unknown postfix operator '{}' for {:?}",
token.syntax(),
expr
),
}
2016-02-29 22:43:45 +01:00
}
match &mut root_expr {
2020-05-09 04:00:59 +02:00
// Cache the hash key for module-qualified variables
Expr::Variable(x) if x.1.is_some() => {
2020-05-09 18:19:13 +02:00
let ((name, _), modules, hash, _) = x.as_mut();
let modules = modules.as_mut().unwrap();
2020-05-09 04:00:59 +02:00
// Qualifiers + variable name
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0, empty());
2020-07-29 10:10:06 +02:00
modules.set_index(state.find_module(&modules[0].0));
}
_ => (),
}
2020-07-16 06:09:31 +02:00
// Make sure identifiers are valid
Ok(root_expr)
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse a potential unary operator.
2020-06-11 12:13:33 +02:00
fn parse_unary(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap();
settings.pos = *token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
match token {
2020-03-27 16:47:23 +01:00
// If statement is allowed to act as expressions
Token::If if settings.allow_if_expr => Ok(Expr::Stmt(Box::new((
parse_if(input, state, lib, settings.level_up())?,
settings.pos,
)))),
2020-03-18 03:36:50 +01:00
// -expr
Token::UnaryMinus => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::UnaryMinus);
match parse_unary(input, state, lib, settings.level_up())? {
// Negative integer
Expr::IntegerConstant(x) => {
let (num, pos) = *x;
num.checked_neg()
.map(|i| Expr::IntegerConstant(Box::new((i, pos))))
.or_else(|| {
#[cfg(not(feature = "no_float"))]
2020-07-19 11:14:55 +02:00
return Some(Expr::FloatConstant(Box::new(FloatWrapper(
-(x.0 as FLOAT),
pos,
))));
#[cfg(feature = "no_float")]
2020-07-04 16:53:00 +02:00
return None;
})
2020-06-14 10:56:36 +02:00
.ok_or_else(|| LexError::MalformedNumber(format!("-{}", x.0)).into_err(pos))
}
// Negative float
#[cfg(not(feature = "no_float"))]
2020-07-19 11:14:55 +02:00
Expr::FloatConstant(x) => {
Ok(Expr::FloatConstant(Box::new(FloatWrapper(-x.0, x.1))))
}
// Call negative function
expr => {
2020-05-09 10:15:50 +02:00
let op = "-";
2020-09-11 16:09:09 +02:00
let hash = calc_fn_hash(empty(), op, 1, empty());
let mut args = StaticVec::new();
args.push(expr);
2020-05-09 10:15:50 +02:00
Ok(Expr::FnCall(Box::new((
2020-07-30 12:18:28 +02:00
(op.into(), true, false, pos),
2020-05-09 10:15:50 +02:00
None,
hash,
args,
2020-05-09 10:15:50 +02:00
None,
))))
2020-05-09 10:15:50 +02:00
}
}
2019-09-18 12:21:07 +02:00
}
2020-03-18 03:36:50 +01:00
// +expr
Token::UnaryPlus => {
eat_token(input, Token::UnaryPlus);
parse_unary(input, state, lib, settings.level_up())
2019-09-18 12:21:07 +02:00
}
2020-03-18 03:36:50 +01:00
// !expr
Token::Bang => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::Bang);
let mut args = StaticVec::new();
let expr = parse_primary(input, state, lib, settings.level_up())?;
args.push(expr);
2020-05-09 18:19:13 +02:00
2020-05-09 10:15:50 +02:00
let op = "!";
2020-09-11 16:09:09 +02:00
let hash = calc_fn_hash(empty(), op, 1, empty());
2020-05-09 10:15:50 +02:00
Ok(Expr::FnCall(Box::new((
2020-07-30 12:18:28 +02:00
(op.into(), true, false, pos),
2020-05-04 11:43:54 +02:00
None,
2020-05-09 10:15:50 +02:00
hash,
args,
2020-07-19 11:14:55 +02:00
Some(false), // NOT operator, when operating on invalid operand, defaults to false
))))
2019-09-18 12:21:07 +02:00
}
2020-07-19 11:14:55 +02:00
// | ...
#[cfg(not(feature = "no_function"))]
Token::Pipe | Token::Or if settings.allow_anonymous_fn => {
2020-07-29 13:52:54 +02:00
let mut new_state = ParseState::new(
2020-07-19 11:14:55 +02:00
state.engine,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
2020-07-19 11:14:55 +02:00
state.max_function_expr_depth,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
2020-07-19 11:14:55 +02:00
state.max_function_expr_depth,
);
let settings = ParseSettings {
allow_if_expr: true,
allow_stmt_expr: true,
allow_anonymous_fn: true,
is_global: false,
is_function_scope: true,
is_breakable: false,
level: 0,
pos: *token_pos,
};
2020-07-29 13:52:54 +02:00
let (expr, func) = parse_anon_fn(input, &mut new_state, lib, settings)?;
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-29 16:43:50 +02:00
new_state.externals.iter().for_each(|(closure, pos)| {
state.access_var(closure, *pos);
});
2020-07-19 11:14:55 +02:00
// Qualifiers (none) + function name + number of arguments.
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
lib.insert(hash, func);
Ok(expr)
}
// <EOF>
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
// All other tokens
_ => parse_primary(input, state, lib, settings.level_up()),
2017-10-30 16:08:44 +01:00
}
}
fn make_assignment_stmt<'a>(
2020-05-25 14:14:31 +02:00
fn_name: Cow<'static, str>,
state: &mut ParseState,
2020-04-22 11:37:06 +02:00
lhs: Expr,
rhs: Expr,
pos: Position,
) -> Result<Expr, ParseError> {
match &lhs {
2020-06-11 17:21:39 +02:00
// var (non-indexed) = rhs
2020-05-25 14:14:31 +02:00
Expr::Variable(x) if x.3.is_none() => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
2020-06-11 17:21:39 +02:00
// var (indexed) = rhs
Expr::Variable(x) => {
2020-05-09 18:19:13 +02:00
let ((name, name_pos), _, _, index) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
2020-05-25 14:14:31 +02:00
ScopeEntryType::Normal => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
// Constant values cannot be assigned to
ScopeEntryType::Constant => {
2020-05-09 18:19:13 +02:00
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos))
}
}
}
2020-06-11 17:21:39 +02:00
// xxx[???] = rhs, xxx.??? = rhs
Expr::Index(x) | Expr::Dot(x) => match &x.0 {
2020-06-11 17:21:39 +02:00
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
2020-05-25 14:14:31 +02:00
Expr::Variable(x) if x.3.is_none() => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
2020-06-11 17:21:39 +02:00
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
Expr::Variable(x) => {
2020-05-09 18:19:13 +02:00
let ((name, name_pos), _, _, index) = x.as_ref();
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
2020-05-25 14:14:31 +02:00
ScopeEntryType::Normal => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
// Constant values cannot be assigned to
ScopeEntryType::Constant => {
2020-05-09 18:19:13 +02:00
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos))
}
}
}
2020-06-11 17:21:39 +02:00
// expr[???] = rhs, expr.??? = rhs
_ => Err(PERR::AssignmentToCopy.into_err(x.0.position())),
},
2020-06-11 17:21:39 +02:00
// const_expr = rhs
expr if expr.is_constant() => {
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
}
2020-06-11 17:21:39 +02:00
// ??? && ??? = rhs, ??? || ??? = rhs
Expr::And(_) | Expr::Or(_) => {
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(pos))
}
// expr = rhs
_ => Err(PERR::AssignmentToCopy.into_err(lhs.position())),
}
2020-04-22 11:37:06 +02:00
}
2020-03-18 03:36:50 +01:00
/// Parse an operator-assignment expression.
2020-06-11 12:13:33 +02:00
fn parse_op_assignment_stmt(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
2020-04-06 06:29:01 +02:00
lhs: Expr,
mut settings: ParseSettings,
) -> Result<Expr, ParseError> {
let (token, token_pos) = input.peek().unwrap();
settings.pos = *token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let op = match token {
2020-05-25 14:14:31 +02:00
Token::Equals => "".into(),
Token::PlusAssign
| Token::MinusAssign
| Token::MultiplyAssign
| Token::DivideAssign
| Token::LeftShiftAssign
| Token::RightShiftAssign
| Token::ModuloAssign
| Token::PowerOfAssign
| Token::AndAssign
| Token::OrAssign
| Token::XOrAssign => token.syntax(),
_ => return Ok(lhs),
2020-04-22 11:37:06 +02:00
};
2020-05-25 14:14:31 +02:00
let (_, pos) = input.next().unwrap();
let rhs = parse_expr(input, state, lib, settings.level_up())?;
2020-05-25 14:14:31 +02:00
make_assignment_stmt(op, state, lhs, rhs, pos)
2020-04-26 12:04:07 +02:00
}
/// Make a dot expression.
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "no_object"))]
fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
2020-05-04 11:43:54 +02:00
Ok(match (lhs, rhs) {
// idx_lhs[idx_expr].rhs
2020-04-26 13:37:32 +02:00
// Attach dot chain to the bottom level of indexing chain
(Expr::Index(x), rhs) => {
let (idx_lhs, idx_expr, pos) = *x;
Expr::Index(Box::new((
idx_lhs,
make_dot_expr(idx_expr, rhs, op_pos)?,
pos,
)))
}
2020-04-26 12:04:07 +02:00
// lhs.id
(lhs, Expr::Variable(x)) if x.1.is_none() => {
let (name, pos) = x.0;
let getter = make_getter(&name);
let setter = make_setter(&name);
let rhs = Expr::Property(Box::new(((name.into(), getter, setter), pos)));
2020-05-09 18:19:13 +02:00
Expr::Dot(Box::new((lhs, rhs, op_pos)))
}
2020-05-04 13:36:58 +02:00
// lhs.module::id - syntax error
(_, Expr::Variable(x)) if x.1.is_some() => {
2020-07-29 10:10:06 +02:00
return Err(PERR::PropertyExpected.into_err(x.1.unwrap()[0].1));
2020-05-04 11:43:54 +02:00
}
// lhs.prop
(lhs, prop @ Expr::Property(_)) => Expr::Dot(Box::new((lhs, prop, op_pos))),
2020-04-26 12:04:07 +02:00
// lhs.dot_lhs.dot_rhs
2020-05-09 18:19:13 +02:00
(lhs, Expr::Dot(x)) => {
let (dot_lhs, dot_rhs, pos) = *x;
Expr::Dot(Box::new((
lhs,
Expr::Dot(Box::new((dot_lhs.into_property(), dot_rhs, pos))),
2020-05-09 18:19:13 +02:00
op_pos,
)))
}
2020-04-26 12:04:07 +02:00
// lhs.idx_lhs[idx_rhs]
2020-05-09 18:19:13 +02:00
(lhs, Expr::Index(x)) => {
let (dot_lhs, dot_rhs, pos) = *x;
2020-05-09 18:19:13 +02:00
Expr::Dot(Box::new((
lhs,
Expr::Index(Box::new((dot_lhs.into_property(), dot_rhs, pos))),
2020-05-09 18:19:13 +02:00
op_pos,
)))
}
// lhs.Fn() or lhs.eval()
(_, Expr::FnCall(x))
if x.3.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&(x.0).0.as_ref()) =>
{
return Err(PERR::BadInput(format!(
"'{}' should not be called in method style. Try {}(...);",
(x.0).0,
(x.0).0
))
.into_err((x.0).3));
}
2020-07-30 12:18:28 +02:00
// lhs.func!(...)
(_, Expr::FnCall(x)) if (x.0).2 => {
return Err(PERR::MalformedCapture(
"method-call style does not support capturing".into(),
)
.into_err((x.0).3))
}
// lhs.func(...)
(lhs, func @ Expr::FnCall(_)) => Expr::Dot(Box::new((lhs, func, op_pos))),
2020-04-26 12:04:07 +02:00
// lhs.rhs
2020-07-03 16:48:33 +02:00
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
2020-05-04 11:43:54 +02:00
})
2020-03-07 06:39:28 +01:00
}
2020-04-26 12:04:07 +02:00
/// Make an 'in' expression.
fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
2020-04-06 11:47:34 +02:00
match (&lhs, &rhs) {
2020-05-25 14:14:31 +02:00
(_, x @ Expr::IntegerConstant(_))
| (_, x @ Expr::And(_))
| (_, x @ Expr::Or(_))
| (_, x @ Expr::In(_))
| (_, x @ Expr::Assignment(_))
| (_, x @ Expr::True(_))
| (_, x @ Expr::False(_))
| (_, x @ Expr::Unit(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
#[cfg(not(feature = "no_float"))]
2020-05-25 14:14:31 +02:00
(_, x @ Expr::FloatConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
}
2020-04-06 11:47:34 +02:00
// "xxx" in "xxxx", 'x' in "xxxx" - OK!
(Expr::StringConstant(_), Expr::StringConstant(_))
| (Expr::CharConstant(_), Expr::StringConstant(_)) => (),
2020-04-06 11:47:34 +02:00
// 123.456 in "xxxx"
#[cfg(not(feature = "no_float"))]
2020-05-25 14:14:31 +02:00
(x @ Expr::FloatConstant(_), Expr::StringConstant(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a float".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// 123 in "xxxx"
2020-05-25 14:14:31 +02:00
(x @ Expr::IntegerConstant(_), Expr::StringConstant(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a number".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// (??? && ???) in "xxxx", (??? || ???) in "xxxx", (??? in ???) in "xxxx",
// true in "xxxx", false in "xxxx"
2020-05-25 14:14:31 +02:00
(x @ Expr::And(_), Expr::StringConstant(_))
| (x @ Expr::Or(_), Expr::StringConstant(_))
| (x @ Expr::In(_), Expr::StringConstant(_))
| (x @ Expr::True(_), Expr::StringConstant(_))
| (x @ Expr::False(_), Expr::StringConstant(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a boolean".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// [???, ???, ???] in "xxxx"
2020-05-25 14:14:31 +02:00
(x @ Expr::Array(_), Expr::StringConstant(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not an array".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// #{...} in "xxxx"
2020-05-25 14:14:31 +02:00
(x @ Expr::Map(_), Expr::StringConstant(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not an object map".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
}
// (??? = ???) in "xxxx"
2020-05-25 14:14:31 +02:00
(x @ Expr::Assignment(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
2020-05-25 14:14:31 +02:00
"'in' expression for a string expects a string, not an assignment".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// () in "xxxx"
2020-05-25 14:14:31 +02:00
(x @ Expr::Unit(_), Expr::StringConstant(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not ()".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// "xxx" in #{...}, 'x' in #{...} - OK!
(Expr::StringConstant(_), Expr::Map(_)) | (Expr::CharConstant(_), Expr::Map(_)) => (),
2020-04-06 11:47:34 +02:00
// 123.456 in #{...}
#[cfg(not(feature = "no_float"))]
2020-05-25 14:14:31 +02:00
(x @ Expr::FloatConstant(_), Expr::Map(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a float".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// 123 in #{...}
2020-05-25 14:14:31 +02:00
(x @ Expr::IntegerConstant(_), Expr::Map(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a number".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...},
// true in #{...}, false in #{...}
2020-05-25 14:14:31 +02:00
(x @ Expr::And(_), Expr::Map(_))
| (x @ Expr::Or(_), Expr::Map(_))
| (x @ Expr::In(_), Expr::Map(_))
| (x @ Expr::True(_), Expr::Map(_))
| (x @ Expr::False(_), Expr::Map(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a boolean".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// [???, ???, ???] in #{..}
2020-05-25 14:14:31 +02:00
(x @ Expr::Array(_), Expr::Map(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not an array".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// #{...} in #{..}
2020-05-25 14:14:31 +02:00
(x @ Expr::Map(_), Expr::Map(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not an object map".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
}
// (??? = ???) in #{...}
2020-05-25 14:14:31 +02:00
(x @ Expr::Assignment(_), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
2020-05-25 14:14:31 +02:00
"'in' expression for an object map expects a string, not an assignment".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
// () in #{...}
2020-05-25 14:14:31 +02:00
(x @ Expr::Unit(_), Expr::Map(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not ()".into(),
)
2020-05-25 14:14:31 +02:00
.into_err(x.position()))
2020-04-06 11:47:34 +02:00
}
_ => (),
}
Ok(Expr::In(Box::new((lhs, rhs, op_pos))))
2020-04-06 11:47:34 +02:00
}
2020-03-18 03:36:50 +01:00
/// Parse a binary expression.
2020-06-11 12:13:33 +02:00
fn parse_binary_op(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
2020-03-14 04:51:45 +01:00
parent_precedence: u8,
2019-09-18 12:21:07 +02:00
lhs: Expr,
mut settings: ParseSettings,
) -> Result<Expr, ParseError> {
settings.pos = lhs.position();
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut root = lhs;
2016-02-29 22:43:45 +01:00
loop {
let (current_op, _) = input.peek().unwrap();
2020-07-05 11:41:45 +02:00
let custom = state.engine.custom_keywords.as_ref();
let precedence = current_op.precedence(custom);
let bind_right = current_op.is_bind_right();
2020-03-14 04:51:45 +01:00
// Bind left to the parent lhs expression if precedence is higher
// If same precedence, then check if the operator binds right
if precedence < parent_precedence || (precedence == parent_precedence && !bind_right) {
return Ok(root);
2016-02-29 22:43:45 +01:00
}
let (op_token, pos) = input.next().unwrap();
2020-07-31 16:30:23 +02:00
if cfg!(not(feature = "no_object")) && op_token == Token::Period {
if let (Token::Identifier(_), _) = input.peek().unwrap() {
// prevents capturing of the object properties as vars: xxx.<var>
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-31 16:30:23 +02:00
{
2020-08-03 06:10:20 +02:00
state.allow_capture = false;
2020-07-31 16:30:23 +02:00
}
}
}
let rhs = parse_unary(input, state, lib, settings)?;
2016-02-29 22:43:45 +01:00
let next_precedence = input.peek().unwrap().0.precedence(custom);
// Bind to right if the next operator has higher precedence
// If same precedence, then check if the operator binds right
let rhs = if (precedence == next_precedence && bind_right) || precedence < next_precedence {
parse_binary_op(input, state, lib, precedence, rhs, settings)?
} else {
// Otherwise bind to left (even if next operator has the same precedence)
rhs
};
settings = settings.level_up();
settings.pos = pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2020-07-19 11:14:55 +02:00
let cmp_def = Some(false);
let op = op_token.syntax();
let hash = calc_fn_hash(empty(), &op, 2, empty());
2020-07-30 12:18:28 +02:00
let op = (op, true, false, pos);
let mut args = StaticVec::new();
args.push(root);
args.push(rhs);
2020-04-22 11:37:06 +02:00
root = match op_token {
Token::Plus
| Token::Minus
| Token::Multiply
| Token::Divide
| Token::LeftShift
| Token::RightShift
| Token::Modulo
| Token::PowerOf
| Token::Ampersand
| Token::Pipe
| Token::XOr => Expr::FnCall(Box::new((op, None, hash, args, None))),
// '!=' defaults to true when passed invalid operands
2020-07-19 11:14:55 +02:00
Token::NotEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, Some(true)))),
2020-04-28 17:05:03 +02:00
// Comparison operators default to false when passed invalid operands
Token::EqualsTo
| Token::LessThan
| Token::LessThanEqualsTo
| Token::GreaterThan
| Token::GreaterThanEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, cmp_def))),
Token::Or => {
2020-07-29 10:10:06 +02:00
let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap();
Expr::Or(Box::new((current_lhs, rhs, pos)))
}
Token::And => {
2020-07-29 10:10:06 +02:00
let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap();
Expr::And(Box::new((current_lhs, rhs, pos)))
}
Token::In => {
2020-07-29 10:10:06 +02:00
let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap();
make_in_expr(current_lhs, rhs, pos)?
}
#[cfg(not(feature = "no_object"))]
Token::Period => {
2020-07-29 10:10:06 +02:00
let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap();
make_dot_expr(current_lhs, rhs, pos)?
}
2020-07-05 11:41:45 +02:00
Token::Custom(s)
if state
.engine
.custom_keywords
.as_ref()
.map(|c| c.contains_key(&s))
.unwrap_or(false) =>
{
// Accept non-native functions for custom operators
2020-07-30 12:18:28 +02:00
let op = (op.0, false, op.2, op.3);
2020-07-05 11:41:45 +02:00
Expr::FnCall(Box::new((op, None, hash, args, None)))
}
2020-06-14 08:25:47 +02:00
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)),
};
2016-02-29 22:43:45 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Parse an expression.
2020-06-11 12:13:33 +02:00
fn parse_expr(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Expr, ParseError> {
settings.pos = input.peek().unwrap().1;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2020-07-09 13:54:28 +02:00
// Check if it is a custom syntax.
if let Some(ref custom) = state.engine.custom_syntax {
let (token, pos) = input.peek().unwrap();
let token_pos = *pos;
match token {
Token::Custom(key) if custom.contains_key(key) => {
let custom = custom.get_key_value(key).unwrap();
let (key, syntax) = custom;
input.next().unwrap();
let mut exprs: StaticVec<Expr> = Default::default();
// Adjust the variables stack
match syntax.scope_delta {
delta if delta > 0 => {
state.stack.push(("".to_string(), ScopeEntryType::Normal))
}
delta if delta < 0 && state.stack.len() <= delta.abs() as usize => {
state.stack.clear()
}
delta if delta < 0 => state
.stack
.truncate(state.stack.len() - delta.abs() as usize),
_ => (),
}
for segment in syntax.segments.iter() {
settings.pos = input.peek().unwrap().1;
let settings = settings.level_up();
match segment.as_str() {
MARKER_IDENT => match input.next().unwrap() {
(Token::Identifier(s), pos) => {
exprs.push(Expr::Variable(Box::new(((s, pos), None, 0, None))));
}
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
2020-07-09 13:54:28 +02:00
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
},
MARKER_EXPR => exprs.push(parse_expr(input, state, lib, settings)?),
MARKER_BLOCK => {
let stmt = parse_block(input, state, lib, settings)?;
let pos = stmt.position();
exprs.push(Expr::Stmt(Box::new((stmt, pos))))
}
s => match input.peek().unwrap() {
(t, _) if t.syntax().as_ref() == s => {
input.next().unwrap();
}
(_, pos) => {
return Err(PERR::MissingToken(
s.to_string(),
format!("for '{}' expression", key),
)
.into_err(*pos))
}
},
}
}
return Ok(Expr::Custom(Box::new((
CustomExpr(exprs, syntax.func.clone()),
token_pos,
))));
}
_ => (),
}
}
// Parse expression normally.
let lhs = parse_unary(input, state, lib, settings.level_up())?;
parse_binary_op(input, state, lib, 1, lhs, settings.level_up())
2016-02-29 22:43:45 +01:00
}
2020-04-22 11:37:06 +02:00
/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`).
2020-06-11 12:13:33 +02:00
fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result<(), ParseError> {
match input.peek().unwrap() {
// Disallow statement expressions
(Token::LeftBrace, pos) | (Token::EOF, pos) => {
Err(PERR::ExprExpected(type_name.to_string()).into_err(*pos))
}
// No need to check for others at this time - leave it for the expr parser
_ => Ok(()),
}
}
2020-04-22 11:37:06 +02:00
/// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`).
2020-06-11 12:13:33 +02:00
fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
2020-04-22 11:37:06 +02:00
match input.peek().unwrap() {
(Token::Equals, pos) => {
Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos))
2020-04-22 11:37:06 +02:00
}
(Token::PlusAssign, pos)
| (Token::MinusAssign, pos)
| (Token::MultiplyAssign, pos)
| (Token::DivideAssign, pos)
| (Token::LeftShiftAssign, pos)
| (Token::RightShiftAssign, pos)
| (Token::ModuloAssign, pos)
| (Token::PowerOfAssign, pos)
| (Token::AndAssign, pos)
| (Token::OrAssign, pos)
| (Token::XOrAssign, pos) => Err(PERR::BadInput(
"Expecting a boolean expression, not an assignment".to_string(),
)
.into_err(*pos)),
2020-04-22 11:37:06 +02:00
_ => Ok(()),
}
}
2020-03-18 03:36:50 +01:00
/// Parse an if statement.
2020-06-11 12:13:33 +02:00
fn parse_if(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// if ...
let token_pos = eat_token(input, Token::If);
settings.pos = token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2016-02-29 22:43:45 +01:00
// if guard { if_body }
ensure_not_statement_expr(input, "a boolean")?;
let guard = parse_expr(input, state, lib, settings.level_up())?;
2020-04-22 11:37:06 +02:00
ensure_not_assignment(input)?;
let if_body = parse_block(input, state, lib, settings.level_up())?;
2016-02-29 22:43:45 +01:00
// if guard { if_body } else ...
let else_body = if match_token(input, Token::Else).unwrap_or(false) {
Some(if let (Token::If, _) = input.peek().unwrap() {
// if guard { if_body } else if ...
parse_if(input, state, lib, settings.level_up())?
2020-03-16 16:51:32 +01:00
} else {
// if guard { if_body } else { else-body }
parse_block(input, state, lib, settings.level_up())?
})
2020-03-16 16:51:32 +01:00
} else {
None
};
2020-03-02 10:04:56 +01:00
Ok(Stmt::IfThenElse(Box::new((
guard, if_body, else_body, token_pos,
))))
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse a while loop.
2020-06-11 12:13:33 +02:00
fn parse_while(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// while ...
let token_pos = eat_token(input, Token::While);
settings.pos = token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2016-02-29 22:43:45 +01:00
2020-03-18 11:41:18 +01:00
// while guard { body }
ensure_not_statement_expr(input, "a boolean")?;
let guard = parse_expr(input, state, lib, settings.level_up())?;
2020-04-22 11:37:06 +02:00
ensure_not_assignment(input)?;
settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?;
2016-02-29 22:43:45 +01:00
Ok(Stmt::While(Box::new((guard, body, token_pos))))
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse a loop statement.
2020-06-11 12:13:33 +02:00
fn parse_loop(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// loop ...
let token_pos = eat_token(input, Token::Loop);
settings.pos = token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2017-10-30 16:08:44 +01:00
2020-03-18 11:41:18 +01:00
// loop { body }
settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?;
2017-10-30 16:08:44 +01:00
Ok(Stmt::Loop(Box::new((body, token_pos))))
2017-10-30 16:08:44 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse a for loop.
2020-06-11 12:13:33 +02:00
fn parse_for(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// for ...
let token_pos = eat_token(input, Token::For);
settings.pos = token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2020-03-18 11:41:18 +01:00
// for name ...
let name = match input.next().unwrap() {
2020-03-18 11:41:18 +01:00
// Variable name
2020-03-16 16:51:32 +01:00
(Token::Identifier(s), _) => s,
// Reserved keyword
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
2020-03-18 11:41:18 +01:00
// Bad identifier
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
2020-03-18 11:41:18 +01:00
// Not a variable name
2020-03-24 09:46:47 +01:00
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
};
2020-03-18 11:41:18 +01:00
// for name in ...
match input.next().unwrap() {
(Token::In, _) => (),
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => {
return Err(
2020-05-04 13:36:58 +02:00
PERR::MissingToken(Token::In.into(), "after the iteration variable".into())
.into_err(pos),
)
}
}
2020-03-18 11:41:18 +01:00
// for name in expr { body }
ensure_not_statement_expr(input, "a boolean")?;
let expr = parse_expr(input, state, lib, settings.level_up())?;
2020-04-28 17:05:03 +02:00
2020-06-28 09:49:24 +02:00
let prev_stack_len = state.stack.len();
state.stack.push((name.clone(), ScopeEntryType::Normal));
2020-04-28 17:05:03 +02:00
settings.is_breakable = true;
let body = parse_block(input, state, lib, settings.level_up())?;
2020-04-28 17:05:03 +02:00
2020-06-28 09:49:24 +02:00
state.stack.truncate(prev_stack_len);
Ok(Stmt::For(Box::new((name, expr, body, token_pos))))
}
2020-03-18 03:36:50 +01:00
/// Parse a variable definition statement.
2020-06-11 12:13:33 +02:00
fn parse_let(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
2020-03-25 04:27:18 +01:00
var_type: ScopeEntryType,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// let/const... (specified in `var_type`)
let token_pos = input.next().unwrap().1;
settings.pos = token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2020-03-16 16:51:32 +01:00
2020-03-18 11:41:18 +01:00
// let name ...
let (name, pos) = match input.next().unwrap() {
2020-03-18 11:41:18 +01:00
(Token::Identifier(s), pos) => (s, pos),
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
2020-03-24 09:46:47 +01:00
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
2016-02-29 22:43:45 +01:00
};
2020-03-18 11:41:18 +01:00
// let name = ...
2020-10-09 05:15:25 +02:00
let init_value = if match_token(input, Token::Equals)? {
2020-03-18 11:41:18 +01:00
// let name = expr
2020-10-09 05:15:25 +02:00
Some(parse_expr(input, state, lib, settings.level_up())?)
2020-03-14 16:41:15 +01:00
} else {
2020-10-09 05:15:25 +02:00
None
};
match var_type {
// let name = expr
ScopeEntryType::Normal => {
state.stack.push((name.clone(), ScopeEntryType::Normal));
Ok(Stmt::Let(Box::new(((name, pos), init_value, token_pos))))
}
// const name = { expr:constant }
ScopeEntryType::Constant => {
state.stack.push((name.clone(), ScopeEntryType::Constant));
Ok(Stmt::Const(Box::new(((name, pos), init_value, token_pos))))
}
2016-02-29 22:43:45 +01:00
}
}
2020-05-04 13:36:58 +02:00
/// Parse an import statement.
2020-07-01 16:21:43 +02:00
#[cfg(not(feature = "no_module"))]
2020-06-11 12:13:33 +02:00
fn parse_import(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
2020-05-04 13:36:58 +02:00
// import ...
let token_pos = eat_token(input, Token::Import);
settings.pos = token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2020-05-04 13:36:58 +02:00
// import expr ...
let expr = parse_expr(input, state, lib, settings.level_up())?;
2020-05-04 13:36:58 +02:00
// import expr as ...
if !match_token(input, Token::As)? {
return Ok(Stmt::Import(Box::new((expr, None, token_pos))));
2020-05-04 13:36:58 +02:00
}
// import expr as name ...
let (name, _) = match input.next().unwrap() {
(Token::Identifier(s), pos) => (s, pos),
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
2020-05-04 13:36:58 +02:00
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
};
state.modules.push(name.clone());
Ok(Stmt::Import(Box::new((
expr,
2020-10-02 12:52:18 +02:00
Some((name.into(), settings.pos)),
token_pos,
))))
2020-05-04 13:36:58 +02:00
}
2020-05-08 10:49:24 +02:00
/// Parse an export statement.
#[cfg(not(feature = "no_module"))]
2020-06-11 12:13:33 +02:00
fn parse_export(
input: &mut TokenStream,
2020-07-26 09:53:22 +02:00
_state: &mut ParseState,
2020-07-09 13:54:28 +02:00
_lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
let token_pos = eat_token(input, Token::Export);
settings.pos = token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(_state.max_expr_depth)?;
2020-05-08 10:49:24 +02:00
let mut exports = StaticVec::new();
2020-05-08 10:49:24 +02:00
loop {
let (id, id_pos) = match input.next().unwrap() {
(Token::Identifier(s), pos) => (s.clone(), pos),
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
2020-05-08 10:49:24 +02:00
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
};
let rename = if match_token(input, Token::As)? {
match input.next().unwrap() {
(Token::Identifier(s), pos) => Some((s.clone(), pos)),
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos));
}
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
2020-05-08 10:49:24 +02:00
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
}
} else {
None
};
2020-05-09 18:19:13 +02:00
exports.push(((id, id_pos), rename));
2020-05-08 10:49:24 +02:00
match input.peek().unwrap() {
(Token::Comma, _) => {
eat_token(input, Token::Comma);
}
(Token::Identifier(_), pos) => {
return Err(PERR::MissingToken(
Token::Comma.into(),
"to separate the list of exports".into(),
)
.into_err(*pos))
}
_ => break,
}
}
// Check for duplicating parameters
exports
.iter()
.enumerate()
2020-05-09 18:19:13 +02:00
.try_for_each(|(i, ((id1, _), _))| {
2020-05-08 10:49:24 +02:00
exports
.iter()
.skip(i + 1)
2020-05-09 18:19:13 +02:00
.find(|((id2, _), _)| id2 == id1)
.map_or_else(|| Ok(()), |((id2, pos), _)| Err((id2, *pos)))
2020-05-08 10:49:24 +02:00
})
2020-05-09 18:19:13 +02:00
.map_err(|(id2, pos)| PERR::DuplicatedExport(id2.to_string()).into_err(pos))?;
2020-05-08 10:49:24 +02:00
Ok(Stmt::Export(Box::new((exports, token_pos))))
2020-05-08 10:49:24 +02:00
}
2020-03-18 03:36:50 +01:00
/// Parse a statement block.
2020-06-11 12:13:33 +02:00
fn parse_block(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// Must start with {
settings.pos = match input.next().unwrap() {
2020-03-16 16:51:32 +01:00
(Token::LeftBrace, pos) => pos,
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => {
2020-05-04 13:36:58 +02:00
return Err(PERR::MissingToken(
Token::LeftBrace.into(),
"to start a statement block".into(),
)
2020-05-04 13:36:58 +02:00
.into_err(pos))
}
2020-03-12 05:40:28 +01:00
};
2016-02-29 22:43:45 +01:00
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut statements = StaticVec::new();
2020-06-28 09:49:24 +02:00
let prev_stack_len = state.stack.len();
let prev_mods_len = state.modules.len();
while !match_token(input, Token::RightBrace)? {
2020-03-17 10:33:37 +01:00
// Parse statements inside the block
settings.is_global = false;
let stmt = match parse_stmt(input, state, lib, settings.level_up())? {
Some(s) => s,
None => continue,
};
2020-03-17 10:33:37 +01:00
// See if it needs a terminating semicolon
let need_semicolon = !stmt.is_self_terminated();
2017-10-02 23:44:45 +02:00
2020-03-17 10:33:37 +01:00
statements.push(stmt);
match input.peek().unwrap() {
2020-03-18 11:41:18 +01:00
// { ... stmt }
(Token::RightBrace, _) => {
eat_token(input, Token::RightBrace);
break;
}
2020-03-18 11:41:18 +01:00
// { ... stmt;
(Token::SemiColon, _) if need_semicolon => {
eat_token(input, Token::SemiColon);
2020-03-17 10:33:37 +01:00
}
2020-03-18 11:41:18 +01:00
// { ... { stmt } ;
(Token::SemiColon, _) if !need_semicolon => (),
2020-03-18 11:41:18 +01:00
// { ... { stmt } ???
(_, _) if !need_semicolon => (),
// { ... stmt <error>
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
// { ... stmt ???
(_, pos) => {
2020-03-17 10:33:37 +01:00
// Semicolons are not optional between statements
2020-05-04 13:36:58 +02:00
return Err(PERR::MissingToken(
Token::SemiColon.into(),
"to terminate this statement".into(),
)
.into_err(*pos));
2019-09-18 12:21:07 +02:00
}
}
}
2016-02-29 22:43:45 +01:00
2020-06-28 09:49:24 +02:00
state.stack.truncate(prev_stack_len);
state.modules.truncate(prev_mods_len);
2020-04-28 17:05:03 +02:00
Ok(Stmt::Block(Box::new((statements, settings.pos))))
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse an expression as a statement.
2020-06-11 12:13:33 +02:00
fn parse_expr_stmt(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Stmt, ParseError> {
settings.pos = input.peek().unwrap().1;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let expr = parse_expr(input, state, lib, settings.level_up())?;
let expr = parse_op_assignment_stmt(input, state, lib, expr, settings.level_up())?;
2020-04-22 11:37:06 +02:00
Ok(Stmt::Expr(Box::new(expr)))
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse a single statement.
2020-06-11 12:13:33 +02:00
fn parse_stmt(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<Option<Stmt>, ParseError> {
use ScopeEntryType::{Constant, Normal};
let (token, token_pos) = match input.peek().unwrap() {
(Token::EOF, pos) => return Ok(Some(Stmt::Noop(*pos))),
x => x,
};
settings.pos = *token_pos;
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
match token {
2020-03-18 11:41:18 +01:00
// Semicolon - empty statement
Token::SemiColon => Ok(Some(Stmt::Noop(settings.pos))),
2020-03-18 11:41:18 +01:00
Token::LeftBrace => parse_block(input, state, lib, settings.level_up()).map(Some),
2020-04-01 10:22:18 +02:00
2020-03-18 11:41:18 +01:00
// fn ...
#[cfg(not(feature = "no_function"))]
Token::Fn if !settings.is_global => Err(PERR::WrongFnDefinition.into_err(settings.pos)),
#[cfg(not(feature = "no_function"))]
Token::Fn | Token::Private => {
let access = if matches!(token, Token::Private) {
eat_token(input, Token::Private);
FnAccess::Private
} else {
FnAccess::Public
};
match input.next().unwrap() {
(Token::Fn, pos) => {
2020-07-30 07:28:06 +02:00
let mut new_state = ParseState::new(
state.engine,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth,
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth,
);
let settings = ParseSettings {
allow_if_expr: true,
allow_stmt_expr: true,
allow_anonymous_fn: true,
is_global: false,
2020-07-16 06:09:31 +02:00
is_function_scope: true,
is_breakable: false,
level: 0,
pos: pos,
};
2020-07-30 07:28:06 +02:00
let func = parse_fn(input, &mut new_state, lib, access, settings)?;
// Qualifiers (none) + function name + number of arguments.
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
lib.insert(hash, func);
Ok(None)
}
(_, pos) => Err(PERR::MissingToken(
Token::Fn.into(),
format!("following '{}'", Token::Private.syntax()),
)
.into_err(pos)),
}
}
Token::If => parse_if(input, state, lib, settings.level_up()).map(Some),
Token::While => parse_while(input, state, lib, settings.level_up()).map(Some),
Token::Loop => parse_loop(input, state, lib, settings.level_up()).map(Some),
Token::For => parse_for(input, state, lib, settings.level_up()).map(Some),
2020-04-01 10:22:18 +02:00
Token::Continue if settings.is_breakable => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::Continue);
Ok(Some(Stmt::Continue(pos)))
2020-04-01 10:22:18 +02:00
}
Token::Break if settings.is_breakable => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::Break);
Ok(Some(Stmt::Break(pos)))
}
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(settings.pos)),
2020-04-01 10:22:18 +02:00
Token::Return | Token::Throw => {
let (return_type, token_pos) = input
.next()
.map(|(token, pos)| {
(
match token {
Token::Return => ReturnType::Return,
Token::Throw => ReturnType::Exception,
_ => unreachable!(),
},
pos,
)
})
.unwrap();
2020-03-03 11:15:20 +01:00
match input.peek().unwrap() {
// `return`/`throw` at <EOF>
(Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, *pos),
None,
token_pos,
))))),
2020-03-18 03:36:50 +01:00
// `return;` or `throw;`
(Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal(Box::new((
(return_type, settings.pos),
None,
token_pos,
))))),
2020-03-18 03:36:50 +01:00
// `return` or `throw` with expression
(_, _) => {
let expr = parse_expr(input, state, lib, settings.level_up())?;
2020-03-19 12:53:42 +01:00
let pos = expr.position();
Ok(Some(Stmt::ReturnWithVal(Box::new((
2020-05-09 18:19:13 +02:00
(return_type, pos),
Some(expr),
token_pos,
)))))
}
}
}
2020-04-01 10:22:18 +02:00
Token::Let => parse_let(input, state, lib, Normal, settings.level_up()).map(Some),
Token::Const => parse_let(input, state, lib, Constant, settings.level_up()).map(Some),
2020-07-01 16:21:43 +02:00
#[cfg(not(feature = "no_module"))]
Token::Import => parse_import(input, state, lib, settings.level_up()).map(Some),
2020-05-04 13:36:58 +02:00
2020-05-08 10:49:24 +02:00
#[cfg(not(feature = "no_module"))]
Token::Export if !settings.is_global => Err(PERR::WrongExport.into_err(settings.pos)),
2020-05-08 10:49:24 +02:00
#[cfg(not(feature = "no_module"))]
Token::Export => parse_export(input, state, lib, settings.level_up()).map(Some),
2020-05-08 10:49:24 +02:00
_ => parse_expr_stmt(input, state, lib, settings.level_up()).map(Some),
2016-02-29 22:43:45 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Parse a function definition.
#[cfg(not(feature = "no_function"))]
2020-06-11 12:13:33 +02:00
fn parse_fn(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
access: FnAccess,
mut settings: ParseSettings,
) -> Result<ScriptFnDef, ParseError> {
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
2020-07-26 16:25:30 +02:00
let (token, pos) = input.next().unwrap();
2020-07-30 12:18:28 +02:00
let name = token
.into_function_name_for_override()
.map_err(|t| match t {
Token::Reserved(s) => PERR::Reserved(s).into_err(pos),
_ => PERR::FnMissingName.into_err(pos),
})?;
match input.peek().unwrap() {
(Token::LeftParen, _) => eat_token(input, Token::LeftParen),
(_, pos) => return Err(PERR::FnMissingParams(name).into_err(*pos)),
2020-04-06 11:47:34 +02:00
};
let mut params = Vec::new();
if !match_token(input, Token::RightParen)? {
let sep_err = format!("to separate the parameters of function '{}'", name);
2020-03-24 09:46:47 +01:00
2020-03-14 16:41:15 +01:00
loop {
2020-07-19 11:14:55 +02:00
match input.next().unwrap() {
(Token::RightParen, _) => break,
(Token::Identifier(s), pos) => {
state.stack.push((s.clone(), ScopeEntryType::Normal));
params.push((s, pos))
}
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => {
return Err(PERR::MissingToken(
Token::RightParen.into(),
format!("to close the parameters list of function '{}'", name),
)
.into_err(pos))
}
2020-03-16 16:51:32 +01:00
}
match input.next().unwrap() {
(Token::RightParen, _) => break,
(Token::Comma, _) => (),
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
2020-05-04 13:36:58 +02:00
(_, pos) => {
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
}
}
2020-03-14 16:41:15 +01:00
}
2016-02-29 22:43:45 +01:00
}
// Check for duplicating parameters
params
.iter()
.enumerate()
.try_for_each(|(i, (p1, _))| {
params
.iter()
.skip(i + 1)
.find(|(p2, _)| p2 == p1)
.map_or_else(|| Ok(()), |(p2, pos)| Err((p2, *pos)))
})
.map_err(|(p, pos)| {
PERR::FnDuplicatedParam(name.to_string(), p.to_string()).into_err(pos)
})?;
// Parse function body
let body = match input.peek().unwrap() {
(Token::LeftBrace, _) => {
settings.is_breakable = false;
parse_block(input, state, lib, settings.level_up())?
}
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
};
2020-07-30 07:28:06 +02:00
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-30 07:28:06 +02:00
let externals = state
.externals
.iter()
.map(|(name, _)| name)
.filter(|name| !params.contains(name))
.cloned()
.collect();
2020-04-06 11:47:34 +02:00
Ok(ScriptFnDef {
name: name.into(),
access,
2020-04-06 11:47:34 +02:00
params,
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-30 07:28:06 +02:00
externals,
2020-03-09 14:57:07 +01:00
body,
pos: settings.pos,
lib: None,
})
}
2020-07-29 16:43:50 +02:00
/// Creates a curried expression from a list of external variables
2020-08-05 16:53:01 +02:00
#[cfg(not(feature = "no_function"))]
2020-07-29 16:43:50 +02:00
fn make_curry_from_externals(
fn_expr: Expr,
2020-07-30 07:28:06 +02:00
externals: StaticVec<(String, Position)>,
pos: Position,
2020-07-29 16:43:50 +02:00
) -> Expr {
2020-07-30 07:28:06 +02:00
if externals.is_empty() {
2020-07-29 16:43:50 +02:00
return fn_expr;
}
2020-07-30 07:28:06 +02:00
let num_externals = externals.len();
2020-07-29 16:43:50 +02:00
let mut args: StaticVec<_> = Default::default();
args.push(fn_expr);
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
externals.iter().for_each(|(var_name, pos)| {
args.push(Expr::Variable(Box::new((
(var_name.into(), *pos),
None,
0,
None,
))));
});
#[cfg(feature = "no_closure")]
2020-07-30 07:28:06 +02:00
externals.into_iter().for_each(|(var_name, pos)| {
args.push(Expr::Variable(Box::new(((var_name, pos), None, 0, None))));
2020-07-29 16:43:50 +02:00
});
let hash = calc_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1, empty());
2020-07-29 16:43:50 +02:00
let expr = Expr::FnCall(Box::new((
2020-07-30 12:18:28 +02:00
(KEYWORD_FN_PTR_CURRY.into(), false, false, pos),
2020-07-29 16:43:50 +02:00
None,
hash,
args,
None,
)));
2020-08-03 06:10:20 +02:00
// If there are captured variables, convert the entire expression into a statement block,
// then insert the relevant `Share` statements.
#[cfg(not(feature = "no_closure"))]
{
// Statement block
let mut statements: StaticVec<_> = Default::default();
// Insert `Share` statements
statements.extend(externals.into_iter().map(|x| Stmt::Share(Box::new(x))));
// Final expression
statements.push(Stmt::Expr(Box::new(expr)));
Expr::Stmt(Box::new((Stmt::Block(Box::new((statements, pos))), pos)))
}
#[cfg(feature = "no_closure")]
return expr;
2020-07-29 16:43:50 +02:00
}
2020-07-19 11:14:55 +02:00
/// Parse an anonymous function definition.
#[cfg(not(feature = "no_function"))]
fn parse_anon_fn(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
) -> Result<(Expr, ScriptFnDef), ParseError> {
2020-07-26 09:53:22 +02:00
#[cfg(not(feature = "unchecked"))]
2020-07-19 11:14:55 +02:00
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut params = Vec::new();
if input.next().unwrap().0 != Token::Or {
if !match_token(input, Token::Pipe)? {
loop {
match input.next().unwrap() {
(Token::Pipe, _) => break,
(Token::Identifier(s), pos) => {
state.stack.push((s.clone(), ScopeEntryType::Normal));
params.push((s, pos))
}
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => {
return Err(PERR::MissingToken(
Token::Pipe.into(),
"to close the parameters list of anonymous function".into(),
)
.into_err(pos))
}
}
match input.next().unwrap() {
(Token::Pipe, _) => break,
(Token::Comma, _) => (),
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => {
return Err(PERR::MissingToken(
Token::Comma.into(),
"to separate the parameters of anonymous function".into(),
)
.into_err(pos))
}
}
}
}
}
// Check for duplicating parameters
params
.iter()
.enumerate()
.try_for_each(|(i, (p1, _))| {
params
.iter()
.skip(i + 1)
.find(|(p2, _)| p2 == p1)
.map_or_else(|| Ok(()), |(p2, pos)| Err((p2, *pos)))
})
.map_err(|(p, pos)| PERR::FnDuplicatedParam("".to_string(), p.to_string()).into_err(pos))?;
// Parse function body
settings.is_breakable = false;
let pos = input.peek().unwrap().1;
let body = parse_stmt(input, state, lib, settings.level_up())
.map(|stmt| stmt.unwrap_or_else(|| Stmt::Noop(pos)))?;
2020-07-30 07:28:06 +02:00
// External variables may need to be processed in a consistent order,
// so extract them into a list.
2020-07-31 16:30:23 +02:00
let externals: StaticVec<_> = {
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-31 16:30:23 +02:00
{
state
.externals
.iter()
.map(|(k, &v)| (k.clone(), v))
.collect()
}
2020-08-03 06:10:20 +02:00
#[cfg(feature = "no_closure")]
2020-07-31 16:30:23 +02:00
Default::default()
};
2020-07-30 07:28:06 +02:00
2020-08-03 06:10:20 +02:00
let params: StaticVec<_> = if cfg!(not(feature = "no_closure")) {
2020-07-31 16:30:23 +02:00
externals
.iter()
.map(|(k, _)| k)
.cloned()
.chain(params.into_iter().map(|(v, _)| v))
.collect()
} else {
params.into_iter().map(|(v, _)| v).collect()
};
2020-07-19 11:14:55 +02:00
// Calculate hash
#[cfg(feature = "no_std")]
let mut s: AHasher = Default::default();
#[cfg(not(feature = "no_std"))]
let mut s = DefaultHasher::new();
2020-07-29 17:34:48 +02:00
s.write_usize(params.len());
params.iter().for_each(|a| a.hash(&mut s));
2020-07-19 11:14:55 +02:00
body.hash(&mut s);
let hash = s.finish();
// Create unique function name
2020-08-03 17:12:42 +02:00
let fn_name: ImmutableString = format!("{}{:016x}", FN_ANONYMOUS, hash).into();
2020-07-19 11:14:55 +02:00
2020-07-30 07:28:06 +02:00
// Define the function
2020-07-19 11:14:55 +02:00
let script = ScriptFnDef {
name: fn_name.clone(),
access: FnAccess::Public,
2020-07-29 17:34:48 +02:00
params,
2020-08-03 06:10:20 +02:00
#[cfg(not(feature = "no_closure"))]
2020-07-30 07:28:06 +02:00
externals: Default::default(),
2020-07-19 11:14:55 +02:00
body,
pos: settings.pos,
lib: None,
2020-07-19 11:14:55 +02:00
};
let expr = Expr::FnPointer(Box::new((fn_name, settings.pos)));
2020-07-19 11:14:55 +02:00
2020-08-03 06:10:20 +02:00
let expr = if cfg!(not(feature = "no_closure")) {
2020-07-31 16:30:23 +02:00
make_curry_from_externals(expr, externals, settings.pos)
} else {
expr
};
2020-07-29 16:43:50 +02:00
2020-07-19 11:14:55 +02:00
Ok((expr, script))
}
2020-06-03 04:44:36 +02:00
impl Engine {
2020-06-11 12:13:33 +02:00
pub(crate) fn parse_global_expr(
2020-06-03 04:44:36 +02:00
&self,
2020-06-11 12:13:33 +02:00
input: &mut TokenStream,
2020-06-03 04:44:36 +02:00
scope: &Scope,
optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> {
let mut functions = Default::default();
2020-07-26 09:53:22 +02:00
let mut state = ParseState::new(
self,
#[cfg(not(feature = "unchecked"))]
self.limits.max_expr_depth,
#[cfg(not(feature = "unchecked"))]
self.limits.max_function_expr_depth,
);
let settings = ParseSettings {
allow_if_expr: false,
allow_stmt_expr: false,
allow_anonymous_fn: false,
is_global: true,
2020-07-16 06:09:31 +02:00
is_function_scope: false,
is_breakable: false,
level: 0,
pos: Position::none(),
};
let expr = parse_expr(input, &mut state, &mut functions, settings)?;
2020-06-03 04:44:36 +02:00
assert!(functions.is_empty());
2020-06-03 04:44:36 +02:00
match input.peek().unwrap() {
(Token::EOF, _) => (),
// Return error if the expression doesn't end
(token, pos) => {
return Err(
PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(*pos)
)
}
}
let expr = vec![Stmt::Expr(Box::new(expr))];
Ok(
// Optimize AST
optimize_into_ast(self, scope, expr, Default::default(), optimization_level),
)
}
2020-06-14 08:25:47 +02:00
/// Parse the global level statements.
fn parse_global_level(
&self,
input: &mut TokenStream,
) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
let mut statements: Vec<Stmt> = Default::default();
let mut functions = Default::default();
2020-07-26 09:53:22 +02:00
let mut state = ParseState::new(
self,
#[cfg(not(feature = "unchecked"))]
self.limits.max_expr_depth,
#[cfg(not(feature = "unchecked"))]
self.limits.max_function_expr_depth,
);
2020-06-14 08:25:47 +02:00
while !input.peek().unwrap().0.is_eof() {
let settings = ParseSettings {
allow_if_expr: true,
allow_stmt_expr: true,
allow_anonymous_fn: true,
2020-06-14 08:25:47 +02:00
is_global: true,
2020-07-16 06:09:31 +02:00
is_function_scope: false,
2020-06-14 08:25:47 +02:00
is_breakable: false,
level: 0,
pos: Position::none(),
};
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {
Some(s) => s,
None => continue,
};
2020-06-14 08:25:47 +02:00
let need_semicolon = !stmt.is_self_terminated();
statements.push(stmt);
match input.peek().unwrap() {
// EOF
(Token::EOF, _) => break,
// stmt ;
(Token::SemiColon, _) if need_semicolon => {
eat_token(input, Token::SemiColon);
}
// stmt ;
(Token::SemiColon, _) if !need_semicolon => (),
// { stmt } ???
(_, _) if !need_semicolon => (),
// stmt <error>
2020-06-14 10:56:36 +02:00
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
2020-06-14 08:25:47 +02:00
// stmt ???
(_, pos) => {
// Semicolons are not optional between statements
return Err(PERR::MissingToken(
Token::SemiColon.into(),
"to terminate this statement".into(),
)
.into_err(*pos));
}
}
}
Ok((statements, functions.into_iter().map(|(_, v)| v).collect()))
}
2020-06-03 04:44:36 +02:00
/// Run the parser on an input stream, returning an AST.
2020-10-08 16:25:50 +02:00
#[inline(always)]
2020-06-11 12:13:33 +02:00
pub(crate) fn parse(
2020-06-03 04:44:36 +02:00
&self,
2020-06-11 12:13:33 +02:00
input: &mut TokenStream,
2020-06-03 04:44:36 +02:00
scope: &Scope,
optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> {
2020-06-14 08:25:47 +02:00
let (statements, lib) = self.parse_global_level(input)?;
2020-06-03 04:44:36 +02:00
Ok(
// Optimize AST
optimize_into_ast(self, scope, statements, lib, optimization_level),
)
}
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Map a `Dynamic` value to an expression.
///
/// Returns Some(expression) if conversion is successful. Otherwise None.
2020-04-05 11:44:48 +02:00
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
2020-04-12 17:00:06 +02:00
match value.0 {
#[cfg(not(feature = "no_float"))]
2020-07-19 11:14:55 +02:00
Union::Float(value) => Some(Expr::FloatConstant(Box::new(FloatWrapper(value, pos)))),
2020-04-12 17:00:06 +02:00
Union::Unit(_) => Some(Expr::Unit(pos)),
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))),
Union::Char(value) => Some(Expr::CharConstant(Box::new((value, pos)))),
2020-09-20 20:07:43 +02:00
Union::Str(value) => Some(Expr::StringConstant(Box::new((value, pos)))),
2020-04-12 17:00:06 +02:00
Union::Bool(true) => Some(Expr::True(pos)),
Union::Bool(false) => Some(Expr::False(pos)),
2020-04-10 06:16:39 +02:00
#[cfg(not(feature = "no_index"))]
2020-04-12 17:00:06 +02:00
Union::Array(array) => {
let items: Vec<_> = array
.into_iter()
.map(|x| map_dynamic_to_expr(x, pos))
.collect();
if items.iter().all(Option::is_some) {
Some(Expr::Array(Box::new((
2020-04-12 17:00:06 +02:00
items.into_iter().map(Option::unwrap).collect(),
pos,
))))
2020-04-12 17:00:06 +02:00
} else {
None
2020-04-10 06:16:39 +02:00
}
}
#[cfg(not(feature = "no_object"))]
2020-04-12 17:00:06 +02:00
Union::Map(map) => {
let items: Vec<_> = map
.into_iter()
2020-05-09 18:19:13 +02:00
.map(|(k, v)| ((k, pos), map_dynamic_to_expr(v, pos)))
2020-04-12 17:00:06 +02:00
.collect();
2020-05-09 18:19:13 +02:00
if items.iter().all(|(_, expr)| expr.is_some()) {
Some(Expr::Map(Box::new((
2020-04-12 17:00:06 +02:00
items
.into_iter()
2020-05-09 18:19:13 +02:00
.map(|((k, pos), expr)| ((k, pos), expr.unwrap()))
2020-04-12 17:00:06 +02:00
.collect(),
pos,
))))
2020-04-12 17:00:06 +02:00
} else {
None
2020-04-10 06:16:39 +02:00
}
}
2020-04-12 17:00:06 +02:00
_ => None,
}
2016-02-29 22:43:45 +01:00
}