rhai/src/parser.rs

2648 lines
88 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::calc_fn_hash;
use crate::engine::{make_getter, make_setter, Engine, FunctionsLib};
2020-03-04 15:00:01 +01:00
use crate::error::{LexError, ParseError, ParseErrorType};
use crate::optimize::{optimize_into_ast, OptimizationLevel};
2020-04-10 06:16:39 +02:00
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::{Position, Token, TokenIterator};
use crate::utils::StaticVec;
2020-05-08 08:50:48 +02:00
#[cfg(not(feature = "no_module"))]
use crate::module::ModuleRef;
#[cfg(feature = "no_module")]
2020-05-12 12:48:25 +02:00
#[derive(Debug, Eq, PartialEq, Clone, Hash, Copy, Default)]
2020-05-08 08:50:48 +02:00
pub struct ModuleRef;
2020-03-17 19:26:11 +01:00
use crate::stdlib::{
borrow::Cow,
boxed::Box,
2020-03-29 17:53:35 +02:00
char,
collections::HashMap,
2020-04-06 06:29:01 +02:00
format,
iter::{empty, Peekable},
2020-05-30 04:27:48 +02:00
mem,
2020-04-29 10:11:54 +02:00
num::NonZeroUsize,
ops::{Add, Deref, DerefMut},
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-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;
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
///
/// 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.
FunctionsLib,
);
2020-03-27 04:50:24 +01:00
impl AST {
/// Create a new `AST`.
pub fn new(statements: Vec<Stmt>, lib: FunctionsLib) -> Self {
Self(statements, lib)
2020-05-05 09:00:10 +02:00
}
/// Get the statements.
pub(crate) fn statements(&self) -> &Vec<Stmt> {
&self.0
}
/// Get a mutable reference to the statements.
pub(crate) fn statements_mut(&mut self) -> &mut Vec<Stmt> {
&mut self.0
}
/// Get the script-defined functions.
pub(crate) fn lib(&self) -> &FunctionsLib {
2020-05-19 16:25:57 +02:00
&self.1
}
/// 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.
///
/// The second `AST` is 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>> {
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)"#)?;
2020-04-01 04:21:49 +02:00
/// 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(())
/// # }
/// ```
pub fn merge(&self, other: &Self) -> 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
2020-05-19 16:25:57 +02:00
Self::new(ast, functions.merge(&other.1))
}
2020-03-27 04:50:24 +01:00
/// Clear all function definitions in the `AST`.
#[cfg(not(feature = "no_function"))]
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
/// Clear all statements in the `AST`, leaving only function definitions.
#[cfg(not(feature = "no_function"))]
pub fn retain_functions(&mut self) {
self.0 = vec![];
2020-03-27 04:50:24 +01:00
}
}
impl Add<Self> for &AST {
type Output = AST;
2020-03-27 04:50:24 +01:00
fn add(self, rhs: Self) -> Self::Output {
self.merge(rhs)
}
}
/// 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 {
/// Private function.
Private,
/// Public function.
Public,
}
/// A scripted function definition.
#[derive(Debug, Clone)]
2020-03-11 16:43:04 +01:00
pub struct FnDef {
2020-03-18 03:36:50 +01:00
/// Function name.
2020-03-11 16:43:04 +01:00
pub name: String,
/// Function access mode.
pub access: FnAccess,
2020-03-18 03:36:50 +01:00
/// Names of function parameters.
pub params: StaticVec<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,
}
2020-03-18 03:36:50 +01:00
/// `return`/`throw` statement.
2020-05-12 12:48:25 +02:00
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
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-05-05 09:00:10 +02:00
#[derive(Debug, Clone, Default)]
struct ParseState {
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(String, ScopeEntryType)>,
max_expr_depth: usize,
}
2020-04-28 17:05:03 +02:00
impl ParseState {
/// Create a new `ParseState`.
pub fn new(max_expr_depth: usize) -> Self {
Self {
max_expr_depth,
..Default::default()
}
2020-04-28 17:05:03 +02:00
}
/// Find a variable by name in the `ParseState`, searching in reverse.
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 zero when the variable name is not found in the `ParseState`.
2020-04-29 10:11:54 +02:00
pub fn find(&self, name: &str) -> Option<NonZeroUsize> {
self.stack
2020-04-28 17:05:03 +02:00
.iter()
.rev()
.enumerate()
2020-05-04 13:36:58 +02:00
.find(|(_, (n, typ))| match typ {
ScopeEntryType::Normal | ScopeEntryType::Constant => *n == name,
2020-05-05 04:39:12 +02:00
ScopeEntryType::Module => false,
2020-05-04 13:36:58 +02:00
})
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
}
/// 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 zero when the variable name is not found in the `ParseState`.
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
self.stack
2020-05-04 13:36:58 +02:00
.iter()
.rev()
.enumerate()
.find(|(_, (n, typ))| match typ {
2020-05-05 04:39:12 +02:00
ScopeEntryType::Module => *n == name,
2020-05-04 13:36:58 +02:00
ScopeEntryType::Normal | ScopeEntryType::Constant => false,
})
2020-04-29 10:11:54 +02:00
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
2020-04-28 17:05:03 +02:00
}
}
impl Deref for ParseState {
2020-05-04 13:36:58 +02:00
type Target = Vec<(String, ScopeEntryType)>;
fn deref(&self) -> &Self::Target {
&self.stack
}
}
impl DerefMut for ParseState {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.stack
}
}
2020-03-18 03:36:50 +01:00
/// A statement.
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)]
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>)>),
2020-03-18 03:36:50 +01:00
/// while expr { stmt }
While(Box<(Expr, Stmt)>),
2020-03-18 03:36:50 +01:00
/// loop { stmt }
2017-10-30 16:08:44 +01:00
Loop(Box<Stmt>),
2020-03-18 03:36:50 +01:00
/// for id in expr { stmt }
For(Box<(String, Expr, Stmt)>),
2020-03-18 03:36:50 +01:00
/// let id = expr
2020-05-09 18:19:13 +02:00
Let(Box<((String, Position), Option<Expr>)>),
2020-03-18 03:36:50 +01:00
/// const id = expr
2020-05-09 18:19:13 +02:00
Const(Box<((String, Position), Expr)>),
2020-03-18 03:36:50 +01:00
/// { stmt; ... }
Block(Box<(StaticVec<Stmt>, Position)>),
2020-03-18 03:36:50 +01:00
/// { stmt }
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
2020-05-09 18:19:13 +02:00
ReturnWithVal(Box<((ReturnType, Position), Option<Expr>)>),
2020-05-04 13:36:58 +02:00
/// import expr as module
2020-05-09 18:19:13 +02:00
Import(Box<(Expr, (String, Position))>),
2020-05-08 10:49:24 +02:00
/// expr id as name, ...
Export(Box<StaticVec<((String, Position), Option<(String, Position)>)>>),
}
impl Default for Stmt {
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.0.position(),
Stmt::Expr(x) => x.position(),
Stmt::While(x) => x.1.position(),
Stmt::Loop(x) => x.position(),
Stmt::For(x) => x.2.position(),
2020-05-09 18:19:13 +02:00
Stmt::Import(x) => (x.1).1,
2020-05-17 16:19:49 +02:00
Stmt::Export(x) => (x.get(0).0).1,
2020-03-17 10:33:37 +01:00
}
}
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::Import(_)
2020-05-08 10:49:24 +02:00
| Stmt::Export(_)
| Stmt::Expr(_)
2020-04-01 10:22:18 +02:00
| Stmt::Continue(_)
| Stmt::Break(_)
| Stmt::ReturnWithVal(_) => 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.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,
Stmt::Import(_) => false,
2020-05-08 10:49:24 +02:00
Stmt::Export(_) => false,
2020-03-11 16:43:04 +01:00
}
}
2020-03-09 14:57:07 +01:00
}
2020-03-18 03:36:50 +01:00
/// An expression.
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)]
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"))]
FloatConstant(Box<(FLOAT, Position)>),
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-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<((String, 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-05-23 12:59:28 +02:00
/// func(expr, ... ) - ((function name, native_only, 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-05-23 12:59:28 +02:00
(Cow<'static, str>, bool, Position),
Option<Box<ModuleRef>>,
u64,
StaticVec<Expr>,
2020-05-09 18:19:13 +02:00
Option<Dynamic>,
)>,
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<((String, 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),
}
impl Default for Expr {
fn default() -> Self {
Self::Unit(Default::default())
}
}
impl Expr {
2020-03-18 03:36:50 +01:00
/// Get the `Dynamic` value of a constant expression.
///
/// # Panics
///
/// Panics when the expression is not constant.
pub fn get_constant_value(&self) -> Dynamic {
match self {
2020-05-30 04:27:48 +02:00
Self::Expr(x) => x.get_constant_value(),
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(),
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(Self::get_constant_value).collect::<Vec<_>>(),
))),
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()
2020-05-09 18:19:13 +02:00
.map(|((k, _), v)| (k.clone(), v.get_constant_value()))
2020-04-12 17:00:06 +02:00
.collect::<HashMap<_, _>>(),
)))
}
2020-03-29 17:53:35 +02:00
_ => panic!("cannot get value of non-constant expression"),
}
}
2020-03-18 03:36:50 +01:00
/// Get the display value of a constant expression.
///
/// # Panics
///
/// Panics when the expression is not constant.
pub fn get_constant_str(&self) -> String {
2020-03-13 11:12:41 +01:00
match self {
2020-05-30 04:27:48 +02:00
Self::Expr(x) => x.get_constant_str(),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(x) => x.0.to_string(),
Self::IntegerConstant(x) => x.0.to_string(),
Self::CharConstant(x) => x.0.to_string(),
Self::StringConstant(_) => "string".to_string(),
2020-04-06 11:47:34 +02:00
Self::True(_) => "true".to_string(),
Self::False(_) => "false".to_string(),
Self::Unit(_) => "()".to_string(),
2020-03-13 11:12:41 +01:00
Self::Array(x) if x.0.iter().all(Self::is_constant) => "array".to_string(),
_ => panic!("cannot get value of non-constant expression"),
2020-03-13 11:12:41 +01:00
}
}
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,
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-05-23 12:59:28 +02:00
Self::FnCall(x) => (x.0).2,
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-03-10 04:22:41 +01:00
/// Override the `Position` of the expression.
pub(crate) fn set_position(mut self, new_pos: Position) -> Self {
match &mut self {
2020-05-30 04:27:48 +02:00
Self::Expr(ref mut x) => {
let expr = mem::take(x);
*x = Box::new(expr.set_position(new_pos));
}
#[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,
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-05-23 12:59:28 +02:00
Self::FnCall(x) => (x.0).2 = 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,
}
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,
expr => expr.is_constant(),
2020-03-12 05:40:28 +01:00
}
}
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-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(_)
| Self::In(_)
| Self::And(_)
| Self::Or(_)
2020-04-26 12:04:07 +02:00
| Self::True(_)
| Self::False(_)
| Self::Unit(_) => false,
Self::StringConstant(_)
| Self::Stmt(_)
| Self::FnCall(_)
| Self::Assignment(_)
| Self::Dot(_)
| Self::Index(_)
| Self::Array(_)
| Self::Map(_) => match token {
2020-04-17 13:00:52 +02:00
Token::LeftBracket => true,
_ => false,
},
Self::Variable(_) => match token {
2020-05-05 11:51:40 +02:00
Token::LeftBracket | Token::LeftParen => true,
2020-05-05 04:39:12 +02:00
#[cfg(not(feature = "no_module"))]
2020-05-04 11:43:54 +02:00
Token::DoubleColon => true,
_ => false,
},
Self::Property(_) => match token {
2020-04-17 13:00:52 +02:00
Token::LeftBracket | Token::LeftParen => true,
_ => false,
},
}
}
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-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.clone(), 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-04-17 13:00:52 +02:00
fn eat_token(input: &mut Peekable<TokenIterator>, token: Token) -> Position {
let (t, pos) = input.next().unwrap();
if t != token {
panic!(
"expecting {} (found {}) at {}",
token.syntax(),
t.syntax(),
pos
);
}
pos
}
/// Match a particular token, consuming it if matched.
fn match_token(input: &mut Peekable<TokenIterator>, 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 )
fn parse_paren_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-05-04 13:36:58 +02:00
pos: Position,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
if match_token(input, Token::RightParen)? {
2020-05-04 13:36:58 +02:00
return Ok(Expr::Unit(pos));
}
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
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>
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).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(),
)
.into_err(pos)),
2016-02-29 22:43:45 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Parse a function call.
2020-04-28 13:39:36 +02:00
fn parse_call_expr<'a>(
2019-09-18 12:21:07 +02:00
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-04-28 17:05:03 +02:00
id: String,
2020-05-23 18:29:06 +02:00
mut modules: Option<Box<ModuleRef>>,
begin: Position,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
let (token, pos) = input.peek().unwrap();
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(*pos));
}
let mut args = StaticVec::new();
2017-10-28 05:30:12 +02:00
match token {
// 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),
)
.into_err(*pos))
2020-04-06 06:29:01 +02:00
}
// id <error>
Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(*pos)),
2020-04-06 06:29:01 +02:00
// id()
Token::RightParen => {
eat_token(input, Token::RightParen);
2020-05-08 08:50:48 +02:00
#[cfg(not(feature = "no_module"))]
2020-05-09 18:19:13 +02:00
let hash_fn_def = {
2020-05-08 08:50:48 +02:00
if let Some(modules) = modules.as_mut() {
modules.set_index(state.find_module(&modules.get(0).0));
2020-05-09 10:15:50 +02:00
2020-05-09 04:00:59 +02:00
// 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.
2020-05-09 04:00:59 +02:00
// 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())
2020-05-09 10:15:50 +02:00
} else {
2020-05-23 18:29:06 +02:00
// Qualifiers (none) + function name + no parameters.
calc_fn_hash(empty(), &id, 0, empty())
2020-05-08 08:50:48 +02:00
}
2020-05-09 10:15:50 +02:00
};
// Qualifiers (none) + function name + no parameters.
#[cfg(feature = "no_module")]
2020-05-23 18:29:06 +02:00
let hash_fn_def = calc_fn_hash(empty(), &id, 0, empty());
2020-05-09 10:15:50 +02:00
return Ok(Expr::FnCall(Box::new((
2020-05-23 12:59:28 +02:00
(id.into(), false, begin),
2020-05-04 13:36:58 +02:00
modules,
2020-05-09 18:19:13 +02:00
hash_fn_def,
args,
None,
))));
2020-04-06 06:29:01 +02:00
}
// id...
_ => (),
2016-02-29 22:43:45 +01:00
}
loop {
args.push(parse_expr(input, state, level + 1, allow_stmt_expr)?);
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-05-08 08:50:48 +02:00
#[cfg(not(feature = "no_module"))]
2020-05-09 18:19:13 +02:00
let hash_fn_def = {
2020-05-08 08:50:48 +02:00
if let Some(modules) = modules.as_mut() {
modules.set_index(state.find_module(&modules.get(0).0));
2020-05-09 10:15:50 +02:00
2020-05-09 04:00:59 +02:00
// 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.
2020-05-09 04:00:59 +02:00
// 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())
2020-05-09 10:15:50 +02:00
} else {
2020-05-23 18:29:06 +02:00
// Qualifiers (none) + function name + number of arguments.
calc_fn_hash(empty(), &id, args.len(), empty())
2020-05-08 08:50:48 +02:00
}
2020-05-09 10:15:50 +02:00
};
2020-05-23 18:29:06 +02:00
// Qualifiers (none) + function name + number of arguments.
2020-05-09 10:15:50 +02:00
#[cfg(feature = "no_module")]
2020-05-23 18:29:06 +02:00
let hash_fn_def = calc_fn_hash(empty(), &id, args.len(), empty());
2020-05-09 10:15:50 +02:00
return Ok(Expr::FnCall(Box::new((
2020-05-23 12:59:28 +02:00
(id.into(), false, begin),
2020-05-04 13:36:58 +02:00
modules,
2020-05-09 18:19:13 +02:00
hash_fn_def,
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>
(Token::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
2020-04-17 13:00:52 +02:00
}
// 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.
fn parse_index_chain<'a>(
2019-09-18 12:21:07 +02:00
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-04-28 17:05:03 +02:00
lhs: Expr,
pos: Position,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
let idx_expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
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-05-11 17:48:50 +02:00
let idx_pos = eat_token(input, Token::LeftBracket);
2020-04-26 13:37:32 +02:00
// Recursively parse the indexing chain, right-binding each
2020-05-30 04:27:48 +02:00
let idx_expr = parse_index_chain(
input,
state,
idx_expr,
idx_pos,
level + 1,
allow_stmt_expr,
)?;
2020-04-26 13:37:32 +02:00
// Indexing binds to right
2020-05-30 04:27:48 +02:00
Ok(Expr::Index(Box::new((lhs, idx_expr, 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)), pos)),
)),
_ => Ok(Expr::Index(Box::new((lhs, idx_expr, pos)))),
}
}
2020-04-26 12:04:07 +02:00
}
}
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).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.
fn parse_array_literal<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-05-04 13:36:58 +02:00
pos: Position,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
let mut arr = StaticVec::new();
2016-03-26 18:46:28 +01:00
if !match_token(input, Token::RightBracket)? {
while !input.peek().unwrap().0.is_eof() {
arr.push(parse_expr(input, state, level + 1, allow_stmt_expr)?);
match input.peek().unwrap() {
(Token::Comma, _) => eat_token(input, Token::Comma),
(Token::RightBracket, _) => {
eat_token(input, Token::RightBracket);
break;
}
(Token::EOF, pos) => {
2020-05-04 13:36:58 +02:00
return Err(PERR::MissingToken(
Token::RightBracket.into(),
"to end this array literal".into(),
)
2020-05-04 13:36:58 +02:00
.into_err(*pos))
}
(Token::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
}
(_, pos) => {
return Err(PERR::MissingToken(
2020-05-04 13:36:58 +02:00
Token::Comma.into(),
2020-03-29 17:53:35 +02:00
"to separate the items of this array literal".into(),
2020-03-27 16:47:23 +01:00
)
.into_err(*pos))
}
2020-04-06 11:47:34 +02:00
};
}
2016-03-26 18:46:28 +01:00
}
Ok(Expr::Array(Box::new((arr, pos))))
2016-03-26 18:46:28 +01:00
}
2020-03-29 17:53:35 +02:00
/// Parse a map literal.
fn parse_map_literal<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-05-04 13:36:58 +02:00
pos: Position,
level: usize,
2020-03-29 17:53:35 +02:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
let mut map = StaticVec::new();
2020-03-29 17:53:35 +02:00
if !match_token(input, Token::RightBrace)? {
while !input.peek().unwrap().0.is_eof() {
const MISSING_RBRACE: &str = "to end this object map literal";
let (name, pos) = match input.next().unwrap() {
(Token::Identifier(s), pos) => (s, pos),
(Token::StringConst(s), pos) => (s, pos),
(Token::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).into_err(pos))
}
(_, pos) if map.is_empty() => {
2020-05-04 13:36:58 +02:00
return Err(
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
.into_err(pos),
)
}
(Token::EOF, pos) => {
2020-05-04 13:36:58 +02:00
return Err(
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
.into_err(pos),
)
2020-03-29 17:53:35 +02:00
}
(_, 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(PERR::BadInput(err.to_string()).into_err(pos))
}
(_, pos) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MissingToken(
2020-05-04 13:36:58 +02:00
Token::Colon.into(),
2020-03-29 17:53:35 +02:00
format!(
"to follow the property '{}' in this object map literal",
name
),
)
.into_err(pos))
}
};
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
2020-03-29 17:53:35 +02:00
2020-05-09 18:19:13 +02:00
map.push(((name, pos), expr));
2020-03-29 17:53:35 +02:00
match input.peek().unwrap() {
(Token::Comma, _) => {
2020-04-17 13:00:52 +02:00
eat_token(input, Token::Comma);
}
(Token::RightBrace, _) => {
eat_token(input, Token::RightBrace);
break;
2020-03-29 17:53:35 +02:00
}
(Token::Identifier(_), pos) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MissingToken(
2020-05-04 13:36:58 +02:00
Token::Comma.into(),
2020-03-29 17:53:35 +02:00
"to separate the items of this object map literal".into(),
)
.into_err(*pos))
}
(Token::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
}
(_, pos) => {
2020-05-04 13:36:58 +02:00
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, pos))))
2020-03-29 17:53:35 +02:00
}
2020-03-18 03:36:50 +01:00
/// Parse a primary expression.
2020-03-22 14:03:58 +01:00
fn parse_primary<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
let (token, pos) = input.peek().unwrap();
let pos = *pos;
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
let (token, _) = match token {
// { - block statement as expression
Token::LeftBrace if allow_stmt_expr => {
return parse_block(input, state, false, level + 1, allow_stmt_expr)
.map(|block| Expr::Stmt(Box::new((block, pos))));
2020-03-07 03:39:00 +01:00
}
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(pos)),
_ => input.next().unwrap(),
};
2020-03-05 13:28:03 +01:00
let mut root_expr = match token {
Token::IntegerConstant(x) => Expr::IntegerConstant(Box::new((x, pos))),
2020-04-17 14:08:41 +02:00
#[cfg(not(feature = "no_float"))]
Token::FloatConstant(x) => Expr::FloatConstant(Box::new((x, pos))),
Token::CharConstant(c) => Expr::CharConstant(Box::new((c, pos))),
2020-05-25 07:44:28 +02:00
Token::StringConst(s) => Expr::StringConstant(Box::new((s.into(), pos))),
2020-04-28 17:05:03 +02:00
Token::Identifier(s) => {
let index = state.find(&s);
2020-05-09 18:19:13 +02:00
Expr::Variable(Box::new(((s, pos), None, 0, index)))
2020-04-28 17:05:03 +02:00
}
Token::LeftParen => parse_paren_expr(input, state, pos, level + 1, allow_stmt_expr)?,
2020-03-17 10:33:37 +01:00
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => parse_array_literal(input, state, pos, level + 1, allow_stmt_expr)?,
2020-03-29 17:53:35 +02:00
#[cfg(not(feature = "no_object"))]
Token::MapStart => parse_map_literal(input, state, pos, level + 1, allow_stmt_expr)?,
Token::True => Expr::True(pos),
Token::False => Expr::False(pos),
Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
token => {
2020-04-17 13:00:52 +02:00
return Err(PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(pos))
}
};
// 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-04-10 06:16:39 +02:00
2020-04-17 13:00:52 +02:00
root_expr = match (root_expr, token) {
// Function call
(Expr::Variable(x), Token::LeftParen) => {
2020-05-09 18:19:13 +02:00
let ((name, pos), modules, _, _) = *x;
parse_call_expr(input, state, name, modules, pos, level + 1, allow_stmt_expr)?
}
2020-05-12 04:20:29 +02:00
(Expr::Property(_), _) => unreachable!(),
2020-05-04 17:07:42 +02:00
// module access
2020-05-05 04:39:12 +02:00
#[cfg(not(feature = "no_module"))]
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;
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
}
(_, 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, expr, token_pos, level + 1, allow_stmt_expr)?
2020-04-28 17:05:03 +02:00
}
2020-04-17 13:00:52 +02:00
// Unknown postfix operator
(expr, token) => panic!("unknown postfix operator {:?} for {:?}", token, 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
2020-05-08 08:50:48 +02:00
#[cfg(not(feature = "no_module"))]
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());
modules.set_index(state.find_module(&modules.get(0).0));
}
_ => (),
}
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-03-22 14:03:58 +01:00
fn parse_unary<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
let (token, pos) = input.peek().unwrap();
let pos = *pos;
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
match token {
2020-03-27 16:47:23 +01:00
// If statement is allowed to act as expressions
Token::If => Ok(Expr::Stmt(Box::new((
parse_if(input, state, false, level + 1, allow_stmt_expr)?,
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, level + 1, allow_stmt_expr)? {
// 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"))]
{
Some(Expr::FloatConstant(Box::new((-(x.0 as FLOAT), pos))))
}
#[cfg(feature = "no_float")]
{
None
}
})
.ok_or_else(|| {
PERR::BadInput(
LexError::MalformedNumber(format!("-{}", x.0)).to_string(),
)
2020-03-24 09:46:47 +01:00
.into_err(pos)
})
}
// Negative float
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(x) => Ok(Expr::FloatConstant(Box::new((-x.0, x.1)))),
// Call negative function
expr => {
2020-05-09 10:15:50 +02:00
let op = "-";
let hash = calc_fn_hash(empty(), op, 2, empty());
let mut args = StaticVec::new();
args.push(expr);
2020-05-09 10:15:50 +02:00
Ok(Expr::FnCall(Box::new((
2020-05-23 12:59:28 +02:00
(op.into(), true, 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, level + 1, allow_stmt_expr)
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();
args.push(parse_primary(input, state, level + 1, allow_stmt_expr)?);
2020-05-09 18:19:13 +02:00
2020-05-09 10:15:50 +02:00
let op = "!";
let hash = calc_fn_hash(empty(), op, 2, empty());
2020-05-09 10:15:50 +02:00
Ok(Expr::FnCall(Box::new((
2020-05-23 12:59:28 +02:00
(op.into(), true, pos),
2020-05-04 11:43:54 +02:00
None,
2020-05-09 10:15:50 +02:00
hash,
args,
2020-05-09 18:19:13 +02:00
Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
))))
2019-09-18 12:21:07 +02:00
}
// <EOF>
Token::EOF => Err(PERR::UnexpectedEOF.into_err(pos)),
// All other tokens
_ => parse_primary(input, state, level + 1, allow_stmt_expr),
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-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))))
}
Expr::Variable(x) => {
2020-05-09 18:19:13 +02:00
let ((name, name_pos), _, _, index) = x.as_ref();
match state.stack[(state.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))
}
ScopeEntryType::Module => unreachable!(),
}
}
Expr::Index(x) | Expr::Dot(x) => match &x.0 {
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))))
}
Expr::Variable(x) => {
2020-05-09 18:19:13 +02:00
let ((name, name_pos), _, _, index) = x.as_ref();
match state.stack[(state.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))
}
ScopeEntryType::Module => unreachable!(),
}
}
_ => Err(PERR::AssignmentToCopy.into_err(x.0.position())),
},
expr if expr.is_constant() => {
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
}
_ => 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-04-22 11:37:06 +02:00
fn parse_op_assignment_stmt<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-04-06 06:29:01 +02:00
lhs: Expr,
level: usize,
2020-04-22 11:37:06 +02:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
let (token, pos) = input.peek().unwrap();
let pos = *pos;
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
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, level + 1, allow_stmt_expr)?;
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-05-04 11:43:54 +02:00
fn make_dot_expr(
lhs: Expr,
rhs: Expr,
op_pos: Position,
is_index: bool,
) -> Result<Expr, ParseError> {
2020-05-04 11:43:54 +02:00
Ok(match (lhs, rhs) {
2020-04-26 12:04:07 +02:00
// idx_lhs[idx_rhs].rhs
2020-04-26 13:37:32 +02:00
// Attach dot chain to the bottom level of indexing chain
(Expr::Index(x), rhs) => {
Expr::Index(Box::new((x.0, make_dot_expr(x.1, rhs, op_pos, true)?, x.2)))
}
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 lhs = if is_index { lhs.into_property() } else { lhs };
let getter = make_getter(&name);
let setter = make_setter(&name);
let rhs = Expr::Property(Box::new(((name, getter, setter), pos)));
2020-05-09 18:19:13 +02:00
Expr::Dot(Box::new((lhs, rhs, op_pos)))
}
(lhs, Expr::Property(x)) => {
2020-04-26 12:04:07 +02:00
let lhs = if is_index { lhs.into_property() } else { lhs };
2020-05-09 18:19:13 +02:00
let rhs = Expr::Property(x);
Expr::Dot(Box::new((lhs, rhs, op_pos)))
2020-04-26 12:04:07 +02:00
}
2020-05-04 13:36:58 +02:00
// lhs.module::id - syntax error
(_, Expr::Variable(x)) if x.1.is_some() => {
2020-05-08 08:50:48 +02:00
#[cfg(feature = "no_module")]
unreachable!();
#[cfg(not(feature = "no_module"))]
2020-05-17 16:19:49 +02:00
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get(0).1));
2020-05-04 11:43:54 +02:00
}
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.into_property(),
pos,
))),
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 (idx_lhs, idx_rhs, pos) = *x;
Expr::Dot(Box::new((
lhs,
Expr::Index(Box::new((
idx_lhs.into_property(),
idx_rhs.into_property(),
pos,
))),
op_pos,
)))
}
2020-04-26 12:04:07 +02:00
// lhs.rhs
(lhs, rhs) => Expr::Dot(Box::new((lhs, rhs.into_property(), op_pos))),
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.
fn parse_binary_op<'a>(
2019-09-18 12:21:07 +02:00
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-03-14 04:51:45 +01:00
parent_precedence: u8,
2019-09-18 12:21:07 +02:00
lhs: Expr,
mut level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(lhs.position()));
}
let mut root = lhs;
2016-02-29 22:43:45 +01:00
loop {
let (current_op, _) = input.peek().unwrap();
let precedence = current_op.precedence();
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();
let rhs = parse_unary(input, state, level, allow_stmt_expr)?;
2016-02-29 22:43:45 +01:00
let next_precedence = input.peek().unwrap().0.precedence();
2016-02-29 22:43:45 +01:00
// 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, precedence, rhs, level, allow_stmt_expr)?
} else {
// Otherwise bind to left (even if next operator has the same precedence)
rhs
};
level += 1;
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
2020-05-09 18:19:13 +02:00
let cmp_def = Some(false.into());
let op = op_token.syntax();
let hash = calc_fn_hash(empty(), &op, 2, empty());
let mut args = StaticVec::new();
args.push(root);
args.push(rhs);
2020-04-22 11:37:06 +02:00
root = match op_token {
2020-05-23 12:59:28 +02:00
Token::Plus => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::Minus => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::Multiply => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::Divide => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
2020-04-28 17:05:03 +02:00
2020-05-23 12:59:28 +02:00
Token::LeftShift => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::RightShift => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::Modulo => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::PowerOf => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
2020-04-28 17:05:03 +02:00
// Comparison operators default to false when passed invalid operands
2020-05-23 12:59:28 +02:00
Token::EqualsTo => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def))),
Token::NotEqualsTo => {
Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def)))
}
Token::LessThan => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def))),
Token::LessThanEqualsTo => {
2020-05-23 12:59:28 +02:00
Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def)))
}
Token::GreaterThan => {
Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def)))
}
Token::GreaterThanEqualsTo => {
2020-05-23 12:59:28 +02:00
Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def)))
}
Token::Or => {
let rhs = args.pop();
let current_lhs = args.pop();
Expr::Or(Box::new((current_lhs, rhs, pos)))
}
Token::And => {
let rhs = args.pop();
let current_lhs = args.pop();
Expr::And(Box::new((current_lhs, rhs, pos)))
}
2020-05-23 12:59:28 +02:00
Token::Ampersand => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::Pipe => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::XOr => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
Token::In => {
let rhs = args.pop();
let current_lhs = args.pop();
make_in_expr(current_lhs, rhs, pos)?
}
#[cfg(not(feature = "no_object"))]
Token::Period => {
let mut rhs = args.pop();
let current_lhs = args.pop();
2020-05-12 04:20:29 +02:00
match &mut rhs {
// current_lhs.rhs(...) - method call
Expr::FnCall(x) => {
2020-05-23 12:59:28 +02:00
let ((id, _, _), _, hash, args, _) = x.as_mut();
2020-05-12 04:20:29 +02:00
// Recalculate function call hash because there is an additional argument
*hash = calc_fn_hash(empty(), id, args.len() + 1, empty());
2020-05-12 04:20:29 +02:00
}
_ => (),
}
make_dot_expr(current_lhs, rhs, pos, false)?
}
2020-05-04 13:36:58 +02:00
token => return Err(PERR::UnknownOperator(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-03-22 14:03:58 +01:00
fn parse_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
let (_, pos) = input.peek().unwrap();
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(*pos));
}
let lhs = parse_unary(input, state, level + 1, allow_stmt_expr)?;
parse_binary_op(input, state, 1, lhs, level + 1, allow_stmt_expr)
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 `{}`).
fn ensure_not_statement_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
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`).
fn ensure_not_assignment<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<(), ParseError> {
2020-04-22 11:37:06 +02:00
match input.peek().unwrap() {
(Token::Equals, pos) => {
return Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos))
}
(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) => {
return Err(PERR::BadInput(
"Expecting a boolean expression, not an assignment".to_string(),
)
.into_err(*pos))
}
_ => Ok(()),
}
}
2020-03-18 03:36:50 +01:00
/// Parse an if statement.
2020-03-17 10:33:37 +01:00
fn parse_if<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-03-17 10:33:37 +01:00
breakable: bool,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// if ...
let pos = eat_token(input, Token::If);
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
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, level + 1, allow_stmt_expr)?;
2020-04-22 11:37:06 +02:00
ensure_not_assignment(input)?;
let if_body = parse_block(input, state, breakable, level + 1, allow_stmt_expr)?;
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, breakable, level + 1, allow_stmt_expr)?
2020-03-16 16:51:32 +01:00
} else {
// if guard { if_body } else { else-body }
parse_block(input, state, breakable, level + 1, allow_stmt_expr)?
})
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))))
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse a while loop.
2020-03-22 14:03:58 +01:00
fn parse_while<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// while ...
let pos = eat_token(input, Token::While);
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
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, level + 1, allow_stmt_expr)?;
2020-04-22 11:37:06 +02:00
ensure_not_assignment(input)?;
let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?;
2016-02-29 22:43:45 +01:00
Ok(Stmt::While(Box::new((guard, body))))
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse a loop statement.
2020-03-22 14:03:58 +01:00
fn parse_loop<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// loop ...
let pos = eat_token(input, Token::Loop);
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
2017-10-30 16:08:44 +01:00
2020-03-18 11:41:18 +01:00
// loop { body }
let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?;
2017-10-30 16:08:44 +01:00
Ok(Stmt::Loop(Box::new(body)))
}
2020-03-18 03:36:50 +01:00
/// Parse a for loop.
2020-03-22 14:03:58 +01:00
fn parse_for<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// for ...
let pos = eat_token(input, Token::For);
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
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,
2020-03-18 11:41:18 +01:00
// Bad identifier
2020-03-24 09:46:47 +01:00
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
// EOF
(Token::EOF, pos) => return Err(PERR::VariableExpected.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, _) => (),
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).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, level + 1, allow_stmt_expr)?;
2020-04-28 17:05:03 +02:00
let prev_len = state.len();
state.push((name.clone(), ScopeEntryType::Normal));
2020-04-28 17:05:03 +02:00
let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?;
2020-04-28 17:05:03 +02:00
state.truncate(prev_len);
Ok(Stmt::For(Box::new((name, expr, body))))
}
2020-03-18 03:36:50 +01:00
/// Parse a variable definition statement.
fn parse_let<'a>(
2020-03-13 11:12:41 +01:00
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-03-25 04:27:18 +01:00
var_type: ScopeEntryType,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// let/const... (specified in `var_type`)
let (_, pos) = input.next().unwrap();
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
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),
2020-03-24 09:46:47 +01:00
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
(_, 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 = ...
if match_token(input, Token::Equals)? {
2020-03-18 11:41:18 +01:00
// let name = expr
let init_value = parse_expr(input, state, level + 1, allow_stmt_expr)?;
2020-03-13 11:12:41 +01:00
2020-03-14 16:41:15 +01:00
match var_type {
2020-03-18 11:41:18 +01:00
// let name = expr
2020-04-28 17:05:03 +02:00
ScopeEntryType::Normal => {
state.push((name.clone(), ScopeEntryType::Normal));
2020-05-09 18:19:13 +02:00
Ok(Stmt::Let(Box::new(((name, pos), Some(init_value)))))
2020-04-28 17:05:03 +02:00
}
2020-03-18 11:41:18 +01:00
// const name = { expr:constant }
2020-03-25 04:27:18 +01:00
ScopeEntryType::Constant if init_value.is_constant() => {
state.push((name.clone(), ScopeEntryType::Constant));
2020-05-09 18:19:13 +02:00
Ok(Stmt::Const(Box::new(((name, pos), init_value))))
2020-03-13 11:12:41 +01:00
}
2020-03-18 11:41:18 +01:00
// const name = expr - error
2020-03-27 16:47:23 +01:00
ScopeEntryType::Constant => {
Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position()))
}
// Variable cannot be a module
2020-05-05 04:39:12 +02:00
ScopeEntryType::Module => unreachable!(),
2016-02-29 22:43:45 +01:00
}
2020-03-14 16:41:15 +01:00
} else {
2020-03-18 11:41:18 +01:00
// let name
match var_type {
ScopeEntryType::Normal => {
state.push((name.clone(), ScopeEntryType::Normal));
2020-05-09 18:19:13 +02:00
Ok(Stmt::Let(Box::new(((name, pos), None))))
}
ScopeEntryType::Constant => {
state.push((name.clone(), ScopeEntryType::Constant));
2020-05-09 18:19:13 +02:00
Ok(Stmt::Const(Box::new(((name, pos), Expr::Unit(pos)))))
}
// Variable cannot be a module
ScopeEntryType::Module => unreachable!(),
}
2016-02-29 22:43:45 +01:00
}
}
2020-05-04 13:36:58 +02:00
/// Parse an import statement.
fn parse_import<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
2020-05-04 13:36:58 +02:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
2020-05-04 13:36:58 +02:00
// import ...
let pos = eat_token(input, Token::Import);
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
2020-05-04 13:36:58 +02:00
// import expr ...
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
2020-05-04 13:36:58 +02:00
// import expr as ...
match input.next().unwrap() {
(Token::As, _) => (),
(_, pos) => {
return Err(
PERR::MissingToken(Token::As.into(), "in this import statement".into())
.into_err(pos),
)
}
}
// import expr as name ...
let (name, _) = match input.next().unwrap() {
(Token::Identifier(s), pos) => (s, pos),
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
};
state.push((name.clone(), ScopeEntryType::Module));
2020-05-09 18:19:13 +02:00
Ok(Stmt::Import(Box::new((expr, (name, pos)))))
2020-05-04 13:36:58 +02:00
}
2020-05-08 10:49:24 +02:00
/// Parse an export statement.
fn parse_export<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
) -> Result<Stmt, ParseError> {
let pos = eat_token(input, Token::Export);
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
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::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).into_err(pos))
}
(_, 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)),
(_, 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)))
2020-05-08 10:49:24 +02:00
}
2020-03-18 03:36:50 +01:00
/// Parse a statement block.
2020-03-17 10:33:37 +01:00
fn parse_block<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-03-17 10:33:37 +01:00
breakable: bool,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
2020-03-18 11:41:18 +01:00
// Must start with {
let pos = match input.next().unwrap() {
2020-03-16 16:51:32 +01:00
(Token::LeftBrace, pos) => pos,
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).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
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
let mut statements = StaticVec::new();
let prev_len = state.len();
while !match_token(input, Token::RightBrace)? {
2020-03-17 10:33:37 +01:00
// Parse statements inside the block
let stmt = parse_stmt(input, state, breakable, false, level + 1, allow_stmt_expr)?;
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>
(Token::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).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
state.truncate(prev_len);
2020-04-28 17:05:03 +02:00
Ok(Stmt::Block(Box::new((statements, pos))))
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse an expression as a statement.
2020-03-22 14:03:58 +01:00
fn parse_expr_stmt<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
let (_, pos) = input.peek().unwrap();
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(*pos));
}
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
let expr = parse_op_assignment_stmt(input, state, expr, level + 1, allow_stmt_expr)?;
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-03-17 10:33:37 +01:00
fn parse_stmt<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
2020-03-17 10:33:37 +01:00
breakable: bool,
2020-05-08 10:49:24 +02:00
is_global: bool,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, ParseError> {
use ScopeEntryType::{Constant, Normal};
let (token, pos) = match input.peek().unwrap() {
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
x => x,
};
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(*pos));
}
match token {
2020-03-18 11:41:18 +01:00
// Semicolon - empty statement
Token::SemiColon => Ok(Stmt::Noop(*pos)),
2020-03-18 11:41:18 +01:00
Token::LeftBrace => parse_block(input, state, breakable, level + 1, allow_stmt_expr),
2020-04-01 10:22:18 +02:00
2020-03-18 11:41:18 +01:00
// fn ...
Token::Fn if !is_global => Err(PERR::WrongFnDefinition.into_err(*pos)),
Token::Fn => unreachable!(),
Token::If => parse_if(input, state, breakable, level + 1, allow_stmt_expr),
Token::While => parse_while(input, state, level + 1, allow_stmt_expr),
Token::Loop => parse_loop(input, state, level + 1, allow_stmt_expr),
Token::For => parse_for(input, state, level + 1, allow_stmt_expr),
2020-04-01 10:22:18 +02:00
Token::Continue if breakable => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::Continue);
2020-04-01 10:22:18 +02:00
Ok(Stmt::Continue(pos))
}
Token::Break if breakable => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::Break);
Ok(Stmt::Break(pos))
}
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(*pos)),
2020-04-01 10:22:18 +02:00
Token::Return | Token::Throw => {
let pos = *pos;
let return_type = match input.next().unwrap() {
(Token::Return, _) => ReturnType::Return,
(Token::Throw, _) => ReturnType::Exception,
_ => panic!("token should be return or throw"),
2020-03-03 11:15:20 +01:00
};
match input.peek().unwrap() {
// `return`/`throw` at <EOF>
2020-05-09 18:19:13 +02:00
(Token::EOF, pos) => Ok(Stmt::ReturnWithVal(Box::new(((return_type, *pos), None)))),
2020-03-18 03:36:50 +01:00
// `return;` or `throw;`
(Token::SemiColon, _) => {
2020-05-09 18:19:13 +02:00
Ok(Stmt::ReturnWithVal(Box::new(((return_type, pos), None))))
}
2020-03-18 03:36:50 +01:00
// `return` or `throw` with expression
(_, _) => {
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
2020-03-19 12:53:42 +01:00
let pos = expr.position();
Ok(Stmt::ReturnWithVal(Box::new((
2020-05-09 18:19:13 +02:00
(return_type, pos),
Some(expr),
))))
}
}
}
2020-04-01 10:22:18 +02:00
Token::Let => parse_let(input, state, Normal, level + 1, allow_stmt_expr),
Token::Const => parse_let(input, state, Constant, level + 1, allow_stmt_expr),
2020-04-01 10:22:18 +02:00
#[cfg(not(feature = "no_module"))]
Token::Import => parse_import(input, state, level + 1, allow_stmt_expr),
2020-05-04 13:36:58 +02:00
2020-05-08 10:49:24 +02:00
#[cfg(not(feature = "no_module"))]
Token::Export if !is_global => Err(PERR::WrongExport.into_err(*pos)),
#[cfg(not(feature = "no_module"))]
Token::Export => parse_export(input, state, level + 1),
2020-05-08 10:49:24 +02:00
_ => parse_expr_stmt(input, state, level + 1, allow_stmt_expr),
2016-02-29 22:43:45 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Parse a function definition.
2020-03-22 14:03:58 +01:00
fn parse_fn<'a>(
input: &mut Peekable<TokenIterator<'a>>,
state: &mut ParseState,
access: FnAccess,
level: usize,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<FnDef, ParseError> {
2020-05-08 10:49:24 +02:00
let pos = eat_token(input, Token::Fn);
2020-03-16 16:51:32 +01:00
if level > state.max_expr_depth {
return Err(PERR::ExprTooDeep.into_err(pos));
}
let name = match input.next().unwrap() {
(Token::Identifier(s), _) => s,
(_, pos) => return Err(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 end_err = format!("to close the parameters list of function '{}'", name);
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 {
match input.next().unwrap() {
2020-04-28 17:05:03 +02:00
(Token::Identifier(s), pos) => {
state.push((s.clone(), ScopeEntryType::Normal));
2020-04-28 17:05:03 +02:00
params.push((s, pos))
}
(Token::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).into_err(pos))
}
2020-05-04 13:36:58 +02:00
(_, pos) => {
return Err(PERR::MissingToken(Token::RightParen.into(), end_err).into_err(pos))
}
2020-03-16 16:51:32 +01:00
}
match input.next().unwrap() {
(Token::RightParen, _) => break,
(Token::Comma, _) => (),
(Token::Identifier(_), pos) => {
2020-05-04 13:36:58 +02:00
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
}
(Token::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).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, _) => parse_block(input, state, false, level + 1, allow_stmt_expr)?,
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
};
2020-04-06 11:47:34 +02:00
let params = params.into_iter().map(|(p, _)| p).collect();
Ok(FnDef {
2020-03-11 16:43:04 +01:00
name,
access,
2020-04-06 11:47:34 +02:00
params,
2020-03-09 14:57:07 +01:00
body,
pos,
})
}
2020-04-16 17:31:48 +02:00
pub fn parse_global_expr<'a>(
2020-03-22 14:03:58 +01:00
input: &mut Peekable<TokenIterator<'a>>,
2020-04-16 17:31:48 +02:00
engine: &Engine,
2020-03-22 14:03:58 +01:00
scope: &Scope,
2020-04-10 11:14:07 +02:00
optimization_level: OptimizationLevel,
max_expr_depth: usize,
) -> Result<AST, ParseError> {
let mut state = ParseState::new(max_expr_depth);
let expr = parse_expr(input, &mut state, 0, false)?;
2020-03-22 14:03:58 +01:00
match input.peek().unwrap() {
(Token::EOF, _) => (),
2020-03-22 14:03:58 +01:00
// Return error if the expression doesn't end
(token, pos) => {
return Err(PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(*pos))
}
2020-03-22 14:03:58 +01:00
}
Ok(
// Optimize AST
optimize_into_ast(
engine,
scope,
vec![Stmt::Expr(Box::new(expr))],
vec![],
2020-04-10 11:14:07 +02:00
optimization_level,
),
2020-03-22 14:03:58 +01:00
)
}
2020-03-18 03:36:50 +01:00
/// Parse the global level statements.
2020-03-22 14:03:58 +01:00
fn parse_global_level<'a>(
2020-03-09 14:57:07 +01:00
input: &mut Peekable<TokenIterator<'a>>,
max_expr_depth: (usize, usize),
) -> Result<(Vec<Stmt>, HashMap<u64, FnDef>), ParseError> {
let mut statements = Vec::<Stmt>::new();
2020-04-16 17:58:57 +02:00
let mut functions = HashMap::<u64, FnDef>::new();
let mut state = ParseState::new(max_expr_depth.0);
while !input.peek().unwrap().0.is_eof() {
2020-04-10 06:16:39 +02:00
// Collect all the function definitions
#[cfg(not(feature = "no_function"))]
{
let mut access = FnAccess::Public;
let mut must_be_fn = false;
if match_token(input, Token::Private)? {
access = FnAccess::Private;
must_be_fn = true;
}
match input.peek().unwrap() {
(Token::Fn, _) => {
let mut state = ParseState::new(max_expr_depth.1);
let func = parse_fn(input, &mut state, access, 0, true)?;
// Qualifiers (none) + function name + number of arguments.
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
functions.insert(hash, func);
continue;
}
(_, pos) if must_be_fn => {
return Err(PERR::MissingToken(
Token::Fn.into(),
format!("following '{}'", Token::Private.syntax()),
)
.into_err(*pos))
}
_ => (),
}
2016-02-29 22:43:45 +01:00
}
2020-03-18 11:41:18 +01:00
// Actual statement
let stmt = parse_stmt(input, &mut state, false, true, 0, true)?;
let need_semicolon = !stmt.is_self_terminated();
statements.push(stmt);
match input.peek().unwrap() {
2020-03-18 11:41:18 +01:00
// EOF
(Token::EOF, _) => break,
2020-03-18 11:41:18 +01:00
// stmt ;
(Token::SemiColon, _) if need_semicolon => {
eat_token(input, Token::SemiColon);
}
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>
(Token::LexError(err), pos) => {
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
}
// stmt ???
(_, pos) => {
// 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));
}
2016-02-29 22:43:45 +01:00
}
}
Ok((statements, functions))
}
2020-03-18 03:36:50 +01:00
/// Run the parser on an input stream, returning an AST.
2020-04-16 17:31:48 +02:00
pub fn parse<'a>(
input: &mut Peekable<TokenIterator<'a>>,
2020-04-16 17:31:48 +02:00
engine: &Engine,
scope: &Scope,
2020-04-10 06:16:39 +02:00
optimization_level: OptimizationLevel,
max_expr_depth: (usize, usize),
) -> Result<AST, ParseError> {
let (statements, functions) = parse_global_level(input, max_expr_depth)?;
let lib = functions.into_iter().map(|(_, v)| v).collect();
Ok(
2020-03-18 11:41:18 +01:00
// Optimize AST
optimize_into_ast(engine, 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"))]
Union::Float(value) => Some(Expr::FloatConstant(Box::new((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-05-25 07:44:28 +02:00
Union::Str(value) => Some(Expr::StringConstant(Box::new((value.clone(), 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
}