rhai/src/parser.rs

1864 lines
63 KiB
Rust
Raw Normal View History

2020-03-08 12:54:02 +01:00
//! Main module defining the lexer and parser.
2020-04-12 17:00:06 +02:00
use crate::any::{Dynamic, Union};
2020-04-16 17:58:57 +02:00
use crate::engine::{calc_fn_def, Engine, FunctionsLib};
2020-03-04 15:00:01 +01:00
use crate::error::{LexError, ParseError, ParseErrorType};
use crate::optimize::{optimize_into_ast, OptimizationLevel};
2020-04-10 06:16:39 +02:00
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::{Position, Token, TokenIterator};
2020-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
fmt::Display,
format,
2020-03-17 19:26:11 +01:00
iter::Peekable,
2020-03-27 04:50:24 +01:00
ops::Add,
rc::Rc,
2020-03-17 19:26:11 +01:00
string::{String, ToString},
sync::Arc,
vec,
2020-03-17 19:26:11 +01:00
vec::Vec,
2020-03-12 05:40:28 +01:00
};
2016-02-29 22:43:45 +01:00
2020-03-11 04:03:18 +01:00
/// The system integer type.
///
/// If the `only_i32` feature is enabled, this will be `i32` instead.
#[cfg(not(feature = "only_i32"))]
pub type INT = i64;
2020-03-18 03:36:50 +01:00
/// The system integer type.
2020-03-11 04:03:18 +01:00
///
/// If the `only_i32` feature is not enabled, this will be `i64` instead.
#[cfg(feature = "only_i32")]
pub type INT = i32;
2020-03-18 03:36:50 +01:00
/// The system floating-point type.
2020-04-03 13:42:01 +02:00
///
/// Not available under the `no_float` feature.
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-03-11 16:43:04 +01:00
#[derive(Debug, Clone)]
pub struct AST(
pub(crate) Vec<Stmt>,
#[cfg(feature = "sync")] pub(crate) Arc<FunctionsLib>,
#[cfg(not(feature = "sync"))] pub(crate) Rc<FunctionsLib>,
);
2020-03-27 04:50:24 +01:00
impl AST {
/// Create a new `AST`.
pub fn new() -> Self {
Default::default()
}
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
2020-03-27 04:50:24 +01:00
/// is returned.
///
/// The second `AST` is simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
/// Of course, if the first `AST` uses a `return` statement at the end, then
/// the second `AST` will essentially be dead code.
///
/// All script-defined functions in the second `AST` overwrite similarly-named functions
/// in the first `AST` with the same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
2020-04-01 04:21:49 +02:00
/// # #[cfg(not(feature = "no_function"))]
/// # {
2020-03-27 04:50:24 +01:00
/// use rhai::Engine;
///
/// let engine = Engine::new();
2020-03-27 04:50:24 +01:00
///
/// let ast1 = engine.compile(r#"fn foo(x) { 42 + x } foo(1)"#)?;
2020-04-01 04:21:49 +02:00
/// let ast2 = engine.compile(r#"fn foo(n) { "hello" + n } foo("!")"#)?;
2020-03-27 04:50:24 +01:00
///
/// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1'
2020-03-27 04:50:24 +01:00
///
/// // Notice that using the '+' operator also works:
/// // let ast = &ast1 + &ast2;
2020-03-27 04:50:24 +01:00
///
/// // 'ast' is essentially:
/// //
/// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten
/// // foo(1) // <- notice this will be "hello1" instead of 43,
/// // // but it is no longer the return value
2020-04-01 04:21:49 +02:00
/// // foo("!") // returns "hello!"
2020-03-27 04:50:24 +01:00
///
/// // Evaluate it
2020-04-01 04:21:49 +02:00
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "hello!");
/// # }
2020-03-27 04:50:24 +01:00
/// # Ok(())
/// # }
/// ```
pub fn merge(&self, other: &Self) -> Self {
2020-04-05 11:44:48 +02:00
let Self(statements, functions) = self;
2020-04-05 11:44:48 +02:00
let ast = match (statements.is_empty(), other.0.is_empty()) {
(false, false) => {
2020-04-05 11:44:48 +02:00
let mut statements = statements.clone();
statements.extend(other.0.iter().cloned());
statements
}
2020-04-05 11:44:48 +02:00
(false, true) => statements.clone(),
(true, false) => other.0.clone(),
(true, true) => vec![],
};
2020-03-27 04:50:24 +01:00
#[cfg(feature = "sync")]
{
Self(ast, Arc::new(functions.merge(other.1.as_ref())))
}
#[cfg(not(feature = "sync"))]
{
Self(ast, Rc::new(functions.merge(other.1.as_ref())))
}
}
2020-03-27 04:50:24 +01:00
/// Clear all function definitions in the `AST`.
pub fn clear_functions(&mut self) {
#[cfg(feature = "sync")]
{
self.1 = Arc::new(FunctionsLib::new());
2020-03-27 04:50:24 +01:00
}
#[cfg(not(feature = "sync"))]
{
self.1 = Rc::new(FunctionsLib::new());
}
}
2020-03-27 04:50:24 +01:00
/// Clear all statements in the `AST`, leaving only function definitions.
pub fn retain_functions(&mut self) {
self.0 = vec![];
2020-03-27 04:50:24 +01:00
}
}
impl Default for AST {
fn default() -> Self {
#[cfg(feature = "sync")]
{
Self(vec![], Arc::new(FunctionsLib::new()))
}
#[cfg(not(feature = "sync"))]
{
Self(vec![], Rc::new(FunctionsLib::new()))
}
}
}
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-03-18 03:36:50 +01:00
/// A script-function definition.
#[derive(Debug, Clone)]
2020-03-11 16:43:04 +01:00
pub struct FnDef {
2020-03-18 03:36:50 +01:00
/// Function name.
2020-03-11 16:43:04 +01:00
pub name: String,
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-03-09 14:57:07 +01:00
pub body: Stmt,
2020-03-18 03:36:50 +01:00
/// Position of the function definition.
pub pos: Position,
}
2020-03-18 03:36:50 +01:00
/// `return`/`throw` statement.
2020-03-11 16:43:04 +01:00
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum ReturnType {
2020-03-18 03:36:50 +01:00
/// `return` statement.
2020-03-11 16:43:04 +01:00
Return,
2020-03-18 03:36:50 +01:00
/// `throw` statement.
2020-03-11 16:43:04 +01:00
Exception,
}
2020-03-18 03:36:50 +01:00
/// A statement.
#[derive(Debug, Clone)]
pub enum Stmt {
2020-03-18 03:36:50 +01:00
/// No-op.
2020-03-09 14:57:07 +01:00
Noop(Position),
2020-03-18 03:36:50 +01:00
/// if expr { stmt } else { stmt }
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 }
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-04-06 06:29:01 +02:00
For(Cow<'static, str>, Box<Expr>, Box<Stmt>),
2020-03-18 03:36:50 +01:00
/// let id = expr
2020-04-06 06:29:01 +02:00
Let(Cow<'static, str>, Option<Box<Expr>>, Position),
2020-03-18 03:36:50 +01:00
/// const id = expr
2020-04-06 06:29:01 +02:00
Const(Cow<'static, str>, 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 }
Expr(Box<Expr>),
2020-04-01 10:22:18 +02:00
/// continue
Continue(Position),
2020-03-18 03:36:50 +01:00
/// break
Break(Position),
2020-03-18 03:36:50 +01:00
/// `return`/`throw`
2020-03-11 16:43:04 +01:00
ReturnWithVal(Option<Box<Expr>>, ReturnType, Position),
}
2016-02-29 22:43:45 +01:00
2020-03-09 14:57:07 +01:00
impl Stmt {
2020-03-18 03:36:50 +01:00
/// Get the `Position` of this statement.
2020-03-17 10:33:37 +01:00
pub fn position(&self) -> Position {
match self {
Stmt::Noop(pos)
| Stmt::Let(_, _, pos)
| Stmt::Const(_, _, pos)
| 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-03-22 03:18:16 +01:00
Stmt::IfThenElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
2020-03-17 10:33:37 +01:00
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
}
}
2020-03-18 03:36:50 +01:00
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
pub fn is_self_terminated(&self) -> bool {
match self {
2020-03-22 03:18:16 +01:00
Stmt::IfThenElse(_, _, _)
| Stmt::While(_, _)
| Stmt::Loop(_)
| Stmt::For(_, _, _)
| Stmt::Block(_, _) => true,
2020-03-18 11:41:18 +01:00
// A No-op requires a semicolon in order to know it is an empty statement!
Stmt::Noop(_) => false,
Stmt::Let(_, _, _)
| Stmt::Const(_, _, _)
| Stmt::Expr(_)
2020-04-01 10:22:18 +02:00
| Stmt::Continue(_)
| Stmt::Break(_)
| Stmt::ReturnWithVal(_, _, _) => false,
}
}
2020-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-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.
#[derive(Debug, Clone)]
pub enum Expr {
2020-03-18 03:36:50 +01:00
/// Integer constant.
IntegerConstant(INT, Position),
2020-03-18 03:36:50 +01:00
/// Floating-point constant.
FloatConstant(FLOAT, Position),
2020-03-18 03:36:50 +01:00
/// Character constant.
CharConstant(char, Position),
2020-03-18 03:36:50 +01:00
/// String constant.
2020-04-06 06:29:01 +02:00
StringConstant(Cow<'static, str>, Position),
2020-03-18 03:36:50 +01:00
/// Variable access.
2020-04-06 06:29:01 +02:00
Variable(Cow<'static, str>, Position),
2020-03-18 03:36:50 +01:00
/// Property access.
2020-04-06 06:29:01 +02:00
Property(Cow<'static, str>, Position),
2020-03-18 03:36:50 +01:00
/// { stmt }
2020-03-09 14:57:07 +01:00
Stmt(Box<Stmt>, Position),
2020-03-18 03:36:50 +01:00
/// func(expr, ... )
2020-04-06 06:29:01 +02:00
FunctionCall(Cow<'static, str>, Vec<Expr>, Option<Dynamic>, Position),
2020-03-18 03:36:50 +01:00
/// expr = expr
Assignment(Box<Expr>, Box<Expr>, Position),
2020-03-18 03:36:50 +01:00
/// lhs.rhs
Dot(Box<Expr>, Box<Expr>, Position),
2020-03-18 03:36:50 +01:00
/// expr[expr]
Index(Box<Expr>, Box<Expr>, Position),
2020-03-18 03:36:50 +01:00
/// [ expr, ... ]
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
True(Position),
2020-03-18 03:36:50 +01:00
/// false
False(Position),
2020-03-18 03:36:50 +01:00
/// ()
Unit(Position),
}
impl Expr {
2020-03-18 03:36:50 +01:00
/// Get the `Dynamic` value of a constant expression.
///
/// # Panics
///
/// Panics when the expression is not constant.
pub fn get_constant_value(&self) -> Dynamic {
match self {
2020-04-12 17:00:06 +02:00
Self::IntegerConstant(i, _) => Dynamic::from_int(*i),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(f, _) => Dynamic::from_float(*f),
Self::CharConstant(c, _) => Dynamic::from_char(*c),
Self::StringConstant(s, _) => Dynamic::from_string(s.to_string()),
Self::True(_) => Dynamic::from_bool(true),
Self::False(_) => Dynamic::from_bool(false),
Self::Unit(_) => Dynamic::from_unit(),
Self::Array(items, _) if items.iter().all(Self::is_constant) => Dynamic(Union::Array(
items
.iter()
.map(Self::get_constant_value)
.collect::<Vec<_>>(),
)),
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
_ => panic!("cannot get value of non-constant expression"),
}
}
2020-03-18 03:36:50 +01:00
/// Get the display value of a constant expression.
///
/// # Panics
///
/// Panics when the expression is not constant.
pub fn get_constant_str(&self) -> String {
2020-03-13 11:12:41 +01:00
match self {
2020-04-06 11:47:34 +02:00
Self::IntegerConstant(i, _) => i.to_string(),
2020-04-10 06:16:39 +02:00
Self::FloatConstant(f, _) => f.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(),
_ => panic!("cannot get value of non-constant expression"),
2020-03-13 11:12:41 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Get the `Position` of the expression.
pub fn position(&self) -> Position {
match self {
2020-04-06 11:47:34 +02:00
Self::IntegerConstant(_, pos)
2020-04-10 06:16:39 +02:00
| Self::FloatConstant(_, pos)
2020-04-06 11:47:34 +02:00
| Self::CharConstant(_, pos)
| Self::StringConstant(_, pos)
2020-04-10 06:16:39 +02:00
| Self::Array(_, pos)
| Self::Map(_, pos)
2020-04-06 11:47:34 +02:00
| Self::Variable(_, pos)
| Self::Property(_, pos)
| Self::Stmt(_, pos)
| Self::FunctionCall(_, _, _, pos)
| 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-10 04:22:41 +01:00
/// Get the `Position` of the expression.
pub(crate) fn set_position(mut self, new_pos: Position) -> Self {
match &mut self {
Self::IntegerConstant(_, pos)
| Self::FloatConstant(_, pos)
| Self::CharConstant(_, pos)
| Self::StringConstant(_, pos)
| Self::Array(_, pos)
| Self::Map(_, pos)
| Self::Variable(_, pos)
| Self::Property(_, pos)
| Self::Stmt(_, pos)
| Self::FunctionCall(_, _, _, pos)
| 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-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-04-06 11:47:34 +02:00
Self::Stmt(stmt, _) => stmt.is_pure(),
2020-03-17 10:33:37 +01:00
Self::Variable(_, _) => true,
expr => expr.is_constant(),
2020-03-12 05:40:28 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Is the expression a constant?
2020-03-12 05:40:28 +01:00
pub fn is_constant(&self) -> bool {
match self {
2020-04-06 11:47:34 +02:00
Self::IntegerConstant(_, _)
2020-04-10 06:16:39 +02:00
| Self::FloatConstant(_, _)
2020-04-06 11:47:34 +02:00
| 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,
},
_ => 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 {
Expr::IntegerConstant(_, _)
| Expr::FloatConstant(_, _)
| Expr::CharConstant(_, _)
| Expr::In(_, _, _)
| Expr::And(_, _, _)
| Expr::Or(_, _, _)
| Expr::True(_)
| Expr::False(_)
| Expr::Unit(_) => false,
Expr::StringConstant(_, _)
| Expr::Stmt(_, _)
| Expr::FunctionCall(_, _, _, _)
| Expr::Assignment(_, _, _)
| Expr::Dot(_, _, _)
| Expr::Index(_, _, _)
| Expr::Array(_, _)
| Expr::Map(_, _) => match token {
Token::LeftBracket => true,
_ => false,
},
Expr::Variable(_, _) | Expr::Property(_, _) => match token {
Token::LeftBracket | Token::LeftParen => true,
_ => false,
},
}
}
}
2016-02-29 22:43:45 +01:00
/// Consume a particular token, checking that it is the expected one.
2020-04-17 13:00:52 +02:00
fn eat_token(input: &mut Peekable<TokenIterator>, token: Token) -> Position {
if let Some((t, pos)) = input.next() {
if t != token {
panic!(
"expecting {} (found {}) at {}",
token.syntax(),
t.syntax(),
pos
);
}
2020-04-17 13:00:52 +02:00
pos
} else {
panic!("expecting {} but already EOF", token.syntax());
}
}
/// Match a particular token, consuming it if matched.
fn match_token(input: &mut Peekable<TokenIterator>, token: Token) -> Result<bool, Box<ParseError>> {
let (t, _) = input.peek().unwrap();
if *t == token {
eat_token(input, token);
Ok(true)
} else {
Ok(false)
}
}
2020-03-18 03:36:50 +01:00
/// Parse ( expr )
fn parse_paren_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
begin: Position,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
if match_token(input, Token::RightParen)? {
2020-03-24 09:46:47 +01:00
return Ok(Expr::Unit(begin));
}
2020-03-22 14:03:58 +01:00
let expr = parse_expr(input, allow_stmt_expr)?;
2016-02-29 22:43:45 +01:00
match input.next().unwrap() {
2020-03-18 03:36:50 +01:00
// ( xxx )
(Token::RightParen, _) => Ok(expr),
2020-03-18 03:36:50 +01:00
// ( xxx ???
(_, pos) => Err(PERR::MissingToken(
")".into(),
"for a matching ( in this expression".into(),
)
.into_err(pos)),
2016-02-29 22:43:45 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Parse a function call.
2020-04-06 06:29:01 +02:00
fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
id: S,
2019-09-18 12:21:07 +02:00
input: &mut Peekable<TokenIterator<'a>>,
begin: Position,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
let mut args_expr_list = Vec::new();
2017-10-28 05:30:12 +02:00
match input.peek().unwrap() {
2020-04-06 06:29:01 +02:00
//id {EOF}
(Token::EOF, pos) => {
2020-04-06 06:29:01 +02:00
return Err(PERR::MissingToken(
")".into(),
format!("to close the arguments list of this function call '{}'", id),
)
.into_err(*pos))
2020-04-06 06:29:01 +02:00
}
// id()
(Token::RightParen, _) => {
eat_token(input, Token::RightParen);
2020-04-06 06:29:01 +02:00
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
}
// id...
_ => (),
2016-02-29 22:43:45 +01:00
}
loop {
2020-03-22 14:03:58 +01:00
args_expr_list.push(parse_expr(input, allow_stmt_expr)?);
2016-02-29 22:43:45 +01:00
match input.peek().unwrap() {
(Token::EOF, pos) => {
2020-04-06 06:29:01 +02:00
return Err(PERR::MissingToken(
")".into(),
format!("to close the arguments list of this function call '{}'", id),
)
.into_err(*pos))
2020-04-06 06:29:01 +02:00
}
(Token::RightParen, _) => {
eat_token(input, Token::RightParen);
2020-04-06 06:29:01 +02:00
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
}
(Token::Comma, _) => {
2020-04-17 13:00:52 +02:00
eat_token(input, Token::Comma);
}
(_, pos) => {
return Err(PERR::MissingToken(
",".into(),
format!("to separate the arguments to function call '{}'", id),
)
2020-03-24 09:46:47 +01:00
.into_err(*pos))
}
2016-02-29 22:43:45 +01:00
}
}
}
2020-03-29 17:53:35 +02:00
/// Parse an indexing expression.
2019-09-18 12:21:07 +02:00
fn parse_index_expr<'a>(
2020-03-05 13:28:03 +01:00
lhs: Box<Expr>,
2019-09-18 12:21:07 +02:00
input: &mut Peekable<TokenIterator<'a>>,
pos: Position,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2020-03-22 14:03:58 +01:00
let idx_expr = parse_expr(input, 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-03-29 17:53:35 +02:00
Expr::IntegerConstant(_, pos) => match *lhs {
Expr::Array(_, _) | Expr::StringConstant(_, _) => (),
Expr::Map(_, _) => {
return Err(PERR::MalformedIndexExpr(
"Object map access expects string index, not a number".into(),
)
.into_err(*pos))
}
Expr::FloatConstant(_, pos)
| Expr::CharConstant(_, pos)
| 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]
Expr::StringConstant(_, pos) => match *lhs {
Expr::Map(_, _) => (),
Expr::Array(_, _) | Expr::StringConstant(_, _) => {
return Err(PERR::MalformedIndexExpr(
"Array or string expects numeric index, not a string".into(),
)
.into_err(*pos))
}
Expr::FloatConstant(_, pos)
| Expr::CharConstant(_, pos)
| 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-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
match input.peek().unwrap() {
(Token::RightBracket, _) => {
eat_token(input, Token::RightBracket);
2020-03-24 09:46:47 +01:00
Ok(Expr::Index(lhs, Box::new(idx_expr), pos))
}
(_, pos) => Err(PERR::MissingToken(
"]".into(),
"for a matching [ in this index expression".into(),
)
.into_err(*pos)),
2020-03-09 03:41:17 +01:00
}
2016-03-26 18:46:28 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse an array literal.
fn parse_array_literal<'a>(
input: &mut Peekable<TokenIterator<'a>>,
begin: Position,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2016-03-26 18:46:28 +01:00
let mut arr = Vec::new();
if !match_token(input, Token::RightBracket)? {
while !input.peek().unwrap().0.is_eof() {
2020-03-22 14:03:58 +01:00
arr.push(parse_expr(input, allow_stmt_expr)?);
match input.peek().unwrap() {
(Token::EOF, pos) => {
return Err(
PERR::MissingToken("]".into(), "to end this array literal".into())
.into_err(*pos),
)
}
(Token::Comma, _) => eat_token(input, Token::Comma),
(Token::RightBracket, _) => {
eat_token(input, Token::RightBracket);
break;
}
(_, pos) => {
return Err(PERR::MissingToken(
",".into(),
2020-03-29 17:53:35 +02:00
"to separate the items of this array literal".into(),
2020-03-27 16:47:23 +01:00
)
.into_err(*pos))
}
2020-04-06 11:47:34 +02:00
};
}
2016-03-26 18:46:28 +01:00
}
Ok(Expr::Array(arr, begin))
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>>,
begin: Position,
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2020-03-29 17:53:35 +02:00
let mut map = Vec::new();
if !match_token(input, Token::RightBrace)? {
while !input.peek().unwrap().0.is_eof() {
const MISSING_RBRACE: &str = "to end this object map literal";
let (name, pos) = match input.next().unwrap() {
(Token::Identifier(s), pos) => (s, pos),
(Token::StringConst(s), pos) => (s, pos),
(_, pos) if map.is_empty() => {
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(pos))
}
(Token::EOF, pos) => {
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(pos))
2020-03-29 17:53:35 +02:00
}
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
2020-03-29 17:53:35 +02:00
};
match input.next().unwrap() {
(Token::Colon, _) => (),
(_, pos) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MissingToken(
":".into(),
format!(
"to follow the property '{}' in this object map literal",
name
),
)
.into_err(pos))
}
};
let expr = parse_expr(input, allow_stmt_expr)?;
map.push((name, expr, pos));
match input.peek().unwrap() {
(Token::Comma, _) => {
2020-04-17 13:00:52 +02:00
eat_token(input, Token::Comma);
}
(Token::RightBrace, _) => {
eat_token(input, Token::RightBrace);
break;
2020-03-29 17:53:35 +02:00
}
(Token::Identifier(_), pos) => {
2020-03-29 17:53:35 +02:00
return Err(PERR::MissingToken(
",".into(),
"to separate the items of this object map literal".into(),
)
.into_err(*pos))
}
(_, pos) => {
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(*pos))
}
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))?;
Ok(Expr::Map(map, begin))
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>>,
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
let (token, pos) = match input.peek().unwrap() {
// { - block statement as expression
(Token::LeftBrace, pos) if allow_stmt_expr => {
2020-03-16 16:51:32 +01:00
let pos = *pos;
2020-03-22 14:03:58 +01:00
return parse_block(input, false, allow_stmt_expr)
.map(|block| Expr::Stmt(Box::new(block), pos));
2020-03-07 03:39:00 +01:00
}
(Token::EOF, pos) => return Err(PERR::UnexpectedEOF.into_err(*pos)),
_ => input.next().unwrap(),
};
2020-03-05 13:28:03 +01:00
let mut root_expr = match token {
Token::IntegerConstant(x) => Expr::IntegerConstant(x, pos),
2020-04-17 14:08:41 +02:00
#[cfg(not(feature = "no_float"))]
Token::FloatConstant(x) => Expr::FloatConstant(x, pos),
Token::CharConstant(c) => Expr::CharConstant(c, pos),
Token::StringConst(s) => Expr::StringConstant(s.into(), pos),
Token::Identifier(s) => Expr::Variable(s.into(), pos),
Token::LeftParen => parse_paren_expr(input, pos, allow_stmt_expr)?,
2020-03-17 10:33:37 +01:00
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => parse_array_literal(input, pos, allow_stmt_expr)?,
2020-03-29 17:53:35 +02:00
#[cfg(not(feature = "no_object"))]
Token::MapStart => parse_map_literal(input, pos, allow_stmt_expr)?,
Token::True => Expr::True(pos),
Token::False => Expr::False(pos),
Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
token => {
2020-04-17 13:00:52 +02:00
return Err(PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(pos))
}
};
// Tail processing all possible postfix operators
loop {
let (token, _) = input.peek().unwrap();
2020-04-17 13:00:52 +02:00
if !root_expr.is_valid_postfix(token) {
break;
2020-03-24 09:46:47 +01:00
}
2020-03-05 13:28:03 +01:00
2020-04-17 13:00:52 +02:00
let (token, pos) = input.next().unwrap();
2020-04-10 06:16:39 +02:00
2020-04-17 13:00:52 +02:00
root_expr = match (root_expr, token) {
// Function call
(Expr::Variable(id, pos), Token::LeftParen)
| (Expr::Property(id, pos), Token::LeftParen) => {
parse_call_expr(id, input, pos, allow_stmt_expr)?
}
// Indexing
(expr, Token::LeftBracket) => {
parse_index_expr(Box::new(expr), input, pos, allow_stmt_expr)?
}
// Unknown postfix operator
(expr, token) => panic!("unknown postfix operator {:?} for {:?}", token, expr),
}
2016-02-29 22:43:45 +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>>,
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
match input.peek().unwrap() {
2020-03-27 16:47:23 +01:00
// If statement is allowed to act as expressions
(Token::If, pos) => {
2020-03-27 16:47:23 +01:00
let pos = *pos;
Ok(Expr::Stmt(
Box::new(parse_if(input, false, allow_stmt_expr)?),
pos,
))
}
2020-03-18 03:36:50 +01:00
// -expr
(Token::UnaryMinus, _) => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::UnaryMinus);
2020-03-22 14:03:58 +01:00
match parse_unary(input, allow_stmt_expr)? {
// Negative integer
2020-03-16 16:51:32 +01:00
Expr::IntegerConstant(i, _) => i
.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
})
.ok_or_else(|| {
PERR::BadInput(LexError::MalformedNumber(format!("-{}", i)).to_string())
2020-03-24 09:46:47 +01:00
.into_err(pos)
}),
// Negative float
#[cfg(not(feature = "no_float"))]
2020-03-16 16:51:32 +01:00
Expr::FloatConstant(f, pos) => Ok(Expr::FloatConstant(-f, pos)),
// Call negative function
2020-03-16 16:51:32 +01:00
expr => Ok(Expr::FunctionCall("-".into(), vec![expr], None, pos)),
}
2019-09-18 12:21:07 +02:00
}
2020-03-18 03:36:50 +01:00
// +expr
(Token::UnaryPlus, _) => {
eat_token(input, Token::UnaryPlus);
2020-03-22 14:03:58 +01:00
parse_unary(input, allow_stmt_expr)
2019-09-18 12:21:07 +02:00
}
2020-03-18 03:36:50 +01:00
// !expr
(Token::Bang, _) => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::Bang);
Ok(Expr::FunctionCall(
"!".into(),
2020-03-22 14:03:58 +01:00
vec![parse_primary(input, allow_stmt_expr)?],
2020-04-12 17:00:06 +02:00
Some(Dynamic::from_bool(false)), // NOT operator, when operating on invalid operand, defaults to false
pos,
))
2019-09-18 12:21:07 +02:00
}
// {EOF}
(Token::EOF, pos) => Err(PERR::UnexpectedEOF.into_err(*pos)),
// All other tokens
_ => parse_primary(input, allow_stmt_expr),
2017-10-30 16:08:44 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Parse an assignment.
fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// Is the LHS in a valid format for an assignment target?
fn valid_assignment_chain(expr: &Expr, is_top: bool) -> Option<Box<ParseError>> {
match expr {
2020-03-18 03:36:50 +01:00
// var
2020-03-13 11:12:41 +01:00
Expr::Variable(_, _) => {
assert!(is_top, "property expected but gets variable");
None
}
2020-03-18 03:36:50 +01:00
// property
2020-03-13 11:12:41 +01:00
Expr::Property(_, _) => {
assert!(!is_top, "variable expected but gets property");
None
}
2020-03-18 03:36:50 +01:00
// idx_lhs[...]
2020-03-24 09:46:47 +01:00
Expr::Index(idx_lhs, _, pos) => match idx_lhs.as_ref() {
// var[...]
Expr::Variable(_, _) => {
assert!(is_top, "property expected but gets variable");
None
}
// property[...]
Expr::Property(_, _) => {
assert!(!is_top, "variable expected but gets property");
None
}
// ???[...][...]
2020-03-24 09:46:47 +01:00
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
// idx_lhs[...]
2020-03-24 09:46:47 +01:00
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(*pos)),
},
2020-03-18 03:36:50 +01:00
// dot_lhs.dot_rhs
Expr::Dot(dot_lhs, dot_rhs, pos) => match dot_lhs.as_ref() {
2020-03-18 03:36:50 +01:00
// var.dot_rhs
2020-03-13 11:12:41 +01:00
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
2020-03-18 03:36:50 +01:00
// property.dot_rhs
2020-03-13 11:12:41 +01:00
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
// idx_lhs[...].dot_rhs
Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() {
// var[...].dot_rhs
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
// property[...].dot_rhs
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
// ???[...][...].dot_rhs
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
// idx_lhs[...].dot_rhs
_ => Some(ParseErrorType::AssignmentToCopy.into_err(idx_lhs.position())),
},
2020-03-13 11:12:41 +01:00
expr => panic!("unexpected dot expression {:#?}", expr),
},
2020-03-24 09:46:47 +01:00
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(expr.position())),
2020-03-07 03:15:42 +01:00
}
}
2020-03-07 03:15:42 +01:00
2020-03-13 11:12:41 +01:00
match valid_assignment_chain(&lhs, true) {
None => Ok(Expr::Assignment(Box::new(lhs), Box::new(rhs), pos)),
Some(err) => Err(err),
}
2020-03-07 03:15:42 +01:00
}
2020-03-18 03:36:50 +01:00
/// Parse an operator-assignment expression.
2020-04-06 06:29:01 +02:00
fn parse_op_assignment<S: Into<Cow<'static, str>>>(
op: S,
lhs: Expr,
rhs: Expr,
pos: Position,
) -> Result<Expr, Box<ParseError>> {
2020-03-07 06:39:28 +01:00
let lhs_copy = lhs.clone();
2020-03-18 11:41:18 +01:00
// lhs op= rhs -> lhs = op(lhs, rhs)
2020-03-07 06:39:28 +01:00
parse_assignment(
lhs,
2020-03-18 11:41:18 +01:00
Expr::FunctionCall(op.into(), vec![lhs_copy, rhs], None, pos),
2020-03-07 06:39:28 +01:00
pos,
)
}
2020-04-06 11:47:34 +02:00
/// Parse an 'in' expression.
fn parse_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))
2020-04-10 06:16:39 +02:00
| (_, Expr::FloatConstant(_, pos))
2020-04-06 11:47:34 +02:00
| (_, 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))
}
// "xxx" in "xxxx", 'x' in "xxxx" - OK!
(Expr::StringConstant(_, _), Expr::StringConstant(_, _))
| (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (),
// 123.456 in "xxxx"
(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 #{...}
(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.
fn parse_binary_op<'a>(
2019-09-18 12:21:07 +02:00
input: &mut Peekable<TokenIterator<'a>>,
2020-03-14 04:51:45 +01:00
parent_precedence: u8,
2019-09-18 12:21:07 +02:00
lhs: Expr,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
let mut current_lhs = lhs;
2016-02-29 22:43:45 +01:00
loop {
2020-04-11 12:09:03 +02:00
let (current_precedence, bind_right) = input.peek().map_or_else(
|| (0, false),
|(current_op, _)| (current_op.precedence(), current_op.is_bind_right()),
);
2020-03-14 04:51:45 +01:00
// Bind left to the parent lhs expression if precedence is higher
// If same precedence, then check if the operator binds right
if current_precedence < parent_precedence
|| (current_precedence == parent_precedence && !bind_right)
{
return Ok(current_lhs);
2016-02-29 22:43:45 +01:00
}
2020-02-29 13:12:10 +01:00
if let Some((op_token, pos)) = input.next() {
2020-03-22 14:03:58 +01:00
let rhs = parse_unary(input, allow_stmt_expr)?;
2020-03-16 16:51:32 +01:00
let next_precedence = if let Some((next_op, _)) = input.peek() {
next_op.precedence()
2020-03-14 04:51:45 +01:00
} else {
0
};
2016-02-29 22:43:45 +01:00
2020-03-14 04:51:45 +01:00
// Bind to right if the next operator has higher precedence
// If same precedence, then check if the operator binds right
let rhs = if (current_precedence == next_precedence && bind_right)
|| current_precedence < next_precedence
{
2020-03-22 14:03:58 +01:00
parse_binary_op(input, current_precedence, rhs, allow_stmt_expr)?
2020-03-14 04:51:45 +01:00
} else {
// Otherwise bind to left (even if next operator has the same precedence)
rhs
};
2016-02-29 22:43:45 +01:00
current_lhs = match op_token {
Token::Plus => Expr::FunctionCall("+".into(), vec![current_lhs, rhs], None, pos),
Token::Minus => Expr::FunctionCall("-".into(), vec![current_lhs, rhs], None, pos),
Token::Multiply => {
Expr::FunctionCall("*".into(), vec![current_lhs, rhs], None, pos)
}
Token::Divide => Expr::FunctionCall("/".into(), vec![current_lhs, rhs], None, pos),
Token::Equals => parse_assignment(current_lhs, rhs, pos)?,
2020-03-07 06:39:28 +01:00
Token::PlusAssign => parse_op_assignment("+", current_lhs, rhs, pos)?,
Token::MinusAssign => parse_op_assignment("-", current_lhs, rhs, pos)?,
#[cfg(not(feature = "no_object"))]
2020-03-13 11:12:41 +01:00
Token::Period => {
fn check_property(expr: Expr) -> Result<Expr, Box<ParseError>> {
2020-03-13 11:12:41 +01:00
match expr {
// xxx.lhs.rhs
Expr::Dot(lhs, rhs, pos) => Ok(Expr::Dot(
Box::new(check_property(*lhs)?),
Box::new(check_property(*rhs)?),
2020-03-13 11:12:41 +01:00
pos,
)),
// xxx.lhs[idx]
2020-03-13 11:12:41 +01:00
Expr::Index(lhs, idx, pos) => {
Ok(Expr::Index(Box::new(check_property(*lhs)?), idx, pos))
2020-03-13 11:12:41 +01:00
}
// xxx.id
Expr::Variable(id, pos) => Ok(Expr::Property(id, pos)),
// xxx.prop
expr @ Expr::Property(_, _) => Ok(expr),
// xxx.fn()
expr @ Expr::FunctionCall(_, _, _, _) => Ok(expr),
expr => Err(PERR::PropertyExpected.into_err(expr.position())),
2020-03-13 11:12:41 +01:00
}
}
Expr::Dot(Box::new(current_lhs), Box::new(check_property(rhs)?), pos)
2020-03-13 11:12:41 +01:00
}
// Comparison operators default to false when passed invalid operands
Token::EqualsTo => Expr::FunctionCall(
"==".into(),
vec![current_lhs, rhs],
2020-04-12 17:00:06 +02:00
Some(Dynamic::from_bool(false)),
pos,
),
Token::NotEqualsTo => Expr::FunctionCall(
"!=".into(),
vec![current_lhs, rhs],
2020-04-12 17:00:06 +02:00
Some(Dynamic::from_bool(false)),
pos,
),
Token::LessThan => Expr::FunctionCall(
"<".into(),
vec![current_lhs, rhs],
2020-04-12 17:00:06 +02:00
Some(Dynamic::from_bool(false)),
pos,
),
Token::LessThanEqualsTo => Expr::FunctionCall(
"<=".into(),
vec![current_lhs, rhs],
2020-04-12 17:00:06 +02:00
Some(Dynamic::from_bool(false)),
pos,
),
Token::GreaterThan => Expr::FunctionCall(
">".into(),
vec![current_lhs, rhs],
2020-04-12 17:00:06 +02:00
Some(Dynamic::from_bool(false)),
pos,
),
Token::GreaterThanEqualsTo => Expr::FunctionCall(
">=".into(),
vec![current_lhs, rhs],
2020-04-12 17:00:06 +02:00
Some(Dynamic::from_bool(false)),
pos,
),
2020-04-06 11:47:34 +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),
Token::In => parse_in_expr(current_lhs, rhs, pos)?,
Token::XOr => Expr::FunctionCall("^".into(), vec![current_lhs, rhs], None, pos),
2020-03-07 06:39:28 +01:00
Token::OrAssign => parse_op_assignment("|", current_lhs, rhs, pos)?,
Token::AndAssign => parse_op_assignment("&", current_lhs, rhs, pos)?,
Token::XOrAssign => parse_op_assignment("^", current_lhs, rhs, pos)?,
Token::MultiplyAssign => parse_op_assignment("*", current_lhs, rhs, pos)?,
Token::DivideAssign => parse_op_assignment("/", current_lhs, rhs, pos)?,
Token::Pipe => Expr::FunctionCall("|".into(), vec![current_lhs, rhs], None, pos),
Token::LeftShift => {
Expr::FunctionCall("<<".into(), vec![current_lhs, rhs], None, pos)
}
Token::RightShift => {
Expr::FunctionCall(">>".into(), vec![current_lhs, rhs], None, pos)
}
2020-03-07 06:39:28 +01:00
Token::LeftShiftAssign => parse_op_assignment("<<", current_lhs, rhs, pos)?,
Token::RightShiftAssign => parse_op_assignment(">>", current_lhs, rhs, pos)?,
Token::Ampersand => {
Expr::FunctionCall("&".into(), vec![current_lhs, rhs], None, pos)
}
Token::Modulo => Expr::FunctionCall("%".into(), vec![current_lhs, rhs], None, pos),
2020-03-07 06:39:28 +01:00
Token::ModuloAssign => parse_op_assignment("%", current_lhs, rhs, pos)?,
Token::PowerOf => Expr::FunctionCall("~".into(), vec![current_lhs, rhs], None, pos),
2020-03-07 06:39:28 +01:00
Token::PowerOfAssign => parse_op_assignment("~", current_lhs, rhs, pos)?,
2020-03-24 09:46:47 +01:00
token => return Err(PERR::UnknownOperator(token.syntax().into()).into_err(pos)),
2016-02-29 22:43:45 +01:00
};
}
}
}
2020-03-18 03:36:50 +01:00
/// Parse an expression.
2020-03-22 14:03:58 +01:00
fn parse_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
2020-03-27 16:47:23 +01:00
// Parse a real expression
2020-03-22 14:03:58 +01:00
let lhs = parse_unary(input, allow_stmt_expr)?;
parse_binary_op(input, 1, lhs, allow_stmt_expr)
2016-02-29 22:43:45 +01:00
}
/// Make sure that the expression is not a statement expression (i.e. wrapped in {})
fn ensure_not_statement_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
type_name: &str,
) -> Result<(), Box<ParseError>> {
match input.peek().unwrap() {
// Disallow statement expressions
(Token::LeftBrace, pos) | (Token::EOF, pos) => {
Err(PERR::ExprExpected(type_name.to_string()).into_err(*pos))
}
// No need to check for others at this time - leave it for the expr parser
_ => Ok(()),
}
}
2020-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>>,
breakable: bool,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// if ...
eat_token(input, Token::If);
2016-02-29 22:43:45 +01:00
// if guard { if_body }
ensure_not_statement_expr(input, "a boolean")?;
2020-03-22 14:03:58 +01:00
let guard = parse_expr(input, allow_stmt_expr)?;
let if_body = parse_block(input, breakable, allow_stmt_expr)?;
2016-02-29 22:43:45 +01:00
// if guard { if_body } else ...
let else_body = if match_token(input, Token::Else).unwrap_or(false) {
2020-03-16 16:51:32 +01:00
Some(Box::new(if matches!(input.peek(), Some((Token::If, _))) {
// if guard { if_body } else if ...
2020-03-22 14:03:58 +01:00
parse_if(input, breakable, allow_stmt_expr)?
2020-03-16 16:51:32 +01:00
} else {
// if guard { if_body } else { else-body }
2020-03-22 14:03:58 +01:00
parse_block(input, 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>>,
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// while ...
eat_token(input, Token::While);
2016-02-29 22:43:45 +01:00
2020-03-18 11:41:18 +01:00
// while guard { body }
ensure_not_statement_expr(input, "a boolean")?;
2020-03-22 14:03:58 +01:00
let guard = parse_expr(input, allow_stmt_expr)?;
let body = parse_block(input, 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>>,
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// loop ...
eat_token(input, Token::Loop);
2017-10-30 16:08:44 +01:00
2020-03-18 11:41:18 +01:00
// loop { body }
2020-03-22 14:03:58 +01:00
let body = parse_block(input, 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>>,
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// for ...
eat_token(input, Token::For);
2020-03-18 11:41:18 +01:00
// for name ...
let name = match input.next().unwrap() {
2020-03-18 11:41:18 +01:00
// Variable name
2020-03-16 16:51:32 +01:00
(Token::Identifier(s), _) => s,
2020-03-18 11:41:18 +01:00
// Bad identifier
2020-03-24 09:46:47 +01:00
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
// EOF
(Token::EOF, pos) => return Err(PERR::VariableExpected.into_err(pos)),
2020-03-18 11:41:18 +01:00
// Not a variable name
2020-03-24 09:46:47 +01:00
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
};
2020-03-18 11:41:18 +01:00
// for name in ...
match input.next().unwrap() {
(Token::In, _) => (),
(_, pos) => {
return Err(
PERR::MissingToken("in".into(), "after the iteration variable".into())
.into_err(pos),
)
}
}
2020-03-18 11:41:18 +01:00
// for name in expr { body }
ensure_not_statement_expr(input, "a boolean")?;
2020-03-22 14:03:58 +01:00
let expr = parse_expr(input, allow_stmt_expr)?;
let body = parse_block(input, true, allow_stmt_expr)?;
2020-04-06 06:29:01 +02:00
Ok(Stmt::For(name.into(), Box::new(expr), Box::new(body)))
}
2020-03-18 03:36:50 +01:00
/// Parse a variable definition statement.
fn parse_let<'a>(
2020-03-13 11:12:41 +01:00
input: &mut Peekable<TokenIterator<'a>>,
2020-03-25 04:27:18 +01:00
var_type: ScopeEntryType,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// let/const... (specified in `var_type`)
input.next();
2020-03-16 16:51:32 +01:00
2020-03-18 11:41:18 +01:00
// let name ...
let (name, pos) = match input.next().unwrap() {
2020-03-18 11:41:18 +01:00
(Token::Identifier(s), pos) => (s, pos),
2020-03-24 09:46:47 +01:00
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
2016-02-29 22:43:45 +01:00
};
2020-03-18 11:41:18 +01:00
// let name = ...
if match_token(input, Token::Equals)? {
2020-03-18 11:41:18 +01:00
// let name = expr
2020-03-22 14:03:58 +01:00
let init_value = parse_expr(input, 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-06 06:29:01 +02:00
ScopeEntryType::Normal => Ok(Stmt::Let(name.into(), Some(Box::new(init_value)), pos)),
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-04-06 06:29:01 +02:00
Ok(Stmt::Const(name.into(), 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()))
}
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-04-06 06:29:01 +02:00
Ok(Stmt::Let(name.into(), None, pos))
2016-02-29 22:43:45 +01:00
}
}
2020-03-18 03:36:50 +01:00
/// Parse a statement block.
2020-03-17 10:33:37 +01:00
fn parse_block<'a>(
input: &mut Peekable<TokenIterator<'a>>,
breakable: bool,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-18 11:41:18 +01:00
// Must start with {
let pos = match input.next().unwrap() {
2020-03-16 16:51:32 +01:00
(Token::LeftBrace, pos) => pos,
(_, pos) => {
return Err(
PERR::MissingToken("{".into(), "to start a statement block".into()).into_err(pos),
)
}
2020-03-12 05:40:28 +01:00
};
2016-02-29 22:43:45 +01:00
let mut statements = Vec::new();
while !match_token(input, Token::RightBrace)? {
2020-03-17 10:33:37 +01:00
// Parse statements inside the block
2020-03-22 14:03:58 +01:00
let stmt = parse_stmt(input, breakable, allow_stmt_expr)?;
2020-03-17 10:33:37 +01:00
// See if it needs a terminating semicolon
let need_semicolon = !stmt.is_self_terminated();
2017-10-02 23:44:45 +02:00
2020-03-17 10:33:37 +01:00
statements.push(stmt);
match input.peek().unwrap() {
2020-03-18 11:41:18 +01:00
// { ... stmt }
(Token::RightBrace, _) => {
eat_token(input, Token::RightBrace);
break;
}
2020-03-18 11:41:18 +01:00
// { ... stmt;
(Token::SemiColon, _) if need_semicolon => {
eat_token(input, Token::SemiColon);
2020-03-17 10:33:37 +01:00
}
2020-03-18 11:41:18 +01:00
// { ... { stmt } ;
(Token::SemiColon, _) if !need_semicolon => (),
2020-03-18 11:41:18 +01:00
// { ... { stmt } ???
(_, _) if !need_semicolon => (),
2020-03-18 11:41:18 +01:00
// { ... stmt ??? - error
(_, pos) => {
2020-03-17 10:33:37 +01:00
// Semicolons are not optional between statements
return Err(
PERR::MissingToken(";".into(), "to terminate this statement".into())
.into_err(*pos),
);
2019-09-18 12:21:07 +02:00
}
}
}
2016-02-29 22:43:45 +01:00
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>>,
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
2020-03-22 14:03:58 +01:00
Ok(Stmt::Expr(Box::new(parse_expr(input, allow_stmt_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>>,
breakable: bool,
2020-03-22 14:03:58 +01:00
allow_stmt_expr: bool,
) -> Result<Stmt, Box<ParseError>> {
let (token, pos) = match input.peek().unwrap() {
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
x => x,
};
match token {
2020-03-18 11:41:18 +01:00
// Semicolon - empty statement
Token::SemiColon => Ok(Stmt::Noop(*pos)),
2020-03-18 11:41:18 +01:00
Token::LeftBrace => parse_block(input, breakable, allow_stmt_expr),
2020-04-01 10:22:18 +02:00
2020-03-18 11:41:18 +01:00
// fn ...
#[cfg(not(feature = "no_function"))]
Token::Fn => Err(PERR::WrongFnDefinition.into_err(*pos)),
Token::If => parse_if(input, breakable, allow_stmt_expr),
Token::While => parse_while(input, allow_stmt_expr),
Token::Loop => parse_loop(input, allow_stmt_expr),
Token::For => parse_for(input, allow_stmt_expr),
2020-04-01 10:22:18 +02:00
Token::Continue if breakable => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::Continue);
2020-04-01 10:22:18 +02:00
Ok(Stmt::Continue(pos))
}
Token::Break if breakable => {
2020-04-17 13:00:52 +02:00
let pos = eat_token(input, Token::Break);
Ok(Stmt::Break(pos))
}
Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(*pos)),
2020-04-01 10:22:18 +02:00
Token::Return | Token::Throw => {
let pos = *pos;
let return_type = match input.next() {
Some((Token::Return, _)) => ReturnType::Return,
Some((Token::Throw, _)) => ReturnType::Exception,
_ => panic!("token should be return or throw"),
2020-03-03 11:15:20 +01:00
};
match input.peek().unwrap() {
// `return`/`throw` at {EOF}
(Token::EOF, pos) => Ok(Stmt::ReturnWithVal(None, return_type, *pos)),
2020-03-18 03:36:50 +01:00
// `return;` or `throw;`
(Token::SemiColon, _) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
2020-03-18 03:36:50 +01:00
// `return` or `throw` with expression
(_, _) => {
2020-03-22 14:03:58 +01:00
let expr = parse_expr(input, 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))
}
}
}
2020-04-01 10:22:18 +02:00
Token::Let => parse_let(input, ScopeEntryType::Normal, allow_stmt_expr),
Token::Const => parse_let(input, ScopeEntryType::Constant, allow_stmt_expr),
2020-04-01 10:22:18 +02:00
2020-03-22 14:03:58 +01:00
_ => parse_expr_stmt(input, 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>>,
allow_stmt_expr: bool,
) -> Result<FnDef, Box<ParseError>> {
let pos = input.next().expect("should be fn").1;
2020-03-16 16:51:32 +01:00
let name = match input.next().unwrap() {
(Token::Identifier(s), _) => s,
(_, pos) => return Err(PERR::FnMissingName.into_err(pos)),
};
match input.peek().unwrap() {
(Token::LeftParen, _) => eat_token(input, Token::LeftParen),
(_, pos) => return Err(PERR::FnMissingParams(name).into_err(*pos)),
2020-04-06 11:47:34 +02:00
};
let mut params = Vec::new();
if !match_token(input, Token::RightParen)? {
let end_err = format!("to close the parameters list of function '{}'", name);
let sep_err = format!("to separate the parameters of function '{}'", name);
2020-03-24 09:46:47 +01:00
2020-03-14 16:41:15 +01:00
loop {
match input.next().unwrap() {
(Token::Identifier(s), pos) => params.push((s, pos)),
(_, pos) => return Err(PERR::MissingToken(")".into(), end_err).into_err(pos)),
2020-03-16 16:51:32 +01:00
}
match input.next().unwrap() {
(Token::RightParen, _) => break,
(Token::Comma, _) => (),
(Token::Identifier(_), pos) => {
return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos))
}
(_, pos) => return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos)),
}
2020-03-14 16:41:15 +01:00
}
2016-02-29 22:43:45 +01:00
}
// Check for duplicating parameters
params
.iter()
.enumerate()
.try_for_each(|(i, (p1, _))| {
params
.iter()
.skip(i + 1)
.find(|(p2, _)| p2 == p1)
.map_or_else(|| Ok(()), |(p2, pos)| Err((p2, *pos)))
})
.map_err(|(p, pos)| {
PERR::FnDuplicatedParam(name.to_string(), p.to_string()).into_err(pos)
})?;
// Parse function body
let body = match input.peek().unwrap() {
(Token::LeftBrace, _) => parse_block(input, false, allow_stmt_expr)?,
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
2020-03-17 10:33:37 +01:00
};
2020-04-06 11:47:34 +02:00
let params = params.into_iter().map(|(p, _)| p).collect();
Ok(FnDef {
2020-03-11 16:43:04 +01:00
name,
2020-04-06 11:47:34 +02:00
params,
2020-03-09 14:57:07 +01:00
body,
pos,
})
}
2020-04-16 17:31:48 +02:00
pub fn parse_global_expr<'a>(
2020-03-22 14:03:58 +01:00
input: &mut Peekable<TokenIterator<'a>>,
2020-04-16 17:31:48 +02:00
engine: &Engine,
2020-03-22 14:03:58 +01:00
scope: &Scope,
2020-04-10 11:14:07 +02:00
optimization_level: OptimizationLevel,
) -> Result<AST, Box<ParseError>> {
2020-03-22 14:03:58 +01:00
let expr = parse_expr(input, false)?;
match input.peek().unwrap() {
(Token::EOF, _) => (),
2020-03-22 14:03:58 +01:00
// Return error if the expression doesn't end
(token, pos) => {
return Err(PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(*pos))
}
2020-03-22 14:03:58 +01:00
}
Ok(
// Optimize AST
optimize_into_ast(
engine,
scope,
vec![Stmt::Expr(Box::new(expr))],
vec![],
2020-04-10 11:14:07 +02:00
optimization_level,
),
2020-03-22 14:03:58 +01:00
)
}
2020-03-18 03:36:50 +01:00
/// Parse the global level statements.
2020-03-22 14:03:58 +01:00
fn parse_global_level<'a>(
2020-03-09 14:57:07 +01:00
input: &mut Peekable<TokenIterator<'a>>,
) -> Result<(Vec<Stmt>, HashMap<u64, FnDef>), Box<ParseError>> {
let mut statements = Vec::<Stmt>::new();
2020-04-16 17:58:57 +02:00
let mut functions = HashMap::<u64, FnDef>::new();
while !input.peek().unwrap().0.is_eof() {
2020-04-10 06:16:39 +02:00
// Collect all the function definitions
#[cfg(not(feature = "no_function"))]
{
if matches!(input.peek().expect("should not be None"), (Token::Fn, _)) {
2020-03-22 14:03:58 +01:00
let f = parse_fn(input, true)?;
2020-04-16 17:58:57 +02:00
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
continue;
2020-03-12 06:02:13 +01:00
}
2016-02-29 22:43:45 +01:00
}
2020-03-18 11:41:18 +01:00
// Actual statement
2020-03-22 14:03:58 +01:00
let stmt = parse_stmt(input, false, true)?;
let need_semicolon = !stmt.is_self_terminated();
statements.push(stmt);
match input.peek().unwrap() {
2020-03-18 11:41:18 +01:00
// EOF
(Token::EOF, _) => break,
2020-03-18 11:41:18 +01:00
// stmt ;
(Token::SemiColon, _) if need_semicolon => {
eat_token(input, Token::SemiColon);
}
2020-03-18 11:41:18 +01:00
// stmt ;
(Token::SemiColon, _) if !need_semicolon => (),
2020-03-18 11:41:18 +01:00
// { stmt } ???
(_, _) if !need_semicolon => (),
2020-03-18 11:41:18 +01:00
// stmt ??? - error
(_, pos) => {
// Semicolons are not optional between statements
return Err(
PERR::MissingToken(";".into(), "to terminate this statement".into())
.into_err(*pos),
);
}
2016-02-29 22:43:45 +01:00
}
}
Ok((statements, functions))
}
2020-03-18 03:36:50 +01:00
/// Run the parser on an input stream, returning an AST.
2020-04-16 17:31:48 +02:00
pub fn parse<'a>(
input: &mut Peekable<TokenIterator<'a>>,
2020-04-16 17:31:48 +02:00
engine: &Engine,
scope: &Scope,
2020-04-10 06:16:39 +02:00
optimization_level: OptimizationLevel,
) -> Result<AST, Box<ParseError>> {
let (statements, functions) = parse_global_level(input)?;
2020-04-16 17:58:57 +02:00
let fn_lib = functions.into_iter().map(|(_, v)| v).collect();
Ok(
2020-03-18 11:41:18 +01:00
// Optimize AST
2020-04-16 17:58:57 +02:00
optimize_into_ast(engine, scope, statements, fn_lib, optimization_level),
)
2016-02-29 22:43:45 +01:00
}
2020-03-18 03:36:50 +01:00
/// Map a `Dynamic` value to an expression.
///
/// Returns Some(expression) if conversion is successful. Otherwise None.
2020-04-05 11:44:48 +02:00
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
2020-04-12 17:00:06 +02:00
match value.0 {
Union::Unit(_) => Some(Expr::Unit(pos)),
Union::Int(value) => Some(Expr::IntegerConstant(value, pos)),
Union::Char(value) => Some(Expr::CharConstant(value, pos)),
Union::Str(value) => Some(Expr::StringConstant(value.into(), pos)),
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
}
}
#[cfg(not(feature = "no_float"))]
2020-04-12 17:00:06 +02:00
Union::Float(value) => Some(Expr::FloatConstant(value, pos)),
2020-04-12 17:00:06 +02:00
_ => None,
}
2016-02-29 22:43:45 +01:00
}