pr47 0.1.4-CHARLIE

A semi-experimental programming language. Still working in progress.
Documentation
pub mod attr;
pub mod base;
pub mod decl;
pub mod expr;
pub mod stmt;
pub mod top_level;
pub mod ty;

use std::cell::RefCell;
use std::mem::swap;

use xjbutil::defer;

use crate::diag::{DiagContext, DiagMark};
use crate::diag::diag_data;
use crate::parse::lexer::{Lexer, LexerMode};
use crate::syntax::token::{Token, TokenInner};

pub struct Parser<'src, 'diag> {
    lexer: Lexer<'src, 'diag>,
    m_current_token: Token<'src>,
    m_peek_token: Option<Token<'src>>,

    #[allow(unused)] file_id: u32,
    #[allow(unused)] source: &'src str,
    diag: &'diag RefCell<DiagContext>
}

impl<'s, 'd> Parser<'s, 'd> {
    pub fn new(file_id: u32, source: &'s str, diag: &'d RefCell<DiagContext>) -> Self {
        let mut lexer: Lexer<'s, 'd> = Lexer::new(file_id, source, diag);
        let current_token: Token<'s> = lexer.next_token();
        let peek_token: Option<Token<'s>> = None;

        Parser {
            lexer,
            m_current_token: current_token,
            m_peek_token: peek_token,

            file_id,
            source,
            diag
        }
    }

    fn consume_token(&mut self) -> Token<'s> {
        if self.m_current_token.is_eoi() {
            return Token::new_eoi(self.m_current_token.range);
        }

        let mut token: Token<'s> = if let Some(peek_token) = self.m_peek_token.take() {
            peek_token
        } else {
            self.lexer.next_token()
        };
        swap(&mut token, &mut self.m_current_token);
        token
    }

    fn current_token(&self) -> &Token<'s> {
        &self.m_current_token
    }

    #[allow(unused)]
    fn peek_token(&mut self) -> &Token<'s> {
        if self.m_current_token.is_eoi() {
            return &self.m_current_token;
        }

        if self.m_peek_token.is_none() {
            self.m_peek_token = Some(self.lexer.next_token())
        }
        unsafe { self.m_peek_token.as_ref().unwrap_unchecked() }
    }

    #[must_use = "not using the return value is likely to be a mistake"]
    pub fn expect_token(
        &mut self,
        token_kind: TokenInner<'_>,
        skip_on_failure: &[&[TokenInner<'_>]]
    ) -> Option<&Token<'s>> {
        if self.current_token().token_inner != token_kind {
            self.diag.borrow_mut()
                .diag(self.current_token().range.left(), diag_data::err_expected_token_0_got_1)
                .add_arg2(token_kind)
                .add_arg2(self.current_token().token_inner)
                .add_mark(
                    DiagMark::from(self.current_token().range).add_comment("unexpected token")
                )
                .emit();
            if !skip_on_failure.is_empty() {
                self.skip_to_any_of(skip_on_failure);
            }
            None
        } else {
            Some(self.current_token())
        }
    }

    #[must_use = "not using the return value is likely to be a mistake"]
    pub fn expect_n_consume(
        &mut self,
        token_kind: TokenInner<'_>,
        skip_on_failure: &[&[TokenInner<'_>]]
    ) -> Option<Token<'s>> {
        if self.expect_token(token_kind, skip_on_failure).is_some() {
            Some(self.consume_token())
        } else {
            None
        }
    }

    #[allow(unused)]
    fn skip_optional(&mut self, token_kind: TokenInner<'static>) -> bool {
        if self.current_token().token_inner != token_kind {
            false
        } else {
            self.consume_token();
            true
        }
    }

    #[allow(unused)]
    fn skip_when(&mut self, skipped: &[&[TokenInner<'_>]]) {
        self.without_attr_mode(|this: &mut Self| {
            while !this.current_token().is_eoi() {
                if skipped.iter().any(|tokens| tokens.contains(&this.current_token().token_inner)) {
                    this.consume_token();
                } else {
                    break;
                }
            }
        });
    }

    fn skip_to_any_of(&mut self, skip_choices: &[&[TokenInner<'_>]]) {
        self.without_attr_mode(|this: &mut Self| {
            while !this.current_token().is_eoi() {
                for skip_choice in skip_choices {
                    if skip_choice.contains(&this.current_token().token_inner) {
                        return;
                    }
                }
                this.consume_token();
            }
        });
    }

    fn without_attr_mode<T>(&mut self, skip_operation: impl FnOnce(&mut Self) -> T) -> T {
        if self.lexer.current_mode() == LexerMode::LexAttr {
            let this: &mut Self = self;
            this.lexer.push_lexer_mode(this.lexer.prev_mode());
            defer!(|this: &mut Self| {
                this.lexer.pop_lexer_mode();
            }, this);
            skip_operation(this)
        } else {
            skip_operation(self)
        }
    }

    #[cfg(test)]
    pub fn push_lexer_mode(&mut self, lexer_mode: LexerMode) {
        self.lexer.push_lexer_mode(lexer_mode);
    }

    #[cfg(test)]
    pub fn pop_lexer_mode(&mut self) {
        self.lexer.pop_lexer_mode();
    }
}

const TOP_LEVEL_DECL_FIRST: &[TokenInner<'static>] = &[
    TokenInner::KwdConst,
    TokenInner::KwdExport,
    TokenInner::KwdFunc,
    TokenInner::KwdImport,
    TokenInner::KwdOpen,
];

const ATTR_FIRST: &[TokenInner<'static>] = &[TokenInner::SymHash];

const TOP_LEVEL_FIRST: &[&[TokenInner<'static>]] = &[
    TOP_LEVEL_DECL_FIRST,
    ATTR_FIRST
];

const TOP_LEVEL_DECL_FAILSAFE: &[&[TokenInner<'static>]] = &[
    TOP_LEVEL_DECL_FIRST
];