midenc_hir/parser/
mod.rs

1/// Simple macro used in the grammar definition for constructing spans
2macro_rules! span {
3    ($source_id:expr, $l:expr, $r:expr) => {
4        SourceSpan::new($source_id, $l..$r)
5    };
6    ($source_id:expr, $i:expr) => {
7        SourceSpan::at($source_id, $i)
8    };
9}
10
11pub mod ast;
12mod error;
13mod lexer;
14#[cfg(test)]
15mod tests;
16
17lalrpop_mod!(
18    #[allow(clippy::all)]
19    grammar,
20    "/parser/grammar.rs"
21);
22
23use alloc::sync::Arc;
24use std::path::Path;
25
26pub use self::error::ParseError;
27use self::{
28    ast::ConvertAstToHir,
29    lexer::{Lexed, Lexer},
30};
31use crate::diagnostics::{Report, SourceFile, SourceManagerExt};
32
33pub type ParseResult<T> = Result<T, Report>;
34
35/// This is the parser for HIR text format
36pub struct Parser<'a> {
37    session: &'a midenc_session::Session,
38}
39impl<'a> Parser<'a> {
40    /// Construct a new [Parser]
41    pub fn new(session: &'a midenc_session::Session) -> Self {
42        Self { session }
43    }
44
45    /// Parse a `T` from a source file stored in the current code map
46    pub fn parse<T>(&self, source: Arc<SourceFile>) -> ParseResult<T>
47    where
48        T: Parse,
49    {
50        <T as Parse>::parse(self, source)
51    }
52
53    /// Parse a `T` from a string
54    pub fn parse_str<T>(&self, source: impl AsRef<str>) -> ParseResult<T>
55    where
56        T: Parse,
57    {
58        let file = self.session.source_manager.load("nofile", source.as_ref().to_string());
59        self.parse(file)
60    }
61
62    /// Parse a `T` from the given file path
63    pub fn parse_file<T>(&self, path: impl AsRef<Path>) -> ParseResult<T>
64    where
65        T: Parse,
66    {
67        let path = path.as_ref();
68        let file = self.session.source_manager.load_file(path).map_err(|err| {
69            Report::msg(err).wrap_err(format!("failed to load '{}' from disk", path.display()))
70        })?;
71        self.parse(file)
72    }
73}
74
75pub trait Parse: Sized {
76    type Grammar;
77
78    fn parse(parser: &Parser, source: Arc<SourceFile>) -> ParseResult<Self> {
79        let lexer = Lexer::new(source.id(), source.as_str());
80
81        Self::parse_tokens(parser, source.clone(), lexer)
82    }
83
84    fn parse_tokens(
85        parser: &Parser,
86        source: Arc<SourceFile>,
87        tokens: impl IntoIterator<Item = Lexed>,
88    ) -> ParseResult<Self>;
89}
90impl Parse for ast::Module {
91    type Grammar = grammar::ModuleParser;
92
93    fn parse_tokens(
94        _parser: &Parser,
95        source: Arc<SourceFile>,
96        tokens: impl IntoIterator<Item = Lexed>,
97    ) -> ParseResult<Self> {
98        let source_id = source.id();
99        let mut next_var = 0;
100        let result = <Self as Parse>::Grammar::new().parse(source_id, &mut next_var, tokens);
101        match result {
102            Ok(ast) => Ok(ast),
103            Err(lalrpop_util::ParseError::User { error }) => {
104                Err(Report::from(error).with_source_code(source))
105            }
106            Err(err) => {
107                let error = ParseError::from(err);
108                Err(Report::from(error).with_source_code(source))
109            }
110        }
111    }
112}
113impl Parse for crate::Module {
114    type Grammar = grammar::ModuleParser;
115
116    fn parse_tokens(
117        parser: &Parser,
118        source: Arc<SourceFile>,
119        tokens: impl IntoIterator<Item = Lexed>,
120    ) -> ParseResult<Self> {
121        use crate::pass::{AnalysisManager, ConversionPass};
122
123        let source_id = source.id();
124        let mut next_var = 0;
125        let result = <Self as Parse>::Grammar::new()
126            .parse(source_id, &mut next_var, tokens)
127            .map(Box::new);
128        match result {
129            Ok(ast) => {
130                let mut analyses = AnalysisManager::new();
131                let mut convert_to_hir = ConvertAstToHir;
132                convert_to_hir
133                    .convert(ast, &mut analyses, parser.session)
134                    .map_err(|err| err.with_source_code(source))
135            }
136            Err(lalrpop_util::ParseError::User { error }) => {
137                Err(Report::from(error).with_source_code(source))
138            }
139            Err(err) => {
140                let error = ParseError::from(err);
141                Err(Report::from(error).with_source_code(source))
142            }
143        }
144    }
145}