wgsl_parser/
lib.rs

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