diff --git a/crates/oq3_parser/src/grammar/expressions.rs b/crates/oq3_parser/src/grammar/expressions.rs index 54c0a75..825fd23 100644 --- a/crates/oq3_parser/src/grammar/expressions.rs +++ b/crates/oq3_parser/src/grammar/expressions.rs @@ -223,13 +223,11 @@ fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind, Associativity) { T![>] => (5, T![>], Left), T![=] if p.at(T![=>]) => NOT_AN_OP, T![=] if p.at(T![==]) => (5, T![==], Left), - // r-a had 1 as the bp here. But this attempts to parse - // `x + y = 3`; as `(x + y) = 3;` which is probably not what the user meant. - // Putting 12 as the bp instead of 1 parses this as - // `x + (y = 3)`. In OQ3, this is still illegal, but the user will get a more - // informative error message. That an assignment statement is not allowed here. - // This may have unintended consequences and we will need to replace the 12 with 1. - T![=] => (12, T![=], Right), + // Assignment has lower precedence than every value-producing binary + // operator, so `x = y ^ z` keeps the complete expression on the RHS. + // `expr_bp` separately checks that the completed LHS is an identifier + // or indexed identifier, rejecting forms such as `(x + y) = z`. + T![=] => (1, T![=], Right), T![<] if p.at(T![<=]) => (5, T![<=], Left), T![<] if p.at(T![<<=]) => (1, T![<<=], Right), T![<] if p.at(T![<<]) => (9, T![<<], Left), diff --git a/crates/oq3_syntax/src/tests.rs b/crates/oq3_syntax/src/tests.rs index 9bb6ac8..4282864 100644 --- a/crates/oq3_syntax/src/tests.rs +++ b/crates/oq3_syntax/src/tests.rs @@ -325,3 +325,22 @@ z + int[32](x); let parse = SourceFile::parse(code); assert_eq!(parse.errors.len(), 0); } + +#[test] +fn parse_assignment_rhs_test() { + let code = r##" +bit a; +bit b; +bit c; +a = b ^ c; + "##; + let parse = SourceFile::parse(code); + assert!(parse.ok().is_ok()); +} + +#[test] +fn parse_assignment_invalid_lhs_test() { + let parse = SourceFile::parse("x + y = 3;"); + assert_eq!(parse.errors.len(), 1); + assert_eq!(parse.errors[0].message(), "Illegal LHS in assignment"); +} diff --git a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__measure.qasm-parse.snap b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__measure.qasm-parse.snap index 69283ff..c183538 100644 --- a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__measure.qasm-parse.snap +++ b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__invalid__statements__measure.qasm-parse.snap @@ -7,7 +7,7 @@ expect-parse: Diag --- parser --- ok: false panicked: false -errors: 15 +errors: 12 --- ast --- SOURCE_FILE@0..234: measure $0, $1; @@ -39,10 +39,9 @@ HARDWARE_QUBIT@34..36: $0 ERROR@36..37: , EXPR_STMT@38..41: $1; HARDWARE_QUBIT@38..40: $1 -EXPR_STMT@42..59: a = measure $0 -> -BIN_EXPR@42..59: a = measure $0 -> -ASSIGNMENT_STMT@42..56: a = measure $0 +ASSIGNMENT_STMT@42..59: a = measure $0 -> IDENTIFIER@42..43: a +BIN_EXPR@46..59: measure $0 -> MEASURE_EXPRESSION@46..56: measure $0 HARDWARE_QUBIT@54..56: $0 ERROR@58..59: > @@ -77,17 +76,15 @@ SCALAR_TYPE@126..132: bit[1] DESIGNATOR@129..132: [1] LITERAL@130..131: 1 NAME@133..134: a -EXPR_STMT@181..200: a = 2 * measure $0; -BIN_EXPR@181..199: a = 2 * measure $0 -ASSIGNMENT_STMT@181..186: a = 2 +ASSIGNMENT_STMT@181..200: a = 2 * measure $0; IDENTIFIER@181..182: a +BIN_EXPR@185..199: 2 * measure $0 LITERAL@185..186: 2 MEASURE_EXPRESSION@189..199: measure $0 HARDWARE_QUBIT@197..199: $0 -EXPR_STMT@201..233: a = (measure $0) + (measure $1); -BIN_EXPR@201..232: a = (measure $0) + (measure $1) -ASSIGNMENT_STMT@201..217: a = (measure $0) +ASSIGNMENT_STMT@201..233: a = (measure $0) + (measure $1); IDENTIFIER@201..202: a +BIN_EXPR@205..232: (measure $0) + (measure $1) PAREN_EXPR@205..217: (measure $0) MEASURE_EXPRESSION@206..216: measure $0 HARDWARE_QUBIT@214..216: $0 diff --git a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__assignment.qasm-parse.snap b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__assignment.qasm-parse.snap index 66ff10f..d9daec2 100644 --- a/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__assignment.qasm-parse.snap +++ b/crates/pipeline-tests/tests/snapshots/runner__tests__snippets__reference__assignment__assignment.qasm-parse.snap @@ -7,7 +7,7 @@ expect-parse: Todo --- parser --- ok: false panicked: false -errors: 3 +errors: 2 --- ast --- SOURCE_FILE@0..161: bit[2] a; @@ -103,9 +103,8 @@ IDENTIFIER@143..144: q INDEX_OPERATOR@144..147: [0] EXPRESSION_LIST@145..146: 0 LITERAL@145..146: 0 -EXPR_STMT@149..160: b = a == 0; -BIN_EXPR@149..159: b = a == 0 -ASSIGNMENT_STMT@149..154: b = a +ASSIGNMENT_STMT@149..160: b = a == 0; IDENTIFIER@149..150: b +BIN_EXPR@153..159: a == 0 IDENTIFIER@153..154: a LITERAL@158..159: 0