wgsl_parser/
common.rs

1//! A module for syntax nodes that can appear in many different parts of a
2//! program, like attributes and type annotations.
3
4use std::sync::Arc;
5
6use gramatika::{Parse, ParseStreamer, Span, Spanned, SpannedError, Token as _};
7
8use crate::{
9	expr::{Expr, IdentExpr, IdentExprBuilder, NamespacedIdentBuilder},
10	parser::ErrorRecoveringParseStream,
11	token::{brace, operator, punct},
12	ParseStream, Token, TokenKind,
13};
14
15#[derive(Clone, DebugLisp)]
16pub struct AttributeList {
17	pub attributes: Arc<[Attribute]>,
18}
19
20#[derive(Clone, DebugLisp)]
21pub struct Attribute {
22	pub at_sign: Token,
23	pub name: Token,
24	pub params: Option<ArgumentList>,
25}
26
27#[derive(Clone, DebugLisp)]
28pub struct TypeDecl {
29	pub annotator: Option<Token>,
30	pub attributes: Option<AttributeList>,
31	pub name: IdentExpr,
32	pub child_ty: Option<Arc<TypeDecl>>,
33	pub storage_class: Option<Token>,
34	pub access_mode: Option<Token>,
35	pub element_count: Option<Token>,
36}
37
38#[derive(Clone, DebugLisp)]
39pub struct ArgumentList {
40	pub brace_open: Token,
41	pub arguments: Arc<[Expr]>,
42	pub brace_close: Token,
43}
44
45impl Spanned for AttributeList {
46	fn span(&self) -> Span {
47		match self.attributes.len() {
48			0 => Span::default(),
49			1 => self.attributes.first().unwrap().span(),
50			_ => self
51				.attributes
52				.first()
53				.unwrap()
54				.span()
55				.through(self.attributes.last().unwrap().span()),
56		}
57	}
58}
59
60impl Parse for AttributeList {
61	type Stream = ParseStream;
62
63	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
64		let attributes = input.parse_seq(|input| input.check(punct!["@"]));
65
66		Ok(Self {
67			attributes: attributes.into(),
68		})
69	}
70}
71
72impl Spanned for Attribute {
73	fn span(&self) -> Span {
74		if let Some(ref params) = self.params {
75			self.at_sign.span().through(params.span())
76		} else {
77			self.at_sign.span().through(self.name.span())
78		}
79	}
80}
81
82impl Parse for Attribute {
83	type Stream = ParseStream;
84
85	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
86		let at_sign = input.consume(punct!["@"])?;
87		let name = input.consume_as(TokenKind::Ident, Token::attribute)?;
88		let params = if input.check(brace!["("]) {
89			Some(input.parse()?)
90		} else {
91			None
92		};
93
94		Ok(Self {
95			at_sign,
96			name,
97			params,
98		})
99	}
100}
101
102#[derive(Default)]
103struct TypeDeclBuilder {
104	attributes: Option<AttributeList>,
105	annotator: Option<Token>,
106	name: Option<IdentExpr>,
107	child_ty: Option<Arc<TypeDecl>>,
108	storage_class: Option<Token>,
109	access_mode: Option<Token>,
110	element_count: Option<Token>,
111}
112
113impl TypeDeclBuilder {
114	fn new() -> Self {
115		Self::default()
116	}
117	fn attributes(&mut self, attributes: AttributeList) -> &mut Self {
118		self.attributes = Some(attributes);
119		self
120	}
121	fn annotator(&mut self, colon: Token) -> &mut Self {
122		self.annotator = Some(colon);
123		self
124	}
125	fn name(&mut self, name: IdentExpr) -> &mut Self {
126		self.name = Some(name);
127		self
128	}
129	fn child_ty(&mut self, child_ty: TypeDecl) -> &mut Self {
130		self.child_ty = Some(Arc::new(child_ty));
131		self
132	}
133	fn storage_class(&mut self, storage_class: Token) -> &mut Self {
134		self.storage_class = Some(storage_class);
135		self
136	}
137	fn access_mode(&mut self, access_mode: Token) -> &mut Self {
138		self.access_mode = Some(access_mode);
139		self
140	}
141	fn element_count(&mut self, element_count: Token) -> &mut Self {
142		self.element_count = Some(element_count);
143		self
144	}
145	fn build(self) -> TypeDecl {
146		TypeDecl {
147			annotator: self.annotator,
148			attributes: self.attributes,
149			name: self.name.expect("`name` field is required!"),
150			child_ty: self.child_ty,
151			storage_class: self.storage_class,
152			access_mode: self.access_mode,
153			element_count: self.element_count,
154		}
155	}
156}
157
158impl Spanned for TypeDecl {
159	fn span(&self) -> Span {
160		let first = self
161			.annotator
162			.as_ref()
163			.map(|token| token.span())
164			.or_else(|| self.attributes.as_ref().map(|attr| attr.span()))
165			.unwrap_or_else(|| self.name.span());
166
167		let last = self
168			.access_mode
169			.as_ref()
170			.map(|token| token.span())
171			.or_else(|| self.storage_class.as_ref().map(|token| token.span()))
172			.or_else(|| {
173				self.element_count.as_ref().map(|token| {
174					let mut child_span = token.span();
175					child_span.end.character += 1; // Account for the `>`
176					child_span
177				})
178			})
179			.or_else(|| {
180				self.child_ty.as_ref().map(|token| {
181					let mut child_span = token.span();
182					child_span.end.character += 1; // Account for the `>`
183					child_span
184				})
185			})
186			.unwrap_or_else(|| self.name.span());
187
188		first.through(last)
189	}
190}
191
192impl Parse for TypeDecl {
193	type Stream = ParseStream;
194
195	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
196		use TokenKind::*;
197		let mut builder = TypeDeclBuilder::new();
198
199		if input.check(punct![:]) || input.check(operator![->]) {
200			builder.annotator(input.next().unwrap());
201		}
202		if input.check(punct!["@"]) {
203			builder.attributes(input.parse()?);
204		}
205		if input.check_kind(TokenKind::Type) {
206			let name = input.next().unwrap();
207
208			builder.name(IdentExpr::Leaf(name.clone()));
209
210			if input.check(operator![<]) {
211				input.consume(operator![<])?;
212
213				while !input.check(operator![>]) && !input.check(operator![>>]) {
214					match input.peek() {
215						Some(token) => match token.as_matchable() {
216							(Type | Ident, _, _) => {
217								builder.child_ty(input.parse()?);
218							}
219							(Keyword, "function" | "private" | "workgroup" | "uniform" | "storage", _) => {
220								builder.storage_class(input.next().unwrap());
221							}
222							(Keyword, "read" | "write" | "read_write", _) => {
223								builder.access_mode(input.next().unwrap());
224							}
225							(Punct, ",", _) => {
226								input.discard();
227							},
228							(IntLiteral, _, _)
229								if matches!(name.lexeme().as_str(), "array" | "binding_array") =>
230							{
231								builder.element_count(input.next().unwrap());
232							}
233							(_, _, span) => {
234								return Err(SpannedError {
235									message: "Expected type, storage class, access mode, texel format, or element count"
236										.into(),
237									source: input.source(),
238									span: Some(span),
239								})
240							}
241						}
242						None => {
243							return Err(SpannedError {
244								message: "Unexpected end of input".into(),
245								source: input.source(),
246								span: input.prev().map(|token| token.span()),
247							})
248						}
249					}
250				}
251
252				if input.check(operator![>>]) {
253					input.split_next(1, (Token::operator, Token::operator))?;
254				} else {
255					input.consume(operator![>])?;
256				}
257			}
258		} else {
259			let mut ident = input.parse::<IdentExprBuilder>()?;
260			let mut expr = &mut ident;
261			while let IdentExprBuilder::Namespaced(NamespacedIdentBuilder { ident, .. }) = expr {
262				expr = ident.as_mut();
263			}
264			let IdentExprBuilder::Leaf(name) = expr else {
265				unreachable!();
266			};
267			*name = input.upgrade_last(TokenKind::Ident, Token::struct_)?;
268
269			builder.name(ident.build());
270		}
271
272		Ok(builder.build())
273	}
274}
275
276impl Parse for ArgumentList {
277	type Stream = ParseStream;
278
279	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
280		let brace_open = input.consume(brace!["("])?;
281		let arguments = input.parse_seq_separated(punct![,], |input| !input.check(brace![")"]))?;
282		let brace_close = input.consume(brace![")"])?;
283
284		Ok(Self {
285			brace_open,
286			arguments: arguments.into(),
287			brace_close,
288		})
289	}
290}
291
292impl Spanned for ArgumentList {
293	fn span(&self) -> Span {
294		self.brace_open.span().through(self.brace_close.span())
295	}
296}