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