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};
|
2020-05-07 18:19:08 +02:00
|
|
|
use crate::calc_fn_hash;
|
2020-05-05 06:24:13 +02:00
|
|
|
use crate::engine::{Engine, FunctionsLib};
|
2020-03-04 15:00:01 +01:00
|
|
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
2020-04-08 04:19:03 +02:00
|
|
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
2020-04-10 06:16:39 +02:00
|
|
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
2020-04-15 16:21:23 +02:00
|
|
|
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-14 13:06:10 +01:00
|
|
|
|
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,
|
2020-05-08 05:34:56 +02:00
|
|
|
iter::{empty, repeat, Peekable},
|
2020-04-29 10:11:54 +02:00
|
|
|
num::NonZeroUsize,
|
2020-05-03 10:54:24 +02:00
|
|
|
ops::{Add, Deref, DerefMut},
|
2020-04-04 16:00:44 +02:00
|
|
|
rc::Rc,
|
2020-03-17 19:26:11 +01:00
|
|
|
string::{String, ToString},
|
|
|
|
sync::Arc,
|
2020-04-15 16:21:23 +02:00
|
|
|
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.
|
2020-03-10 16:06:20 +01:00
|
|
|
#[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.
|
2020-03-10 16:06:20 +01:00
|
|
|
#[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.
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-10 16:06:20 +01:00
|
|
|
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)]
|
2020-04-04 16:00:44 +02:00
|
|
|
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-04-04 16:00:44 +02:00
|
|
|
);
|
2020-02-24 16:25:52 +01:00
|
|
|
|
2020-03-27 04:50:24 +01:00
|
|
|
impl AST {
|
2020-04-04 16:00:44 +02:00
|
|
|
/// 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")]
|
2020-05-06 10:09:44 +02:00
|
|
|
return Self(statements, Arc::new(fn_lib));
|
2020-05-05 09:00:10 +02:00
|
|
|
#[cfg(not(feature = "sync"))]
|
2020-05-06 10:09:44 +02:00
|
|
|
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()
|
2020-04-04 16:00:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 17:25:12 +02:00
|
|
|
/// # 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;
|
|
|
|
///
|
2020-04-07 07:23:06 +02:00
|
|
|
/// 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
|
|
|
///
|
2020-04-04 16:00:44 +02: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:
|
2020-04-04 16:00:44 +02:00
|
|
|
/// // let ast = &ast1 + &ast2;
|
2020-03-27 04:50:24 +01:00
|
|
|
///
|
|
|
|
/// // 'ast' is essentially:
|
|
|
|
/// //
|
|
|
|
/// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten
|
|
|
|
/// // foo(1) // <- notice this will be "hello1" instead of 43,
|
|
|
|
/// // // but it is no longer the return value
|
2020-04-01 04:21:49 +02:00
|
|
|
/// // foo("!") // returns "hello!"
|
2020-03-27 04:50:24 +01:00
|
|
|
///
|
|
|
|
/// // Evaluate it
|
2020-04-01 04:21:49 +02:00
|
|
|
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "hello!");
|
|
|
|
/// # }
|
2020-03-27 04:50:24 +01:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-04 16:00:44 +02:00
|
|
|
pub fn merge(&self, other: &Self) -> Self {
|
2020-04-05 11:44:48 +02:00
|
|
|
let Self(statements, functions) = self;
|
2020-04-04 16:00:44 +02:00
|
|
|
|
2020-04-05 11:44:48 +02:00
|
|
|
let ast = match (statements.is_empty(), other.0.is_empty()) {
|
2020-04-04 16:00:44 +02:00
|
|
|
(false, false) => {
|
2020-04-05 11:44:48 +02:00
|
|
|
let mut statements = statements.clone();
|
|
|
|
statements.extend(other.0.iter().cloned());
|
|
|
|
statements
|
2020-04-04 16:00:44 +02:00
|
|
|
}
|
2020-04-05 11:44:48 +02:00
|
|
|
(false, true) => statements.clone(),
|
2020-04-04 16:00:44 +02:00
|
|
|
(true, false) => other.0.clone(),
|
|
|
|
(true, true) => vec![],
|
|
|
|
};
|
2020-03-27 04:50:24 +01:00
|
|
|
|
2020-05-06 13:45:17 +02:00
|
|
|
Self::new(ast, functions.merge(other.1.as_ref()))
|
2020-04-04 16:00:44 +02:00
|
|
|
}
|
2020-03-27 04:50:24 +01:00
|
|
|
|
2020-04-04 16:00:44 +02:00
|
|
|
/// Clear all function definitions in the `AST`.
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-04-04 16:00:44 +02:00
|
|
|
pub fn clear_functions(&mut self) {
|
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
{
|
2020-05-07 06:25:09 +02:00
|
|
|
self.1 = Arc::new(Default::default());
|
2020-03-27 04:50:24 +01:00
|
|
|
}
|
2020-04-04 16:00:44 +02:00
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
{
|
2020-05-07 06:25:09 +02:00
|
|
|
self.1 = Rc::new(Default::default());
|
2020-04-04 16:00:44 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-27 04:50:24 +01:00
|
|
|
|
2020-04-04 16:00:44 +02:00
|
|
|
/// Clear all statements in the `AST`, leaving only function definitions.
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-04-04 16:00:44 +02:00
|
|
|
pub fn retain_functions(&mut self) {
|
|
|
|
self.0 = vec![];
|
2020-03-27 04:50:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-04 16:00:44 +02: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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-09 05:29:30 +02:00
|
|
|
/// 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.
|
2020-03-15 15:39:58 +01:00
|
|
|
#[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,
|
2020-05-09 05:29:30 +02:00
|
|
|
/// 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.
|
2020-04-29 10:11:54 +02:00
|
|
|
pub body: Box<Stmt>,
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Position of the function definition.
|
2020-03-02 15:13:14 +01:00
|
|
|
pub pos: Position,
|
2016-03-01 21:16:10 +01:00
|
|
|
}
|
|
|
|
|
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))
|
|
|
|
}
|
2020-05-06 16:54:34 +02:00
|
|
|
/// 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`.
|
2020-05-06 16:54:34 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-03 10:54:24 +02:00
|
|
|
impl Deref for Stack {
|
2020-05-04 13:36:58 +02:00
|
|
|
type Target = Vec<(String, ScopeEntryType)>;
|
2020-05-03 10:54:24 +02:00
|
|
|
|
|
|
|
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-03-02 07:28:42 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2016-04-17 04:32:18 +02:00
|
|
|
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 }
|
2020-03-22 03:18:16 +01:00
|
|
|
IfThenElse(Box<Expr>, Box<Stmt>, Option<Box<Stmt>>),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// while expr { stmt }
|
2016-04-17 04:32:18 +02:00
|
|
|
While(Box<Expr>, Box<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 }
|
2020-05-01 11:32:39 +02:00
|
|
|
For(Box<String>, Box<Expr>, Box<Stmt>),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// let id = expr
|
2020-05-01 11:32:39 +02:00
|
|
|
Let(Box<String>, Option<Box<Expr>>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// const id = expr
|
2020-05-01 11:32:39 +02:00
|
|
|
Const(Box<String>, Box<Expr>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// { stmt; ... }
|
2020-03-09 14:57:07 +01:00
|
|
|
Block(Vec<Stmt>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// { stmt }
|
2016-04-17 04:32:18 +02:00
|
|
|
Expr(Box<Expr>),
|
2020-04-01 10:22:18 +02:00
|
|
|
/// continue
|
|
|
|
Continue(Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// break
|
2020-03-02 15:13:14 +01:00
|
|
|
Break(Position),
|
2020-05-04 11:43:54 +02:00
|
|
|
/// return/throw
|
2020-03-11 16:43:04 +01:00
|
|
|
ReturnWithVal(Option<Box<Expr>>, ReturnType, Position),
|
2020-05-04 13:36:58 +02:00
|
|
|
/// import expr as module
|
|
|
|
Import(Box<Expr>, Box<String>, Position),
|
2020-05-08 10:49:24 +02:00
|
|
|
/// expr id as name, ...
|
|
|
|
Export(Vec<(String, Position, Option<(String, Position)>)>),
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
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::Let(_, _, pos)
|
|
|
|
| Stmt::Const(_, _, pos)
|
2020-05-04 13:36:58 +02:00
|
|
|
| Stmt::Import(_, _, pos)
|
2020-03-17 10:33:37 +01:00
|
|
|
| Stmt::Block(_, pos)
|
2020-04-01 10:22:18 +02:00
|
|
|
| Stmt::Continue(pos)
|
2020-03-17 10:33:37 +01:00
|
|
|
| Stmt::Break(pos)
|
|
|
|
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
2020-05-03 19:19:01 +02:00
|
|
|
|
2020-05-04 13:36:58 +02:00
|
|
|
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
|
2020-05-03 19:19:01 +02:00
|
|
|
|
2020-03-17 10:33:37 +01:00
|
|
|
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
|
2020-05-08 10:49:24 +02:00
|
|
|
|
|
|
|
Stmt::Export(list) => list.get(0).unwrap().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)?
|
2020-03-17 07:29:22 +01:00
|
|
|
pub fn is_self_terminated(&self) -> bool {
|
|
|
|
match self {
|
2020-03-22 03:18:16 +01:00
|
|
|
Stmt::IfThenElse(_, _, _)
|
2020-03-17 07:29:22 +01:00
|
|
|
| Stmt::While(_, _)
|
|
|
|
| Stmt::Loop(_)
|
|
|
|
| Stmt::For(_, _, _)
|
2020-05-04 13:36:58 +02:00
|
|
|
| Stmt::Block(_, _) => true,
|
2020-03-17 07:29:22 +01:00
|
|
|
|
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,
|
|
|
|
|
2020-03-17 07:29:22 +01:00
|
|
|
Stmt::Let(_, _, _)
|
|
|
|
| Stmt::Const(_, _, _)
|
2020-05-04 13:36:58 +02:00
|
|
|
| Stmt::Import(_, _, _)
|
2020-05-08 10:49:24 +02:00
|
|
|
| Stmt::Export(_)
|
2020-03-17 07:29:22 +01:00
|
|
|
| Stmt::Expr(_)
|
2020-04-01 10:22:18 +02:00
|
|
|
| Stmt::Continue(_)
|
2020-03-17 07:29:22 +01:00
|
|
|
| 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(),
|
2020-03-22 03:18:16 +01:00
|
|
|
Stmt::IfThenElse(guard, if_block, Some(else_block)) => {
|
2020-03-17 10:33:37 +01:00
|
|
|
guard.is_pure() && if_block.is_pure() && else_block.is_pure()
|
|
|
|
}
|
2020-03-22 03:18:16 +01:00
|
|
|
Stmt::IfThenElse(guard, block, None) | Stmt::While(guard, block) => {
|
2020-03-17 10:33:37 +01:00
|
|
|
guard.is_pure() && block.is_pure()
|
|
|
|
}
|
|
|
|
Stmt::Loop(block) => block.is_pure(),
|
|
|
|
Stmt::For(_, range, block) => range.is_pure() && block.is_pure(),
|
|
|
|
Stmt::Let(_, _, _) | Stmt::Const(_, _, _) => false,
|
|
|
|
Stmt::Block(statements, _) => statements.iter().all(Stmt::is_pure),
|
2020-04-01 10:22:18 +02:00
|
|
|
Stmt::Continue(_) | Stmt::Break(_) | Stmt::ReturnWithVal(_, _, _) => false,
|
2020-05-04 13:36:58 +02:00
|
|
|
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-03-02 07:28:42 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2016-04-17 04:32:18 +02:00
|
|
|
pub enum Expr {
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Integer constant.
|
2020-03-10 16:06:20 +01:00
|
|
|
IntegerConstant(INT, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Floating-point constant.
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-10 16:06:20 +01:00
|
|
|
FloatConstant(FLOAT, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Character constant.
|
2020-03-02 15:13:14 +01:00
|
|
|
CharConstant(char, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// String constant.
|
2020-04-28 13:39:36 +02:00
|
|
|
StringConstant(String, Position),
|
2020-05-07 18:19:08 +02:00
|
|
|
/// Variable access - (variable name, optional modules, hash, optional index, position)
|
|
|
|
Variable(
|
|
|
|
Box<String>,
|
2020-05-08 08:50:48 +02:00
|
|
|
#[cfg(not(feature = "no_module"))] Option<Box<ModuleRef>>,
|
|
|
|
#[cfg(feature = "no_module")] Option<ModuleRef>,
|
2020-05-09 10:15:50 +02:00
|
|
|
u64,
|
2020-05-07 18:19:08 +02:00
|
|
|
Option<NonZeroUsize>,
|
|
|
|
Position,
|
|
|
|
),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Property access.
|
2020-04-28 13:39:36 +02:00
|
|
|
Property(String, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// { stmt }
|
2020-03-09 14:57:07 +01:00
|
|
|
Stmt(Box<Stmt>, Position),
|
2020-05-09 10:15:50 +02:00
|
|
|
/// func(expr, ... ) - (function name, optional modules, hash, arguments, optional default value, position)
|
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`.
|
2020-05-01 11:32:39 +02:00
|
|
|
FnCall(
|
|
|
|
Box<Cow<'static, str>>,
|
2020-05-08 08:50:48 +02:00
|
|
|
#[cfg(not(feature = "no_module"))] Option<Box<ModuleRef>>,
|
|
|
|
#[cfg(feature = "no_module")] Option<ModuleRef>,
|
2020-05-09 10:15:50 +02:00
|
|
|
u64,
|
2020-05-03 19:19:01 +02:00
|
|
|
Box<Vec<Expr>>,
|
|
|
|
Option<Box<Dynamic>>,
|
|
|
|
Position,
|
|
|
|
),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// expr = expr
|
2020-03-07 03:16:20 +01:00
|
|
|
Assignment(Box<Expr>, Box<Expr>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// lhs.rhs
|
2020-03-07 03:16:20 +01:00
|
|
|
Dot(Box<Expr>, Box<Expr>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// expr[expr]
|
2020-03-07 03:16:20 +01:00
|
|
|
Index(Box<Expr>, Box<Expr>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// [ expr, ... ]
|
2020-03-02 15:13:14 +01:00
|
|
|
Array(Vec<Expr>, Position),
|
2020-03-30 11:40:26 +02:00
|
|
|
/// #{ name:expr, ... }
|
2020-03-29 17:53:35 +02:00
|
|
|
Map(Vec<(String, Expr, Position)>, Position),
|
2020-04-06 11:47:34 +02:00
|
|
|
/// lhs in rhs
|
|
|
|
In(Box<Expr>, Box<Expr>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// lhs && rhs
|
2020-04-06 11:47:34 +02:00
|
|
|
And(Box<Expr>, Box<Expr>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// lhs || rhs
|
2020-04-06 11:47:34 +02:00
|
|
|
Or(Box<Expr>, Box<Expr>, Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// true
|
2020-03-02 15:13:14 +01:00
|
|
|
True(Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// false
|
2020-03-02 15:13:14 +01:00
|
|
|
False(Position),
|
2020-03-18 03:36:50 +01:00
|
|
|
/// ()
|
2020-03-02 15:13:14 +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.
|
2020-03-15 15:39:58 +01:00
|
|
|
pub fn get_constant_value(&self) -> Dynamic {
|
|
|
|
match self {
|
2020-04-22 06:12:13 +02:00
|
|
|
Self::IntegerConstant(i, _) => (*i).into(),
|
2020-04-12 17:00:06 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-04-22 06:12:13 +02:00
|
|
|
Self::FloatConstant(f, _) => (*f).into(),
|
|
|
|
Self::CharConstant(c, _) => (*c).into(),
|
2020-04-28 13:39:36 +02:00
|
|
|
Self::StringConstant(s, _) => s.clone().into(),
|
2020-04-22 06:12:13 +02:00
|
|
|
Self::True(_) => true.into(),
|
|
|
|
Self::False(_) => false.into(),
|
|
|
|
Self::Unit(_) => ().into(),
|
2020-04-12 17:00:06 +02:00
|
|
|
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-04-18 05:10:03 +02:00
|
|
|
Self::Array(items, _) if items.iter().all(Self::is_constant) => {
|
|
|
|
Dynamic(Union::Array(Box::new(
|
|
|
|
items
|
|
|
|
.iter()
|
|
|
|
.map(Self::get_constant_value)
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
)))
|
|
|
|
}
|
2020-04-12 17:00:06 +02:00
|
|
|
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-04-12 17:00:06 +02:00
|
|
|
Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => {
|
|
|
|
Dynamic(Union::Map(Box::new(
|
|
|
|
items
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v, _)| (k.clone(), v.get_constant_value()))
|
|
|
|
.collect::<HashMap<_, _>>(),
|
|
|
|
)))
|
|
|
|
}
|
2020-03-29 17:53:35 +02:00
|
|
|
|
2020-03-15 15:39:58 +01: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.
|
2020-03-15 15:39:58 +01:00
|
|
|
pub fn get_constant_str(&self) -> String {
|
2020-03-13 11:12:41 +01:00
|
|
|
match self {
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-04-10 06:16:39 +02:00
|
|
|
Self::FloatConstant(f, _) => f.to_string(),
|
2020-05-06 13:45:17 +02:00
|
|
|
|
|
|
|
Self::IntegerConstant(i, _) => i.to_string(),
|
2020-04-06 11:47:34 +02:00
|
|
|
Self::CharConstant(c, _) => c.to_string(),
|
|
|
|
Self::StringConstant(_, _) => "string".to_string(),
|
|
|
|
Self::True(_) => "true".to_string(),
|
|
|
|
Self::False(_) => "false".to_string(),
|
|
|
|
Self::Unit(_) => "()".to_string(),
|
2020-03-13 11:12:41 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
Self::Array(items, _) if items.iter().all(Self::is_constant) => "array".to_string(),
|
2020-03-15 15:39:58 +01:00
|
|
|
|
|
|
|
_ => 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.
|
2020-03-02 15:13:14 +01:00
|
|
|
pub fn position(&self) -> Position {
|
|
|
|
match self {
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Self::FloatConstant(_, pos) => *pos,
|
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
Self::IntegerConstant(_, pos)
|
|
|
|
| Self::CharConstant(_, pos)
|
|
|
|
| Self::StringConstant(_, pos)
|
2020-04-10 06:16:39 +02:00
|
|
|
| Self::Array(_, pos)
|
|
|
|
| Self::Map(_, pos)
|
2020-05-09 10:15:50 +02:00
|
|
|
| Self::Variable(_, _, _, _, pos)
|
2020-04-06 11:47:34 +02:00
|
|
|
| Self::Property(_, pos)
|
|
|
|
| Self::Stmt(_, pos)
|
2020-05-09 10:15:50 +02:00
|
|
|
| Self::FnCall(_, _, _, _, _, pos)
|
2020-04-06 11:47:34 +02:00
|
|
|
| Self::And(_, _, pos)
|
|
|
|
| Self::Or(_, _, pos)
|
|
|
|
| Self::In(_, _, pos)
|
|
|
|
| Self::True(pos)
|
|
|
|
| Self::False(pos)
|
|
|
|
| Self::Unit(pos) => *pos,
|
|
|
|
|
2020-04-10 06:16:39 +02:00
|
|
|
Self::Assignment(expr, _, _) | Self::Dot(expr, _, _) | Self::Index(expr, _, _) => {
|
|
|
|
expr.position()
|
|
|
|
}
|
2020-03-02 15:13:14 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-10 04:22:41 +01:00
|
|
|
|
2020-04-10 15:02:13 +02:00
|
|
|
/// Get the `Position` of the expression.
|
|
|
|
pub(crate) fn set_position(mut self, new_pos: Position) -> Self {
|
|
|
|
match &mut self {
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Self::FloatConstant(_, pos) => *pos = new_pos,
|
|
|
|
|
2020-04-10 15:02:13 +02:00
|
|
|
Self::IntegerConstant(_, pos)
|
|
|
|
| Self::CharConstant(_, pos)
|
|
|
|
| Self::StringConstant(_, pos)
|
|
|
|
| Self::Array(_, pos)
|
|
|
|
| Self::Map(_, pos)
|
2020-05-09 10:15:50 +02:00
|
|
|
| Self::Variable(_, _, _, _, pos)
|
2020-04-10 15:02:13 +02:00
|
|
|
| Self::Property(_, pos)
|
|
|
|
| Self::Stmt(_, pos)
|
2020-05-09 10:15:50 +02:00
|
|
|
| Self::FnCall(_, _, _, _, _, pos)
|
2020-04-10 15:02:13 +02:00
|
|
|
| Self::And(_, _, pos)
|
|
|
|
| Self::Or(_, _, pos)
|
|
|
|
| Self::In(_, _, pos)
|
|
|
|
| Self::True(pos)
|
|
|
|
| Self::False(pos)
|
|
|
|
| Self::Unit(pos)
|
|
|
|
| Self::Assignment(_, _, pos)
|
|
|
|
| Self::Dot(_, _, pos)
|
|
|
|
| Self::Index(_, _, pos) => *pos = 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-04-06 11:47:34 +02:00
|
|
|
Self::Array(expressions, _) => expressions.iter().all(Self::is_pure),
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-04-10 06:16:39 +02:00
|
|
|
Self::Index(x, y, _) | Self::And(x, y, _) | Self::Or(x, y, _) | Self::In(x, y, _) => {
|
2020-04-06 11:47:34 +02:00
|
|
|
x.is_pure() && y.is_pure()
|
|
|
|
}
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
Self::Stmt(stmt, _) => stmt.is_pure(),
|
2020-03-17 10:33:37 +01:00
|
|
|
|
2020-05-09 10:15:50 +02:00
|
|
|
Self::Variable(_, _, _, _, _) => true,
|
2020-04-15 15:41:28 +02:00
|
|
|
|
|
|
|
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-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Self::FloatConstant(_, _) => true,
|
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
Self::IntegerConstant(_, _)
|
|
|
|
| Self::CharConstant(_, _)
|
|
|
|
| Self::StringConstant(_, _)
|
|
|
|
| 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
|
2020-04-06 11:47:34 +02:00
|
|
|
Self::Array(expressions, _) => expressions.iter().all(Self::is_constant),
|
|
|
|
|
|
|
|
// An map literal is constant if all items are constant
|
|
|
|
Self::Map(items, _) => items.iter().map(|(_, expr, _)| expr).all(Self::is_constant),
|
|
|
|
|
|
|
|
// Check in expression
|
|
|
|
Self::In(lhs, rhs, _) => match (lhs.as_ref(), rhs.as_ref()) {
|
|
|
|
(Self::StringConstant(_, _), Self::StringConstant(_, _))
|
|
|
|
| (Self::CharConstant(_, _), Self::StringConstant(_, _)) => true,
|
|
|
|
_ => false,
|
|
|
|
},
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-03-10 11:32:01 +01:00
|
|
|
_ => 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-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Self::FloatConstant(_, _) => false,
|
|
|
|
|
2020-04-26 12:04:07 +02:00
|
|
|
Self::IntegerConstant(_, _)
|
|
|
|
| Self::CharConstant(_, _)
|
|
|
|
| Self::In(_, _, _)
|
|
|
|
| Self::And(_, _, _)
|
|
|
|
| Self::Or(_, _, _)
|
|
|
|
| Self::True(_)
|
|
|
|
| Self::False(_)
|
|
|
|
| Self::Unit(_) => false,
|
|
|
|
|
|
|
|
Self::StringConstant(_, _)
|
|
|
|
| Self::Stmt(_, _)
|
2020-05-09 10:15:50 +02:00
|
|
|
| Self::FnCall(_, _, _, _, _, _)
|
2020-04-26 12:04:07 +02:00
|
|
|
| Self::Assignment(_, _, _)
|
|
|
|
| Self::Dot(_, _, _)
|
|
|
|
| Self::Index(_, _, _)
|
|
|
|
| Self::Array(_, _)
|
|
|
|
| Self::Map(_, _) => match token {
|
2020-04-17 13:00:52 +02:00
|
|
|
Token::LeftBracket => true,
|
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
|
2020-05-09 10:15:50 +02:00
|
|
|
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 10:15:50 +02:00
|
|
|
Self::Variable(id, None, _, _, pos) => Self::Property(*id, pos),
|
2020-04-26 12:04:07 +02:00
|
|
|
_ => self,
|
|
|
|
}
|
|
|
|
}
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-04-15 15:41:28 +02: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 {
|
2020-04-23 07:24:24 +02:00
|
|
|
let (t, pos) = input.next().unwrap();
|
|
|
|
|
|
|
|
if t != token {
|
|
|
|
panic!(
|
|
|
|
"expecting {} (found {}) at {}",
|
|
|
|
token.syntax(),
|
|
|
|
t.syntax(),
|
|
|
|
pos
|
|
|
|
);
|
2020-04-15 15:41:28 +02:00
|
|
|
}
|
2020-04-23 07:24:24 +02:00
|
|
|
pos
|
2020-04-15 15:41:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Match a particular token, consuming it if matched.
|
2020-04-17 18:14:33 +02:00
|
|
|
fn match_token(input: &mut Peekable<TokenIterator>, token: Token) -> Result<bool, Box<ParseError>> {
|
2020-04-17 14:01:41 +02:00
|
|
|
let (t, _) = input.peek().unwrap();
|
|
|
|
if *t == token {
|
|
|
|
eat_token(input, token);
|
|
|
|
Ok(true)
|
2020-04-15 15:41:28 +02:00
|
|
|
} else {
|
2020-04-17 14:01:41 +02:00
|
|
|
Ok(false)
|
2020-04-15 15:41:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Parse ( expr )
|
2020-03-02 15:13:14 +01:00
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Expr, Box<ParseError>> {
|
2020-04-15 15:41:28 +02:00
|
|
|
if match_token(input, Token::RightParen)? {
|
2020-05-04 13:36:58 +02:00
|
|
|
return Ok(Expr::Unit(pos));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.next().unwrap() {
|
2020-03-18 03:36:50 +01:00
|
|
|
// ( xxx )
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::RightParen, _) => Ok(expr),
|
2020-04-20 18:11:25 +02:00
|
|
|
// ( <error>
|
|
|
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
2020-03-18 03:36:50 +01:00
|
|
|
// ( xxx ???
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => Err(PERR::MissingToken(
|
2020-05-04 13:36:58 +02:00
|
|
|
Token::RightParen.into(),
|
2020-04-17 14:01:41 +02:00
|
|
|
"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>,
|
2020-03-02 15:13:14 +01:00
|
|
|
begin: Position,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> 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
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
2020-04-20 18:11:25 +02:00
|
|
|
// id <EOF>
|
2020-04-17 14:01:41 +02:00
|
|
|
(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),
|
|
|
|
)
|
2020-04-17 14:01:41 +02:00
|
|
|
.into_err(*pos))
|
2020-04-06 06:29:01 +02:00
|
|
|
}
|
2020-04-20 18:11:25 +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()
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::RightParen, _) => {
|
2020-04-15 15:41:28 +02:00
|
|
|
eat_token(input, Token::RightParen);
|
2020-05-08 05:34:56 +02:00
|
|
|
|
2020-05-08 08:50:48 +02:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2020-05-09 10:15:50 +02:00
|
|
|
let hash1 = {
|
2020-05-08 08:50:48 +02:00
|
|
|
if let Some(modules) = modules.as_mut() {
|
2020-05-09 10:15:50 +02:00
|
|
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
|
|
|
|
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
|
|
|
let hash1 = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
|
|
|
|
// 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 10:15:50 +02:00
|
|
|
hash1
|
|
|
|
} 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")]
|
|
|
|
let hash1 = calc_fn_hash(empty(), &id, empty());
|
|
|
|
|
2020-05-01 11:32:39 +02:00
|
|
|
return Ok(Expr::FnCall(
|
|
|
|
Box::new(id.into()),
|
2020-05-04 13:36:58 +02:00
|
|
|
modules,
|
2020-05-09 10:15:50 +02:00
|
|
|
hash1,
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(args),
|
|
|
|
None,
|
|
|
|
begin,
|
|
|
|
));
|
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
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
2020-05-08 05:34:56 +02:00
|
|
|
// id(...args)
|
2020-04-20 18:11:25 +02:00
|
|
|
(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 10:15:50 +02:00
|
|
|
let hash1 = {
|
2020-05-08 08:50:48 +02:00
|
|
|
if let Some(modules) = modules.as_mut() {
|
2020-05-09 10:15:50 +02:00
|
|
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
|
|
|
|
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).
|
|
|
|
let hash1 = calc_fn_hash(
|
2020-05-08 08:50:48 +02:00
|
|
|
modules.iter().map(|(m, _)| m.as_str()),
|
|
|
|
&id,
|
|
|
|
repeat(EMPTY_TYPE_ID()).take(args.len()),
|
|
|
|
);
|
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 10:15:50 +02:00
|
|
|
hash1
|
|
|
|
} 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")]
|
|
|
|
let hash1 = calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()));
|
|
|
|
|
2020-05-01 11:32:39 +02:00
|
|
|
return Ok(Expr::FnCall(
|
|
|
|
Box::new(id.into()),
|
2020-05-04 13:36:58 +02:00
|
|
|
modules,
|
2020-05-09 10:15:50 +02:00
|
|
|
hash1,
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(args),
|
|
|
|
None,
|
|
|
|
begin,
|
|
|
|
));
|
2020-04-20 18:11:25 +02:00
|
|
|
}
|
2020-05-08 05:34:56 +02:00
|
|
|
// id(...args,
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::Comma, _) => {
|
|
|
|
eat_token(input, Token::Comma);
|
|
|
|
}
|
2020-05-08 05:34:56 +02:00
|
|
|
// id(...args <EOF>
|
2020-04-17 14:01:41 +02:00
|
|
|
(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),
|
|
|
|
)
|
2020-04-17 14:01:41 +02:00
|
|
|
.into_err(*pos))
|
2020-04-06 06:29:01 +02:00
|
|
|
}
|
2020-05-08 05:34:56 +02:00
|
|
|
// id(...args <error>
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::LexError(err), pos) => {
|
|
|
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
2020-04-17 13:00:52 +02:00
|
|
|
}
|
2020-05-08 05:34:56 +02:00
|
|
|
// id(...args ???
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => {
|
2020-03-29 07:44:27 +02:00
|
|
|
return Err(PERR::MissingToken(
|
2020-05-04 13:36:58 +02:00
|
|
|
Token::Comma.into(),
|
2020-03-29 07:44:27 +02:00
|
|
|
format!("to separate the arguments to function call '{}'", id),
|
|
|
|
)
|
2020-03-24 09:46:47 +01:00
|
|
|
.into_err(*pos))
|
2020-03-05 18:05:02 +01:00
|
|
|
}
|
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,
|
2020-03-07 03:16:20 +01:00
|
|
|
pos: Position,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> 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]
|
2020-03-09 03:41:17 +01:00
|
|
|
Expr::IntegerConstant(i, pos) if *i < 0 => {
|
2020-03-24 09:46:47 +01:00
|
|
|
return Err(PERR::MalformedIndexExpr(format!(
|
|
|
|
"Array access expects non-negative index: {} < 0",
|
|
|
|
i
|
2020-03-09 03:41:17 +01:00
|
|
|
))
|
2020-03-24 09:46:47 +01:00
|
|
|
.into_err(*pos))
|
2020-03-09 03:41:17 +01:00
|
|
|
}
|
2020-04-26 13:37:32 +02:00
|
|
|
Expr::IntegerConstant(_, pos) => match lhs {
|
2020-03-29 17:53:35 +02:00
|
|
|
Expr::Array(_, _) | Expr::StringConstant(_, _) => (),
|
|
|
|
|
|
|
|
Expr::Map(_, _) => {
|
|
|
|
return Err(PERR::MalformedIndexExpr(
|
|
|
|
"Object map access expects string index, not a number".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Expr::FloatConstant(_, pos) => {
|
|
|
|
return Err(PERR::MalformedIndexExpr(
|
|
|
|
"Only arrays, object maps and strings can be indexed".into(),
|
|
|
|
)
|
|
|
|
.into_err(pos))
|
|
|
|
}
|
|
|
|
|
|
|
|
Expr::CharConstant(_, pos)
|
2020-03-29 17:53:35 +02:00
|
|
|
| Expr::Assignment(_, _, pos)
|
2020-04-06 11:47:34 +02:00
|
|
|
| Expr::And(_, _, pos)
|
|
|
|
| Expr::Or(_, _, pos)
|
|
|
|
| Expr::In(_, _, pos)
|
2020-03-29 17:53:35 +02:00
|
|
|
| Expr::True(pos)
|
2020-04-06 11:47:34 +02:00
|
|
|
| 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]
|
2020-04-26 13:37:32 +02:00
|
|
|
Expr::StringConstant(_, pos) => match lhs {
|
2020-03-29 17:53:35 +02:00
|
|
|
Expr::Map(_, _) => (),
|
|
|
|
|
|
|
|
Expr::Array(_, _) | Expr::StringConstant(_, _) => {
|
|
|
|
return Err(PERR::MalformedIndexExpr(
|
|
|
|
"Array or string expects numeric index, not a string".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
2020-05-06 13:45:17 +02:00
|
|
|
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Expr::FloatConstant(_, pos) => {
|
|
|
|
return Err(PERR::MalformedIndexExpr(
|
|
|
|
"Only arrays, object maps and strings can be indexed".into(),
|
|
|
|
)
|
|
|
|
.into_err(pos))
|
|
|
|
}
|
|
|
|
|
|
|
|
Expr::CharConstant(_, pos)
|
2020-03-29 17:53:35 +02:00
|
|
|
| Expr::Assignment(_, _, pos)
|
2020-04-06 11:47:34 +02:00
|
|
|
| Expr::And(_, _, pos)
|
|
|
|
| Expr::Or(_, _, pos)
|
|
|
|
| Expr::In(_, _, pos)
|
2020-03-29 17:53:35 +02:00
|
|
|
| Expr::True(pos)
|
2020-04-06 11:47:34 +02:00
|
|
|
| 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]
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-09 03:41:17 +01:00
|
|
|
Expr::FloatConstant(_, pos) => {
|
2020-03-24 09:46:47 +01:00
|
|
|
return Err(PERR::MalformedIndexExpr(
|
|
|
|
"Array access expects integer index, not a float".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
2020-03-09 03:41:17 +01:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// lhs[char]
|
2020-03-09 03:41:17 +01:00
|
|
|
Expr::CharConstant(_, pos) => {
|
2020-03-24 09:46:47 +01:00
|
|
|
return Err(PERR::MalformedIndexExpr(
|
|
|
|
"Array access expects integer index, not a character".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
2020-03-09 03:41:17 +01:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// lhs[??? = ??? ], lhs[()]
|
2020-03-09 03:41:17 +01:00
|
|
|
Expr::Assignment(_, _, pos) | 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
|
|
|
}
|
2020-04-06 11:47:34 +02:00
|
|
|
// lhs[??? && ???], lhs[??? || ???], lhs[??? in ???], lhs[true], lhs[false]
|
|
|
|
Expr::And(_, _, pos)
|
|
|
|
| Expr::Or(_, _, pos)
|
|
|
|
| Expr::In(_, _, pos)
|
|
|
|
| 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
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
|
|
|
(Token::RightBracket, _) => {
|
2020-04-15 15:41:28 +02:00
|
|
|
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), Box::new(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), Box::new(idx_expr), pos)),
|
2020-04-26 12:04:07 +02:00
|
|
|
}
|
2020-03-05 18:05:02 +01:00
|
|
|
}
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(*pos)),
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => Err(PERR::MissingToken(
|
2020-05-04 13:36:58 +02:00
|
|
|
Token::RightBracket.into(),
|
2020-04-17 14:01:41 +02:00
|
|
|
"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>(
|
2020-03-02 15:13:14 +01:00
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Expr, Box<ParseError>> {
|
2016-03-26 18:46:28 +01:00
|
|
|
let mut arr = Vec::new();
|
|
|
|
|
2020-04-15 15:41:28 +02:00
|
|
|
if !match_token(input, Token::RightBracket)? {
|
2020-04-17 14:01:41 +02:00
|
|
|
while !input.peek().unwrap().0.is_eof() {
|
2020-04-28 17:05:03 +02:00
|
|
|
arr.push(parse_expr(input, stack, allow_stmt_expr)?);
|
2020-03-02 15:13:14 +01:00
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::Comma, _) => eat_token(input, Token::Comma),
|
|
|
|
(Token::RightBracket, _) => {
|
|
|
|
eat_token(input, Token::RightBracket);
|
|
|
|
break;
|
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(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-04-17 14:01:41 +02:00
|
|
|
)
|
2020-05-04 13:36:58 +02:00
|
|
|
.into_err(*pos))
|
2020-04-17 14:01:41 +02:00
|
|
|
}
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::LexError(err), pos) => {
|
|
|
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
2020-04-15 15:41:28 +02:00
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => {
|
2020-03-29 07:44:27 +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 array literal".into(),
|
2020-03-27 16:47:23 +01:00
|
|
|
)
|
2020-03-29 07:44:27 +02:00
|
|
|
.into_err(*pos))
|
2020-03-03 09:23:55 +01:00
|
|
|
}
|
2020-04-06 11:47:34 +02:00
|
|
|
};
|
2016-04-14 03:01:08 +02:00
|
|
|
}
|
2016-03-26 18:46:28 +01:00
|
|
|
}
|
|
|
|
|
2020-05-04 13:36:58 +02:00
|
|
|
Ok(Expr::Array(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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Expr, Box<ParseError>> {
|
2020-03-29 17:53:35 +02:00
|
|
|
let mut map = Vec::new();
|
|
|
|
|
2020-04-15 15:41:28 +02:00
|
|
|
if !match_token(input, Token::RightBrace)? {
|
2020-04-17 14:01:41 +02:00
|
|
|
while !input.peek().unwrap().0.is_eof() {
|
2020-04-15 15:41:28 +02:00
|
|
|
const MISSING_RBRACE: &str = "to end this object map literal";
|
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
let (name, pos) = match input.next().unwrap() {
|
|
|
|
(Token::Identifier(s), pos) => (s, pos),
|
|
|
|
(Token::StringConst(s), pos) => (s, pos),
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::LexError(err), pos) => {
|
|
|
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, 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),
|
|
|
|
)
|
2020-04-15 15:41:28 +02:00
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(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
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
2020-03-29 17:53:35 +02:00
|
|
|
};
|
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.next().unwrap() {
|
|
|
|
(Token::Colon, _) => (),
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::LexError(err), pos) => {
|
|
|
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, 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
|
|
|
|
|
|
|
map.push((name, expr, pos));
|
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
|
|
|
(Token::Comma, _) => {
|
2020-04-17 13:00:52 +02:00
|
|
|
eat_token(input, Token::Comma);
|
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::RightBrace, _) => {
|
2020-04-15 15:41:28 +02:00
|
|
|
eat_token(input, Token::RightBrace);
|
|
|
|
break;
|
2020-03-29 17:53:35 +02:00
|
|
|
}
|
2020-04-17 14:01:41 +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))
|
|
|
|
}
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::LexError(err), pos) => {
|
|
|
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => {
|
2020-05-04 13:36:58 +02:00
|
|
|
return Err(
|
|
|
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
|
|
|
.into_err(*pos),
|
|
|
|
)
|
2020-04-15 15:41:28 +02:00
|
|
|
}
|
2020-03-29 17:53:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for duplicating properties
|
|
|
|
map.iter()
|
|
|
|
.enumerate()
|
|
|
|
.try_for_each(|(i, (k1, _, _))| {
|
|
|
|
map.iter()
|
|
|
|
.skip(i + 1)
|
|
|
|
.find(|(k2, _, _)| k2 == k1)
|
|
|
|
.map_or_else(|| Ok(()), |(k2, _, pos)| Err((k2, *pos)))
|
|
|
|
})
|
|
|
|
.map_err(|(key, pos)| PERR::DuplicatedProperty(key.to_string()).into_err(pos))?;
|
|
|
|
|
2020-05-04 13:36:58 +02:00
|
|
|
Ok(Expr::Map(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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Expr, Box<ParseError>> {
|
2020-04-17 14:01:41 +02:00
|
|
|
let (token, pos) = match input.peek().unwrap() {
|
2020-03-20 12:50:58 +01:00
|
|
|
// { - block statement as expression
|
2020-04-17 14:01:41 +02:00
|
|
|
(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)
|
2020-03-22 14:03:58 +01:00
|
|
|
.map(|block| Expr::Stmt(Box::new(block), pos));
|
2020-03-07 03:39:00 +01:00
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::EOF, pos) => return Err(PERR::UnexpectedEOF.into_err(*pos)),
|
|
|
|
_ => input.next().unwrap(),
|
2020-03-20 12:50:58 +01:00
|
|
|
};
|
2020-03-05 13:28:03 +01:00
|
|
|
|
2020-03-20 12:50:58 +01:00
|
|
|
let mut root_expr = match token {
|
2020-04-17 14:01:41 +02:00
|
|
|
Token::IntegerConstant(x) => Expr::IntegerConstant(x, pos),
|
2020-04-17 14:08:41 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-04-17 14:01:41 +02:00
|
|
|
Token::FloatConstant(x) => Expr::FloatConstant(x, pos),
|
|
|
|
Token::CharConstant(c) => Expr::CharConstant(c, pos),
|
2020-04-28 13:39:36 +02:00
|
|
|
Token::StringConst(s) => Expr::StringConstant(s, pos),
|
2020-04-28 17:05:03 +02:00
|
|
|
Token::Identifier(s) => {
|
|
|
|
let index = stack.find(&s);
|
2020-05-09 10:15:50 +02:00
|
|
|
Expr::Variable(Box::new(s), None, 0, index, pos)
|
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)?,
|
2020-04-17 14:01:41 +02:00
|
|
|
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
|
2020-04-17 14:01:41 +02:00
|
|
|
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
|
2020-05-09 10:15:50 +02:00
|
|
|
(Expr::Variable(id, modules, _, _, pos), Token::LeftParen) => {
|
2020-05-04 13:36:58 +02:00
|
|
|
parse_call_expr(input, stack, *id, modules, pos, allow_stmt_expr)?
|
2020-05-01 11:32:39 +02:00
|
|
|
}
|
|
|
|
(Expr::Property(id, pos), Token::LeftParen) => {
|
2020-05-04 11:43:54 +02:00
|
|
|
parse_call_expr(input, stack, id, None, pos, allow_stmt_expr)?
|
|
|
|
}
|
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 10:15:50 +02:00
|
|
|
(Expr::Variable(id, mut modules, _, index, pos), Token::DoubleColon) => {
|
2020-05-04 11:43:54 +02:00
|
|
|
match input.next().unwrap() {
|
|
|
|
(Token::Identifier(id2), pos2) => {
|
2020-05-04 13:36:58 +02:00
|
|
|
if let Some(ref mut modules) = modules {
|
|
|
|
modules.push((*id, pos));
|
2020-05-04 11:43:54 +02:00
|
|
|
} else {
|
2020-05-08 08:50:48 +02:00
|
|
|
let mut m: ModuleRef = Default::default();
|
|
|
|
m.push((*id, pos));
|
|
|
|
modules = Some(Box::new(m));
|
2020-05-04 11:43:54 +02:00
|
|
|
}
|
2020-05-04 13:36:58 +02:00
|
|
|
|
2020-05-09 10:15:50 +02:00
|
|
|
Expr::Variable(Box::new(id2), modules, 0, index, pos2)
|
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
|
2020-05-06 13:45:17 +02:00
|
|
|
#[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),
|
2020-03-10 10:10:33 +01:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
2020-03-06 03:50:52 +01:00
|
|
|
|
2020-05-07 18:19:08 +02: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"))]
|
2020-05-09 10:15:50 +02:00
|
|
|
Expr::Variable(id, Some(modules), hash, _, _) => {
|
2020-05-09 04:00:59 +02:00
|
|
|
// Qualifiers + variable name
|
2020-05-09 10:15:50 +02:00
|
|
|
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
|
2020-05-08 16:38:56 +02:00
|
|
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
2020-05-07 18:19:08 +02:00
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:50:52 +01:00
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Expr, Box<ParseError>> {
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
2020-03-27 16:47:23 +01:00
|
|
|
// If statement is allowed to act as expressions
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::If, pos) => {
|
2020-03-27 16:47:23 +01:00
|
|
|
let pos = *pos;
|
|
|
|
Ok(Expr::Stmt(
|
2020-04-28 17:05:03 +02:00
|
|
|
Box::new(parse_if(input, stack, false, allow_stmt_expr)?),
|
2020-03-27 16:47:23 +01:00
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// -expr
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::UnaryMinus, _) => {
|
2020-04-17 13:00:52 +02:00
|
|
|
let pos = eat_token(input, Token::UnaryMinus);
|
2020-03-02 15:13:14 +01:00
|
|
|
|
2020-04-28 17:05:03 +02:00
|
|
|
match parse_unary(input, stack, allow_stmt_expr)? {
|
2020-03-09 03:10:19 +01:00
|
|
|
// Negative integer
|
2020-03-16 16:51:32 +01:00
|
|
|
Expr::IntegerConstant(i, _) => i
|
2020-03-10 11:32:01 +01:00
|
|
|
.checked_neg()
|
|
|
|
.map(|x| Expr::IntegerConstant(x, pos))
|
2020-03-11 16:43:04 +01:00
|
|
|
.or_else(|| {
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-04-10 06:16:39 +02:00
|
|
|
{
|
|
|
|
Some(Expr::FloatConstant(-(i as FLOAT), pos))
|
|
|
|
}
|
2020-03-11 16:43:04 +01:00
|
|
|
#[cfg(feature = "no_float")]
|
2020-04-10 06:16:39 +02:00
|
|
|
{
|
|
|
|
None
|
|
|
|
}
|
2020-03-11 16:43:04 +01:00
|
|
|
})
|
2020-03-10 11:32:01 +01:00
|
|
|
.ok_or_else(|| {
|
2020-04-15 16:21:23 +02:00
|
|
|
PERR::BadInput(LexError::MalformedNumber(format!("-{}", i)).to_string())
|
2020-03-24 09:46:47 +01:00
|
|
|
.into_err(pos)
|
2020-03-10 11:32:01 +01:00
|
|
|
}),
|
|
|
|
|
2020-03-09 03:10:19 +01:00
|
|
|
// Negative float
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-16 16:51:32 +01:00
|
|
|
Expr::FloatConstant(f, pos) => Ok(Expr::FloatConstant(-f, pos)),
|
2020-03-10 11:32:01 +01:00
|
|
|
|
2020-03-09 03:10:19 +01:00
|
|
|
// 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(op.into()),
|
|
|
|
None,
|
|
|
|
hash,
|
|
|
|
Box::new(vec![e]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
2020-03-09 03:10:19 +01:00
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// +expr
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::UnaryPlus, _) => {
|
2020-04-15 15:41:28 +02:00
|
|
|
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
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::Bang, _) => {
|
2020-04-17 13:00:52 +02:00
|
|
|
let pos = eat_token(input, Token::Bang);
|
2020-05-09 10:15:50 +02:00
|
|
|
let op = "!";
|
|
|
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
|
|
|
|
2020-05-01 11:32:39 +02:00
|
|
|
Ok(Expr::FnCall(
|
2020-05-09 10:15:50 +02:00
|
|
|
Box::new(op.into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
hash,
|
2020-04-28 17:05:03 +02:00
|
|
|
Box::new(vec![parse_primary(input, stack, allow_stmt_expr)?]),
|
|
|
|
Some(Box::new(false.into())), // NOT operator, when operating on invalid operand, defaults to false
|
2020-03-02 15:13:14 +01:00
|
|
|
pos,
|
2020-03-02 07:28:42 +01:00
|
|
|
))
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-04-20 18:11:25 +02:00
|
|
|
// <EOF>
|
2020-04-17 14:01:41 +02:00
|
|
|
(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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-07 06:25:09 +02: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,
|
2020-05-07 06:25:09 +02:00
|
|
|
rhs: Expr,
|
|
|
|
pos: Position,
|
2020-04-22 11:37:06 +02:00
|
|
|
) -> Result<Expr, Box<ParseError>> {
|
2020-05-07 06:25:09 +02:00
|
|
|
match &lhs {
|
2020-05-09 10:15:50 +02:00
|
|
|
Expr::Variable(_, _, _, None, _) => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
|
|
|
Expr::Variable(name, _, _, Some(index), var_pos) => {
|
2020-05-07 06:25:09 +02:00
|
|
|
match stack[(stack.len() - index.get())].1 {
|
|
|
|
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
|
|
|
|
// Constant values cannot be assigned to
|
|
|
|
ScopeEntryType::Constant => {
|
|
|
|
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*var_pos))
|
|
|
|
}
|
|
|
|
ScopeEntryType::Module => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Expr::Index(idx_lhs, _, _) | Expr::Dot(idx_lhs, _, _) => match idx_lhs.as_ref() {
|
2020-05-09 10:15:50 +02:00
|
|
|
Expr::Variable(_, _, _, None, _) => {
|
2020-05-07 06:25:09 +02:00
|
|
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
|
|
|
}
|
2020-05-09 10:15:50 +02:00
|
|
|
Expr::Variable(name, _, _, Some(index), var_pos) => {
|
2020-05-07 06:25:09 +02:00
|
|
|
match stack[(stack.len() - index.get())].1 {
|
|
|
|
ScopeEntryType::Normal => {
|
|
|
|
Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos))
|
|
|
|
}
|
|
|
|
// Constant values cannot be assigned to
|
|
|
|
ScopeEntryType::Constant => {
|
|
|
|
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*var_pos))
|
|
|
|
}
|
|
|
|
ScopeEntryType::Module => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err(PERR::AssignmentToCopy.into_err(idx_lhs.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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Expr, Box<ParseError>> {
|
2020-04-22 11:37:06 +02:00
|
|
|
let (op, pos) = match *input.peek().unwrap() {
|
2020-05-07 06:25:09 +02:00
|
|
|
(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)
|
2020-05-01 11:32:39 +02:00
|
|
|
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()));
|
|
|
|
let rhs_expr = Expr::FnCall(Box::new(op.into()), None, hash, Box::new(args), None, pos);
|
2020-05-07 06:25:09 +02:00
|
|
|
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
|
2020-04-26 12:04:07 +02:00
|
|
|
(Expr::Index(idx_lhs, idx_rhs, idx_pos), rhs) => Expr::Index(
|
|
|
|
idx_lhs,
|
2020-05-04 11:43:54 +02:00
|
|
|
Box::new(make_dot_expr(*idx_rhs, rhs, op_pos, true)?),
|
2020-04-26 12:04:07 +02:00
|
|
|
idx_pos,
|
|
|
|
),
|
|
|
|
// lhs.id
|
2020-05-09 10:15:50 +02:00
|
|
|
(lhs, rhs @ Expr::Variable(_, None, _, _, _)) | (lhs, rhs @ Expr::Property(_, _)) => {
|
2020-04-26 12:04:07 +02:00
|
|
|
let lhs = if is_index { lhs.into_property() } else { lhs };
|
|
|
|
Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos)
|
|
|
|
}
|
2020-05-04 13:36:58 +02:00
|
|
|
// lhs.module::id - syntax error
|
2020-05-09 10:15:50 +02:00
|
|
|
(_, Expr::Variable(_, Some(modules), _, _, _)) => {
|
2020-05-08 08:50:48 +02:00
|
|
|
#[cfg(feature = "no_module")]
|
|
|
|
unreachable!();
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
return Err(PERR::PropertyExpected.into_err(modules.get(0).1));
|
2020-05-04 11:43:54 +02:00
|
|
|
}
|
2020-04-26 12:04:07 +02:00
|
|
|
// lhs.dot_lhs.dot_rhs
|
|
|
|
(lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot(
|
|
|
|
Box::new(lhs),
|
|
|
|
Box::new(Expr::Dot(
|
|
|
|
Box::new(dot_lhs.into_property()),
|
|
|
|
Box::new(dot_rhs.into_property()),
|
|
|
|
dot_pos,
|
|
|
|
)),
|
|
|
|
op_pos,
|
|
|
|
),
|
|
|
|
// lhs.idx_lhs[idx_rhs]
|
|
|
|
(lhs, Expr::Index(idx_lhs, idx_rhs, idx_pos)) => Expr::Dot(
|
|
|
|
Box::new(lhs),
|
|
|
|
Box::new(Expr::Index(
|
|
|
|
Box::new(idx_lhs.into_property()),
|
|
|
|
Box::new(idx_rhs.into_property()),
|
|
|
|
idx_pos,
|
|
|
|
)),
|
|
|
|
op_pos,
|
|
|
|
),
|
|
|
|
// lhs.rhs
|
|
|
|
(lhs, rhs) => Expr::Dot(Box::new(lhs), Box::new(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(_, pos))
|
|
|
|
| (_, Expr::And(_, _, pos))
|
|
|
|
| (_, Expr::Or(_, _, pos))
|
|
|
|
| (_, Expr::In(_, _, pos))
|
|
|
|
| (_, Expr::True(pos))
|
|
|
|
| (_, Expr::False(pos))
|
|
|
|
| (_, Expr::Assignment(_, _, pos))
|
|
|
|
| (_, Expr::Unit(pos)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression expects a string, array or object map".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
(_, Expr::FloatConstant(_, pos)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression expects a string, array or object map".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
// "xxx" in "xxxx", 'x' in "xxxx" - OK!
|
|
|
|
(Expr::StringConstant(_, _), Expr::StringConstant(_, _))
|
|
|
|
| (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (),
|
|
|
|
|
|
|
|
// 123.456 in "xxxx"
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-04-06 11:47:34 +02:00
|
|
|
(Expr::FloatConstant(_, pos), Expr::StringConstant(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for a string expects a string, not a float".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// 123 in "xxxx"
|
|
|
|
(Expr::IntegerConstant(_, pos), Expr::StringConstant(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for a string expects a string, not a number".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// (??? && ???) in "xxxx", (??? || ???) in "xxxx", (??? in ???) in "xxxx",
|
|
|
|
// true in "xxxx", false in "xxxx"
|
|
|
|
(Expr::And(_, _, pos), Expr::StringConstant(_, _))
|
|
|
|
| (Expr::Or(_, _, pos), Expr::StringConstant(_, _))
|
|
|
|
| (Expr::In(_, _, pos), Expr::StringConstant(_, _))
|
|
|
|
| (Expr::True(pos), Expr::StringConstant(_, _))
|
|
|
|
| (Expr::False(pos), Expr::StringConstant(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for a string expects a string, not a boolean".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// [???, ???, ???] in "xxxx"
|
|
|
|
(Expr::Array(_, pos), Expr::StringConstant(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for a string expects a string, not an array".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// #{...} in "xxxx"
|
|
|
|
(Expr::Map(_, pos), Expr::StringConstant(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for a string expects a string, not an object map".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// (??? = ???) in "xxxx", () in "xxxx"
|
|
|
|
(Expr::Assignment(_, _, pos), Expr::StringConstant(_, _))
|
|
|
|
| (Expr::Unit(pos), Expr::StringConstant(_, _)) => {
|
|
|
|
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(_, _)) => (),
|
|
|
|
|
|
|
|
// 123.456 in #{...}
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-04-06 11:47:34 +02:00
|
|
|
(Expr::FloatConstant(_, pos), Expr::Map(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for an object map expects a string, not a float".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// 123 in #{...}
|
|
|
|
(Expr::IntegerConstant(_, pos), Expr::Map(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for an object map expects a string, not a number".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...},
|
|
|
|
// true in #{...}, false in #{...}
|
|
|
|
(Expr::And(_, _, pos), Expr::Map(_, _))
|
|
|
|
| (Expr::Or(_, _, pos), Expr::Map(_, _))
|
|
|
|
| (Expr::In(_, _, pos), Expr::Map(_, _))
|
|
|
|
| (Expr::True(pos), Expr::Map(_, _))
|
|
|
|
| (Expr::False(pos), Expr::Map(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for an object map expects a string, not a boolean".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// [???, ???, ???] in #{..}
|
|
|
|
(Expr::Array(_, pos), Expr::Map(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for an object map expects a string, not an array".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// #{...} in #{..}
|
|
|
|
(Expr::Map(_, pos), Expr::Map(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for an object map expects a string, not an object map".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
// (??? = ???) in #{...}, () in #{...}
|
|
|
|
(Expr::Assignment(_, _, pos), Expr::Map(_, _)) | (Expr::Unit(pos), Expr::Map(_, _)) => {
|
|
|
|
return Err(PERR::MalformedInExpr(
|
|
|
|
"'in' expression for an object map expects a string, not ()".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Expr::In(Box::new(lhs), Box::new(rhs), op_pos))
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Parse a binary expression.
|
2020-03-03 09:23:55 +01:00
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Expr, Box<ParseError>> {
|
2020-03-03 09:23:55 +01:00
|
|
|
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)
|
|
|
|
{
|
2020-03-03 09:23:55 +01:00
|
|
|
return Ok(current_lhs);
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
2020-04-23 07:24:24 +02:00
|
|
|
let (op_token, pos) = input.next().unwrap();
|
2016-04-14 03:01:08 +02:00
|
|
|
|
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
|
|
|
|
2020-04-23 07:24:24 +02:00
|
|
|
let next_precedence = input.peek().unwrap().0.precedence();
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-04-23 07:24:24 +02: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)?
|
2020-04-23 07:24:24 +02:00
|
|
|
} else {
|
|
|
|
// Otherwise bind to left (even if next operator has the same precedence)
|
|
|
|
rhs
|
|
|
|
};
|
2020-03-02 07:28:42 +01:00
|
|
|
|
2020-04-28 17:05:03 +02:00
|
|
|
let cmp_default = Some(Box::new(false.into()));
|
2020-04-22 11:37:06 +02:00
|
|
|
|
2020-04-28 17:05:03 +02:00
|
|
|
current_lhs = match op_token {
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::Plus => Expr::FnCall(
|
|
|
|
Box::new("+".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "+", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::Minus => Expr::FnCall(
|
|
|
|
Box::new("-".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "-", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::Multiply => Expr::FnCall(
|
|
|
|
Box::new("*".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "*", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::Divide => Expr::FnCall(
|
|
|
|
Box::new("/".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "/", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
2020-04-28 17:05:03 +02:00
|
|
|
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::LeftShift => Expr::FnCall(
|
|
|
|
Box::new("<<".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "<<", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::RightShift => Expr::FnCall(
|
|
|
|
Box::new(">>".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), ">>", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::Modulo => Expr::FnCall(
|
|
|
|
Box::new("%".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "%", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::PowerOf => Expr::FnCall(
|
|
|
|
Box::new("~".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "~", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
2020-04-28 17:05:03 +02:00
|
|
|
|
|
|
|
// Comparison operators default to false when passed invalid operands
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::EqualsTo => Expr::FnCall(
|
|
|
|
Box::new("==".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "==", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-04-28 17:05:03 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
cmp_default,
|
|
|
|
pos,
|
|
|
|
),
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::NotEqualsTo => Expr::FnCall(
|
|
|
|
Box::new("!=".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "!=", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-04-28 17:05:03 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
cmp_default,
|
|
|
|
pos,
|
|
|
|
),
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::LessThan => Expr::FnCall(
|
|
|
|
Box::new("<".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "<", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-04-28 17:05:03 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
cmp_default,
|
|
|
|
pos,
|
|
|
|
),
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::LessThanEqualsTo => Expr::FnCall(
|
|
|
|
Box::new("<=".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "<=", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-04-28 17:05:03 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
cmp_default,
|
|
|
|
pos,
|
|
|
|
),
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::GreaterThan => Expr::FnCall(
|
|
|
|
Box::new(">".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), ">", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-04-28 17:05:03 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
cmp_default,
|
|
|
|
pos,
|
|
|
|
),
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::GreaterThanEqualsTo => Expr::FnCall(
|
|
|
|
Box::new(">=".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), ">=", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-04-28 17:05:03 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
cmp_default,
|
|
|
|
pos,
|
|
|
|
),
|
2020-04-23 07:24:24 +02:00
|
|
|
|
|
|
|
Token::Or => Expr::Or(Box::new(current_lhs), Box::new(rhs), pos),
|
|
|
|
Token::And => Expr::And(Box::new(current_lhs), Box::new(rhs), pos),
|
2020-05-01 11:32:39 +02:00
|
|
|
Token::Ampersand => Expr::FnCall(
|
|
|
|
Box::new("&".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "&", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::Pipe => Expr::FnCall(
|
|
|
|
Box::new("|".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "|", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::XOr => Expr::FnCall(
|
|
|
|
Box::new("^".into()),
|
2020-05-04 11:43:54 +02:00
|
|
|
None,
|
2020-05-09 10:15:50 +02:00
|
|
|
calc_fn_hash(empty(), "^", repeat(EMPTY_TYPE_ID()).take(2)),
|
2020-05-01 11:32:39 +02:00
|
|
|
Box::new(vec![current_lhs, rhs]),
|
|
|
|
None,
|
|
|
|
pos,
|
|
|
|
),
|
2020-04-23 07:24:24 +02:00
|
|
|
|
2020-04-26 12:04:07 +02:00
|
|
|
Token::In => make_in_expr(current_lhs, rhs, pos)?,
|
2020-04-23 07:24:24 +02:00
|
|
|
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-05-04 11:43:54 +02:00
|
|
|
Token::Period => make_dot_expr(current_lhs, rhs, pos, false)?,
|
2020-04-23 07:24:24 +02:00
|
|
|
|
2020-05-04 13:36:58 +02:00
|
|
|
token => return Err(PERR::UnknownOperator(token.into()).into_err(pos)),
|
2020-04-23 07:24:24 +02:00
|
|
|
};
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> 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 `{}`).
|
2020-03-20 12:50:58 +01:00
|
|
|
fn ensure_not_statement_expr<'a>(
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
|
|
|
type_name: &str,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<(), Box<ParseError>> {
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
2020-03-20 12:50:58 +01:00
|
|
|
// Disallow statement expressions
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::LeftBrace, pos) | (Token::EOF, pos) => {
|
|
|
|
Err(PERR::ExprExpected(type_name.to_string()).into_err(*pos))
|
|
|
|
}
|
2020-03-20 12:50:58 +01:00
|
|
|
// 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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Stmt, Box<ParseError>> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// if ...
|
2020-04-15 15:41:28 +02:00
|
|
|
eat_token(input, Token::If);
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-03-20 12:50:58 +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
|
|
|
|
2020-03-20 12:50:58 +01:00
|
|
|
// if guard { if_body } else ...
|
2020-04-15 15:41:28 +02:00
|
|
|
let else_body = if match_token(input, Token::Else).unwrap_or(false) {
|
2020-04-23 07:24:24 +02:00
|
|
|
Some(Box::new(if let (Token::If, _) = input.peek().unwrap() {
|
2020-03-20 12:50:58 +01:00
|
|
|
// 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 {
|
2020-03-20 12:50:58 +01:00
|
|
|
// 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
|
|
|
|
2020-03-22 03:18:16 +01:00
|
|
|
Ok(Stmt::IfThenElse(
|
|
|
|
Box::new(guard),
|
|
|
|
Box::new(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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Stmt, Box<ParseError>> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// while ...
|
2020-04-15 15:41:28 +02:00
|
|
|
eat_token(input, Token::While);
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-03-18 11:41:18 +01:00
|
|
|
// while guard { body }
|
2020-03-20 12:50:58 +01:00
|
|
|
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), Box::new(body)))
|
|
|
|
}
|
|
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Stmt, Box<ParseError>> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// loop ...
|
2020-04-15 15:41:28 +02:00
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Stmt, Box<ParseError>> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// for ...
|
2020-04-15 15:41:28 +02:00
|
|
|
eat_token(input, Token::For);
|
2020-02-23 15:48:46 +01:00
|
|
|
|
2020-03-18 11:41:18 +01:00
|
|
|
// for name ...
|
2020-04-17 14:01:41 +02:00
|
|
|
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)),
|
2020-04-17 14:01:41 +02:00
|
|
|
// 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-02-23 15:48:46 +01:00
|
|
|
};
|
|
|
|
|
2020-03-18 11:41:18 +01:00
|
|
|
// for name in ...
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.next().unwrap() {
|
|
|
|
(Token::In, _) => (),
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => {
|
|
|
|
return Err(
|
2020-05-04 13:36:58 +02:00
|
|
|
PERR::MissingToken(Token::In.into(), "after the iteration variable".into())
|
2020-04-17 14:01:41 +02:00
|
|
|
.into_err(pos),
|
|
|
|
)
|
2020-03-30 06:14:59 +02:00
|
|
|
}
|
2020-02-23 15:48:46 +01:00
|
|
|
}
|
|
|
|
|
2020-03-18 11:41:18 +01:00
|
|
|
// for name in expr { body }
|
2020-03-20 12:50:58 +01:00
|
|
|
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)?;
|
|
|
|
|
2020-05-03 10:54:24 +02:00
|
|
|
stack.truncate(prev_len);
|
2020-02-23 15:48:46 +01:00
|
|
|
|
2020-05-01 11:32:39 +02:00
|
|
|
Ok(Stmt::For(Box::new(name), Box::new(expr), Box::new(body)))
|
2020-02-23 15:48:46 +01:00
|
|
|
}
|
|
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> 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 ...
|
2020-04-17 14:01:41 +02:00
|
|
|
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 = ...
|
2020-04-15 15:41:28 +02:00
|
|
|
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-01 11:32:39 +02:00
|
|
|
Ok(Stmt::Let(Box::new(name), Some(Box::new(init_value)), pos))
|
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-01 11:32:39 +02:00
|
|
|
Ok(Stmt::Const(Box::new(name), Box::new(init_value), pos))
|
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()))
|
|
|
|
}
|
2020-05-06 16:54:34 +02:00
|
|
|
// 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
|
2020-05-06 16:54:34 +02:00
|
|
|
match var_type {
|
|
|
|
ScopeEntryType::Normal => {
|
|
|
|
stack.push((name.clone(), ScopeEntryType::Normal));
|
|
|
|
Ok(Stmt::Let(Box::new(name), None, pos))
|
|
|
|
}
|
|
|
|
ScopeEntryType::Constant => {
|
|
|
|
stack.push((name.clone(), ScopeEntryType::Constant));
|
|
|
|
Ok(Stmt::Const(Box::new(name), Box::new(Expr::Unit(pos)), 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-04 13:36:58 +02:00
|
|
|
Ok(Stmt::Import(Box::new(expr), Box::new(name), pos))
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.push((id, id_pos, rename));
|
|
|
|
|
|
|
|
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()
|
|
|
|
.try_for_each(|(i, (p1, _, _))| {
|
|
|
|
exports
|
|
|
|
.iter()
|
|
|
|
.skip(i + 1)
|
|
|
|
.find(|(p2, _, _)| p2 == p1)
|
|
|
|
.map_or_else(|| Ok(()), |(p2, pos, _)| Err((p2, *pos)))
|
|
|
|
})
|
|
|
|
.map_err(|(p, pos)| PERR::DuplicatedExport(p.to_string()).into_err(pos))?;
|
|
|
|
|
|
|
|
Ok(Stmt::Export(exports))
|
|
|
|
}
|
|
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Stmt, Box<ParseError>> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// Must start with {
|
2020-04-17 14:01:41 +02:00
|
|
|
let pos = match input.next().unwrap() {
|
2020-03-16 16:51:32 +01:00
|
|
|
(Token::LeftBrace, pos) => pos,
|
2020-04-20 18:11:25 +02:00
|
|
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
2020-03-29 07:44:27 +02:00
|
|
|
(_, pos) => {
|
2020-05-04 13:36:58 +02:00
|
|
|
return Err(PERR::MissingToken(
|
|
|
|
Token::LeftBrace.into(),
|
|
|
|
"to start a statement block".into(),
|
2020-03-29 07:44:27 +02:00
|
|
|
)
|
2020-05-04 13:36:58 +02:00
|
|
|
.into_err(pos))
|
2020-03-29 07:44:27 +02:00
|
|
|
}
|
2020-03-12 05:40:28 +01:00
|
|
|
};
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
let mut statements = Vec::new();
|
2020-04-28 17:05:03 +02:00
|
|
|
let prev_len = stack.len();
|
2016-03-01 21:16:10 +01:00
|
|
|
|
2020-04-15 15:41:28 +02:00
|
|
|
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)?;
|
2016-03-01 21:16:10 +01:00
|
|
|
|
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);
|
2020-03-17 07:29:22 +01:00
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
2020-03-18 11:41:18 +01:00
|
|
|
// { ... stmt }
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::RightBrace, _) => {
|
2020-04-15 15:41:28 +02:00
|
|
|
eat_token(input, Token::RightBrace);
|
|
|
|
break;
|
|
|
|
}
|
2020-03-18 11:41:18 +01:00
|
|
|
// { ... stmt;
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::SemiColon, _) if need_semicolon => {
|
2020-04-15 15:41:28 +02:00
|
|
|
eat_token(input, Token::SemiColon);
|
2020-03-17 10:33:37 +01:00
|
|
|
}
|
2020-03-18 11:41:18 +01:00
|
|
|
// { ... { stmt } ;
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::SemiColon, _) if !need_semicolon => (),
|
2020-03-18 11:41:18 +01:00
|
|
|
// { ... { stmt } ???
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, _) if !need_semicolon => (),
|
2020-04-20 18:11:25 +02:00
|
|
|
// { ... stmt <error>
|
|
|
|
(Token::LexError(err), pos) => {
|
|
|
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
|
|
|
}
|
|
|
|
// { ... stmt ???
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, 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-04-14 03:01:08 +02:00
|
|
|
}
|
2016-03-01 21:16:10 +01:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-05-03 10:54:24 +02:00
|
|
|
stack.truncate(prev_len);
|
2020-04-28 17:05:03 +02:00
|
|
|
|
2020-04-15 15:41:28 +02:00
|
|
|
Ok(Stmt::Block(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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> 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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<Stmt, Box<ParseError>> {
|
2020-04-17 14:01:41 +02:00
|
|
|
let (token, pos) = match input.peek().unwrap() {
|
|
|
|
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
|
|
|
|
x => x,
|
2020-03-20 12:50:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
match token {
|
2020-03-18 11:41:18 +01:00
|
|
|
// Semicolon - empty statement
|
2020-04-17 14:01:41 +02:00
|
|
|
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 ...
|
2020-05-08 16:38:56 +02:00
|
|
|
Token::Fn if !is_global => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
|
|
|
Token::Fn => unreachable!(),
|
2020-03-17 07:29:22 +01:00
|
|
|
|
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
|
|
|
|
2020-04-17 14:01:41 +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))
|
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
Token::Break if breakable => {
|
2020-04-17 13:00:52 +02:00
|
|
|
let pos = eat_token(input, Token::Break);
|
2020-03-02 15:13:14 +01:00
|
|
|
Ok(Stmt::Break(pos))
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-04-17 14:01:41 +02:00
|
|
|
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(*pos)),
|
2020-04-01 10:22:18 +02:00
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
Token::Return | Token::Throw => {
|
2020-04-15 15:41:28 +02:00
|
|
|
let pos = *pos;
|
|
|
|
|
2020-04-23 07:24:24 +02:00
|
|
|
let return_type = match input.next().unwrap() {
|
|
|
|
(Token::Return, _) => ReturnType::Return,
|
|
|
|
(Token::Throw, _) => ReturnType::Exception,
|
2020-03-15 15:39:58 +01:00
|
|
|
_ => panic!("token should be return or throw"),
|
2020-03-03 11:15:20 +01:00
|
|
|
};
|
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
2020-04-20 18:11:25 +02:00
|
|
|
// `return`/`throw` at <EOF>
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::EOF, pos) => Ok(Stmt::ReturnWithVal(None, return_type, *pos)),
|
2020-03-18 03:36:50 +01:00
|
|
|
// `return;` or `throw;`
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::SemiColon, _) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
2020-03-18 03:36:50 +01:00
|
|
|
// `return` or `throw` with expression
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, _) => {
|
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(Some(Box::new(expr)), return_type, pos))
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2016-03-03 15:31:42 +01:00
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
2020-05-06 13:45:17 +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,
|
2020-05-09 05:29:30 +02:00
|
|
|
access: FnAccess,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> 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
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
let name = match input.next().unwrap() {
|
|
|
|
(Token::Identifier(s), _) => s,
|
|
|
|
(_, pos) => return Err(PERR::FnMissingName.into_err(pos)),
|
2016-03-01 21:16:10 +01:00
|
|
|
};
|
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
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
|
|
|
};
|
2016-03-01 21:16:10 +01:00
|
|
|
|
|
|
|
let mut params = Vec::new();
|
|
|
|
|
2020-04-15 15:41:28 +02:00
|
|
|
if !match_token(input, Token::RightParen)? {
|
2020-03-29 07:44:27 +02:00
|
|
|
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 {
|
2020-04-17 14:01:41 +02:00
|
|
|
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))
|
|
|
|
}
|
2020-04-20 18:11:25 +02:00
|
|
|
(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
|
|
|
}
|
|
|
|
|
2020-04-17 14:01:41 +02: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))
|
2020-03-29 07:44:27 +02:00
|
|
|
}
|
2020-04-20 18:11:25 +02:00
|
|
|
(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))
|
|
|
|
}
|
2016-03-01 21:16:10 +01:00
|
|
|
}
|
2020-03-14 16:41:15 +01:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
2020-03-27 09:46:08 +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)| {
|
2020-03-29 07:44:27 +02:00
|
|
|
PERR::FnDuplicatedParam(name.to_string(), p.to_string()).into_err(pos)
|
2020-03-27 09:46:08 +01:00
|
|
|
})?;
|
|
|
|
|
|
|
|
// Parse function body
|
2020-04-29 10:11:54 +02:00
|
|
|
let body = Box::new(match input.peek().unwrap() {
|
2020-04-28 17:05:03 +02:00
|
|
|
(Token::LeftBrace, _) => parse_block(input, stack, false, allow_stmt_expr)?,
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
|
2020-04-29 10:11:54 +02:00
|
|
|
});
|
2016-03-01 21:16:10 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
let params = params.into_iter().map(|(p, _)| p).collect();
|
|
|
|
|
2016-04-17 04:32:18 +02:00
|
|
|
Ok(FnDef {
|
2020-03-11 16:43:04 +01:00
|
|
|
name,
|
2020-05-09 05:29:30 +02:00
|
|
|
access,
|
2020-04-06 11:47:34 +02:00
|
|
|
params,
|
2020-03-09 14:57:07 +01:00
|
|
|
body,
|
|
|
|
pos,
|
2016-04-17 04:32:18 +02:00
|
|
|
})
|
2016-03-01 21:16:10 +01:00
|
|
|
}
|
|
|
|
|
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,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> 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
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
|
|
|
(Token::EOF, _) => (),
|
2020-03-22 14:03:58 +01:00
|
|
|
// Return error if the expression doesn't end
|
2020-04-17 14:01:41 +02:00
|
|
|
(token, pos) => {
|
|
|
|
return Err(PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(*pos))
|
|
|
|
}
|
2020-03-22 14:03:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(
|
|
|
|
// Optimize AST
|
2020-04-08 03:30:50 +02:00
|
|
|
optimize_into_ast(
|
|
|
|
engine,
|
|
|
|
scope,
|
|
|
|
vec![Stmt::Expr(Box::new(expr))],
|
|
|
|
vec![],
|
2020-04-10 11:14:07 +02:00
|
|
|
optimization_level,
|
2020-04-08 03:30:50 +02:00
|
|
|
),
|
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>>,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<(Vec<Stmt>, HashMap<u64, FnDef>), Box<ParseError>> {
|
2020-03-11 06:28:12 +01:00
|
|
|
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();
|
2016-03-01 21:16:10 +01:00
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
while !input.peek().unwrap().0.is_eof() {
|
2020-04-10 06:16:39 +02:00
|
|
|
// Collect all the function definitions
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
{
|
2020-05-09 05:29:30 +02:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
_ => (),
|
2020-05-06 13:45:17 +02:00
|
|
|
}
|
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)?;
|
2020-03-17 07:29:22 +01:00
|
|
|
|
|
|
|
let need_semicolon = !stmt.is_self_terminated();
|
|
|
|
|
|
|
|
statements.push(stmt);
|
|
|
|
|
2020-04-17 14:01:41 +02:00
|
|
|
match input.peek().unwrap() {
|
2020-03-18 11:41:18 +01:00
|
|
|
// EOF
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::EOF, _) => break,
|
2020-03-18 11:41:18 +01:00
|
|
|
// stmt ;
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::SemiColon, _) if need_semicolon => {
|
2020-04-15 15:41:28 +02:00
|
|
|
eat_token(input, Token::SemiColon);
|
2020-03-17 07:29:22 +01:00
|
|
|
}
|
2020-03-18 11:41:18 +01:00
|
|
|
// stmt ;
|
2020-04-17 14:01:41 +02:00
|
|
|
(Token::SemiColon, _) if !need_semicolon => (),
|
2020-03-18 11:41:18 +01:00
|
|
|
// { stmt } ???
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, _) if !need_semicolon => (),
|
2020-04-20 18:11:25 +02:00
|
|
|
// stmt <error>
|
|
|
|
(Token::LexError(err), pos) => {
|
|
|
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
|
|
|
}
|
|
|
|
// stmt ???
|
2020-04-17 14:01:41 +02:00
|
|
|
(_, pos) => {
|
2020-03-17 07:29:22 +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));
|
2020-03-17 07:29:22 +01:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-15 15:39:58 +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>(
|
2020-03-15 15:39:58 +01:00
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
2020-04-16 17:31:48 +02:00
|
|
|
engine: &Engine,
|
2020-03-15 15:39:58 +01:00
|
|
|
scope: &Scope,
|
2020-04-10 06:16:39 +02:00
|
|
|
optimization_level: OptimizationLevel,
|
2020-04-17 18:14:33 +02:00
|
|
|
) -> Result<AST, Box<ParseError>> {
|
2020-03-17 07:29:22 +01:00
|
|
|
let (statements, functions) = parse_global_level(input)?;
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-04-16 17:58:57 +02:00
|
|
|
let fn_lib = functions.into_iter().map(|(_, v)| v).collect();
|
2020-03-14 13:06:10 +01:00
|
|
|
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),
|
2020-03-14 13:06:10 +01:00
|
|
|
)
|
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 {
|
|
|
|
Union::Unit(_) => Some(Expr::Unit(pos)),
|
|
|
|
Union::Int(value) => Some(Expr::IntegerConstant(value, pos)),
|
|
|
|
Union::Char(value) => Some(Expr::CharConstant(value, pos)),
|
2020-04-28 13:39:36 +02:00
|
|
|
Union::Str(value) => Some(Expr::StringConstant((*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(
|
|
|
|
items.into_iter().map(Option::unwrap).collect(),
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
} 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()
|
|
|
|
.map(|(k, v)| (k, map_dynamic_to_expr(v, pos), pos))
|
|
|
|
.collect();
|
|
|
|
if items.iter().all(|(_, expr, _)| expr.is_some()) {
|
|
|
|
Some(Expr::Map(
|
|
|
|
items
|
|
|
|
.into_iter()
|
|
|
|
.map(|(k, expr, pos)| (k, expr.unwrap(), pos))
|
|
|
|
.collect(),
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
2020-04-10 06:16:39 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-15 15:39:58 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-04-12 17:00:06 +02:00
|
|
|
Union::Float(value) => Some(Expr::FloatConstant(value, pos)),
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-04-12 17:00:06 +02:00
|
|
|
_ => None,
|
2020-03-15 15:39:58 +01:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|