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:
1 parent
39ddf11ddc
commit
a6d1a6f9ae
1 file changed
+150
@@ -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 变量名"""
|
||||
|
||||
Reference in new issue
Block a user