wgsl_parse/
syntax.rs

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