1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
//! A hand-rolled, zero-copy recursive-descent parser for WebGPU shading
//! language, written with [Gramatika](::gramatika).
//!
//! # Parsing a source file
//!
//! ```
//! use wgsl_parser::{Parse, ParseResult, ParseStream, ParseStreamer, SyntaxTree};
//!
//! # const INPUT: &str = include_str!("../test-files/mesh.wgsl");
//! // const INPUT: &str = include_str!("path/to/some/shader.wgsl");
//!
//! let mut parser = ParseStream::from(INPUT);
//! let tree = parser.parse::<SyntaxTree>();
//! assert!(tree.is_ok());
//!
//! let ParseResult {
//! source,
//! tokens,
//! comments,
//! errors,
//! } = parser.into_inner();
//!
//! assert_eq!(source.as_str(), INPUT);
//! ```
//!
//! # Tokenizing a source file without doing a full parse
//!
//! ```
//! use wgsl_parser::{gramatika::Lexer as _, Lexer};
//!
//! # const INPUT: &str = include_str!("../test-files/mesh.wgsl");
//! // const INPUT: &str = include_str!("path/to/some/shader.wgsl");
//!
//! let mut lexer = Lexer::new(INPUT.into());
//! let _tokens = lexer.scan();
//! ```
//!
//! # Syntax tree representation
//!
//! A [`SyntaxTree`] contains a vector of [`Decl`]s representing the top-level
//! syntax types defined by the [WGSL grammar], e.g.:
//!
//! * `Decl::Var(VarDecl { .. })`
//! ```wgsl
//! @group(1) @binding(2)
//! var<uniform> uniforms: Uniforms;
//! ```
//!
//! * `Decl::Const(VarDecl { .. })`
//! ```wgsl
//! const FOO: u32 = 1u;
//! ```
//!
//! * `Decl::Struct(StructDecl { .. })`
//! ```wgsl
//! struct Foo {
//! foo: mat3x4<f32>,
//! bar: vec2<u32>,
//! baz: array<mat4x4<f32>, 256u>,
//! }
//! ```
//!
//! * `Decl::Function(FunctionDecl { .. })`
//! ```wgsl
//! fn sum(a: f32, b: f32) -> f32 {
//! return a + b;
//! }
//! ```
//!
//! The structures wrapped by those declarations can contain sub-declarations,
//! e.g.:
//!
//! * `Decl::Field(FieldDecl { .. })` inside of a [`StructDecl`]
//! * `Decl::Param(ParamDecl { .. })` inside of a [`FunctionDecl`]
//!
//! The `body` of a [`FunctionDecl`] contains a vector of [`Stmt`]s.
//!
//! [`Stmt`] is an enum in a form similar to [`Decl`], with variants indicating
//! the kind of statement it represents, each wrapping an inner structure that
//! describes the syntax in further detail, often recursively, e.g.:
//!
//! ```text
//! Stmt::If(IfStmt {
//! ..
//! else_branch: Some(ElseStmt {
//! ..
//! body: Arc(Stmt::Block(BlockStmt {
//! ..
//! stmts: Arc<[Stmt]>,
//! })),
//! }),
//! })
//! ```
//!
//! Finally, [`Expr`] is the "lowest" type of syntax node in the tree, taking
//! the same general form as [`Decl`] and [`Stmt`] above.
//!
//! # Inspecting a syntax tree
//!
//! Each node of the syntax tree derives a bespoke [`Debug`] implementation,
//! which prints the tree in a format that's a sort of cross between Lisp (a
//! format commonly used for representing syntax trees) and Rust syntax.
//!
//! That format looks like this:
//!
//! ```wgsl
//! max(4, 2) // The expression represented by the tree below
//! ```
//! ```text
//! (Expr::Primary (PrimaryExpr
//! expr: (Expr::FnCall (FnCallExpr
//! ident: (IdentExpr::Leaf `max` (Function (1:1...1:4))),
//! arguments: (ArgumentList
//! brace_open: `(` (Brace (1:4...1:5)),
//! arguments: [
//! (Expr::Primary (PrimaryExpr
//! expr: (Expr::Literal `4` (IntLiteral (1:5...1:6))),
//! )),
//! (Expr::Primary (PrimaryExpr
//! expr: (Expr::Literal `2` (IntLiteral (1:8...1:9))),
//! )),
//! ],
//! brace_close: `)` (Brace (1:9...1:10)),
//! ),
//! )),
//! ))
//! ```
//!
//! ### Traversing a syntax tree
//!
//! The package exports a [`Visitor`] trait which can be implemented to
//! efficiently traverse the tree. [`Visitor`] defines a `visit_*` method for
//! each type of syntax represented by the tree. `visit_*` methods for nodes
//! that contain child nodes must return either [`FlowControl::Continue`] to
//! traverse their children, or [`FlowControl::Break`] to stop traversing the
//! current branch.
//!
//! The default [`Visitor`] implementation returns [`FlowControl::Continue`] for
//! every node, so you only need to actually implement the `visit_*` methods
//! that your particular use case calls for:
//!
//! ```
//! # fn main() -> gramatika::Result<()> {
//! use std::collections::HashMap;
//!
//! use wgsl_parser::{
//! decl::VarDecl,
//! expr::{IdentExpr, NamespacedIdent},
//! gramatika::{ParseStreamer, Substr, Token as _},
//! traversal::{FlowControl, Visitor, Walk},
//! ParseStream, SyntaxTree,
//! };
//!
//! // Note: Not actually a robust implementation of a reference-counter,
//! // but good enough for this toy example
//! #[derive(Default)]
//! struct ReferenceCounter {
//! counts: HashMap<Substr, usize>,
//! }
//!
//! impl Visitor for ReferenceCounter {
//! fn visit_var_decl(&mut self, decl: &VarDecl) -> FlowControl {
//! // Create an entry in the map for the identifier being declared
//! self.counts.insert(decl.name.lexeme(), 0);
//!
//! // The expression being assigned to the new variable could include
//! // references to other variables, so we'll call `expr.walk(self)` to
//! // make sure our visitor sees those identifiers as well.
//! if let Some(ref expr) = decl.assignment {
//! expr.walk(self);
//! }
//!
//! // We could have returned `FlowControl::Continue` _instead_ of
//! // explicitly stepping into the assignment expression above, but
//! // since we don't really care about any other child nodes of the
//! // `VarDecl`, this lets us skip some extra work.
//! FlowControl::Break
//! }
//!
//! fn visit_ident_expr(&mut self, mut expr: &IdentExpr) {
//! // Find the count in our map for this identifier and increment it
//! if let IdentExpr::Leaf(name) = expr {
//! if let Some(count) = self.counts.get_mut(&name.lexeme()) {
//! *count += 1;
//! }
//! }
//! }
//! }
//!
//! let input = r#"
//! fn main() {
//! var a: i32 = 4;
//! let b = a;
//! let c = 2;
//!
//! do_something(a, c);
//! }
//! "#;
//!
//! let tree = ParseStream::from(input).parse::<SyntaxTree>()?;
//! let mut ref_counter = ReferenceCounter::default();
//! tree.walk(&mut ref_counter);
//!
//! assert_eq!(ref_counter.counts["a"], 2);
//! assert_eq!(ref_counter.counts["b"], 0);
//! assert_eq!(ref_counter.counts["c"], 1);
//! # Ok(())
//! # }
//! ```
//!
//! [WGSL grammar]: https://www.w3.org/TR/WGSL/
//! [`StructDecl`]: crate::decl::StructDecl
//! [`FunctionDecl`]: crate::decl::FunctionDecl
//! [`Stmt`]: crate::stmt::Stmt
//! [`Expr`]: crate::expr::Expr
//! [`Debug`]: std::fmt::Debug
//! [`Visitor`]: crate::traversal::Visitor
//! [`FlowControl::Continue`]: crate::traversal::FlowControl
//! [`FlowControl::Break`]: crate::traversal::FlowControl
pub extern crate gramatika;
use Decl;
pub use ;
use ErrorRecoveringParseStream;
pub use ;
pub use Text;
pub use ;
/// The syntax tree for a WGSL program.