diff --git a/compiler/lexer.v b/compiler/lexer.v index 83e9a51..731c877 100644 --- a/compiler/lexer.v +++ b/compiler/lexer.v @@ -27,6 +27,10 @@ pub enum TokKind { gt ge assign + plus_eq + minus_eq + star_eq + slash_eq dotdot dotdotdot kw_fn @@ -172,18 +176,34 @@ fn (mut l Lexer) next() !Tok { } `+` { l.advance() + if l.peek() == `=` { + l.advance() + return Tok{ kind: .plus_eq, lit: '+=', line: line } + } return Tok{ kind: .plus, lit: '+', line: line } } `-` { l.advance() + if l.peek() == `=` { + l.advance() + return Tok{ kind: .minus_eq, lit: '-=', line: line } + } return Tok{ kind: .minus, lit: '-', line: line } } `*` { l.advance() + if l.peek() == `=` { + l.advance() + return Tok{ kind: .star_eq, lit: '*=', line: line } + } return Tok{ kind: .star, lit: '*', line: line } } `/` { l.advance() + if l.peek() == `=` { + l.advance() + return Tok{ kind: .slash_eq, lit: '/=', line: line } + } return Tok{ kind: .slash, lit: '/', line: line } } `%` { diff --git a/compiler/parser.v b/compiler/parser.v index ab73caf..ce1085d 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -435,26 +435,56 @@ fn (mut p Parser) parse_stmt() !Stmt { } else { e = p.parse_postfix_tail(Expr{ kind: .ident, name: t.lit, line: t.line })! } - if p.cur().kind == .assign { - // assignment to an ident, an index, or a field - p.advance() - rhs := p.parse_expr()! - match e.kind { - .ident { - return Stmt{ kind: .assign_stmt, target: e.name, expr: rhs, line: t.line } - } - .index { - return Stmt{ kind: .index_assign, base: *e.left, idx: *e.right, expr: rhs, line: t.line } - } - .field { - return Stmt{ kind: .field_assign, base: *e.left, target: e.name, expr: rhs, line: t.line } - } - else { - return error('cannot assign to this expression (line ${t.line})') - } + if p.cur().kind == .assign { + // assignment to an ident, an index, or a field + p.advance() + rhs := p.parse_expr()! + match e.kind { + .ident { + return Stmt{ kind: .assign_stmt, target: e.name, expr: rhs, line: t.line } + } + .index { + return Stmt{ kind: .index_assign, base: *e.left, idx: *e.right, expr: rhs, line: t.line } + } + .field { + return Stmt{ kind: .field_assign, base: *e.left, target: e.name, expr: rhs, line: t.line } + } + else { + return error('cannot assign to this expression (line ${t.line})') } } - return Stmt{ kind: .expr_stmt, expr: e, line: t.line } + } + // compound assignment: x += expr, a[i] += expr, a.b += expr + if p.cur().kind == .plus_eq || p.cur().kind == .minus_eq || p.cur().kind == .star_eq || p.cur().kind == .slash_eq { + op_tok := p.advance() + rhs := p.parse_expr()! + bin_op := match op_tok.kind { + .plus_eq { TokKind.plus } + .minus_eq { TokKind.minus } + .star_eq { TokKind.star } + .slash_eq { TokKind.slash } + else { return error('unexpected compound operator (line ${t.line})') } + } + // desugar: LHS op= RHS → LHS = LHS op RHS + match e.kind { + .ident { + full_rhs := bin_node(bin_op, e, rhs, op_tok.line) + return Stmt{ kind: .assign_stmt, target: e.name, expr: full_rhs, line: t.line } + } + .index { + full_rhs := bin_node(bin_op, e, rhs, op_tok.line) + return Stmt{ kind: .index_assign, base: *e.left, idx: *e.right, expr: full_rhs, line: t.line } + } + .field { + full_rhs := bin_node(bin_op, e, rhs, op_tok.line) + return Stmt{ kind: .field_assign, base: *e.left, target: e.name, expr: full_rhs, line: t.line } + } + else { + return error('cannot use compound assignment on this expression (line ${t.line})') + } + } + } + return Stmt{ kind: .expr_stmt, expr: e, line: t.line } } .kw_print, .kw_println { p.advance() diff --git a/examples/compound_assign.vr b/examples/compound_assign.vr new file mode 100644 index 0000000..5d98986 --- /dev/null +++ b/examples/compound_assign.vr @@ -0,0 +1,34 @@ +// compound assignment operators: +=, -=, *=, /= + +fn main() { + let x = 10 + println(x) + + x += 5 + println(x) // 15 + + x -= 3 + println(x) // 12 + + x *= 2 + println(x) // 24 + + x /= 4 + println(x) // 6 + + // compound assignment on arrays (field access for structs/maps) + let m = { "count": 0 } + m["count"] += 1 + println(m["count"]) // 1 + + m["count"] += 10 + println(m["count"]) // 11 + + // compound assignment on array index + let arr = [10, 20, 30] + arr[1] += 5 + println(arr[1]) // 25 + + arr[2] *= 3 + println(arr[2]) // 90 +}