use std::rc::Rc;
use crate::ast::{Exp, Parser, PrefixExp};
use crate::error::{LuaError, Result};
use crate::lexer::{Pos, Spanned, Token};
use crate::vm::Literal;
pub enum TableField {
Index(Exp, Exp),
List(Exp),
}
impl<'a> Parser<'a> {
pub(super) fn parse_table_constructor(&mut self) -> Result<(Vec<TableField>, Pos)> {
let mut fields = Vec::new();
let pos = tok_expect!(self, Token::CurlO);
if !matches!(self.tok_peek_opt()?.map(Spanned::inner), Some(Token::CurlC)) {
fields.push(self.parse_table_field()?);
}
loop {
match self.tok_next_opt()? {
Some(token) => match token.into() {
(_, Token::CurlC) => break,
(_, Token::Comma | Token::Semi) => {
if matches!(self.tok_peek_opt()?.map(Spanned::inner), Some(Token::CurlC)) {
self.tok_next()?;
break;
}
fields.push(self.parse_table_field()?);
}
(pos, tok) => return err!(self.source, pos, LuaError::UnexpectedToken(tok)),
},
None => {
return err!(
self.source,
self.tokens.last_pos(),
LuaError::UnexpectedEofBalanced('{', '}', pos.line())
)
}
}
}
Ok((fields, pos))
}
fn parse_table_field(&mut self) -> Result<TableField> {
Ok(match self.tok_peek_opt()?.map(Spanned::inner) {
Some(Token::SqrO) => {
self.tok_next()?;
let index = self.parse_exp()?;
tok_expect!(self, Token::SqrC);
tok_expect!(self, Token::Is);
TableField::Index(index, self.parse_exp()?)
}
_ => {
let exp = self.parse_exp()?;
match self.tok_peek_opt()?.map(Spanned::inner) {
Some(Token::Comma | Token::Semi | Token::CurlC) | None => TableField::List(exp),
_ => {
tok_expect!(self, Token::Is);
let (name, pos) = match exp {
Exp::Prefix(PrefixExp::Var(name, pos)) => (name, pos),
_ => return err!(LuaError::TableFieldParse),
};
TableField::Index(
Exp::Lit(Literal::String(Rc::new(name.into_bytes())), pos),
self.parse_exp()?,
)
}
}
}
})
}
}