use crate::ast::{Block, Exp, Parser};
use crate::error::{LuaError, Result};
use crate::lexer::{Pos, Spanned, Token};
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,
})
}
}