luallaby 0.1.0

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
use std::rc::Rc;

use crate::error::{LuaError, Result};
use crate::lexer::{Pos, Spanned, Token, Tokenizer};

macro_rules! tok_expect {
    ( $self:ident, $pat:pat ) => {{
        let (pos, tok) = $self.tok_next()?.into();
        if !matches!(tok, $pat) {
            return err!(
                &*$self.source,
                pos,
                crate::error::LuaError::UnexpectedToken(tok)
            );
        }
        pos
    }};
}

mod assignment;
mod block;
mod chunk;
mod expression;
mod r#for;
mod function;
mod r#if;
mod local;
mod prefix_exp;
mod repeat;
mod statement;
mod table;
mod r#while;

pub use assignment::{Ass, Var};
pub use block::Block;
pub use chunk::Chunk;
pub use expression::Exp;
pub use function::Func;
pub use local::{Attr, Local};
pub use prefix_exp::PrefixExp;
pub use r#for::{For, ForGen, ForNum};
pub use r#if::If;
pub use r#while::While;
pub use repeat::Repeat;
pub use statement::Stat;
pub use table::TableField;

/// Parser to parse Lua code into a syntax tree.
/// Entry point for parsing the Lua code is at [`Chunk`].
pub struct Parser<'a> {
    tokens: Tokenizer<'a>,
    peek: Option<Option<Spanned<Token>>>,
    source: Rc<Vec<u8>>,
    exp_depth: usize,
    scope_depth: usize,
}

const MAX_ARGS: usize = 200;
const MAX_ASSIGN: usize = 200;
const MAX_EXP: usize = 200;
const MAX_SCOPE: usize = 200;

impl<'a> Parser<'a> {
    pub fn parse(input: &'a [u8], source: Rc<Vec<u8>>) -> Result<Chunk> {
        let mut parser = Self {
            tokens: Tokenizer::read(input, source.clone()),
            peek: None,
            source,
            exp_depth: 0,
            scope_depth: 0,
        };

        let chunk = parser.parse_chunk()?;
        // Ensure all was read
        if let Ok((pos, tok)) = parser.tok_next().map(Into::into) {
            return err!(&*parser.source, pos, LuaError::UnexpectedToken(tok));
        }
        assert!(parser.exp_depth == 0);
        assert!(parser.scope_depth == 0);
        Ok(chunk)
    }

    fn tok_next(&mut self) -> Result<Spanned<Token>> {
        let opt = match self.peek.take() {
            Some(opt) => opt,
            None => self.tokens.read_token()?,
        };
        match opt {
            Some(tok) => Ok(tok),
            None => err!(
                &*self.source,
                self.tokens.last_pos(),
                LuaError::UnexpectedEof
            ),
        }
    }

    fn tok_next_opt(&mut self) -> Result<Option<Spanned<Token>>> {
        match self.peek.take() {
            Some(opt) => Ok(opt),
            None => self.tokens.read_token(),
        }
    }

    fn tok_ident(&mut self) -> Result<(Pos, String)> {
        let (pos, tok) = self.tok_next()?.into();
        match tok {
            Token::Ident(ident) => Ok((pos, ident)),
            tok => err!(&*self.source, pos, LuaError::UnexpectedToken(tok)),
        }
    }

    fn tok_peek(&mut self) -> Result<&Spanned<Token>> {
        if self.peek.is_none() {
            self.peek = Some(self.tokens.read_token()?);
        }
        match self.peek.as_ref().unwrap() {
            Some(tok) => Ok(tok),
            None => err!(
                &*self.source,
                self.tokens.last_pos(),
                LuaError::UnexpectedEof
            ),
        }
    }

    fn tok_peek_opt(&mut self) -> Result<Option<&Spanned<Token>>> {
        if self.peek.is_none() {
            self.peek = Some(self.tokens.read_token()?);
        }
        Ok(self.peek.as_ref().unwrap().as_ref())
    }
}