diff --git a/naga/src/front/wgsl/parse/mod.rs b/naga/src/front/wgsl/parse/mod.rs index 8057a03ab1..edf592788f 100644 --- a/naga/src/front/wgsl/parse/mod.rs +++ b/naga/src/front/wgsl/parse/mod.rs @@ -151,6 +151,7 @@ enum Rule { Directive, GenericExpr, EnclosedExpr, + LhsExpr, } struct ParsedAttribute { @@ -937,6 +938,53 @@ impl Parser { }) } + /// Parse a `lhs_expression`. + /// + /// LHS expressions only support the `&` and `*` operators and + /// the `[]` and `.` postfix selectors. + fn lhs_expression<'a>( + &mut self, + lexer: &mut Lexer<'a>, + ctx: &mut ExpressionContext<'a, '_, '_>, + ) -> Result>, Error<'a>> { + self.track_recursion(|this| { + this.push_rule_span(Rule::LhsExpr, lexer); + let start = lexer.start_byte_offset(); + let expr = match lexer.peek() { + (Token::Operation('*'), _) => { + let _ = lexer.next(); + let expr = this.lhs_expression(lexer, ctx)?; + let expr = ast::Expression::Deref(expr); + let span = this.peek_rule_span(lexer); + ctx.expressions.append(expr, span) + } + (Token::Operation('&'), _) => { + let _ = lexer.next(); + let expr = this.lhs_expression(lexer, ctx)?; + let expr = ast::Expression::AddrOf(expr); + let span = this.peek_rule_span(lexer); + ctx.expressions.append(expr, span) + } + (Token::Operation('('), _) => { + let _ = lexer.next(); + let primary_expr = this.lhs_expression(lexer, ctx)?; + lexer.expect(Token::Paren(')'))?; + this.postfix(start, lexer, ctx, primary_expr)? + } + (Token::Word(word), span) => { + let _ = lexer.next(); + let ident = this.ident_expr(word, span, ctx); + let primary_expr = ctx.expressions.append(ast::Expression::Ident(ident), span); + this.postfix(start, lexer, ctx, primary_expr)? + } + _ => this.singular_expression(lexer, ctx)?, + }; + + this.pop_rule_span(lexer); + Ok(expr) + }) + } + /// Parse a `singular_expression`. fn singular_expression<'a>( &mut self, @@ -1781,7 +1829,7 @@ impl Parser { block: &mut ast::Block<'a>, ) -> Result<(), Error<'a>> { let span_start = lexer.start_byte_offset(); - let target = self.general_expression(lexer, ctx)?; + let target = self.lhs_expression(lexer, ctx)?; self.assignment_op_and_rhs(lexer, ctx, block, target, span_start) } diff --git a/naga/src/front/wgsl/tests.rs b/naga/src/front/wgsl/tests.rs index 1746df56fa..c0a24fbdf7 100644 --- a/naga/src/front/wgsl/tests.rs +++ b/naga/src/front/wgsl/tests.rs @@ -420,6 +420,42 @@ fn parse_expressions() { }").unwrap(); } +#[test] +fn parse_assignment_statements() { + parse_str( + " + struct Foo { x: i32 }; + + fn foo() { + var x: u32 = 0u; + x++; + x--; + x = 1u; + x += 1u; + var v: vec2 = vec2(1.0, 1.0); + v[0] += 1.0; + (v)[0] += 1.0; + var s: Foo = Foo(0); + s.x -= 1; + (s.x) -= 1; + (s).x -= 1; + _ = 5u; + }", + ) + .unwrap(); + + let error = parse_str( + "fn foo() { + x|x++; + }", + ) + .unwrap_err(); + assert_eq!( + error.message(), + "expected assignment or increment/decrement, found \"|\"", + ); +} + #[test] fn binary_expression_mixed_scalar_and_vector_operands() { for (operand, expect_splat) in [ diff --git a/naga/tests/wgsl_errors.rs b/naga/tests/wgsl_errors.rs index 6c40081583..7d459dd7ad 100644 --- a/naga/tests/wgsl_errors.rs +++ b/naga/tests/wgsl_errors.rs @@ -1759,11 +1759,11 @@ fn binary_statement() { 3 + 5; } ", - r###"error: expected assignment or increment/decrement, found ";" - ┌─ wgsl:3:18 + r###"error: expected assignment or increment/decrement, found "+" + ┌─ wgsl:3:15 │ 3 │ 3 + 5; - │ ^ expected assignment or increment/decrement + │ ^ expected assignment or increment/decrement "###, ); @@ -1777,11 +1777,11 @@ fn assign_to_expr() { 3 + 5 = 10; } ", - r###"error: invalid left-hand side of assignment - ┌─ wgsl:3:13 + r###"error: expected assignment or increment/decrement, found "+" + ┌─ wgsl:3:15 │ 3 │ 3 + 5 = 10; - │ ^^^^^ cannot assign to this expression + │ ^ expected assignment or increment/decrement "###, );