1macro_rules! span {
3 ($l:expr, $r:expr) => {
4 miden_diagnostics::SourceSpan::new($l, $r)
5 };
6 ($i:expr) => {
7 miden_diagnostics::SourceSpan::new($i, $i)
8 };
9}
10
11lalrpop_mod!(
12 #[allow(clippy::all)]
13 grammar,
14 "/parser/grammar.rs"
15);
16
17use std::sync::Arc;
18
19use miden_diagnostics::{
20 CodeMap, Diagnostic, DiagnosticsHandler, Label, SourceIndex, SourceSpan, ToDiagnostic,
21};
22use miden_parsing::{Scanner, Source};
23
24use crate::{
25 ast,
26 lexer::{Lexed, Lexer, LexicalError, Token},
27 sema,
28};
29
30pub type Parser = miden_parsing::Parser<()>;
31
32#[derive(Debug, thiserror::Error)]
33pub enum ParseError {
34 #[error(transparent)]
35 Lexer(#[from] LexicalError),
36 #[error(transparent)]
37 Analysis(#[from] sema::SemanticAnalysisError),
38 #[error("error reading {path:?}: {source}")]
39 FileError {
40 source: std::io::Error,
41 path: std::path::PathBuf,
42 },
43 #[error("invalid token")]
44 InvalidToken(SourceIndex),
45 #[error("unexpected end of file")]
46 UnexpectedEof {
47 at: SourceIndex,
48 expected: Vec<String>,
49 },
50 #[error("unrecognized token '{token}'")]
51 UnrecognizedToken {
52 span: SourceSpan,
53 token: Token,
54 expected: Vec<String>,
55 },
56 #[error("extraneous token '{token}'")]
57 ExtraToken { span: SourceSpan, token: Token },
58 #[error("parsing failed, see diagnostics for details")]
59 Failed,
60}
61
62impl Eq for ParseError {}
63impl PartialEq for ParseError {
64 fn eq(&self, other: &Self) -> bool {
65 match (self, other) {
66 (Self::Lexer(l), Self::Lexer(r)) => l == r,
67 (Self::Analysis(l), Self::Analysis(r)) => l == r,
68 (Self::FileError { .. }, Self::FileError { .. }) => true,
69 (Self::InvalidToken(_), Self::InvalidToken(_)) => true,
70 (Self::UnexpectedEof { expected: l, .. }, Self::UnexpectedEof { expected: r, .. }) => {
71 l == r
72 }
73 (
74 Self::UnrecognizedToken {
75 token: lt,
76 expected: l,
77 ..
78 },
79 Self::UnrecognizedToken {
80 token: rt,
81 expected: r,
82 ..
83 },
84 ) => lt == rt && l == r,
85 (Self::ExtraToken { token: l, .. }, Self::ExtraToken { token: r, .. }) => l == r,
86 (Self::Failed, Self::Failed) => true,
87 _ => false,
88 }
89 }
90}
91impl From<lalrpop_util::ParseError<SourceIndex, Token, ParseError>> for ParseError {
92 fn from(err: lalrpop_util::ParseError<SourceIndex, Token, ParseError>) -> Self {
93 use lalrpop_util::ParseError as LError;
94
95 match err {
96 LError::InvalidToken { location } => Self::InvalidToken(location),
97 LError::UnrecognizedEof {
98 location: at,
99 expected,
100 } => Self::UnexpectedEof { at, expected },
101 LError::UnrecognizedToken {
102 token: (l, token, r),
103 expected,
104 } => Self::UnrecognizedToken {
105 span: SourceSpan::new(l, r),
106 token,
107 expected,
108 },
109 LError::ExtraToken {
110 token: (l, token, r),
111 } => Self::ExtraToken {
112 span: SourceSpan::new(l, r),
113 token,
114 },
115 LError::User { error } => error,
116 }
117 }
118}
119impl ToDiagnostic for ParseError {
120 fn to_diagnostic(self) -> Diagnostic {
121 match self {
122 Self::Lexer(err) => err.to_diagnostic(),
123 Self::Analysis(err) => err.to_diagnostic(),
124 Self::InvalidToken(start) => Diagnostic::error()
125 .with_message("invalid token")
126 .with_labels(vec![Label::primary(
127 start.source_id(),
128 SourceSpan::new(start, start),
129 )]),
130 Self::UnexpectedEof { at, ref expected } => {
131 let mut message = "expected one of: ".to_string();
132 for (i, t) in expected.iter().enumerate() {
133 if i == 0 {
134 message.push_str(&format!("'{t}'"));
135 } else {
136 message.push_str(&format!(", '{t}'"));
137 }
138 }
139
140 Diagnostic::error()
141 .with_message("unexpected eof")
142 .with_labels(vec![
143 Label::primary(at.source_id(), SourceSpan::new(at, at))
144 .with_message(message),
145 ])
146 }
147 Self::UnrecognizedToken {
148 span, ref expected, ..
149 } => {
150 let mut message = "expected one of: ".to_string();
151 for (i, t) in expected.iter().enumerate() {
152 if i == 0 {
153 message.push_str(&format!("'{t}'"));
154 } else {
155 message.push_str(&format!(", '{t}'"));
156 }
157 }
158
159 Diagnostic::error()
160 .with_message("unexpected token")
161 .with_labels(vec![
162 Label::primary(span.source_id(), span).with_message(message),
163 ])
164 }
165 Self::ExtraToken { span, .. } => Diagnostic::error()
166 .with_message("extraneous token")
167 .with_labels(vec![Label::primary(span.source_id(), span)]),
168 err => Diagnostic::error().with_message(err.to_string()),
169 }
170 }
171}
172
173impl miden_parsing::Parse for ast::Source {
174 type Parser = grammar::SourceParser;
175 type Error = ParseError;
176 type Config = ();
177 type Token = Lexed;
178
179 fn root_file_error(source: std::io::Error, path: std::path::PathBuf) -> Self::Error {
180 ParseError::FileError { source, path }
181 }
182
183 fn parse<S>(
184 parser: &Parser,
185 diagnostics: &DiagnosticsHandler,
186 source: S,
187 ) -> Result<Self, Self::Error>
188 where
189 S: Source,
190 {
191 let scanner = Scanner::new(source);
192 let lexer = Lexer::new(scanner);
193 Self::parse_tokens(diagnostics, parser.codemap.clone(), lexer)
194 }
195
196 fn parse_tokens<S: IntoIterator<Item = Lexed>>(
197 diagnostics: &DiagnosticsHandler,
198 codemap: Arc<CodeMap>,
199 tokens: S,
200 ) -> Result<Self, Self::Error> {
201 let mut next_var = 0;
202 let result = Self::Parser::new().parse(diagnostics, &codemap, &mut next_var, tokens);
203 match result {
204 Ok(ast) => {
205 if diagnostics.has_errors() {
206 return Err(ParseError::Failed);
207 }
208 Ok(ast)
209 }
210 Err(lalrpop_util::ParseError::User { error }) => Err(error),
211 Err(err) => Err(err.into()),
212 }
213 }
214}
215
216impl miden_parsing::Parse for ast::Program {
217 type Parser = grammar::ProgramParser;
218 type Error = ParseError;
219 type Config = ();
220 type Token = Lexed;
221
222 fn root_file_error(source: std::io::Error, path: std::path::PathBuf) -> Self::Error {
223 ParseError::FileError { source, path }
224 }
225
226 fn parse<S>(
227 parser: &Parser,
228 diagnostics: &DiagnosticsHandler,
229 source: S,
230 ) -> Result<Self, Self::Error>
231 where
232 S: Source,
233 {
234 let scanner = Scanner::new(source);
235 let lexer = Lexer::new(scanner);
236 Self::parse_tokens(diagnostics, parser.codemap.clone(), lexer)
237 }
238
239 fn parse_tokens<S: IntoIterator<Item = Lexed>>(
240 diagnostics: &DiagnosticsHandler,
241 codemap: Arc<CodeMap>,
242 tokens: S,
243 ) -> Result<Self, Self::Error> {
244 let mut next_var = 0;
245 let result = Self::Parser::new().parse(diagnostics, &codemap, &mut next_var, tokens);
246 match result {
247 Ok(ast) => {
248 if diagnostics.has_errors() {
249 return Err(ParseError::Failed);
250 }
251 Ok(ast)
252 }
253 Err(lalrpop_util::ParseError::User { error }) => Err(error),
254 Err(err) => Err(err.into()),
255 }
256 }
257}
258
259impl miden_parsing::Parse for ast::Module {
260 type Parser = grammar::AnyModuleParser;
261 type Error = ParseError;
262 type Config = ();
263 type Token = Lexed;
264
265 fn root_file_error(source: std::io::Error, path: std::path::PathBuf) -> Self::Error {
266 ParseError::FileError { source, path }
267 }
268
269 fn parse<S>(
270 parser: &Parser,
271 diagnostics: &DiagnosticsHandler,
272 source: S,
273 ) -> Result<Self, Self::Error>
274 where
275 S: Source,
276 {
277 let scanner = Scanner::new(source);
278 let lexer = Lexer::new(scanner);
279 Self::parse_tokens(diagnostics, parser.codemap.clone(), lexer)
280 }
281
282 fn parse_tokens<S: IntoIterator<Item = Lexed>>(
283 diagnostics: &DiagnosticsHandler,
284 codemap: Arc<CodeMap>,
285 tokens: S,
286 ) -> Result<Self, Self::Error> {
287 let mut next_var = 0;
288 let result = Self::Parser::new().parse(diagnostics, &codemap, &mut next_var, tokens);
289 match result {
290 Ok(ast) => {
291 if diagnostics.has_errors() {
292 return Err(ParseError::Failed);
293 }
294 Ok(ast)
295 }
296 Err(lalrpop_util::ParseError::User { error }) => Err(error),
297 Err(err) => Err(err.into()),
298 }
299 }
300}
301
302#[cfg(test)]
303mod tests;