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
+36 -1
View File
@@ -204,13 +204,48 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
}
node->type.kind = var_type;
if (!scope_insert(scope, a, node->as.let_stmt.name, SYM_VARIABLE, var_type)) {
Symbol* sym = scope_insert(scope, a, node->as.let_stmt.name, SYM_VARIABLE, var_type);
if (!sym) {
error_add(errors, "<sema>", node->line, node->col,
"变量 '%s' 重复定义", node->as.let_stmt.name);
} else {
sym->is_mut = node->as.let_stmt.is_mut;
}
break;
}
case AST_ASSIGN_STMT: {
Symbol* sym = scope_lookup(scope, node->as.assign_stmt.name);
if (!sym) {
error_add(errors, "<sema>", node->line, node->col,
"未定义的变量 '%s'", node->as.assign_stmt.name);
node->type.kind = TYPE_ERROR;
break;
}
if (sym->kind != SYM_VARIABLE) {
error_add(errors, "<sema>", node->line, node->col,
"'%s' 不是变量,不能赋值", node->as.assign_stmt.name);
node->type.kind = TYPE_ERROR;
break;
}
if (!sym->is_mut) {
error_add(errors, "<sema>", node->line, node->col,
"不能对不可变变量 '%s' 赋值(需用 let mut 声明)",
node->as.assign_stmt.name);
node->type.kind = TYPE_ERROR;
break;
}
analyze_expr(node->as.assign_stmt.value, scope, errors, a);
TypeKind value_ty = node->as.assign_stmt.value->type.kind;
if (value_ty != TYPE_ERROR && value_ty != sym->type) {
error_add(errors, "<sema>", node->line, node->col,
"赋值类型不匹配: 变量 '%s' 类型为 '%s',但表达式类型为 '%s'",
node->as.assign_stmt.name, type_name(sym->type), type_name(value_ty));
}
node->type.kind = TYPE_VOID;
break;
}
case AST_IF_STMT:
analyze_expr(node->as.if_stmt.cond, scope, errors, a);
if (node->as.if_stmt.cond->type.kind != TYPE_BOOL &&