air_parser/parser/
mod.rs

1// Simple macro used in the grammar definition for constructing spans
2macro_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;