Skip to main content

ridl_syntax/
syntax_kind.rs

1//! Token and node kinds shared by the lexer ([`crate::lexer`]) and the grammar.
2
3/// The kinds of syntax tokens and nodes the family grammar produces.
4///
5/// The token variants are the full family token set the lexer recognises
6/// (docs/ROADMAP.md epic E1.1, typl reference §1.4 and §2). The node variants
7/// are the typl grammar's node inventory (epic E1.2a) — one variant per rule
8/// in `family.ungram`, plus [`ErrorNode`](SyntaxKind::ErrorNode) for recovery.
9/// [`RidlLanguage`] maps between this enum and rowan's raw kind through the
10/// `#[repr(u16)]` discriminants, so the node variants stay grouped at the end
11/// with [`ErrorNode`](SyntaxKind::ErrorNode) last — that is the range the
12/// `kind_from_raw` assertion below guards.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[repr(u16)]
15pub enum SyntaxKind {
16    // Tokens — the full family lexer (typl reference §1.4, §2).
17    //
18    // Keywords the typl profile uses (typl reference §1.4). Each used keyword
19    // maps to a distinct variant; every family-registry word the typl profile
20    // does not use maps to `ReservedWord` instead.
21    PackageKw,
22    ImportKw,
23    AsKw,
24    InternalKw,
25    TypeKw,
26    ConstKw,
27    StructKw,
28    EnumKw,
29    EnumsetKw,
30    UnionKw,
31    BooleanKw,
32    IntegerKw,
33    FloatKw,
34    StringKw,
35    BytesKw,
36    TrueKw,
37    FalseKw,
38    StepKw,
39    MatchKw,
40    ReservedKw,
41    ErrorKw,
42    // Keywords the ridl profile activates beyond typl's set (ridl reference
43    // §2.3): the interaction and container words plus the expr-core pair.
44    // Under `Profile::Typl` these words still lex to `ReservedWord`.
45    InterfaceKw,
46    ServiceKw,
47    SignalKw,
48    EventKw,
49    CommandKw,
50    QueryKw,
51    FixedKw,
52    RequireKw,
53    EnsureKw,
54    // Keywords the rsdl profile activates beyond typl's set (rsdl reference
55    // §2). Under `Profile::Typl` and `Profile::Ridl` these words still lex to
56    // `ReservedWord`.
57    SystemKw,
58    ComponentKw,
59    DistributionKw,
60    DeploymentKw,
61    MachineKw,
62    OffersKw,
63    RequiresKw,
64    ForKw,
65    /// A family-registry word that the active profile does not use (typl
66    /// reference §1.4). Reserved in every profile, never a valid identifier.
67    ReservedWord,
68    // Names and literals.
69    Ident,
70    IntNumber,
71    FloatNumber,
72    String,
73    Regex,
74    Duration,
75    // Punctuation.
76    Colon,
77    Eq,
78    LBracket,
79    RBracket,
80    LBrace,
81    RBrace,
82    LParen,
83    RParen,
84    DotDot,
85    Dot,
86    Slash,
87    Comma,
88    Question,
89    Semicolon,
90    At,
91    Pipe,
92    Percent,
93    Minus,
94    // Expression operators (ridl reference §13, general form expr core).
95    // Family tokens like `Duration`: the lexer recognises them under every
96    // profile; the typl grammar simply never accepts them.
97    Lt,
98    Le,
99    Gt,
100    Ge,
101    EqEq,
102    Neq,
103    AmpAmp,
104    PipePipe,
105    Bang,
106    Plus,
107    Star,
108    // Trivia and the catch-all for unrecognised input.
109    Whitespace,
110    LineComment,
111    BlockComment,
112    DocComment,
113    Error,
114    // Nodes — the typl grammar's node inventory (`family.ungram`, epic E1.2a).
115    // The full parser (task E1.2b) produces them; until it lands, the
116    // generated typed AST casts over trees built directly in tests.
117    SourceFile,
118    PackageDecl,
119    Import,
120    TypeDef,
121    ConstDef,
122    StructDef,
123    FieldDef,
124    ReservedEntry,
125    EnumDef,
126    EnumValue,
127    EnumSetDef,
128    EnumSetBit,
129    UnionDef,
130    UnionArm,
131    TupleType,
132    TupleField,
133    ArrayType,
134    MapType,
135    OptionalType,
136    PathType,
137    PrimitiveType,
138    UnitExpr,
139    Constraint,
140    Bound,
141    Name,
142    QualifiedName,
143    Literal,
144    InitValue,
145    // Nodes of the ridl interaction grammar (`family.ungram`, epic E2.1a —
146    // ridl reference Appendix C, ADR-0008 decisions 1 and 2).
147    InterfaceDef,
148    SignalDef,
149    EventDef,
150    CommandDef,
151    QueryDef,
152    FixedDef,
153    Param,
154    ParamList,
155    ReturnType,
156    StreamType,
157    FallibleType,
158    Timing,
159    TimingRange,
160    AttrBlock,
161    // Nodes of the attribute and guaranteed-subset expression grammar
162    // (`family.ungram`, epic E2.4 — general form §4.2, expr-core
163    // specification §3.1).
164    Attribute,
165    AttrValue,
166    BinaryExpr,
167    PrefixExpr,
168    MemberExpr,
169    PathExpr,
170    ParenExpr,
171    LiteralExpr,
172    // Nodes of the ridl service grammar (`family.ungram`, epic E2.13 — ridl
173    // reference §14.5). `service_def` is authored by E2 task 8 (absent from
174    // Appendix C).
175    ServiceDef,
176    DottedName,
177    // Nodes of the rsdl grammar (`family.ungram` — rsdl reference §3, §4,
178    // Appendix B).
179    SystemDef,
180    ComponentDef,
181    ComponentLine,
182    DistributionDef,
183    DeploymentDef,
184    MachineDef,
185    MemberLine,
186    Reference,
187    /// A recovery node: error recovery wraps the tokens it skips in one of
188    /// these, so broken input still produces a lossless tree. It is the one
189    /// node kind with no rule in `family.ungram`, and the last variant — the
190    /// `kind_from_raw` assertion below guards the range up to it.
191    ErrorNode,
192}
193
194impl SyntaxKind {
195    /// Whitespace and comments — tokens that carry no grammatical meaning but
196    /// must stay in the tree for losslessness. Doc comments are trivia too: the
197    /// grammar reads them from the trivia preceding a definition.
198    pub fn is_trivia(self) -> bool {
199        matches!(
200            self,
201            SyntaxKind::Whitespace
202                | SyntaxKind::LineComment
203                | SyntaxKind::BlockComment
204                | SyntaxKind::DocComment
205        )
206    }
207}
208
209impl From<SyntaxKind> for rowan::SyntaxKind {
210    fn from(kind: SyntaxKind) -> Self {
211        Self(kind as u16)
212    }
213}
214
215/// The rowan [`Language`](rowan::Language) instance for the RIDL family. It
216/// binds rowan's raw `u16` kinds to [`SyntaxKind`] through the enum's
217/// `#[repr(u16)]` discriminants.
218#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
219pub enum RidlLanguage {}
220
221impl rowan::Language for RidlLanguage {
222    type Kind = SyntaxKind;
223
224    fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind {
225        assert!(
226            raw.0 <= SyntaxKind::ErrorNode as u16,
227            "raw kind {} is out of range for SyntaxKind",
228            raw.0,
229        );
230        // SAFETY: SyntaxKind is `#[repr(u16)]` with contiguous discriminants
231        // from 0 up to and including `ErrorNode`. The assertion above proves
232        // `raw.0` names one of them, so the transmute produces a valid variant.
233        unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
234    }
235
236    fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
237        kind.into()
238    }
239}
240
241/// A node in the red (cursor) tree over [`RidlLanguage`].
242pub type SyntaxNode = rowan::SyntaxNode<RidlLanguage>;
243/// A token in the red (cursor) tree over [`RidlLanguage`].
244pub type SyntaxToken = rowan::SyntaxToken<RidlLanguage>;