wgsl_parser/
stmt.rs

1//! A module for the syntax nodes representing WGSL statements.
2
3#![allow(unused_variables)]
4
5use std::sync::Arc;
6
7use gramatika::{Parse, ParseStreamer, Spanned, SpannedError, Token as _};
8
9use crate::{
10	decl::VarDecl,
11	expr::{Expr, IdentExpr},
12	parser::ErrorRecoveringParseStream,
13	token::{brace, keyword, punct},
14	ParseStream, Span, Token, TokenKind,
15};
16
17#[allow(clippy::large_enum_variant)] // TODO
18#[derive(Clone, DebugLisp)]
19pub enum Stmt {
20	Block(BlockStmt),
21	Return(ReturnStmt),
22	If(IfStmt),
23	Switch(SwitchStmt),
24	Loop(LoopStmt),
25	Continuing(ContinuingStmt),
26	For(ForStmt),
27	Var(VarDecl),
28	Break(KeywordStmt),
29	Continue(KeywordStmt),
30	Discard(KeywordStmt),
31	Fallthrough(KeywordStmt),
32	Expr(ExprStmt),
33	Empty(Token),
34}
35
36#[derive(Clone, DebugLisp)]
37pub struct BlockStmt {
38	pub brace_open: Token,
39	pub stmts: Arc<[Stmt]>,
40	pub brace_close: Token,
41}
42
43#[derive(Clone, DebugLisp)]
44pub struct ReturnStmt {
45	pub keyword: Token,
46	pub value: Option<Expr>,
47	pub semicolon: Token,
48}
49
50#[derive(Clone, DebugLisp)]
51pub struct IfStmt {
52	pub keyword: Token,
53	pub condition: Expr,
54	pub then_branch: BlockStmt,
55	pub else_branch: Option<ElseStmt>,
56}
57
58#[derive(Clone, DebugLisp)]
59pub struct ElseStmt {
60	pub keyword: Token,
61	pub body: Arc<Stmt>,
62}
63
64#[derive(Clone, DebugLisp)]
65pub struct SwitchStmt {
66	pub keyword: Token,
67	pub subject: Expr,
68	pub body: SwitchBody,
69}
70
71#[derive(Clone, DebugLisp)]
72pub struct SwitchBody {
73	pub brace_open: Token,
74	pub cases: Arc<[CaseStmt]>,
75	pub brace_close: Token,
76}
77
78#[derive(Clone, DebugLisp)]
79pub struct CaseStmt {
80	pub keyword: Token,
81	pub selectors: Arc<[Expr]>,
82	pub body: BlockStmt,
83}
84
85#[derive(Clone, DebugLisp)]
86pub struct LoopStmt {
87	pub keyword: Token,
88	pub body: BlockStmt,
89}
90
91#[derive(Clone, DebugLisp)]
92pub struct ContinuingStmt {
93	pub keyword: Token,
94	pub body: BlockStmt,
95}
96
97#[derive(Clone, DebugLisp)]
98pub struct ForStmt {
99	pub keyword: Token,
100	pub initializer: Option<Arc<Stmt>>,
101	pub condition: Option<Arc<Stmt>>,
102	pub increment: Option<Expr>,
103	pub body: BlockStmt,
104}
105
106#[derive(Clone, DebugLisp)]
107pub struct KeywordStmt {
108	pub keyword: Token,
109	pub semicolon: Token,
110}
111
112#[derive(Clone, DebugLisp)]
113pub struct ExprStmt {
114	pub expr: Expr,
115	pub semicolon: Token,
116}
117
118// -- Parse impl -------------------------------------------------------------------------
119
120impl Parse for Stmt {
121	type Stream = ParseStream;
122
123	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
124		use TokenKind::*;
125
126		match input.peek() {
127			#[rustfmt::skip]
128			Some(token) => match token.as_matchable() {
129				(Keyword, "return", _)      => Ok(Stmt::Return(input.parse()?)),
130				(Keyword, "if", _)          => Ok(Stmt::If(input.parse()?)),
131				(Keyword, "switch", _)      => Ok(Stmt::Switch(input.parse()?)),
132				(Keyword, "loop", _)        => Ok(Stmt::Loop(input.parse()?)),
133				(Keyword, "continuing", _)  => Ok(Stmt::Continuing(input.parse()?)),
134				(Keyword, "for", _)         => Ok(Stmt::For(input.parse()?)),
135				(Keyword, "const" |"var" | "let", _) => Ok(Stmt::Var(input.parse()?)),
136				(Keyword, "break", _)       => Ok(Stmt::Break(input.parse()?)),
137				(Keyword, "continue", _)    => Ok(Stmt::Continue(input.parse()?)),
138				(Keyword, "discard", _)     => Ok(Stmt::Discard(input.parse()?)),
139				(Keyword, "fallthrough", _) => Ok(Stmt::Fallthrough(input.parse()?)),
140				(Brace, "{", _)             => Ok(Stmt::Block(input.parse()?)),
141				(Punct, ";", _)             => Ok(Stmt::Empty(input.next().unwrap())),
142				_ => Ok(Stmt::Expr(input.parse()?)),
143			},
144			None => Err(SpannedError {
145				message: "Unexpected end of input".into(),
146				source: input.source(),
147				span: input.prev().map(|token| token.span()),
148			}),
149		}
150	}
151}
152
153impl Spanned for Stmt {
154	fn span(&self) -> Span {
155		use Stmt::*;
156
157		match self {
158			Block(inner) => inner.span(),
159			Return(inner) => inner.span(),
160			If(inner) => inner.span(),
161			Switch(inner) => inner.span(),
162			Loop(inner) => inner.span(),
163			Continuing(inner) => inner.span(),
164			For(inner) => inner.span(),
165			Var(inner) => inner.span(),
166			Break(inner) => inner.span(),
167			Continue(inner) => inner.span(),
168			Discard(inner) => inner.span(),
169			Fallthrough(inner) => inner.span(),
170			Expr(inner) => inner.span(),
171			Empty(inner) => inner.span(),
172		}
173	}
174}
175
176impl Parse for BlockStmt {
177	type Stream = ParseStream;
178
179	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
180		let brace_open = input.consume(brace!["{"])?;
181		let stmts = input.parse_seq(|input| !input.check(brace!["}"]));
182		let brace_close = input.consume(brace!["}"])?;
183
184		Ok(Self {
185			brace_open,
186			stmts: stmts.into(),
187			brace_close,
188		})
189	}
190}
191
192impl Spanned for BlockStmt {
193	fn span(&self) -> Span {
194		self.brace_open.span().through(self.brace_close.span())
195	}
196}
197
198impl Parse for ReturnStmt {
199	type Stream = ParseStream;
200
201	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
202		let keyword = input.consume(keyword![return])?;
203
204		if input.check(punct![;]) {
205			let semicolon = input.consume(punct![;])?;
206
207			Ok(Self {
208				keyword,
209				value: None,
210				semicolon,
211			})
212		} else {
213			let value = input.parse::<Expr>()?;
214			let semicolon = input.consume(punct![;])?;
215
216			Ok(Self {
217				keyword,
218				value: Some(value),
219				semicolon,
220			})
221		}
222	}
223}
224
225impl Spanned for ReturnStmt {
226	fn span(&self) -> Span {
227		self.keyword.span().through(self.semicolon.span())
228	}
229}
230
231impl Parse for IfStmt {
232	type Stream = ParseStream;
233
234	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
235		let keyword = input.consume_kind(TokenKind::Keyword)?;
236		let condition = input.parse::<Expr>()?;
237		let then_branch = input.parse::<BlockStmt>()?;
238
239		let else_branch = if input.check(keyword![else]) {
240			Some(input.parse::<ElseStmt>()?)
241		} else {
242			None
243		};
244
245		Ok(Self {
246			keyword,
247			condition,
248			then_branch,
249			else_branch,
250		})
251	}
252}
253
254impl Spanned for IfStmt {
255	fn span(&self) -> Span {
256		let end_span = self
257			.else_branch
258			.as_ref()
259			.map(|stmt| stmt.span())
260			.unwrap_or_else(|| self.then_branch.span());
261
262		self.keyword.span().through(end_span)
263	}
264}
265
266impl Parse for ElseStmt {
267	type Stream = ParseStream;
268
269	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
270		let keyword = input.consume(keyword![else])?;
271		let body = Arc::new(input.parse::<Stmt>()?);
272
273		Ok(Self { keyword, body })
274	}
275}
276
277impl Spanned for ElseStmt {
278	fn span(&self) -> Span {
279		self.keyword.span().through(self.body.span())
280	}
281}
282
283impl Parse for SwitchStmt {
284	type Stream = ParseStream;
285
286	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
287		let keyword = input.consume(keyword![switch])?;
288		let subject = input.parse::<Expr>()?;
289		let body = input.parse::<SwitchBody>()?;
290
291		Ok(Self {
292			keyword,
293			subject,
294			body,
295		})
296	}
297}
298
299impl Spanned for SwitchStmt {
300	fn span(&self) -> Span {
301		self.keyword.span().through(self.body.span())
302	}
303}
304
305impl Parse for SwitchBody {
306	type Stream = ParseStream;
307
308	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
309		use TokenKind::*;
310
311		let brace_open = input.consume(brace!["{"])?;
312
313		let mut cases = vec![];
314
315		while !input.check(brace!["}"]) {
316			match input.peek() {
317				Some(token) => match token.as_matchable() {
318					(Keyword, "case" | "default", _) => cases.push(input.parse::<CaseStmt>()?),
319					(_, _, span) => {
320						return Err(SpannedError {
321							message: "Expected `case` or `default`".into(),
322							span: Some(span),
323							source: input.source(),
324						})
325					}
326				},
327				None => {
328					return Err(SpannedError {
329						message: "Unexpected end of input".into(),
330						source: input.source(),
331						span: input.prev().map(|token| token.span()),
332					})
333				}
334			};
335		}
336
337		let brace_close = input.consume(brace!["}"])?;
338
339		Ok(Self {
340			brace_open,
341			cases: cases.into(),
342			brace_close,
343		})
344	}
345}
346
347impl Spanned for SwitchBody {
348	fn span(&self) -> Span {
349		self.brace_open.span().through(self.brace_close.span())
350	}
351}
352
353impl Parse for CaseStmt {
354	type Stream = ParseStream;
355
356	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
357		use TokenKind::*;
358
359		let keyword = match input.next() {
360			Some(token) => match token.as_matchable() {
361				(Keyword, "case" | "default", _) => token,
362				(_, _, span) => {
363					return Err(SpannedError {
364						message: "Expected `case` or `default`".into(),
365						source: input.source(),
366						span: Some(span),
367					});
368				}
369			},
370			None => {
371				return Err(SpannedError {
372					message: "Unexpected end of input".into(),
373					source: input.source(),
374					span: input.prev().map(|token| token.span()),
375				});
376			}
377		};
378
379		let mut selectors = vec![];
380		while !input.check(punct![:]) && !input.check(brace!["{"]) {
381			if input.check(keyword![default]) {
382				selectors.push(Expr::Ident(IdentExpr::Leaf(input.next().unwrap())));
383			} else {
384				selectors.push(input.parse()?);
385			}
386
387			if !input.check(punct![:]) && !input.check(brace!["{"]) {
388				input.consume(punct![,])?;
389			}
390		}
391
392		if input.check(punct![:]) {
393			input.consume(punct![:])?;
394		}
395
396		let body = input.parse::<BlockStmt>()?;
397
398		Ok(Self {
399			keyword,
400			selectors: selectors.into(),
401			body,
402		})
403	}
404}
405
406impl Spanned for CaseStmt {
407	fn span(&self) -> Span {
408		self.keyword.span().through(self.body.span())
409	}
410}
411
412impl Parse for LoopStmt {
413	type Stream = ParseStream;
414
415	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
416		let keyword = input.consume(keyword![loop])?;
417		let body = input.parse::<BlockStmt>()?;
418
419		Ok(Self { keyword, body })
420	}
421}
422
423impl Spanned for LoopStmt {
424	fn span(&self) -> Span {
425		self.keyword.span().through(self.body.span())
426	}
427}
428
429impl Parse for ContinuingStmt {
430	type Stream = ParseStream;
431
432	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
433		let keyword = input.consume(keyword![continuing])?;
434		let body = input.parse::<BlockStmt>()?;
435
436		Ok(Self { keyword, body })
437	}
438}
439
440impl Spanned for ContinuingStmt {
441	fn span(&self) -> Span {
442		self.keyword.span().through(self.body.span())
443	}
444}
445
446impl Parse for ForStmt {
447	type Stream = ParseStream;
448
449	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
450		let keyword = input.consume(keyword![for])?;
451		input.consume(brace!["("])?;
452
453		let mut initializer = None;
454		let mut condition = None;
455		let mut increment = None;
456
457		let mut semis = 0u8;
458
459		while !input.check(brace![")"]) {
460			match input.peek() {
461				Some(token) => match semis {
462					0 => {
463						initializer = Some(Arc::new(input.parse::<Stmt>()?));
464						semis += 1;
465					}
466					1 => {
467						condition = Some(Arc::new(input.parse::<Stmt>()?));
468						semis += 1;
469					}
470					2 => increment = Some(input.parse::<Expr>()?),
471					_ => {
472						return Err(SpannedError {
473							message: "Expected `)`".into(),
474							span: Some(token.span()),
475							source: input.source(),
476						})
477					}
478				},
479				None => {
480					return Err(SpannedError {
481						message: "Unexpected end of input".into(),
482						source: input.source(),
483						span: input.prev().map(|token| token.span()),
484					})
485				}
486			};
487		}
488
489		input.consume(brace![")"])?;
490		let body = input.parse::<BlockStmt>()?;
491
492		Ok(Self {
493			keyword,
494			initializer,
495			condition,
496			increment,
497			body,
498		})
499	}
500}
501
502impl Spanned for ForStmt {
503	fn span(&self) -> Span {
504		self.keyword.span().through(self.body.span())
505	}
506}
507
508impl Parse for KeywordStmt {
509	type Stream = ParseStream;
510
511	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
512		let keyword = input.consume_kind(TokenKind::Keyword)?;
513		let semicolon = input.consume(punct![;])?;
514
515		Ok(Self { keyword, semicolon })
516	}
517}
518
519impl Spanned for KeywordStmt {
520	fn span(&self) -> Span {
521		self.keyword.span().through(self.semicolon.span())
522	}
523}
524
525impl Parse for ExprStmt {
526	type Stream = ParseStream;
527
528	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
529		let expr = input.parse::<Expr>()?;
530		let semicolon = input.consume(punct![;])?;
531
532		Ok(Self { expr, semicolon })
533	}
534}
535
536impl Spanned for ExprStmt {
537	fn span(&self) -> Span {
538		self.expr.span().through(self.semicolon.span())
539	}
540}