biome_parser/parsed_syntax.rs
1use crate::parse_recovery::{ParseRecovery, ParseRecoveryTokenSet, RecoveryResult};
2use crate::parsed_syntax::ParsedSyntax::{Absent, Present};
3use crate::prelude::*;
4use biome_rowan::TextRange;
5
6/// Syntax that is either present in the source tree or absent.
7///
8/// This type is commonly used as the return type of parse functions with the following types
9///
10///
11/// ## Parse Rule conventions
12///
13/// * A parse rule must return [ParsedSyntax::Present] if it is able to parse a node or at least parts of it. For example,
14/// the `parse_for_statement` should return [ParsedSyntax::Present] for `for (` even tough many of the required children are missing
15/// because it is still able to parse parts of the for statement.
16/// * A parse rule must return [ParsedSyntax::Absent] if the expected node isn't present in the source code.
17/// In most cases, this means if the first expected token isn't present, for example,
18/// if the `for` keyword isn't present when parsing a for statement.
19///
20/// However, it can be possible for rules to recover even if the first token doesn't match. One example
21/// is when parsing an assignment target that has an optional default. The rule can recover even
22/// if the assignment target is missing as long as the cursor is then positioned at an `=` token.
23///
24/// The rule must then return [ParsedSyntax::Present] with the partial parsed node.
25/// * A parse rule must not eat any tokens when it returns [ParsedSyntax::Absent]
26/// * A parse rule must not add any errors when it returns [ParsedSyntax::Absent]
27///
28/// This is a custom enum over using `Option` because [ParsedSyntax::Absent] values must be handled by the caller.
29#[derive(Debug, PartialEq, Eq)]
30#[must_use = "this `ParsedSyntax` may be an `Absent` variant, which should be handled"]
31pub enum ParsedSyntax {
32 /// A syntax that isn't present in the source code. Used when a parse rule can't match the current
33 /// token of the parser.
34 Absent,
35
36 /// A completed syntax node with all or some of its children.
37 Present(CompletedMarker),
38}
39
40impl ParsedSyntax {
41 /// Converts from `ParsedSyntax` to `Option<CompletedMarker>`.
42 ///
43 /// Converts `self` into an `Option<CompletedMarker>`, consuming `self`
44 #[inline]
45 pub fn ok(self) -> Option<CompletedMarker> {
46 match self {
47 Absent => None,
48 Present(marker) => Some(marker),
49 }
50 }
51
52 /// Calls `op` if the syntax is present and otherwise returns [ParsedSyntax::Absent]
53 #[inline]
54 pub fn and_then<F>(self, op: F) -> ParsedSyntax
55 where
56 F: FnOnce(CompletedMarker) -> ParsedSyntax,
57 {
58 match self {
59 Absent => Absent,
60 Present(marker) => op(marker),
61 }
62 }
63
64 /// Calls `op` if the syntax is absent and otherwise returns [ParsedSyntax::Present]
65 #[inline]
66 pub fn or_else<F>(self, op: F) -> ParsedSyntax
67 where
68 F: FnOnce() -> ParsedSyntax,
69 {
70 match self {
71 Absent => op(),
72 t => t,
73 }
74 }
75
76 /// Returns `true` if the parsed syntax is [ParsedSyntax::Present]
77 #[inline]
78 #[must_use]
79 pub fn is_present(&self) -> bool {
80 matches!(self, Present(_))
81 }
82
83 /// Returns `true` if the parsed syntax is [ParsedSyntax::Absent]
84 #[inline]
85 #[must_use]
86 pub fn is_absent(&self) -> bool {
87 matches!(self, Absent)
88 }
89
90 /// It returns the contained [ParsedSyntax::Present] value, consuming the `self` value
91 ///
92 /// # Panics
93 ///
94 /// Panics if the current syntax is [ParsedSyntax::Absent]
95 #[inline]
96 #[track_caller]
97 pub fn unwrap(self) -> CompletedMarker {
98 match self {
99 Absent => {
100 panic!("Called `unwrap` on an `Absent` syntax");
101 }
102 Present(marker) => marker,
103 }
104 }
105
106 /// Returns the contained [ParsedSyntax::Present] value or passed default
107 #[allow(unused)]
108 #[inline]
109 pub fn unwrap_or(self, default: CompletedMarker) -> CompletedMarker {
110 match self {
111 Absent => default,
112 Present(marker) => marker,
113 }
114 }
115
116 /// Returns the contained [ParsedSyntax::Present] value or computes it from a clojure.
117 #[inline]
118 #[allow(unused)]
119 pub fn unwrap_or_else<F>(self, default: F) -> CompletedMarker
120 where
121 F: FnOnce() -> CompletedMarker,
122 {
123 match self {
124 Absent => default(),
125 Present(marker) => marker,
126 }
127 }
128
129 /// Returns the contained [ParsedSyntax::Present] value, consuming the self value.
130 ///
131 /// # Panics
132 ///
133 /// Panics if the value is an [ParsedSyntax::Absent] with a custom panic message provided by msg.
134 #[inline]
135 #[track_caller]
136 pub fn expect(self, msg: &str) -> CompletedMarker {
137 match self {
138 Present(marker) => marker,
139 Absent => panic!("{}", msg),
140 }
141 }
142
143 /// Maps a [ParsedSyntax::Present] `ParsedSyntax` by applying a function to a contained [ParsedSyntax::Present] value,
144 /// leaving an [ParsedSyntax::Absent] value untouched.
145 ///
146 /// This function can be used to compose the results of two functions.
147 pub fn map<F>(self, mapper: F) -> ParsedSyntax
148 where
149 F: FnOnce(CompletedMarker) -> CompletedMarker,
150 {
151 match self {
152 Absent => Absent,
153 Present(element) => Present(mapper(element)),
154 }
155 }
156
157 /// Returns the kind of the syntax if it is present or [None] otherwise
158 #[inline]
159 pub fn kind<P>(&self, p: &P) -> Option<P::Kind>
160 where
161 P: Parser,
162 {
163 match self {
164 Absent => None,
165 Present(marker) => Some(marker.kind(p)),
166 }
167 }
168
169 /// Adds a diagnostic at the current parser position if the syntax is present and return its marker.
170 #[inline]
171 pub fn add_diagnostic_if_present<P, E, D>(
172 self,
173 p: &mut P,
174 error_builder: E,
175 ) -> Option<CompletedMarker>
176 where
177 P: Parser,
178 E: FnOnce(&P, TextRange) -> D,
179 D: ToDiagnostic<P>,
180 {
181 match self {
182 Present(syntax) => {
183 let range = syntax.range(p);
184 let range = TextRange::new(range.start(), range.end());
185 let diagnostic = error_builder(p, range);
186 p.error(diagnostic);
187 Some(syntax)
188 }
189 Absent => None,
190 }
191 }
192
193 /// It returns the syntax if present or adds a diagnostic at the current parser position.
194 #[inline]
195 pub fn or_add_diagnostic<P, E, D>(self, p: &mut P, error_builder: E) -> Option<CompletedMarker>
196 where
197 P: Parser,
198 E: FnOnce(&P, TextRange) -> D,
199 D: ToDiagnostic<P>,
200 {
201 match self {
202 Present(syntax) => Some(syntax),
203 Absent => {
204 let diagnostic = error_builder(p, p.cur_range());
205 p.error(diagnostic);
206 None
207 }
208 }
209 }
210
211 /// It creates and returns a marker preceding this parsed syntax if it is present or starts
212 /// a new marker and adds an error to the current parser position.
213 /// See [CompletedMarker.precede]
214 #[inline]
215 pub fn precede_or_add_diagnostic<P, E, D>(self, p: &mut P, error_builder: E) -> Marker
216 where
217 P: Parser,
218 E: FnOnce(&P, TextRange) -> D,
219 D: ToDiagnostic<P>,
220 {
221 match self {
222 Present(completed) => completed.precede(p),
223 Absent => {
224 let diagnostic = error_builder(p, p.cur_range());
225 p.error(diagnostic);
226 p.start()
227 }
228 }
229 }
230
231 /// Creates a new marker that precedes this syntax or starts a new marker
232 #[inline]
233 pub fn precede<P>(self, p: &mut P) -> Marker
234 where
235 P: Parser,
236 {
237 match self {
238 Present(marker) => marker.precede(p),
239 Absent => p.start(),
240 }
241 }
242
243 /// Returns this Syntax if it is present in the source text or tries to recover the
244 /// parser if the syntax is absent. The recovery...
245 ///
246 /// * eats all unexpected tokens into a `Bogus*` node until the parser reaches one
247 /// of the "safe tokens" configured in the [ParseRecoveryTokenSet].
248 /// * creates an error using the passed in error builder and adds it to the parsing diagnostics.
249 ///
250 /// The error recovery can fail if the parser is located at the EOF token or if the parser
251 /// is already at a valid position according to the [ParseRecoveryTokenSet].
252 pub fn or_recover_with_token_set<P, E>(
253 self,
254 p: &mut P,
255 recovery: &ParseRecoveryTokenSet<P::Kind>,
256 error_builder: E,
257 ) -> RecoveryResult
258 where
259 P: Parser,
260 E: FnOnce(&P, TextRange) -> ParseDiagnostic,
261 {
262 match self {
263 Present(syntax) => Ok(syntax),
264 Absent => match recovery.recover(p) {
265 Ok(recovered) => {
266 let diagnostic = error_builder(p, recovered.range(p));
267 p.error(diagnostic);
268 Ok(recovered)
269 }
270
271 Err(recovery_error) => {
272 let diagnostic = error_builder(p, p.cur_range());
273 p.error(diagnostic);
274 Err(recovery_error)
275 }
276 },
277 }
278 }
279
280 /// Returns this Syntax if it is present in the source text or tries to recover the
281 /// parser if the syntax is absent. The recovery...
282 ///
283 /// * eats all unexpected tokens into a `Bogus*` node until the parser reaches one
284 /// of the "safe tokens" configured in the [ParseRecovery].
285 /// * creates an error using the passed in error builder and adds it to the parsing diagnostics.
286 ///
287 /// The error recovery can fail if the parser is located at the EOF token or if the parser
288 /// is already at a valid position according to the [ParseRecovery].
289 pub fn or_recover<'source, P, E, R>(
290 self,
291 p: &mut P,
292 recovery: &R,
293 error_builder: E,
294 ) -> RecoveryResult
295 where
296 P: Parser,
297 R: ParseRecovery<Parser<'source> = P>,
298 E: FnOnce(&P, TextRange) -> ParseDiagnostic,
299 {
300 match self {
301 Present(syntax) => Ok(syntax),
302 Absent => match recovery.recover(p) {
303 Ok(recovered) => {
304 let diagnostic = error_builder(p, recovered.range(p));
305 p.error(diagnostic);
306 Ok(recovered)
307 }
308
309 Err(recovery_error) => {
310 let diagnostic = error_builder(p, p.cur_range());
311 p.error(diagnostic);
312 Err(recovery_error)
313 }
314 },
315 }
316 }
317}
318
319impl From<CompletedMarker> for ParsedSyntax {
320 fn from(marker: CompletedMarker) -> Self {
321 Present(marker)
322 }
323}
324
325impl From<Option<CompletedMarker>> for ParsedSyntax {
326 fn from(option: Option<CompletedMarker>) -> Self {
327 match option {
328 Some(completed) => Present(completed),
329 None => Absent,
330 }
331 }
332}