use crate::ast::{Func, Parser, PrefixExp, TableField};
use crate::error::{LuaError, Result};
use crate::lexer::{Pos, Spanned, Token};
use crate::vm::{BinOp, Literal, UnOp};
use super::MAX_EXP;
pub enum Exp {
BinOp(Box<Exp>, BinOp, Box<Exp>, Pos),
UnOp(UnOp, Box<Exp>, Pos),
Lit(Literal, Pos),
Table(Vec<TableField>, Pos),
Prefix(PrefixExp),
Func(Func),
Varargs(Pos),
}
#[derive(PartialEq)]
enum ExpParseState {
Init,
Operand,
Operator,
Done,
}
enum Op {
BinOp((BinOp, Pos)),
UnOp((UnOp, Pos)),
}
struct ExpParser<'a, 'b> {
parser: &'a mut Parser<'b>,
state: ExpParseState,
out: Vec<Exp>,
ops: Vec<Op>,
}
impl<'a> Parser<'a> {
pub(super) fn parse_exp(&mut self) -> Result<Exp> {
ExpParser {
parser: self,
state: ExpParseState::Init,
out: Vec::new(),
ops: Vec::new(),
}
.parse()
}
}
impl Op {
fn precedence(&self) -> usize {
match self {
Op::BinOp((op, _)) => op.precedence(),
Op::UnOp((op, _)) => op.precedence(),
}
}
}
impl<'a, 'b> ExpParser<'a, 'b> {
fn parse(mut self) -> Result<Exp> {
self.parser.exp_depth += 1;
if self.parser.exp_depth > MAX_EXP {
return err!(LuaError::SyntaxLimit("nested expressions", None));
}
while self.state != ExpParseState::Done {
self.transition()?;
}
self.parser.exp_depth -= 1;
self.finalize()
}
fn transition(&mut self) -> Result<()> {
if self.ops.len() > MAX_EXP {
return err!(LuaError::SyntaxLimit("expression operands", None));
}
self.state = match self.state {
ExpParseState::Init => {
if let Some(op) = self.peek_un_op() {
self.parser.tok_next()?;
self.ops.push(Op::UnOp(op));
ExpParseState::Operator
} else {
let op = self.parse_operand()?;
self.out.push(op);
ExpParseState::Operand
}
}
ExpParseState::Operand => {
if let Some((op, pos)) = self.peek_bin_op() {
while let Some(pop) = self.ops.last() {
if pop.precedence() > op.precedence()
|| (op.is_left_ass() && pop.precedence() == op.precedence())
{
self.pop_op()?;
} else {
break;
}
}
self.parser.tok_next()?;
self.ops.push(Op::BinOp((op, pos)));
ExpParseState::Operator
} else {
ExpParseState::Done
}
}
ExpParseState::Operator => {
if let Some(op) = self.peek_un_op() {
self.parser.tok_next()?;
self.ops.push(Op::UnOp(op));
ExpParseState::Operator
} else {
let op = self.parse_operand()?;
self.out.push(op);
ExpParseState::Operand
}
}
ExpParseState::Done => unreachable!(),
};
Ok(())
}
fn finalize(mut self) -> Result<Exp> {
while !self.ops.is_empty() {
self.pop_op()?;
}
if self.out.len() == 1 {
Ok(self.out.pop().unwrap())
} else {
panic!()
}
}
fn parse_operand(&mut self) -> Result<Exp> {
if self.peek_prefix_exp() {
Ok(Exp::Prefix(self.parser.parse_prefix_exp()?))
} else if let Some(lit) = self.peek_literal() {
let (pos, _) = self.parser.tok_next()?.into();
Ok(Exp::Lit(lit, pos))
} else if self.peek_table() {
let (tbl, pos) = self.parser.parse_table_constructor()?;
Ok(Exp::Table(tbl, pos))
} else if self.peek_function() {
self.parser.tok_next()?;
Ok(Exp::Func(self.parser.parse_func()?))
} else if let Some((pos, &Token::Dots)) = self.parser.tok_peek_opt()?.map(Into::into) {
self.parser.tok_next()?;
Ok(Exp::Varargs(pos))
} else {
let (pos, tok) = self.parser.tok_next()?.into();
return err!(&*self.parser.source, pos, LuaError::UnexpectedToken(tok));
}
}
fn peek_prefix_exp(&mut self) -> bool {
matches!(
self.parser.tok_peek().ok().map(Spanned::inner),
Some(Token::Ident(..) | Token::ParO)
)
}
fn peek_table(&mut self) -> bool {
matches!(
self.parser.tok_peek().ok().map(Spanned::inner),
Some(Token::CurlO)
)
}
fn peek_function(&mut self) -> bool {
matches!(
self.parser.tok_peek().ok().map(Spanned::inner),
Some(Token::Function)
)
}
fn peek_literal(&mut self) -> Option<Literal> {
match self.parser.tok_peek().ok()?.inner() {
Token::Nil => Some(Literal::Nil),
Token::True => Some(Literal::Bool(true)),
Token::False => Some(Literal::Bool(false)),
Token::Integer(n, _) => Some(Literal::Int(*n)),
Token::Float(f, _) => Some(Literal::Float(*f)),
Token::String(s, _) => Some(Literal::String(s.clone())),
_ => None,
}
}
fn peek_bin_op(&mut self) -> Option<(BinOp, Pos)> {
let (pos, token) = self.parser.tok_peek().ok()?.into();
match token {
Token::Add => Some(BinOp::Add),
Token::Sub => Some(BinOp::Sub),
Token::Mul => Some(BinOp::Mul),
Token::Div => Some(BinOp::Div),
Token::FlDiv => Some(BinOp::FlDiv),
Token::Pow => Some(BinOp::Pow),
Token::Mod => Some(BinOp::Mod),
Token::Amp => Some(BinOp::BitAnd),
Token::Tilde => Some(BinOp::BitXor),
Token::Bar => Some(BinOp::BitOr),
Token::Shl => Some(BinOp::Shl),
Token::Shr => Some(BinOp::Shr),
Token::Conc => Some(BinOp::Conc),
Token::Eq => Some(BinOp::Eq),
Token::Neq => Some(BinOp::Neq),
Token::Lt => Some(BinOp::Lt),
Token::Leq => Some(BinOp::Leq),
Token::Gt => Some(BinOp::Gt),
Token::Geq => Some(BinOp::Geq),
Token::And => Some(BinOp::And),
Token::Or => Some(BinOp::Or),
_ => None,
}
.map(|op| (op, pos))
}
fn peek_un_op(&mut self) -> Option<(UnOp, Pos)> {
let (pos, token) = self.parser.tok_peek().ok()?.into();
match token {
Token::Sub => Some(UnOp::Minus),
Token::Tilde => Some(UnOp::BitNot),
Token::Not => Some(UnOp::Not),
Token::Hash => Some(UnOp::Len),
_ => None,
}
.map(|op| (op, pos))
}
fn pop_op(&mut self) -> Result<()> {
let op = match self.ops.pop() {
Some(o) => o,
None => panic!(),
};
let out = match op {
Op::BinOp((op, pos)) => {
let op2 = match self.out.pop() {
Some(v) => v,
None => panic!(),
};
let op1 = match self.out.pop() {
Some(v) => v,
None => panic!(),
};
Exp::BinOp(Box::new(op1), op, Box::new(op2), pos)
}
Op::UnOp((op, pos)) => {
let op1 = match self.out.pop() {
Some(v) => v,
None => panic!(),
};
Exp::UnOp(op, Box::new(op1), pos)
}
};
self.out.push(out);
Ok(())
}
}