feat: 复合赋值 += -= *= /= + CHANGELOG 更新
- lexer: 4 个复合赋值 Token (解析时优先于单字符) - parser: desugar x+=expr → x=x+expr(零 sema/codegen 改动) - 新增集成测试 09_compound_assign.l (15 12 24 6) - CHANGELOG 新增 v0.2.0 条目
This commit is contained in:
@@ -1,5 +1,25 @@
|
||||
# Changelog
|
||||
|
||||
## 0.2.0 (2026-06-05)
|
||||
|
||||
### Added
|
||||
- `let mut` 可变变量 + 赋值语句 (`x = expr;`)
|
||||
- 可变性检查:对不可变变量赋值报编译错误
|
||||
- 字符串类型 `str` + 双引号字面量 (`"Hello"`)
|
||||
- 字符串拼接 `str + str` (malloc + strlen + memcpy 运行时实现)
|
||||
- `print_str` 内置函数 (委托 printf)
|
||||
- 复合赋值运算符:`+=` `-=` `*=` `/=`
|
||||
- 集成测试:`06_mut_while.l` (while 循环修改变量)、`07_hello_str.l` (字符串输出)、`08_str_concat.l` (字符串拼接)
|
||||
|
||||
### Changed
|
||||
- codegen malloc → arena 统一分配
|
||||
- LLVM 目标初始化解耦为 `target.h/c` 独立模块
|
||||
- 新增 `.codegraphignore`
|
||||
|
||||
### Fixed
|
||||
- codegen.c 内存管理不一致 (malloc 混用 arena)
|
||||
- str+str 运行时拼接返回左操作数的 bug
|
||||
|
||||
## 0.1.0 (2026-06-05)
|
||||
|
||||
### Added
|
||||
|
||||
+6
-2
@@ -104,10 +104,14 @@ Token* lex(Arena* a, const char* source, const char* filename,
|
||||
tokens[idx++] = make_token(&l, TOK_STR_LIT, start, len);
|
||||
}
|
||||
else if (isalpha(c) || c == '_') { tokens[idx++] = lex_ident_or_keyword(&l); }
|
||||
else if (c == '+' && peek_next(&l) != '=') { tokens[idx++] = make_token(&l, TOK_PLUS, l.pos, 1); advance(&l); }
|
||||
else if (c == '-' && peek_next(&l) != '>') { tokens[idx++] = make_token(&l, TOK_MINUS, l.pos, 1); advance(&l); }
|
||||
else if (c == '+' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_PLUS_EQ, l.pos, 2); advance(&l); advance(&l); }
|
||||
else if (c == '+') { tokens[idx++] = make_token(&l, TOK_PLUS, l.pos, 1); advance(&l); }
|
||||
else if (c == '-' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_MINUS_EQ, l.pos, 2); advance(&l); advance(&l); }
|
||||
else if (c == '-' && peek_next(&l) == '>') { tokens[idx++] = make_token(&l, TOK_ARROW, l.pos, 2); advance(&l); advance(&l); }
|
||||
else if (c == '-') { tokens[idx++] = make_token(&l, TOK_MINUS, l.pos, 1); advance(&l); }
|
||||
else if (c == '*' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_STAR_EQ, l.pos, 2); advance(&l); advance(&l); }
|
||||
else if (c == '*') { tokens[idx++] = make_token(&l, TOK_STAR, l.pos, 1); advance(&l); }
|
||||
else if (c == '/' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_SLASH_EQ, l.pos, 2); advance(&l); advance(&l); }
|
||||
else if (c == '/') { tokens[idx++] = make_token(&l, TOK_SLASH, l.pos, 1); advance(&l); }
|
||||
else if (c == '%') { tokens[idx++] = make_token(&l, TOK_PERCENT, l.pos, 1); advance(&l); }
|
||||
else if (c == '=' && peek_next(&l) == '=') { tokens[idx++] = make_token(&l, TOK_EQ_EQ, l.pos, 2); advance(&l); advance(&l); }
|
||||
|
||||
@@ -17,6 +17,7 @@ static const char* NAMES[] = {
|
||||
[TOK_LT] = "<", [TOK_GT] = ">", [TOK_LT_EQ] = "<=", [TOK_GT_EQ] = ">=",
|
||||
[TOK_AND_AND] = "&&", [TOK_PIPE_PIPE] = "||", [TOK_BANG] = "!",
|
||||
[TOK_ARROW] = "->",
|
||||
[TOK_PLUS_EQ] = "+=", [TOK_MINUS_EQ] = "-=", [TOK_STAR_EQ] = "*=", [TOK_SLASH_EQ] = "/=",
|
||||
[TOK_LPAREN] = "(", [TOK_RPAREN] = ")",
|
||||
[TOK_LBRACE] = "{", [TOK_RBRACE] = "}",
|
||||
[TOK_COMMA] = ",", [TOK_COLON] = ":", [TOK_SEMICOLON] = ";",
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef enum {
|
||||
TOK_EQ_EQ, TOK_BANG_EQ, TOK_LT, TOK_GT, TOK_LT_EQ, TOK_GT_EQ,
|
||||
TOK_AND_AND, TOK_PIPE_PIPE, TOK_BANG,
|
||||
TOK_ARROW,
|
||||
TOK_PLUS_EQ, TOK_MINUS_EQ, TOK_STAR_EQ, TOK_SLASH_EQ,
|
||||
// 分隔符
|
||||
TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE,
|
||||
TOK_COMMA, TOK_COLON, TOK_SEMICOLON, TOK_ASSIGN,
|
||||
|
||||
@@ -277,6 +277,37 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
||||
value, name->line, name->col);
|
||||
}
|
||||
|
||||
// 复合赋值: ident += expr → ident = ident + expr
|
||||
if (t->kind == TOK_IDENT) {
|
||||
TokenKind next_kind = (t + 1)->kind;
|
||||
if (next_kind >= TOK_PLUS_EQ && next_kind <= TOK_SLASH_EQ) {
|
||||
const Token* name = advance(p); // 消费标识符
|
||||
TokenKind comp_op = advance(p)->kind;
|
||||
|
||||
BinaryOp binop;
|
||||
switch (comp_op) {
|
||||
case TOK_PLUS_EQ: binop = OP_ADD; break;
|
||||
case TOK_MINUS_EQ: binop = OP_SUB; break;
|
||||
case TOK_STAR_EQ: binop = OP_MUL; break;
|
||||
case TOK_SLASH_EQ: binop = OP_DIV; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
AstNode* rhs = parse_expr(p, error);
|
||||
if (!rhs) return NULL;
|
||||
if (!expect(p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
|
||||
|
||||
AstNode* lhs_ident = ast_make_ident(p->arena,
|
||||
arena_strdup_impl(p->arena, name->start, name->length),
|
||||
name->line, name->col);
|
||||
AstNode* bin_expr = ast_make_binary(p->arena, binop, lhs_ident, rhs,
|
||||
name->line, name->col);
|
||||
return ast_make_assign(p->arena,
|
||||
arena_strdup_impl(p->arena, name->start, name->length),
|
||||
bin_expr, name->line, name->col);
|
||||
}
|
||||
}
|
||||
|
||||
// 表达式语句
|
||||
AstNode* expr = parse_expr(p, error);
|
||||
if (!expr) return NULL;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fn main() -> i64 {
|
||||
let mut x: i64 = 10;
|
||||
x += 5;
|
||||
print_i64(x); // 15
|
||||
x -= 3;
|
||||
print_i64(x); // 12
|
||||
x *= 2;
|
||||
print_i64(x); // 24
|
||||
x /= 4;
|
||||
print_i64(x); // 6
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user