1use 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; 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; 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}