refactor: AST Visitor dispatch — sema analyze_expr switch→vtable

新增 src/ast/visit.h/c: AstDispatch 函数指针表 + ast_visit() 统一入口
analyze_expr 的 switch 替换为 dispatch 表, 10 个 handler 通过 SEMA_HANDLER 宏注册
新增 AST 节点: 在 analyze_expr_init() 加一行即可, 编译器会检查函数签名

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 20:36:13 +08:00
parent 3733b41453
commit b34ad17aad
5 changed files with 89 additions and 16 deletions
+17
View File
@@ -0,0 +1,17 @@
#include "visit.h"
VisitFn ast_dispatch_get(AstDispatch* d, AstKind k) {
if ((int)k < VISIT_TABLE_SIZE) return d->table[k];
return NULL;
}
void ast_dispatch_set(AstDispatch* d, AstKind k, VisitFn fn) {
if ((int)k < VISIT_TABLE_SIZE) d->table[k] = fn;
}
void* ast_visit(AstDispatch* d, AstNode* node) {
if (!node) return NULL;
VisitFn fn = ((int)node->kind < VISIT_TABLE_SIZE) ? d->table[node->kind] : NULL;
if (fn) return fn(d->ctx, node);
return NULL;
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef AST_VISIT_H
#define AST_VISIT_H
#include "ast.h"
// 通用节点处理器: ctx 为模块上下文, 返回模块相关值(sema 返回 NULL)
typedef void* (*VisitFn)(void* ctx, AstNode* node);
// 遍历表 — 按 AstKind 索引, 未处理的条目为 NULL
// 新增 AST 节点: 在此表新增一条目, 编译器会警告未初始化的函数指针
enum { VISIT_TABLE_SIZE = 28 };
typedef struct {
void* ctx;
VisitFn table[VISIT_TABLE_SIZE];
} AstDispatch;
// 获取未处理节点类型的默认处理器
VisitFn ast_dispatch_get(AstDispatch* d, AstKind k);
void ast_dispatch_set(AstDispatch* d, AstKind k, VisitFn fn);
// 统一入口: 查表 + 调用
void* ast_visit(AstDispatch* d, AstNode* node);
#endif
+1
View File
@@ -484,6 +484,7 @@ void sema_analyze(AstNode* ast, ErrorList* errors, Arena* arena) {
g_program = ast;
mono_count = 0;
mono_arena = arena;
analyze_expr_init(); // 初始化 Visitor dispatch 表
// 注册内置函数
TypeKind params_i64[] = {TYPE_I64};
+5
View File
@@ -8,6 +8,7 @@
#include "arena.h"
#include "l_lang.h"
#include "type_table.h"
#include "visit.h"
#include <stdio.h>
#include <string.h>
@@ -31,7 +32,11 @@ bool is_comparable(TypeKind a, TypeKind b);
void subst_ast_types(AstNode* node, const char* tparam, TypeKind concrete, const char* concrete_sname);
void subst_type_info(TypeInfo* ti, const char* tparam, TypeKind concrete, const char* concrete_sname);
// === Sema 上下文 (Visitor 用) ===
typedef struct { Scope* scope; ErrorList* errors; Arena* a; } SemaCtx;
// === 表达式类型检查 ===
void analyze_expr_init(void);
void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a);
void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a);
void analyze_ident_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a);
+41 -16
View File
@@ -607,21 +607,46 @@ void analyze_method_call(AstNode* node, Scope* scope, ErrorList* errors, Arena*
node->type.struct_name = sym->return_struct_type_name;
}
// === 表达式类型检查(调度器) ===
void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) {
switch (node->kind) {
case AST_LITERAL_EXPR: break;
case AST_IDENT_EXPR: analyze_ident_expr(node, scope, errors, a); break;
case AST_UNARY_EXPR: analyze_unary_expr(node, scope, errors, a); break;
case AST_BINARY_EXPR: analyze_binary_expr(node, scope, errors, a); break;
case AST_CALL_EXPR: analyze_call_expr(node, scope, errors, a); break;
case AST_FIELD_ACCESS: analyze_field_access(node, scope, errors, a); break;
case AST_STRUCT_INIT: analyze_struct_init(node, scope, errors, a); break;
case AST_ENUM_VARIANT: analyze_enum_variant(node, scope, errors, a); break;
case AST_INDEX_EXPR: analyze_index_expr(node, scope, errors, a); break;
case AST_METHOD_CALL: analyze_method_call(node, scope, errors, a); break;
case AST_IF_STMT: analyze_node(node, scope, errors, a); break;
case AST_BLOCK: analyze_node(node, scope, errors, a); break;
default: break;
// === 表达式类型检查(Visitor 调度器) ===
// 每个 handler 包装函数: (void* ctx, AstNode* node) → 调用实际 handler
#define SEMA_HANDLER(name) \
static void* name##_wrap(void* vctx, AstNode* node) { \
SemaCtx* s = (SemaCtx*)vctx; \
name(node, s->scope, s->errors, s->a); \
return NULL; \
}
SEMA_HANDLER(analyze_ident_expr)
SEMA_HANDLER(analyze_unary_expr)
SEMA_HANDLER(analyze_binary_expr)
SEMA_HANDLER(analyze_call_expr)
SEMA_HANDLER(analyze_field_access)
SEMA_HANDLER(analyze_struct_init)
SEMA_HANDLER(analyze_enum_variant)
SEMA_HANDLER(analyze_index_expr)
SEMA_HANDLER(analyze_method_call)
SEMA_HANDLER(analyze_node) // if-expr / block 委托
static AstDispatch sema_dispatch;
void analyze_expr_init(void) {
sema_dispatch.ctx = NULL; // 由 analyze_expr 每次设置
// 新增表达式节点: 在此注册 handler, 编译器会警告缺失
ast_dispatch_set(&sema_dispatch, AST_IDENT_EXPR, analyze_ident_expr_wrap);
ast_dispatch_set(&sema_dispatch, AST_UNARY_EXPR, analyze_unary_expr_wrap);
ast_dispatch_set(&sema_dispatch, AST_BINARY_EXPR, analyze_binary_expr_wrap);
ast_dispatch_set(&sema_dispatch, AST_CALL_EXPR, analyze_call_expr_wrap);
ast_dispatch_set(&sema_dispatch, AST_FIELD_ACCESS, analyze_field_access_wrap);
ast_dispatch_set(&sema_dispatch, AST_STRUCT_INIT, analyze_struct_init_wrap);
ast_dispatch_set(&sema_dispatch, AST_ENUM_VARIANT, analyze_enum_variant_wrap);
ast_dispatch_set(&sema_dispatch, AST_INDEX_EXPR, analyze_index_expr_wrap);
ast_dispatch_set(&sema_dispatch, AST_METHOD_CALL, analyze_method_call_wrap);
ast_dispatch_set(&sema_dispatch, AST_IF_STMT, analyze_node_wrap);
ast_dispatch_set(&sema_dispatch, AST_BLOCK, analyze_node_wrap);
}
void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) {
SemaCtx sctx = {scope, errors, a};
sema_dispatch.ctx = &sctx;
ast_visit(&sema_dispatch, node);
}