Skip to main content

wgsl_parse/
syntax.rs

1//! A syntax tree for WGSL and WESL files. The root of the tree is [`TranslationUnit`].
2//!
3//! The syntax tree closely mirrors WGSL spec syntax while allowing language extensions.
4//!
5//! ## Strictness
6//!
7//! This syntax tree is rather strict, meaning it cannot represent most syntactically
8//! incorrect programs. But it is only syntactic, meaning it doesn't perform many
9//! contextual checks: for example, certain attributes can only appear in certain places,
10//! or declarations have different constraints depending on where they appear.
11//!
12//! ## WESL Extensions
13//!
14//! WESL extensions are enabled with the `imports`, `generics`, `attributes` and `condcomp`. Read more about WESL at <https://wesl-lang.dev>.
15//!
16//! ## Design considerations
17//!
18//! The parsing is not designed to be primarily efficient, but flexible and correct.
19//! It is made with the ultimate goal to implement spec-compliant language extensions.
20
21use std::sync::{Arc, RwLock, RwLockReadGuard};
22
23use derive_more::{From, IsVariant, Unwrap};
24
25pub use crate::span::{Span, Spanned};
26
27pub use wgsl_types::syntax::*;
28
29#[cfg(feature = "tokrepr")]
30use tokrepr::TokRepr;
31
32#[cfg(feature = "serde")]
33use serde::{Deserialize, Serialize};
34
35#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37#[derive(Default, Clone, Debug, PartialEq)]
38pub struct TranslationUnit {
39    #[cfg(feature = "imports")]
40    pub imports: Vec<ImportStatement>,
41    pub global_directives: Vec<GlobalDirective>,
42    pub global_declarations: Vec<GlobalDeclarationNode>,
43}
44
45/// Identifiers correspond to WGSL `ident` syntax node, except that they have several
46/// convenience features:
47/// * Can be shared by cloning (they are shared pointers)
48/// * Can be [renamed][Self::rename] (with interior mutability)
49/// * References to the same Ident can be [counted][Self::use_count]
50/// * Equality and Hash compares the reference, NOT the internal string value
51#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
52#[derive(Clone, Debug)]
53pub struct Ident(Arc<RwLock<String>>);
54
55impl Ident {
56    /// Create a new Ident
57    pub fn new(name: String) -> Ident {
58        // TODO: check that the name is a valid ident
59        Ident(Arc::new(RwLock::new(name)))
60    }
61    /// Get the name of the Ident
62    pub fn name(&self) -> RwLockReadGuard<'_, String> {
63        self.0.read().unwrap()
64    }
65    /// Rename all shared instances of the ident
66    pub fn rename(&mut self, name: String) {
67        *self.0.write().unwrap() = name;
68    }
69    /// Count shared instances of the ident
70    pub fn use_count(&self) -> usize {
71        Arc::<_>::strong_count(&self.0)
72    }
73}
74
75impl From<String> for Ident {
76    fn from(name: String) -> Self {
77        Ident::new(name)
78    }
79}
80
81/// equality for idents is based on address, NOT internal value
82impl PartialEq for Ident {
83    fn eq(&self, other: &Self) -> bool {
84        Arc::ptr_eq(&self.0, &other.0)
85    }
86}
87
88/// equality for idents is based on address, NOT internal value
89impl Eq for Ident {}
90
91/// hash for idents is based on address, NOT internal value
92impl std::hash::Hash for Ident {
93    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
94        std::ptr::hash(&*self.0, state)
95    }
96}
97
98#[cfg(feature = "imports")]
99#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
100#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
101#[derive(Clone, Debug, PartialEq)]
102pub struct ImportStatement {
103    #[cfg(feature = "attributes")]
104    pub attributes: Attributes,
105    pub path: Option<ModulePath>,
106    pub content: ImportContent,
107}
108
109#[cfg(feature = "imports")]
110#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
111#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
112#[derive(Clone, Debug, PartialEq, Eq, Hash, IsVariant)]
113pub enum PathOrigin {
114    /// Import relative to the current package root, starting with 'package::'.
115    Absolute,
116    /// Import relative to the current module, starting with 'super::'. The usize is the number of 'super's.
117    Relative(usize),
118    /// Import from a package dependency, starting with the extern package name.
119    Package(String),
120}
121
122#[cfg(feature = "imports")]
123#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
124#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
125#[derive(Clone, Debug, PartialEq, Eq, Hash)]
126pub struct ModulePath {
127    pub origin: PathOrigin,
128    pub components: Vec<String>,
129}
130
131#[cfg(feature = "imports")]
132#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
133#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
134#[derive(Clone, Debug, PartialEq)]
135pub struct Import {
136    pub path: Vec<String>,
137    pub content: ImportContent,
138}
139
140#[cfg(feature = "imports")]
141#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
142#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
143#[derive(Clone, Debug, PartialEq, IsVariant)]
144pub enum ImportContent {
145    Item(ImportItem),
146    Collection(Vec<Import>),
147}
148
149#[cfg(feature = "imports")]
150#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
151#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
152#[derive(Clone, Debug, PartialEq)]
153pub struct ImportItem {
154    pub ident: Ident,
155    pub rename: Option<Ident>,
156}
157
158#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
159#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
160#[derive(Clone, Debug, PartialEq, From, IsVariant, Unwrap)]
161pub enum GlobalDirective {
162    Diagnostic(DiagnosticDirective),
163    Enable(EnableDirective),
164    Requires(RequiresDirective),
165}
166
167#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
168#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
169#[derive(Clone, Debug, PartialEq)]
170pub struct DiagnosticDirective {
171    #[cfg(feature = "attributes")]
172    pub attributes: Attributes,
173    pub severity: DiagnosticSeverity,
174    pub rule_name: String,
175}
176
177#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
178#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
179#[derive(Clone, Debug, PartialEq)]
180pub struct EnableDirective {
181    #[cfg(feature = "attributes")]
182    pub attributes: Attributes,
183    pub extensions: Vec<String>,
184}
185
186#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
187#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
188#[derive(Clone, Debug, PartialEq)]
189pub struct RequiresDirective {
190    #[cfg(feature = "attributes")]
191    pub attributes: Attributes,
192    pub extensions: Vec<String>,
193}
194
195#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
196#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
197#[derive(Clone, Debug, PartialEq, From, IsVariant, Unwrap)]
198pub enum GlobalDeclaration {
199    Void,
200    Declaration(Declaration),
201    TypeAlias(TypeAlias),
202    Struct(Struct),
203    Function(Function),
204    ConstAssert(ConstAssert),
205    #[cfg(feature = "condcomp")]
206    Compound(CompoundGlobalDeclaration),
207}
208
209pub type GlobalDeclarationNode = Spanned<GlobalDeclaration>;
210
211#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213#[derive(Clone, Debug, PartialEq)]
214pub struct Declaration {
215    pub attributes: Attributes,
216    pub kind: DeclarationKind,
217    pub ident: Ident,
218    pub ty: Option<TypeExpression>,
219    pub initializer: Option<ExpressionNode>,
220}
221
222#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
223#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
224#[derive(Clone, Copy, Debug, PartialEq, Eq, IsVariant)]
225pub enum DeclarationKind {
226    Const,
227    Override,
228    Let,
229    Var(Option<(AddressSpace, Option<AccessMode>)>), // "None" corresponds to handle space if it is a module-scope declaration, otherwise function space.
230}
231
232#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
233#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
234#[derive(Clone, Debug, PartialEq)]
235pub struct TypeAlias {
236    #[cfg(feature = "attributes")]
237    pub attributes: Attributes,
238    pub ident: Ident,
239    pub ty: TypeExpression,
240}
241
242#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
243#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
244#[derive(Clone, Debug, PartialEq)]
245pub struct Struct {
246    #[cfg(feature = "attributes")]
247    pub attributes: Attributes,
248    pub ident: Ident,
249    pub members: Vec<StructMemberNode>,
250}
251
252#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
253#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
254#[derive(Clone, Debug, PartialEq)]
255pub struct StructMember {
256    pub attributes: Attributes,
257    pub ident: Ident,
258    pub ty: TypeExpression,
259}
260
261pub type StructMemberNode = Spanned<StructMember>;
262
263#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
264#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
265#[derive(Clone, Debug, PartialEq)]
266pub struct Function {
267    pub attributes: Attributes,
268    pub ident: Ident,
269    pub parameters: Vec<FormalParameter>,
270    pub return_attributes: Attributes,
271    pub return_type: Option<TypeExpression>,
272    pub body: CompoundStatement,
273}
274
275#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
277#[derive(Clone, Debug, PartialEq)]
278pub struct FormalParameter {
279    pub attributes: Attributes,
280    pub ident: Ident,
281    pub ty: TypeExpression,
282}
283
284#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
285#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
286#[derive(Clone, Debug, PartialEq)]
287pub struct ConstAssert {
288    #[cfg(feature = "attributes")]
289    pub attributes: Attributes,
290    pub expression: ExpressionNode,
291}
292
293#[cfg(feature = "condcomp")]
294#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
295#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
296#[derive(Clone, Debug, PartialEq)]
297pub struct CompoundGlobalDeclaration {
298    pub attributes: Attributes,
299    pub body: Vec<GlobalDeclarationNode>,
300}
301
302#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
303#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
304#[derive(Clone, Debug, PartialEq)]
305pub struct DiagnosticAttribute {
306    pub severity: DiagnosticSeverity,
307    pub rule: String,
308}
309
310#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
311#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
312#[derive(Clone, Debug, PartialEq)]
313pub struct InterpolateAttribute {
314    pub ty: InterpolationType,
315    pub sampling: Option<InterpolationSampling>,
316}
317
318#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
319#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
320#[derive(Clone, Debug, PartialEq)]
321pub struct WorkgroupSizeAttribute {
322    pub x: ExpressionNode,
323    pub y: Option<ExpressionNode>,
324    pub z: Option<ExpressionNode>,
325}
326
327#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
328#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
329#[derive(Clone, Debug, PartialEq)]
330pub struct CustomAttribute {
331    pub name: String,
332    pub arguments: Option<Vec<ExpressionNode>>,
333}
334
335#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
336#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
337#[derive(Clone, Debug, PartialEq, From, IsVariant, Unwrap)]
338pub enum Attribute {
339    Align(ExpressionNode),
340    Binding(ExpressionNode),
341    BlendSrc(ExpressionNode),
342    #[from]
343    Builtin(BuiltinValue),
344    Const,
345    #[from]
346    Diagnostic(DiagnosticAttribute),
347    Group(ExpressionNode),
348    Id(ExpressionNode),
349    #[from]
350    Interpolate(InterpolateAttribute),
351    Invariant,
352    Location(ExpressionNode),
353    MustUse,
354    Size(ExpressionNode),
355    #[from]
356    WorkgroupSize(WorkgroupSizeAttribute),
357    Vertex,
358    Fragment,
359    Compute,
360    #[cfg(feature = "naga-ext")]
361    Task,
362    #[cfg(feature = "naga-ext")]
363    Payload(ExpressionNode),
364    #[cfg(feature = "naga-ext")]
365    Mesh(ExpressionNode),
366    #[cfg(feature = "imports")]
367    Publish,
368    #[cfg(feature = "condcomp")]
369    If(ExpressionNode),
370    #[cfg(feature = "condcomp")]
371    Elif(ExpressionNode),
372    #[cfg(feature = "condcomp")]
373    Else,
374    #[cfg(feature = "generics")]
375    #[from]
376    Type(TypeConstraint),
377    #[cfg(feature = "naga-ext")]
378    EarlyDepthTest(Option<ConservativeDepth>),
379    #[from]
380    Custom(CustomAttribute),
381}
382
383impl Attribute {
384    pub fn is_entry_point(&self) -> bool {
385        match self {
386            Attribute::Vertex | Attribute::Fragment | Attribute::Compute => true,
387            #[cfg(feature = "naga-ext")]
388            Attribute::Task | Attribute::Mesh(_) => true,
389            _ => false,
390        }
391    }
392
393    #[cfg(feature = "condcomp")]
394    pub fn is_condcomp(&self) -> bool {
395        matches!(
396            self,
397            Attribute::If(_) | Attribute::Elif(_) | Attribute::Else
398        )
399    }
400}
401
402pub type AttributeNode = Spanned<Attribute>;
403
404#[cfg(feature = "generics")]
405#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
406#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
407#[derive(Clone, Debug, PartialEq, From)]
408pub struct TypeConstraint {
409    pub ident: Ident,
410    pub variants: Vec<TypeExpression>,
411}
412
413pub type Attributes = Vec<AttributeNode>;
414
415#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
416#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
417#[derive(Clone, Debug, PartialEq, From, IsVariant, Unwrap)]
418pub enum Expression {
419    Literal(LiteralExpression),
420    Parenthesized(ParenthesizedExpression),
421    NamedComponent(NamedComponentExpression),
422    Indexing(IndexingExpression),
423    Unary(UnaryExpression),
424    Binary(BinaryExpression),
425    FunctionCall(FunctionCallExpression),
426    TypeOrIdentifier(TypeExpression),
427}
428
429pub type ExpressionNode = Spanned<Expression>;
430
431#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
432#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
433#[derive(Clone, Copy, Debug, PartialEq, From, IsVariant, Unwrap)]
434pub enum LiteralExpression {
435    Bool(bool),
436    AbstractInt(i64),
437    AbstractFloat(f64),
438    I32(i32),
439    U32(u32),
440    F32(f32),
441    #[from(skip)]
442    F16(f32),
443    #[cfg(feature = "naga-ext")]
444    #[from(skip)]
445    I64(i64),
446    #[cfg(feature = "naga-ext")]
447    #[from(skip)]
448    U64(u64),
449    #[cfg(feature = "naga-ext")]
450    #[from(skip)]
451    F64(f64),
452}
453
454#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
455#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
456#[derive(Clone, Debug, PartialEq)]
457pub struct ParenthesizedExpression {
458    pub expression: ExpressionNode,
459}
460
461#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
462#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
463#[derive(Clone, Debug, PartialEq)]
464pub struct NamedComponentExpression {
465    pub base: ExpressionNode,
466    pub component: Ident,
467}
468
469#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
470#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
471#[derive(Clone, Debug, PartialEq)]
472pub struct IndexingExpression {
473    pub base: ExpressionNode,
474    pub index: ExpressionNode,
475}
476
477#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
478#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
479#[derive(Clone, Debug, PartialEq)]
480pub struct UnaryExpression {
481    pub operator: UnaryOperator,
482    pub operand: ExpressionNode,
483}
484
485#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
486#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
487#[derive(Clone, Debug, PartialEq)]
488pub struct BinaryExpression {
489    pub operator: BinaryOperator,
490    pub left: ExpressionNode,
491    pub right: ExpressionNode,
492}
493
494#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
495#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
496#[derive(Clone, Debug, PartialEq)]
497pub struct FunctionCall {
498    pub ty: TypeExpression,
499    pub arguments: Vec<ExpressionNode>,
500}
501
502pub type FunctionCallExpression = FunctionCall;
503
504#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
505#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
506#[derive(Clone, Debug, PartialEq)]
507pub struct TypeExpression {
508    #[cfg(feature = "imports")]
509    pub path: Option<ModulePath>,
510    pub ident: Ident,
511    pub template_args: TemplateArgs,
512}
513
514#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
515#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
516#[derive(Clone, Debug, PartialEq)]
517pub struct TemplateArg {
518    pub expression: ExpressionNode,
519}
520pub type TemplateArgs = Option<Vec<TemplateArg>>;
521
522#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
523#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
524#[derive(Clone, Debug, PartialEq, From, IsVariant, Unwrap)]
525pub enum Statement {
526    Void,
527    Compound(CompoundStatement),
528    Assignment(AssignmentStatement),
529    Increment(IncrementStatement),
530    Decrement(DecrementStatement),
531    If(IfStatement),
532    Switch(SwitchStatement),
533    Loop(LoopStatement),
534    For(ForStatement),
535    While(WhileStatement),
536    Break(BreakStatement),
537    Continue(ContinueStatement),
538    Return(ReturnStatement),
539    Discard(DiscardStatement),
540    FunctionCall(FunctionCallStatement),
541    ConstAssert(ConstAssertStatement),
542    Declaration(DeclarationStatement),
543}
544
545pub type StatementNode = Spanned<Statement>;
546
547#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
548#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
549#[derive(Clone, Debug, PartialEq, Default)]
550pub struct CompoundStatement {
551    pub attributes: Attributes,
552    pub statements: Vec<StatementNode>,
553}
554
555#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
556#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
557#[derive(Clone, Debug, PartialEq)]
558pub struct AssignmentStatement {
559    #[cfg(feature = "attributes")]
560    pub attributes: Attributes,
561    pub operator: AssignmentOperator,
562    pub lhs: ExpressionNode,
563    pub rhs: ExpressionNode,
564}
565
566#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
567#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
568#[derive(Clone, Debug, PartialEq)]
569pub struct IncrementStatement {
570    #[cfg(feature = "attributes")]
571    pub attributes: Attributes,
572    pub expression: ExpressionNode,
573}
574
575#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
576#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
577#[derive(Clone, Debug, PartialEq)]
578pub struct DecrementStatement {
579    #[cfg(feature = "attributes")]
580    pub attributes: Attributes,
581    pub expression: ExpressionNode,
582}
583
584#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
585#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
586#[derive(Clone, Debug, PartialEq)]
587pub struct IfStatement {
588    pub attributes: Attributes,
589    pub if_clause: IfClause,
590    pub else_if_clauses: Vec<ElseIfClause>,
591    pub else_clause: Option<ElseClause>,
592}
593
594#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
595#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
596#[derive(Clone, Debug, PartialEq)]
597pub struct IfClause {
598    pub expression: ExpressionNode,
599    pub body: CompoundStatement,
600}
601
602#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
603#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
604#[derive(Clone, Debug, PartialEq)]
605pub struct ElseIfClause {
606    #[cfg(feature = "attributes")]
607    pub attributes: Attributes,
608    pub expression: ExpressionNode,
609    pub body: CompoundStatement,
610}
611
612#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
613#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
614#[derive(Clone, Debug, PartialEq)]
615pub struct ElseClause {
616    #[cfg(feature = "attributes")]
617    pub attributes: Attributes,
618    pub body: CompoundStatement,
619}
620
621#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
622#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
623#[derive(Clone, Debug, PartialEq)]
624pub struct SwitchStatement {
625    pub attributes: Attributes,
626    pub expression: ExpressionNode,
627    pub body_attributes: Attributes,
628    pub clauses: Vec<SwitchClause>,
629}
630
631#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
632#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
633#[derive(Clone, Debug, PartialEq)]
634pub struct SwitchClause {
635    #[cfg(feature = "attributes")]
636    pub attributes: Attributes,
637    pub case_selectors: Vec<CaseSelector>,
638    pub body: CompoundStatement,
639}
640
641#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
642#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
643#[derive(Clone, Debug, PartialEq, From, IsVariant, Unwrap)]
644pub enum CaseSelector {
645    Default,
646    Expression(ExpressionNode),
647}
648
649#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
650#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
651#[derive(Clone, Debug, PartialEq)]
652pub struct LoopStatement {
653    pub attributes: Attributes,
654    pub body: CompoundStatement,
655    // a ContinuingStatement can only appear inside a LoopStatement body, therefore it is
656    // not part of the StatementNode enum. it appears here instead, but consider it part of
657    // body as the last statement of the CompoundStatement.
658    pub continuing: Option<ContinuingStatement>,
659}
660
661#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
662#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
663#[derive(Clone, Debug, PartialEq)]
664pub struct ContinuingStatement {
665    #[cfg(feature = "attributes")]
666    pub attributes: Attributes,
667    pub body: CompoundStatement,
668    // a BreakIfStatement can only appear inside a ContinuingStatement body, therefore it
669    // not part of the StatementNode enum. it appears here instead, but consider it part of
670    // body as the last statement of the CompoundStatement.
671    pub break_if: Option<BreakIfStatement>,
672}
673
674#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
675#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
676#[derive(Clone, Debug, PartialEq)]
677pub struct BreakIfStatement {
678    #[cfg(feature = "attributes")]
679    pub attributes: Attributes,
680    pub expression: ExpressionNode,
681}
682
683#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
684#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
685#[derive(Clone, Debug, PartialEq)]
686pub struct ForStatement {
687    pub attributes: Attributes,
688    pub initializer: Option<StatementNode>,
689    pub condition: Option<ExpressionNode>,
690    pub update: Option<StatementNode>,
691    pub body: CompoundStatement,
692}
693
694#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
695#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
696#[derive(Clone, Debug, PartialEq)]
697pub struct WhileStatement {
698    pub attributes: Attributes,
699    pub condition: ExpressionNode,
700    pub body: CompoundStatement,
701}
702
703#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
704#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
705#[derive(Clone, Debug, PartialEq)]
706pub struct BreakStatement {
707    #[cfg(feature = "attributes")]
708    pub attributes: Attributes,
709}
710
711#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
712#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
713#[derive(Clone, Debug, PartialEq)]
714pub struct ContinueStatement {
715    #[cfg(feature = "attributes")]
716    pub attributes: Attributes,
717}
718
719#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
720#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
721#[derive(Clone, Debug, PartialEq)]
722pub struct ReturnStatement {
723    #[cfg(feature = "attributes")]
724    pub attributes: Attributes,
725    pub expression: Option<ExpressionNode>,
726}
727
728#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
729#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
730#[derive(Clone, Debug, PartialEq)]
731pub struct DiscardStatement {
732    #[cfg(feature = "attributes")]
733    pub attributes: Attributes,
734}
735
736#[cfg_attr(feature = "tokrepr", derive(TokRepr))]
737#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
738#[derive(Clone, Debug, PartialEq)]
739pub struct FunctionCallStatement {
740    #[cfg(feature = "attributes")]
741    pub attributes: Attributes,
742    pub call: FunctionCall,
743}
744
745pub type ConstAssertStatement = ConstAssert;
746
747pub type DeclarationStatement = Declaration;