luallaby 0.1.0

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

/// ```text
/// stat ::= local attnamelist [‘=’ explist]
/// attnamelist ::=  Name attrib {‘,’ Name attrib}
/// ```
pub struct Local {
    pub vars: Vec<(String, Option<Attr>, Pos)>,
    pub exps: Vec<Exp>,
}

#[derive(Clone, Copy)]
pub enum Attr {
    Const,
    Close,
}

impl<'a> Parser<'a> {
    pub(super) fn parse_local(&mut self) -> Result<Local> {
        let mut vars = vec![self.parse_name_attr()?];
        loop {
            match self.tok_peek_opt()?.map(Spanned::inner) {
                Some(Token::Comma) => {
                    self.tok_next()?;
                    vars.push(self.parse_name_attr()?);
                    // XXX: Does not need to be enforced, but Lua suite expects it
                    if vars.len() > 255 {
                        return err!(LuaError::SyntaxLimit("local variables", None));
                    }
                }
                Some(Token::Is) => {
                    self.tok_next()?;
                    break;
                }
                _ => {
                    return Ok(Local {
                        vars,
                        exps: Vec::new(),
                    })
                }
            }
        }

        let mut exps = vec![self.parse_exp()?];
        while let Some(Token::Comma) = self.tok_peek_opt()?.map(Spanned::inner) {
            self.tok_next()?;
            exps.push(self.parse_exp()?);
        }

        Ok(Local { vars, exps })
    }

    fn parse_name_attr(&mut self) -> Result<(String, Option<Attr>, Pos)> {
        let (pos, name) = self.tok_ident()?;
        let attr = match self.tok_peek_opt()?.map(Spanned::inner) {
            Some(Token::Lt) => {
                self.tok_next()?;
                let attr = match self.tok_ident()?.1.as_str() {
                    "const" => Attr::Const,
                    "close" => Attr::Close,
                    attr => return err!(LuaError::UnknownAttribute(attr.to_string())),
                };
                tok_expect!(self, Token::Gt);
                Some(attr)
            }
            _ => None,
        };
        Ok((name, attr, pos))
    }
}