2020-03-08 12:54:02 +01:00
|
|
|
//! Main module defining the lexer and parser.
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
use crate::any::Dynamic;
|
|
|
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
2020-03-14 07:30:44 +01:00
|
|
|
use crate::scope::{Scope, VariableType};
|
2020-03-11 04:03:18 +01:00
|
|
|
|
2020-03-14 13:06:10 +01:00
|
|
|
#[cfg(not(feature = "no_optimize"))]
|
|
|
|
use crate::optimize::optimize;
|
|
|
|
|
2020-03-12 05:40:28 +01:00
|
|
|
use std::{
|
|
|
|
borrow::Cow, char, cmp::Ordering, fmt, iter::Peekable, str::Chars, str::FromStr, sync::Arc,
|
|
|
|
usize,
|
|
|
|
};
|
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;
|
|
|
|
|
|
|
|
/// 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;
|
|
|
|
|
|
|
|
/// The system floating-point type
|
2020-03-14 13:06:10 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
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-04 02:33:35 +01:00
|
|
|
line: usize,
|
|
|
|
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-08 15:47:13 +01:00
|
|
|
if line == 0 || (line == usize::MAX && position == usize::MAX) {
|
|
|
|
panic!("invalid position: ({}, {})", line, position);
|
|
|
|
}
|
|
|
|
|
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-08 15:47:13 +01:00
|
|
|
if self.is_none() || self.is_eof() {
|
|
|
|
None
|
|
|
|
} else if self.pos == 0 {
|
|
|
|
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-03-11 16:43:04 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2020-03-11 06:28:12 +01:00
|
|
|
pub struct AST(
|
|
|
|
pub(crate) Vec<Stmt>,
|
2020-03-11 16:43:04 +01:00
|
|
|
#[cfg(not(feature = "no_function"))] pub(crate) Vec<Arc<FnDef>>,
|
2020-03-11 06:28:12 +01:00
|
|
|
);
|
2020-02-24 16:25:52 +01:00
|
|
|
|
2020-03-14 07:30:44 +01:00
|
|
|
impl AST {
|
|
|
|
/// Optimize the AST with constants defined in an external Scope.
|
|
|
|
///
|
|
|
|
/// Although optimization is performed by default during compilation, sometimes it is necessary to
|
2020-03-14 07:57:59 +01:00
|
|
|
/// _re_-optimize an AST. For example, when working with constants that are passed in via an
|
2020-03-14 07:30:44 +01:00
|
|
|
/// external scope, it will be more efficient to optimize the AST once again to take advantage
|
|
|
|
/// of the new constants.
|
|
|
|
///
|
|
|
|
/// With this method, it is no longer necessary to regenerate a large script with hard-coded
|
2020-03-14 07:57:59 +01:00
|
|
|
/// constant values. The script AST can be compiled just once. During actual evaluation,
|
2020-03-14 07:30:44 +01:00
|
|
|
/// constants are passed into the Engine via an external scope (i.e. with `scope.push_constant(...)`).
|
|
|
|
/// Then, the AST is cloned and the copy re-optimized before running.
|
2020-03-14 13:06:10 +01:00
|
|
|
#[cfg(not(feature = "no_optimize"))]
|
2020-03-14 07:30:44 +01:00
|
|
|
pub fn optimize(self, scope: &Scope) -> Self {
|
|
|
|
AST(
|
|
|
|
crate::optimize::optimize(self.0, scope),
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
self.1
|
|
|
|
.into_iter()
|
|
|
|
.map(|fn_def| {
|
|
|
|
let pos = fn_def.body.position();
|
|
|
|
let body = optimize(vec![fn_def.body.clone()], scope)
|
|
|
|
.into_iter()
|
|
|
|
.next()
|
|
|
|
.unwrap_or_else(|| Stmt::Noop(pos));
|
|
|
|
Arc::new(FnDef {
|
|
|
|
name: fn_def.name.clone(),
|
|
|
|
params: fn_def.params.clone(),
|
|
|
|
body,
|
|
|
|
pos: fn_def.pos,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 16:43:04 +01:00
|
|
|
#[derive(Debug)] // Do not derive Clone because it is expensive
|
|
|
|
pub struct FnDef {
|
|
|
|
pub name: String,
|
|
|
|
pub params: Vec<String>,
|
2020-03-09 14:57:07 +01:00
|
|
|
pub body: Stmt,
|
2020-03-02 15:13:14 +01:00
|
|
|
pub pos: Position,
|
2016-03-01 21:16:10 +01:00
|
|
|
}
|
|
|
|
|
2020-03-12 05:40:28 +01:00
|
|
|
impl FnDef {
|
|
|
|
pub fn compare(&self, name: &str, params_len: usize) -> Ordering {
|
|
|
|
match self.name.as_str().cmp(name) {
|
|
|
|
Ordering::Equal => self.params.len().cmp(¶ms_len),
|
|
|
|
order => order,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 16:43:04 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
|
|
pub enum ReturnType {
|
|
|
|
Return,
|
|
|
|
Exception,
|
|
|
|
}
|
|
|
|
|
2020-03-02 07:28:42 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2016-04-17 04:32:18 +02:00
|
|
|
pub enum Stmt {
|
2020-03-09 14:57:07 +01:00
|
|
|
Noop(Position),
|
2020-03-02 10:04:56 +01:00
|
|
|
IfElse(Box<Expr>, Box<Stmt>, Option<Box<Stmt>>),
|
2016-04-17 04:32:18 +02:00
|
|
|
While(Box<Expr>, Box<Stmt>),
|
2017-10-30 16:08:44 +01:00
|
|
|
Loop(Box<Stmt>),
|
2020-02-23 15:48:46 +01:00
|
|
|
For(String, Box<Expr>, Box<Stmt>),
|
2020-03-02 15:13:14 +01:00
|
|
|
Let(String, Option<Box<Expr>>, Position),
|
2020-03-13 11:12:41 +01:00
|
|
|
Const(String, Box<Expr>, Position),
|
2020-03-09 14:57:07 +01:00
|
|
|
Block(Vec<Stmt>, Position),
|
2016-04-17 04:32:18 +02:00
|
|
|
Expr(Box<Expr>),
|
2020-03-02 15:13:14 +01:00
|
|
|
Break(Position),
|
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-12 16:46:52 +01:00
|
|
|
pub fn is_noop(&self) -> bool {
|
2020-03-14 16:41:15 +01:00
|
|
|
matches!(self, Stmt::Noop(_))
|
2020-03-12 16:46:52 +01:00
|
|
|
}
|
|
|
|
|
2020-03-09 14:57:07 +01:00
|
|
|
pub fn is_op(&self) -> bool {
|
2020-03-14 16:41:15 +01:00
|
|
|
!matches!(self, Stmt::Noop(_))
|
2020-03-09 14:57:07 +01:00
|
|
|
}
|
2020-03-11 16:43:04 +01:00
|
|
|
|
|
|
|
pub fn is_var(&self) -> bool {
|
2020-03-14 16:41:15 +01:00
|
|
|
matches!(self, Stmt::Let(_, _, _))
|
2020-03-11 16:43:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn position(&self) -> Position {
|
|
|
|
match self {
|
|
|
|
Stmt::Noop(pos)
|
|
|
|
| Stmt::Let(_, _, pos)
|
2020-03-13 11:12:41 +01:00
|
|
|
| Stmt::Const(_, _, pos)
|
2020-03-11 16:43:04 +01:00
|
|
|
| Stmt::Block(_, pos)
|
|
|
|
| Stmt::Break(pos)
|
|
|
|
| Stmt::ReturnWithVal(_, _, pos) => *pos,
|
|
|
|
Stmt::IfElse(expr, _, _) | Stmt::Expr(expr) => expr.position(),
|
|
|
|
Stmt::While(_, stmt) | Stmt::Loop(stmt) | Stmt::For(_, _, stmt) => stmt.position(),
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 14:57:07 +01:00
|
|
|
}
|
|
|
|
|
2020-03-02 07:28:42 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2016-04-17 04:32:18 +02:00
|
|
|
pub enum Expr {
|
2020-03-10 16:06:20 +01:00
|
|
|
IntegerConstant(INT, Position),
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-10 16:06:20 +01:00
|
|
|
FloatConstant(FLOAT, Position),
|
2020-03-13 11:12:41 +01:00
|
|
|
Variable(String, Position),
|
|
|
|
Property(String, Position),
|
2020-03-02 15:13:14 +01:00
|
|
|
CharConstant(char, Position),
|
|
|
|
StringConstant(String, Position),
|
2020-03-09 14:57:07 +01:00
|
|
|
Stmt(Box<Stmt>, Position),
|
2020-03-02 15:13:14 +01:00
|
|
|
FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position),
|
2020-03-07 03:16:20 +01:00
|
|
|
Assignment(Box<Expr>, Box<Expr>, Position),
|
|
|
|
Dot(Box<Expr>, Box<Expr>, Position),
|
|
|
|
Index(Box<Expr>, Box<Expr>, Position),
|
2020-03-02 15:13:14 +01:00
|
|
|
Array(Vec<Expr>, Position),
|
2020-03-02 05:08:03 +01:00
|
|
|
And(Box<Expr>, Box<Expr>),
|
|
|
|
Or(Box<Expr>, Box<Expr>),
|
2020-03-02 15:13:14 +01:00
|
|
|
True(Position),
|
|
|
|
False(Position),
|
|
|
|
Unit(Position),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Expr {
|
2020-03-13 11:12:41 +01:00
|
|
|
pub fn get_value_str(&self) -> String {
|
|
|
|
match self {
|
|
|
|
Expr::IntegerConstant(i, _) => i.to_string(),
|
|
|
|
Expr::CharConstant(c, _) => c.to_string(),
|
|
|
|
Expr::StringConstant(_, _) => "string".to_string(),
|
|
|
|
Expr::True(_) => "true".to_string(),
|
|
|
|
Expr::False(_) => "false".to_string(),
|
|
|
|
Expr::Unit(_) => "()".to_string(),
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Expr::FloatConstant(f, _) => f.to_string(),
|
|
|
|
|
|
|
|
_ => "".to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 15:13:14 +01:00
|
|
|
pub fn position(&self) -> Position {
|
|
|
|
match self {
|
|
|
|
Expr::IntegerConstant(_, pos)
|
|
|
|
| Expr::CharConstant(_, pos)
|
|
|
|
| Expr::StringConstant(_, pos)
|
2020-03-13 11:12:41 +01:00
|
|
|
| Expr::Variable(_, pos)
|
|
|
|
| Expr::Property(_, pos)
|
2020-03-09 14:57:07 +01:00
|
|
|
| Expr::Stmt(_, pos)
|
2020-03-11 04:03:18 +01:00
|
|
|
| Expr::FunctionCall(_, _, _, pos)
|
|
|
|
| Expr::Array(_, pos)
|
2020-03-02 15:13:14 +01:00
|
|
|
| Expr::True(pos)
|
|
|
|
| Expr::False(pos)
|
|
|
|
| Expr::Unit(pos) => *pos,
|
|
|
|
|
2020-03-11 04:03:18 +01:00
|
|
|
Expr::Assignment(e, _, _)
|
|
|
|
| Expr::Dot(e, _, _)
|
|
|
|
| Expr::Index(e, _, _)
|
|
|
|
| Expr::And(e, _)
|
|
|
|
| Expr::Or(e, _) => e.position(),
|
2020-03-10 10:10:33 +01:00
|
|
|
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Expr::FloatConstant(_, pos) => *pos,
|
2020-03-02 15:13:14 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-10 04:22:41 +01:00
|
|
|
|
2020-03-11 16:43:04 +01:00
|
|
|
/// Is this expression pure?
|
|
|
|
///
|
|
|
|
/// A pure expression has no side effects.
|
|
|
|
pub fn is_pure(&self) -> bool {
|
2020-03-10 04:22:41 +01:00
|
|
|
match self {
|
2020-03-12 05:40:28 +01:00
|
|
|
Expr::Array(expressions, _) => expressions.iter().all(Expr::is_pure),
|
2020-03-12 16:46:52 +01:00
|
|
|
Expr::And(x, y) | Expr::Or(x, y) | Expr::Index(x, y, _) => x.is_pure() && y.is_pure(),
|
2020-03-14 16:41:15 +01:00
|
|
|
expr => expr.is_constant() || matches!(expr, Expr::Variable(_, _)),
|
2020-03-12 05:40:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_constant(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Expr::IntegerConstant(_, _)
|
2020-03-10 04:22:41 +01:00
|
|
|
| Expr::CharConstant(_, _)
|
|
|
|
| Expr::StringConstant(_, _)
|
|
|
|
| Expr::True(_)
|
|
|
|
| Expr::False(_)
|
|
|
|
| Expr::Unit(_) => true,
|
|
|
|
|
2020-03-14 12:46:44 +01:00
|
|
|
Expr::Array(expressions, _) => expressions.iter().all(Expr::is_constant),
|
|
|
|
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Expr::FloatConstant(_, _) => true,
|
|
|
|
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
|
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),
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-10 16:06:20 +01:00
|
|
|
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-02-24 16:25:52 +01:00
|
|
|
SemiColon,
|
2016-04-17 04:32:18 +02:00
|
|
|
Colon,
|
|
|
|
Comma,
|
|
|
|
Period,
|
|
|
|
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,
|
2016-04-17 04:32:18 +02:00
|
|
|
LessThan,
|
|
|
|
GreaterThan,
|
|
|
|
Bang,
|
2020-02-24 16:25:52 +01:00
|
|
|
LessThanEqualsTo,
|
|
|
|
GreaterThanEqualsTo,
|
|
|
|
EqualsTo,
|
|
|
|
NotEqualsTo,
|
2016-04-17 04:32:18 +02:00
|
|
|
Pipe,
|
|
|
|
Or,
|
|
|
|
Ampersand,
|
|
|
|
And,
|
|
|
|
Fn,
|
|
|
|
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,
|
2017-10-31 06:55:20 +01:00
|
|
|
LeftShift,
|
|
|
|
RightShift,
|
|
|
|
XOr,
|
2017-10-31 23:06:13 +01:00
|
|
|
Modulo,
|
2017-12-21 12:32:18 +01:00
|
|
|
ModuloAssign,
|
2017-11-24 08:56:22 +01:00
|
|
|
PowerOf,
|
2017-12-21 12:32:18 +01:00
|
|
|
PowerOfAssign,
|
2020-02-23 15:48:46 +01:00
|
|
|
For,
|
|
|
|
In,
|
2020-03-04 15:00:01 +01:00
|
|
|
LexError(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-07 03:15:42 +01:00
|
|
|
pub fn syntax<'a>(&'a self) -> Cow<'a, str> {
|
2020-03-03 09:23:55 +01:00
|
|
|
use self::Token::*;
|
|
|
|
|
|
|
|
match *self {
|
2020-03-07 03:15:42 +01:00
|
|
|
IntegerConstant(ref i) => i.to_string().into(),
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-07 03:15:42 +01:00
|
|
|
FloatConstant(ref f) => f.to_string().into(),
|
|
|
|
Identifier(ref s) => s.into(),
|
|
|
|
CharConstant(ref c) => c.to_string().into(),
|
2020-03-04 15:00:01 +01:00
|
|
|
LexError(ref err) => err.to_string().into(),
|
2020-03-03 09:23:55 +01:00
|
|
|
|
|
|
|
ref token => (match token {
|
|
|
|
StringConst(_) => "string",
|
|
|
|
LeftBrace => "{",
|
|
|
|
RightBrace => "}",
|
|
|
|
LeftParen => "(",
|
|
|
|
RightParen => ")",
|
|
|
|
LeftBracket => "[",
|
|
|
|
RightBracket => "]",
|
|
|
|
Plus => "+",
|
|
|
|
UnaryPlus => "+",
|
|
|
|
Minus => "-",
|
|
|
|
UnaryMinus => "-",
|
|
|
|
Multiply => "*",
|
|
|
|
Divide => "/",
|
|
|
|
SemiColon => ";",
|
|
|
|
Colon => ":",
|
|
|
|
Comma => ",",
|
|
|
|
Period => ".",
|
|
|
|
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 => "&&",
|
|
|
|
Fn => "fn",
|
|
|
|
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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 16:08:44 +01:00
|
|
|
// if another operator is after these, it's probably an unary operator
|
|
|
|
// not sure about fn's name
|
|
|
|
pub fn is_next_unary(&self) -> bool {
|
|
|
|
use self::Token::*;
|
|
|
|
|
|
|
|
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
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
2020-03-04 15:00:01 +01:00
|
|
|
pub fn is_binary_op(&self) -> bool {
|
2017-10-30 16:08:44 +01:00
|
|
|
use self::Token::*;
|
|
|
|
|
|
|
|
match *self {
|
2020-03-04 15:00:01 +01:00
|
|
|
RightBrace | RightParen | RightBracket | Plus | Minus | Multiply | Divide | Comma
|
|
|
|
| Equals | LessThan | GreaterThan | LessThanEqualsTo | GreaterThanEqualsTo
|
|
|
|
| EqualsTo | NotEqualsTo | Pipe | Or | Ampersand | And | PowerOf => true,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2017-10-30 16:08:44 +01:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
2020-03-04 15:00:01 +01:00
|
|
|
pub fn is_unary_op(&self) -> bool {
|
2017-10-30 16:08:44 +01:00
|
|
|
use self::Token::*;
|
|
|
|
|
|
|
|
match *self {
|
2020-03-03 11:15:20 +01:00
|
|
|
UnaryPlus | UnaryMinus | Equals | Bang | Return | Throw => true,
|
2017-10-30 16:08:44 +01:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-29 22:43:45 +01:00
|
|
|
pub struct TokenIterator<'a> {
|
2017-10-30 16:08:44 +01:00
|
|
|
last: Token,
|
2020-02-29 13:12:10 +01:00
|
|
|
pos: Position,
|
2016-04-17 04:32:18 +02:00
|
|
|
char_stream: Peekable<Chars<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TokenIterator<'a> {
|
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-04 02:33:35 +01:00
|
|
|
fn rewind(&mut self) {
|
|
|
|
self.pos.rewind();
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_string_const(
|
|
|
|
&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 {
|
|
|
|
let next_char = self.char_stream.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-03 14:39:25 +01:00
|
|
|
'\\' if escape.is_empty() => {
|
|
|
|
escape.push('\\');
|
|
|
|
}
|
|
|
|
'\\' if !escape.is_empty() => {
|
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
result.push('\\');
|
|
|
|
}
|
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-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-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-03 14:39:25 +01:00
|
|
|
'x' if !escape.is_empty() => {
|
|
|
|
let mut seq = escape.clone();
|
|
|
|
seq.push('x');
|
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
let mut out_val: u32 = 0;
|
|
|
|
for _ in 0..2 {
|
|
|
|
if let Some(c) = self.char_stream.next() {
|
2020-03-03 14:39:25 +01:00
|
|
|
seq.push(c);
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-03-03 14:39:25 +01:00
|
|
|
|
2016-04-17 04:32:18 +02:00
|
|
|
if let Some(d1) = c.to_digit(16) {
|
|
|
|
out_val *= 16;
|
|
|
|
out_val += d1;
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(r) = char::from_u32(out_val) {
|
|
|
|
result.push(r);
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-03 14:39:25 +01:00
|
|
|
'u' if !escape.is_empty() => {
|
|
|
|
let mut seq = escape.clone();
|
|
|
|
seq.push('u');
|
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
let mut out_val: u32 = 0;
|
|
|
|
for _ in 0..4 {
|
|
|
|
if let Some(c) = self.char_stream.next() {
|
2020-03-03 14:39:25 +01:00
|
|
|
seq.push(c);
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-03-03 14:39:25 +01:00
|
|
|
|
2016-04-17 04:32:18 +02:00
|
|
|
if let Some(d1) = c.to_digit(16) {
|
|
|
|
out_val *= 16;
|
|
|
|
out_val += d1;
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(r) = char::from_u32(out_val) {
|
|
|
|
result.push(r);
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-03 14:39:25 +01:00
|
|
|
'U' if !escape.is_empty() => {
|
|
|
|
let mut seq = escape.clone();
|
|
|
|
seq.push('U');
|
|
|
|
escape.clear();
|
2016-04-17 04:32:18 +02:00
|
|
|
let mut out_val: u32 = 0;
|
|
|
|
for _ in 0..8 {
|
|
|
|
if let Some(c) = self.char_stream.next() {
|
2020-03-03 14:39:25 +01:00
|
|
|
seq.push(c);
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-03-03 14:39:25 +01:00
|
|
|
|
2016-04-17 04:32:18 +02:00
|
|
|
if let Some(d1) = c.to_digit(16) {
|
|
|
|
out_val *= 16;
|
|
|
|
out_val += d1;
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(r) = char::from_u32(out_val) {
|
|
|
|
result.push(r);
|
|
|
|
} else {
|
2020-03-03 14:39:25 +01:00
|
|
|
return Err((LERR::MalformedEscapeSequence(seq), self.pos));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-03 14:39:25 +01:00
|
|
|
x if enclosing_char == x && !escape.is_empty() => result.push(x),
|
|
|
|
x if enclosing_char == x && escape.is_empty() => break,
|
|
|
|
_ if !escape.is_empty() => {
|
|
|
|
return Err((LERR::MalformedEscapeSequence(escape), self.pos))
|
|
|
|
}
|
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-02-24 16:25:52 +01:00
|
|
|
x => {
|
2020-03-03 14:39:25 +01:00
|
|
|
escape.clear();
|
2020-02-24 16:25:52 +01:00
|
|
|
result.push(x);
|
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-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;
|
|
|
|
|
2016-02-29 22:43:45 +01:00
|
|
|
while let Some(c) = self.char_stream.next() {
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
|
|
|
|
let pos = self.pos;
|
|
|
|
|
2016-02-29 22:43:45 +01:00
|
|
|
match c {
|
2020-03-04 02:33:35 +01:00
|
|
|
'\n' => self.new_line(),
|
2019-09-18 12:21:07 +02: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-03-03 06:30:46 +01:00
|
|
|
while let Some(&next_char) = self.char_stream.peek() {
|
|
|
|
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);
|
2016-04-17 04:32:18 +02:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
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);
|
2017-10-30 04:32:41 +01:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-03-03 06:30:46 +01:00
|
|
|
while let Some(&next_char_in_float) = self.char_stream.peek() {
|
|
|
|
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);
|
2017-10-30 04:32:41 +01:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2017-10-30 04:32:41 +01:00
|
|
|
}
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-03 14:39:25 +01:00
|
|
|
'x' | 'X' if c == '0' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char);
|
2017-11-01 06:02:36 +01:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-03-03 06:30:46 +01:00
|
|
|
while let Some(&next_char_in_hex) = self.char_stream.peek() {
|
|
|
|
match next_char_in_hex {
|
2020-03-03 14:39:25 +01:00
|
|
|
'0'..='9' | 'a'..='f' | 'A'..='F' | '_' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char_in_hex);
|
2017-11-01 06:02:36 +01:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2017-11-01 06:02:36 +01:00
|
|
|
}
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
2017-11-01 06:10:46 +01:00
|
|
|
radix_base = Some(16);
|
2017-11-01 06:02:36 +01:00
|
|
|
}
|
2020-03-03 14:39:25 +01:00
|
|
|
'o' | 'O' if c == '0' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char);
|
2017-11-01 06:02:36 +01:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-03-03 06:30:46 +01:00
|
|
|
while let Some(&next_char_in_oct) = self.char_stream.peek() {
|
|
|
|
match next_char_in_oct {
|
2020-03-03 14:39:25 +01:00
|
|
|
'0'..='8' | '_' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char_in_oct);
|
2017-11-01 06:02:36 +01:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2017-11-01 06:02:36 +01:00
|
|
|
}
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
2017-11-01 06:10:46 +01:00
|
|
|
radix_base = Some(8);
|
2017-11-01 06:02:36 +01:00
|
|
|
}
|
2020-03-03 14:39:25 +01:00
|
|
|
'b' | 'B' if c == '0' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char);
|
2017-11-01 06:02:36 +01:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-03-03 06:30:46 +01:00
|
|
|
while let Some(&next_char_in_binary) = self.char_stream.peek() {
|
|
|
|
match next_char_in_binary {
|
2017-11-01 06:02:36 +01:00
|
|
|
'0' | '1' | '_' => {
|
2020-03-03 06:30:46 +01:00
|
|
|
result.push(next_char_in_binary);
|
2017-11-01 06:02:36 +01:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2017-11-01 06:02:36 +01:00
|
|
|
}
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
2017-11-01 06:10:46 +01:00
|
|
|
radix_base = Some(2);
|
2017-11-01 06:02:36 +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, '-');
|
|
|
|
}
|
|
|
|
|
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(|_| {
|
|
|
|
Token::LexError(LERR::MalformedNumber(result.iter().collect()))
|
|
|
|
}),
|
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();
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(feature = "no_float")]
|
|
|
|
return Some((
|
2020-03-10 16:06:20 +01:00
|
|
|
INT::from_str(&out)
|
2020-03-10 11:32:01 +01:00
|
|
|
.map(Token::IntegerConstant)
|
|
|
|
.unwrap_or_else(|_| {
|
|
|
|
Token::LexError(LERR::MalformedNumber(result.iter().collect()))
|
|
|
|
}),
|
|
|
|
pos,
|
|
|
|
));
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-07 03:15:42 +01:00
|
|
|
return Some((
|
2020-03-10 16:06:20 +01:00
|
|
|
INT::from_str(&out)
|
2020-03-09 14:57:07 +01:00
|
|
|
.map(Token::IntegerConstant)
|
2020-03-10 16:06:20 +01:00
|
|
|
.or_else(|_| FLOAT::from_str(&out).map(Token::FloatConstant))
|
2020-03-09 14:57:07 +01:00
|
|
|
.unwrap_or_else(|_| {
|
|
|
|
Token::LexError(LERR::MalformedNumber(result.iter().collect()))
|
|
|
|
}),
|
2020-03-07 03:15:42 +01:00
|
|
|
pos,
|
|
|
|
));
|
|
|
|
}
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
'A'..='Z' | 'a'..='z' | '_' => {
|
2016-02-29 22:43:45 +01:00
|
|
|
let mut result = Vec::new();
|
|
|
|
result.push(c);
|
|
|
|
|
2020-03-03 06:30:46 +01:00
|
|
|
while let Some(&next_char) = self.char_stream.peek() {
|
|
|
|
match next_char {
|
|
|
|
x if x.is_ascii_alphanumeric() || x == '_' => {
|
2017-10-14 19:46:22 +02:00
|
|
|
result.push(x);
|
2016-04-17 04:32:18 +02:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
|
|
|
_ => break,
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 14:39:25 +01:00
|
|
|
let out: String = result.iter().collect();
|
2020-02-24 16:25:52 +01:00
|
|
|
|
|
|
|
return Some((
|
2020-03-02 05:08:03 +01:00
|
|
|
match out.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,
|
|
|
|
"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
|
|
|
"fn" => Token::Fn,
|
|
|
|
"for" => Token::For,
|
|
|
|
"in" => Token::In,
|
2020-03-07 03:15:42 +01:00
|
|
|
_ => Token::Identifier(out),
|
2020-02-24 16:25:52 +01:00
|
|
|
},
|
|
|
|
pos,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
'"' => {
|
|
|
|
return match self.parse_string_const('"') {
|
2020-02-29 13:12:10 +01:00
|
|
|
Ok(out) => Some((Token::StringConst(out), pos)),
|
2020-03-04 15:00:01 +01:00
|
|
|
Err(e) => Some((Token::LexError(e.0), e.1)),
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
'\'' => match self.parse_string_const('\'') {
|
|
|
|
Ok(result) => {
|
|
|
|
let mut chars = result.chars();
|
|
|
|
|
2020-02-24 16:25:52 +01:00
|
|
|
return Some((
|
2020-03-03 06:30:46 +01:00
|
|
|
if let Some(first_char) = chars.next() {
|
2020-02-24 16:25:52 +01:00
|
|
|
if chars.count() != 0 {
|
2020-03-04 15:00:01 +01:00
|
|
|
Token::LexError(LERR::MalformedChar(format!("'{}'", result)))
|
2020-02-24 16:25:52 +01:00
|
|
|
} else {
|
2020-03-03 06:30:46 +01:00
|
|
|
Token::CharConstant(first_char)
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
|
|
|
} else {
|
2020-03-04 15:00:01 +01:00
|
|
|
Token::LexError(LERR::MalformedChar(format!("'{}'", result)))
|
2020-02-24 16:25:52 +01:00
|
|
|
},
|
|
|
|
pos,
|
|
|
|
));
|
2016-04-14 03:40:06 +02:00
|
|
|
}
|
2020-03-04 15:00:01 +01:00
|
|
|
Err(e) => return Some((Token::LexError(e.0), e.1)),
|
2019-09-18 12:21:07 +02:00
|
|
|
},
|
2020-02-29 13:12:10 +01:00
|
|
|
'{' => return Some((Token::LeftBrace, pos)),
|
|
|
|
'}' => return Some((Token::RightBrace, pos)),
|
|
|
|
'(' => return Some((Token::LeftParen, pos)),
|
|
|
|
')' => return Some((Token::RightParen, pos)),
|
|
|
|
'[' => return Some((Token::LeftBracket, pos)),
|
|
|
|
']' => return Some((Token::RightBracket, pos)),
|
2017-10-30 16:08:44 +01:00
|
|
|
'+' => {
|
2020-02-24 16:25:52 +01:00
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::PlusAssign
|
|
|
|
}
|
|
|
|
_ if self.last.is_next_unary() => Token::UnaryPlus,
|
|
|
|
_ => Token::Plus,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-03-09 03:10:19 +01:00
|
|
|
'-' => match self.char_stream.peek() {
|
|
|
|
// Negative number?
|
2020-03-13 11:12:41 +01:00
|
|
|
Some('0'..='9') if self.last.is_next_unary() => negated = true,
|
|
|
|
Some('0'..='9') => return Some((Token::Minus, pos)),
|
2020-03-09 03:10:19 +01:00
|
|
|
Some('=') => {
|
|
|
|
self.char_stream.next();
|
|
|
|
self.advance();
|
|
|
|
return Some((Token::MinusAssign, pos));
|
|
|
|
}
|
|
|
|
_ if self.last.is_next_unary() => return Some((Token::UnaryMinus, pos)),
|
|
|
|
_ => return Some((Token::Minus, pos)),
|
|
|
|
},
|
2017-10-31 06:55:20 +01:00
|
|
|
'*' => {
|
2020-02-24 16:25:52 +01:00
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::MultiplyAssign
|
|
|
|
}
|
|
|
|
_ => Token::Multiply,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
|
|
|
'/' => match self.char_stream.peek() {
|
|
|
|
Some(&'/') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2019-09-18 12:21:07 +02:00
|
|
|
while let Some(c) = self.char_stream.next() {
|
2020-03-03 06:30:46 +01:00
|
|
|
match c {
|
|
|
|
'\n' => {
|
2020-03-04 15:00:01 +01:00
|
|
|
self.new_line();
|
2020-03-03 06:30:46 +01:00
|
|
|
break;
|
|
|
|
}
|
2020-03-04 02:33:35 +01:00
|
|
|
_ => self.advance(),
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
|
|
|
Some(&'*') => {
|
|
|
|
let mut level = 1;
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2019-09-18 12:21:07 +02:00
|
|
|
while let Some(c) = self.char_stream.next() {
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
match c {
|
|
|
|
'/' => {
|
|
|
|
if let Some('*') = self.char_stream.next() {
|
|
|
|
level += 1;
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
|
|
|
'*' => {
|
|
|
|
if let Some('/') = self.char_stream.next() {
|
|
|
|
level -= 1;
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
2020-03-04 15:00:01 +01:00
|
|
|
'\n' => self.new_line(),
|
2019-09-18 12:21:07 +02:00
|
|
|
_ => (),
|
|
|
|
}
|
2017-10-30 16:08:44 +01:00
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
if level == 0 {
|
|
|
|
break;
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-29 13:12:10 +01:00
|
|
|
return Some((Token::DivideAssign, pos));
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-02-29 13:12:10 +01:00
|
|
|
_ => return Some((Token::Divide, pos)),
|
2019-09-18 12:21:07 +02:00
|
|
|
},
|
2020-02-29 13:12:10 +01:00
|
|
|
';' => return Some((Token::SemiColon, pos)),
|
|
|
|
':' => return Some((Token::Colon, pos)),
|
|
|
|
',' => return Some((Token::Comma, pos)),
|
|
|
|
'.' => return Some((Token::Period, pos)),
|
2019-09-18 12:21:07 +02:00
|
|
|
'=' => match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-29 13:12:10 +01:00
|
|
|
return Some((Token::EqualsTo, pos));
|
2016-03-01 15:40:48 +01:00
|
|
|
}
|
2020-02-29 13:12:10 +01:00
|
|
|
_ => return Some((Token::Equals, pos)),
|
2019-09-18 12:21:07 +02:00
|
|
|
},
|
|
|
|
'<' => match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-29 13:12:10 +01:00
|
|
|
return Some((Token::LessThanEqualsTo, pos));
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
|
|
|
Some(&'<') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2019-09-18 12:21:07 +02:00
|
|
|
return match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::LeftShiftAssign, pos))
|
2017-10-31 06:55:20 +01:00
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
_ => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::LeftShift, pos))
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
|
|
|
};
|
2016-03-01 15:40:48 +01:00
|
|
|
}
|
2020-02-29 13:12:10 +01:00
|
|
|
_ => return Some((Token::LessThan, pos)),
|
2019-09-18 12:21:07 +02:00
|
|
|
},
|
2020-02-24 16:25:52 +01:00
|
|
|
'>' => {
|
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
2019-09-18 12:21:07 +02:00
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::GreaterThanEqualsTo
|
2017-10-31 06:55:20 +01:00
|
|
|
}
|
2020-02-24 16:25:52 +01:00
|
|
|
Some(&'>') => {
|
2019-09-18 12:21:07 +02:00
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::RightShiftAssign
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::RightShift
|
|
|
|
}
|
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-02-24 16:25:52 +01:00
|
|
|
_ => Token::GreaterThan,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
'!' => {
|
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::NotEqualsTo
|
|
|
|
}
|
|
|
|
_ => Token::Bang,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
'|' => {
|
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'|') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::Or
|
|
|
|
}
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::OrAssign
|
|
|
|
}
|
|
|
|
_ => Token::Pipe,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
'&' => {
|
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'&') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::And
|
|
|
|
}
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::AndAssign
|
|
|
|
}
|
|
|
|
_ => Token::Ampersand,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
'^' => {
|
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::XOrAssign
|
|
|
|
}
|
|
|
|
_ => Token::XOr,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
'%' => {
|
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::ModuloAssign
|
|
|
|
}
|
|
|
|
_ => Token::Modulo,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
'~' => {
|
|
|
|
return Some((
|
|
|
|
match self.char_stream.peek() {
|
|
|
|
Some(&'=') => {
|
|
|
|
self.char_stream.next();
|
2020-03-04 02:33:35 +01:00
|
|
|
self.advance();
|
2020-02-24 16:25:52 +01:00
|
|
|
Token::PowerOfAssign
|
|
|
|
}
|
|
|
|
_ => Token::PowerOf,
|
|
|
|
},
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
x if x.is_whitespace() => (),
|
2020-03-04 15:00:01 +01:00
|
|
|
x => return Some((Token::LexError(LERR::UnexpectedChar(x)), 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
|
|
|
|
|
|
|
// TODO - perhaps this could be optimized?
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
2020-02-24 16:25:52 +01:00
|
|
|
self.inner_next().map(|x| {
|
|
|
|
self.last = x.0.clone();
|
|
|
|
x
|
|
|
|
})
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
pub fn lex(input: &str) -> TokenIterator<'_> {
|
|
|
|
TokenIterator {
|
2020-03-04 15:00:01 +01:00
|
|
|
last: Token::LexError(LERR::InputError("".into())),
|
|
|
|
pos: Position::new(1, 0),
|
2019-09-18 12:21:07 +02:00
|
|
|
char_stream: input.chars().peekable(),
|
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
fn get_precedence(token: &Token) -> u8 {
|
2016-02-29 22:43:45 +01:00
|
|
|
match *token {
|
2017-10-30 16:08:44 +01:00
|
|
|
Token::Equals
|
2017-12-21 12:32:18 +01:00
|
|
|
| Token::PlusAssign
|
|
|
|
| Token::MinusAssign
|
|
|
|
| Token::MultiplyAssign
|
|
|
|
| Token::DivideAssign
|
|
|
|
| Token::LeftShiftAssign
|
|
|
|
| Token::RightShiftAssign
|
|
|
|
| Token::AndAssign
|
|
|
|
| Token::OrAssign
|
|
|
|
| Token::XOrAssign
|
|
|
|
| Token::ModuloAssign
|
|
|
|
| Token::PowerOfAssign => 10,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
Token::Or | Token::XOr | Token::Pipe => 50,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
Token::And | Token::Ampersand => 60,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2017-10-02 23:44:45 +02:00
|
|
|
Token::LessThan
|
2020-02-24 16:25:52 +01:00
|
|
|
| Token::LessThanEqualsTo
|
2017-10-02 23:44:45 +02:00
|
|
|
| Token::GreaterThan
|
2020-02-24 16:25:52 +01:00
|
|
|
| Token::GreaterThanEqualsTo
|
|
|
|
| Token::EqualsTo
|
2020-03-14 04:51:45 +01:00
|
|
|
| Token::NotEqualsTo => 70,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
Token::Plus | Token::Minus => 80,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
Token::Divide | Token::Multiply | Token::PowerOf => 90,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
Token::LeftShift | Token::RightShift => 100,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
Token::Modulo => 110,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
Token::Period => 120,
|
2020-03-09 03:10:19 +01:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
_ => 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_bind_right(token: &Token) -> bool {
|
|
|
|
match *token {
|
|
|
|
Token::Equals
|
|
|
|
| Token::PlusAssign
|
|
|
|
| Token::MinusAssign
|
|
|
|
| Token::MultiplyAssign
|
|
|
|
| Token::DivideAssign
|
|
|
|
| Token::LeftShiftAssign
|
|
|
|
| Token::RightShiftAssign
|
|
|
|
| Token::AndAssign
|
|
|
|
| Token::OrAssign
|
|
|
|
| Token::XOrAssign
|
|
|
|
| Token::ModuloAssign
|
|
|
|
| Token::PowerOfAssign => true,
|
|
|
|
|
|
|
|
Token::Period => true,
|
|
|
|
|
|
|
|
_ => false,
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 15:13:14 +01:00
|
|
|
fn parse_paren_expr<'a>(
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
|
|
|
begin: Position,
|
|
|
|
) -> Result<Expr, ParseError> {
|
2020-02-24 16:25:52 +01:00
|
|
|
match input.peek() {
|
2020-03-09 03:10:19 +01:00
|
|
|
// ()
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::RightParen, _)) => {
|
2020-02-24 16:25:52 +01:00
|
|
|
input.next();
|
2020-03-02 15:13:14 +01:00
|
|
|
return Ok(Expr::Unit(begin));
|
2020-02-24 16:25:52 +01:00
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
let expr = parse_expr(input)?;
|
2016-02-29 22:43:45 +01:00
|
|
|
|
|
|
|
match input.next() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::RightParen, _)) => Ok(expr),
|
2020-03-05 18:05:02 +01:00
|
|
|
Some((_, pos)) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MissingRightParen("a matching ( in the expression".into()),
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
None => Err(ParseError::new(
|
|
|
|
PERR::MissingRightParen("a matching ( in the expression".into()),
|
|
|
|
Position::eof(),
|
|
|
|
)),
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
fn parse_call_expr<'a>(
|
|
|
|
id: String,
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
2020-03-02 15:13:14 +01:00
|
|
|
begin: Position,
|
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-02-29 13:12:10 +01:00
|
|
|
if let Some(&(Token::RightParen, _)) = input.peek() {
|
2017-10-02 23:44:45 +02:00
|
|
|
input.next();
|
2020-03-12 07:54:14 +01:00
|
|
|
return Ok(Expr::FunctionCall(id, args_expr_list, None, begin));
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
loop {
|
2020-03-12 07:54:14 +01:00
|
|
|
args_expr_list.push(parse_expr(input)?);
|
2016-02-29 22:43:45 +01:00
|
|
|
|
|
|
|
match input.peek() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some(&(Token::RightParen, _)) => {
|
2016-03-01 21:16:10 +01:00
|
|
|
input.next();
|
2020-03-12 07:54:14 +01:00
|
|
|
return Ok(Expr::FunctionCall(id, args_expr_list, None, begin));
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-02-29 13:12:10 +01:00
|
|
|
Some(&(Token::Comma, _)) => (),
|
2020-03-05 18:05:02 +01:00
|
|
|
Some(&(_, pos)) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MissingRightParen(format!(
|
2020-03-12 07:54:14 +01:00
|
|
|
"closing the parameters list to function call of '{}'",
|
2020-03-05 18:05:02 +01:00
|
|
|
id
|
|
|
|
)),
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MissingRightParen(format!(
|
2020-03-12 07:54:14 +01:00
|
|
|
"closing the parameters list to function call of '{}'",
|
2020-03-05 18:05:02 +01:00
|
|
|
id
|
|
|
|
)),
|
|
|
|
Position::eof(),
|
|
|
|
))
|
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
input.next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
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,
|
2019-09-18 12:21:07 +02:00
|
|
|
) -> Result<Expr, ParseError> {
|
2020-03-09 03:41:17 +01:00
|
|
|
let idx_expr = parse_expr(input)?;
|
|
|
|
|
|
|
|
// Check type of indexing - must be integer
|
|
|
|
match &idx_expr {
|
|
|
|
Expr::IntegerConstant(i, pos) if *i < 0 => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedIndexExpr(format!(
|
|
|
|
"Array access expects non-negative index: {} < 0",
|
|
|
|
i
|
|
|
|
)),
|
|
|
|
*pos,
|
|
|
|
))
|
|
|
|
}
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-09 03:41:17 +01:00
|
|
|
Expr::FloatConstant(_, pos) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedIndexExpr("Array access expects integer index, not a float".into()),
|
|
|
|
*pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
Expr::CharConstant(_, pos) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedIndexExpr(
|
|
|
|
"Array access expects integer index, not a character".into(),
|
|
|
|
),
|
|
|
|
*pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
Expr::StringConstant(_, pos) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedIndexExpr("Array access expects integer index, not a string".into()),
|
|
|
|
*pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
Expr::Assignment(_, _, pos) | Expr::Unit(pos) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedIndexExpr("Array access expects integer index, not ()".into()),
|
|
|
|
*pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
Expr::And(lhs, _) | Expr::Or(lhs, _) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedIndexExpr(
|
|
|
|
"Array access expects integer index, not a boolean".into(),
|
|
|
|
),
|
|
|
|
lhs.position(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
Expr::True(pos) | Expr::False(pos) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedIndexExpr(
|
|
|
|
"Array access expects integer index, not a boolean".into(),
|
|
|
|
),
|
|
|
|
*pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if there is a closing bracket
|
|
|
|
match input.peek() {
|
2020-03-05 18:05:02 +01:00
|
|
|
Some(&(Token::RightBracket, _)) => {
|
|
|
|
input.next();
|
2020-03-07 03:16:20 +01:00
|
|
|
return Ok(Expr::Index(lhs, Box::new(idx_expr), pos));
|
2020-03-05 18:05:02 +01:00
|
|
|
}
|
|
|
|
Some(&(_, pos)) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MissingRightBracket("index expression".into()),
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MissingRightBracket("index expression".into()),
|
|
|
|
Position::eof(),
|
|
|
|
))
|
|
|
|
}
|
2020-03-09 03:41:17 +01:00
|
|
|
}
|
2016-03-26 18:46:28 +01:00
|
|
|
}
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
fn parse_ident_expr<'a>(
|
|
|
|
id: String,
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
2020-03-02 15:13:14 +01:00
|
|
|
begin: Position,
|
2019-09-18 12:21:07 +02:00
|
|
|
) -> Result<Expr, ParseError> {
|
2016-03-26 18:46:28 +01:00
|
|
|
match input.peek() {
|
2020-03-04 15:00:01 +01:00
|
|
|
Some(&(Token::LeftParen, _)) => {
|
2016-04-17 04:32:18 +02:00
|
|
|
input.next();
|
2020-03-04 15:00:01 +01:00
|
|
|
parse_call_expr(id, input, begin)
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-10 10:10:33 +01:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-03-07 03:16:20 +01:00
|
|
|
Some(&(Token::LeftBracket, pos)) => {
|
2016-04-17 04:32:18 +02:00
|
|
|
input.next();
|
2020-03-13 11:12:41 +01:00
|
|
|
parse_index_expr(Box::new(Expr::Variable(id, begin)), input, pos)
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-13 11:12:41 +01:00
|
|
|
Some(_) => Ok(Expr::Variable(id, begin)),
|
|
|
|
None => Ok(Expr::Variable(id, Position::eof())),
|
2016-03-26 18:46:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-03-02 15:13:14 +01:00
|
|
|
fn parse_array_expr<'a>(
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
|
|
|
begin: Position,
|
|
|
|
) -> Result<Expr, ParseError> {
|
2016-03-26 18:46:28 +01:00
|
|
|
let mut arr = Vec::new();
|
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
match input.peek() {
|
|
|
|
Some(&(Token::RightBracket, _)) => (),
|
2016-03-26 18:46:28 +01:00
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
_ => {
|
|
|
|
while input.peek().is_some() {
|
|
|
|
arr.push(parse_expr(input)?);
|
2020-03-02 15:13:14 +01:00
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
if let Some(&(Token::Comma, _)) = input.peek() {
|
|
|
|
input.next();
|
|
|
|
}
|
2016-03-26 18:46:28 +01:00
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
if let Some(&(Token::RightBracket, _)) = input.peek() {
|
|
|
|
break;
|
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2016-04-14 03:01:08 +02:00
|
|
|
}
|
2016-03-26 18:46:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
match input.peek() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some(&(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-05 18:05:02 +01:00
|
|
|
Some(&(_, pos)) => Err(ParseError::new(
|
|
|
|
PERR::MissingRightBracket("the end of array literal".into()),
|
|
|
|
pos,
|
|
|
|
)),
|
|
|
|
None => Err(ParseError::new(
|
|
|
|
PERR::MissingRightBracket("the end of array literal".into()),
|
|
|
|
Position::eof(),
|
|
|
|
)),
|
2016-03-26 18:46:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-29 22:43:45 +01:00
|
|
|
fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
|
2020-03-07 03:39:00 +01:00
|
|
|
// Block statement as expression
|
|
|
|
match input.peek() {
|
|
|
|
Some(&(Token::LeftBrace, pos)) => {
|
2020-03-09 14:57:07 +01:00
|
|
|
return parse_block(input).map(|block| Expr::Stmt(Box::new(block), pos))
|
2020-03-07 03:39:00 +01:00
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
2020-03-05 13:28:03 +01:00
|
|
|
let token = input.next();
|
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
let mut can_be_indexed = false;
|
2020-03-05 13:28:03 +01:00
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
#[allow(unused_mut)]
|
2020-03-06 03:50:52 +01:00
|
|
|
let mut root_expr = match token {
|
2020-03-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_float"))]
|
2020-03-03 09:23:55 +01:00
|
|
|
Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)),
|
2020-03-10 11:32:01 +01:00
|
|
|
|
|
|
|
Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)),
|
2020-03-03 09:23:55 +01:00
|
|
|
Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)),
|
2020-03-05 13:28:03 +01:00
|
|
|
Some((Token::StringConst(s), pos)) => {
|
2020-03-10 10:10:33 +01:00
|
|
|
can_be_indexed = true;
|
2020-03-05 13:28:03 +01:00
|
|
|
Ok(Expr::StringConstant(s, pos))
|
|
|
|
}
|
|
|
|
Some((Token::Identifier(s), pos)) => {
|
2020-03-10 10:10:33 +01:00
|
|
|
can_be_indexed = true;
|
2020-03-05 13:28:03 +01:00
|
|
|
parse_ident_expr(s, input, pos)
|
|
|
|
}
|
|
|
|
Some((Token::LeftParen, pos)) => {
|
2020-03-10 10:10:33 +01:00
|
|
|
can_be_indexed = true;
|
2020-03-05 13:28:03 +01:00
|
|
|
parse_paren_expr(input, pos)
|
|
|
|
}
|
2020-03-10 10:10:33 +01:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-03-05 13:28:03 +01:00
|
|
|
Some((Token::LeftBracket, pos)) => {
|
2020-03-10 10:10:33 +01:00
|
|
|
can_be_indexed = true;
|
2020-03-05 13:28:03 +01:00
|
|
|
parse_array_expr(input, pos)
|
|
|
|
}
|
2020-03-03 09:23:55 +01:00
|
|
|
Some((Token::True, pos)) => Ok(Expr::True(pos)),
|
|
|
|
Some((Token::False, pos)) => Ok(Expr::False(pos)),
|
2020-03-04 15:00:01 +01:00
|
|
|
Some((Token::LexError(le), pos)) => {
|
|
|
|
Err(ParseError::new(PERR::BadInput(le.to_string()), pos))
|
|
|
|
}
|
|
|
|
Some((token, pos)) => Err(ParseError::new(
|
2020-03-03 09:23:55 +01:00
|
|
|
PERR::BadInput(format!("Unexpected '{}'", token.syntax())),
|
|
|
|
pos,
|
|
|
|
)),
|
2020-03-04 15:00:01 +01:00
|
|
|
None => Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
|
2020-03-05 13:28:03 +01:00
|
|
|
}?;
|
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
if can_be_indexed {
|
|
|
|
// Tail processing all possible indexing
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
|
|
|
while let Some(&(Token::LeftBracket, pos)) = input.peek() {
|
|
|
|
input.next();
|
|
|
|
root_expr = parse_index_expr(Box::new(root_expr), input, pos)?;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2017-10-30 16:08:44 +01:00
|
|
|
fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
|
2020-03-03 09:23:55 +01:00
|
|
|
match input.peek() {
|
|
|
|
Some(&(Token::UnaryMinus, pos)) => {
|
2019-09-18 12:21:07 +02:00
|
|
|
input.next();
|
2020-03-02 15:13:14 +01:00
|
|
|
|
2020-03-09 03:10:19 +01:00
|
|
|
match parse_unary(input) {
|
|
|
|
// Negative integer
|
2020-03-10 11:32:01 +01:00
|
|
|
Ok(Expr::IntegerConstant(i, _)) => i
|
|
|
|
.checked_neg()
|
|
|
|
.map(|x| Expr::IntegerConstant(x, pos))
|
2020-03-11 16:43:04 +01:00
|
|
|
.or_else(|| {
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
return Some(Expr::FloatConstant(-(i as FLOAT), pos));
|
|
|
|
|
|
|
|
#[cfg(feature = "no_float")]
|
|
|
|
return None;
|
|
|
|
})
|
2020-03-10 11:32:01 +01:00
|
|
|
.ok_or_else(|| {
|
|
|
|
ParseError::new(
|
|
|
|
PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string()),
|
|
|
|
pos,
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
|
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-09 03:10:19 +01:00
|
|
|
Ok(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
|
|
|
|
Ok(expr) => Ok(Expr::FunctionCall("-".into(), vec![expr], None, pos)),
|
2020-03-10 11:32:01 +01:00
|
|
|
|
2020-03-09 03:10:19 +01:00
|
|
|
err @ Err(_) => err,
|
|
|
|
}
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-03-03 09:23:55 +01:00
|
|
|
Some(&(Token::UnaryPlus, _)) => {
|
2019-09-18 12:21:07 +02:00
|
|
|
input.next();
|
2020-03-09 03:10:19 +01:00
|
|
|
parse_unary(input)
|
2019-09-18 12:21:07 +02:00
|
|
|
}
|
2020-03-03 09:23:55 +01:00
|
|
|
Some(&(Token::Bang, 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(),
|
|
|
|
vec![parse_primary(input)?],
|
|
|
|
Some(Box::new(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
|
|
|
}
|
|
|
|
_ => parse_primary(input),
|
2017-10-30 16:08:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 03:16:20 +01:00
|
|
|
fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, ParseError> {
|
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-13 11:12:41 +01:00
|
|
|
Expr::Variable(_, _) => {
|
|
|
|
assert!(is_top, "property expected but gets variable");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
Expr::Property(_, _) => {
|
|
|
|
assert!(!is_top, "variable expected but gets property");
|
|
|
|
None
|
|
|
|
}
|
2020-03-07 15:50:46 +01:00
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
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-10 11:32:01 +01:00
|
|
|
#[cfg(not(feature = "no_index"))]
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-03-14 12:46:44 +01:00
|
|
|
Expr::Index(idx_lhs, _, pos) => Some(ParseError::new(
|
|
|
|
match idx_lhs.as_ref() {
|
|
|
|
Expr::Index(_, _, _) => ParseErrorType::AssignmentToCopy,
|
|
|
|
_ => ParseErrorType::AssignmentToInvalidLHS,
|
|
|
|
},
|
|
|
|
*pos,
|
2020-03-13 11:12:41 +01:00
|
|
|
)),
|
2020-03-07 15:50:46 +01:00
|
|
|
|
2020-03-07 10:32:15 +01:00
|
|
|
Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() {
|
2020-03-13 11:12:41 +01:00
|
|
|
Expr::Variable(_, _) if is_top => valid_assignment_chain(dot_rhs, false),
|
|
|
|
Expr::Property(_, _) if !is_top => valid_assignment_chain(dot_rhs, false),
|
2020-03-10 10:10:33 +01:00
|
|
|
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
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)
|
|
|
|
}
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
|
|
|
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
|
|
|
}
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
2020-03-13 11:12:41 +01:00
|
|
|
Expr::Index(idx_lhs, _, _) => Some(ParseError::new(
|
|
|
|
ParseErrorType::AssignmentToCopy,
|
|
|
|
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-13 11:12:41 +01:00
|
|
|
_ => Some(ParseError::new(
|
|
|
|
ParseErrorType::AssignmentToInvalidLHS,
|
|
|
|
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-11 16:43:04 +01:00
|
|
|
//println!("{:#?} = {:#?}", lhs, rhs);
|
2020-03-07 10:32:15 +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-07 06:39:28 +01:00
|
|
|
fn parse_op_assignment(
|
|
|
|
function: &str,
|
|
|
|
lhs: Expr,
|
|
|
|
rhs: Expr,
|
|
|
|
pos: Position,
|
|
|
|
) -> Result<Expr, ParseError> {
|
|
|
|
let lhs_copy = lhs.clone();
|
|
|
|
|
|
|
|
parse_assignment(
|
|
|
|
lhs,
|
|
|
|
Expr::FunctionCall(function.into(), vec![lhs_copy, rhs], None, pos),
|
|
|
|
pos,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
) -> 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-03-14 04:51:45 +01:00
|
|
|
let (current_precedence, bind_right) = if let Some(&(ref current_op, _)) = input.peek() {
|
|
|
|
(get_precedence(current_op), is_bind_right(current_op))
|
|
|
|
} else {
|
|
|
|
(0, false)
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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-14 04:51:45 +01:00
|
|
|
let rhs = parse_unary(input)?;
|
2016-04-14 03:01:08 +02:00
|
|
|
|
2020-03-14 04:51:45 +01:00
|
|
|
let next_precedence = if let Some(&(ref next_op, _)) = input.peek() {
|
|
|
|
get_precedence(next_op)
|
|
|
|
} 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
|
|
|
|
{
|
|
|
|
parse_binary_op(input, current_precedence, rhs)?
|
|
|
|
} 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-13 11:12:41 +01:00
|
|
|
Token::Period => {
|
|
|
|
fn change_var_to_property(expr: Expr) -> Expr {
|
|
|
|
match expr {
|
|
|
|
Expr::Dot(lhs, rhs, pos) => Expr::Dot(
|
|
|
|
Box::new(change_var_to_property(*lhs)),
|
|
|
|
Box::new(change_var_to_property(*rhs)),
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Expr::Index(lhs, idx, pos) => {
|
|
|
|
Expr::Index(Box::new(change_var_to_property(*lhs)), idx, pos)
|
|
|
|
}
|
|
|
|
Expr::Variable(s, pos) => Expr::Property(s, pos),
|
|
|
|
expr => expr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Expr::Dot(
|
|
|
|
Box::new(current_lhs),
|
|
|
|
Box::new(change_var_to_property(rhs)),
|
|
|
|
pos,
|
|
|
|
)
|
|
|
|
}
|
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],
|
|
|
|
Some(Box::new(false)),
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::NotEqualsTo => Expr::FunctionCall(
|
|
|
|
"!=".into(),
|
|
|
|
vec![current_lhs, rhs],
|
|
|
|
Some(Box::new(false)),
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::LessThan => Expr::FunctionCall(
|
|
|
|
"<".into(),
|
|
|
|
vec![current_lhs, rhs],
|
|
|
|
Some(Box::new(false)),
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::LessThanEqualsTo => Expr::FunctionCall(
|
|
|
|
"<=".into(),
|
|
|
|
vec![current_lhs, rhs],
|
|
|
|
Some(Box::new(false)),
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::GreaterThan => Expr::FunctionCall(
|
|
|
|
">".into(),
|
|
|
|
vec![current_lhs, rhs],
|
|
|
|
Some(Box::new(false)),
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
Token::GreaterThanEqualsTo => Expr::FunctionCall(
|
|
|
|
">=".into(),
|
|
|
|
vec![current_lhs, rhs],
|
|
|
|
Some(Box::new(false)),
|
|
|
|
pos,
|
|
|
|
),
|
|
|
|
|
|
|
|
Token::Or => Expr::Or(Box::new(current_lhs), Box::new(rhs)),
|
|
|
|
Token::And => Expr::And(Box::new(current_lhs), Box::new(rhs)),
|
|
|
|
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-03 16:31:29 +01:00
|
|
|
token => {
|
2020-03-04 15:00:01 +01:00
|
|
|
return Err(ParseError::new(
|
2020-03-05 18:05:02 +01:00
|
|
|
PERR::UnknownOperator(token.syntax().into()),
|
2020-03-03 16:31:29 +01:00
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_expr<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
|
2020-02-24 16:25:52 +01:00
|
|
|
let lhs = parse_unary(input)?;
|
2020-03-14 04:51:45 +01:00
|
|
|
parse_binary_op(input, 1, lhs)
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_if<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
|
|
|
|
input.next();
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
let guard = parse_expr(input)?;
|
|
|
|
let body = parse_block(input)?;
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2016-03-03 14:20:55 +01:00
|
|
|
match input.peek() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some(&(Token::Else, _)) => {
|
2016-03-03 14:20:55 +01:00
|
|
|
input.next();
|
2020-03-02 10:04:56 +01:00
|
|
|
|
2020-03-14 16:41:15 +01:00
|
|
|
let else_body = if matches!(input.peek(), Some(&(Token::If, _))) {
|
|
|
|
parse_if(input)?
|
|
|
|
} else {
|
|
|
|
parse_block(input)?
|
2020-03-02 10:04:56 +01:00
|
|
|
};
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
Ok(Stmt::IfElse(
|
|
|
|
Box::new(guard),
|
|
|
|
Box::new(body),
|
2020-03-02 10:04:56 +01:00
|
|
|
Some(Box::new(else_body)),
|
2019-09-18 12:21:07 +02:00
|
|
|
))
|
2016-03-03 14:20:55 +01:00
|
|
|
}
|
2020-03-02 10:04:56 +01:00
|
|
|
_ => Ok(Stmt::IfElse(Box::new(guard), Box::new(body), None)),
|
2016-03-03 14:20:55 +01:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_while<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
|
|
|
|
input.next();
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
let guard = parse_expr(input)?;
|
|
|
|
let body = parse_block(input)?;
|
2016-02-29 22:43:45 +01:00
|
|
|
|
|
|
|
Ok(Stmt::While(Box::new(guard), Box::new(body)))
|
|
|
|
}
|
|
|
|
|
2017-10-30 16:08:44 +01:00
|
|
|
fn parse_loop<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
|
|
|
|
input.next();
|
|
|
|
|
2019-09-18 12:21:07 +02:00
|
|
|
let body = parse_block(input)?;
|
2017-10-30 16:08:44 +01:00
|
|
|
|
|
|
|
Ok(Stmt::Loop(Box::new(body)))
|
|
|
|
}
|
|
|
|
|
2020-02-23 15:48:46 +01:00
|
|
|
fn parse_for<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
|
|
|
|
input.next();
|
|
|
|
|
|
|
|
let name = match input.next() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::Identifier(s), _)) => s,
|
2020-03-04 15:00:01 +01:00
|
|
|
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)),
|
|
|
|
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())),
|
2020-02-23 15:48:46 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
match input.next() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::In, _)) => {}
|
2020-03-04 15:00:01 +01:00
|
|
|
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)),
|
|
|
|
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())),
|
2020-02-23 15:48:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let expr = parse_expr(input)?;
|
|
|
|
|
|
|
|
let body = parse_block(input)?;
|
|
|
|
|
|
|
|
Ok(Stmt::For(name, Box::new(expr), Box::new(body)))
|
|
|
|
}
|
|
|
|
|
2020-03-13 11:12:41 +01:00
|
|
|
fn parse_var<'a>(
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
|
|
|
var_type: VariableType,
|
|
|
|
) -> Result<Stmt, ParseError> {
|
2020-03-02 15:13:14 +01:00
|
|
|
let pos = match input.next() {
|
|
|
|
Some((_, tok_pos)) => tok_pos,
|
2020-03-04 15:00:01 +01:00
|
|
|
_ => return Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
|
2020-03-02 15:13:14 +01:00
|
|
|
};
|
2016-02-29 22:43:45 +01:00
|
|
|
|
|
|
|
let name = match input.next() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::Identifier(s), _)) => s,
|
2020-03-04 15:00:01 +01:00
|
|
|
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)),
|
|
|
|
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())),
|
2016-02-29 22:43:45 +01:00
|
|
|
};
|
|
|
|
|
2020-03-14 16:41:15 +01:00
|
|
|
if matches!(input.peek(), Some(&(Token::Equals, _))) {
|
|
|
|
input.next();
|
|
|
|
let init_value = parse_expr(input)?;
|
2020-03-13 11:12:41 +01:00
|
|
|
|
2020-03-14 16:41:15 +01:00
|
|
|
match var_type {
|
|
|
|
VariableType::Normal => Ok(Stmt::Let(name, Some(Box::new(init_value)), pos)),
|
2020-03-13 11:12:41 +01:00
|
|
|
|
2020-03-14 16:41:15 +01:00
|
|
|
VariableType::Constant if init_value.is_constant() => {
|
|
|
|
Ok(Stmt::Const(name, Box::new(init_value), pos))
|
2020-03-13 11:12:41 +01:00
|
|
|
}
|
2020-03-14 16:41:15 +01:00
|
|
|
// Constants require a constant expression
|
|
|
|
VariableType::Constant => Err(ParseError(
|
|
|
|
PERR::ForbiddenConstantExpr(name.to_string()),
|
|
|
|
init_value.position(),
|
|
|
|
)),
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
2020-03-14 16:41:15 +01:00
|
|
|
} else {
|
|
|
|
Ok(Stmt::Let(name, None, pos))
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_block<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
|
2020-03-12 05:40:28 +01:00
|
|
|
let pos = match input.next() {
|
|
|
|
Some((Token::LeftBrace, pos)) => pos,
|
|
|
|
Some((_, pos)) => return Err(ParseError::new(PERR::MissingLeftBrace, pos)),
|
2020-03-04 15:00:01 +01:00
|
|
|
None => return Err(ParseError::new(PERR::MissingLeftBrace, Position::eof())),
|
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-03 09:23:55 +01:00
|
|
|
match input.peek() {
|
|
|
|
Some(&(Token::RightBrace, _)) => (), // empty block
|
2020-03-04 15:00:01 +01:00
|
|
|
Some(&(Token::Fn, pos)) => return Err(ParseError::new(PERR::WrongFnDefinition, pos)),
|
2016-03-01 21:16:10 +01:00
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
_ => {
|
|
|
|
while input.peek().is_some() {
|
|
|
|
// Parse statements inside the block
|
|
|
|
statements.push(parse_stmt(input)?);
|
2016-03-01 21:16:10 +01:00
|
|
|
|
2020-03-03 11:15:20 +01:00
|
|
|
// Notice semicolons are optional
|
2020-03-03 09:23:55 +01:00
|
|
|
if let Some(&(Token::SemiColon, _)) = input.peek() {
|
|
|
|
input.next();
|
|
|
|
}
|
2017-10-02 23:44:45 +02:00
|
|
|
|
2020-03-03 09:23:55 +01:00
|
|
|
if let Some(&(Token::RightBrace, _)) = input.peek() {
|
|
|
|
break;
|
|
|
|
}
|
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
|
|
|
|
|
|
|
match input.peek() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some(&(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-05 18:05:02 +01:00
|
|
|
Some(&(_, pos)) => Err(ParseError::new(
|
|
|
|
PERR::MissingRightBrace("end of block".into()),
|
|
|
|
pos,
|
|
|
|
)),
|
|
|
|
None => Err(ParseError::new(
|
|
|
|
PERR::MissingRightBrace("end of block".into()),
|
|
|
|
Position::eof(),
|
|
|
|
)),
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_expr_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
|
2020-02-24 16:25:52 +01:00
|
|
|
Ok(Stmt::Expr(Box::new(parse_expr(input)?)))
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
|
|
|
|
match input.peek() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some(&(Token::If, _)) => parse_if(input),
|
|
|
|
Some(&(Token::While, _)) => parse_while(input),
|
|
|
|
Some(&(Token::Loop, _)) => parse_loop(input),
|
|
|
|
Some(&(Token::For, _)) => parse_for(input),
|
2020-03-02 15:13:14 +01:00
|
|
|
Some(&(Token::Break, 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-03-03 11:15:20 +01:00
|
|
|
Some(&(ref token @ Token::Return, _)) | Some(&(ref token @ Token::Throw, _)) => {
|
2020-03-11 16:43:04 +01:00
|
|
|
let return_type = match token {
|
|
|
|
Token::Return => ReturnType::Return,
|
|
|
|
Token::Throw => ReturnType::Exception,
|
|
|
|
_ => panic!("unexpected token!"),
|
2020-03-03 11:15:20 +01:00
|
|
|
};
|
|
|
|
|
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-03 11:15:20 +01:00
|
|
|
// return; or throw;
|
2020-03-11 16:43:04 +01:00
|
|
|
Some(&(Token::SemiColon, pos)) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
2020-03-03 11:15:20 +01:00
|
|
|
// Just a return/throw without anything at the end of script
|
2020-03-11 16:43:04 +01:00
|
|
|
None => Ok(Stmt::ReturnWithVal(None, return_type, Position::eof())),
|
2020-03-03 11:15:20 +01:00
|
|
|
// return or throw with expression
|
2020-03-02 15:13:14 +01:00
|
|
|
Some(&(_, pos)) => {
|
2019-09-18 12:21:07 +02:00
|
|
|
let ret = parse_expr(input)?;
|
2020-03-11 16:43:04 +01:00
|
|
|
Ok(Stmt::ReturnWithVal(Some(Box::new(ret)), return_type, pos))
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2016-03-03 15:31:42 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-29 13:12:10 +01:00
|
|
|
Some(&(Token::LeftBrace, _)) => parse_block(input),
|
2020-03-13 11:12:41 +01:00
|
|
|
Some(&(Token::Let, _)) => parse_var(input, VariableType::Normal),
|
|
|
|
Some(&(Token::Const, _)) => parse_var(input, VariableType::Constant),
|
2016-04-17 04:32:18 +02:00
|
|
|
_ => parse_expr_stmt(input),
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 06:28:12 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-03-11 16:43:04 +01:00
|
|
|
fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef, ParseError> {
|
2020-03-02 15:13:14 +01:00
|
|
|
let pos = match input.next() {
|
|
|
|
Some((_, tok_pos)) => tok_pos,
|
2020-03-04 15:00:01 +01:00
|
|
|
_ => return Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
|
2020-03-02 15:13:14 +01:00
|
|
|
};
|
2016-02-29 22:43:45 +01:00
|
|
|
|
2016-03-01 21:16:10 +01:00
|
|
|
let name = match input.next() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::Identifier(s), _)) => s,
|
2020-03-04 15:00:01 +01:00
|
|
|
Some((_, pos)) => return Err(ParseError::new(PERR::FnMissingName, pos)),
|
|
|
|
None => return Err(ParseError::new(PERR::FnMissingName, Position::eof())),
|
2016-03-01 21:16:10 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
match input.peek() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some(&(Token::LeftParen, _)) => {
|
2016-04-17 04:32:18 +02:00
|
|
|
input.next();
|
|
|
|
}
|
2020-03-04 15:00:01 +01:00
|
|
|
Some(&(_, pos)) => return Err(ParseError::new(PERR::FnMissingParams(name), pos)),
|
|
|
|
None => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::FnMissingParams(name),
|
|
|
|
Position::eof(),
|
|
|
|
))
|
|
|
|
}
|
2016-03-01 21:16:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut params = Vec::new();
|
|
|
|
|
2020-03-14 16:41:15 +01:00
|
|
|
if matches!(input.peek(), Some(&(Token::RightParen, _))) {
|
|
|
|
input.next();
|
|
|
|
} else {
|
|
|
|
loop {
|
2016-03-01 21:16:10 +01:00
|
|
|
match input.next() {
|
2020-02-29 13:12:10 +01:00
|
|
|
Some((Token::RightParen, _)) => break,
|
|
|
|
Some((Token::Comma, _)) => (),
|
|
|
|
Some((Token::Identifier(s), _)) => {
|
2020-03-07 03:15:42 +01:00
|
|
|
params.push(s.into());
|
2016-04-17 04:32:18 +02:00
|
|
|
}
|
2020-03-09 03:41:17 +01:00
|
|
|
Some((_, pos)) => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedCallExpr(
|
|
|
|
"Function call arguments missing either a ',' or a ')'".into(),
|
|
|
|
),
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
return Err(ParseError::new(
|
|
|
|
PERR::MalformedCallExpr(
|
|
|
|
"Function call arguments missing a closing ')'".into(),
|
|
|
|
),
|
|
|
|
Position::eof(),
|
|
|
|
))
|
|
|
|
}
|
2016-03-01 21:16:10 +01:00
|
|
|
}
|
2020-03-14 16:41:15 +01:00
|
|
|
}
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
2017-12-21 12:28:59 +01:00
|
|
|
let body = parse_block(input)?;
|
2016-03-01 21:16:10 +01:00
|
|
|
|
2016-04-17 04:32:18 +02:00
|
|
|
Ok(FnDef {
|
2020-03-11 16:43:04 +01:00
|
|
|
name,
|
2020-03-09 14:57:07 +01:00
|
|
|
params,
|
|
|
|
body,
|
|
|
|
pos,
|
2016-04-17 04:32:18 +02:00
|
|
|
})
|
2016-03-01 21:16:10 +01:00
|
|
|
}
|
|
|
|
|
2020-03-09 14:57:07 +01:00
|
|
|
fn parse_top_level<'a>(
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
2020-03-14 07:30:44 +01:00
|
|
|
scope: &Scope,
|
2020-03-09 14:57:07 +01:00
|
|
|
optimize_ast: bool,
|
|
|
|
) -> Result<AST, ParseError> {
|
2020-03-11 06:28:12 +01:00
|
|
|
let mut statements = Vec::<Stmt>::new();
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
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() {
|
2016-02-29 22:43:45 +01:00
|
|
|
match input.peek() {
|
2020-03-11 06:28:12 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-03-12 06:02:13 +01:00
|
|
|
Some(&(Token::Fn, _)) => {
|
|
|
|
let f = parse_fn(input)?;
|
|
|
|
|
|
|
|
// 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-03 09:23:55 +01:00
|
|
|
_ => statements.push(parse_stmt(input)?),
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
2020-03-03 11:15:20 +01:00
|
|
|
// Notice semicolons are optional
|
2020-02-29 13:12:10 +01:00
|
|
|
if let Some(&(Token::SemiColon, _)) = input.peek() {
|
2017-10-02 23:44:45 +02:00
|
|
|
input.next();
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-14 13:06:10 +01:00
|
|
|
Ok(
|
|
|
|
#[cfg(not(feature = "no_optimize"))]
|
|
|
|
AST(
|
|
|
|
if optimize_ast {
|
|
|
|
optimize(statements, &scope)
|
|
|
|
} else {
|
|
|
|
statements
|
|
|
|
},
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
functions
|
|
|
|
.into_iter()
|
|
|
|
.map(|mut fn_def| {
|
|
|
|
if optimize_ast {
|
|
|
|
let pos = fn_def.body.position();
|
|
|
|
let mut body = optimize(vec![fn_def.body], &scope);
|
|
|
|
fn_def.body = body.pop().unwrap_or_else(|| Stmt::Noop(pos));
|
|
|
|
}
|
|
|
|
Arc::new(fn_def)
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
),
|
|
|
|
#[cfg(feature = "no_optimize")]
|
|
|
|
AST(
|
|
|
|
statements,
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
functions.into_iter().map(Arc::new).collect(),
|
|
|
|
),
|
|
|
|
)
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|
|
|
|
|
2020-03-09 14:57:07 +01:00
|
|
|
pub fn parse<'a>(
|
|
|
|
input: &mut Peekable<TokenIterator<'a>>,
|
2020-03-14 07:30:44 +01:00
|
|
|
scope: &Scope,
|
2020-03-09 14:57:07 +01:00
|
|
|
optimize_ast: bool,
|
|
|
|
) -> Result<AST, ParseError> {
|
2020-03-14 07:30:44 +01:00
|
|
|
parse_top_level(input, scope, optimize_ast)
|
2016-02-29 22:43:45 +01:00
|
|
|
}
|