feat: for 循环 + range (for i in start..end)

- lexer: TOK_FOR, TOK_IN, TOK_DOT_DOT + 修复数字中 .. 解析
- parser: for-in-range → desugar to {let mut, while, assign}
- 零 sema/codegen 改动,复用现有机制
- 新增 2 个集成测试 (10_for_range.l, 11_for_step2.l)
- mem2reg: LLVM 22 C API 不导出,标注已知限制

基于 Codex 分析报告 §6 P0 #3。
This commit is contained in:
2026-06-05 04:42:44 +08:00
parent 72a971e5bf
commit 8144f1bfd7
8 changed files with 349 additions and 6 deletions
+64
View File
@@ -254,6 +254,70 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
return ast_make_while(p->arena, cond, body, t->line, t->col);
}
if (t->kind == TOK_FOR) {
advance(p); // 跳过 'for'
// 解析循环变量名
const Token* var_name = expect(p, TOK_IDENT, error, "for 后应为变量名");
if (!var_name) return NULL;
// 解析 'in'
if (!expect(p, TOK_IN, error, "缺少 'in'")) return NULL;
// 解析起始表达式
AstNode* start_expr = parse_expr(p, error);
if (!start_expr) return NULL;
// 解析 '..'
if (!expect(p, TOK_DOT_DOT, error, "缺少 '..'")) return NULL;
// 解析结束表达式
AstNode* end_expr = parse_expr(p, error);
if (!end_expr) return NULL;
// 解析循环体
AstNode* body = parse_block(p, error);
if (!body) return NULL;
// 脱糖: for i in start..end { body; }
// → { let mut i = start; while i < end { body; i = i + 1; } }
const char* vname = arena_strdup_impl(p->arena, var_name->start, var_name->length);
// 构建: let mut i = start;
AstNode* let_stmt = ast_make_let(p->arena, vname, TYPE_UNKNOWN, false, true, start_expr, var_name->line, var_name->col);
// 构建: i < end (while 条件)
AstNode* cond = ast_make_binary(p->arena, OP_LT,
ast_make_ident(p->arena, vname, var_name->line, var_name->col),
end_expr, var_name->line, var_name->col);
// 构建: i = i + 1 (循环增量)
AstNode* incr = ast_make_assign(p->arena, vname,
ast_make_binary(p->arena, OP_ADD,
ast_make_ident(p->arena, vname, var_name->line, var_name->col),
ast_make_literal_i64(p->arena, 1, var_name->line, var_name->col),
var_name->line, var_name->col),
var_name->line, var_name->col);
// 将增量追加到循环体末尾
AstNode** new_stmts = arena_alloc_impl(p->arena,
(body->as.block.stmt_count + 1) * sizeof(AstNode*));
memcpy(new_stmts, body->as.block.stmts, body->as.block.stmt_count * sizeof(AstNode*));
new_stmts[body->as.block.stmt_count] = incr;
AstNode* new_body = ast_make_block(p->arena, new_stmts,
body->as.block.stmt_count + 1, body->line, body->col);
// 构建: while i < end { ... body ... ; i = i + 1; }
AstNode* while_loop = ast_make_while(p->arena, cond, new_body, t->line, t->col);
// 包装: { let mut i = start; while i < end { ... } }
AstNode* stmts_arr[2] = { let_stmt, while_loop };
AstNode** stmts = arena_alloc_impl(p->arena, 2 * sizeof(AstNode*));
memcpy(stmts, stmts_arr, 2 * sizeof(AstNode*));
return ast_make_block(p->arena, stmts, 2, t->line, t->col);
}
if (t->kind == TOK_RETURN) {
advance(p);
if (match(p, TOK_SEMICOLON)) {