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(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#[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}