zerodds_idl_cst/tree.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! The green/red tree. Spec §2.
4//!
5//! Green nodes are immutable, carry only kind and width, and are shared
6//! through `Arc` — two identical subtrees can be one allocation. Red
7//! nodes are built on demand and add what green cannot have without
8//! losing sharing: an absolute offset and a parent.
9//!
10//! Trivia lives in the tree as ordinary tokens. That is what makes
11//! `text()` reproduce the input rather than approximate it, and it is
12//! why a green token carries its text instead of a span into some
13//! particular file — a span would tie the tree to one source and
14//! make sharing impossible.
15
16use crate::span::Span;
17use crate::token::TokenKind;
18use alloc::string::String;
19use alloc::sync::Arc;
20use alloc::vec::Vec;
21use core::fmt;
22
23/// What a syntax node is.
24///
25/// One flat enum over every form the grammar produces. Adding a form is
26/// a variant, and every exhaustive match on it has to account for the
27/// new one.
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
29#[non_exhaustive]
30pub enum CstKind {
31 /// The whole file.
32 File,
33
34 // -- declarations ----------------------------------------------------
35 /// `module X { ... };`
36 Module,
37 /// `const T N = expr;`
38 ConstDecl,
39 /// `typedef T N;`
40 TypedefDecl,
41 /// `struct S { ... };`
42 StructDecl,
43 /// `union U switch (T) { ... };`
44 UnionDecl,
45 /// `enum E { ... };`
46 EnumDecl,
47 /// `bitmask B { ... };`
48 BitmaskDecl,
49 /// `bitset B { ... };`
50 BitsetDecl,
51 /// `native N;`
52 NativeDecl,
53 /// `exception E { ... };`
54 ExceptionDecl,
55 /// `interface I { ... };`
56 InterfaceDecl,
57 /// `valuetype V { ... };`
58 ValuetypeDecl,
59 /// `valuetype VB long;` — a boxed value. IDL 4.2 §7.4.7 rule (126).
60 ///
61 /// Its own kind and not a `ValuetypeDecl` with a type beside it: a
62 /// value box has no state members, no initialisers and no bases, and
63 /// a consumer matching on the kind should not have to look for the
64 /// absence of a body to tell the two apart.
65 ValueBoxDecl,
66 /// A forward declaration: `struct S;`
67 ForwardDecl,
68 /// `@annotation A { ... };`
69 AnnotationDecl,
70 /// `typeid X "id";` or `typeprefix X "prefix";`
71 RepositoryDirective,
72 /// `import X;`
73 ImportDecl,
74 /// `keylist T (a, b);` — an RTI Connext directive.
75 KeylistDecl,
76 /// The `<...>` formal parameter list of a template module.
77 /// `module M<long> N;` — a template module instantiation.
78 /// IDL 4.2 §7.4.12 rule (190).
79 TemplateModuleInst,
80 /// The actual parameters of an instantiation, rule (191). Each child
81 /// is a type specification or a constant expression, rule (192).
82 ActualParameters,
83 /// `alias M<T> R;` inside a template module body, rule (193).
84 TemplateModuleRef,
85 /// The formal parameters of a template module, rule (186).
86 TemplateParameters,
87 /// One formal parameter of a template module.
88 TemplateParameter,
89 /// One port of a component: `provides`, `uses`, `emits`,
90 /// `publishes`, `consumes`, `port` or `mirrorport`.
91 PortDecl,
92 /// A home's `manages X primarykey K` clause.
93 ManagesClause,
94
95 // -- members and parts -----------------------------------------------
96 /// One member of a struct, exception or valuetype.
97 Member,
98 /// One `case` of a union, with its labels.
99 UnionCase,
100 /// A `case <expr>:` or `default:` label.
101 CaseLabel,
102 /// One enumerator.
103 Enumerator,
104 /// One bitmask bit value.
105 BitValue,
106 /// One bitset field.
107 BitField,
108 /// An operation.
109 Operation,
110 /// One parameter of an operation.
111 Parameter,
112 /// An attribute.
113 AttributeDecl,
114 /// A `raises (...)` clause.
115 RaisesClause,
116 /// A `context (...)` clause.
117 ContextClause,
118 /// An inheritance clause.
119 InheritanceClause,
120
121 // -- types -----------------------------------------------------------
122 /// A named type reference.
123 NamedType,
124 /// A primitive keyword used as a type.
125 PrimitiveType,
126 /// `sequence<T>` or `sequence<T, N>`.
127 SequenceType,
128 /// `string` or `string<N>`.
129 StringType,
130 /// `wstring` or `wstring<N>`.
131 WStringType,
132 /// `map<K, V>` or `map<K, V, N>`.
133 MapType,
134 /// `fixed<d, s>`.
135 FixedType,
136 /// The `<...>` argument list of a parameterised type.
137 TypeArguments,
138 /// A declarator, simple or array.
139 Declarator,
140 /// One `[expr]` of an array declarator.
141 ArrayDimension,
142
143 // -- names and expressions -------------------------------------------
144 /// A scoped name, possibly rooted at `::`.
145 ScopedName,
146 /// A literal used as an expression.
147 LiteralExpr,
148 /// A scoped name used as an expression.
149 NameExpr,
150 /// A unary application.
151 UnaryExpr,
152 /// A binary application.
153 BinaryExpr,
154 /// A parenthesised expression.
155 ParenExpr,
156
157 // -- annotations -----------------------------------------------------
158 /// `@ann`, `@ann(v)` or `@ann(k = v, ...)`.
159 Annotation,
160 /// The parenthesised part of an annotation.
161 AnnotationArguments,
162 /// One `k = v` inside an annotation.
163 AnnotationArgument,
164
165 // -- recovery --------------------------------------------------------
166 /// A range the parser could not make sense of.
167 ///
168 /// An ordinary node, not a separate channel: a file that fails to
169 /// parse still round-trips because its bad ranges are in the tree
170 /// rather than dropped. Spec §2.2, §3.2.
171 Error,
172}
173
174impl CstKind {
175 /// Whether this node is one the parser produced while recovering.
176 #[must_use]
177 pub const fn is_error(self) -> bool {
178 matches!(self, Self::Error)
179 }
180}
181
182/// A token in the green tree.
183///
184/// Carries its own text rather than a span, so an identical token in two
185/// files is one value and identical subtrees can share an allocation.
186#[derive(Debug, Clone, PartialEq, Eq, Hash)]
187pub struct GreenToken {
188 kind: TokenKind,
189 text: Arc<str>,
190}
191
192impl GreenToken {
193 /// A token of `kind` spelling `text`.
194 #[must_use]
195 pub fn new(kind: TokenKind, text: &str) -> Self {
196 Self {
197 kind,
198 text: Arc::from(text),
199 }
200 }
201
202 /// What it is.
203 #[must_use]
204 pub const fn kind(&self) -> TokenKind {
205 self.kind
206 }
207
208 /// How it is spelled.
209 #[must_use]
210 pub fn text(&self) -> &str {
211 &self.text
212 }
213
214 /// How many bytes it occupies.
215 #[must_use]
216 #[allow(clippy::cast_possible_truncation)]
217 pub fn width(&self) -> u32 {
218 self.text.len() as u32
219 }
220}
221
222/// A child of a green node.
223#[derive(Debug, Clone, PartialEq, Eq, Hash)]
224pub enum GreenChild {
225 /// A nested node.
226 Node(Arc<GreenNode>),
227 /// A token, which may be trivia.
228 Token(GreenToken),
229}
230
231impl GreenChild {
232 /// How many bytes it occupies.
233 #[must_use]
234 pub fn width(&self) -> u32 {
235 match self {
236 Self::Node(node) => node.width(),
237 Self::Token(token) => token.width(),
238 }
239 }
240}
241
242/// An immutable node.
243///
244/// Knows its kind, its children and its total width — never its
245/// position, because a node that knew where it was could not be shared
246/// between two places.
247#[derive(Debug, Clone, PartialEq, Eq, Hash)]
248pub struct GreenNode {
249 kind: CstKind,
250 width: u32,
251 children: Vec<GreenChild>,
252}
253
254impl GreenNode {
255 /// A node of `kind` over `children`, with the width they sum to.
256 #[must_use]
257 pub fn new(kind: CstKind, children: Vec<GreenChild>) -> Self {
258 let width = children
259 .iter()
260 .fold(0u32, |sum, child| sum.saturating_add(child.width()));
261 Self {
262 kind,
263 width,
264 children,
265 }
266 }
267
268 /// What it is.
269 #[must_use]
270 pub const fn kind(&self) -> CstKind {
271 self.kind
272 }
273
274 /// Total bytes, trivia included.
275 #[must_use]
276 pub const fn width(&self) -> u32 {
277 self.width
278 }
279
280 /// Its children in order.
281 #[must_use]
282 pub fn children(&self) -> &[GreenChild] {
283 &self.children
284 }
285
286 /// Reconstruct the source this node covers.
287 ///
288 /// The property the whole tree exists for: for the root, this equals
289 /// the input byte for byte. Spec §2.2.
290 ///
291 /// Iterative rather than recursive. A deeply nested type — the
292 /// grammar puts no bound on how far `sequence<sequence<...>>` can
293 /// go — must not make reconstruction depend on stack depth, and this
294 /// crate forbids panics, which a stack overflow would not respect.
295 #[must_use]
296 pub fn text(&self) -> String {
297 let mut out = String::with_capacity(self.width as usize);
298 let mut stack: Vec<&GreenChild> = self.children.iter().rev().collect();
299 while let Some(child) = stack.pop() {
300 match child {
301 GreenChild::Token(token) => out.push_str(token.text()),
302 GreenChild::Node(node) => stack.extend(node.children.iter().rev()),
303 }
304 }
305 out
306 }
307}
308
309/// Dropping a deeply nested tree must not depend on stack depth either.
310///
311/// `text()` is iterative, but the derived drop glue is not: releasing a
312/// chain of fifty thousand nested `Arc<GreenNode>` recurses once per
313/// level and aborts the process. Found by the depth test, which built
314/// the tree successfully and then died tearing it down.
315///
316/// This unnests the children into a work list and releases them there,
317/// so depth costs heap rather than stack. `Arc::into_inner` yields the
318/// node only when this was the last reference, which is what keeps
319/// shared subtrees alive.
320impl Drop for GreenNode {
321 fn drop(&mut self) {
322 let mut stack: Vec<GreenChild> = core::mem::take(&mut self.children);
323 while let Some(child) = stack.pop() {
324 if let GreenChild::Node(node) = child {
325 if let Some(mut inner) = Arc::into_inner(node) {
326 stack.append(&mut inner.children);
327 }
328 }
329 }
330 }
331}
332
333/// A node with a position.
334///
335/// Built on demand from a green node plus the offset it sits at and the
336/// parent it hangs from. Cheap to make and cheap to drop; nothing caches
337/// them.
338#[derive(Debug, Clone)]
339pub struct SyntaxNode {
340 green: Arc<GreenNode>,
341 offset: u32,
342 parent: Option<Arc<SyntaxNode>>,
343}
344
345impl SyntaxNode {
346 /// The root of a tree.
347 #[must_use]
348 pub const fn root(green: Arc<GreenNode>) -> Self {
349 Self {
350 green,
351 offset: 0,
352 parent: None,
353 }
354 }
355
356 /// What it is.
357 #[must_use]
358 pub fn kind(&self) -> CstKind {
359 self.green.kind()
360 }
361
362 /// Where it sits in the file, trivia included.
363 #[must_use]
364 pub fn span(&self) -> Span {
365 Span::new(self.offset, self.offset.saturating_add(self.green.width()))
366 }
367
368 /// The green node behind it.
369 #[must_use]
370 pub fn green(&self) -> &Arc<GreenNode> {
371 &self.green
372 }
373
374 /// Its parent, if it is not the root.
375 #[must_use]
376 pub fn parent(&self) -> Option<&Arc<Self>> {
377 self.parent.as_ref()
378 }
379
380 /// The chain from this node up to the root, this node first.
381 #[must_use]
382 pub fn ancestors(&self) -> Vec<Arc<Self>> {
383 let mut out = Vec::new();
384 let mut current = self.parent.clone();
385 while let Some(node) = current {
386 current = node.parent.clone();
387 out.push(node);
388 }
389 out
390 }
391
392 /// Its child nodes, with their positions filled in.
393 #[must_use]
394 pub fn children(self: &Arc<Self>) -> Vec<Arc<Self>> {
395 let mut out = Vec::new();
396 let mut at = self.offset;
397 for child in self.green.children() {
398 match child {
399 GreenChild::Node(green) => {
400 out.push(Arc::new(Self {
401 green: green.clone(),
402 offset: at,
403 parent: Some(self.clone()),
404 }));
405 at = at.saturating_add(green.width());
406 }
407 GreenChild::Token(token) => at = at.saturating_add(token.width()),
408 }
409 }
410 out
411 }
412
413 /// Its direct tokens, with their positions.
414 #[must_use]
415 pub fn tokens(&self) -> Vec<(Span, GreenToken)> {
416 let mut out = Vec::new();
417 let mut at = self.offset;
418 for child in self.green.children() {
419 let width = child.width();
420 if let GreenChild::Token(token) = child {
421 out.push((Span::new(at, at.saturating_add(width)), token.clone()));
422 }
423 at = at.saturating_add(width);
424 }
425 out
426 }
427
428 /// The node's own tokens with the trivia left out.
429 ///
430 /// [`SyntaxNode::tokens`] keeps everything, because the round trip
431 /// needs it. A rule asking what a node says wants this one: the
432 /// first token of `@ key` is `@`, and the one after it is `key`,
433 /// not the space between them.
434 #[must_use]
435 pub fn syntax_tokens(&self) -> Vec<(Span, GreenToken)> {
436 self.tokens()
437 .into_iter()
438 .filter(|(_, token)| !token.kind().is_trivia())
439 .collect()
440 }
441
442 /// Reconstruct the source this node covers.
443 #[must_use]
444 pub fn text(&self) -> String {
445 self.green.text()
446 }
447
448 /// Every node in the subtree, this one first, in source order.
449 #[must_use]
450 pub fn descendants(self: &Arc<Self>) -> Vec<Arc<Self>> {
451 let mut out = alloc::vec![self.clone()];
452 let mut index = 0;
453 while index < out.len() {
454 let node = out[index].clone();
455 out.extend(node.children());
456 index += 1;
457 }
458 out
459 }
460
461 /// Whether this node or any below it came from recovery.
462 #[must_use]
463 pub fn has_errors(self: &Arc<Self>) -> bool {
464 self.descendants().iter().any(|node| node.kind().is_error())
465 }
466}
467
468impl fmt::Display for SyntaxNode {
469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470 write!(f, "{}", self.text())
471 }
472}
473
474/// A marked position among an open node's children.
475///
476/// Only meaningful to the builder that produced it and only while that
477/// builder is at the same depth.
478#[derive(Debug, Clone, Copy, PartialEq, Eq)]
479pub struct Checkpoint {
480 depth: usize,
481 children: usize,
482}
483
484/// Builds a green tree.
485///
486/// Nodes are opened and closed around the tokens they contain, so a
487/// parser writes what it is doing rather than assembling children by
488/// hand.
489#[derive(Debug, Default)]
490pub struct TreeBuilder {
491 stack: Vec<(CstKind, Vec<GreenChild>)>,
492 finished: Option<Arc<GreenNode>>,
493}
494
495impl TreeBuilder {
496 /// An empty builder.
497 #[must_use]
498 pub const fn new() -> Self {
499 Self {
500 stack: Vec::new(),
501 finished: None,
502 }
503 }
504
505 /// Begin a node.
506 pub fn start(&mut self, kind: CstKind) {
507 self.stack.push((kind, Vec::new()));
508 }
509
510 /// Add a token to the node in progress.
511 pub fn token(&mut self, kind: TokenKind, text: &str) {
512 if let Some((_, children)) = self.stack.last_mut() {
513 children.push(GreenChild::Token(GreenToken::new(kind, text)));
514 }
515 }
516
517 /// End the node in progress, attaching it to its parent.
518 pub fn finish(&mut self) {
519 let Some((kind, children)) = self.stack.pop() else {
520 return;
521 };
522 let node = Arc::new(GreenNode::new(kind, children));
523 match self.stack.last_mut() {
524 Some((_, siblings)) => siblings.push(GreenChild::Node(node)),
525 None => self.finished = Some(node),
526 }
527 }
528
529 /// The completed tree.
530 ///
531 /// `None` if a node was left open, which is a parser defect rather
532 /// than a malformed input — every `start` has to have its `finish`.
533 #[must_use]
534 pub fn build(mut self) -> Option<Arc<GreenNode>> {
535 while !self.stack.is_empty() {
536 self.finish();
537 }
538 self.finished
539 }
540
541 /// How deep the builder currently is.
542 #[must_use]
543 pub fn depth(&self) -> usize {
544 self.stack.len()
545 }
546
547 /// Mark the current position among the open node's children.
548 ///
549 /// Precedence climbing does not know it is building a binary
550 /// expression until it has already parsed the left operand, so it
551 /// needs to wrap children it has emitted. A checkpoint plus
552 /// [`TreeBuilder::start_at`] is that, and it is why the builder
553 /// exists rather than callers assembling children by hand.
554 #[must_use]
555 pub fn checkpoint(&self) -> Checkpoint {
556 Checkpoint {
557 depth: self.stack.len(),
558 children: self.stack.last().map_or(0, |(_, children)| children.len()),
559 }
560 }
561
562 /// Wrap everything emitted since `checkpoint` in a new node.
563 ///
564 /// A checkpoint from a different nesting depth is ignored rather
565 /// than acted on: acting on it would silently reparent a subtree.
566 pub fn start_at(&mut self, checkpoint: Checkpoint, kind: CstKind) {
567 if checkpoint.depth != self.stack.len() {
568 self.start(kind);
569 return;
570 }
571 let Some((_, children)) = self.stack.last_mut() else {
572 self.start(kind);
573 return;
574 };
575 if checkpoint.children > children.len() {
576 self.start(kind);
577 return;
578 }
579 let wrapped: Vec<GreenChild> = children.split_off(checkpoint.children);
580 self.stack.push((kind, wrapped));
581 }
582}
583
584/// Where a file's syntax is, with the trivia left out.
585///
586/// A node's [`span`](SyntaxNode::span) covers the trivia too, because
587/// the round trip needs it: the lexer attaches whitespace and comments
588/// leading, so the blank line and the indentation before a declaration
589/// belong to the node that follows them. That is right for
590/// reconstruction and wrong for a diagnostic — the span of the second
591/// declarator of `long a,\n b;` begins at the newline after the
592/// comma, so a message about `b` names the line before it.
593///
594/// This is the index that turns such a span back into the text it names:
595/// the first significant token at or after the start, and the last one
596/// ending at or before the end. A span that holds no significant token
597/// at all — an empty one, or one covering only trivia — is returned
598/// unchanged rather than moved somewhere it did not point.
599#[derive(Debug, Clone, Default)]
600pub struct SyntaxSpans {
601 /// Every non-trivia token's range, ascending. Tokens do not overlap,
602 /// so this is sorted by `start` and by `end` at once.
603 spans: Vec<Span>,
604}
605
606impl SyntaxSpans {
607 /// Index the subtree under `root`.
608 ///
609 /// Iterative for the same reason [`GreenNode::text`] is: the grammar
610 /// puts no bound on nesting, and this crate forbids panics, which a
611 /// stack overflow would not respect.
612 #[must_use]
613 pub fn of(root: &Arc<SyntaxNode>) -> Self {
614 let mut spans = Vec::new();
615 let mut stack: Vec<(&GreenNode, u32)> = alloc::vec![(root.green(), root.span().start)];
616 while let Some((green, offset)) = stack.pop() {
617 let mut at = offset;
618 for child in green.children() {
619 let width = child.width();
620 match child {
621 GreenChild::Token(token) => {
622 if !token.kind().is_trivia() {
623 spans.push(Span::new(at, at.saturating_add(width)));
624 }
625 }
626 GreenChild::Node(node) => stack.push((node, at)),
627 }
628 at = at.saturating_add(width);
629 }
630 }
631 spans.sort_unstable_by_key(|span| span.start);
632 Self { spans }
633 }
634
635 /// `span` narrowed to the text it covers.
636 #[must_use]
637 pub fn trim(&self, span: Span) -> Span {
638 if span.is_empty() {
639 return span;
640 }
641 let first = self.spans.partition_point(|token| token.start < span.start);
642 let Some(head) = self.spans.get(first) else {
643 return span;
644 };
645 if head.start >= span.end {
646 return span;
647 }
648 let last = self.spans.partition_point(|token| token.end <= span.end);
649 let end = match last.checked_sub(1).and_then(|index| self.spans.get(index)) {
650 Some(tail) if tail.end > head.start => tail.end,
651 _ => span.end,
652 };
653 Span::new(head.start, end)
654 }
655}