luallaby 0.1.0

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

/// ```text
/// prefixexp ::= var | functioncall | ‘(’ exp ‘)’
/// var ::=  Name | prefixexp ‘[’ exp ‘]’ | prefixexp ‘.’ Name
/// functioncall ::=  prefixexp args | prefixexp ‘:’ Name args
/// args ::=  ‘(’ [explist] ‘)’ | tableconstructor | LiteralString
/// ```
///
/// This means that a prefix expression always starts with either a `Name` or `‘(’`
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 {
                                // XXX: Does not need to be enforced, but Lua test suite does
                                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())),
        }
    }
}