luallaby 0.1.0

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
use crate::ast::{Block, Parser};
use crate::error::{LuaError, Result};
use crate::lexer::{Pos, Spanned, Token};

/// ```text
/// stat ::= function funcname funcbody
/// functiondef ::= function funcbody
/// funcbody ::= ‘(’ [parlist] ‘)’ block end
/// parlist ::= namelist [‘,’ ‘...’] | ‘...’
/// ```
pub struct Func {
    pub params: Vec<String>,
    pub varargs: bool,
    pub block: Box<Block>,
    pub pos_start: Pos,
    pub pos_end: Pos,
}

impl<'a> Parser<'a> {
    pub(super) fn parse_func(&mut self) -> Result<Func> {
        let (params, varargs, pos_start) = self.parse_params()?;

        let block = self.parse_block().map_err(|e| {
            e.map_lua_error(|l| match l {
                LuaError::SyntaxLimit(what, None) => {
                    LuaError::SyntaxLimit(what, Some(pos_start.line()))
                }
                l => l,
            })
        })?;

        let pos_end = tok_expect!(self, Token::End);

        Ok(Func {
            params,
            varargs,
            block: Box::new(block),
            pos_start,
            pos_end,
        })
    }

    fn parse_params(&mut self) -> Result<(Vec<String>, bool, Pos)> {
        let pos_start = tok_expect!(self, Token::ParO);

        let mut params = Vec::new();
        let mut varargs = false;

        if let Some(Token::ParC) = self.tok_peek_opt()?.map(Spanned::inner) {
            self.tok_next()?;
            return Ok((params, varargs, pos_start));
        }

        loop {
            match self.tok_next()?.into() {
                (_, Token::Ident(name)) => {
                    params.push(name.to_string());
                    match self.tok_next()?.into() {
                        (_, Token::Comma) => {}
                        (_, Token::ParC) => break,
                        (pos, tok) => {
                            return err!(&*self.source, pos, LuaError::UnexpectedToken(tok))
                        }
                    }
                }
                (_, Token::Dots) => {
                    varargs = true;
                    tok_expect!(self, Token::ParC);
                    break;
                }
                (_, Token::ParC) => break,
                (pos, tok) => return err!(&*self.source, pos, LuaError::UnexpectedToken(tok)),
            }
        }

        Ok((params, varargs, pos_start))
    }
}