mago_syntax/parser/
mod.rs1use bumpalo::Bump;
2use bumpalo::collections::Vec;
3
4use mago_database::file::File;
5use mago_database::file::FileId;
6use mago_database::file::HasFileId;
7use mago_syntax_core::input::Input;
8
9use crate::ast::Program;
10use crate::ast::sequence::Sequence;
11use crate::error::ParseError;
12use crate::lexer::Lexer;
13use crate::parser::stream::TokenStream;
14use crate::settings::ParserSettings;
15
16mod internal;
17
18pub mod stream;
19
20const MAX_RECURSION_DEPTH: u16 = 512;
23
24#[derive(Debug, Default)]
25pub struct State {
26 pub within_indirect_variable: bool,
27 pub within_string_interpolation: bool,
28 pub recursion_depth: u16,
29}
30
31#[derive(Debug)]
35pub struct Parser<'input, 'arena> {
36 pub(crate) arena: &'arena Bump,
37 pub(crate) state: State,
38 pub(crate) stream: TokenStream<'input, 'arena>,
39 pub(crate) errors: Vec<'arena, ParseError>,
40}
41
42impl<'input, 'arena> Parser<'input, 'arena> {
43 #[inline]
56 pub fn new(arena: &'arena Bump, file_id: FileId, content: &'input str, settings: ParserSettings) -> Self {
57 let input = Input::new(file_id, content.as_bytes());
58 let lexer = Lexer::new(input, settings.lexer);
59 let stream = TokenStream::new(arena, lexer);
60
61 Self { arena, state: State::default(), stream, errors: Vec::new_in(arena) }
62 }
63
64 pub fn for_file(arena: &'arena Bump, file: &'input File, settings: ParserSettings) -> Self {
76 Self::new(arena, file.file_id(), file.contents.as_ref(), settings)
77 }
78
79 fn parse(mut self, source_text: &'arena str, file_id: FileId) -> &'arena Program<'arena> {
81 let mut statements = Vec::new_in(self.arena);
82
83 loop {
84 let reached_eof = match self.stream.has_reached_eof() {
85 Ok(eof) => eof,
86 Err(err) => {
87 self.errors.push(ParseError::from(err));
88 break;
89 }
90 };
91
92 if reached_eof {
93 break;
94 }
95
96 let position_before = self.stream.current_position();
98
99 match self.parse_statement() {
100 Ok(statement) => statements.push(statement),
101 Err(err) => self.errors.push(err),
102 }
103
104 let position_after = self.stream.current_position();
108 if position_after == position_before
109 && let Ok(Some(token)) = self.stream.lookahead(0)
110 {
111 self.errors.push(self.stream.unexpected(Some(token), &[]));
112 let _ = self.stream.consume();
113 }
114 }
115
116 self.arena.alloc(Program {
117 file_id,
118 source_text,
119 statements: Sequence::new(statements),
120 trivia: self.stream.get_trivia(),
121 errors: self.errors,
122 })
123 }
124}
125
126#[inline]
137pub fn parse_file<'arena>(arena: &'arena Bump, file: &File) -> &'arena Program<'arena> {
138 parse_file_content(arena, file.file_id(), file.contents.as_ref())
139}
140
141#[inline]
153pub fn parse_file_with_settings<'arena>(
154 arena: &'arena Bump,
155 file: &File,
156 settings: ParserSettings,
157) -> &'arena Program<'arena> {
158 parse_file_content_with_settings(arena, file.file_id(), file.contents.as_ref(), settings)
159}
160
161pub fn parse_file_content<'arena>(arena: &'arena Bump, file_id: FileId, content: &str) -> &'arena Program<'arena> {
173 let source_text = arena.alloc_str(content);
174 Parser::new(arena, file_id, source_text, ParserSettings::default()).parse(source_text, file_id)
175}
176
177pub fn parse_file_content_with_settings<'arena>(
190 arena: &'arena Bump,
191 file_id: FileId,
192 content: &str,
193 settings: ParserSettings,
194) -> &'arena Program<'arena> {
195 let source_text = arena.alloc_str(content);
196 Parser::new(arena, file_id, source_text, settings).parse(source_text, file_id)
197}