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}