luallaby 0.1.0

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
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;

/// ```text
/// tableconstructor ::= ‘{’ [fieldlist] ‘}’
/// fieldlist ::= field {fieldsep field} [fieldsep]
/// field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp
/// fieldsep ::= ‘,’ | ‘;’
/// ```
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);

        // Consume first field (if there is any)
        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) => {
                        // Detect trailing separator
                        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()?,
                        )
                    }
                }
            }
        })
    }
}