lady_deirdre/syntax/
immutable.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::{
36    fmt::{Debug, Formatter},
37    iter::FusedIterator,
38    marker::PhantomData,
39    ops::Range,
40};
41
42use crate::{
43    arena::{Entry, EntryIndex, Id, Identifiable, SubId},
44    lexis::TokenCursor,
45    report::system_panic,
46    syntax::{
47        observer::ObservableSyntaxSession,
48        session::ImmutableSyntaxSession,
49        ErrorRef,
50        Node,
51        NodeRef,
52        Observer,
53        SyntaxError,
54        SyntaxSession,
55        SyntaxTree,
56        ROOT_RULE,
57    },
58};
59
60/// A canonical implementation of the [SyntaxTree] trait.
61///
62/// Parses a syntax grammar only and does not provides any reparse
63/// capabilities.
64///
65/// Use this interface when you need to build a syntax tree from already
66/// existing stream of tokens or a part of it.
67pub struct ImmutableSyntaxTree<N: Node> {
68    id: SubId,
69    nodes: Vec<Option<N>>,
70    errors: Vec<SyntaxError>,
71}
72
73impl<N: Node> PartialEq for ImmutableSyntaxTree<N> {
74    #[inline(always)]
75    fn eq(&self, other: &Self) -> bool {
76        self.id.eq(&other.id)
77    }
78}
79
80impl<N: Node> Eq for ImmutableSyntaxTree<N> {}
81
82impl<N: Node> Debug for ImmutableSyntaxTree<N> {
83    #[inline]
84    fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
85        formatter
86            .debug_struct("SyntaxTree")
87            .field("id", &self.id())
88            .finish_non_exhaustive()
89    }
90}
91
92impl<N: Node> Identifiable for ImmutableSyntaxTree<N> {
93    #[inline(always)]
94    fn id(&self) -> Id {
95        self.id.id()
96    }
97}
98
99impl<N: Node> SyntaxTree for ImmutableSyntaxTree<N> {
100    type Node = N;
101
102    type NodeIterator<'tree> = NodeIter;
103
104    type ErrorIterator<'tree> = ErrorIter;
105
106    #[inline(always)]
107    fn root_node_ref(&self) -> NodeRef {
108        #[cfg(debug_assertions)]
109        if self.nodes.is_empty() {
110            system_panic!("Empty syntax tree.");
111        }
112
113        NodeRef {
114            id: self.id.id(),
115            entry: Entry {
116                index: 0,
117                version: 0,
118            },
119        }
120    }
121
122    #[inline(always)]
123    fn node_refs(&self) -> Self::NodeIterator<'_> {
124        NodeIter {
125            id: self.id.id(),
126            inner: 0..self.nodes.len(),
127        }
128    }
129
130    #[inline(always)]
131    fn error_refs(&self) -> Self::ErrorIterator<'_> {
132        ErrorIter {
133            id: self.id.id(),
134            inner: 0..self.errors.len(),
135        }
136    }
137
138    #[inline(always)]
139    fn has_node(&self, entry: &Entry) -> bool {
140        if entry.version > 0 {
141            return false;
142        }
143
144        entry.index < self.nodes.len()
145    }
146
147    #[inline(always)]
148    fn get_node(&self, entry: &Entry) -> Option<&Self::Node> {
149        if entry.version > 0 {
150            return None;
151        }
152
153        self.nodes.get(entry.index)?.as_ref()
154    }
155
156    #[inline(always)]
157    fn get_node_mut(&mut self, entry: &Entry) -> Option<&mut Self::Node> {
158        if entry.version > 0 {
159            return None;
160        }
161
162        self.nodes.get_mut(entry.index)?.as_mut()
163    }
164
165    #[inline(always)]
166    fn has_error(&self, entry: &Entry) -> bool {
167        if entry.version > 0 {
168            return false;
169        }
170
171        entry.index < self.errors.len()
172    }
173
174    #[inline(always)]
175    fn get_error(&self, entry: &Entry) -> Option<&SyntaxError> {
176        if entry.version > 0 {
177            return None;
178        }
179
180        self.errors.get(entry.index)
181    }
182}
183
184impl<N: Node> ImmutableSyntaxTree<N> {
185    /// An ImmutableSyntaxTree constructor.
186    ///
187    /// The `token_cursor` parameter of type [TokenCursor] provides access
188    /// to the token stream that needs to be parsed.
189    ///
190    /// You can get this cursor from a [TokenBuffer](crate::lexis::TokenBuffer),
191    /// [TokenStream](crate::lexis::TokenStream), or any compilation unit type
192    /// (e.g., [Document](crate::units::Document)).
193    ///
194    /// See [SourceCode::cursor](crate::lexis::SourceCode::cursor) function for
195    /// details.
196    #[inline(always)]
197    pub fn parse<'code>(token_cursor: impl TokenCursor<'code, Token = <N as Node>::Token>) -> Self {
198        Self::parse_with_id(SubId::new(), token_cursor)
199    }
200
201    /// An extended [constructor](Self::parse), where the additional parameter
202    /// `observer` specifies an [Observer] object into which the syntax parser
203    /// will report parsing steps.
204    ///
205    /// One use case of the observer is to debug your parser. In particular,
206    /// the [DebugObserver](crate::syntax::DebugObserver) prints each parsing
207    /// step performed by the syntax parser to the stdout.
208    #[inline(always)]
209    pub fn parse_with_observer<'code>(
210        token_cursor: impl TokenCursor<'code, Token = <N as Node>::Token>,
211        observer: &mut impl Observer<Node = N>,
212    ) -> Self {
213        Self::parse_with_id_and_observer(SubId::new(), token_cursor, observer)
214    }
215
216    pub(crate) fn parse_with_id<'code, 'observer>(
217        id: SubId,
218        token_cursor: impl TokenCursor<'code, Token = <N as Node>::Token>,
219    ) -> Self {
220        let mut session = ImmutableSyntaxSession {
221            id: id.id(),
222            context: Vec::new(),
223            nodes: Vec::new(),
224            errors: Vec::new(),
225            failing: false,
226            token_cursor,
227            _phantom: PhantomData,
228        };
229
230        let _ = session.descend(ROOT_RULE);
231
232        Self {
233            id,
234            nodes: session.nodes,
235            errors: session.errors,
236        }
237    }
238
239    pub(crate) fn parse_with_id_and_observer<'code, 'observer>(
240        id: SubId,
241        token_cursor: impl TokenCursor<'code, Token = <N as Node>::Token>,
242        observer: &'observer mut impl Observer<Node = N>,
243    ) -> Self {
244        let mut session = ObservableSyntaxSession {
245            id: id.id(),
246            context: Vec::new(),
247            nodes: Vec::new(),
248            errors: Vec::new(),
249            failing: false,
250            token_cursor,
251            observer,
252            _phantom: PhantomData,
253        };
254
255        let _ = session.descend(ROOT_RULE);
256
257        Self {
258            id,
259            nodes: session.nodes,
260            errors: session.errors,
261        }
262    }
263}
264
265pub struct NodeIter {
266    id: Id,
267    inner: Range<EntryIndex>,
268}
269
270impl Iterator for NodeIter {
271    type Item = NodeRef;
272
273    #[inline(always)]
274    fn next(&mut self) -> Option<Self::Item> {
275        let index = self.inner.next()?;
276
277        Some(NodeRef {
278            id: self.id,
279            entry: Entry { index, version: 0 },
280        })
281    }
282}
283
284impl FusedIterator for NodeIter {}
285
286pub struct ErrorIter {
287    id: Id,
288    inner: Range<EntryIndex>,
289}
290
291impl Iterator for ErrorIter {
292    type Item = ErrorRef;
293
294    #[inline(always)]
295    fn next(&mut self) -> Option<Self::Item> {
296        let index = self.inner.next()?;
297
298        Some(ErrorRef {
299            id: self.id,
300            entry: Entry { index, version: 0 },
301        })
302    }
303}
304
305impl FusedIterator for ErrorIter {}