1use std::str::FromStr;
4
5use itertools::Itertools;
6
7use crate::{error::ParseError, span::Span, syntax::*};
8
9type E = ParseError;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum DeclarationScope {
13 Module,
14 Function,
15}
16
17pub fn validate_declaration(decl: &Declaration, scope: DeclarationScope) -> Result<(), ParseError> {
18 if matches!(decl.kind, DeclarationKind::Const | DeclarationKind::Let)
20 && decl.initializer.is_none()
21 {
22 return Err(ParseError::InvalidDeclaration(
23 decl.kind,
24 "missing required initializer",
25 ));
26 }
27
28 if let DeclarationKind::Var(Some((a_s, _))) = decl.kind {
30 let initializer_disallowed = match a_s {
31 AddressSpace::Function | AddressSpace::Private => false,
32 AddressSpace::Workgroup
33 | AddressSpace::Uniform
34 | AddressSpace::Storage
35 | AddressSpace::Handle
36 | AddressSpace::Immediate => true,
37 #[cfg(feature = "naga-ext")]
38 AddressSpace::TaskPayload
39 | AddressSpace::RayPayload
40 | AddressSpace::IncomingRayPayload => true,
41 };
42 if initializer_disallowed && decl.initializer.is_some() {
43 return Err(ParseError::InvalidDeclaration(
44 decl.kind,
45 "unexpected initializer",
46 ));
47 }
48 }
49
50 match scope {
52 DeclarationScope::Module => match decl.kind {
53 DeclarationKind::Let | DeclarationKind::Var(Some((AddressSpace::Function, _))) => {
54 return Err(ParseError::InvalidDeclaration(
55 decl.kind,
56 "not allowed in module scope",
57 ));
58 }
59 DeclarationKind::Const | DeclarationKind::Override | DeclarationKind::Var(_) => {}
60 },
61 DeclarationScope::Function => match decl.kind {
62 DeclarationKind::Const
63 | DeclarationKind::Let
64 | DeclarationKind::Var(None | Some((AddressSpace::Function, _))) => {}
65 DeclarationKind::Override | DeclarationKind::Var(_) => {
66 return Err(ParseError::InvalidDeclaration(
67 decl.kind,
68 "not allowed in functions",
69 ));
70 }
71 },
72 }
73
74 Ok(())
75}
76
77pub fn fold_same_operator(
86 mut left: ExpressionNode,
87 right_chain: Vec<(BinaryOperator, ExpressionNode)>,
88) -> Result<Expression, E> {
89 if right_chain.is_empty() {
90 return Ok(left.into_inner());
91 }
92 let first_op = right_chain[0].0;
93 for (operator, right) in right_chain {
94 if operator != first_op {
95 return Err(E::MixedBinaryOperators(first_op, operator));
96 }
97 let span = left.span().extend(right.span());
98 let expr = Expression::Binary(BinaryExpression {
99 operator,
100 left,
101 right,
102 });
103 left = ExpressionNode::new(expr, span);
104 }
105 Ok(left.into_inner())
106}
107
108pub enum ParseEntryPoint {
111 TryTemplateList(Span),
112 TranslationUnit(TranslationUnit),
113 GlobalDecl(GlobalDeclaration),
114 Literal(LiteralExpression),
115 GlobalDirective(GlobalDirective),
116 Expression(Expression),
117 Statement(Statement),
118 ImportStatement(ImportStatement),
119}
120
121impl FromStr for DeclarationKind {
122 type Err = ();
123
124 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 match s {
126 "const" => Ok(Self::Const),
127 "override" => Ok(Self::Override),
128 "let" => Ok(Self::Let),
129 "var" => Ok(Self::Var(None)),
130 _ => Err(()),
131 }
132 }
133}
134
135fn one_arg(arguments: Option<Vec<ExpressionNode>>) -> Option<ExpressionNode> {
136 match arguments {
137 Some(mut args) => (args.len() == 1).then(|| args.pop().unwrap()),
138 None => None,
139 }
140}
141fn two_args(arguments: Option<Vec<ExpressionNode>>) -> Option<(ExpressionNode, ExpressionNode)> {
142 match arguments {
143 Some(args) => (args.len() == 2).then(|| args.into_iter().collect_tuple().unwrap()),
144 None => None,
145 }
146}
147fn zero_args(arguments: Option<Vec<ExpressionNode>>) -> bool {
148 arguments.is_none()
149}
150fn ident(expr: ExpressionNode) -> Option<Ident> {
151 match expr.into_inner() {
152 Expression::TypeOrIdentifier(TypeExpression {
153 path: _,
154 ident,
155 template_args: None,
156 }) => Some(ident),
157 _ => None,
158 }
159}
160
161pub(crate) fn parse_attribute(
162 name: String,
163 args: Option<Vec<ExpressionNode>>,
164) -> Result<Attribute, E> {
165 match name.as_str() {
166 "align" => match one_arg(args) {
167 Some(expr) => Ok(Attribute::Align(expr)),
168 _ => Err(E::Attribute("align", "expected 1 argument")),
169 },
170 "binding" => match one_arg(args) {
171 Some(expr) => Ok(Attribute::Binding(expr)),
172 _ => Err(E::Attribute("binding", "expected 1 argument")),
173 },
174 "blend_src" => match one_arg(args) {
175 Some(expr) => Ok(Attribute::BlendSrc(expr)),
176 _ => Err(E::Attribute("blend_src", "expected 1 argument")),
177 },
178 "builtin" => match one_arg(args) {
179 Some(expr) => match ident(expr).and_then(|id| id.name().parse().ok()) {
180 Some(b) => Ok(Attribute::Builtin(b)),
181 _ => Err(E::Attribute(
182 "builtin",
183 "the argument is not a valid built-in value name",
184 )),
185 },
186 _ => Err(E::Attribute("builtin", "expected 1 argument")),
187 },
188 "const" => match zero_args(args) {
189 true => Ok(Attribute::Const),
190 false => Err(E::Attribute("const", "expected 0 arguments")),
191 },
192 "diagnostic" => match two_args(args) {
193 Some((e1, e2)) => {
194 let severity = ident(e1).and_then(|id| id.name().parse().ok());
195 let rule = match e2.into_inner() {
196 Expression::TypeOrIdentifier(TypeExpression {
197 path: _,
198 ident,
199 template_args: None,
200 }) => Some(ident.name().to_string()),
201 Expression::NamedComponent(e) => {
202 ident(e.base).map(|id| format!("{}.{}", id.name(), e.component))
203 }
204 _ => None,
205 };
206 match (severity, rule) {
207 (Some(severity), Some(rule)) => {
208 Ok(Attribute::Diagnostic(DiagnosticAttribute {
209 severity,
210 rule,
211 }))
212 }
213 _ => Err(E::Attribute("diagnostic", "invalid arguments")),
214 }
215 }
216 _ => Err(E::Attribute("diagnostic", "expected 1 argument")),
217 },
218 "group" => match one_arg(args) {
219 Some(expr) => Ok(Attribute::Group(expr)),
220 _ => Err(E::Attribute("group", "expected 1 argument")),
221 },
222 "id" => match one_arg(args) {
223 Some(expr) => Ok(Attribute::Id(expr)),
224 _ => Err(E::Attribute("id", "expected 1 argument")),
225 },
226 "interpolate" => match args {
227 Some(v) if v.len() == 2 => {
228 let (e1, e2) = v.into_iter().collect_tuple().unwrap();
229 let ty = ident(e1).and_then(|id| id.name().parse().ok());
230 let sampling = ident(e2).and_then(|id| id.name().parse().ok());
231 match (ty, sampling) {
232 (Some(ty), Some(sampling)) => {
233 Ok(Attribute::Interpolate(InterpolateAttribute {
234 ty,
235 sampling: Some(sampling),
236 }))
237 }
238 _ => Err(E::Attribute("interpolate", "invalid arguments")),
239 }
240 }
241 Some(v) if v.len() == 1 => {
242 let e1 = v.into_iter().next().unwrap();
243 let ty = ident(e1).and_then(|id| id.name().parse().ok());
244 match ty {
245 Some(ty) => Ok(Attribute::Interpolate(InterpolateAttribute {
246 ty,
247 sampling: None,
248 })),
249 _ => Err(E::Attribute("interpolate", "invalid arguments")),
250 }
251 }
252 _ => Err(E::Attribute("interpolate", "invalid arguments")),
253 },
254
255 "invariant" => match zero_args(args) {
256 true => Ok(Attribute::Invariant),
257 false => Err(E::Attribute("invariant", "expected 0 arguments")),
258 },
259 "location" => match one_arg(args) {
260 Some(expr) => Ok(Attribute::Location(expr)),
261 _ => Err(E::Attribute("location", "expected 1 argument")),
262 },
263 "must_use" => match zero_args(args) {
264 true => Ok(Attribute::MustUse),
265 false => Err(E::Attribute("must_use", "expected 0 arguments")),
266 },
267 "size" => match one_arg(args) {
268 Some(expr) => Ok(Attribute::Size(expr)),
269 _ => Err(E::Attribute("size", "expected 1 argument")),
270 },
271 "workgroup_size" => match args {
272 Some(args) => {
273 let mut it = args.into_iter();
274 match (it.next(), it.next(), it.next(), it.next()) {
275 (Some(x), y, z, None) => {
276 Ok(Attribute::WorkgroupSize(WorkgroupSizeAttribute { x, y, z }))
277 }
278 _ => Err(E::Attribute("workgroup_size", "expected 1-3 arguments")),
279 }
280 }
281 _ => Err(E::Attribute("workgroup_size", "expected 1-3 arguments")),
282 },
283 "vertex" => match zero_args(args) {
284 true => Ok(Attribute::Vertex),
285 false => Err(E::Attribute("vertex", "expected 0 arguments")),
286 },
287 "fragment" => match zero_args(args) {
288 true => Ok(Attribute::Fragment),
289 false => Err(E::Attribute("fragment", "expected 0 arguments")),
290 },
291 "compute" => match zero_args(args) {
292 true => Ok(Attribute::Compute),
293 false => Err(E::Attribute("compute", "expected 0 arguments")),
294 },
295 #[cfg(feature = "naga-ext")]
296 "task" => match zero_args(args) {
297 true => Ok(Attribute::Task),
298 false => Err(E::Attribute("task", "expected 0 arguments")),
299 },
300 #[cfg(feature = "naga-ext")]
301 "payload" => match one_arg(args) {
302 Some(expr) => Ok(Attribute::Payload(expr)),
303 None => Err(E::Attribute("payload", "expected 1 arguments")),
304 },
305 #[cfg(feature = "naga-ext")]
306 "mesh" => match one_arg(args) {
307 Some(expr) => Ok(Attribute::Mesh(expr)),
308 None => Err(E::Attribute("mesh", "expected 1 arguments")),
309 },
310 #[cfg(feature = "naga-ext")]
311 "ray_generation" => match zero_args(args) {
312 true => Ok(Attribute::RayGeneration),
313 false => Err(E::Attribute("ray_generation", "expected 0 arguments")),
314 },
315 #[cfg(feature = "naga-ext")]
316 "any_hit" => match zero_args(args) {
317 true => Ok(Attribute::AnyHit),
318 false => Err(E::Attribute("any_hit", "expected 0 arguments")),
319 },
320 #[cfg(feature = "naga-ext")]
321 "closest_hit" => match zero_args(args) {
322 true => Ok(Attribute::ClosestHit),
323 false => Err(E::Attribute("closest_hit", "expected 0 arguments")),
324 },
325 #[cfg(feature = "naga-ext")]
326 "miss" => match zero_args(args) {
327 true => Ok(Attribute::Miss),
328 false => Err(E::Attribute("miss", "expected 0 arguments")),
329 },
330 #[cfg(feature = "naga-ext")]
331 "incoming_payload" => match one_arg(args) {
332 Some(expr) => Ok(Attribute::IncomingPayload(expr)),
333 None => Err(E::Attribute("incoming_payload", "expected 1 arguments")),
334 },
335 "if" => match one_arg(args) {
336 Some(expr) => Ok(Attribute::If(expr)),
337 None => Err(E::Attribute("if", "expected 1 argument")),
338 },
339 "elif" => match one_arg(args) {
340 Some(expr) => Ok(Attribute::Elif(expr)),
341 None => Err(E::Attribute("elif", "expected 1 argument")),
342 },
343 "else" => match zero_args(args) {
344 true => Ok(Attribute::Else),
345 false => Err(E::Attribute("else", "expected 0 arguments")),
346 },
347 #[cfg(feature = "generics")]
348 "type" => parse_attr_type(args).map(Attribute::Type),
349 #[cfg(feature = "naga-ext")]
350 "early_depth_test" => match args {
351 Some(args) => {
352 let mut it = args.into_iter();
353 match (it.next(), it.next()) {
354 (Some(expr), None) => match ident(expr).and_then(|id| id.name().parse().ok()) {
355 Some(c) => Ok(Attribute::EarlyDepthTest(Some(c))),
356 _ => Err(E::Attribute(
357 "early_depth_test",
358 "the argument must be one of `greater_equal`, `less_equal`, `unchanged`",
359 )),
360 },
361 (None, None) => Ok(Attribute::EarlyDepthTest(None)),
362 _ => Err(E::Attribute(
363 "early_depth_test",
364 "expected 0 or 1 arguments",
365 )),
366 }
367 }
368 _ => Err(E::Attribute(
369 "early_depth_test",
370 "expected 0 or 1 arguments",
371 )),
372 },
373 _ => Ok(Attribute::Custom(CustomAttribute {
374 name,
375 arguments: args,
376 })),
377 }
378}
379
380#[cfg(feature = "generics")]
382fn parse_attr_type(arguments: Option<Vec<ExpressionNode>>) -> Result<TypeConstraint, E> {
383 fn parse_rec(expr: Expression) -> Result<Vec<TypeExpression>, E> {
384 match expr {
385 Expression::TypeOrIdentifier(ty) => Ok(vec![ty]),
386 Expression::Binary(BinaryExpression {
387 operator: BinaryOperator::BitwiseOr,
388 left,
389 right,
390 }) => {
391 let ty = match right.into_inner() {
392 Expression::TypeOrIdentifier(ty) => Ok(ty),
393 _ => Err(E::Attribute(
394 "type",
395 "invalid second argument (type constraint)",
396 )),
397 }?;
398 let mut v = parse_rec(left.into_inner())?;
399 v.push(ty);
400 Ok(v)
401 }
402 _ => Err(E::Attribute(
403 "type",
404 "invalid second argument (type constraint)",
405 )),
406 }
407 }
408 match two_args(arguments) {
409 Some((e1, e2)) => ident(e1)
410 .map(|ident| {
411 parse_rec(e2.into_inner()).map(|variants| TypeConstraint { ident, variants })
412 })
413 .unwrap_or_else(|| Err(E::Attribute("type", "invalid first argument (type name)"))),
414
415 None => Err(E::Attribute("type", "expected 2 arguments")),
416 }
417}
418
419pub(crate) fn parse_var_template(
420 template_args: TemplateArgs,
421) -> Result<Option<(AddressSpace, Option<AccessMode>)>, E> {
422 match template_args {
423 Some(tplt) => {
424 let mut it = tplt.into_iter();
425 match (it.next(), it.next(), it.next()) {
426 (Some(e1), e2, None) => {
427 let addr_space = ident(e1.expression)
428 .and_then(|id| id.name().parse().ok())
429 .ok_or(E::VarTemplate("invalid address space"))?;
430 let mut access_mode = None;
431 if let Some(e2) = e2 {
432 if addr_space == AddressSpace::Storage {
433 access_mode = Some(
434 ident(e2.expression)
435 .and_then(|id| id.name().parse().ok())
436 .ok_or(E::VarTemplate("invalid access mode"))?,
437 );
438 } else {
439 return Err(E::VarTemplate(
440 "only variables with `storage` address space can have an access mode",
441 ));
442 }
443 }
444 Ok(Some((addr_space, access_mode)))
445 }
446 _ => Err(E::VarTemplate("template is empty")),
447 }
448 }
449 None => Ok(None),
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use std::str::FromStr;
456
457 use super::*;
458
459 #[test]
460 fn component_base_spans_end_before_the_component() {
461 let expression = Expression::from_str("globals.voxel_clip.count").unwrap();
462 let Expression::NamedComponent(outer) = expression else {
463 panic!("expected outer named component");
464 };
465 assert_eq!(outer.base.span(), Span::new(0..18));
466
467 let Expression::NamedComponent(inner) = outer.base.node() else {
468 panic!("expected inner named component");
469 };
470 assert_eq!(inner.base.span(), Span::new(0..7));
471 }
472}