naga/front/glsl/parser/
expressions.rs

1use alloc::{vec, vec::Vec};
2use core::num::NonZeroU32;
3
4use crate::{
5    front::glsl::{
6        ast::{FunctionCall, FunctionCallKind, HirExpr, HirExprKind},
7        context::{Context, StmtContext},
8        error::{ErrorKind, ExpectedToken},
9        parser::ParsingContext,
10        token::{Token, TokenValue},
11        Error, Frontend, Result, Span,
12    },
13    ArraySize, BinaryOperator, Handle, Literal, Type, TypeInner, UnaryOperator,
14};
15
16impl ParsingContext<'_> {
17    pub fn parse_primary(
18        &mut self,
19        frontend: &mut Frontend,
20        ctx: &mut Context,
21        stmt: &mut StmtContext,
22    ) -> Result<Handle<HirExpr>> {
23        let mut token = self.bump(frontend)?;
24
25        let literal = match token.value {
26            TokenValue::IntConstant(int) => {
27                if int.width != 32 {
28                    frontend.errors.push(Error {
29                        kind: ErrorKind::SemanticError("Unsupported non-32bit integer".into()),
30                        meta: token.meta,
31                    });
32                }
33                if int.signed {
34                    Literal::I32(int.value as i32)
35                } else {
36                    Literal::U32(int.value as u32)
37                }
38            }
39            TokenValue::FloatConstant(float) => {
40                if float.width != 32 {
41                    frontend.errors.push(Error {
42                        kind: ErrorKind::SemanticError(
43                            concat!(
44                                "Unsupported floating-point value ",
45                                "(expected single-precision floating-point number)"
46                            )
47                            .into(),
48                        ),
49                        meta: token.meta,
50                    });
51                }
52                Literal::F32(float.value)
53            }
54            TokenValue::BoolConstant(value) => Literal::Bool(value),
55            TokenValue::LeftParen => {
56                let expr = self.parse_expression(frontend, ctx, stmt)?;
57                let meta = self.expect(frontend, TokenValue::RightParen)?.meta;
58
59                token.meta.subsume(meta);
60
61                return Ok(expr);
62            }
63            _ => {
64                return Err(Error {
65                    kind: ErrorKind::InvalidToken(
66                        token.value,
67                        vec![
68                            TokenValue::LeftParen.into(),
69                            ExpectedToken::IntLiteral,
70                            ExpectedToken::FloatLiteral,
71                            ExpectedToken::BoolLiteral,
72                        ],
73                    ),
74                    meta: token.meta,
75                });
76            }
77        };
78
79        Ok(stmt.hir_exprs.append(
80            HirExpr {
81                kind: HirExprKind::Literal(literal),
82                meta: token.meta,
83            },
84            Default::default(),
85        ))
86    }
87
88    pub fn parse_function_call_args(
89        &mut self,
90        frontend: &mut Frontend,
91        ctx: &mut Context,
92        stmt: &mut StmtContext,
93        meta: &mut Span,
94    ) -> Result<Vec<Handle<HirExpr>>> {
95        let mut args = Vec::new();
96        if let Some(token) = self.bump_if(frontend, TokenValue::RightParen) {
97            meta.subsume(token.meta);
98        } else {
99            loop {
100                args.push(self.parse_assignment(frontend, ctx, stmt)?);
101
102                let token = self.bump(frontend)?;
103                match token.value {
104                    TokenValue::Comma => {}
105                    TokenValue::RightParen => {
106                        meta.subsume(token.meta);
107                        break;
108                    }
109                    _ => {
110                        return Err(Error {
111                            kind: ErrorKind::InvalidToken(
112                                token.value,
113                                vec![TokenValue::Comma.into(), TokenValue::RightParen.into()],
114                            ),
115                            meta: token.meta,
116                        });
117                    }
118                }
119            }
120        }
121
122        Ok(args)
123    }
124
125    pub fn parse_postfix(
126        &mut self,
127        frontend: &mut Frontend,
128        ctx: &mut Context,
129        stmt: &mut StmtContext,
130    ) -> Result<Handle<HirExpr>> {
131        let mut base = if self.peek_type_name(frontend) {
132            let (mut handle, mut meta) = self.parse_type_non_void(frontend, ctx)?;
133
134            self.expect(frontend, TokenValue::LeftParen)?;
135            let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;
136
137            if let TypeInner::Array {
138                size: ArraySize::Dynamic,
139                stride,
140                base,
141            } = ctx.module.types[handle].inner
142            {
143                let span = ctx.module.types.get_span(handle);
144
145                let size = u32::try_from(args.len())
146                    .ok()
147                    .and_then(NonZeroU32::new)
148                    .ok_or(Error {
149                        kind: ErrorKind::SemanticError(
150                            "There must be at least one argument".into(),
151                        ),
152                        meta,
153                    })?;
154
155                handle = ctx.module.types.insert(
156                    Type {
157                        name: None,
158                        inner: TypeInner::Array {
159                            stride,
160                            base,
161                            size: ArraySize::Constant(size),
162                        },
163                    },
164                    span,
165                )
166            }
167
168            stmt.hir_exprs.append(
169                HirExpr {
170                    kind: HirExprKind::Call(FunctionCall {
171                        kind: FunctionCallKind::TypeConstructor(handle),
172                        args,
173                    }),
174                    meta,
175                },
176                Default::default(),
177            )
178        } else if let TokenValue::Identifier(_) = self.expect_peek(frontend)?.value {
179            let (name, mut meta) = self.expect_ident(frontend)?;
180
181            let expr = if self.bump_if(frontend, TokenValue::LeftParen).is_some() {
182                let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;
183
184                let kind = match frontend.lookup_type.get(&name) {
185                    Some(ty) => FunctionCallKind::TypeConstructor(*ty),
186                    None => FunctionCallKind::Function(name),
187                };
188
189                HirExpr {
190                    kind: HirExprKind::Call(FunctionCall { kind, args }),
191                    meta,
192                }
193            } else {
194                let var = match frontend.lookup_variable(ctx, &name, meta)? {
195                    Some(var) => var,
196                    None => {
197                        return Err(Error {
198                            kind: ErrorKind::UnknownVariable(name),
199                            meta,
200                        })
201                    }
202                };
203
204                HirExpr {
205                    kind: HirExprKind::Variable(var),
206                    meta,
207                }
208            };
209
210            stmt.hir_exprs.append(expr, Default::default())
211        } else {
212            self.parse_primary(frontend, ctx, stmt)?
213        };
214
215        while let TokenValue::LeftBracket
216        | TokenValue::Dot
217        | TokenValue::Increment
218        | TokenValue::Decrement = self.expect_peek(frontend)?.value
219        {
220            let Token { value, mut meta } = self.bump(frontend)?;
221
222            match value {
223                TokenValue::LeftBracket => {
224                    let index = self.parse_expression(frontend, ctx, stmt)?;
225                    let end_meta = self.expect(frontend, TokenValue::RightBracket)?.meta;
226
227                    meta.subsume(end_meta);
228                    base = stmt.hir_exprs.append(
229                        HirExpr {
230                            kind: HirExprKind::Access { base, index },
231                            meta,
232                        },
233                        Default::default(),
234                    )
235                }
236                TokenValue::Dot => {
237                    let (field, end_meta) = self.expect_ident(frontend)?;
238
239                    if self.bump_if(frontend, TokenValue::LeftParen).is_some() {
240                        let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;
241
242                        base = stmt.hir_exprs.append(
243                            HirExpr {
244                                kind: HirExprKind::Method {
245                                    expr: base,
246                                    name: field,
247                                    args,
248                                },
249                                meta,
250                            },
251                            Default::default(),
252                        );
253                        continue;
254                    }
255
256                    meta.subsume(end_meta);
257                    base = stmt.hir_exprs.append(
258                        HirExpr {
259                            kind: HirExprKind::Select { base, field },
260                            meta,
261                        },
262                        Default::default(),
263                    )
264                }
265                TokenValue::Increment | TokenValue::Decrement => {
266                    base = stmt.hir_exprs.append(
267                        HirExpr {
268                            kind: HirExprKind::PrePostfix {
269                                op: match value {
270                                    TokenValue::Increment => BinaryOperator::Add,
271                                    _ => BinaryOperator::Subtract,
272                                },
273                                postfix: true,
274                                expr: base,
275                            },
276                            meta,
277                        },
278                        Default::default(),
279                    )
280                }
281                _ => unreachable!(),
282            }
283        }
284
285        Ok(base)
286    }
287
288    pub fn parse_unary(
289        &mut self,
290        frontend: &mut Frontend,
291        ctx: &mut Context,
292        stmt: &mut StmtContext,
293    ) -> Result<Handle<HirExpr>> {
294        Ok(match self.expect_peek(frontend)?.value {
295            TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => {
296                let Token { value, mut meta } = self.bump(frontend)?;
297
298                let expr = self.parse_unary(frontend, ctx, stmt)?;
299                let end_meta = stmt.hir_exprs[expr].meta;
300
301                let kind = match value {
302                    TokenValue::Dash => HirExprKind::Unary {
303                        op: UnaryOperator::Negate,
304                        expr,
305                    },
306                    TokenValue::Bang => HirExprKind::Unary {
307                        op: UnaryOperator::LogicalNot,
308                        expr,
309                    },
310                    TokenValue::Tilde => HirExprKind::Unary {
311                        op: UnaryOperator::BitwiseNot,
312                        expr,
313                    },
314                    _ => return Ok(expr),
315                };
316
317                meta.subsume(end_meta);
318                stmt.hir_exprs
319                    .append(HirExpr { kind, meta }, Default::default())
320            }
321            TokenValue::Increment | TokenValue::Decrement => {
322                let Token { value, meta } = self.bump(frontend)?;
323
324                let expr = self.parse_unary(frontend, ctx, stmt)?;
325
326                stmt.hir_exprs.append(
327                    HirExpr {
328                        kind: HirExprKind::PrePostfix {
329                            op: match value {
330                                TokenValue::Increment => BinaryOperator::Add,
331                                _ => BinaryOperator::Subtract,
332                            },
333                            postfix: false,
334                            expr,
335                        },
336                        meta,
337                    },
338                    Default::default(),
339                )
340            }
341            _ => self.parse_postfix(frontend, ctx, stmt)?,
342        })
343    }
344
345    pub fn parse_binary(
346        &mut self,
347        frontend: &mut Frontend,
348        ctx: &mut Context,
349        stmt: &mut StmtContext,
350        passthrough: Option<Handle<HirExpr>>,
351        min_bp: u8,
352    ) -> Result<Handle<HirExpr>> {
353        let mut left = passthrough
354            .ok_or(ErrorKind::EndOfFile /* Dummy error */)
355            .or_else(|_| self.parse_unary(frontend, ctx, stmt))?;
356        let mut meta = stmt.hir_exprs[left].meta;
357
358        while let Some((l_bp, r_bp)) = binding_power(&self.expect_peek(frontend)?.value) {
359            if l_bp < min_bp {
360                break;
361            }
362
363            let Token { value, .. } = self.bump(frontend)?;
364
365            let right = self.parse_binary(frontend, ctx, stmt, None, r_bp)?;
366            let end_meta = stmt.hir_exprs[right].meta;
367
368            meta.subsume(end_meta);
369            left = stmt.hir_exprs.append(
370                HirExpr {
371                    kind: HirExprKind::Binary {
372                        left,
373                        op: match value {
374                            TokenValue::LogicalOr => BinaryOperator::LogicalOr,
375                            TokenValue::LogicalXor => BinaryOperator::NotEqual,
376                            TokenValue::LogicalAnd => BinaryOperator::LogicalAnd,
377                            TokenValue::VerticalBar => BinaryOperator::InclusiveOr,
378                            TokenValue::Caret => BinaryOperator::ExclusiveOr,
379                            TokenValue::Ampersand => BinaryOperator::And,
380                            TokenValue::Equal => BinaryOperator::Equal,
381                            TokenValue::NotEqual => BinaryOperator::NotEqual,
382                            TokenValue::GreaterEqual => BinaryOperator::GreaterEqual,
383                            TokenValue::LessEqual => BinaryOperator::LessEqual,
384                            TokenValue::LeftAngle => BinaryOperator::Less,
385                            TokenValue::RightAngle => BinaryOperator::Greater,
386                            TokenValue::LeftShift => BinaryOperator::ShiftLeft,
387                            TokenValue::RightShift => BinaryOperator::ShiftRight,
388                            TokenValue::Plus => BinaryOperator::Add,
389                            TokenValue::Dash => BinaryOperator::Subtract,
390                            TokenValue::Star => BinaryOperator::Multiply,
391                            TokenValue::Slash => BinaryOperator::Divide,
392                            TokenValue::Percent => BinaryOperator::Modulo,
393                            _ => unreachable!(),
394                        },
395                        right,
396                    },
397                    meta,
398                },
399                Default::default(),
400            )
401        }
402
403        Ok(left)
404    }
405
406    pub fn parse_conditional(
407        &mut self,
408        frontend: &mut Frontend,
409        ctx: &mut Context,
410        stmt: &mut StmtContext,
411        passthrough: Option<Handle<HirExpr>>,
412    ) -> Result<Handle<HirExpr>> {
413        let mut condition = self.parse_binary(frontend, ctx, stmt, passthrough, 0)?;
414        let mut meta = stmt.hir_exprs[condition].meta;
415
416        if self.bump_if(frontend, TokenValue::Question).is_some() {
417            let accept = self.parse_expression(frontend, ctx, stmt)?;
418            self.expect(frontend, TokenValue::Colon)?;
419            let reject = self.parse_assignment(frontend, ctx, stmt)?;
420            let end_meta = stmt.hir_exprs[reject].meta;
421
422            meta.subsume(end_meta);
423            condition = stmt.hir_exprs.append(
424                HirExpr {
425                    kind: HirExprKind::Conditional {
426                        condition,
427                        accept,
428                        reject,
429                    },
430                    meta,
431                },
432                Default::default(),
433            )
434        }
435
436        Ok(condition)
437    }
438
439    pub fn parse_assignment(
440        &mut self,
441        frontend: &mut Frontend,
442        ctx: &mut Context,
443        stmt: &mut StmtContext,
444    ) -> Result<Handle<HirExpr>> {
445        let tgt = self.parse_unary(frontend, ctx, stmt)?;
446        let mut meta = stmt.hir_exprs[tgt].meta;
447
448        Ok(match self.expect_peek(frontend)?.value {
449            TokenValue::Assign => {
450                self.bump(frontend)?;
451                let value = self.parse_assignment(frontend, ctx, stmt)?;
452                let end_meta = stmt.hir_exprs[value].meta;
453
454                meta.subsume(end_meta);
455                stmt.hir_exprs.append(
456                    HirExpr {
457                        kind: HirExprKind::Assign { tgt, value },
458                        meta,
459                    },
460                    Default::default(),
461                )
462            }
463            TokenValue::OrAssign
464            | TokenValue::AndAssign
465            | TokenValue::AddAssign
466            | TokenValue::DivAssign
467            | TokenValue::ModAssign
468            | TokenValue::SubAssign
469            | TokenValue::MulAssign
470            | TokenValue::LeftShiftAssign
471            | TokenValue::RightShiftAssign
472            | TokenValue::XorAssign => {
473                let token = self.bump(frontend)?;
474                let right = self.parse_assignment(frontend, ctx, stmt)?;
475                let end_meta = stmt.hir_exprs[right].meta;
476
477                meta.subsume(end_meta);
478                let value = stmt.hir_exprs.append(
479                    HirExpr {
480                        meta,
481                        kind: HirExprKind::Binary {
482                            left: tgt,
483                            op: match token.value {
484                                TokenValue::OrAssign => BinaryOperator::InclusiveOr,
485                                TokenValue::AndAssign => BinaryOperator::And,
486                                TokenValue::AddAssign => BinaryOperator::Add,
487                                TokenValue::DivAssign => BinaryOperator::Divide,
488                                TokenValue::ModAssign => BinaryOperator::Modulo,
489                                TokenValue::SubAssign => BinaryOperator::Subtract,
490                                TokenValue::MulAssign => BinaryOperator::Multiply,
491                                TokenValue::LeftShiftAssign => BinaryOperator::ShiftLeft,
492                                TokenValue::RightShiftAssign => BinaryOperator::ShiftRight,
493                                TokenValue::XorAssign => BinaryOperator::ExclusiveOr,
494                                _ => unreachable!(),
495                            },
496                            right,
497                        },
498                    },
499                    Default::default(),
500                );
501
502                stmt.hir_exprs.append(
503                    HirExpr {
504                        kind: HirExprKind::Assign { tgt, value },
505                        meta,
506                    },
507                    Default::default(),
508                )
509            }
510            _ => self.parse_conditional(frontend, ctx, stmt, Some(tgt))?,
511        })
512    }
513
514    pub fn parse_expression(
515        &mut self,
516        frontend: &mut Frontend,
517        ctx: &mut Context,
518        stmt: &mut StmtContext,
519    ) -> Result<Handle<HirExpr>> {
520        let mut expr = self.parse_assignment(frontend, ctx, stmt)?;
521
522        while let TokenValue::Comma = self.expect_peek(frontend)?.value {
523            self.bump(frontend)?;
524            expr = self.parse_assignment(frontend, ctx, stmt)?;
525        }
526
527        Ok(expr)
528    }
529}
530
531const fn binding_power(value: &TokenValue) -> Option<(u8, u8)> {
532    Some(match *value {
533        TokenValue::LogicalOr => (1, 2),
534        TokenValue::LogicalXor => (3, 4),
535        TokenValue::LogicalAnd => (5, 6),
536        TokenValue::VerticalBar => (7, 8),
537        TokenValue::Caret => (9, 10),
538        TokenValue::Ampersand => (11, 12),
539        TokenValue::Equal | TokenValue::NotEqual => (13, 14),
540        TokenValue::GreaterEqual
541        | TokenValue::LessEqual
542        | TokenValue::LeftAngle
543        | TokenValue::RightAngle => (15, 16),
544        TokenValue::LeftShift | TokenValue::RightShift => (17, 18),
545        TokenValue::Plus | TokenValue::Dash => (19, 20),
546        TokenValue::Star | TokenValue::Slash | TokenValue::Percent => (21, 22),
547        _ => return None,
548    })
549}