Fix: add assignment as right-associative expression

`=` was missing from the Pratt table, causing `a = b;` to fail with
"expected `;`, found `=`". Assignment is now BinaryOp::Assign with
binding power (2, 2) — lowest precedence, right-associative — so
`a = b = c` parses as `a = (b = c)`.
This commit is contained in:
2026-03-10 18:10:35 +01:00
parent 546dc119d0
commit 1a4e464d5e
4 changed files with 100 additions and 24 deletions

View File

@@ -33,6 +33,8 @@ pub enum BinaryOp {
Mul, // `*`
Div, // `/`
Rem, // `%`
// Assignment (lowest precedence, right-associative)
Assign, // `=`
}
// ── Types ──────────────────────────────────────────────────────────────────────

View File

@@ -35,6 +35,9 @@ impl fmt::Display for ParseError {
fn infix_bp(kind: TokenKind) -> Option<(u8, u8)> {
let bp = match kind {
// Assignment: lowest precedence, right-associative (left_bp == right_bp).
// `a = b = c` → `a = (b = c)`.
TokenKind::Eq => (2, 2),
TokenKind::Or => (10, 11),
TokenKind::And => (20, 21),
TokenKind::Pipe => (30, 31),
@@ -97,6 +100,7 @@ fn token_to_binary_op(kind: TokenKind) -> BinaryOp {
TokenKind::Star => BinaryOp::Mul,
TokenKind::Slash => BinaryOp::Div,
TokenKind::Percent => BinaryOp::Rem,
TokenKind::Eq => BinaryOp::Assign,
_ => unreachable!("not a binary op: {:?}", kind),
}
}
@@ -1048,6 +1052,55 @@ mod tests {
}
}
#[test]
fn assignment_expr() {
let expr = parse("a = b");
assert!(matches!(
expr.kind,
ExprKind::Binary {
op: BinaryOp::Assign,
..
}
));
}
#[test]
fn assignment_right_associative() {
// `a = b = c` → `a = (b = c)`
let expr = parse("a = b = c");
match &expr.kind {
ExprKind::Binary {
op: BinaryOp::Assign,
rhs,
..
} => {
assert!(matches!(
rhs.kind,
ExprKind::Binary {
op: BinaryOp::Assign,
..
}
));
}
_ => panic!("expected assignment"),
}
}
#[test]
fn assignment_stmt() {
let s = stmt("a = b + 1;");
match &s.kind {
StmtKind::Expr(e) => assert!(matches!(
e.kind,
ExprKind::Binary {
op: BinaryOp::Assign,
..
}
)),
_ => panic!("expected expr stmt with assignment"),
}
}
#[test]
fn deref_and_addrof() {
assert!(matches!(