Skip to main content

wirespec_syntax/
ast.rs

1//! AST node definitions for wirespec.
2//!
3//! These correspond to the canonical AST schema defined in `AST_SCHEMA_SPEC.md`.
4//! The AST is syntax-oriented: it preserves source structure, declaration order,
5//! and field order. It does NOT perform name resolution, type checking, or any
6//! semantic analysis.
7
8use crate::span::Span;
9
10// ── Root ──
11
12/// Root of a parsed `.wspec` file.
13#[derive(Debug, Clone, PartialEq)]
14pub struct AstModule {
15    pub module_decl: Option<AstModuleDecl>,
16    pub imports: Vec<AstImport>,
17    pub annotations: Vec<AstAnnotation>,
18    pub items: Vec<AstTopItem>,
19    pub span: Option<Span>,
20}
21
22// ── Module / Import ──
23
24#[derive(Debug, Clone, PartialEq)]
25pub struct AstModuleDecl {
26    pub name: String,
27    pub span: Option<Span>,
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub struct AstImport {
32    pub module: String,
33    pub name: Option<String>,
34    pub span: Option<Span>,
35}
36
37// ── Annotations ──
38
39#[derive(Debug, Clone, PartialEq)]
40pub struct AstAnnotation {
41    pub name: String,
42    pub args: Vec<AstAnnotationArg>,
43    pub span: Option<Span>,
44}
45
46#[derive(Debug, Clone, PartialEq)]
47pub enum AstAnnotationArg {
48    Identifier(String),
49    Int(i64),
50    Bool(bool),
51    String(String),
52    NamedValue {
53        name: String,
54        value: AstLiteralValue,
55    },
56}
57
58// ── Expressions ──
59
60#[derive(Debug, Clone, PartialEq)]
61pub enum AstExpr {
62    Int {
63        value: i64,
64        span: Option<Span>,
65    },
66    Bool {
67        value: bool,
68        span: Option<Span>,
69    },
70    Null {
71        span: Option<Span>,
72    },
73    NameRef {
74        name: String,
75        span: Option<Span>,
76    },
77    MemberAccess {
78        base: Box<AstExpr>,
79        field: String,
80        span: Option<Span>,
81    },
82    Binary {
83        op: BinOp,
84        left: Box<AstExpr>,
85        right: Box<AstExpr>,
86        span: Option<Span>,
87    },
88    Unary {
89        op: UnaryOp,
90        operand: Box<AstExpr>,
91        span: Option<Span>,
92    },
93    Coalesce {
94        expr: Box<AstExpr>,
95        default: Box<AstExpr>,
96        span: Option<Span>,
97    },
98    InState {
99        expr: Box<AstExpr>,
100        state_name: String,
101        span: Option<Span>,
102    },
103    Subscript {
104        base: Box<AstExpr>,
105        index: Box<AstExpr>,
106        span: Option<Span>,
107    },
108    StateConstructor {
109        sm_name: String,
110        state_name: String,
111        args: Vec<AstExpr>,
112        span: Option<Span>,
113    },
114    Fill {
115        value: Box<AstExpr>,
116        count: Box<AstExpr>,
117        span: Option<Span>,
118    },
119    Slice {
120        base: Box<AstExpr>,
121        start: Box<AstExpr>,
122        end: Box<AstExpr>,
123        span: Option<Span>,
124    },
125    All {
126        collection: Box<AstExpr>,
127        state_name: String,
128        span: Option<Span>,
129    },
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
133pub enum BinOp {
134    Add,
135    Sub,
136    Mul,
137    Div,
138    Mod,
139    BitAnd,
140    BitOr,
141    BitXor,
142    Shl,
143    Shr,
144    Eq,
145    Ne,
146    Lt,
147    Le,
148    Gt,
149    Ge,
150    And,
151    Or,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
155pub enum UnaryOp {
156    Not,
157    Neg,
158}
159
160// ── ASN.1 Length ──
161
162#[derive(Debug, Clone, PartialEq)]
163pub enum Asn1Length {
164    Expr(Box<AstExpr>),
165    Remaining,
166}
167
168// ── Type Expressions ──
169
170#[derive(Debug, Clone, PartialEq)]
171pub enum AstTypeExpr {
172    Named {
173        name: String,
174        span: Option<Span>,
175    },
176    Bits {
177        width: u16,
178        span: Option<Span>,
179    },
180    Match {
181        field_name: String,
182        branches: Vec<AstMatchBranch>,
183        span: Option<Span>,
184    },
185    Bytes {
186        kind: AstBytesKind,
187        fixed_size: Option<u64>,
188        size_expr: Option<Box<AstExpr>>,
189        span: Option<Span>,
190    },
191    Array {
192        element_type: Box<AstTypeExpr>,
193        count: AstArrayCount,
194        within_expr: Option<Box<AstExpr>>,
195        span: Option<Span>,
196    },
197    Optional {
198        condition: AstExpr,
199        inner_type: Box<AstTypeExpr>,
200        span: Option<Span>,
201    },
202    Asn1 {
203        type_name: String,
204        encoding: String,
205        length: Asn1Length,
206        span: Option<Span>,
207    },
208}
209
210#[derive(Debug, Clone, PartialEq)]
211pub struct AstMatchBranch {
212    pub pattern: AstPattern,
213    pub result_type: AstTypeExpr,
214    pub span: Option<Span>,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub enum AstBytesKind {
219    Fixed,
220    Length,
221    Remaining,
222    LengthOrRemaining,
223}
224
225#[derive(Debug, Clone, PartialEq)]
226pub enum AstArrayCount {
227    Expr(AstExpr),
228    Fill,
229}
230
231// ── Patterns ──
232
233#[derive(Debug, Clone, PartialEq)]
234pub enum AstPattern {
235    Value {
236        value: i64,
237        span: Option<Span>,
238    },
239    RangeInclusive {
240        start: i64,
241        end: i64,
242        span: Option<Span>,
243    },
244    Wildcard {
245        span: Option<Span>,
246    },
247}
248
249// ── Fields ──
250
251#[derive(Debug, Clone, PartialEq)]
252pub enum AstFieldItem {
253    Field(AstFieldDef),
254    Derived(AstDerivedField),
255    Require(AstRequireClause),
256}
257
258#[derive(Debug, Clone, PartialEq)]
259pub struct AstFieldDef {
260    pub name: String,
261    pub type_expr: AstTypeExpr,
262    pub annotations: Vec<AstAnnotation>,
263    pub span: Option<Span>,
264}
265
266#[derive(Debug, Clone, PartialEq)]
267pub struct AstDerivedField {
268    pub name: String,
269    pub type_name: String,
270    pub expr: AstExpr,
271    pub span: Option<Span>,
272}
273
274#[derive(Debug, Clone, PartialEq)]
275pub struct AstRequireClause {
276    pub expr: AstExpr,
277    pub span: Option<Span>,
278}
279
280// ── Top-Level Items ──
281
282#[derive(Debug, Clone, PartialEq)]
283pub enum AstTopItem {
284    Const(AstConstDecl),
285    Enum(AstEnumDecl),
286    Flags(AstFlagsDecl),
287    StaticAssert(AstStaticAssertDecl),
288    Type(AstTypeDecl),
289    Packet(AstPacketDecl),
290    Frame(AstFrameDecl),
291    Capsule(AstCapsuleDecl),
292    ContinuationVarInt(AstContinuationVarIntDecl),
293    StateMachine(AstStateMachineDecl),
294    ExternAsn1(AstExternAsn1),
295}
296
297// ── Extern ASN.1 ──
298
299#[derive(Debug, Clone, PartialEq)]
300pub struct AstExternAsn1 {
301    pub path: String,
302    pub rust_module: Option<String>,
303    pub type_names: Vec<String>,
304    pub span: Option<Span>,
305}
306
307// ── Const / Enum / Flags ──
308
309#[derive(Debug, Clone, PartialEq)]
310pub struct AstConstDecl {
311    pub name: String,
312    pub type_name: String,
313    pub value: AstLiteralValue,
314    pub annotations: Vec<AstAnnotation>,
315    pub exported: bool,
316    pub span: Option<Span>,
317}
318
319#[derive(Debug, Clone, PartialEq)]
320pub struct AstEnumMember {
321    pub name: String,
322    pub value: i64,
323    pub span: Option<Span>,
324}
325
326#[derive(Debug, Clone, PartialEq)]
327pub struct AstEnumDecl {
328    pub name: String,
329    pub underlying_type: String,
330    pub members: Vec<AstEnumMember>,
331    pub annotations: Vec<AstAnnotation>,
332    pub exported: bool,
333    pub span: Option<Span>,
334}
335
336#[derive(Debug, Clone, PartialEq)]
337pub struct AstFlagsDecl {
338    pub name: String,
339    pub underlying_type: String,
340    pub members: Vec<AstEnumMember>,
341    pub annotations: Vec<AstAnnotation>,
342    pub exported: bool,
343    pub span: Option<Span>,
344}
345
346#[derive(Debug, Clone, PartialEq)]
347pub struct AstStaticAssertDecl {
348    pub expr: AstExpr,
349    pub span: Option<Span>,
350}
351
352// ── Type / Packet ──
353
354#[derive(Debug, Clone, PartialEq)]
355pub struct AstTypeDecl {
356    pub name: String,
357    pub annotations: Vec<AstAnnotation>,
358    pub body: AstTypeDeclBody,
359    pub exported: bool,
360    pub span: Option<Span>,
361}
362
363#[derive(Debug, Clone, PartialEq)]
364pub enum AstTypeDeclBody {
365    Fields { fields: Vec<AstFieldDef> },
366    Alias { target: AstTypeExpr },
367}
368
369#[derive(Debug, Clone, PartialEq)]
370pub struct AstPacketDecl {
371    pub name: String,
372    pub fields: Vec<AstFieldItem>,
373    pub annotations: Vec<AstAnnotation>,
374    pub exported: bool,
375    pub span: Option<Span>,
376}
377
378// ── Frame / Capsule ──
379
380#[derive(Debug, Clone, PartialEq)]
381pub struct AstFrameBranch {
382    pub pattern: AstPattern,
383    pub variant_name: String,
384    pub fields: Vec<AstFieldItem>,
385    pub span: Option<Span>,
386}
387
388#[derive(Debug, Clone, PartialEq)]
389pub struct AstFrameDecl {
390    pub name: String,
391    pub tag_field: String,
392    pub tag_type: String,
393    pub branches: Vec<AstFrameBranch>,
394    pub annotations: Vec<AstAnnotation>,
395    pub exported: bool,
396    pub span: Option<Span>,
397}
398
399#[derive(Debug, Clone, PartialEq)]
400pub struct AstCapsuleDecl {
401    pub name: String,
402    pub fields: Vec<AstFieldItem>,
403    pub payload_tag: AstPayloadTagSelector,
404    pub payload_within: String,
405    pub branches: Vec<AstFrameBranch>,
406    pub annotations: Vec<AstAnnotation>,
407    pub exported: bool,
408    pub span: Option<Span>,
409}
410
411#[derive(Debug, Clone, PartialEq)]
412pub enum AstPayloadTagSelector {
413    Field { field_name: String },
414    Expr { expr: AstExpr },
415}
416
417// ── Continuation VarInt ──
418
419#[derive(Debug, Clone, PartialEq)]
420pub struct AstContinuationVarIntDecl {
421    pub name: String,
422    pub annotations: Vec<AstAnnotation>,
423    pub continuation_bit: String,
424    pub value_bits: u8,
425    pub max_bytes: u8,
426    pub byte_order: String,
427    pub exported: bool,
428    pub span: Option<Span>,
429}
430
431// ── State Machine ──
432
433#[derive(Debug, Clone, PartialEq)]
434pub struct AstStateMachineDecl {
435    pub name: String,
436    pub states: Vec<AstStateDecl>,
437    pub initial_state: String,
438    pub transitions: Vec<AstTransitionDecl>,
439    pub verify_declarations: Vec<AstVerifyDecl>,
440    pub annotations: Vec<AstAnnotation>,
441    pub exported: bool,
442    pub span: Option<Span>,
443}
444
445#[derive(Debug, Clone, PartialEq)]
446pub enum AstVerifyDecl {
447    /// `verify NoDeadlock` or `verify AllReachClosed`
448    BuiltIn { name: String, span: Option<Span> },
449    /// `verify property Name: formula`
450    Property {
451        name: String,
452        formula: AstVerifyFormula,
453        span: Option<Span>,
454    },
455}
456
457/// A verify formula — simplified temporal logic expression
458#[derive(Debug, Clone, PartialEq)]
459pub enum AstVerifyFormula {
460    InState {
461        state_name: String,
462    },
463    Not {
464        inner: Box<AstVerifyFormula>,
465    },
466    And {
467        left: Box<AstVerifyFormula>,
468        right: Box<AstVerifyFormula>,
469    },
470    Or {
471        left: Box<AstVerifyFormula>,
472        right: Box<AstVerifyFormula>,
473    },
474    Implies {
475        left: Box<AstVerifyFormula>,
476        right: Box<AstVerifyFormula>,
477    },
478    Always {
479        inner: Box<AstVerifyFormula>,
480    },
481    Eventually {
482        inner: Box<AstVerifyFormula>,
483    },
484    LeadsTo {
485        left: Box<AstVerifyFormula>,
486        right: Box<AstVerifyFormula>,
487    },
488    Compare {
489        left: Box<AstVerifyFormula>,
490        op: String,
491        right: Box<AstVerifyFormula>,
492    },
493    FieldRef {
494        path: Vec<String>,
495    },
496    Literal {
497        value: AstLiteralValue,
498    },
499}
500
501#[derive(Debug, Clone, PartialEq)]
502pub struct AstStateDecl {
503    pub name: String,
504    pub fields: Vec<AstStateFieldDef>,
505    pub is_terminal: bool,
506    pub span: Option<Span>,
507}
508
509#[derive(Debug, Clone, PartialEq)]
510pub struct AstStateFieldDef {
511    pub name: String,
512    pub type_expr: AstTypeExpr,
513    pub default_value: Option<AstLiteralValue>,
514    pub span: Option<Span>,
515}
516
517#[derive(Debug, Clone, PartialEq)]
518pub struct AstEventDecl {
519    pub name: String,
520    pub params: Vec<AstEventParam>,
521    pub span: Option<Span>,
522}
523
524#[derive(Debug, Clone, PartialEq)]
525pub struct AstEventParam {
526    pub name: String,
527    pub type_expr: AstTypeExpr,
528    pub span: Option<Span>,
529}
530
531#[derive(Debug, Clone, PartialEq)]
532pub struct AstTransitionDecl {
533    pub src_state: String,
534    pub dst_state: String,
535    pub events: Vec<AstEventDecl>,
536    pub guard: Option<AstExpr>,
537    pub actions: Vec<AstAssignment>,
538    pub delegate: Option<AstDelegateClause>,
539    pub span: Option<Span>,
540}
541
542#[derive(Debug, Clone, PartialEq)]
543pub struct AstAssignment {
544    pub target: AstExpr,
545    pub op: String,
546    pub value: AstExpr,
547    pub span: Option<Span>,
548}
549
550#[derive(Debug, Clone, PartialEq)]
551pub struct AstDelegateClause {
552    pub target: AstExpr,
553    pub event_name: String,
554    pub span: Option<Span>,
555}
556
557#[derive(Debug, Clone, PartialEq)]
558pub enum AstLiteralValue {
559    Int(i64),
560    Bool(bool),
561    String(String),
562    Null,
563}