feat: let mut + 赋值语句 — while 循环可修改变量

- lexer: 新增 TOK_MUT 关键字
- ast: AST_ASSIGN_STMT 节点 + let_stmt.is_mut 标志
- parser: ‘let mut’ 前缀识别 + ‘ident = expr;’ 赋值语句
- sema: Symbol.is_mut 可变性检查(不可变变量赋值报错)
- codegen: AST_ASSIGN_STMT → store 指令
- 新增集成测试 06_mut_while.l(while 循环 + 计数器)

基于 Codex 分析报告 P0 建议。
This commit is contained in:
2026-06-05 00:42:50 +08:00
parent f8c5e18188
commit bd02a4989e
11 changed files with 88 additions and 8 deletions
+15 -1
View File
@@ -194,6 +194,8 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
if (t->kind == TOK_LET) {
advance(p);
bool is_mut = false;
if (peek(p)->kind == TOK_MUT) { is_mut = true; advance(p); }
const Token* name = expect(p, TOK_IDENT, error, "let 后应为变量名");
if (!name) return NULL;
// 可选的类型标注
@@ -214,7 +216,7 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
if (!expect(p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
return ast_make_let(p->arena,
arena_strdup_impl(p->arena, name->start, name->length),
annot_type, has_type_annot, init, t->line, t->col);
annot_type, has_type_annot, is_mut, init, t->line, t->col);
}
if (t->kind == TOK_IF) {
@@ -255,6 +257,18 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
return ast_make_return(p->arena, expr, t->line, t->col);
}
// 赋值语句: ident = expr ;
if (t->kind == TOK_IDENT && (t + 1)->kind == TOK_ASSIGN) {
const Token* name = advance(p); // 消费标识符
advance(p); // 消费 '='
AstNode* value = parse_expr(p, error);
if (!value) return NULL;
if (!expect(p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
return ast_make_assign(p->arena,
arena_strdup_impl(p->arena, name->start, name->length),
value, name->line, name->col);
}
// 表达式语句
AstNode* expr = parse_expr(p, error);
if (!expr) return NULL;