wgsl_parse/
parser_support.rs

1//! support functions to be injected in the lalrpop parser.
2
3use 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(crate) enum Component {
16    Named(Ident),
17    Index(ExpressionNode),
18}
19
20pub(crate) fn apply_components(
21    expr: Expression,
22    span: Span,
23    components: Vec<Spanned<Component>>,
24) -> Expression {
25    components.into_iter().fold(expr, |base, comp| {
26        let span = span.extend(comp.span());
27        let base = Spanned::new(base, span);
28        match comp.into_inner() {
29            Component::Named(component) => {
30                Expression::NamedComponent(NamedComponentExpression { base, component })
31            }
32            Component::Index(index) => Expression::Indexing(IndexingExpression { base, index }),
33        }
34    })
35}
36
37impl FromStr for DeclarationKind {
38    type Err = ();
39
40    fn from_str(s: &str) -> Result<Self, Self::Err> {
41        match s {
42            "const" => Ok(Self::Const),
43            "override" => Ok(Self::Override),
44            "let" => Ok(Self::Let),
45            "var" => Ok(Self::Var(None)),
46            _ => Err(()),
47        }
48    }
49}
50
51fn one_arg(arguments: Option<Vec<ExpressionNode>>) -> Option<ExpressionNode> {
52    match arguments {
53        Some(mut args) => (args.len() == 1).then(|| args.pop().unwrap()),
54        None => None,
55    }
56}
57fn two_args(arguments: Option<Vec<ExpressionNode>>) -> Option<(ExpressionNode, ExpressionNode)> {
58    match arguments {
59        Some(args) => (args.len() == 2).then(|| args.into_iter().collect_tuple().unwrap()),
60        None => None,
61    }
62}
63fn zero_args(arguments: Option<Vec<ExpressionNode>>) -> bool {
64    arguments.is_none()
65}
66fn ident(expr: ExpressionNode) -> Option<Ident> {
67    match expr.into_inner() {
68        Expression::TypeOrIdentifier(TypeExpression {
69            #[cfg(feature = "imports")]
70                path: _,
71            ident,
72            template_args: None,
73        }) => Some(ident),
74        _ => None,
75    }
76}
77
78pub(crate) fn parse_attribute(
79    name: String,
80    args: Option<Vec<ExpressionNode>>,
81) -> Result<Attribute, E> {
82    match name.as_str() {
83        "align" => match one_arg(args) {
84            Some(expr) => Ok(Attribute::Align(expr)),
85            _ => Err(E::Attribute("align", "expected 1 argument")),
86        },
87        "binding" => match one_arg(args) {
88            Some(expr) => Ok(Attribute::Binding(expr)),
89            _ => Err(E::Attribute("binding", "expected 1 argument")),
90        },
91        "blend_src" => match one_arg(args) {
92            Some(expr) => Ok(Attribute::BlendSrc(expr)),
93            _ => Err(E::Attribute("blend_src", "expected 1 argument")),
94        },
95        "builtin" => match one_arg(args) {
96            Some(expr) => match ident(expr).and_then(|id| id.name().parse().ok()) {
97                Some(b) => Ok(Attribute::Builtin(b)),
98                _ => Err(E::Attribute(
99                    "builtin",
100                    "the argument is not a valid built-in value name",
101                )),
102            },
103            _ => Err(E::Attribute("builtin", "expected 1 argument")),
104        },
105        "const" => match zero_args(args) {
106            true => Ok(Attribute::Const),
107            false => Err(E::Attribute("const", "expected 0 arguments")),
108        },
109        "diagnostic" => match two_args(args) {
110            Some((e1, e2)) => {
111                let severity = ident(e1).and_then(|id| id.name().parse().ok());
112                let rule = match e2.into_inner() {
113                    Expression::TypeOrIdentifier(TypeExpression {
114                        #[cfg(feature = "imports")]
115                            path: _,
116                        ident,
117                        template_args: None,
118                    }) => Some(ident.name().to_string()),
119                    Expression::NamedComponent(e) => {
120                        ident(e.base).map(|id| format!("{}.{}", id.name(), e.component))
121                    }
122                    _ => None,
123                };
124                match (severity, rule) {
125                    (Some(severity), Some(rule)) => {
126                        Ok(Attribute::Diagnostic(DiagnosticAttribute {
127                            severity,
128                            rule,
129                        }))
130                    }
131                    _ => Err(E::Attribute("diagnostic", "invalid arguments")),
132                }
133            }
134            _ => Err(E::Attribute("diagnostic", "expected 1 argument")),
135        },
136        "group" => match one_arg(args) {
137            Some(expr) => Ok(Attribute::Group(expr)),
138            _ => Err(E::Attribute("group", "expected 1 argument")),
139        },
140        "id" => match one_arg(args) {
141            Some(expr) => Ok(Attribute::Id(expr)),
142            _ => Err(E::Attribute("id", "expected 1 argument")),
143        },
144        "interpolate" => match args {
145            Some(v) if v.len() == 2 => {
146                let (e1, e2) = v.into_iter().collect_tuple().unwrap();
147                let ty = ident(e1).and_then(|id| id.name().parse().ok());
148                let sampling = ident(e2).and_then(|id| id.name().parse().ok());
149                match (ty, sampling) {
150                    (Some(ty), Some(sampling)) => {
151                        Ok(Attribute::Interpolate(InterpolateAttribute {
152                            ty,
153                            sampling: Some(sampling),
154                        }))
155                    }
156                    _ => Err(E::Attribute("interpolate", "invalid arguments")),
157                }
158            }
159            Some(v) if v.len() == 1 => {
160                let e1 = v.into_iter().next().unwrap();
161                let ty = ident(e1).and_then(|id| id.name().parse().ok());
162                match ty {
163                    Some(ty) => Ok(Attribute::Interpolate(InterpolateAttribute {
164                        ty,
165                        sampling: None,
166                    })),
167                    _ => Err(E::Attribute("interpolate", "invalid arguments")),
168                }
169            }
170            _ => Err(E::Attribute("interpolate", "invalid arguments")),
171        },
172
173        "invariant" => match zero_args(args) {
174            true => Ok(Attribute::Invariant),
175            false => Err(E::Attribute("invariant", "expected 0 arguments")),
176        },
177        "location" => match one_arg(args) {
178            Some(expr) => Ok(Attribute::Location(expr)),
179            _ => Err(E::Attribute("location", "expected 1 argument")),
180        },
181        "must_use" => match zero_args(args) {
182            true => Ok(Attribute::MustUse),
183            false => Err(E::Attribute("must_use", "expected 0 arguments")),
184        },
185        "size" => match one_arg(args) {
186            Some(expr) => Ok(Attribute::Size(expr)),
187            _ => Err(E::Attribute("size", "expected 1 argument")),
188        },
189        "workgroup_size" => match args {
190            Some(args) => {
191                let mut it = args.into_iter();
192                match (it.next(), it.next(), it.next(), it.next()) {
193                    (Some(x), y, z, None) => {
194                        Ok(Attribute::WorkgroupSize(WorkgroupSizeAttribute { x, y, z }))
195                    }
196                    _ => Err(E::Attribute("workgroup_size", "expected 1-3 arguments")),
197                }
198            }
199            _ => Err(E::Attribute("workgroup_size", "expected 1-3 arguments")),
200        },
201        "vertex" => match zero_args(args) {
202            true => Ok(Attribute::Vertex),
203            false => Err(E::Attribute("vertex", "expected 0 arguments")),
204        },
205        "fragment" => match zero_args(args) {
206            true => Ok(Attribute::Fragment),
207            false => Err(E::Attribute("fragment", "expected 0 arguments")),
208        },
209        "compute" => match zero_args(args) {
210            true => Ok(Attribute::Compute),
211            false => Err(E::Attribute("compute", "expected 0 arguments")),
212        },
213        #[cfg(feature = "imports")]
214        "publish" => Ok(Attribute::Publish),
215        #[cfg(feature = "condcomp")]
216        "if" => match one_arg(args) {
217            Some(expr) => Ok(Attribute::If(expr)),
218            None => Err(E::Attribute("if", "expected 1 argument")),
219        },
220        #[cfg(feature = "condcomp")]
221        "elif" => match one_arg(args) {
222            Some(expr) => Ok(Attribute::Elif(expr)),
223            None => Err(E::Attribute("elif", "expected 1 argument")),
224        },
225        #[cfg(feature = "condcomp")]
226        "else" => match zero_args(args) {
227            true => Ok(Attribute::Else),
228            false => Err(E::Attribute("else", "expected 0 arguments")),
229        },
230        #[cfg(feature = "generics")]
231        "type" => parse_attr_type(args).map(Attribute::Type),
232        #[cfg(feature = "naga-ext")]
233        "early_depth_test" => match args {
234            Some(args) => {
235                let mut it = args.into_iter();
236                match (it.next(), it.next()) {
237                    (Some(expr), None) => match ident(expr).and_then(|id| id.name().parse().ok()) {
238                        Some(c) => Ok(Attribute::EarlyDepthTest(Some(c))),
239                        _ => Err(E::Attribute(
240                            "early_depth_test",
241                            "the argument must be one of `greater_equal`, `less_equal`, `unchanged`",
242                        )),
243                    },
244                    (None, None) => Ok(Attribute::EarlyDepthTest(None)),
245                    _ => Err(E::Attribute(
246                        "early_depth_test",
247                        "expected 0 or 1 arguments",
248                    )),
249                }
250            }
251            _ => Err(E::Attribute(
252                "early_depth_test",
253                "expected 0 or 1 arguments",
254            )),
255        },
256        _ => Ok(Attribute::Custom(CustomAttribute {
257            name,
258            arguments: args,
259        })),
260    }
261}
262
263// format: @type(T, foo | bar | baz)
264#[cfg(feature = "generics")]
265fn parse_attr_type(arguments: Option<Vec<ExpressionNode>>) -> Result<TypeConstraint, E> {
266    fn parse_rec(expr: Expression) -> Result<Vec<TypeExpression>, E> {
267        match expr {
268            Expression::TypeOrIdentifier(ty) => Ok(vec![ty]),
269            Expression::Binary(BinaryExpression {
270                operator: BinaryOperator::BitwiseOr,
271                left,
272                right,
273            }) => {
274                let ty = match right.into_inner() {
275                    Expression::TypeOrIdentifier(ty) => Ok(ty),
276                    _ => Err(E::Attribute(
277                        "type",
278                        "invalid second argument (type constraint)",
279                    )),
280                }?;
281                let mut v = parse_rec(left.into_inner())?;
282                v.push(ty);
283                Ok(v)
284            }
285            _ => Err(E::Attribute(
286                "type",
287                "invalid second argument (type constraint)",
288            )),
289        }
290    }
291    match two_args(arguments) {
292        Some((e1, e2)) => ident(e1)
293            .map(|ident| {
294                parse_rec(e2.into_inner()).map(|variants| TypeConstraint { ident, variants })
295            })
296            .unwrap_or_else(|| Err(E::Attribute("type", "invalid first argument (type name)"))),
297
298        None => Err(E::Attribute("type", "expected 2 arguments")),
299    }
300}
301
302pub(crate) fn parse_var_template(
303    template_args: TemplateArgs,
304) -> Result<Option<(AddressSpace, Option<AccessMode>)>, E> {
305    match template_args {
306        Some(tplt) => {
307            let mut it = tplt.into_iter();
308            match (it.next(), it.next(), it.next()) {
309                (Some(e1), e2, None) => {
310                    let addr_space = ident(e1.expression)
311                        .and_then(|id| id.name().parse().ok())
312                        .ok_or(E::VarTemplate("invalid address space"))?;
313                    let mut access_mode = None;
314                    if let Some(e2) = e2 {
315                        if addr_space == AddressSpace::Storage {
316                            access_mode = Some(
317                                ident(e2.expression)
318                                    .and_then(|id| id.name().parse().ok())
319                                    .ok_or(E::VarTemplate("invalid access mode"))?,
320                            );
321                        } else {
322                            return Err(E::VarTemplate(
323                                "only variables with `storage` address space can have an access mode",
324                            ));
325                        }
326                    }
327                    Ok(Some((addr_space, access_mode)))
328                }
329                _ => Err(E::VarTemplate("template is empty")),
330            }
331        }
332        None => Ok(None),
333    }
334}