lady_deirdre/syntax/session.rs
1////////////////////////////////////////////////////////////////////////////////
2// This file is part of "Lady Deirdre", a compiler front-end foundation //
3// technology. //
4// //
5// This work is proprietary software with source-available code. //
6// //
7// To copy, use, distribute, or contribute to this work, you must agree to //
8// the terms of the General License Agreement: //
9// //
10// https://github.com/Eliah-Lakhin/lady-deirdre/blob/master/EULA.md //
11// //
12// The agreement grants a Basic Commercial License, allowing you to use //
13// this work in non-commercial and limited commercial products with a total //
14// gross revenue cap. To remove this commercial limit for one of your //
15// products, you must acquire a Full Commercial License. //
16// //
17// If you contribute to the source code, documentation, or related materials, //
18// you must grant me an exclusive license to these contributions. //
19// Contributions are governed by the "Contributions" section of the General //
20// License Agreement. //
21// //
22// Copying the work in parts is strictly forbidden, except as permitted //
23// under the General License Agreement. //
24// //
25// If you do not or cannot agree to the terms of this Agreement, //
26// do not use this work. //
27// //
28// This work is provided "as is", without any warranties, express or implied, //
29// except where such disclaimers are legally invalid. //
30// //
31// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин). //
32// All rights reserved. //
33////////////////////////////////////////////////////////////////////////////////
34
35use std::{marker::PhantomData, mem::replace};
36
37use crate::{
38 arena::{Entry, EntryIndex, Id, Identifiable},
39 lexis::{Length, Site, SiteRef, TokenCount, TokenCursor, TokenRef},
40 report::ld_unreachable,
41 syntax::{ErrorRef, Node, NodeRef, NodeRule, SyntaxError},
42};
43
44/// A communication channel of the syntax tree parsing process.
45///
46/// Lady Deirdre distinguishes two independent sides of the syntax parsing process:
47///
48/// - The parsing environment side (e.g., [Document](crate::units::Document))
49/// that manages the parsing input (tokens), and manages the parsing output
50/// (syntax tree).
51///
52/// - The parsing algorithm implementation side (via the [Node::parse] function).
53///
54/// Both sides unaware of each other, and they use SyntaxSession
55/// for intercommunication.
56///
57/// 1. Whenever the parsing environment side wants to parse a particular node
58/// of the syntax tree, it gives control flow to the parser by providing
59/// it's own implementation of the SyntaxSession to the [Node::parse]
60/// function.
61///
62/// 2. The parse function reads particular tokens of the token input stream
63/// and advances the token cursor using SyntaxSession's functions.
64///
65/// 3. If the parse algorithm needs to further descend into the syntax tree,
66/// it calls [SyntaxSession::descend] function returning control flow back
67/// to the parsing environment side.
68///
69/// 4. The parsing environment side decides whether to give control flow to
70/// the Node's parse function again, continuing recursive descending,
71/// or returning a result from its own cache.
72///
73/// Since both sides of the parsing process are isolated from each other,
74/// it opens up a lot of implementation configurations.
75///
76/// ## As the user of the SyntaxSession instances, you can
77///
78/// - Implement a custom parser that inherently suit the needs
79/// of both one-time parsing of the entire file and the incremental
80/// reparsing process.
81///
82/// - Adopt a 3rd party parsing library to Lady Deirdre by adopting
83/// its interface to the SyntaxSession interface.
84///
85/// - Implement a parsing algorithm of almost any programming
86/// language as long as its grammar does not surpass a class of
87/// the context free grammars with unlimited lookahead.
88///
89/// In particular, the [Node derive macro](lady_deirdre_derive::Node)
90/// implements Node's parse function for LL(1) grammars, but you are free to
91/// implement your own parsers and alternative recursive-descending parsing
92/// libraries utilizing the SyntaxSession interface.
93///
94/// ## As the author of the SyntaxSession implementations
95///
96/// You have control over arbitrary programming language parser steps
97/// without the need to know the details of the language's grammar.
98///
99/// Therefore, you can implement a wide set of the parsing environments with
100/// different capabilities.
101///
102/// In particular, Lady Deirdre provides the following parsing environments
103/// through custom implementations of the SyntaxSession trait:
104///
105/// - The immutable [Document](crate::units::Document) and
106/// the [ImmutableSyntaxTree](crate::syntax::ImmutableSyntaxTree) objects
107/// provide one-time parsing capabilities, always returning control-flow
108/// back to the Node's parse function when the function is trying to descend.
109/// The performance characteristics of this approach are close to ordinary
110/// non-incremental parsers.
111///
112/// - The mutable Document and the [MutableUnit](crate::units::MutableUnit)
113/// have their own reusable caches of the syntax tree nodes that they utilize
114/// during incremental reparsing by returning previously parsed node cache
115/// whenever possible when the Node's parse function is trying to descend.
116///
117/// - [Node::debug] function under the hood uses special implementation of the
118/// SyntaxSession trait that does not store any nodes in the syntax tree
119/// but instead just prints parser's actions to the terminal for debugging
120/// purposes.
121///
122/// - [ParseTree](crate::syntax::ParseTree) tracks parser's interactions
123/// with the SyntaxSession to reconstruct concrete parsing trees.
124///
125/// ## Parsing algorithm considerations
126///
127/// When implementing the [Node::parse] function, several considerations should
128/// be taken into account:
129///
130/// 1. The parsing algorithm should handle a **context-free grammar**.
131/// Each rule of the grammar should be parseable from any **arbitrary**
132/// token sequence, and the algorithm should not rely on parsing rules
133/// applied previously.
134///
135/// 2. The [Node::parse] function must be infallible regardless of the input
136/// token sequence or the `rule` parameter. If the token sequence provided
137/// to the parser by the SyntaxSession does not fit the parsing rule at any
138/// step (including the first step) of the rule’s algorithm, **the algorithm
139/// must attempt to recover from syntax errors** by skipping or ignoring
140/// parts of the input tokens and by reporting syntax errors using the
141/// [SyntaxSession::failure] function.
142///
143/// 3. In the end, the [Node::parse] function must advance the SyntaxSession
144/// token cursor by consuming **at least one token** from the input
145/// sequence, and it must return an instance of the [Node] that corresponds
146/// to the requested rule.
147///
148/// ## Parse rules
149///
150/// Both [Node::parse] and the [SyntaxSession::descend] functions have a
151/// `rule` parameter of the [NodeRule] type, which is an arbitrary number used
152/// to distinguish between concrete parsing rules.
153///
154/// In the context of parsing rules:
155///
156/// 1. Rule `0` ([ROOT_RULE](crate::syntax::ROOT_RULE)), denoting the root node
157/// of the syntax tree, should be used only once per the entire parsing
158/// process, as there can only be one root in the syntax tree.
159/// Therefore, the [Node::parse] function itself **should never call**
160/// [descend](SyntaxSession::descend) or [enter](SyntaxSession::enter)
161/// functions with the rule `0`.
162///
163/// 2. Rule `u16::MAX` ([NON_RULE](crate::syntax::NON_RULE)) is a reserved rule
164/// that denotes an invalid rule, indicating a rule that does not parse
165/// anything. Therefore, it should never be supplied to either
166/// the [Node::parse] function or any of the SyntaxSession functions that
167/// accept a NodeRule type.
168///
169/// 3. Any other rule number in a range of `1..u16::MAX` is a valid rule
170/// uniquely denotes a parsing rule of a particular syntax tree node.
171///
172/// 4. The SyntaxSession trait is unaware of the mapping between these numbers
173/// and the node types upfront. They are determined by each implementer of
174/// the programming language parsers. However, the SyntaxSession can rely on
175/// the fact that **passing the same rule number to the [Node::parse]
176/// function would produce a node of the same type**.
177///
178/// ## Cache control
179///
180/// The parser algorithm typically descends into the sub-rules of the currently
181/// parsed rule by calling the [SyntaxSession::descend] function, which gives
182/// control flow to the SyntaxSession and leaves the decision about the node’s
183/// caching to the SyntaxSession implementation.
184///
185/// The nodes computed this way are called _primary nodes_.
186///
187/// Alternatively, the algorithm could compute a sub-rule in place, without
188/// returning control flow back to the SyntaxSession.
189///
190/// These nodes are called _secondary nodes_. As the secondary nodes
191/// are computed in places, they cannot be cached by the SyntaxSession.
192///
193/// Whenever the parser starts in-place parsing of the secondary node it should
194/// call the [SyntaxSession::enter] function specifying the node's parsing rule.
195///
196/// When the the parsing finishes, the parser should call the
197/// [SyntaxSession::leave] function specifying an instance of the [Node] as
198/// a product of this sub-rule.
199///
200/// ## Nodes and rules nesting
201///
202/// The SyntaxSession tracks rules and their products ([Nodes](Node)) nesting.
203///
204/// It is the parser's implementor responsibility to balance the
205/// [enter](SyntaxSession::enter) and [leave](SyntaxSession::leave) functions
206/// properly when parsing the secondary nodes in place, always leaving the nodes
207/// entered before.
208///
209/// When the algorithm uses the [descend](SyntaxSession::descend) function,
210/// the nesting process is controlled by the SyntaxSession implementation.
211///
212/// [SyntaxSession::node_ref] returns a [NodeRef] of the node currently being
213/// parsed. Using this function, you can fetch the node's reference while it is
214/// being parsed.
215///
216/// [SyntaxSession::parent_ref] returns a NodeRef reference of the parent rule's
217/// node.
218///
219/// The parsing algorithm cannot access either of the syntax tree node
220/// instances during the parsing process, but it can use these NodeRef
221/// references to set up the [parent_ref](crate::syntax::AbstractNode::parent_ref)
222/// and the [node_ref](crate::syntax::AbstractNode::node_ref) values of
223/// the resulting node instance.
224///
225/// These values of the Node instance establish back references from the child
226/// nodes to their parents and are useful for the resulting syntax tree
227/// ascending traverse.
228///
229/// ## Left recursion
230///
231/// To handle left recursion, the parser could utilize either lookahead
232/// capabilities of the SyntaxSession or to use the node lifting feature.
233///
234/// For example, to parse infix expressions such as `a + b`, `a * b`, or
235/// just `a`, where the binary operator is not known upfront or could absent,
236/// you can descend into the operand parsing rule first, receiving its
237/// [NodeRef] reference.
238///
239/// Then, if the parser encounters an operator token, you can
240/// [enter](SyntaxSession::enter) the corresponding binary operation rule and
241/// immediately call the [lift](SyntaxSession::lift) function, providing
242/// the operand's NodeRef. Then, parse the rest of the expression normally.
243///
244/// The lift function would "transplant" the operand's node parsed outside of
245/// the operator's rule to the context of the operator's rule, rearranging
246/// operand's node nesting.
247///
248/// ## Tokens access
249///
250/// The SyntaxSession trait is a super-trait of the [TokenCursor] trait.
251///
252/// Functions of the TokenCursor grant access to the current state of the token
253/// sequence being parsed.
254///
255/// The [TokenCursor::advance] and the [TokenCursor::skip] functions advance
256/// the SyntaxSession parsing cursor, consuming corresponding tokens.
257///
258/// The access functions of the TokenCursor grant potentially unlimited
259/// lookahead capabilities.
260///
261/// The SyntaxSession could track the lookahead distance used by the parsing
262/// algorithm. The lookahead distance could limit the underlying parsing
263/// environment caching capabilities.
264///
265/// Therefore, the parser's implementor **should prefer to limit the lookahead**
266/// whenever possible.
267///
268/// ## Safety and Panic
269///
270/// The SyntaxSession trait and all of it's functions **are safe**.
271///
272/// Violations of the above specification **are not** undefined behavior, but
273/// the failure to follow the specified contract could lead to bugs and panics
274/// depending on the implementation.
275pub trait SyntaxSession<'code>: TokenCursor<'code, Token = <Self::Node as Node>::Token> {
276 /// Specifies a type of the Node that is currently being parsed.
277 type Node: Node;
278
279 /// Instructs the parsing environment to parse the parsing rule denoted by
280 /// the `rule` parameter starting from the current token.
281 ///
282 /// The valid values of the `rule` are any values within the [NodeRule]
283 /// range except the [ROOT_RULE](crate::syntax::ROOT_RULE) and
284 /// the [NON_RULE](crate::syntax::NON_RULE).
285 ///
286 /// Returns a [NodeRef] reference of the node inside the
287 /// [SyntaxTree](crate::syntax::SyntaxTree) that would be parsed by this
288 /// rule and consumes the sequence of tokens required to apply the rule.
289 fn descend(&mut self, rule: NodeRule) -> NodeRef;
290
291 /// Begins parsing of the parsing rule denoted by the `rule` parameter
292 /// from the current token.
293 ///
294 /// The valid values of the `rule` are any values within the [NodeRule]
295 /// range except the [ROOT_RULE](crate::syntax::ROOT_RULE) and
296 /// the [NON_RULE](crate::syntax::NON_RULE).
297 ///
298 /// Returns a [NodeRef] reference of the node inside the
299 /// [SyntaxTree](crate::syntax::SyntaxTree) that will be placed to
300 /// the syntax tree when the rule parsing finishes.
301 ///
302 /// Each enter function must be paired with
303 /// the [leave](SyntaxSession::leave) function that finises the rule.
304 ///
305 /// After entering into the rule parser, the parsing algorithm must consume
306 /// at least one token directly or indirectly by entering another sub-rule.
307 fn enter(&mut self, rule: NodeRule) -> NodeRef;
308
309 /// Completes parsing of the rule started previously by
310 /// the [enter](SyntaxSession::enter) function.
311 ///
312 /// The `node` parameter specifies rule's parsing result.
313 ///
314 /// Returns a [NodeRef] reference of the `node` inside
315 /// the [SyntaxTree](crate::syntax::SyntaxTree).
316 fn leave(&mut self, node: Self::Node) -> NodeRef;
317
318 /// Reinterprets the previously parsed sibling node of the current node
319 /// as the current node's child.
320 ///
321 /// See the [Left recursion](SyntaxSession#left-recursion) section of
322 /// the parsing process specification for details.
323 fn lift(&mut self, node_ref: &NodeRef);
324
325 /// Returns the [NodeRef] reference of the node inside
326 /// the [SyntaxTree](crate::syntax::SyntaxTree) being parsed by the current
327 /// parsing rule.
328 fn node_ref(&self) -> NodeRef;
329
330 /// Returns the [NodeRef] reference of the node inside
331 /// the [SyntaxTree](crate::syntax::SyntaxTree) being parsed by the parental
332 /// parsing rule.
333 ///
334 /// If the current rule is the root, this function returns [NodeRef::nil].
335 fn parent_ref(&self) -> NodeRef;
336
337 /// Reports a syntax error occur during the syntax recovery.
338 ///
339 /// Returns an [ErrorRef] reference of the error object inside
340 /// the [SyntaxTree](crate::syntax::SyntaxTree).
341 ///
342 /// The SyntaxSession implementation may decide to ignore the provided error
343 /// object. In this case, the failure function returns [ErrorRef::nil].
344 fn failure(&mut self, error: SyntaxError) -> ErrorRef;
345}
346
347pub(super) struct ImmutableSyntaxSession<
348 'code,
349 N: Node,
350 C: TokenCursor<'code, Token = <N as Node>::Token>,
351> {
352 pub(super) id: Id,
353 pub(super) context: Vec<EntryIndex>,
354 pub(super) nodes: Vec<Option<N>>,
355 pub(super) errors: Vec<SyntaxError>,
356 pub(super) failing: bool,
357 pub(super) token_cursor: C,
358 pub(super) _phantom: PhantomData<&'code ()>,
359}
360
361impl<'code, N, C> Identifiable for ImmutableSyntaxSession<'code, N, C>
362where
363 N: Node,
364 C: TokenCursor<'code, Token = <N as Node>::Token>,
365{
366 #[inline(always)]
367 fn id(&self) -> Id {
368 self.id
369 }
370}
371
372impl<'code, N, C> TokenCursor<'code> for ImmutableSyntaxSession<'code, N, C>
373where
374 N: Node,
375 C: TokenCursor<'code, Token = <N as Node>::Token>,
376{
377 type Token = <N as Node>::Token;
378
379 #[inline(always)]
380 fn advance(&mut self) -> bool {
381 let advanced = self.token_cursor.advance();
382
383 self.failing = self.failing && !advanced;
384
385 advanced
386 }
387
388 #[inline(always)]
389 fn skip(&mut self, distance: TokenCount) {
390 let start = self.token_cursor.site(0);
391
392 self.token_cursor.skip(distance);
393
394 self.failing = self.failing && start == self.token_cursor.site(0);
395 }
396
397 #[inline(always)]
398 fn token(&mut self, distance: TokenCount) -> Self::Token {
399 self.token_cursor.token(distance)
400 }
401
402 #[inline(always)]
403 fn site(&mut self, distance: TokenCount) -> Option<Site> {
404 self.token_cursor.site(distance)
405 }
406
407 #[inline(always)]
408 fn length(&mut self, distance: TokenCount) -> Option<Length> {
409 self.token_cursor.length(distance)
410 }
411
412 #[inline(always)]
413 fn string(&mut self, distance: TokenCount) -> Option<&'code str> {
414 self.token_cursor.string(distance)
415 }
416
417 #[inline(always)]
418 fn token_ref(&mut self, distance: TokenCount) -> TokenRef {
419 self.token_cursor.token_ref(distance)
420 }
421
422 #[inline(always)]
423 fn site_ref(&mut self, distance: TokenCount) -> SiteRef {
424 self.token_cursor.site_ref(distance)
425 }
426
427 #[inline(always)]
428 fn end_site_ref(&mut self) -> SiteRef {
429 self.token_cursor.end_site_ref()
430 }
431}
432
433impl<'code, N, C> SyntaxSession<'code> for ImmutableSyntaxSession<'code, N, C>
434where
435 N: Node,
436 C: TokenCursor<'code, Token = <N as Node>::Token>,
437{
438 type Node = N;
439
440 fn descend(&mut self, rule: NodeRule) -> NodeRef {
441 let _ = self.enter(rule);
442
443 let node = N::parse(self, rule);
444
445 self.leave(node)
446 }
447
448 #[inline]
449 fn enter(&mut self, _rule: NodeRule) -> NodeRef {
450 let index = self.nodes.len();
451
452 self.nodes.push(None);
453
454 self.context.push(index);
455
456 NodeRef {
457 id: self.id,
458 entry: Entry { index, version: 0 },
459 }
460 }
461
462 #[inline]
463 fn leave(&mut self, node: Self::Node) -> NodeRef {
464 let Some(index) = self.context.pop() else {
465 #[cfg(debug_assertions)]
466 {
467 panic!("Nesting imbalance.");
468 }
469
470 #[cfg(not(debug_assertions))]
471 {
472 return NodeRef::nil();
473 }
474 };
475
476 let Some(item) = self.nodes.get_mut(index) else {
477 unsafe { ld_unreachable!("Bad context index.") }
478 };
479
480 if replace(item, Some(node)).is_some() {
481 unsafe { ld_unreachable!("Bad context index.") }
482 }
483
484 NodeRef {
485 id: self.id,
486 entry: Entry { index, version: 0 },
487 }
488 }
489
490 #[inline]
491 fn lift(&mut self, node_ref: &NodeRef) {
492 if self.id != node_ref.id {
493 #[cfg(debug_assertions)]
494 {
495 panic!("Cannot lift a node that does not belong to this compilation session.");
496 }
497
498 #[cfg(not(debug_assertions))]
499 {
500 return;
501 }
502 }
503
504 let parent_ref = self.node_ref();
505
506 let Some(Some(node)) = self.nodes.get_mut(node_ref.entry.index) else {
507 #[cfg(debug_assertions)]
508 {
509 panic!("Cannot lift a node that does not belong to this compilation session.");
510 }
511
512 #[cfg(not(debug_assertions))]
513 {
514 return;
515 }
516 };
517
518 node.set_parent_ref(parent_ref);
519 }
520
521 #[inline(always)]
522 fn node_ref(&self) -> NodeRef {
523 let Some(index) = self.context.last() else {
524 #[cfg(debug_assertions)]
525 {
526 panic!("Nesting imbalance.");
527 }
528
529 #[cfg(not(debug_assertions))]
530 {
531 return NodeRef::nil();
532 }
533 };
534
535 NodeRef {
536 id: self.id,
537 entry: Entry {
538 index: *index,
539 version: 0,
540 },
541 }
542 }
543
544 #[inline(always)]
545 fn parent_ref(&self) -> NodeRef {
546 let Some(depth) = self.context.len().checked_sub(2) else {
547 return NodeRef::nil();
548 };
549
550 let index = *unsafe { self.context.get_unchecked(depth) };
551
552 NodeRef {
553 id: self.id,
554 entry: Entry { index, version: 0 },
555 }
556 }
557
558 #[inline(always)]
559 fn failure(&mut self, error: SyntaxError) -> ErrorRef {
560 if self.failing {
561 return ErrorRef::nil();
562 }
563
564 self.failing = true;
565
566 let index = self.errors.len();
567
568 self.errors.push(error);
569
570 ErrorRef {
571 id: self.id,
572 entry: Entry { index, version: 0 },
573 }
574 }
575}