mago_syntax/parser/
mod.rs1use mago_allocator::prelude::*;
2
3use mago_database::file::File;
4use mago_database::file::FileId;
5use mago_database::file::HasFileId;
6use mago_syntax_core::input::Input;
7
8use crate::cst::Program;
9use crate::cst::sequence::Sequence;
10use crate::error::ParseError;
11use crate::lexer::Lexer;
12use crate::parser::stream::TokenStream;
13use crate::settings::ParserSettings;
14
15mod internal;
16
17pub mod stream;
18
19const MAX_RECURSION_DEPTH: u16 = 512;
22
23#[derive(Debug, Default)]
24pub struct State {
25 pub within_string_interpolation: bool,
26 pub recursion_depth: u16,
27}
28
29#[derive(Debug)]
33#[allow(clippy::field_scoped_visibility_modifiers)]
34pub struct Parser<'input, 'arena, A>
35where
36 'input: 'arena,
37 A: Arena,
38{
39 pub(crate) arena: &'arena A,
40 pub(crate) state: State,
41 pub(crate) stream: TokenStream<'input, 'arena, A>,
42 pub(crate) errors: Vec<'arena, ParseError, A>,
43}
44
45impl<'input, 'arena, A> Parser<'input, 'arena, A>
46where
47 A: Arena,
48{
49 #[inline]
62 pub fn new(arena: &'arena A, file_id: FileId, content: &'input [u8], settings: ParserSettings) -> Self {
63 let input = Input::new(file_id, content);
64 let lexer = Lexer::new(input, settings.lexer);
65 let stream = TokenStream::new(arena, lexer);
66
67 Self { arena, state: State::default(), stream, errors: Vec::new_in(arena) }
68 }
69
70 pub fn for_file(arena: &'arena A, file: &'input File, settings: ParserSettings) -> Self {
82 Self::new(arena, file.file_id(), file.contents.as_ref(), settings)
83 }
84
85 fn parse(mut self, source_text: &'arena [u8], file_id: FileId) -> &'arena Program<'arena> {
87 let mut statements = Vec::new_in(self.arena);
88
89 loop {
90 let reached_eof = match self.stream.has_reached_eof() {
91 Ok(eof) => eof,
92 Err(err) => {
93 self.errors.push(ParseError::from(err));
94 break;
95 }
96 };
97
98 if reached_eof {
99 break;
100 }
101
102 let position_before = self.stream.current_position();
104
105 match self.parse_statement() {
106 Ok(statement) => statements.push(statement),
107 Err(err) => self.errors.push(err),
108 }
109
110 let position_after = self.stream.current_position();
114 if position_after == position_before
115 && let Ok(Some(token)) = self.stream.lookahead(0)
116 {
117 self.errors.push(self.stream.unexpected(Some(token), &[]));
118 let _ = self.stream.consume();
119 }
120 }
121
122 self.arena.alloc(Program {
123 file_id,
124 source_text,
125 statements: Sequence::new(statements),
126 trivia: self.stream.get_trivia(),
127 errors: self.errors.leak(),
128 })
129 }
130}
131
132#[inline]
143pub fn parse_file<'arena, A>(arena: &'arena A, file: &File) -> &'arena Program<'arena>
144where
145 A: Arena,
146{
147 parse_file_content(arena, file.file_id(), file.contents.as_ref())
148}
149
150#[inline]
162pub fn parse_file_with_settings<'arena, A>(
163 arena: &'arena A,
164 file: &File,
165 settings: ParserSettings,
166) -> &'arena Program<'arena>
167where
168 A: Arena,
169{
170 parse_file_content_with_settings(arena, file.file_id(), file.contents.as_ref(), settings)
171}
172
173pub fn parse_file_content<'arena, A>(arena: &'arena A, file_id: FileId, content: &[u8]) -> &'arena Program<'arena>
185where
186 A: Arena,
187{
188 let source_text = arena.alloc_slice_copy(content);
189 Parser::new(arena, file_id, source_text, ParserSettings::default()).parse(source_text, file_id)
190}
191
192pub fn parse_file_content_with_settings<'arena, A>(
205 arena: &'arena A,
206 file_id: FileId,
207 content: &[u8],
208 settings: ParserSettings,
209) -> &'arena Program<'arena>
210where
211 A: Arena,
212{
213 let source_text = arena.alloc_slice_copy(content);
214 Parser::new(arena, file_id, source_text, settings).parse(source_text, file_id)
215}