Skip to main content

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}