From cc0615e519a16b8e09915fb2e2b18c716f895f2d Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 2 Oct 2026 22:33:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(match):=20fallthrough=20=E8=BD=AF=E5=85=B3?= =?UTF-8?q?=E9=94=AE=E5=AD=97=20+=20case=20=E5=86=85=20break=20=E8=B7=B3?= =?UTF-8?q?=E5=87=BA=20match?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - vast/Parser:语句位置识别裸 `fallthrough`(仅整条语句命中),脱糖为 c.NoBreak() 标记调用;作标识符的写法不受影响 - HandlesBody:`c.NoBreak()` 标记语句不产出 IR(语义由 HandlesMatch 消费) - HandlesMatch:新增 _HandleMatchCaseBody(case 内 break 目标 = match 的 AfterBB, 经 loop_break_targets 压栈/出栈,不影响外层循环)与 _FallThroughTarget; 9 处 fallthrough 由"只留无终结符块"改为显式 br 下一个 case body --- lib/core/Handles/HandlesBody.py | 17 ++++++++- lib/core/Handles/HandlesMatch.py | 62 +++++++++++++++++++++++++++----- lib/vast/Parser.py | 38 ++++++++++++++++++++ 3 files changed, 107 insertions(+), 10 deletions(-) diff --git a/lib/core/Handles/HandlesBody.py b/lib/core/Handles/HandlesBody.py index 0b08050..740ab34 100644 --- a/lib/core/Handles/HandlesBody.py +++ b/lib/core/Handles/HandlesBody.py @@ -19,7 +19,13 @@ class BodyHandle(BaseHandle): Gen._set_node_info(Node) try: if isinstance(Node, ast.Expr): - self.HandleExprLlvm(Node.value) + # `fallthrough`(vast 软关键字已脱糖为 c.NoBreak())是 match 的 + # 标记语句,本身不产出 IR,语义由 HandlesMatch 消费;后续步骤 + # (含 _EmitTempFrees)保持原样执行。 + if self._IsNoBreakMarkerStmt(Node): + pass + else: + self.HandleExprLlvm(Node.value) elif isinstance(Node, ast.Return): self.Trans.ReturnHandler._HandleReturnLlvm(Node) elif isinstance(Node, ast.Assign): @@ -79,6 +85,15 @@ class BodyHandle(BaseHandle): if Gen.builder and not Gen.builder.block.is_terminated: Gen._EmitTempFrees() + @staticmethod + def _IsNoBreakMarkerStmt(Node: ast.stmt) -> bool: + """是否 `c.NoBreak()` 标记语句(`fallthrough` 软关键字的脱糖形态)""" + if not isinstance(Node, ast.Expr) or not isinstance(Node.value, ast.Call): + return False + Fn: ast.expr = Node.value.func + return (isinstance(Fn, ast.Attribute) and isinstance(Fn.value, ast.Name) + and Fn.value.id == 'c' and Fn.attr == 'NoBreak') + def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None, ConstWidthHint: int | None = None) -> ir.Value | None: return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint) \ No newline at end of file diff --git a/lib/core/Handles/HandlesMatch.py b/lib/core/Handles/HandlesMatch.py index 93c7663..21ba577 100644 --- a/lib/core/Handles/HandlesMatch.py +++ b/lib/core/Handles/HandlesMatch.py @@ -239,43 +239,53 @@ class MatchHandle(BaseHandle): for j in range(NumSubCases): if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB: Gen.builder.position_at_start(CaseBBs[CaseIdx]) - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 elif isinstance(pattern, (ast.MatchValue, ast.MatchSingleton)): if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB: Gen.builder.position_at_start(CaseBBs[CaseIdx]) - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 elif isinstance(pattern, ast.MatchAs): if pattern.pattern is None: Gen.builder.position_at_start(DefaultBB) - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 elif isinstance(pattern, ast.MatchSequence): Gen.builder.position_at_start(DefaultBB) - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 elif isinstance(pattern, ast.MatchClass): # 兜底处理:MatchClass 在非 REnum 路径的 case body 生成 # case 收集阶段已创建 CaseBB,此处生成 body if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB: Gen.builder.position_at_start(CaseBBs[CaseIdx]) - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 if not HasDefault: Gen.builder.position_at_start(DefaultBB) @@ -285,6 +295,32 @@ class MatchHandle(BaseHandle): Gen.builder.branch(AfterBB) Gen.builder.position_at_start(AfterBB) + def _HandleMatchCaseBody(self, Gen: "Translator.LlvmGen", + Body: list[ast.stmt], AfterBB: ir.Block) -> None: + """翻译 match case 体:case 体内的 break 目标是 match 的 AfterBB + + C switch 语义——case 内 break 跳出 **match**(而非外层循环)。故翻译前把 + AfterBB 压入 loop_break_targets,翻译完弹出(外层循环目标不受影响)。 + """ + Targets: list = Gen.loop_break_targets + Targets.append(AfterBB) + try: + self.HandleBodyLlvm(Body) + finally: + Targets.pop() + + @staticmethod + def _FallThroughTarget(CaseBBs: list[ir.Block], CaseIdx: int, AfterBB: ir.Block) -> ir.Block: + """fallthrough 的落点:下一个 case 的 body(无后继槽位时落到 AfterBB) + + `fallthrough`(vast 软关键字,脱糖为 c.NoBreak())表示"本 case 不自动 + break",直接落到下一个 case 的 body(对齐 C 的 [[fallthrough]])。 + """ + NextIdx: int = CaseIdx + 1 + if 0 <= NextIdx < len(CaseBBs): + return CaseBBs[NextIdx] + return AfterBB + def _HandleRenumMatchLlvm(self, Node: ast.Match, RenumName: str, SubjectPtr: ir.Value) -> None: Gen: "Translator.LlvmGen" = self.Trans.LlvmGen if isinstance(SubjectPtr.type, ir.PointerType) and isinstance(SubjectPtr.type.pointee, ir.PointerType): @@ -455,36 +491,44 @@ class MatchHandle(BaseHandle): if isinstance(elem_ptr.type, ir.PointerType) and elem_ptr.type.pointee != member_type: elem_ptr = Gen.builder.bitcast(elem_ptr, ir.PointerType(member_type), name=f"match_{bind_name}_cast") Gen.variables[bind_name] = elem_ptr - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) for bind_name, _, _, _ in bindings: if bind_name in Gen.variables: del Gen.variables[bind_name] if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 elif isinstance(pattern, ast.MatchValue): if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB: Gen.builder.position_at_start(CaseBBs[CaseIdx]) - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 elif isinstance(pattern, ast.MatchAs): if pattern.pattern is None: Gen.builder.position_at_start(DefaultBB) - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 elif isinstance(pattern, ast.MatchSequence): Gen.builder.position_at_start(DefaultBB) - self.HandleBodyLlvm(case.body) + self._HandleMatchCaseBody(Gen, case.body, AfterBB) if not Gen.builder.block.is_terminated: if not HasNoBreak[i]: Gen.builder.branch(AfterBB) + else: + Gen.builder.branch(self._FallThroughTarget(CaseBBs, CaseIdx, AfterBB)) CaseIdx += 1 if not HasDefault: Gen.builder.position_at_start(DefaultBB) diff --git a/lib/vast/Parser.py b/lib/vast/Parser.py index f050999..dd34514 100644 --- a/lib/vast/Parser.py +++ b/lib/vast/Parser.py @@ -82,6 +82,11 @@ _STRUCT_EXAMPLE: str = ( 'struct Node(Base): value: int' ) +# fallthrough 软关键字(语句级):仅当整条语句就是 `fallthrough` 时命中, +# 脱糖为 `c.NoBreak()` 标记调用(match 生成侧据此让 case 体不自动 break, +# 落到下一个 case)。作标识符/属性的写法逐字不变。 +_FALLTHROUGH_KEYWORD: str = 'fallthrough' + class Parser(ExprParserMixin): """Viper/Python 完整语法分析器。""" @@ -186,6 +191,13 @@ class Parser(ExprParserMixin): return self._ParseMatch() except SyntaxError: self.Idx = save + # fallthrough 软关键字(语句级):仅当整条语句就是 `fallthrough` + # (后随 NEWLINE / ';' / ENDMARKER)时命中,脱糖为 `c.NoBreak()` + # 标记调用,由 match 生成侧消费(case 体因此不自动 break)。 + # 作标识符/属性的写法(fallthrough = 1 / fallthrough(x) / + # d.fallthrough)不命中,逐字不变。 + if v == _FALLTHROUGH_KEYWORD and self._IsFallThroughStmt(): + return self._ParseFallThrough() if v == 'return': return self._ParseReturn() if v == 'raise': @@ -224,6 +236,32 @@ class Parser(ExprParserMixin): return self._ParseDecorated() return self._ParseSimpleStmt() + # ================================================================== + # fallthrough 软关键字(语句级) + # ================================================================== + def _IsFallThroughStmt(self) -> bool: + """当前语句是否为软关键字 `fallthrough`(后随 NEWLINE / ';' / ENDMARKER) + + 只在"整条语句就是 fallthrough"时命中;`fallthrough = 1`(赋值)、 + `fallthrough(x)`(调用)、`d.fallthrough`(属性)都不命中。 + """ + nxt: Token = self._Peek(1) + if nxt.Type == NEWLINE or nxt.Type == ENDMARKER: + return True + return nxt.Type == OP and nxt.Value == ';' + + def _ParseFallThrough(self) -> N.stmt: + """`fallthrough` 脱糖为 `c.NoBreak()`(match 生成侧识别的 fallthrough 标记)""" + tok: Token = self._Peek() + self._Next() + marker: N.Call = N.Call( + func=N.Attribute(value=N.Name(id='c', ctx=N.Load()), + attr='NoBreak', ctx=N.Load()), + args=[], keywords=[]) + node: N.Expr = N.Expr(value=marker) + self._FixLoc(node, tok) + return node + # ================================================================== # 简单语句 # ==================================================================