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:
+8
-2
@@ -35,10 +35,16 @@ AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, int line, in
|
||||
return n;
|
||||
}
|
||||
|
||||
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot, AstNode* init, int line, int col) {
|
||||
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot, bool is_mut, AstNode* init, int line, int col) {
|
||||
NEW(alloc, AST_LET_STMT);
|
||||
n->as.let_stmt.name = name; n->as.let_stmt.annot_type = annot_type;
|
||||
n->as.let_stmt.has_type_annot = has_type_annot; n->as.let_stmt.init = init;
|
||||
n->as.let_stmt.has_type_annot = has_type_annot; n->as.let_stmt.is_mut = is_mut; n->as.let_stmt.init = init;
|
||||
return n;
|
||||
}
|
||||
|
||||
AstNode* ast_make_assign(void* alloc, const char* name, AstNode* value, int line, int col) {
|
||||
NEW(alloc, AST_ASSIGN_STMT);
|
||||
n->as.assign_stmt.name = name; n->as.assign_stmt.value = value;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -10,6 +10,7 @@ typedef enum {
|
||||
AST_PARAMETER,
|
||||
AST_BLOCK,
|
||||
AST_LET_STMT,
|
||||
AST_ASSIGN_STMT,
|
||||
AST_IF_STMT,
|
||||
AST_WHILE_STMT,
|
||||
AST_RETURN_STMT,
|
||||
@@ -52,7 +53,9 @@ struct AstNode {
|
||||
// AST_BLOCK
|
||||
struct { struct AstNode** stmts; size_t stmt_count; } block;
|
||||
// AST_LET_STMT
|
||||
struct { const char* name; TypeKind annot_type; bool has_type_annot; struct AstNode* init; } let_stmt;
|
||||
struct { const char* name; TypeKind annot_type; bool has_type_annot; bool is_mut; struct AstNode* init; } let_stmt;
|
||||
// AST_ASSIGN_STMT
|
||||
struct { const char* name; struct AstNode* value; } assign_stmt;
|
||||
// AST_IF_STMT
|
||||
struct { struct AstNode* cond; struct AstNode* then_block; struct AstNode* else_block; } if_stmt;
|
||||
// AST_WHILE_STMT
|
||||
@@ -80,7 +83,8 @@ AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size
|
||||
TypeKind ret, AstNode* body, int line, int col);
|
||||
AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, int line, int col);
|
||||
AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, int line, int col);
|
||||
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot, AstNode* init, int line, int col);
|
||||
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot, bool is_mut, AstNode* init, int line, int col);
|
||||
AstNode* ast_make_assign(void* alloc, const char* name, AstNode* value, int line, int col);
|
||||
AstNode* ast_make_if(void* alloc, AstNode* cond, AstNode* then_b, AstNode* else_b, int line, int col);
|
||||
AstNode* ast_make_while(void* alloc, AstNode* cond, AstNode* body, int line, int col);
|
||||
AstNode* ast_make_return(void* alloc, AstNode* expr, int line, int col);
|
||||
|
||||
@@ -229,6 +229,15 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_ASSIGN_STMT: {
|
||||
LLVMValueRef ptr = find_var(ctx, node->as.assign_stmt.name);
|
||||
if (!ptr) return;
|
||||
LLVMValueRef val = codegen_expr(ctx, node->as.assign_stmt.value);
|
||||
if (!val) return;
|
||||
LLVMBuildStore(ctx->builder, val, ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_EXPR_STMT:
|
||||
codegen_expr(ctx, node->as.expr_stmt.expr);
|
||||
break;
|
||||
|
||||
@@ -55,6 +55,7 @@ static Token lex_number(Lexer* l) {
|
||||
static TokenKind check_keyword(const Token* tok) {
|
||||
#define KW(s, k) if (tok->length == sizeof(s)-1 && memcmp(tok->start, s, sizeof(s)-1) == 0) return k
|
||||
KW("fn", TOK_FN); KW("let", TOK_LET);
|
||||
KW("mut", TOK_MUT);
|
||||
KW("if", TOK_IF); KW("else", TOK_ELSE);
|
||||
KW("while", TOK_WHILE); KW("return", TOK_RETURN);
|
||||
KW("i64", TOK_I64); KW("f64", TOK_F64);
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
static const char* NAMES[] = {
|
||||
[TOK_FN] = "fn", [TOK_LET] = "let", [TOK_IF] = "if",
|
||||
[TOK_FN] = "fn", [TOK_LET] = "let", [TOK_MUT] = "mut", [TOK_IF] = "if",
|
||||
[TOK_ELSE] = "else", [TOK_WHILE] = "while", [TOK_RETURN] = "return",
|
||||
[TOK_I64] = "i64", [TOK_F64] = "f64", [TOK_BOOL] = "bool", [TOK_VOID] = "void",
|
||||
[TOK_INT_LIT] = "整数", [TOK_FLOAT_LIT] = "浮点数",
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
// === Token 类型枚举 ===
|
||||
typedef enum {
|
||||
// 关键字
|
||||
TOK_FN, TOK_LET, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_RETURN,
|
||||
TOK_FN, TOK_LET, TOK_MUT, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_RETURN,
|
||||
// 类型关键字
|
||||
TOK_I64, TOK_F64, TOK_BOOL, TOK_VOID,
|
||||
// 字面量
|
||||
|
||||
+15
-1
@@ -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;
|
||||
|
||||
+36
-1
@@ -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 &&
|
||||
|
||||
@@ -27,6 +27,8 @@ Symbol* scope_insert(Scope* scope, void* alloc, const char* name,
|
||||
}
|
||||
Symbol* sym = (Symbol*)arena_alloc_impl(alloc, sizeof(Symbol));
|
||||
sym->name = name; sym->kind = kind; sym->type = type;
|
||||
sym->is_mut = false; sym->return_type = TYPE_VOID;
|
||||
sym->param_types = NULL; sym->param_count = 0;
|
||||
sym->next = scope->head;
|
||||
scope->head = sym;
|
||||
return sym;
|
||||
|
||||
@@ -10,6 +10,7 @@ typedef struct Symbol {
|
||||
const char* name;
|
||||
SymbolKind kind;
|
||||
TypeKind type; // 变量/参数的类型
|
||||
bool is_mut; // 变量是否可变(可被赋值)
|
||||
// 函数特有
|
||||
TypeKind return_type;
|
||||
TypeKind* param_types;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fn main() -> i64 {
|
||||
let mut i: i64 = 0;
|
||||
while i < 5 {
|
||||
print_i64(i);
|
||||
i = i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user