1macro_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
35pub struct Parser<'a> {
37 session: &'a midenc_session::Session,
38}
39impl<'a> Parser<'a> {
40 pub fn new(session: &'a midenc_session::Session) -> Self {
42 Self { session }
43 }
44
45 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 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 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}