rhai/src/parser.rs

2491 lines
84 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::{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};
2020-05-08 08:50:48 +02:00
use crate::utils::EMPTY_TYPE_ID;
#[cfg(not(feature = "no_module"))]
use crate::module::ModuleRef;
#[cfg(feature = "no_module")]
#[derive(Debug, Clone, Copy)]
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, repeat, Peekable},
2020-04-29 10:11:54 +02:00
num::NonZeroUsize,
ops::{Add, Deref, DerefMut},
rc::Rc,
2020-03-17 19:26:11 +01:00
string::{String, ToString},
sync::Arc,
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-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>,
/// Script-defined functions, wrapped in an `Arc` for shared access.
#[cfg(feature = "sync")]
Arc<FunctionsLib>,
/// Script-defined functions, wrapped in an `Rc` for shared access.
#[cfg(not(feature = "sync"))]
Rc<FunctionsLib>,
);
2020-03-27 04:50:24 +01:00
impl AST {
/// Create a new `AST`.
2020-05-05 09:00:10 +02:00
pub fn new(statements: Vec<Stmt>, fn_lib: FunctionsLib) -> Self {
#[cfg(feature = "sync")]
return Self(statements, Arc::new(fn_lib));
2020-05-05 09:00:10 +02:00
#[cfg(not(feature = "sync"))]
return Self(statements, Rc::new(fn_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 fn_lib(&self) -> &FunctionsLib {
self.1.as_ref()
}
/// 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
Self::new(ast, functions.merge(other.1.as_ref()))
}
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) {
#[cfg(feature = "sync")]
{
self.1 = Arc::new(Default::default());
2020-03-27 04:50:24 +01:00
}
#[cfg(not(feature = "sync"))]
{
self.1 = Rc::new(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.
#[derive(Debug, Clone, Copy, PartialEq, Eq, 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.
2020-03-11 16:43:04 +01:00
pub params: Vec<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-03-11 16:43:04 +01:00
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
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-04-28 17:05:03 +02:00
/// A type that encapsulates a local stack with variable names to simulate an actual runtime scope.
2020-05-05 09:00:10 +02:00
#[derive(Debug, Clone, Default)]
2020-05-04 13:36:58 +02:00
struct Stack(Vec<(String, ScopeEntryType)>);
2020-04-28 17:05:03 +02:00
impl Stack {
2020-04-29 10:11:54 +02:00
/// Create a new `Stack`.
2020-04-28 17:05:03 +02:00
pub fn new() -> Self {
2020-05-05 09:00:10 +02:00
Default::default()
2020-04-28 17:05:03 +02:00
}
2020-04-29 10:11:54 +02:00
/// Find a variable by name in the `Stack`, searching in reverse.
/// The return value is the offset to be deducted from `Stack::len`,
/// i.e. the top element of the `Stack` is offset 1.
/// Return zero when the variable name is not found in the `Stack`.
pub fn find(&self, name: &str) -> Option<NonZeroUsize> {
2020-04-28 17:05:03 +02:00
self.0
.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 `Stack`, 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 `Stack` is offset 1.
/// Return zero when the variable name is not found in the `Stack`.
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
2020-05-04 13:36:58 +02:00
self.0
.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 Stack {
2020-05-04 13:36:58 +02:00
type Target = Vec<(String, ScopeEntryType)>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Stack {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
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<(Vec<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, ...
2020-05-09 18:19:13 +02:00
Export(Box<Vec<((String, Position), Option<(String, Position)>)>>),
}
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,
Stmt::Export(x) => (x.get(0).unwrap().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
}
#[cfg(not(feature = "no_module"))]
type MRef = Option<Box<ModuleRef>>;
#[cfg(feature = "no_module")]
type MRef = Option<ModuleRef>;
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.
StringConstant(Box<(String, Position)>),
2020-05-09 18:19:13 +02:00
/// Variable access - ((variable name, position), optional modules, hash, optional index)
Variable(Box<((String, Position), MRef, u64, Option<NonZeroUsize>)>),
2020-03-18 03:36:50 +01:00
/// Property access.
Property(Box<(String, Position)>),
2020-03-18 03:36:50 +01:00
/// { stmt }
Stmt(Box<(Stmt, Position)>),
2020-05-09 18:19:13 +02:00
/// func(expr, ... ) - ((function name, 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-09 18:19:13 +02:00
(Cow<'static, str>, Position),
MRef,
u64,
Vec<Expr>,
2020-05-09 18:19:13 +02:00
Option<Dynamic>,
)>,
2020-05-03 19:19:01 +02:00
),
2020-03-18 03:36:50 +01:00
/// expr = expr
Assignment(Box<(Expr, 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<(Vec<Expr>, Position)>),
2020-03-30 11:40:26 +02:00
/// #{ name:expr, ... }
2020-05-09 18:19:13 +02:00
Map(Box<(Vec<((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 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 {
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 {
#[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 {
#[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,
Self::FnCall(x) => (x.0).1,
Self::And(x) | Self::Or(x) | Self::In(x) => x.2,
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos,
Self::Assignment(x) | Self::Dot(x) | Self::Index(x) => x.0.position(),
}
}
2020-03-10 04:22:41 +01:00
/// Get the `Position` of the expression.
pub(crate) fn set_position(mut self, new_pos: Position) -> Self {
match &mut self {
#[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-09 18:19:13 +02:00
Self::FnCall(x) => (x.0).1 = 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,
Self::Assignment(x) => x.2 = 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 {
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 {
#[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 {
#[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;
Self::Property(Box::new((name.clone(), pos)))
}
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, Box<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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-05-04 13:36:58 +02:00
pos: Position,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
if match_token(input, Token::RightParen)? {
2020-05-04 13:36:58 +02:00
return Ok(Expr::Unit(pos));
}
2020-04-28 17:05:03 +02:00
let expr = parse_expr(input, stack, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
id: String,
2020-05-08 08:50:48 +02:00
#[cfg(not(feature = "no_module"))] mut modules: Option<Box<ModuleRef>>,
#[cfg(feature = "no_module")] modules: Option<ModuleRef>,
begin: Position,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2020-04-28 17:05:03 +02:00
let mut args = Vec::new();
2017-10-28 05:30:12 +02:00
match input.peek().unwrap() {
// id <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 <error>
(Token::LexError(err), pos) => 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(stack.find_module(&modules.get_ref(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,
2020-05-09 10:15:50 +02:00
// i.e. qualifiers + function name + no parameters.
2020-05-09 04:00:59 +02:00
// 2) Calculate a second hash with no qualifiers, empty function name, and
// the actual list of parameter `TypeId`'.s
// 3) The final hash is the XOR of the two hashes.
2020-05-09 18:19:13 +02:00
calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty())
2020-05-09 10:15:50 +02:00
} else {
calc_fn_hash(empty(), &id, 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-09 18:19:13 +02:00
let hash_fn_def = calc_fn_hash(empty(), &id, empty());
2020-05-09 10:15:50 +02:00
return Ok(Expr::FnCall(Box::new((
2020-05-09 18:19:13 +02:00
(id.into(), 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 {
2020-04-28 17:05:03 +02:00
args.push(parse_expr(input, stack, 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(stack.find_module(&modules.get_ref(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 + dummy parameter types (one for each parameter).
// 2) Calculate a second hash with no qualifiers, empty function name, and
// the actual list of parameter `TypeId`'.s
// 3) The final hash is the XOR of the two hashes.
2020-05-09 18:19:13 +02:00
calc_fn_hash(
modules.iter().map(|(m, _)| m.as_str()),
&id,
repeat(EMPTY_TYPE_ID()).take(args.len()),
)
2020-05-09 10:15:50 +02:00
} else {
calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()))
2020-05-08 08:50:48 +02:00
}
2020-05-09 10:15:50 +02:00
};
// Qualifiers (none) + function name + dummy parameter types (one for each parameter).
#[cfg(feature = "no_module")]
2020-05-09 18:19:13 +02:00
let hash_fn_def =
calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()));
2020-05-09 10:15:50 +02:00
return Ok(Expr::FnCall(Box::new((
2020-05-09 18:19:13 +02:00
(id.into(), 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
lhs: Expr,
pos: Position,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2020-04-28 17:05:03 +02:00
let idx_expr = parse_expr(input, stack, 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"))]
Expr::FloatConstant(x) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.1))
}
Expr::CharConstant(x) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.1))
}
Expr::Assignment(x) | Expr::And(x) | Expr::Or(x) | Expr::In(x) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.2))
}
Expr::True(pos) | Expr::False(pos) | Expr::Unit(pos) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(pos))
}
_ => (),
},
// 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"))]
Expr::FloatConstant(x) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.1))
}
Expr::CharConstant(x) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.1))
}
Expr::Assignment(x) | Expr::And(x) | Expr::Or(x) | Expr::In(x) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.2))
}
Expr::True(pos) | Expr::False(pos) | Expr::Unit(pos) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(pos))
}
_ => (),
},
2020-03-18 03:36:50 +01:00
// lhs[float]
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(x) => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a float".into(),
)
.into_err(x.1))
2020-03-09 03:41:17 +01:00
}
2020-03-18 03:36:50 +01:00
// lhs[char]
Expr::CharConstant(x) => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a character".into(),
)
.into_err(x.1))
}
// lhs[??? = ??? ]
Expr::Assignment(x) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not ()".into(),
)
.into_err(x.2))
2020-03-09 03:41:17 +01:00
}
// lhs[()]
Expr::Unit(pos) => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not ()".into(),
)
.into_err(*pos))
2020-03-09 03:41:17 +01:00
}
// lhs[??? && ???], lhs[??? || ???], lhs[??? in ???]
Expr::And(x) | Expr::Or(x) | Expr::In(x) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(),
)
.into_err(x.2))
}
// lhs[true], lhs[false]
Expr::True(pos) | Expr::False(pos) => {
2020-03-24 09:46:47 +01:00
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(),
)
.into_err(*pos))
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, _) => {
let follow_pos = eat_token(input, Token::LeftBracket);
2020-04-26 13:37:32 +02:00
// Recursively parse the indexing chain, right-binding each
2020-04-28 17:05:03 +02:00
let follow =
parse_index_chain(input, stack, idx_expr, follow_pos, allow_stmt_expr)?;
2020-04-26 13:37:32 +02:00
// Indexing binds to right
Ok(Expr::Index(Box::new((lhs, follow, pos))))
2020-04-26 12:04:07 +02:00
}
2020-04-26 13:37:32 +02:00
// Otherwise terminate the indexing chain
_ => 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-05-04 13:36:58 +02:00
pos: Position,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2016-03-26 18:46:28 +01:00
let mut arr = Vec::new();
if !match_token(input, Token::RightBracket)? {
while !input.peek().unwrap().0.is_eof() {
2020-04-28 17:05:03 +02:00
arr.push(parse_expr(input, stack, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-05-04 13:36:58 +02:00
pos: Position,
2020-03-29 17:53:35 +02:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2020-03-29 17:53:35 +02:00
let mut map = Vec::new();
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))
}
};
2020-04-28 17:05:03 +02:00
let expr = parse_expr(input, stack, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
let (token, pos) = match input.peek().unwrap() {
// { - block statement as expression
(Token::LeftBrace, pos) if allow_stmt_expr => {
2020-03-16 16:51:32 +01:00
let pos = *pos;
2020-04-28 17:05:03 +02:00
return parse_block(input, stack, false, allow_stmt_expr)
.map(|block| Expr::Stmt(Box::new((block, pos))));
2020-03-07 03:39:00 +01:00
}
(Token::EOF, pos) => 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))),
Token::StringConst(s) => Expr::StringConstant(Box::new((s, pos))),
2020-04-28 17:05:03 +02:00
Token::Identifier(s) => {
let index = stack.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, stack, pos, allow_stmt_expr)?,
2020-03-17 10:33:37 +01:00
#[cfg(not(feature = "no_index"))]
2020-04-28 17:05:03 +02:00
Token::LeftBracket => parse_array_literal(input, stack, pos, allow_stmt_expr)?,
2020-03-29 17:53:35 +02:00
#[cfg(not(feature = "no_object"))]
2020-04-28 17:05:03 +02:00
Token::MapStart => parse_map_literal(input, stack, pos, 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, stack, name, modules, pos, allow_stmt_expr)?
}
(Expr::Property(x), Token::LeftParen) => {
2020-05-09 18:19:13 +02:00
let (name, pos) = *x;
parse_call_expr(input, stack, name, None, pos, allow_stmt_expr)?
2020-05-04 11:43:54 +02:00
}
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) => {
2020-05-04 11:43:54 +02:00
parse_index_chain(input, stack, expr, token_pos, 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
2020-05-09 18:19:13 +02:00
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, empty());
modules.set_index(stack.find_module(&modules.get_ref(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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
match input.peek().unwrap() {
2020-03-27 16:47:23 +01:00
// If statement is allowed to act as expressions
(Token::If, pos) => {
2020-03-27 16:47:23 +01:00
let pos = *pos;
Ok(Expr::Stmt(Box::new((
parse_if(input, stack, false, allow_stmt_expr)?,
2020-03-27 16:47:23 +01:00
pos,
))))
2020-03-27 16:47:23 +01:00
}
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);
2020-04-28 17:05:03 +02:00
match parse_unary(input, stack, 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
2020-05-09 10:15:50 +02:00
e => {
let op = "-";
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
Ok(Expr::FnCall(Box::new((
2020-05-09 18:19:13 +02:00
(op.into(), pos),
2020-05-09 10:15:50 +02:00
None,
hash,
vec![e],
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);
2020-04-28 17:05:03 +02:00
parse_unary(input, stack, 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);
2020-05-09 18:19:13 +02:00
let expr = vec![parse_primary(input, stack, allow_stmt_expr)?];
2020-05-09 10:15:50 +02:00
let op = "!";
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
Ok(Expr::FnCall(Box::new((
2020-05-09 18:19:13 +02:00
(op.into(), pos),
2020-05-04 11:43:54 +02:00
None,
2020-05-09 10:15:50 +02:00
hash,
2020-05-09 18:19:13 +02:00
expr,
Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
))))
2019-09-18 12:21:07 +02:00
}
// <EOF>
(Token::EOF, pos) => Err(PERR::UnexpectedEOF.into_err(*pos)),
// All other tokens
2020-04-28 17:05:03 +02:00
_ => parse_primary(input, stack, allow_stmt_expr),
2017-10-30 16:08:44 +01:00
}
}
fn make_assignment_stmt<'a>(
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-04-22 11:37:06 +02:00
lhs: Expr,
rhs: Expr,
pos: Position,
2020-04-22 11:37:06 +02:00
) -> Result<Expr, Box<ParseError>> {
match &lhs {
Expr::Variable(x) if x.3.is_none() => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
Expr::Variable(x) => {
2020-05-09 18:19:13 +02:00
let ((name, name_pos), _, _, index) = x.as_ref();
match stack[(stack.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new((lhs, 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 {
Expr::Variable(x) if x.3.is_none() => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
Expr::Variable(x) => {
2020-05-09 18:19:13 +02:00
let ((name, name_pos), _, _, index) = x.as_ref();
match stack[(stack.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new((lhs, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-04-06 06:29:01 +02:00
lhs: Expr,
2020-04-22 11:37:06 +02:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2020-04-22 11:37:06 +02:00
let (op, pos) = match *input.peek().unwrap() {
(Token::Equals, _) => {
let pos = eat_token(input, Token::Equals);
let rhs = parse_expr(input, stack, allow_stmt_expr)?;
return make_assignment_stmt(stack, lhs, rhs, pos);
}
2020-04-22 11:37:06 +02:00
(Token::PlusAssign, pos) => ("+", pos),
(Token::MinusAssign, pos) => ("-", pos),
(Token::MultiplyAssign, pos) => ("*", pos),
(Token::DivideAssign, pos) => ("/", pos),
(Token::LeftShiftAssign, pos) => ("<<", pos),
(Token::RightShiftAssign, pos) => (">>", pos),
(Token::ModuloAssign, pos) => ("%", pos),
(Token::PowerOfAssign, pos) => ("~", pos),
(Token::AndAssign, pos) => ("&", pos),
(Token::OrAssign, pos) => ("|", pos),
(Token::XOrAssign, pos) => ("^", pos),
(_, _) => return Ok(lhs),
};
input.next();
2020-03-07 06:39:28 +01:00
let lhs_copy = lhs.clone();
2020-04-28 17:05:03 +02:00
let rhs = parse_expr(input, stack, allow_stmt_expr)?;
2020-03-07 06:39:28 +01:00
2020-03-18 11:41:18 +01:00
// lhs op= rhs -> lhs = op(lhs, rhs)
let args = vec![lhs_copy, rhs];
2020-05-09 10:15:50 +02:00
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(args.len()));
2020-05-09 18:19:13 +02:00
let rhs_expr = Expr::FnCall(Box::new(((op.into(), pos), None, hash, args, None)));
make_assignment_stmt(stack, lhs, rhs_expr, 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, Box<ParseError>> {
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 lhs = if is_index { lhs.into_property() } else { lhs };
2020-05-09 18:19:13 +02:00
let rhs = Expr::Property(Box::new(x.0));
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"))]
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get_ref(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, Box<ParseError>> {
2020-04-06 11:47:34 +02:00
match (&lhs, &rhs) {
(_, Expr::IntegerConstant(x)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(x.1))
}
(_, Expr::And(x)) | (_, Expr::Or(x)) | (_, Expr::In(x)) | (_, Expr::Assignment(x)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(x.2))
}
(_, Expr::True(pos)) | (_, Expr::False(pos)) | (_, Expr::Unit(pos)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(*pos))
}
#[cfg(not(feature = "no_float"))]
(_, Expr::FloatConstant(x)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(x.1))
}
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"))]
(Expr::FloatConstant(x), 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(),
)
.into_err(x.1))
2020-04-06 11:47:34 +02:00
}
// 123 in "xxxx"
(Expr::IntegerConstant(x), 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(),
)
.into_err(x.1))
2020-04-06 11:47:34 +02:00
}
// (??? && ???) in "xxxx", (??? || ???) in "xxxx", (??? in ???) in "xxxx",
(Expr::And(x), Expr::StringConstant(_))
| (Expr::Or(x), Expr::StringConstant(_))
| (Expr::In(x), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a boolean".into(),
)
.into_err(x.2))
}
2020-04-06 11:47:34 +02:00
// true in "xxxx", false in "xxxx"
(Expr::True(pos), Expr::StringConstant(_))
| (Expr::False(pos), 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(),
)
.into_err(*pos))
}
// [???, ???, ???] in "xxxx"
(Expr::Array(x), 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(),
)
.into_err(x.1))
2020-04-06 11:47:34 +02:00
}
// #{...} in "xxxx"
(Expr::Map(x), 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(),
)
.into_err(x.1))
}
// (??? = ???) in "xxxx"
(Expr::Assignment(x), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not ()".into(),
)
.into_err(x.2))
2020-04-06 11:47:34 +02:00
}
// () in "xxxx"
(Expr::Unit(pos), Expr::StringConstant(_)) => {
2020-04-06 11:47:34 +02:00
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not ()".into(),
)
.into_err(*pos))
}
// "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"))]
(Expr::FloatConstant(x), 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(),
)
.into_err(x.1))
2020-04-06 11:47:34 +02:00
}
// 123 in #{...}
(Expr::IntegerConstant(x), 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(),
)
.into_err(x.1))
2020-04-06 11:47:34 +02:00
}
// (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...},
(Expr::And(x), Expr::Map(_))
| (Expr::Or(x), Expr::Map(_))
| (Expr::In(x), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a boolean".into(),
)
.into_err(x.2))
}
2020-04-06 11:47:34 +02:00
// true in #{...}, false in #{...}
(Expr::True(pos), Expr::Map(_)) | (Expr::False(pos), 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(),
)
.into_err(*pos))
}
// [???, ???, ???] in #{..}
(Expr::Array(x), 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(),
)
.into_err(x.1))
2020-04-06 11:47:34 +02:00
}
// #{...} in #{..}
(Expr::Map(x), 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(),
)
.into_err(x.1))
}
// (??? = ???) in #{...}
(Expr::Assignment(x), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not ()".into(),
)
.into_err(x.2))
2020-04-06 11:47:34 +02:00
}
// () in #{...}
(Expr::Unit(pos), 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(),
)
.into_err(*pos))
}
_ => (),
}
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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-14 04:51:45 +01:00
parent_precedence: u8,
2019-09-18 12:21:07 +02:00
lhs: Expr,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
let mut current_lhs = lhs;
2016-02-29 22:43:45 +01:00
loop {
2020-04-11 12:09:03 +02:00
let (current_precedence, bind_right) = input.peek().map_or_else(
|| (0, false),
|(current_op, _)| (current_op.precedence(), 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 current_precedence < parent_precedence
|| (current_precedence == parent_precedence && !bind_right)
{
return Ok(current_lhs);
2016-02-29 22:43:45 +01:00
}
let (op_token, pos) = input.next().unwrap();
2020-04-28 17:05:03 +02:00
let rhs = parse_unary(input, stack, 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 (current_precedence == next_precedence && bind_right)
|| current_precedence < next_precedence
{
2020-04-28 17:05:03 +02:00
parse_binary_op(input, stack, current_precedence, rhs, allow_stmt_expr)?
} else {
// Otherwise bind to left (even if next operator has the same precedence)
rhs
};
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, repeat(EMPTY_TYPE_ID()).take(2));
let mut args = vec![current_lhs, rhs];
2020-04-22 11:37:06 +02:00
2020-04-28 17:05:03 +02:00
current_lhs = match op_token {
2020-05-09 18:19:13 +02:00
Token::Plus => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::Minus => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::Multiply => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::Divide => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
2020-04-28 17:05:03 +02:00
2020-05-09 18:19:13 +02:00
Token::LeftShift => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::RightShift => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::Modulo => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::PowerOf => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
2020-04-28 17:05:03 +02:00
// Comparison operators default to false when passed invalid operands
2020-05-09 18:19:13 +02:00
Token::EqualsTo => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
Token::NotEqualsTo => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
Token::LessThan => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
Token::LessThanEqualsTo => {
2020-05-09 18:19:13 +02:00
Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def)))
}
2020-05-09 18:19:13 +02:00
Token::GreaterThan => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
Token::GreaterThanEqualsTo => {
2020-05-09 18:19:13 +02:00
Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def)))
}
Token::Or => {
let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap();
Expr::Or(Box::new((current_lhs, rhs, pos)))
}
Token::And => {
let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap();
Expr::And(Box::new((current_lhs, rhs, pos)))
}
2020-05-09 18:19:13 +02:00
Token::Ampersand => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::Pipe => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::XOr => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
Token::In => {
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 => {
let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap();
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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2020-04-28 17:05:03 +02:00
let lhs = parse_unary(input, stack, allow_stmt_expr)?;
parse_binary_op(input, stack, 1, lhs, 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<(), Box<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<(), Box<ParseError>> {
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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-17 10:33:37 +01:00
breakable: bool,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// if ...
eat_token(input, Token::If);
2016-02-29 22:43:45 +01:00
// if guard { if_body }
ensure_not_statement_expr(input, "a boolean")?;
2020-04-28 17:05:03 +02:00
let guard = parse_expr(input, stack, allow_stmt_expr)?;
2020-04-22 11:37:06 +02:00
ensure_not_assignment(input)?;
2020-04-28 17:05:03 +02:00
let if_body = parse_block(input, stack, breakable, 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 ...
2020-04-28 17:05:03 +02:00
parse_if(input, stack, breakable, allow_stmt_expr)?
2020-03-16 16:51:32 +01:00
} else {
// if guard { if_body } else { else-body }
2020-04-28 17:05:03 +02:00
parse_block(input, stack, breakable, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// while ...
eat_token(input, Token::While);
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")?;
2020-04-28 17:05:03 +02:00
let guard = parse_expr(input, stack, allow_stmt_expr)?;
2020-04-22 11:37:06 +02:00
ensure_not_assignment(input)?;
2020-04-28 17:05:03 +02:00
let body = parse_block(input, stack, true, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// loop ...
eat_token(input, Token::Loop);
2017-10-30 16:08:44 +01:00
2020-03-18 11:41:18 +01:00
// loop { body }
2020-04-28 17:05:03 +02:00
let body = parse_block(input, stack, true, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// for ...
eat_token(input, Token::For);
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")?;
2020-04-28 17:05:03 +02:00
let expr = parse_expr(input, stack, allow_stmt_expr)?;
let prev_len = stack.len();
2020-05-04 13:36:58 +02:00
stack.push((name.clone(), ScopeEntryType::Normal));
2020-04-28 17:05:03 +02:00
let body = parse_block(input, stack, true, allow_stmt_expr)?;
stack.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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-25 04:27:18 +01:00
var_type: ScopeEntryType,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// let/const... (specified in `var_type`)
input.next();
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
2020-04-28 17:05:03 +02:00
let init_value = parse_expr(input, stack, 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 => {
2020-05-04 13:36:58 +02:00
stack.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() => {
2020-05-04 13:36:58 +02:00
stack.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 => {
stack.push((name.clone(), ScopeEntryType::Normal));
2020-05-09 18:19:13 +02:00
Ok(Stmt::Let(Box::new(((name, pos), None))))
}
ScopeEntryType::Constant => {
stack.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>>,
stack: &mut Stack,
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
// import ...
let pos = eat_token(input, Token::Import);
// import expr ...
let expr = parse_expr(input, stack, allow_stmt_expr)?;
// 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)),
};
2020-05-05 04:39:12 +02:00
stack.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>>) -> Result<Stmt, Box<ParseError>> {
eat_token(input, Token::Export);
let mut exports = Vec::new();
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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-17 10:33:37 +01:00
breakable: bool,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<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
let mut statements = Vec::new();
2020-04-28 17:05:03 +02:00
let prev_len = stack.len();
while !match_token(input, Token::RightBrace)? {
2020-03-17 10:33:37 +01:00
// Parse statements inside the block
2020-05-08 10:49:24 +02:00
let stmt = parse_stmt(input, stack, breakable, false, 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
stack.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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-04-28 17:05:03 +02:00
let expr = parse_expr(input, stack, allow_stmt_expr)?;
let expr = parse_op_assignment_stmt(input, stack, expr, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
2020-03-17 10:33:37 +01:00
breakable: bool,
2020-05-08 10:49:24 +02:00
is_global: bool,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
let (token, pos) = match input.peek().unwrap() {
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
x => x,
};
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
2020-04-28 17:05:03 +02:00
Token::LeftBrace => parse_block(input, stack, breakable, 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!(),
2020-04-28 17:05:03 +02:00
Token::If => parse_if(input, stack, breakable, allow_stmt_expr),
Token::While => parse_while(input, stack, allow_stmt_expr),
Token::Loop => parse_loop(input, stack, allow_stmt_expr),
Token::For => parse_for(input, stack, 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
(_, _) => {
2020-04-28 17:05:03 +02:00
let expr = parse_expr(input, stack, 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
2020-04-28 17:05:03 +02:00
Token::Let => parse_let(input, stack, ScopeEntryType::Normal, allow_stmt_expr),
Token::Const => parse_let(input, stack, ScopeEntryType::Constant, allow_stmt_expr),
2020-04-01 10:22:18 +02:00
#[cfg(not(feature = "no_module"))]
2020-05-04 13:36:58 +02:00
Token::Import => parse_import(input, stack, allow_stmt_expr),
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),
2020-04-28 17:05:03 +02:00
_ => parse_expr_stmt(input, stack, 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>>,
2020-04-28 17:05:03 +02:00
stack: &mut Stack,
access: FnAccess,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<FnDef, Box<ParseError>> {
2020-05-08 10:49:24 +02:00
let pos = eat_token(input, Token::Fn);
2020-03-16 16:51:32 +01:00
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) => {
2020-05-04 13:36:58 +02:00
stack.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() {
2020-04-28 17:05:03 +02:00
(Token::LeftBrace, _) => parse_block(input, stack, false, 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,
) -> Result<AST, Box<ParseError>> {
2020-04-28 17:05:03 +02:00
let mut stack = Stack::new();
let expr = parse_expr(input, &mut stack, 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>>,
) -> Result<(Vec<Stmt>, HashMap<u64, FnDef>), Box<ParseError>> {
let mut statements = Vec::<Stmt>::new();
2020-04-16 17:58:57 +02:00
let mut functions = HashMap::<u64, FnDef>::new();
2020-04-28 17:05:03 +02:00
let mut stack = Stack::new();
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 stack = Stack::new();
let func = parse_fn(input, &mut stack, access, true)?;
// Qualifiers (none) + function name + argument `TypeId`'s
let hash = calc_fn_hash(
empty(),
&func.name,
repeat(EMPTY_TYPE_ID()).take(func.params.len()),
);
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
2020-05-08 10:49:24 +02:00
let stmt = parse_stmt(input, &mut stack, false, true, 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,
) -> Result<AST, Box<ParseError>> {
let (statements, functions) = parse_global_level(input)?;
2020-04-16 17:58:57 +02:00
let fn_lib = functions.into_iter().map(|(_, v)| v).collect();
Ok(
2020-03-18 11:41:18 +01:00
// Optimize AST
2020-04-16 17:58:57 +02:00
optimize_into_ast(engine, scope, statements, fn_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)))),
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
}