2020-03-08 12:54:02 +01:00
|
|
|
//! Main module defining the lexer and parser.
|
|
|
|
|
2020-04-12 17:00:06 +02:00
|
|
|
use crate::any::{Dynamic, Union};
|
|
|
|
use crate::engine::{Engine, 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-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
|
|
|
fmt,
|
|
|
|
fmt::Display,
|
|
|
|
format,
|
2020-03-17 19:26:11 +01:00
|
|
|
iter::Peekable,
|
2020-03-27 04:50:24 +01:00
|
|
|
ops::Add,
|
2020-04-04 16:00:44 +02:00
|
|
|
rc::Rc,
|
2020-03-17 19:26:11 +01:00
|
|
|
str::Chars,
|
|
|
|
str::FromStr,
|
|
|
|
string::{String, ToString},
|
|
|
|
sync::Arc,
|
|
|
|
usize, vec,
|
|
|
|
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-03-10 16:06:20 +01:00
|
|
|
pub type FLOAT = f64;
|
|
|
|
|
2020-02-24 16:25:52 +01:00
|
|
|
type LERR = LexError;
|
2020-03-01 06:30:22 +01:00
|
|
|
type PERR = ParseErrorType;
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// A location (line number + character position) in the input script.
|
2020-03-03 06:30:46 +01:00
|
|
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
2020-02-29 13:12:10 +01:00
|
|
|
pub struct Position {
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Line number - 0 = none, MAX = EOF
|
2020-03-04 02:33:35 +01:00
|
|
|
line: usize,
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Character position - 0 = BOL, MAX = EOF
|
2020-03-04 02:33:35 +01:00
|
|
|
pos: usize,
|
2020-02-29 13:12:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Position {
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Create a new `Position`.
|
|
|
|
pub fn new(line: usize, position: usize) -> Self {
|
2020-03-16 16:51:32 +01:00
|
|
|
assert!(line != 0, "line cannot be zero");
|
|
|
|
assert!(
|
|
|
|
line != usize::MAX || position != usize::MAX,
|
|
|
|
"invalid position"
|
|
|
|
);
|
2020-03-08 15:47:13 +01:00
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
Self {
|
|
|
|
line,
|
|
|
|
pos: position,
|
|
|
|
}
|
2020-02-29 13:12:10 +01:00
|
|
|
}
|
2020-03-03 06:30:46 +01:00
|
|
|
|
2020-03-08 15:47:13 +01:00
|
|
|
/// Get the line number (1-based), or `None` if no position or EOF.
|
2020-03-04 15:00:01 +01:00
|
|
|
pub fn line(&self) -> Option<usize> {
|
2020-03-08 15:47:13 +01:00
|
|
|
if self.is_none() || self.is_eof() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(self.line)
|
2020-03-04 15:00:01 +01:00
|
|
|
}
|
2020-02-29 14:10:31 +01:00
|
|
|
}
|
2020-03-03 06:30:46 +01:00
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Get the character position (1-based), or `None` if at beginning of a line.
|
|
|
|
pub fn position(&self) -> Option<usize> {
|
2020-03-24 09:46:47 +01:00
|
|
|
if self.is_none() || self.is_eof() || self.pos == 0 {
|
2020-03-08 15:47:13 +01:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(self.pos)
|
2020-03-04 15:00:01 +01:00
|
|
|
}
|
2020-03-03 06:30:46 +01:00
|
|
|
}
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Advance by one character position.
|
2020-03-04 02:33:35 +01:00
|
|
|
pub(crate) fn advance(&mut self) {
|
|
|
|
self.pos += 1;
|
2020-02-29 13:12:10 +01:00
|
|
|
}
|
2020-03-03 06:30:46 +01:00
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Go backwards by one character position.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if already at beginning of a line - cannot rewind to a previous line.
|
|
|
|
///
|
2020-03-04 02:33:35 +01:00
|
|
|
pub(crate) fn rewind(&mut self) {
|
2020-03-04 16:06:05 +01:00
|
|
|
assert!(self.pos > 0, "cannot rewind at position 0");
|
|
|
|
self.pos -= 1;
|
2020-03-03 06:30:46 +01:00
|
|
|
}
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Advance to the next line.
|
2020-03-04 02:33:35 +01:00
|
|
|
pub(crate) fn new_line(&mut self) {
|
|
|
|
self.line += 1;
|
|
|
|
self.pos = 0;
|
2020-03-03 06:30:46 +01:00
|
|
|
}
|
|
|
|
|
2020-03-08 15:47:13 +01:00
|
|
|
/// Create a `Position` representing no position.
|
|
|
|
pub(crate) fn none() -> Self {
|
|
|
|
Self { line: 0, pos: 0 }
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Create a `Position` at EOF.
|
|
|
|
pub(crate) fn eof() -> Self {
|
2020-03-08 15:47:13 +01:00
|
|
|
Self {
|
|
|
|
line: usize::MAX,
|
|
|
|
pos: usize::MAX,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Is there no `Position`?
|
|
|
|
pub fn is_none(&self) -> bool {
|
|
|
|
self.line == 0 && self.pos == 0
|
2020-02-29 13:12:10 +01:00
|
|
|
}
|
2020-03-03 06:30:46 +01:00
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Is the `Position` at EOF?
|
2020-03-02 15:13:14 +01:00
|
|
|
pub fn is_eof(&self) -> bool {
|
2020-03-08 15:47:13 +01:00
|
|
|
self.line == usize::MAX && self.pos == usize::MAX
|
2020-03-02 15:13:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
impl Default for Position {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new(1, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 12:54:02 +01:00
|
|
|
impl fmt::Display for Position {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2020-03-03 06:30:46 +01:00
|
|
|
if self.is_eof() {
|
|
|
|
write!(f, "EOF")
|
2020-03-08 15:47:13 +01:00
|
|
|
} else if self.is_none() {
|
|
|
|
write!(f, "none")
|
2020-03-03 06:30:46 +01:00
|
|
|
} else {
|
|
|
|
write!(f, "line {}, position {}", self.line, self.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 12:54:02 +01:00
|
|
|
impl fmt::Debug for Position {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2020-03-03 06:30:46 +01:00
|
|
|
if self.is_eof() {
|
|
|
|
write!(f, "(EOF)")
|
|
|
|
} else {
|
|
|
|
write!(f, "({}:{})", self.line, self.pos)
|
2020-03-02 15:13:14 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-29 13:12:10 +01:00
|
|
|
}
|
|
|
|
|
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)]
|
2020-04-04 16:00:44 +02:00
|
|
|
pub struct AST(
|
|
|
|
pub(crate) Vec<Stmt>,
|
|
|
|
#[cfg(feature = "sync")] pub(crate) Arc<FunctionsLib>,
|
|
|
|
#[cfg(not(feature = "sync"))] pub(crate) Rc<FunctionsLib>,
|
|
|
|
);
|
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`.
|
|
|
|
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;
|
|
|
|
///
|
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-04-04 16:00:44 +02: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
|
|
|
|
2020-04-04 16:00:44 +02: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
|
|
|
}
|
2020-04-04 16:00:44 +02:00
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
{
|
|
|
|
self.1 = Rc::new(FunctionsLib::new());
|
|
|
|
}
|
|
|
|
}
|
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.
|
|
|
|
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 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.
|
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-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.
|
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-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-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 }
|
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-03-18 03:36:50 +01:00
|
|
|
/// `return`/`throw`
|
2020-03-11 16:43:04 +01:00
|
|
|
ReturnWithVal(Option<Box<Expr>>, ReturnType, 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)
|
|
|
|
| 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)?
|
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(_, _, _)
|
|
|
|
| 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,
|
|
|
|
|
2020-03-17 07:29:22 +01:00
|
|
|
Stmt::Let(_, _, _)
|
|
|
|
| Stmt::Const(_, _, _)
|
|
|
|
| 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-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-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-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
|
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-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
|
|
|
|
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-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(),
|
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-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-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 {
|
|
|
|
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-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-04-06 11:47:34 +02:00
|
|
|
expr => expr.is_constant() || matches!(expr, Self::Variable(_, _)),
|
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,
|
|
|
|
},
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-03-10 11:32:01 +01:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Tokens.
|
2020-02-24 16:25:52 +01:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
2016-04-17 04:32:18 +02:00
|
|
|
pub enum Token {
|
2020-03-10 16:06:20 +01:00
|
|
|
IntegerConstant(INT),
|
|
|
|
FloatConstant(FLOAT),
|
2016-04-17 04:32:18 +02:00
|
|
|
Identifier(String),
|
2020-02-24 16:25:52 +01:00
|
|
|
CharConstant(char),
|
2016-04-17 04:32:18 +02:00
|
|
|
StringConst(String),
|
2020-02-24 16:25:52 +01:00
|
|
|
LeftBrace,
|
|
|
|
RightBrace,
|
|
|
|
LeftParen,
|
|
|
|
RightParen,
|
|
|
|
LeftBracket,
|
|
|
|
RightBracket,
|
2016-04-17 04:32:18 +02:00
|
|
|
Plus,
|
2017-10-30 16:08:44 +01:00
|
|
|
UnaryPlus,
|
2016-04-17 04:32:18 +02:00
|
|
|
Minus,
|
2017-10-30 16:08:44 +01:00
|
|
|
UnaryMinus,
|
2016-04-17 04:32:18 +02:00
|
|
|
Multiply,
|
|
|
|
Divide,
|
2020-03-19 12:53:42 +01:00
|
|
|
Modulo,
|
|
|
|
PowerOf,
|
|
|
|
LeftShift,
|
|
|
|
RightShift,
|
2020-02-24 16:25:52 +01:00
|
|
|
SemiColon,
|
2016-04-17 04:32:18 +02:00
|
|
|
Colon,
|
|
|
|
Comma,
|
|
|
|
Period,
|
2020-03-29 17:53:35 +02:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
MapStart,
|
2016-04-17 04:32:18 +02:00
|
|
|
Equals,
|
|
|
|
True,
|
|
|
|
False,
|
2020-02-24 16:25:52 +01:00
|
|
|
Let,
|
2020-03-13 11:12:41 +01:00
|
|
|
Const,
|
2016-04-17 04:32:18 +02:00
|
|
|
If,
|
|
|
|
Else,
|
|
|
|
While,
|
2017-10-30 16:08:44 +01:00
|
|
|
Loop,
|
2020-03-19 12:53:42 +01:00
|
|
|
For,
|
|
|
|
In,
|
2016-04-17 04:32:18 +02:00
|
|
|
LessThan,
|
|
|
|
GreaterThan,
|
2020-02-24 16:25:52 +01:00
|
|
|
LessThanEqualsTo,
|
|
|
|
GreaterThanEqualsTo,
|
|
|
|
EqualsTo,
|
|
|
|
NotEqualsTo,
|
2020-03-19 12:53:42 +01:00
|
|
|
Bang,
|
2016-04-17 04:32:18 +02:00
|
|
|
Pipe,
|
|
|
|
Or,
|
2020-03-19 12:53:42 +01:00
|
|
|
XOr,
|
2016-04-17 04:32:18 +02:00
|
|
|
Ampersand,
|
|
|
|
And,
|
2020-03-15 15:39:58 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2016-04-17 04:32:18 +02:00
|
|
|
Fn,
|
2020-04-01 10:22:18 +02:00
|
|
|
Continue,
|
2016-04-17 04:32:18 +02:00
|
|
|
Break,
|
|
|
|
Return,
|
2020-03-03 11:15:20 +01:00
|
|
|
Throw,
|
2017-12-21 12:32:18 +01:00
|
|
|
PlusAssign,
|
|
|
|
MinusAssign,
|
|
|
|
MultiplyAssign,
|
|
|
|
DivideAssign,
|
|
|
|
LeftShiftAssign,
|
|
|
|
RightShiftAssign,
|
|
|
|
AndAssign,
|
|
|
|
OrAssign,
|
|
|
|
XOrAssign,
|
|
|
|
ModuloAssign,
|
|
|
|
PowerOfAssign,
|
2020-03-24 02:49:37 +01:00
|
|
|
LexError(Box<LexError>),
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2017-10-30 16:08:44 +01:00
|
|
|
impl Token {
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Get the syntax of the token.
|
2020-03-24 09:46:47 +01:00
|
|
|
pub fn syntax(&self) -> Cow<str> {
|
2020-04-06 11:47:34 +02:00
|
|
|
use Token::*;
|
2020-03-03 09:23:55 +01:00
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
match self {
|
|
|
|
IntegerConstant(i) => i.to_string().into(),
|
|
|
|
FloatConstant(f) => f.to_string().into(),
|
|
|
|
Identifier(s) => s.into(),
|
|
|
|
CharConstant(c) => c.to_string().into(),
|
|
|
|
LexError(err) => err.to_string().into(),
|
2020-03-03 09:23:55 +01:00
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
token => (match token {
|
2020-03-03 09:23:55 +01:00
|
|
|
StringConst(_) => "string",
|
|
|
|
LeftBrace => "{",
|
|
|
|
RightBrace => "}",
|
|
|
|
LeftParen => "(",
|
|
|
|
RightParen => ")",
|
|
|
|
LeftBracket => "[",
|
|
|
|
RightBracket => "]",
|
|
|
|
Plus => "+",
|
|
|
|
UnaryPlus => "+",
|
|
|
|
Minus => "-",
|
|
|
|
UnaryMinus => "-",
|
|
|
|
Multiply => "*",
|
|
|
|
Divide => "/",
|
|
|
|
SemiColon => ";",
|
|
|
|
Colon => ":",
|
|
|
|
Comma => ",",
|
|
|
|
Period => ".",
|
2020-03-29 17:53:35 +02:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-03-30 11:40:26 +02:00
|
|
|
MapStart => "#{",
|
2020-03-03 09:23:55 +01:00
|
|
|
Equals => "=",
|
|
|
|
True => "true",
|
|
|
|
False => "false",
|
|
|
|
Let => "let",
|
2020-03-13 11:12:41 +01:00
|
|
|
Const => "const",
|
2020-03-03 09:23:55 +01:00
|
|
|
If => "if",
|
|
|
|
Else => "else",
|
|
|
|
While => "while",
|
|
|
|
Loop => "loop",
|
|
|
|
LessThan => "<",
|
|
|
|
GreaterThan => ">",
|
|
|
|
Bang => "!",
|
|
|
|
LessThanEqualsTo => "<=",
|
|
|
|
GreaterThanEqualsTo => ">=",
|
|
|
|
EqualsTo => "==",
|
|
|
|
NotEqualsTo => "!=",
|
|
|
|
Pipe => "|",
|
|
|
|
Or => "||",
|
|
|
|
Ampersand => "&",
|
|
|
|
And => "&&",
|
2020-03-15 15:39:58 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-03-03 09:23:55 +01:00
|
|
|
Fn => "fn",
|
2020-04-01 10:22:18 +02:00
|
|
|
Continue => "continue",
|
2020-03-03 09:23:55 +01:00
|
|
|
Break => "break",
|
|
|
|
Return => "return",
|
2020-03-03 11:15:20 +01:00
|
|
|
Throw => "throw",
|
2020-03-03 09:23:55 +01:00
|
|
|
PlusAssign => "+=",
|
|
|
|
MinusAssign => "-=",
|
|
|
|
MultiplyAssign => "*=",
|
|
|
|
DivideAssign => "/=",
|
|
|
|
LeftShiftAssign => "<<=",
|
|
|
|
RightShiftAssign => ">>=",
|
|
|
|
AndAssign => "&=",
|
|
|
|
OrAssign => "|=",
|
|
|
|
XOrAssign => "^=",
|
|
|
|
LeftShift => "<<",
|
|
|
|
RightShift => ">>",
|
|
|
|
XOr => "^",
|
|
|
|
Modulo => "%",
|
|
|
|
ModuloAssign => "%=",
|
|
|
|
PowerOf => "~",
|
|
|
|
PowerOfAssign => "~=",
|
|
|
|
For => "for",
|
|
|
|
In => "in",
|
2020-03-07 03:15:42 +01:00
|
|
|
_ => panic!("operator should be match in outer scope"),
|
2020-03-03 09:23:55 +01:00
|
|
|
})
|
|
|
|
.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// If another operator is after these, it's probably an unary operator
|
|
|
|
// (not sure about fn name).
|
2017-10-30 16:08:44 +01:00
|
|
|
pub fn is_next_unary(&self) -> bool {
|
2020-04-06 11:47:34 +02:00
|
|
|
use Token::*;
|
2017-10-30 16:08:44 +01:00
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
match self {
|
2020-03-09 03:10:19 +01:00
|
|
|
LexError(_) |
|
2020-02-24 16:25:52 +01:00
|
|
|
LeftBrace | // (+expr) - is unary
|
|
|
|
// RightBrace | {expr} - expr not unary & is closing
|
|
|
|
LeftParen | // {-expr} - is unary
|
|
|
|
// RightParen | (expr) - expr not unary & is closing
|
2020-04-10 06:16:39 +02:00
|
|
|
LeftBracket | // [-expr] - is unary
|
|
|
|
// RightBracket | [expr] - expr not unary & is closing
|
2017-10-30 16:08:44 +01:00
|
|
|
Plus |
|
|
|
|
UnaryPlus |
|
|
|
|
Minus |
|
|
|
|
UnaryMinus |
|
|
|
|
Multiply |
|
|
|
|
Divide |
|
|
|
|
Colon |
|
|
|
|
Comma |
|
|
|
|
Period |
|
|
|
|
Equals |
|
|
|
|
LessThan |
|
|
|
|
GreaterThan |
|
|
|
|
Bang |
|
2020-02-24 16:25:52 +01:00
|
|
|
LessThanEqualsTo |
|
|
|
|
GreaterThanEqualsTo |
|
|
|
|
EqualsTo |
|
|
|
|
NotEqualsTo |
|
2017-10-30 16:08:44 +01:00
|
|
|
Pipe |
|
|
|
|
Or |
|
|
|
|
Ampersand |
|
|
|
|
And |
|
|
|
|
If |
|
|
|
|
While |
|
2017-12-21 12:32:18 +01:00
|
|
|
PlusAssign |
|
|
|
|
MinusAssign |
|
|
|
|
MultiplyAssign |
|
|
|
|
DivideAssign |
|
|
|
|
LeftShiftAssign |
|
|
|
|
RightShiftAssign |
|
|
|
|
AndAssign |
|
|
|
|
OrAssign |
|
|
|
|
XOrAssign |
|
2017-11-01 23:09:07 +01:00
|
|
|
LeftShift |
|
|
|
|
RightShift |
|
|
|
|
XOr |
|
|
|
|
Modulo |
|
2017-12-21 12:32:18 +01:00
|
|
|
ModuloAssign |
|
2017-11-24 08:56:22 +01:00
|
|
|
Return |
|
2020-03-03 11:15:20 +01:00
|
|
|
Throw |
|
2017-11-24 08:56:22 +01:00
|
|
|
PowerOf |
|
2020-02-23 15:48:46 +01:00
|
|
|
In |
|
2017-12-21 12:32:18 +01:00
|
|
|
PowerOfAssign => true,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2017-10-30 16:08:44 +01:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Get the precedence number of the token.
|
2020-03-16 16:51:32 +01:00
|
|
|
pub fn precedence(&self) -> u8 {
|
2020-04-06 11:47:34 +02:00
|
|
|
use Token::*;
|
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
match self {
|
2020-04-06 11:47:34 +02:00
|
|
|
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
|
|
|
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
|
|
|
| PowerOfAssign => 10,
|
|
|
|
|
|
|
|
Or | XOr | Pipe => 40,
|
2017-10-30 16:08:44 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
And | Ampersand => 50,
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo | EqualsTo
|
|
|
|
| NotEqualsTo => 60,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
In => 70,
|
2020-03-16 16:51:32 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
Plus | Minus => 80,
|
2020-03-16 16:51:32 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
Divide | Multiply | PowerOf => 90,
|
2020-03-16 16:51:32 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
LeftShift | RightShift => 100,
|
2020-03-16 16:51:32 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
Modulo => 110,
|
2020-03-16 16:51:32 +01:00
|
|
|
|
2020-04-06 11:47:34 +02:00
|
|
|
Period => 120,
|
2020-03-16 16:51:32 +01:00
|
|
|
|
|
|
|
_ => 0,
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Does an expression bind to the right (instead of left)?
|
2020-03-16 16:51:32 +01:00
|
|
|
pub fn is_bind_right(&self) -> bool {
|
2020-04-06 11:47:34 +02:00
|
|
|
use Token::*;
|
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
match self {
|
2020-03-18 03:36:50 +01:00
|
|
|
// Assignments bind to the right
|
2020-04-06 11:47:34 +02:00
|
|
|
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
|
|
|
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
|
|
|
| PowerOfAssign => true,
|
2020-03-16 16:51:32 +01:00
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// Property access binds to the right
|
2020-04-06 11:47:34 +02:00
|
|
|
Period => true,
|
2017-10-30 16:08:44 +01:00
|
|
|
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// An iterator on a `Token` stream.
|
2016-02-29 22:43:45 +01:00
|
|
|
pub struct TokenIterator<'a> {
|
2020-03-24 04:21:20 +01:00
|
|
|
/// Can the next token be a unary operator?
|
|
|
|
can_be_unary: bool,
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Current position.
|
2020-02-29 13:12:10 +01:00
|
|
|
pos: Position,
|
2020-04-10 11:14:07 +02:00
|
|
|
/// The input character streams.
|
|
|
|
streams: Vec<Peekable<Chars<'a>>>,
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TokenIterator<'a> {
|
2020-03-29 17:53:35 +02:00
|
|
|
/// Consume the next character.
|
|
|
|
fn eat_next(&mut self) {
|
2020-04-10 11:14:07 +02:00
|
|
|
self.get_next();
|
2020-03-29 17:53:35 +02:00
|
|
|
self.advance();
|
|
|
|
}
|
2020-04-10 11:14:07 +02:00
|
|
|
/// Get the next character
|
|
|
|
fn get_next(&mut self) -> Option<char> {
|
|
|
|
loop {
|
|
|
|
if self.streams.is_empty() {
|
|
|
|
return None;
|
|
|
|
} else if let Some(ch) = self.streams[0].next() {
|
|
|
|
return Some(ch);
|
|
|
|
} else {
|
|
|
|
let _ = self.streams.remove(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Peek the next character
|
|
|
|
fn peek_next(&mut self) -> Option<char> {
|
|
|
|
loop {
|
|
|
|
if self.streams.is_empty() {
|
|
|
|
return None;
|
|
|
|
} else if let Some(ch) = self.streams[0].peek() {
|
|
|
|
return Some(*ch);
|
|
|
|
} else {
|
|
|
|
let _ = self.streams.remove(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Move the current position one character ahead.
|
2020-03-04 02:33:35 +01:00
|
|
|
fn advance(&mut self) {
|
|
|
|
self.pos.advance();
|
2020-03-03 06:30:46 +01:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Move the current position back one character.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if already at the beginning of a line - cannot rewind to the previous line.
|
2020-03-04 02:33:35 +01:00
|
|
|
fn rewind(&mut self) {
|
|
|
|
self.pos.rewind();
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Move the current position to the next line.
|
2020-03-04 02:33:35 +01:00
|
|
|
fn new_line(&mut self) {
|
|
|
|
self.pos.new_line()
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Parse a string literal wrapped by `enclosing_char`.
|
|
|
|
pub fn parse_string_literal(
|
2020-02-24 16:25:52 +01:00
|
|
|
&mut self,
|
|
|
|
enclosing_char: char,
|
2020-02-29 13:12:10 +01:00
|
|
|
) -> Result<String, (LexError, Position)> {
|
2016-04-17 04:32:18 +02:00
|
|
|
let mut result = Vec::new();
|
2020-03-03 14:39:25 +01:00
|
|
|
let mut escape = String::with_capacity(12);
|
2016-04-17 04:32:18 +02:00
|
|
|
|
2020-02-29 14:10:31 +01:00
|
|
|
loop {
|
2020-04-10 11:14:07 +02:00
|
|
|
let next_char = self.get_next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
|
2020-03-12 05:40:28 +01:00
|
|
|
match next_char.ok_or((LERR::UnterminatedString, Position::eof()))? {
|
2020-03-18 03:36:50 +01:00
|
|
|
// \...
|
2020-03-03 14:39:25 +01:00
|
|
|
'\\' if escape.is_empty() => {
|
|
|
|
escape.push('\\');
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// \\
|
2020-03-03 14:39:25 +01:00
|
|
|
'\\' if !escape.is_empty() => {
|
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
result.push('\\');
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// \t
|
2020-03-03 14:39:25 +01:00
|
|
|
't' if !escape.is_empty() => {
|
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
result.push('\t');
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// \n
|
2020-03-03 14:39:25 +01:00
|
|
|
'n' if !escape.is_empty() => {
|
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
result.push('\n');
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// \r
|
2020-03-03 14:39:25 +01:00
|
|
|
'r' if !escape.is_empty() => {
|
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
result.push('\r');
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// \x??, \u????, \U????????
|
2020-03-16 16:51:32 +01:00
|
|
|
ch @ 'x' | ch @ 'u' | ch @ 'U' if !escape.is_empty() => {
|
2020-03-03 14:39:25 +01:00
|
|
|
let mut seq = escape.clone();
|
2020-03-16 16:51:32 +01:00
|
|
|
seq.push(ch);
|
2020-03-03 14:39:25 +01:00
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
|
|
|
|
let mut out_val: u32 = 0;
|
2020-03-16 16:51:32 +01:00
|
|
|
let len = match ch {
|
|
|
|
'x' => 2,
|
|
|
|
'u' => 4,
|
|
|
|
'U' => 8,
|
|
|
|
_ => panic!("should be 'x', 'u' or 'U'"),
|
|
|
|
};
|
|
|
|
|
|
|
|
for _ in 0..len {
|
2020-04-10 11:14:07 +02:00
|
|
|
let c = self.get_next().ok_or_else(|| {
|
2020-03-16 16:51:32 +01:00
|
|
|
(LERR::MalformedEscapeSequence(seq.to_string()), self.pos)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
seq.push(c);
|
|
|
|
self.advance();
|
2020-03-03 14:39:25 +01:00
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
out_val *= 16;
|
|
|
|
out_val += c.to_digit(16).ok_or_else(|| {
|
|
|
|
(LERR::MalformedEscapeSequence(seq.to_string()), self.pos)
|
|
|
|
})?;
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
result.push(
|
|
|
|
char::from_u32(out_val)
|
|
|
|
.ok_or_else(|| (LERR::MalformedEscapeSequence(seq), self.pos))?,
|
|
|
|
);
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
|
|
|
|
// \{enclosing_char} - escaped
|
2020-04-11 06:46:10 +02:00
|
|
|
ch if enclosing_char == ch && !escape.is_empty() => {
|
|
|
|
escape.clear();
|
|
|
|
result.push(ch)
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
|
|
|
|
// Close wrapper
|
2020-03-16 16:51:32 +01:00
|
|
|
ch if enclosing_char == ch && escape.is_empty() => break,
|
2020-03-18 03:36:50 +01:00
|
|
|
|
|
|
|
// Unknown escape sequence
|
2020-03-03 14:39:25 +01:00
|
|
|
_ if !escape.is_empty() => {
|
|
|
|
return Err((LERR::MalformedEscapeSequence(escape), self.pos))
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
|
|
|
|
// Cannot have new-lines inside string literals
|
2020-02-29 14:10:31 +01:00
|
|
|
'\n' => {
|
2020-03-04 02:33:35 +01:00
|
|
|
self.rewind();
|
2020-02-29 14:10:31 +01:00
|
|
|
return Err((LERR::UnterminatedString, self.pos));
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
|
|
|
|
// All other characters
|
2020-03-16 16:51:32 +01:00
|
|
|
ch => {
|
2020-03-03 14:39:25 +01:00
|
|
|
escape.clear();
|
2020-03-16 16:51:32 +01:00
|
|
|
result.push(ch);
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 03:15:42 +01:00
|
|
|
Ok(result.iter().collect())
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Get the next token.
|
2020-02-29 13:12:10 +01:00
|
|
|
fn inner_next(&mut self) -> Option<(Token, Position)> {
|
2020-03-09 03:10:19 +01:00
|
|
|
let mut negated = false;
|
|
|
|
|
2020-04-10 11:14:07 +02:00
|
|
|
while let Some(c) = self.get_next() {
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
|
|
|
|
let pos = self.pos;
|
|
|
|
|
2020-04-10 11:14:07 +02:00
|
|
|
match (c, self.peek_next().unwrap_or('\0')) {
|
2020-03-18 03:36:50 +01:00
|
|
|
// \n
|
2020-03-24 09:46:47 +01:00
|
|
|
('\n', _) => self.new_line(),
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// digit ...
|
2020-03-24 09:46:47 +01:00
|
|
|
('0'..='9', _) => {
|
2016-02-29 22:43:45 +01:00
|
|
|
let mut result = Vec::new();
|
2017-11-01 06:10:46 +01:00
|
|
|
let mut radix_base: Option<u32> = None;
|
2016-02-29 22:43:45 +01:00
|
|
|
result.push(c);
|
|
|
|
|
2020-04-10 11:14:07 +02:00
|
|
|
while let Some(next_char) = self.peek_next() {
|
2020-03-03 06:30:46 +01:00
|
|
|
match next_char {
|
2020-03-03 14:39:25 +01:00
|
|
|
'0'..='9' | '_' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char);
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2017-10-30 04:32:41 +01:00
|
|
|
'.' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char);
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-04-10 11:14:07 +02:00
|
|
|
while let Some(next_char_in_float) = self.peek_next() {
|
2020-03-03 06:30:46 +01:00
|
|
|
match next_char_in_float {
|
2020-03-03 14:39:25 +01:00
|
|
|
'0'..='9' | '_' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char_in_float);
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2017-10-30 04:32:41 +01:00
|
|
|
}
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// 0x????, 0o????, 0b????
|
2020-03-16 16:51:32 +01:00
|
|
|
ch @ 'x' | ch @ 'X' | ch @ 'o' | ch @ 'O' | ch @ 'b' | ch @ 'B'
|
|
|
|
if c == '0' =>
|
|
|
|
{
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char);
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-16 16:51:32 +01:00
|
|
|
|
|
|
|
let valid = match ch {
|
|
|
|
'x' | 'X' => [
|
|
|
|
'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F',
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_',
|
|
|
|
],
|
|
|
|
'o' | 'O' => [
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '_', '_', '_', '_',
|
|
|
|
'_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
|
|
|
|
],
|
|
|
|
'b' | 'B' => [
|
|
|
|
'0', '1', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
|
|
|
|
'_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
|
|
|
|
],
|
|
|
|
_ => panic!("unexpected character {}", ch),
|
|
|
|
};
|
|
|
|
|
|
|
|
radix_base = Some(match ch {
|
|
|
|
'x' | 'X' => 16,
|
|
|
|
'o' | 'O' => 8,
|
|
|
|
'b' | 'B' => 2,
|
|
|
|
_ => panic!("unexpected character {}", ch),
|
|
|
|
});
|
|
|
|
|
2020-04-10 11:14:07 +02:00
|
|
|
while let Some(next_char_in_hex) = self.peek_next() {
|
2020-03-16 16:51:32 +01:00
|
|
|
if !valid.contains(&next_char_in_hex) {
|
|
|
|
break;
|
2017-11-01 06:02:36 +01:00
|
|
|
}
|
2020-03-16 16:51:32 +01:00
|
|
|
|
|
|
|
result.push(next_char_in_hex);
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2017-11-01 06:02:36 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-16 16:51:32 +01:00
|
|
|
|
2016-04-17 04:32:18 +02:00
|
|
|
_ => break,
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-09 03:10:19 +01:00
|
|
|
if negated {
|
|
|
|
result.insert(0, '-');
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// Parse number
|
2017-11-01 06:10:46 +01:00
|
|
|
if let Some(radix) = radix_base {
|
2020-03-03 14:39:25 +01:00
|
|
|
let out: String = result.iter().skip(2).filter(|&&c| c != '_').collect();
|
|
|
|
|
|
|
|
return Some((
|
2020-03-10 16:06:20 +01:00
|
|
|
INT::from_str_radix(&out, radix)
|
2020-03-09 14:57:07 +01:00
|
|
|
.map(Token::IntegerConstant)
|
|
|
|
.unwrap_or_else(|_| {
|
2020-03-24 02:49:37 +01:00
|
|
|
Token::LexError(Box::new(LERR::MalformedNumber(
|
|
|
|
result.iter().collect(),
|
|
|
|
)))
|
2020-03-09 14:57:07 +01:00
|
|
|
}),
|
2020-03-03 14:39:25 +01:00
|
|
|
pos,
|
|
|
|
));
|
2020-03-07 03:15:42 +01:00
|
|
|
} else {
|
|
|
|
let out: String = result.iter().filter(|&&c| c != '_').collect();
|
2020-03-16 16:51:32 +01:00
|
|
|
let num = INT::from_str(&out).map(Token::IntegerConstant);
|
2020-03-10 11:32:01 +01:00
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// If integer parsing is unnecessary, try float instead
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-16 16:51:32 +01:00
|
|
|
let num = num.or_else(|_| FLOAT::from_str(&out).map(Token::FloatConstant));
|
|
|
|
|
2020-03-07 03:15:42 +01:00
|
|
|
return Some((
|
2020-03-16 16:51:32 +01:00
|
|
|
num.unwrap_or_else(|_| {
|
2020-03-24 02:49:37 +01:00
|
|
|
Token::LexError(Box::new(LERR::MalformedNumber(
|
|
|
|
result.iter().collect(),
|
|
|
|
)))
|
2020-03-16 16:51:32 +01:00
|
|
|
}),
|
2020-03-07 03:15:42 +01:00
|
|
|
pos,
|
|
|
|
));
|
|
|
|
}
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
|
|
|
|
// letter or underscore ...
|
|
|
|
('A'..='Z', _) | ('a'..='z', _) | ('_', _) => {
|
2016-02-29 22:43:45 +01:00
|
|
|
let mut result = Vec::new();
|
|
|
|
result.push(c);
|
|
|
|
|
2020-04-10 11:14:07 +02:00
|
|
|
while let Some(next_char) = self.peek_next() {
|
2020-03-03 06:30:46 +01:00
|
|
|
match next_char {
|
|
|
|
x if x.is_ascii_alphanumeric() || x == '_' => {
|
2017-10-14 19:46:22 +02:00
|
|
|
result.push(x);
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
_ => break,
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 16:09:53 +01:00
|
|
|
let is_valid_identifier = result
|
|
|
|
.iter()
|
|
|
|
.find(|&ch| char::is_ascii_alphanumeric(ch)) // first alpha-numeric character
|
|
|
|
.map(char::is_ascii_alphabetic) // is a letter
|
|
|
|
.unwrap_or(false); // if no alpha-numeric at all - syntax error
|
|
|
|
|
2020-03-16 05:38:01 +01:00
|
|
|
let identifier: String = result.iter().collect();
|
2020-02-24 16:25:52 +01:00
|
|
|
|
2020-03-18 16:09:53 +01:00
|
|
|
if !is_valid_identifier {
|
2020-03-24 02:49:37 +01:00
|
|
|
return Some((
|
|
|
|
Token::LexError(Box::new(LERR::MalformedIdentifier(identifier))),
|
|
|
|
pos,
|
|
|
|
));
|
2020-03-18 16:09:53 +01:00
|
|
|
}
|
|
|
|
|
2020-02-24 16:25:52 +01:00
|
|
|
return Some((
|
2020-03-16 05:38:01 +01:00
|
|
|
match identifier.as_str() {
|
2020-02-24 16:25:52 +01:00
|
|
|
"true" => Token::True,
|
|
|
|
"false" => Token::False,
|
|
|
|
"let" => Token::Let,
|
2020-03-13 11:12:41 +01:00
|
|
|
"const" => Token::Const,
|
2020-02-24 16:25:52 +01:00
|
|
|
"if" => Token::If,
|
|
|
|
"else" => Token::Else,
|
|
|
|
"while" => Token::While,
|
|
|
|
"loop" => Token::Loop,
|
2020-04-01 10:22:18 +02:00
|
|
|
"continue" => Token::Continue,
|
2020-02-24 16:25:52 +01:00
|
|
|
"break" => Token::Break,
|
|
|
|
"return" => Token::Return,
|
2020-03-03 11:15:20 +01:00
|
|
|
"throw" => Token::Throw,
|
2020-02-24 16:25:52 +01:00
|
|
|
"for" => Token::For,
|
|
|
|
"in" => Token::In,
|
2020-03-15 15:39:58 +01:00
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
"fn" => Token::Fn,
|
|
|
|
|
2020-03-18 16:09:53 +01:00
|
|
|
_ => Token::Identifier(identifier),
|
2020-02-24 16:25:52 +01:00
|
|
|
},
|
|
|
|
pos,
|
|
|
|
));
|
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// " - string literal
|
2020-03-24 09:46:47 +01:00
|
|
|
('"', _) => {
|
|
|
|
return self.parse_string_literal('"').map_or_else(
|
|
|
|
|err| Some((Token::LexError(Box::new(err.0)), err.1)),
|
|
|
|
|out| Some((Token::StringConst(out), pos)),
|
|
|
|
);
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
|
2020-03-24 09:46:47 +01:00
|
|
|
// ' - character literal
|
|
|
|
('\'', '\'') => {
|
|
|
|
return Some((
|
|
|
|
Token::LexError(Box::new(LERR::MalformedChar("".to_string()))),
|
|
|
|
pos,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
('\'', _) => {
|
|
|
|
return Some(self.parse_string_literal('\'').map_or_else(
|
|
|
|
|err| (Token::LexError(Box::new(err.0)), err.1),
|
|
|
|
|result| {
|
|
|
|
let mut chars = result.chars();
|
|
|
|
let first = chars.next();
|
|
|
|
|
|
|
|
if chars.next().is_some() {
|
|
|
|
(Token::LexError(Box::new(LERR::MalformedChar(result))), pos)
|
2020-02-24 16:25:52 +01:00
|
|
|
} else {
|
2020-03-24 09:46:47 +01:00
|
|
|
(Token::CharConstant(first.expect("should be Some")), pos)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
));
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
|
|
|
|
// Braces
|
2020-03-24 09:46:47 +01:00
|
|
|
('{', _) => return Some((Token::LeftBrace, pos)),
|
|
|
|
('}', _) => return Some((Token::RightBrace, pos)),
|
2020-03-18 03:36:50 +01:00
|
|
|
|
|
|
|
// Parentheses
|
2020-03-24 09:46:47 +01:00
|
|
|
('(', _) => return Some((Token::LeftParen, pos)),
|
|
|
|
(')', _) => return Some((Token::RightParen, pos)),
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// Indexing
|
2020-03-24 09:46:47 +01:00
|
|
|
('[', _) => return Some((Token::LeftBracket, pos)),
|
|
|
|
(']', _) => return Some((Token::RightBracket, pos)),
|
2020-03-15 15:39:58 +01:00
|
|
|
|
2020-03-29 17:53:35 +02:00
|
|
|
// Map literal
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-03-30 11:40:26 +02:00
|
|
|
('#', '{') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
|
|
|
return Some((Token::MapStart, pos));
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// Operators
|
2020-03-24 09:46:47 +01:00
|
|
|
('+', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::PlusAssign, pos));
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
('+', _) if self.can_be_unary => return Some((Token::UnaryPlus, pos)),
|
|
|
|
('+', _) => return Some((Token::Plus, pos)),
|
|
|
|
|
|
|
|
('-', '0'..='9') if self.can_be_unary => negated = true,
|
|
|
|
('-', '0'..='9') => return Some((Token::Minus, pos)),
|
|
|
|
('-', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::MinusAssign, pos));
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
('-', _) if self.can_be_unary => return Some((Token::UnaryMinus, pos)),
|
|
|
|
('-', _) => return Some((Token::Minus, pos)),
|
2020-02-24 16:25:52 +01:00
|
|
|
|
2020-03-24 09:46:47 +01:00
|
|
|
('*', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::MultiplyAssign, pos));
|
|
|
|
}
|
|
|
|
('*', _) => return Some((Token::Multiply, pos)),
|
2017-10-30 16:08:44 +01:00
|
|
|
|
2020-03-24 09:46:47 +01:00
|
|
|
// Comments
|
|
|
|
('/', '/') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
|
2020-04-10 11:14:07 +02:00
|
|
|
while let Some(c) = self.get_next() {
|
2020-03-24 09:46:47 +01:00
|
|
|
if c == '\n' {
|
|
|
|
self.new_line();
|
|
|
|
break;
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
}
|
|
|
|
('/', '*') => {
|
|
|
|
let mut level = 1;
|
|
|
|
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
|
2020-04-10 11:14:07 +02:00
|
|
|
while let Some(c) = self.get_next() {
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-03-24 09:46:47 +01:00
|
|
|
|
|
|
|
match c {
|
|
|
|
'/' => {
|
2020-04-10 11:14:07 +02:00
|
|
|
if self.get_next() == Some('*') {
|
2020-03-24 09:46:47 +01:00
|
|
|
level += 1;
|
|
|
|
}
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2017-10-31 06:55:20 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
'*' => {
|
2020-04-10 11:14:07 +02:00
|
|
|
if self.get_next() == Some('/') {
|
2020-03-24 09:46:47 +01:00
|
|
|
level -= 1;
|
|
|
|
}
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
'\n' => self.new_line(),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
if level == 0 {
|
|
|
|
break;
|
|
|
|
}
|
2016-03-01 15:40:48 +01:00
|
|
|
}
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
|
|
|
|
('/', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::DivideAssign, pos));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
('/', _) => return Some((Token::Divide, pos)),
|
|
|
|
|
|
|
|
(';', _) => return Some((Token::SemiColon, pos)),
|
|
|
|
(':', _) => return Some((Token::Colon, pos)),
|
|
|
|
(',', _) => return Some((Token::Comma, pos)),
|
|
|
|
('.', _) => return Some((Token::Period, pos)),
|
|
|
|
|
|
|
|
('=', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::EqualsTo, pos));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
('=', _) => return Some((Token::Equals, pos)),
|
|
|
|
|
|
|
|
('<', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::LessThanEqualsTo, pos));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
('<', '<') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
|
2020-02-24 16:25:52 +01:00
|
|
|
return Some((
|
2020-04-10 11:14:07 +02:00
|
|
|
if self.peek_next() == Some('=') {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
Token::LeftShiftAssign
|
|
|
|
} else {
|
|
|
|
Token::LeftShift
|
2020-02-24 16:25:52 +01:00
|
|
|
},
|
|
|
|
pos,
|
2020-03-24 09:46:47 +01:00
|
|
|
));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
('<', _) => return Some((Token::LessThan, pos)),
|
|
|
|
|
|
|
|
('>', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::GreaterThanEqualsTo, pos));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
('>', '>') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
|
2020-02-24 16:25:52 +01:00
|
|
|
return Some((
|
2020-04-10 11:14:07 +02:00
|
|
|
if self.peek_next() == Some('=') {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
Token::RightShiftAssign
|
|
|
|
} else {
|
|
|
|
Token::RightShift
|
2020-02-24 16:25:52 +01:00
|
|
|
},
|
|
|
|
pos,
|
2020-03-24 09:46:47 +01:00
|
|
|
));
|
|
|
|
}
|
|
|
|
('>', _) => return Some((Token::GreaterThan, pos)),
|
|
|
|
|
|
|
|
('!', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::NotEqualsTo, pos));
|
|
|
|
}
|
|
|
|
('!', _) => return Some((Token::Bang, pos)),
|
|
|
|
|
|
|
|
('|', '|') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::Or, pos));
|
|
|
|
}
|
|
|
|
('|', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::OrAssign, pos));
|
|
|
|
}
|
|
|
|
('|', _) => return Some((Token::Pipe, pos)),
|
|
|
|
|
|
|
|
('&', '&') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::And, pos));
|
|
|
|
}
|
|
|
|
('&', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::AndAssign, pos));
|
|
|
|
}
|
|
|
|
('&', _) => return Some((Token::Ampersand, pos)),
|
|
|
|
|
|
|
|
('^', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::XOrAssign, pos));
|
|
|
|
}
|
|
|
|
('^', _) => return Some((Token::XOr, pos)),
|
|
|
|
|
|
|
|
('%', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::ModuloAssign, pos));
|
|
|
|
}
|
|
|
|
('%', _) => return Some((Token::Modulo, pos)),
|
|
|
|
|
|
|
|
('~', '=') => {
|
2020-03-29 17:53:35 +02:00
|
|
|
self.eat_next();
|
2020-03-24 09:46:47 +01:00
|
|
|
return Some((Token::PowerOfAssign, pos));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
('~', _) => return Some((Token::PowerOf, pos)),
|
|
|
|
|
|
|
|
(ch, _) if ch.is_whitespace() => (),
|
|
|
|
(ch, _) => return Some((Token::LexError(Box::new(LERR::UnexpectedChar(ch))), pos)),
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 16:08:44 +01:00
|
|
|
impl<'a> Iterator for TokenIterator<'a> {
|
2020-02-29 13:12:10 +01:00
|
|
|
type Item = (Token, Position);
|
2017-10-30 16:08:44 +01:00
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
2020-02-24 16:25:52 +01:00
|
|
|
self.inner_next().map(|x| {
|
2020-03-24 04:21:20 +01:00
|
|
|
// Save the last token
|
|
|
|
self.can_be_unary = x.0.is_next_unary();
|
2020-02-24 16:25:52 +01:00
|
|
|
x
|
|
|
|
})
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
/// Tokenize an input text stream.
|
2020-04-10 11:14:07 +02:00
|
|
|
pub fn lex<'a>(input: &'a [&'a str]) -> TokenIterator<'a> {
|
2019-09-18 12:21:07 +02:00
|
|
|
TokenIterator {
|
2020-03-24 04:21:20 +01:00
|
|
|
can_be_unary: true,
|
2020-03-04 15:00:01 +01:00
|
|
|
pos: Position::new(1, 0),
|
2020-04-10 11:14:07 +02:00
|
|
|
streams: input.iter().map(|s| s.chars().peekable()).collect(),
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2016-02-29 22:43:45 +01: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>>,
|
|
|
|
begin: Position,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2020-03-02 15:13:14 +01:00
|
|
|
) -> Result<Expr, ParseError> {
|
2020-03-24 09:46:47 +01:00
|
|
|
if matches!(input.peek(), Some((Token::RightParen, _))) {
|
|
|
|
input.next();
|
|
|
|
return Ok(Expr::Unit(begin));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
|
|
|
|
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() {
|
2020-03-18 03:36:50 +01:00
|
|
|
// ( xxx )
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::RightParen, _)) => Ok(expr),
|
2020-03-18 03:36:50 +01:00
|
|
|
// ( xxx ???
|
2020-03-29 07:44:27 +02:00
|
|
|
Some((_, pos)) => Err(PERR::MissingToken(
|
|
|
|
")".into(),
|
|
|
|
"for a matching ( in this expression".into(),
|
|
|
|
)
|
|
|
|
.into_err(pos)),
|
2020-03-18 03:36:50 +01:00
|
|
|
// ( xxx
|
2020-03-29 07:44:27 +02:00
|
|
|
None => Err(
|
|
|
|
PERR::MissingToken(")".into(), "for a matching ( in this expression".into())
|
|
|
|
.into_err_eof(),
|
|
|
|
),
|
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>>,
|
2020-03-02 15:13:14 +01:00
|
|
|
begin: Position,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2019-09-18 12:21:07 +02:00
|
|
|
) -> Result<Expr, ParseError> {
|
2020-03-12 07:54:14 +01:00
|
|
|
let mut args_expr_list = Vec::new();
|
2017-10-28 05:30:12 +02:00
|
|
|
|
2020-04-06 06:29:01 +02:00
|
|
|
match input.peek() {
|
|
|
|
//id {EOF}
|
|
|
|
None => {
|
|
|
|
return Err(PERR::MissingToken(
|
|
|
|
")".into(),
|
|
|
|
format!("to close the arguments list of this function call '{}'", id),
|
|
|
|
)
|
|
|
|
.into_err_eof())
|
|
|
|
}
|
|
|
|
// id()
|
|
|
|
Some((Token::RightParen, _)) => {
|
|
|
|
input.next();
|
|
|
|
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
|
|
|
|
2020-04-06 06:29:01 +02:00
|
|
|
match input.peek() {
|
|
|
|
None => {
|
|
|
|
return Err(PERR::MissingToken(
|
|
|
|
")".into(),
|
|
|
|
format!("to close the arguments list of this function call '{}'", id),
|
|
|
|
)
|
|
|
|
.into_err_eof())
|
|
|
|
}
|
|
|
|
Some((Token::RightParen, _)) => {
|
2016-03-01 21:16:10 +01:00
|
|
|
input.next();
|
2020-04-06 06:29:01 +02:00
|
|
|
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-04-06 06:29:01 +02:00
|
|
|
Some((Token::Comma, _)) => (),
|
|
|
|
Some((_, pos)) => {
|
2020-03-29 07:44:27 +02:00
|
|
|
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))
|
2020-03-05 18:05:02 +01:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
input.next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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>>,
|
2020-03-07 03:16:20 +01:00
|
|
|
pos: Position,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2019-09-18 12:21:07 +02:00
|
|
|
) -> Result<Expr, 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
|
2020-03-29 07:44:27 +02:00
|
|
|
match input.peek().ok_or_else(|| {
|
|
|
|
PERR::MissingToken(
|
|
|
|
"]".into(),
|
|
|
|
"for a matching [ in this index expression".into(),
|
|
|
|
)
|
|
|
|
.into_err_eof()
|
|
|
|
})? {
|
2020-03-16 16:51:32 +01:00
|
|
|
(Token::RightBracket, _) => {
|
2020-03-05 18:05:02 +01:00
|
|
|
input.next();
|
2020-03-24 09:46:47 +01:00
|
|
|
Ok(Expr::Index(lhs, Box::new(idx_expr), pos))
|
2020-03-05 18:05:02 +01:00
|
|
|
}
|
2020-03-29 07:44:27 +02:00
|
|
|
(_, 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 expression that begins with an identifier.
|
2020-04-06 06:29:01 +02:00
|
|
|
fn parse_ident_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|
|
|
id: S,
|
2019-09-18 12:21:07 +02:00
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
2020-03-02 15:13:14 +01:00
|
|
|
begin: Position,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2019-09-18 12:21:07 +02:00
|
|
|
) -> Result<Expr, ParseError> {
|
2016-03-26 18:46:28 +01:00
|
|
|
match input.peek() {
|
2020-03-18 03:36:50 +01:00
|
|
|
// id(...) - function call
|
2020-03-16 16:51:32 +01:00
|
|
|
Some((Token::LeftParen, _)) => {
|
2016-04-17 04:32:18 +02:00
|
|
|
input.next();
|
2020-03-22 14:03:58 +01:00
|
|
|
parse_call_expr(id, input, begin, allow_stmt_expr)
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// id[...] - indexing
|
2020-03-10 10:10:33 +01:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-03-16 16:51:32 +01:00
|
|
|
Some((Token::LeftBracket, pos)) => {
|
|
|
|
let pos = *pos;
|
2016-04-17 04:32:18 +02:00
|
|
|
input.next();
|
2020-03-22 14:03:58 +01:00
|
|
|
parse_index_expr(
|
2020-04-06 06:29:01 +02:00
|
|
|
Box::new(Expr::Variable(id.into(), begin)),
|
2020-03-22 14:03:58 +01:00
|
|
|
input,
|
|
|
|
pos,
|
|
|
|
allow_stmt_expr,
|
|
|
|
)
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// id - variable
|
2020-04-06 06:29:01 +02:00
|
|
|
Some(_) => Ok(Expr::Variable(id.into(), begin)),
|
2020-03-18 03:36:50 +01:00
|
|
|
// EOF
|
2020-04-06 06:29:01 +02:00
|
|
|
None => Ok(Expr::Variable(id.into(), begin)),
|
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>>,
|
|
|
|
begin: Position,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2020-03-02 15:13:14 +01:00
|
|
|
) -> Result<Expr, ParseError> {
|
2016-03-26 18:46:28 +01:00
|
|
|
let mut arr = Vec::new();
|
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
if !matches!(input.peek(), Some((Token::RightBracket, _))) {
|
|
|
|
while input.peek().is_some() {
|
2020-03-22 14:03:58 +01:00
|
|
|
arr.push(parse_expr(input, allow_stmt_expr)?);
|
2020-03-02 15:13:14 +01:00
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
match input.peek().ok_or_else(|| {
|
2020-03-29 07:44:27 +02:00
|
|
|
PERR::MissingToken("]".into(), "to end this array literal".into()).into_err_eof()
|
2020-03-16 16:51:32 +01:00
|
|
|
})? {
|
2020-04-06 11:47:34 +02:00
|
|
|
(Token::Comma, _) => input.next(),
|
2020-03-16 16:51:32 +01:00
|
|
|
(Token::RightBracket, _) => break,
|
|
|
|
(_, pos) => {
|
2020-03-29 07:44:27 +02:00
|
|
|
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
|
|
|
)
|
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-03-16 16:51:32 +01:00
|
|
|
match input.peek().ok_or_else(|| {
|
2020-03-29 07:44:27 +02:00
|
|
|
PERR::MissingToken("]".into(), "to end this array literal".into()).into_err_eof()
|
2020-03-16 16:51:32 +01:00
|
|
|
})? {
|
|
|
|
(Token::RightBracket, _) => {
|
2016-04-17 04:32:18 +02:00
|
|
|
input.next();
|
2020-03-02 15:13:14 +01:00
|
|
|
Ok(Expr::Array(arr, begin))
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-24 09:46:47 +01:00
|
|
|
(_, pos) => {
|
2020-03-29 07:44:27 +02:00
|
|
|
Err(PERR::MissingToken("]".into(), "to end this array literal".into()).into_err(*pos))
|
2020-03-24 09:46:47 +01:00
|
|
|
}
|
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, ParseError> {
|
|
|
|
let mut map = Vec::new();
|
|
|
|
|
|
|
|
if !matches!(input.peek(), Some((Token::RightBrace, _))) {
|
|
|
|
while input.peek().is_some() {
|
|
|
|
let (name, pos) = match input.next().ok_or_else(|| {
|
|
|
|
PERR::MissingToken("}".into(), "to end this object map literal".into())
|
|
|
|
.into_err_eof()
|
|
|
|
})? {
|
2020-04-05 11:44:48 +02:00
|
|
|
(Token::Identifier(s), pos) => (s, pos),
|
|
|
|
(Token::StringConst(s), pos) => (s, pos),
|
2020-03-29 17:53:35 +02:00
|
|
|
(_, pos) if map.is_empty() => {
|
|
|
|
return Err(PERR::MissingToken(
|
|
|
|
"}".into(),
|
|
|
|
"to end this object map literal".into(),
|
|
|
|
)
|
|
|
|
.into_err(pos))
|
|
|
|
}
|
|
|
|
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
|
|
|
};
|
|
|
|
|
|
|
|
match input.next().ok_or_else(|| {
|
|
|
|
PERR::MissingToken(
|
|
|
|
":".into(),
|
|
|
|
format!(
|
|
|
|
"to follow the property '{}' in this object map literal",
|
|
|
|
name
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.into_err_eof()
|
|
|
|
})? {
|
|
|
|
(Token::Colon, _) => (),
|
|
|
|
(_, pos) => {
|
|
|
|
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().ok_or_else(|| {
|
|
|
|
PERR::MissingToken("}".into(), "to end this object map literal".into())
|
|
|
|
.into_err_eof()
|
|
|
|
})? {
|
|
|
|
(Token::Comma, _) => {
|
|
|
|
input.next();
|
|
|
|
}
|
|
|
|
(Token::RightBrace, _) => break,
|
|
|
|
(Token::Identifier(_), pos) => {
|
|
|
|
return Err(PERR::MissingToken(
|
|
|
|
",".into(),
|
|
|
|
"to separate the items of this object map literal".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
(_, pos) => {
|
|
|
|
return Err(PERR::MissingToken(
|
|
|
|
"}".into(),
|
|
|
|
"to end this object map literal".into(),
|
|
|
|
)
|
|
|
|
.into_err(*pos))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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))?;
|
|
|
|
|
|
|
|
// Ending brace
|
|
|
|
match input.peek().ok_or_else(|| {
|
|
|
|
PERR::MissingToken("}".into(), "to end this object map literal".into()).into_err_eof()
|
|
|
|
})? {
|
|
|
|
(Token::RightBrace, _) => {
|
|
|
|
input.next();
|
|
|
|
Ok(Expr::Map(map, begin))
|
|
|
|
}
|
|
|
|
(_, pos) => Err(
|
|
|
|
PERR::MissingToken("]".into(), "to end this object map literal".into()).into_err(*pos),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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, ParseError> {
|
2020-03-20 12:50:58 +01:00
|
|
|
let token = match input
|
|
|
|
.peek()
|
2020-03-24 09:46:47 +01:00
|
|
|
.ok_or_else(|| PERR::UnexpectedEOF.into_err_eof())?
|
2020-03-20 12:50:58 +01:00
|
|
|
{
|
|
|
|
// { - block statement as expression
|
2020-03-22 14:03:58 +01:00
|
|
|
(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
|
|
|
}
|
2020-03-20 12:50:58 +01:00
|
|
|
_ => input.next().expect("should be a token"),
|
|
|
|
};
|
2020-03-05 13:28:03 +01:00
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
let mut can_be_indexed = false;
|
2020-03-05 13:28:03 +01:00
|
|
|
|
2020-03-20 12:50:58 +01:00
|
|
|
let mut root_expr = match token {
|
2020-03-17 10:33:37 +01:00
|
|
|
(Token::IntegerConstant(x), pos) => Ok(Expr::IntegerConstant(x, pos)),
|
2020-04-10 06:16:39 +02:00
|
|
|
(Token::FloatConstant(x), pos) => Ok(Expr::FloatConstant(x, pos)),
|
2020-03-17 10:33:37 +01:00
|
|
|
(Token::CharConstant(c), pos) => Ok(Expr::CharConstant(c, pos)),
|
|
|
|
(Token::StringConst(s), pos) => {
|
|
|
|
can_be_indexed = true;
|
2020-04-06 06:29:01 +02:00
|
|
|
Ok(Expr::StringConstant(s.into(), pos))
|
2020-03-17 10:33:37 +01:00
|
|
|
}
|
|
|
|
(Token::Identifier(s), pos) => {
|
|
|
|
can_be_indexed = true;
|
2020-03-22 14:03:58 +01:00
|
|
|
parse_ident_expr(s, input, pos, allow_stmt_expr)
|
2020-03-17 10:33:37 +01:00
|
|
|
}
|
|
|
|
(Token::LeftParen, pos) => {
|
|
|
|
can_be_indexed = true;
|
2020-03-22 14:03:58 +01:00
|
|
|
parse_paren_expr(input, pos, allow_stmt_expr)
|
2020-03-17 10:33:37 +01:00
|
|
|
}
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
|
|
|
(Token::LeftBracket, pos) => {
|
|
|
|
can_be_indexed = true;
|
2020-03-22 14:03:58 +01:00
|
|
|
parse_array_literal(input, pos, allow_stmt_expr)
|
2020-03-17 10:33:37 +01:00
|
|
|
}
|
2020-03-29 17:53:35 +02:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
(Token::MapStart, pos) => {
|
|
|
|
can_be_indexed = true;
|
|
|
|
parse_map_literal(input, pos, allow_stmt_expr)
|
|
|
|
}
|
2020-03-17 10:33:37 +01:00
|
|
|
(Token::True, pos) => Ok(Expr::True(pos)),
|
|
|
|
(Token::False, pos) => Ok(Expr::False(pos)),
|
2020-03-24 09:46:47 +01:00
|
|
|
(Token::LexError(err), pos) => Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
|
|
|
(token, pos) => {
|
|
|
|
Err(PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(pos))
|
|
|
|
}
|
2020-03-17 10:33:37 +01:00
|
|
|
}?;
|
2020-03-05 13:28:03 +01:00
|
|
|
|
2020-04-10 06:16:39 +02:00
|
|
|
#[cfg(feature = "no_index")]
|
|
|
|
let can_be_indexed = false;
|
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
if can_be_indexed {
|
|
|
|
// Tail processing all possible indexing
|
2020-03-16 16:51:32 +01:00
|
|
|
while let Some((Token::LeftBracket, pos)) = input.peek() {
|
|
|
|
let pos = *pos;
|
2020-03-10 10:10:33 +01:00
|
|
|
input.next();
|
2020-03-22 14:03:58 +01:00
|
|
|
root_expr = parse_index_expr(Box::new(root_expr), input, pos, allow_stmt_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
|
|
|
|
|
|
|
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, ParseError> {
|
2020-03-16 16:51:32 +01:00
|
|
|
match input
|
|
|
|
.peek()
|
2020-03-24 09:46:47 +01:00
|
|
|
.ok_or_else(|| PERR::UnexpectedEOF.into_err_eof())?
|
2020-03-16 16:51:32 +01:00
|
|
|
{
|
2020-03-27 16:47:23 +01:00
|
|
|
// If statement is allowed to act as expressions
|
|
|
|
(Token::If, pos) => {
|
|
|
|
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
|
2020-03-16 16:51:32 +01:00
|
|
|
(Token::UnaryMinus, pos) => {
|
|
|
|
let pos = *pos;
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
input.next();
|
2020-03-02 15:13:14 +01:00
|
|
|
|
2020-03-22 14:03:58 +01:00
|
|
|
match parse_unary(input, 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-03-24 09:46:47 +01:00
|
|
|
PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string())
|
|
|
|
.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-03-16 16:51:32 +01:00
|
|
|
expr => Ok(Expr::FunctionCall("-".into(), vec![expr], 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-03-16 16:51:32 +01:00
|
|
|
(Token::UnaryPlus, _) => {
|
2019-09-18 12:21:07 +02:00
|
|
|
input.next();
|
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
|
2020-03-16 16:51:32 +01:00
|
|
|
(Token::Bang, pos) => {
|
|
|
|
let pos = *pos;
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
input.next();
|
2020-03-02 15:13:14 +01:00
|
|
|
|
2020-03-02 07:28:42 +01:00
|
|
|
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
|
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-03-18 03:36:50 +01:00
|
|
|
// All other tokens
|
2020-03-22 14:03:58 +01:00
|
|
|
_ => 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.
|
2020-03-07 03:16:20 +01:00
|
|
|
fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseError> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// Is the LHS in a valid format for an assignment target?
|
2020-03-13 11:12:41 +01:00
|
|
|
fn valid_assignment_chain(expr: &Expr, is_top: bool) -> Option<ParseError> {
|
2020-03-07 03:16:20 +01:00
|
|
|
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-07 15:50:46 +01:00
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// var[...]
|
2020-03-14 16:41:15 +01:00
|
|
|
Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) => {
|
2020-03-13 11:12:41 +01:00
|
|
|
assert!(is_top, "property expected but gets variable");
|
|
|
|
None
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// property[...]
|
2020-03-14 16:41:15 +01:00
|
|
|
Expr::Index(idx_lhs, _, _) if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) => {
|
2020-03-13 11:12:41 +01:00
|
|
|
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() {
|
|
|
|
Expr::Index(_, _, _) => Some(ParseErrorType::AssignmentToCopy.into_err(*pos)),
|
|
|
|
_ => Some(ParseErrorType::AssignmentToInvalidLHS.into_err(*pos)),
|
|
|
|
},
|
2020-03-07 15:50:46 +01:00
|
|
|
|
2020-03-18 03:36:50 +01:00
|
|
|
// dot_lhs.dot_rhs
|
2020-03-07 10:32:15 +01:00
|
|
|
Expr::Dot(dot_lhs, dot_rhs, _) => 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),
|
2020-03-18 03:36:50 +01:00
|
|
|
// var[...]
|
2020-03-13 11:12:41 +01:00
|
|
|
Expr::Index(idx_lhs, _, _)
|
2020-03-14 16:41:15 +01:00
|
|
|
if matches!(idx_lhs.as_ref(), &Expr::Variable(_, _)) && is_top =>
|
|
|
|
{
|
|
|
|
valid_assignment_chain(dot_rhs, false)
|
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// property[...]
|
2020-03-14 16:41:15 +01:00
|
|
|
Expr::Index(idx_lhs, _, _)
|
|
|
|
if matches!(idx_lhs.as_ref(), &Expr::Property(_, _)) && !is_top =>
|
2020-03-13 11:12:41 +01:00
|
|
|
{
|
|
|
|
valid_assignment_chain(dot_rhs, false)
|
2020-03-11 16:43:04 +01:00
|
|
|
}
|
2020-03-18 03:36:50 +01:00
|
|
|
// idx_lhs[...]
|
2020-03-24 09:46:47 +01:00
|
|
|
Expr::Index(idx_lhs, _, _) => {
|
|
|
|
Some(ParseErrorType::AssignmentToCopy.into_err(idx_lhs.position()))
|
|
|
|
}
|
2020-03-10 10:10:33 +01:00
|
|
|
|
2020-03-13 11:12:41 +01:00
|
|
|
expr => panic!("unexpected dot expression {:#?}", expr),
|
2020-03-07 03:16:20 +01:00
|
|
|
},
|
2020-03-07 15:50:46 +01:00
|
|
|
|
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:16:20 +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 15:50:46 +01:00
|
|
|
}
|
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, 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, ParseError> {
|
|
|
|
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.
|
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-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,
|
2019-09-18 12:21:07 +02:00
|
|
|
) -> Result<Expr, 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-02-29 13:12:10 +01:00
|
|
|
if let Some((op_token, pos)) = input.next() {
|
2020-03-02 15:13:14 +01:00
|
|
|
input.peek();
|
|
|
|
|
2020-03-22 14:03:58 +01:00
|
|
|
let rhs = parse_unary(input, allow_stmt_expr)?;
|
2016-04-14 03:01:08 +02:00
|
|
|
|
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
|
|
|
|
2020-03-03 09:23:55 +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),
|
2020-03-02 07:28:42 +01:00
|
|
|
|
2020-03-07 03:16:20 +01:00
|
|
|
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)?,
|
2020-03-10 11:32:01 +01:00
|
|
|
|
2020-03-29 11:15:12 +02:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-03-13 11:12:41 +01:00
|
|
|
Token::Period => {
|
2020-03-30 06:14:59 +02:00
|
|
|
fn check_property(expr: Expr) -> Result<Expr, ParseError> {
|
2020-03-13 11:12:41 +01:00
|
|
|
match expr {
|
2020-03-30 06:14:59 +02:00
|
|
|
// 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,
|
2020-03-30 06:14:59 +02:00
|
|
|
)),
|
|
|
|
// xxx.lhs[idx]
|
2020-03-13 11:12:41 +01:00
|
|
|
Expr::Index(lhs, idx, pos) => {
|
2020-03-30 06:14:59 +02:00
|
|
|
Ok(Expr::Index(Box::new(check_property(*lhs)?), idx, pos))
|
2020-03-13 11:12:41 +01:00
|
|
|
}
|
2020-03-30 06:14:59 +02: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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-30 06:14:59 +02:00
|
|
|
Expr::Dot(Box::new(current_lhs), Box::new(check_property(rhs)?), pos)
|
2020-03-13 11:12:41 +01:00
|
|
|
}
|
2020-03-02 07:28:42 +01:00
|
|
|
|
|
|
|
// Comparison operators default to false when passed invalid operands
|
2020-03-03 09:23:55 +01:00
|
|
|
Token::EqualsTo => Expr::FunctionCall(
|
|
|
|
"==".into(),
|
|
|
|
vec![current_lhs, rhs],
|
2020-04-12 17:00:06 +02:00
|
|
|
Some(Dynamic::from_bool(false)),
|
2020-03-03 09:23:55 +01:00
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::NotEqualsTo => Expr::FunctionCall(
|
|
|
|
"!=".into(),
|
|
|
|
vec![current_lhs, rhs],
|
2020-04-12 17:00:06 +02:00
|
|
|
Some(Dynamic::from_bool(false)),
|
2020-03-03 09:23:55 +01:00
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::LessThan => Expr::FunctionCall(
|
|
|
|
"<".into(),
|
|
|
|
vec![current_lhs, rhs],
|
2020-04-12 17:00:06 +02:00
|
|
|
Some(Dynamic::from_bool(false)),
|
2020-03-03 09:23:55 +01:00
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::LessThanEqualsTo => Expr::FunctionCall(
|
|
|
|
"<=".into(),
|
|
|
|
vec![current_lhs, rhs],
|
2020-04-12 17:00:06 +02:00
|
|
|
Some(Dynamic::from_bool(false)),
|
2020-03-03 09:23:55 +01:00
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::GreaterThan => Expr::FunctionCall(
|
|
|
|
">".into(),
|
|
|
|
vec![current_lhs, rhs],
|
2020-04-12 17:00:06 +02:00
|
|
|
Some(Dynamic::from_bool(false)),
|
2020-03-03 09:23:55 +01:00
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::GreaterThanEqualsTo => Expr::FunctionCall(
|
|
|
|
">=".into(),
|
|
|
|
vec![current_lhs, rhs],
|
2020-04-12 17:00:06 +02:00
|
|
|
Some(Dynamic::from_bool(false)),
|
2020-03-03 09:23:55 +01:00
|
|
|
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)?,
|
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
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)?,
|
2020-03-03 09:23:55 +01:00
|
|
|
Token::Pipe => Expr::FunctionCall("|".into(), vec![current_lhs, rhs], None, pos),
|
|
|
|
Token::LeftShift => {
|
|
|
|
Expr::FunctionCall("<<".into(), vec![current_lhs, rhs], None, pos)
|
|
|
|
}
|
2020-03-02 15:13:14 +01:00
|
|
|
Token::RightShift => {
|
2020-03-03 09:23:55 +01:00
|
|
|
Expr::FunctionCall(">>".into(), vec![current_lhs, rhs], None, pos)
|
2020-03-02 15:13:14 +01:00
|
|
|
}
|
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)?,
|
2020-03-03 09:23:55 +01:00
|
|
|
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)?,
|
2020-03-03 09:23:55 +01:00
|
|
|
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, 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
|
|
|
}
|
|
|
|
|
2020-03-20 12:50:58 +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<(), ParseError> {
|
|
|
|
match input
|
|
|
|
.peek()
|
2020-03-27 16:47:23 +01:00
|
|
|
.ok_or_else(|| PERR::ExprExpected(type_name.to_string()).into_err_eof())?
|
2020-03-20 12:50:58 +01:00
|
|
|
{
|
|
|
|
// Disallow statement expressions
|
2020-03-27 16:47:23 +01:00
|
|
|
(Token::LeftBrace, 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-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,
|
2020-03-17 10:33:37 +01:00
|
|
|
) -> Result<Stmt, ParseError> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// if ...
|
2016-02-29 22:43:45 +01:00
|
|
|
input.next();
|
|
|
|
|
2020-03-20 12:50:58 +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
|
|
|
|
2020-03-20 12:50:58 +01:00
|
|
|
// if guard { if_body } else ...
|
2020-03-16 16:51:32 +01:00
|
|
|
let else_body = if matches!(input.peek(), Some((Token::Else, _))) {
|
|
|
|
input.next();
|
2020-03-02 10:04:56 +01:00
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
Some(Box::new(if matches!(input.peek(), Some((Token::If, _))) {
|
2020-03-20 12:50:58 +01:00
|
|
|
// 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 {
|
2020-03-20 12:50:58 +01:00
|
|
|
// 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, ParseError> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// while ...
|
2016-02-29 22:43:45 +01:00
|
|
|
input.next();
|
|
|
|
|
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-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, ParseError> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// loop ...
|
2017-10-30 16:08:44 +01:00
|
|
|
input.next();
|
|
|
|
|
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, ParseError> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// for ...
|
2020-02-23 15:48:46 +01:00
|
|
|
input.next();
|
|
|
|
|
2020-03-18 11:41:18 +01:00
|
|
|
// for name ...
|
2020-03-16 16:51:32 +01:00
|
|
|
let name = match input
|
|
|
|
.next()
|
2020-03-24 09:46:47 +01:00
|
|
|
.ok_or_else(|| PERR::VariableExpected.into_err_eof())?
|
2020-03-16 16:51:32 +01:00
|
|
|
{
|
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-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-03-30 06:14:59 +02:00
|
|
|
match input.next().ok_or_else(|| {
|
|
|
|
PERR::MissingToken("in".into(), "after the iteration variable".into()).into_err_eof()
|
|
|
|
})? {
|
2020-03-16 16:51:32 +01:00
|
|
|
(Token::In, _) => (),
|
2020-03-30 06:14:59 +02:00
|
|
|
(_, pos) => {
|
|
|
|
return Err(
|
|
|
|
PERR::MissingToken("in".into(), "after the iteration variable".into())
|
|
|
|
.into_err(pos),
|
|
|
|
)
|
|
|
|
}
|
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-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-02-23 15:48:46 +01:00
|
|
|
|
2020-04-06 06:29:01 +02:00
|
|
|
Ok(Stmt::For(name.into(), 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-03-25 04:27:18 +01:00
|
|
|
var_type: ScopeEntryType,
|
2020-03-22 14:03:58 +01:00
|
|
|
allow_stmt_expr: bool,
|
2020-03-13 11:12:41 +01:00
|
|
|
) -> Result<Stmt, 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
|
2020-03-16 16:51:32 +01:00
|
|
|
.next()
|
2020-03-24 09:46:47 +01:00
|
|
|
.ok_or_else(|| PERR::VariableExpected.into_err_eof())?
|
2020-03-16 16:51:32 +01:00
|
|
|
{
|
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-03-16 16:51:32 +01:00
|
|
|
if matches!(input.peek(), Some((Token::Equals, _))) {
|
2020-03-14 16:41:15 +01:00
|
|
|
input.next();
|
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,
|
2020-03-17 10:33:37 +01:00
|
|
|
) -> Result<Stmt, ParseError> {
|
2020-03-18 11:41:18 +01:00
|
|
|
// Must start with {
|
2020-03-16 16:51:32 +01:00
|
|
|
let pos = match input
|
|
|
|
.next()
|
2020-03-29 07:44:27 +02:00
|
|
|
.ok_or_else(|| PERR::UnexpectedEOF.into_err_eof())?
|
2020-03-16 16:51:32 +01:00
|
|
|
{
|
|
|
|
(Token::LeftBrace, pos) => pos,
|
2020-03-29 07:44:27 +02:00
|
|
|
(_, 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
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
let mut statements = Vec::new();
|
2016-03-01 21:16:10 +01:00
|
|
|
|
2020-03-17 10:33:37 +01:00
|
|
|
while !matches!(input.peek(), Some((Token::RightBrace, _))) {
|
|
|
|
// Parse statements inside the block
|
2020-03-22 14:03:58 +01:00
|
|
|
let stmt = parse_stmt(input, breakable, 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-03-17 10:33:37 +01:00
|
|
|
match input.peek() {
|
2020-03-18 11:41:18 +01:00
|
|
|
// EOF
|
2020-03-17 10:33:37 +01:00
|
|
|
None => break,
|
2020-03-18 11:41:18 +01:00
|
|
|
// { ... stmt }
|
2020-03-17 10:33:37 +01:00
|
|
|
Some((Token::RightBrace, _)) => break,
|
2020-03-18 11:41:18 +01:00
|
|
|
// { ... stmt;
|
|
|
|
Some((Token::SemiColon, _)) if need_semicolon => {
|
2020-03-17 10:33:37 +01:00
|
|
|
input.next();
|
|
|
|
}
|
2020-03-18 11:41:18 +01:00
|
|
|
// { ... { stmt } ;
|
|
|
|
Some((Token::SemiColon, _)) if !need_semicolon => (),
|
|
|
|
// { ... { stmt } ???
|
2020-03-17 10:33:37 +01:00
|
|
|
Some((_, _)) if !need_semicolon => (),
|
2020-03-18 11:41:18 +01:00
|
|
|
// { ... stmt ??? - error
|
2020-03-17 10:33:37 +01:00
|
|
|
Some((_, pos)) => {
|
|
|
|
// Semicolons are not optional between statements
|
2020-03-29 07:44:27 +02:00
|
|
|
return Err(
|
|
|
|
PERR::MissingToken(";".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-03-29 07:44:27 +02:00
|
|
|
match input.peek().ok_or_else(|| {
|
|
|
|
PERR::MissingToken("}".into(), "to end this statement block".into()).into_err_eof()
|
|
|
|
})? {
|
2020-03-16 16:51:32 +01:00
|
|
|
(Token::RightBrace, _) => {
|
2016-04-17 04:32:18 +02:00
|
|
|
input.next();
|
2020-03-09 14:57:07 +01:00
|
|
|
Ok(Stmt::Block(statements, pos))
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-29 07:44:27 +02:00
|
|
|
(_, pos) => {
|
|
|
|
Err(PERR::MissingToken("}".into(), "to end this statement block".into()).into_err(*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, ParseError> {
|
|
|
|
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,
|
2020-03-17 10:33:37 +01:00
|
|
|
) -> Result<Stmt, ParseError> {
|
2020-03-20 12:50:58 +01:00
|
|
|
let token = match input.peek() {
|
|
|
|
Some(token) => token,
|
|
|
|
None => return Ok(Stmt::Noop(Position::eof())),
|
|
|
|
};
|
|
|
|
|
|
|
|
match token {
|
2020-03-18 11:41:18 +01:00
|
|
|
// Semicolon - empty statement
|
|
|
|
(Token::SemiColon, pos) => Ok(Stmt::Noop(*pos)),
|
|
|
|
|
2020-04-01 10:22:18 +02:00
|
|
|
(Token::LeftBrace, _) => parse_block(input, breakable, allow_stmt_expr),
|
|
|
|
|
2020-03-18 11:41:18 +01:00
|
|
|
// fn ...
|
2020-03-17 07:29:22 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-03-24 09:46:47 +01:00
|
|
|
(Token::Fn, pos) => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
2020-03-17 07:29:22 +01:00
|
|
|
|
2020-03-22 14:03:58 +01:00
|
|
|
(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, pos) if breakable => {
|
|
|
|
let pos = *pos;
|
|
|
|
input.next();
|
|
|
|
Ok(Stmt::Continue(pos))
|
|
|
|
}
|
2020-03-17 10:33:37 +01:00
|
|
|
(Token::Break, pos) if breakable => {
|
2020-03-16 16:51:32 +01:00
|
|
|
let pos = *pos;
|
2016-04-17 04:32:18 +02:00
|
|
|
input.next();
|
2020-03-02 15:13:14 +01:00
|
|
|
Ok(Stmt::Break(pos))
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-04-01 10:22:18 +02:00
|
|
|
(Token::Continue, pos) | (Token::Break, pos) => Err(PERR::LoopBreak.into_err(*pos)),
|
|
|
|
|
2020-03-19 12:53:42 +01:00
|
|
|
(token @ Token::Return, pos) | (token @ Token::Throw, pos) => {
|
2020-03-11 16:43:04 +01:00
|
|
|
let return_type = match token {
|
|
|
|
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-03-19 12:53:42 +01:00
|
|
|
let pos = *pos;
|
2016-04-14 03:01:08 +02:00
|
|
|
input.next();
|
2020-03-03 11:15:20 +01:00
|
|
|
|
2016-03-03 15:31:42 +01:00
|
|
|
match input.peek() {
|
2020-03-18 03:36:50 +01:00
|
|
|
// `return`/`throw` at EOF
|
2020-03-11 16:43:04 +01:00
|
|
|
None => Ok(Stmt::ReturnWithVal(None, return_type, Position::eof())),
|
2020-03-18 03:36:50 +01:00
|
|
|
// `return;` or `throw;`
|
2020-03-19 12:53:42 +01:00
|
|
|
Some((Token::SemiColon, _)) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
2020-03-18 03:36:50 +01:00
|
|
|
// `return` or `throw` with expression
|
2020-03-19 12:53:42 +01:00
|
|
|
Some((_, _)) => {
|
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))
|
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-03-25 04:27:18 +01: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, ParseError> {
|
2020-03-20 12:50:58 +01:00
|
|
|
let pos = input.next().expect("should be fn").1;
|
2020-03-16 16:51:32 +01:00
|
|
|
|
|
|
|
let name = match input
|
|
|
|
.next()
|
2020-03-24 09:46:47 +01:00
|
|
|
.ok_or_else(|| PERR::FnMissingName.into_err_eof())?
|
2020-03-16 16:51:32 +01:00
|
|
|
{
|
|
|
|
(Token::Identifier(s), _) => s,
|
2020-03-24 09:46:47 +01:00
|
|
|
(_, pos) => return Err(PERR::FnMissingName.into_err(pos)),
|
2016-03-01 21:16:10 +01:00
|
|
|
};
|
|
|
|
|
2020-03-16 16:51:32 +01:00
|
|
|
match input
|
|
|
|
.peek()
|
2020-03-24 09:46:47 +01:00
|
|
|
.ok_or_else(|| PERR::FnMissingParams(name.clone()).into_err_eof())?
|
2020-03-16 16:51:32 +01:00
|
|
|
{
|
2020-04-06 11:47:34 +02:00
|
|
|
(Token::LeftParen, _) => input.next(),
|
2020-03-24 09:46:47 +01:00
|
|
|
(_, 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-03-16 16:51:32 +01:00
|
|
|
if matches!(input.peek(), Some((Token::RightParen, _))) {
|
2020-03-14 16:41:15 +01:00
|
|
|
input.next();
|
|
|
|
} else {
|
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-03-24 09:46:47 +01:00
|
|
|
match input
|
|
|
|
.next()
|
2020-03-29 07:44:27 +02:00
|
|
|
.ok_or_else(|| PERR::MissingToken(")".into(), end_err.to_string()).into_err_eof())?
|
2020-03-24 09:46:47 +01:00
|
|
|
{
|
2020-04-06 11:47:34 +02:00
|
|
|
(Token::Identifier(s), pos) => params.push((s, pos)),
|
2020-03-29 07:44:27 +02:00
|
|
|
(_, pos) => return Err(PERR::MissingToken(")".into(), end_err).into_err(pos)),
|
2020-03-16 16:51:32 +01:00
|
|
|
}
|
|
|
|
|
2020-03-24 09:46:47 +01:00
|
|
|
match input
|
|
|
|
.next()
|
2020-03-29 07:44:27 +02:00
|
|
|
.ok_or_else(|| PERR::MissingToken(")".into(), end_err.to_string()).into_err_eof())?
|
2020-03-24 09:46:47 +01:00
|
|
|
{
|
2020-03-16 16:51:32 +01:00
|
|
|
(Token::RightParen, _) => break,
|
|
|
|
(Token::Comma, _) => (),
|
2020-03-29 07:44:27 +02:00
|
|
|
(Token::Identifier(_), pos) => {
|
|
|
|
return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos))
|
|
|
|
}
|
|
|
|
(_, pos) => return Err(PERR::MissingToken(",".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-03-17 10:33:37 +01:00
|
|
|
let body = match input.peek() {
|
2020-03-22 14:03:58 +01:00
|
|
|
Some((Token::LeftBrace, _)) => parse_block(input, false, allow_stmt_expr)?,
|
2020-03-24 09:46:47 +01:00
|
|
|
Some((_, pos)) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
|
|
|
|
None => return Err(PERR::FnMissingBody(name).into_err_eof()),
|
2020-03-17 10:33:37 +01: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-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-03-22 14:03:58 +01:00
|
|
|
pub fn parse_global_expr<'a, 'e>(
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
|
|
|
engine: &Engine<'e>,
|
|
|
|
scope: &Scope,
|
2020-04-10 11:14:07 +02:00
|
|
|
optimization_level: OptimizationLevel,
|
2020-03-22 14:03:58 +01:00
|
|
|
) -> Result<AST, ParseError> {
|
|
|
|
let expr = parse_expr(input, false)?;
|
|
|
|
|
|
|
|
if let Some((token, pos)) = input.peek() {
|
|
|
|
// Return error if the expression doesn't end
|
2020-03-24 09:46:47 +01:00
|
|
|
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-03-15 15:39:58 +01:00
|
|
|
) -> Result<(Vec<Stmt>, Vec<FnDef>), ParseError> {
|
2020-03-11 06:28:12 +01:00
|
|
|
let mut statements = Vec::<Stmt>::new();
|
|
|
|
let mut functions = Vec::<FnDef>::new();
|
2016-03-01 21:16:10 +01:00
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
while input.peek().is_some() {
|
2020-04-10 06:16:39 +02:00
|
|
|
// Collect all the function definitions
|
2020-03-17 07:29:22 +01:00
|
|
|
#[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-03-12 06:02:13 +01:00
|
|
|
|
|
|
|
// Ensure list is sorted
|
|
|
|
match functions.binary_search_by(|fn_def| fn_def.compare(&f.name, f.params.len())) {
|
|
|
|
Ok(n) => functions[n] = f, // Override previous definition
|
|
|
|
Err(n) => functions.insert(n, f), // New function definition
|
|
|
|
}
|
2020-03-17 07:29:22 +01:00
|
|
|
|
|
|
|
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)?;
|
2020-03-17 07:29:22 +01:00
|
|
|
|
|
|
|
let need_semicolon = !stmt.is_self_terminated();
|
|
|
|
|
|
|
|
statements.push(stmt);
|
|
|
|
|
|
|
|
match input.peek() {
|
2020-03-18 11:41:18 +01:00
|
|
|
// EOF
|
2020-03-17 07:29:22 +01:00
|
|
|
None => break,
|
2020-03-18 11:41:18 +01:00
|
|
|
// stmt ;
|
|
|
|
Some((Token::SemiColon, _)) if need_semicolon => {
|
2020-03-17 07:29:22 +01:00
|
|
|
input.next();
|
|
|
|
}
|
2020-03-18 11:41:18 +01:00
|
|
|
// stmt ;
|
|
|
|
Some((Token::SemiColon, _)) if !need_semicolon => (),
|
|
|
|
// { stmt } ???
|
2020-03-17 07:29:22 +01:00
|
|
|
Some((_, _)) if !need_semicolon => (),
|
2020-03-18 11:41:18 +01:00
|
|
|
// stmt ??? - error
|
2020-03-17 07:29:22 +01:00
|
|
|
Some((_, pos)) => {
|
|
|
|
// Semicolons are not optional between statements
|
2020-03-29 07:44:27 +02:00
|
|
|
return Err(
|
|
|
|
PERR::MissingToken(";".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-03-15 15:39:58 +01:00
|
|
|
pub fn parse<'a, 'e>(
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
|
|
|
engine: &Engine<'e>,
|
|
|
|
scope: &Scope,
|
2020-04-10 06:16:39 +02:00
|
|
|
optimization_level: OptimizationLevel,
|
2020-03-15 15:39:58 +01:00
|
|
|
) -> Result<AST, 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-03-14 13:06:10 +01:00
|
|
|
Ok(
|
2020-03-18 11:41:18 +01:00
|
|
|
// Optimize AST
|
2020-04-08 04:19:03 +02:00
|
|
|
optimize_into_ast(engine, scope, statements, functions, 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)),
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
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
|
|
|
}
|