luallaby 0.1.0

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

/// ```text
/// stat ::= for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end |
///          for namelist in explist do block end
/// namelist ::= Name {‘,’ Name}
/// explist ::= exp {‘,’ exp}
/// ```

pub enum For {
    Numeric(ForNum),
    Generic(ForGen),
}

pub struct ForNum {
    pub name: String,
    pub init: Box<Exp>,
    pub limit: Box<Exp>,
    pub step: Option<Box<Exp>>,
    pub block: Box<Block>,
    pub pos_for: Pos,
    pub pos_end: Pos,
}

pub struct ForGen {
    pub names: Vec<String>,
    pub exps: Vec<Exp>,
    pub block: Box<Block>,
    pub pos_for: Pos,
    pub pos_call: Pos,
    pub pos_end: Pos,
}

impl<'a> Parser<'a> {
    pub(super) fn parse_for(&mut self) -> Result<For> {
        let pos_for = tok_expect!(self, Token::For);

        let (_, name) = self.tok_ident()?;

        Ok(match self.tok_peek()?.into() {
            (_, Token::Is) => For::Numeric(self.parse_for_num(name, pos_for)?),
            (_, Token::Comma | Token::In) => For::Generic(self.parse_for_gen(name, pos_for)?),
            (pos, tok) => return err!(&*self.source, pos, LuaError::UnexpectedToken(tok.clone())),
        })
    }

    fn parse_for_num(&mut self, name: String, pos_for: Pos) -> Result<ForNum> {
        tok_expect!(self, Token::Is);

        let init = Box::new(self.parse_exp()?);

        tok_expect!(self, Token::Comma);

        let limit = Box::new(self.parse_exp()?);

        let step = match self.tok_peek_opt()?.map(Spanned::inner) {
            Some(Token::Comma) => {
                self.tok_next()?;
                Some(Box::new(self.parse_exp()?))
            }
            _ => None,
        };

        tok_expect!(self, Token::Do);

        let block = Box::new(self.parse_block()?);

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

        Ok(ForNum {
            name,
            init,
            limit,
            step,
            block,
            pos_for,
            pos_end,
        })
    }

    fn parse_for_gen(&mut self, name: String, pos_for: Pos) -> Result<ForGen> {
        let mut names = vec![name];

        loop {
            match self.tok_next()?.into() {
                (_, Token::Comma) => names.push(self.tok_ident()?.1),
                (_, Token::In) => break,
                (pos, tok) => return err!(&*self.source, pos, LuaError::UnexpectedToken(tok)),
            }
        }

        let pos_call = self.tok_peek()?.pos();

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

        tok_expect!(self, Token::Do);

        let block = Box::new(self.parse_block()?);

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

        Ok(ForGen {
            names,
            exps,
            block,
            pos_for,
            pos_call,
            pos_end,
        })
    }
}