use std::rc::Rc;
use crate::ast::{Exp, Parser, MAX_ARGS};
use crate::error::{LuaError, Result};
use crate::lexer::{Pos, Spanned, Token};
use crate::vm::Literal;
pub enum PrefixExp {
Var(String, Pos),
Index(Box<PrefixExp>, Box<Exp>, Pos),
Call(Pos, Box<PrefixExp>, Vec<Exp>),
CallMethod(Pos, Box<PrefixExp>, String, Vec<Exp>),
Par(Box<Exp>, Pos),
}
impl<'a> Parser<'a> {
pub(super) fn parse_prefix_exp(&mut self) -> Result<PrefixExp> {
let (pos, mut res) = match self.tok_next()?.into() {
(pos, Token::Ident(name)) => (pos, PrefixExp::Var(name, pos)),
(pos, Token::ParO) => {
let res = PrefixExp::Par(Box::new(self.parse_exp()?), pos);
tok_expect!(self, Token::ParC);
(pos, res)
}
(pos, tok) => return err!(&*self.source, pos, LuaError::UnexpectedToken(tok)),
};
loop {
match self.tok_peek_opt()?.map(Into::into) {
Some((pos, Token::SqrO)) => {
self.tok_next()?;
let exp = self.parse_exp()?;
tok_expect!(self, Token::SqrC);
res = PrefixExp::Index(Box::new(res), Box::new(exp), pos);
}
Some((pos, Token::Point)) => {
self.tok_next()?;
let (pos_ind, ind) = self.tok_ident()?;
res = PrefixExp::Index(
Box::new(res),
Box::new(Exp::Lit(
Literal::String(Rc::new(ind.into_bytes())),
pos_ind,
)),
pos,
);
}
Some((_, Token::ParO | Token::CurlO | Token::String(..))) => {
let args = self.parse_args()?;
res = PrefixExp::Call(pos, Box::new(res), args);
}
Some((_, Token::Colon)) => {
self.tok_next()?;
let (_, name) = self.tok_ident()?;
let args = self.parse_args()?;
res = PrefixExp::CallMethod(pos, Box::new(res), name, args);
}
_ => break,
}
}
Ok(res)
}
fn parse_args(&mut self) -> Result<Vec<Exp>> {
match self.tok_peek()?.into() {
(_, Token::ParO) => {
self.tok_next()?;
let mut args = Vec::new();
loop {
match self.tok_peek_opt()?.map(Spanned::inner) {
Some(Token::ParC) => break,
Some(Token::Comma) => {
if args.is_empty() {
panic!()
} else if args.len() > MAX_ARGS {
return err!(LuaError::SyntaxLimit("registers", None));
} else {
self.tok_next()?;
}
}
_ => {}
}
args.push(self.parse_exp()?);
}
tok_expect!(self, Token::ParC);
Ok(args)
}
(_, Token::CurlO) => {
let (fields, pos) = self.parse_table_constructor()?;
let exp = Exp::Table(fields, pos);
Ok(vec![exp])
}
(pos, Token::String(str, _)) => {
let exp = Exp::Lit(Literal::String(str.clone()), pos);
self.tok_next()?;
Ok(vec![exp])
}
(pos, tok) => err!(&*self.source, pos, LuaError::UnexpectedToken(tok.clone())),
}
}
}