pr47 0.1.4-CHARLIE

A semi-experimental programming language. Still working in progress.
Documentation
use super::{Parser, TOP_LEVEL_DECL_FAILSAFE};

use xjbutil::either::Either;

use crate::awa;
use crate::diag::diag_data;
use crate::diag::location::SourceRange;
use crate::parse::lexer::LexerMode;
use crate::parse::parser::TOP_LEVEL_FIRST;
use crate::syntax::ConcreteProgram;
use crate::syntax::attr::Attribute;
use crate::syntax::decl::ConcreteDecl;
use crate::syntax::token::{Token, TokenInner};

impl<'s, 'd> Parser<'s, 'd> {
    pub fn parse(&mut self) -> ConcreteProgram<'s> {
        self.lexer.push_lexer_mode(LexerMode::LexTopDecl);

        let mut program = ConcreteProgram::new();
        while !self.m_current_token.is_eoi() {
            if self.m_current_token.token_inner == TokenInner::SymHash {
                let hash_token: Token<'s> = self.consume_token();
                if let Some(item) = self.parse_global_attr_or_attributed_decl(hash_token) {
                    match item {
                        Either::Left(decl) => program.decls.push(decl),
                        Either::Right(global_attr) => program.global_attrs.push(global_attr)
                    }
                }
            } else if let Some(top_level_decl) = self.parse_top_level_decl() {
                program.decls.push(top_level_decl);
            }
        }

        program
    }

    pub fn parse_global_attr_or_attributed_decl(&mut self, hash_token: Token<'s>)
        -> Option<Either<ConcreteDecl<'s>, Attribute<'s>>>
    {
        #[cfg(debug_assertions)] assert_eq!(hash_token.token_inner, TokenInner::SymHash);

        match self.current_token().token_inner {
            TokenInner::SymLBracket => {
                self.parse_attributed_top_level_decl(hash_token).map(Either::Left)
            },
            TokenInner::SymExclaim => {
                self.parse_attribute(hash_token, true, TOP_LEVEL_FIRST).map(Either::Right)
            },
            _ => {
                self.diag.borrow_mut()
                    .diag(self.current_token().range.left(),
                          diag_data::err_expected_any_of_0_got_1)
                    .add_arg2(awa![TokenInner::SymExclaim, TokenInner::SymLBracket])
                    .add_arg2(self.current_token().token_inner)
                    .add_mark(self.current_token().range.into())
                    .emit();
                None
            }
        }
    }

    pub fn parse_attributed_top_level_decl(
        &mut self,
        hash_token: Token<'s>
    ) -> Option<ConcreteDecl<'s>> {
        #[cfg(debug_assertions)] assert_eq!(hash_token.token_inner, TokenInner::SymHash);

        let attr_list: Attribute = self.parse_attribute(hash_token, false, TOP_LEVEL_FIRST)?;
        let mut decl: ConcreteDecl = self.parse_top_level_decl()?;

        match &mut decl {
            ConcreteDecl::ConstDecl(const_decl) => unsafe {
                const_decl.attr.replace(attr_list).unwrap_unchecked();
            },
            ConcreteDecl::FuncDecl(func_decl) => unsafe {
                func_decl.attr.replace(attr_list).unwrap_unchecked();
            },
            ConcreteDecl::ExportDecl(export_decl) => {
                self.diag.borrow_mut()
                    .diag(export_decl.export_kwd_range.left(),
                          diag_data::err_export_decl_disallow_attr)
                    .add_mark(export_decl.export_kwd_range.into())
                    .emit();
            },
            ConcreteDecl::ImportDecl(import_decl) => {
                self.diag.borrow_mut()
                    .diag(import_decl.import_kwd_range.left(),
                          diag_data::err_import_decl_disallow_attr)
                    .add_mark(import_decl.import_kwd_range.into())
                    .emit();
            },
            ConcreteDecl::OpenImportDecl(open_import_decl) => {
                self.diag.borrow_mut()
                    .diag(open_import_decl.open_kwd_range.left(),
                          diag_data::err_import_decl_disallow_attr)
                    .add_mark(open_import_decl.open_kwd_range.into())
                    .emit();
            },
            ConcreteDecl::VarDecl(_) => {
                unreachable!("variable declarations cannot appear at top level")
            },
        }
        Some(decl)
    }

    pub fn parse_top_level_decl(&mut self) -> Option<ConcreteDecl<'s>> {
        match self.current_token().token_inner {
            TokenInner::KwdConst => {
                let const_token: Token<'s> = self.consume_token();
                self.parse_object_decl(const_token, TOP_LEVEL_DECL_FAILSAFE)
                    .map(ConcreteDecl::ConstDecl)
            },
            TokenInner::KwdVar => {
                self.diag.borrow_mut()
                    .diag(self.current_token().range.left(),
                          diag_data::err_no_top_level_var_decl)
                    .add_mark(self.current_token().range.into())
                    .emit();
                None
            },
            TokenInner::KwdFunc => {
                let func_token: Token<'s> = self.consume_token();
                self.parse_func_decl(func_token, TOP_LEVEL_DECL_FAILSAFE)
                    .map(ConcreteDecl::FuncDecl)
            },
            TokenInner::KwdExport => {
                let export_token: Token<'s> = self.consume_token();
                self.parse_export_decl(export_token, TOP_LEVEL_DECL_FAILSAFE)
                    .map(ConcreteDecl::ExportDecl)
            },
            TokenInner::KwdImport => {
                let import_token: Token<'s> = self.consume_token();
                self.parse_import_decl(import_token, TOP_LEVEL_DECL_FAILSAFE)
                    .map(ConcreteDecl::ImportDecl)
            },
            TokenInner::KwdOpen => {
                let open_token: Token<'s> = self.consume_token();
                self.parse_open_import_decl(open_token, TOP_LEVEL_DECL_FAILSAFE)
                    .map(ConcreteDecl::OpenImportDecl)
            },
            _ => {
                self.diag.borrow_mut()
                    .diag(self.current_token().range.left(), diag_data::err_expected_any_of_0_got_1)
                    .add_arg2(awa![
                        TokenInner::KwdConst,
                        TokenInner::KwdFunc,
                        TokenInner::KwdExport,
                        TokenInner::KwdImport,
                        TokenInner::KwdOpen
                    ])
                    .add_arg2(self.current_token().token_inner)
                    .add_mark(self.current_token().range.into())
                    .emit();
                self.skip_to_any_of(TOP_LEVEL_DECL_FAILSAFE);
                None
            }
        }
    }

    pub fn diag_unexpected_eoi(&mut self, range: SourceRange) {
        self.diag.borrow_mut()
            .diag(range.left(), diag_data::err_unexpected_eoi)
            .emit()
    }
}