feat(stmt): 支持 with 语句翻译(__enter__/__exit__ + MRO 解析)

对齐 TPC HandlesWith 核心语义:构造 context 对象 → 调用 __enter__()
(沿继承链 MRO 回退,如 MemPool → MemManager)→ asname 绑定返回值
→ 翻译 body → 逆序调用 __exit__()。context 对象存入临时变量
__with_ctx,方法调用复用 translate_call 的 Attribute 分派 +
_translate_method_call 的 MRO 回退;嵌套 with 时保存/恢复外层
__with_ctx 避免类型串扰。修复 "with memhub.MemPool(...) as pool:"
报 SyntaxError 未知语句类型 kind=21。
This commit is contained in:
TermiNexus committed 2026-08-13 07:07:00 +08:00
1 parent 39ddf11ddc
commit a6d1a6f9ae
1 file changed
+150
+150
View File
@@ -125,6 +125,8 @@ class StmtGenMixin:
return self.translate_delete(trans, node)
elif k == ast.ASTKind.Raise:
return self.translate_raise(trans, node)
elif k == ast.ASTKind.With:
return self.translate_with(trans, node)
# 未知语句类型:直接报错终止(fail-fast),暴露上游解析问题
HandlesType.fatal_error(node, f"未知语句类型 kind={k}", "SyntaxError")
return 0
@@ -193,6 +195,154 @@ class StmtGenMixin:
llvmlite.build_ret(builder, zero_v)
return 0
# ============================================================
# translate_with - with 语句翻译
#
# with expr() as x: body
# → 1. 构造 context 对象(expr() 调用)
# 2. 调用 __enter__()(沿继承链 MRO 解析,如 MemPool → MemManager)
# 3. x 绑定 __enter__ 返回值(缺失时退回 context 对象)
# 4. 翻译 body
# 5. 逆序调用 __exit__()(无异常传播,__exit__ 仅做清理如 reset)
#
# 实现:context 对象存入临时变量 __with_ctx,方法调用复用
# translate_call 的 Attribute 分派 + _translate_method_call 的 MRO 回退
# (与普通 obj.method() 调用一致)。嵌套 with 时保存外层 __with_ctx,
# 退出后恢复,避免内层覆盖导致外层 __exit__ 解析到错误对象。
# 对齐 TPC HandlesWith 语义(不含 __provides__ provider 机制)。
# ============================================================
def translate_with(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译 with 语句,返回 0"""
if trans is None or node is None:
return 0
wt: ast.With | t.CPtr = (ast.With | t.CPtr)(node)
if wt is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
if pool is None or builder is None or mod is None:
return 0
items: list[ast.AST | t.CPtr] | t.CPtr = wt.items
if items is None:
return 0
n: t.CSizeT = len(items)
# 保存外层 __with_ctx(嵌套 with 时退出后恢复)
old_ctx_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, "__with_ctx")
# 记录每个 item 的类名(__exit__ 调用时设置 __with_ctx 类型用)
ctx_class_buf: t.CSizeT | t.CPtr = pool.alloc(8 * n)
if ctx_class_buf is None:
return 0
string.memset(ctx_class_buf, 0, 8 * n)
# 1. 全部 item:构造 context 对象 + 调用 __enter__ + asname 绑定
for i in range(n):
item_node: ast.AST | t.CPtr = items.get(i)
if item_node is None:
continue
if item_node.kind() != ast.ASTKind.WithItem:
continue
item: ast.WithItem | t.CPtr = (ast.WithItem | t.CPtr)(item_node)
if item is None or item.context_expr is None:
continue
# 翻译 context_expr(对象构造,如 MemPool(arena, 4096))
ctx_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, item.context_expr,
trans._funcs, trans._func_count, trans)
if ctx_val is None:
continue
# 提取类名:Call(func=Attribute("memhub"."MemPool") / Name("MemPool"))
cls_name: str = None
ce: ast.AST | t.CPtr = item.context_expr
if ce.kind() == ast.ASTKind.Call:
cfn: ast.AST | t.CPtr = ((ast.Call | t.CPtr)(ce)).func
if cfn is not None:
if cfn.kind() == ast.ASTKind.Attribute:
cls_name = ((ast.Attribute | t.CPtr)(cfn)).attr
elif cfn.kind() == ast.ASTKind.Name:
cls_name = ((ast.Name | t.CPtr)(cfn)).id
# context 对象存入 __with_ctx(方法调用解析 self 用)
ctx_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(
builder, ctx_val.Ty)
if ctx_alloca is None:
continue
llvmlite.build_store(builder, ctx_val, ctx_alloca)
HandlesVar.define_var(trans.SymTab, "__with_ctx", ctx_alloca)
if cls_name is not None:
HandlesVar.set_var_annot_class_name(
trans.SymTab, "__with_ctx", cls_name)
ctx_class_buf[i] = t.CSizeT(cls_name)
# 调用 __enter__()
enter_val: llvmlite.Value | t.CPtr = self._with_ctx_call(
trans, pool, builder, mod, "__enter__")
# asname 绑定(__enter__ 返回值,缺失时退回 context 对象)
if item.optional_vars is not None:
ov: ast.AST | t.CPtr = item.optional_vars
if ov.kind() == ast.ASTKind.Name:
asname: str = ((ast.Name | t.CPtr)(ov)).id
if asname is not None:
bind_val: llvmlite.Value | t.CPtr = enter_val
if bind_val is None:
bind_val = ctx_val
if bind_val is not None:
asn_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(
builder, bind_val.Ty)
if asn_alloca is not None:
llvmlite.build_store(builder, bind_val, asn_alloca)
HandlesVar.define_var(trans.SymTab, asname, asn_alloca)
if cls_name is not None:
HandlesVar.set_var_annot_class_name(
trans.SymTab, asname, cls_name)
# 2. 翻译 body
body: list[ast.AST | t.CPtr] | t.CPtr = wt.children
if body is not None:
bn: t.CSizeT = len(body)
for bi in range(bn):
st: ast.AST | t.CPtr = body.get(bi)
if st is not None:
self.translate_stmt(trans, st)
# 3. 逆序调用 __exit__()(Python with 语义:后进先出)
for i in range(n):
ci: int = n - 1 - i
if ctx_class_buf[ci] != 0:
cls_ci: str = (str | t.CPtr)(t.CVoid(ctx_class_buf[ci], t.CPtr))
HandlesVar.set_var_annot_class_name(
trans.SymTab, "__with_ctx", cls_ci)
self._with_ctx_call(trans, pool, builder, mod, "__exit__")
# 4. 恢复外层 __with_ctx
if old_ctx_alloca is not None:
HandlesVar.define_var(trans.SymTab, "__with_ctx", old_ctx_alloca)
return 0
def _with_ctx_call(self, trans: HT.Translator | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
method_name: str) -> llvmlite.Value | t.CPtr:
"""构造并翻译 __with_ctx.<method_name>() 调用(__enter__/__exit__)"""
if pool is None or method_name is None:
return None
name_node: ast.AST | t.CPtr = ast.Name(pool, "__with_ctx", ast.ASTCtx.Load)
if name_node is None:
return None
attr_node: ast.AST | t.CPtr = ast.Attribute(
pool, name_node, method_name, ast.ASTCtx.Load)
if attr_node is None:
return None
call_node: ast.AST | t.CPtr = ast.Call(pool, attr_node, None, None)
if call_node is None:
return None
return HandlesExpr.translate_value(
builder, pool, mod, call_node,
trans._funcs, trans._func_count, trans)
def translate_global(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译 global 语句:记录 global 变量名"""