Feat: add compound assignment and shift operators
Compound assignment: +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= Shift: <<, >> Each compound assignment token parses at the same precedence as `=` (right-associative, lowest) and produces ExprKind::CompoundAssign. Shifts parse between additive and multiplicative precedence. GRAMMAR.ebnf and SYNTAX.md updated accordingly.
This commit is contained in:
@@ -2,8 +2,9 @@ use std::fmt;
|
||||
|
||||
use crate::{
|
||||
ast::{
|
||||
BinaryOp, Block, ElseBranch, Expr, ExprKind, FieldDef, FuncDef, Param, Program, Stmt,
|
||||
StmtKind, StructDef, StructField, TopLevelDef, TopLevelDefKind, Type, UnaryOp,
|
||||
BinaryOp, Block, CompoundAssignOp, ElseBranch, Expr, ExprKind, FieldDef, FuncDef, Param,
|
||||
Program, Stmt, StmtKind, StructDef, StructField, TopLevelDef, TopLevelDefKind, Type,
|
||||
UnaryOp,
|
||||
},
|
||||
lexer::Lexer,
|
||||
token::{Span, Token, TokenKind},
|
||||
@@ -35,9 +36,18 @@ 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),
|
||||
// Assignment and compound assignment: lowest precedence, right-associative.
|
||||
TokenKind::Eq
|
||||
| TokenKind::PlusEq
|
||||
| TokenKind::MinusEq
|
||||
| TokenKind::StarEq
|
||||
| TokenKind::SlashEq
|
||||
| TokenKind::PercentEq
|
||||
| TokenKind::AmpEq
|
||||
| TokenKind::PipeEq
|
||||
| TokenKind::CaretEq
|
||||
| TokenKind::ShlEq
|
||||
| TokenKind::ShrEq => (2, 2),
|
||||
TokenKind::Or => (10, 11),
|
||||
TokenKind::And => (20, 21),
|
||||
TokenKind::Pipe => (30, 31),
|
||||
@@ -50,6 +60,7 @@ fn infix_bp(kind: TokenKind) -> Option<(u8, u8)> {
|
||||
| TokenKind::LtEq
|
||||
| TokenKind::GtEq => (55, 56),
|
||||
TokenKind::Plus | TokenKind::Minus => (60, 61),
|
||||
TokenKind::Shl | TokenKind::Shr => (65, 66),
|
||||
TokenKind::Star | TokenKind::Slash | TokenKind::Percent => (70, 71),
|
||||
// Postfix: `.`, `[`, `(` — handled separately in parse_led, bp listed
|
||||
// here only so callers can detect them as infix/postfix operators.
|
||||
@@ -82,6 +93,22 @@ fn token_to_unary_op(kind: TokenKind) -> UnaryOp {
|
||||
}
|
||||
}
|
||||
|
||||
fn token_to_compound_assign_op(kind: TokenKind) -> Option<CompoundAssignOp> {
|
||||
match kind {
|
||||
TokenKind::PlusEq => Some(CompoundAssignOp::Add),
|
||||
TokenKind::MinusEq => Some(CompoundAssignOp::Sub),
|
||||
TokenKind::StarEq => Some(CompoundAssignOp::Mul),
|
||||
TokenKind::SlashEq => Some(CompoundAssignOp::Div),
|
||||
TokenKind::PercentEq => Some(CompoundAssignOp::Rem),
|
||||
TokenKind::AmpEq => Some(CompoundAssignOp::BitAnd),
|
||||
TokenKind::PipeEq => Some(CompoundAssignOp::BitOr),
|
||||
TokenKind::CaretEq => Some(CompoundAssignOp::BitXor),
|
||||
TokenKind::ShlEq => Some(CompoundAssignOp::Shl),
|
||||
TokenKind::ShrEq => Some(CompoundAssignOp::Shr),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn token_to_binary_op(kind: TokenKind) -> BinaryOp {
|
||||
match kind {
|
||||
TokenKind::Or => BinaryOp::Or,
|
||||
@@ -100,6 +127,8 @@ fn token_to_binary_op(kind: TokenKind) -> BinaryOp {
|
||||
TokenKind::Star => BinaryOp::Mul,
|
||||
TokenKind::Slash => BinaryOp::Div,
|
||||
TokenKind::Percent => BinaryOp::Rem,
|
||||
TokenKind::Shl => BinaryOp::Shl,
|
||||
TokenKind::Shr => BinaryOp::Shr,
|
||||
TokenKind::Eq => BinaryOp::Assign,
|
||||
_ => unreachable!("not a binary op: {:?}", kind),
|
||||
}
|
||||
@@ -588,6 +617,22 @@ impl<'src> Parser<'src> {
|
||||
)
|
||||
}
|
||||
|
||||
// Compound assignment: `lhs op= rhs`
|
||||
kind if token_to_compound_assign_op(kind).is_some() => {
|
||||
let op = token_to_compound_assign_op(kind).unwrap();
|
||||
let rhs = self.pratt(r_bp, allow_struct_lit);
|
||||
let span = lhs.span.cover(rhs.span);
|
||||
Expr::new(
|
||||
ExprKind::CompoundAssign {
|
||||
op,
|
||||
op_span: op_tok.span,
|
||||
lhs: Box::new(lhs),
|
||||
rhs: Box::new(rhs),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
// Binary operator
|
||||
kind => {
|
||||
let op = token_to_binary_op(kind);
|
||||
|
||||
Reference in New Issue
Block a user