Skip to main content

syntax_lang/
build.rs

1//! Tree construction: the parser-facing [`Builder`] and the [`BuildError`] it
2//! reports on misuse.
3
4use alloc::vec::Vec;
5use core::fmt;
6
7use span_lang::Span;
8use token_lang::Token;
9
10use crate::tree::{Element, Node};
11
12/// Why a [`Builder`] could not produce a tree.
13///
14/// The builder never panics on misuse; it records the first fault and surfaces it
15/// from [`Builder::finish`]. Each variant names a specific imbalance between the
16/// `start_node` / `token` / `finish_node` calls and what a caller should change.
17///
18/// # Examples
19///
20/// ```
21/// use syntax_lang::{BuildError, Builder};
22///
23/// // Closing a node that was never opened.
24/// let mut b = Builder::<&str>::new();
25/// b.finish_node();
26/// assert_eq!(b.finish(), Err(BuildError::UnbalancedFinish));
27/// ```
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum BuildError {
31    /// [`finish`](Builder::finish) was called before any node was started, so
32    /// there is no tree to return. Start a root node before finishing.
33    EmptyTree,
34    /// [`finish`](Builder::finish) was called while nodes were still open. Balance
35    /// every [`start_node`](Builder::start_node) with a
36    /// [`finish_node`](Builder::finish_node) before finishing.
37    UnclosedNodes,
38    /// [`finish_node`](Builder::finish_node) was called with no open node. Remove
39    /// the extra close, or add the missing [`start_node`](Builder::start_node).
40    UnbalancedFinish,
41    /// [`token`](Builder::token) was called before any node was started. A tree's
42    /// root must be a node; open one before pushing tokens.
43    TokenOutsideNode,
44    /// A second root node was started after the first root was already closed. A
45    /// tree has exactly one root; wrap the roots in an enclosing node instead.
46    MultipleRoots,
47}
48
49impl fmt::Display for BuildError {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        let message = match self {
52            BuildError::EmptyTree => "no node was started before finishing the tree",
53            BuildError::UnclosedNodes => "the tree was finished with nodes still open",
54            BuildError::UnbalancedFinish => "a node was closed with no open node to close",
55            BuildError::TokenOutsideNode => "a token was pushed before any node was started",
56            BuildError::MultipleRoots => "a second root node was started after the first closed",
57        };
58        f.write_str(message)
59    }
60}
61
62impl core::error::Error for BuildError {}
63
64/// A node whose kind is fixed but whose children are still being collected.
65struct Partial<K> {
66    kind: K,
67    children: Vec<Element<K>>,
68}
69
70/// Assembles a [`Node`] tree from a stream of `start_node` / `token` /
71/// `finish_node` calls — the shape a parser drives as it recognises the grammar.
72///
73/// The builder keeps a stack of open nodes. [`start_node`](Builder::start_node)
74/// opens one, [`token`](Builder::token) appends a leaf to whichever node is
75/// innermost, and [`finish_node`](Builder::finish_node) closes the innermost node
76/// and folds it into its parent. When the outermost node closes it becomes the
77/// root, and [`finish`](Builder::finish) hands it back.
78///
79/// Misuse is never a panic. The first structural fault — an unbalanced close, a
80/// token at the root, a second root — is remembered and returned from
81/// [`finish`](Builder::finish) as a [`BuildError`], so a parser can drive the
82/// builder on its hot path without wrapping every call in a `Result`.
83///
84/// # Examples
85///
86/// Build `(1)` — a parenthesised literal — and read it back losslessly:
87///
88/// ```
89/// use syntax_lang::{Builder, Span, Token};
90///
91/// let mut b = Builder::new();
92/// b.start_node("paren");
93/// b.token(Token::new("(", Span::new(0, 1)));
94/// b.start_node("lit");
95/// b.token(Token::new("num", Span::new(1, 2)));
96/// b.finish_node(); // close "lit"
97/// b.token(Token::new(")", Span::new(2, 3)));
98/// b.finish_node(); // close "paren"
99///
100/// let root = b.finish().expect("balanced");
101/// assert_eq!(root.kind(), &"paren");
102/// assert_eq!(root.span(), Span::new(0, 3));
103/// assert_eq!(root.text("(1)"), Some("(1)"));
104/// let kinds: Vec<_> = root.tokens().map(|t| *t.kind()).collect();
105/// assert_eq!(kinds, ["(", "num", ")"]);
106/// ```
107pub struct Builder<K> {
108    stack: Vec<Partial<K>>,
109    root: Option<Node<K>>,
110    cursor: u32,
111    error: Option<BuildError>,
112}
113
114impl<K> Builder<K> {
115    /// Creates an empty builder.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use syntax_lang::Builder;
121    ///
122    /// let b = Builder::<&str>::new();
123    /// assert!(b.is_empty());
124    /// ```
125    #[inline]
126    #[must_use]
127    pub const fn new() -> Self {
128        Self {
129            stack: Vec::new(),
130            root: None,
131            cursor: 0,
132            error: None,
133        }
134    }
135
136    /// Whether nothing has been built yet — no open nodes and no completed root.
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use syntax_lang::{Builder, Span, Token};
142    ///
143    /// let mut b = Builder::new();
144    /// assert!(b.is_empty());
145    /// b.start_node("root");
146    /// assert!(!b.is_empty());
147    /// b.token(Token::new("t", Span::new(0, 1)));
148    /// b.finish_node();
149    /// let _ = b.finish();
150    /// ```
151    #[inline]
152    #[must_use]
153    pub fn is_empty(&self) -> bool {
154        self.stack.is_empty() && self.root.is_none()
155    }
156
157    /// Opens a new node of `kind`. Tokens and child nodes added until the matching
158    /// [`finish_node`](Builder::finish_node) become its children.
159    ///
160    /// Starting a second node once the root has already closed records
161    /// [`BuildError::MultipleRoots`]; the call is otherwise infallible.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// use syntax_lang::{Builder, Span, Token};
167    ///
168    /// let mut b = Builder::new();
169    /// b.start_node("root");
170    /// b.token(Token::new("t", Span::new(0, 1)));
171    /// b.finish_node();
172    /// assert!(b.finish().is_ok());
173    /// ```
174    pub fn start_node(&mut self, kind: K) {
175        if self.error.is_some() {
176            return;
177        }
178        if self.root.is_some() && self.stack.is_empty() {
179            self.fail(BuildError::MultipleRoots);
180            return;
181        }
182        self.stack.push(Partial {
183            kind,
184            children: Vec::new(),
185        });
186    }
187
188    /// Appends `token` as a leaf of the innermost open node.
189    ///
190    /// Pushing a token with no node open records [`BuildError::TokenOutsideNode`],
191    /// since a tree's root must be a node rather than a bare token.
192    ///
193    /// # Examples
194    ///
195    /// ```
196    /// use syntax_lang::{BuildError, Builder, Span, Token};
197    ///
198    /// let mut b = Builder::new();
199    /// b.token(Token::new("t", Span::new(0, 1))); // no node open yet
200    /// assert_eq!(b.finish(), Err(BuildError::TokenOutsideNode));
201    /// ```
202    pub fn token(&mut self, token: Token<K>) {
203        if self.error.is_some() {
204            return;
205        }
206        match self.stack.last_mut() {
207            Some(top) => {
208                self.cursor = self.cursor.max(token.span().end().to_u32());
209                top.children.push(Element::Token(token));
210            }
211            None => self.fail(BuildError::TokenOutsideNode),
212        }
213    }
214
215    /// Closes the innermost open node, folding it into its parent — or promoting it
216    /// to the root when it is the outermost node.
217    ///
218    /// Closing with no node open records [`BuildError::UnbalancedFinish`].
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// use syntax_lang::{Builder, Span, Token};
224    ///
225    /// let mut b = Builder::new();
226    /// b.start_node("outer");
227    /// b.start_node("inner");
228    /// b.token(Token::new("t", Span::new(0, 1)));
229    /// b.finish_node(); // close "inner"
230    /// b.finish_node(); // close "outer"
231    /// let root = b.finish().expect("balanced");
232    /// assert_eq!(root.child_nodes().count(), 1);
233    /// ```
234    pub fn finish_node(&mut self) {
235        if self.error.is_some() {
236            return;
237        }
238        let Some(partial) = self.stack.pop() else {
239            self.fail(BuildError::UnbalancedFinish);
240            return;
241        };
242        let node = self.build(partial);
243        match self.stack.last_mut() {
244            Some(parent) => parent.children.push(Element::Node(node)),
245            None => {
246                if self.root.is_some() {
247                    self.fail(BuildError::MultipleRoots);
248                } else {
249                    self.root = Some(node);
250                }
251            }
252        }
253    }
254
255    /// Consumes the builder, returning the finished root node.
256    ///
257    /// Returns the first recorded [`BuildError`] if the call sequence was
258    /// unbalanced, [`BuildError::UnclosedNodes`] if nodes are still open, or
259    /// [`BuildError::EmptyTree`] if no node was ever started.
260    ///
261    /// # Examples
262    ///
263    /// ```
264    /// use syntax_lang::{BuildError, Builder};
265    ///
266    /// let b = Builder::<&str>::new();
267    /// assert_eq!(b.finish(), Err(BuildError::EmptyTree));
268    /// ```
269    pub fn finish(self) -> Result<Node<K>, BuildError> {
270        if let Some(error) = self.error {
271            return Err(error);
272        }
273        if !self.stack.is_empty() {
274            return Err(BuildError::UnclosedNodes);
275        }
276        self.root.ok_or(BuildError::EmptyTree)
277    }
278
279    /// Turns a completed [`Partial`] into a [`Node`], placing an empty node at the
280    /// current stream cursor.
281    fn build(&self, partial: Partial<K>) -> Node<K> {
282        if partial.children.is_empty() {
283            Node::with_span(partial.kind, partial.children, Span::empty(self.cursor))
284        } else {
285            Node::new(partial.kind, partial.children)
286        }
287    }
288
289    /// Records the first fault; later faults do not overwrite it.
290    fn fail(&mut self, error: BuildError) {
291        if self.error.is_none() {
292            self.error = Some(error);
293        }
294    }
295}
296
297impl<K> Default for Builder<K> {
298    #[inline]
299    fn default() -> Self {
300        Self::new()
301    }
302}
303
304impl<K: fmt::Debug> fmt::Debug for Builder<K> {
305    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306        f.debug_struct("Builder")
307            .field("open_nodes", &self.stack.len())
308            .field("has_root", &self.root.is_some())
309            .field("error", &self.error)
310            .finish()
311    }
312}
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317    use alloc::vec::Vec;
318
319    fn t(kind: &'static str, lo: u32, hi: u32) -> Token<&'static str> {
320        Token::new(kind, Span::new(lo, hi))
321    }
322
323    #[test]
324    fn test_builds_flat_tree() {
325        let mut b = Builder::new();
326        b.start_node("root");
327        b.token(t("a", 0, 1));
328        b.token(t("b", 1, 2));
329        b.finish_node();
330        let root = b.finish().expect("balanced");
331        assert_eq!(root.kind(), &"root");
332        assert_eq!(root.span(), Span::new(0, 2));
333        assert_eq!(root.child_tokens().count(), 2);
334    }
335
336    #[test]
337    fn test_builds_nested_tree_with_covering_spans() {
338        let mut b = Builder::new();
339        b.start_node("outer");
340        b.token(t("(", 0, 1));
341        b.start_node("inner");
342        b.token(t("x", 1, 2));
343        b.finish_node();
344        b.token(t(")", 2, 3));
345        b.finish_node();
346        let root = b.finish().expect("balanced");
347        assert_eq!(root.span(), Span::new(0, 3));
348        let inner = root.child_nodes().next().expect("one inner node");
349        assert_eq!(inner.span(), Span::new(1, 2));
350        let leaves: Vec<_> = root.tokens().map(|tok| *tok.kind()).collect();
351        assert_eq!(leaves, ["(", "x", ")"]);
352    }
353
354    #[test]
355    fn test_empty_node_takes_cursor_position() {
356        let mut b = Builder::new();
357        b.start_node("root");
358        b.token(t("a", 0, 3));
359        b.start_node("gap"); // no tokens
360        b.finish_node();
361        b.finish_node();
362        let root = b.finish().expect("balanced");
363        let gap = root.child_nodes().next().expect("gap node");
364        assert_eq!(gap.span(), Span::empty(3));
365        assert!(gap.is_empty());
366    }
367
368    #[test]
369    fn test_finish_empty_is_error() {
370        let b = Builder::<&str>::new();
371        assert_eq!(b.finish(), Err(BuildError::EmptyTree));
372    }
373
374    #[test]
375    fn test_unclosed_node_is_error() {
376        let mut b = Builder::new();
377        b.start_node("root");
378        b.token(t("a", 0, 1));
379        assert_eq!(b.finish(), Err(BuildError::UnclosedNodes));
380    }
381
382    #[test]
383    fn test_unbalanced_finish_is_error() {
384        let mut b = Builder::<&str>::new();
385        b.finish_node();
386        assert_eq!(b.finish(), Err(BuildError::UnbalancedFinish));
387    }
388
389    #[test]
390    fn test_token_outside_node_is_error() {
391        let mut b = Builder::new();
392        b.token(t("a", 0, 1));
393        assert_eq!(b.finish(), Err(BuildError::TokenOutsideNode));
394    }
395
396    #[test]
397    fn test_multiple_roots_is_error() {
398        let mut b = Builder::new();
399        b.start_node("first");
400        b.token(t("a", 0, 1));
401        b.finish_node();
402        b.start_node("second");
403        b.token(t("b", 1, 2));
404        b.finish_node();
405        assert_eq!(b.finish(), Err(BuildError::MultipleRoots));
406    }
407
408    #[test]
409    fn test_first_error_wins() {
410        let mut b = Builder::<&str>::new();
411        b.finish_node(); // UnbalancedFinish recorded
412        b.token(t("a", 0, 1)); // would be TokenOutsideNode, but ignored
413        assert_eq!(b.finish(), Err(BuildError::UnbalancedFinish));
414    }
415
416    #[test]
417    fn test_deeply_nested_build_and_finish() {
418        let mut b = Builder::new();
419        for _ in 0..10_000 {
420            b.start_node("link");
421        }
422        b.token(t("leaf", 0, 1));
423        for _ in 0..10_000 {
424            b.finish_node();
425        }
426        let root = b.finish().expect("balanced");
427        assert_eq!(root.descendants().count(), 10_000);
428        assert_eq!(root.tokens().count(), 1);
429    }
430}