feat(switch): 新增 switch 语句并禁止 match 内 fallthrough
- vast/Nodes.py:Match 增加 is_switch 字段(含默认值) - vast/Parser.py:新增 _ParseSwitch(case <常量|枚举变体> / default:,desugar 为 Match(is_switch=True));match / struct / fallthrough 改为硬关键字 - vast/Unparser.py:round-trip 忠实还原 switch 与 default: - HandlesMatch / HandlesBody:新增 in_switch_case 门控,fallthrough 只允许出现在 switch 的 case 体内 - HandlesFor:补齐嵌套函数的 saved_loop_break/continue_targets 定义(修 NameError),并隔离 in_switch_case - LLVMCG/BaseGen:新增 in_switch_case 状态
This commit is contained in:
1 parent
8b6c36d24f
commit
74cfaeb917
7 files changed
+187
-97
No files matched your search
+102
-35
@@ -74,17 +74,21 @@ _VIRTUAL_EXAMPLE: str = (
|
||||
# (HandlesClassDef._resolve_class_flags / _MARKER_BASES)零新增判据。
|
||||
# - 允许出现在 REnum 内部(REnum 变体本就是嵌套 ClassDef),也允许 struct 内
|
||||
# 再写 struct(与 class 内写 class 同口径)。
|
||||
# 软关键字边界:只在"语句起始 + 后随标识符"时判定,故 `struct = 5` / `x.struct`
|
||||
# 等作标识符的写法逐字不变。
|
||||
# struct 纯数据类型定义(硬关键字):语句起始一律按 struct 定义解析
|
||||
# (`struct = 5` / `x.struct` 之类把 struct 当标识符的写法已不再允许)。
|
||||
_STRUCT_KEYWORD: str = 'struct'
|
||||
_STRUCT_EXAMPLE: str = (
|
||||
'struct Point: x: int / struct Point: x: int(换行缩进)y: int / '
|
||||
'struct Node(Base): value: int'
|
||||
)
|
||||
|
||||
# fallthrough 软关键字(语句级):仅当整条语句就是 `fallthrough` 时命中,
|
||||
# 脱糖为 `c.NoBreak()` 标记调用(match 生成侧据此让 case 体不自动 break,
|
||||
# 落到下一个 case)。作标识符/属性的写法逐字不变。
|
||||
# switch 多分支语句(硬关键字,C switch 语义):`switch <expr>:` + `case <常量>:`
|
||||
# / `default:`。默认不穿透,case 体内写 `fallthrough` 才落到下一个 case。
|
||||
# 解析时 desugar 为 is_switch=True 的 N.Match。
|
||||
_SWITCH_KEYWORD: str = 'switch'
|
||||
|
||||
# fallthrough 关键字(硬):仅在 switch 的 case 体内合法,脱糖为 `c.NoBreak()`
|
||||
# 标记调用(switch 生成侧据此让 case 体不自动跳出 switch,落到下一个 case)。
|
||||
_FALLTHROUGH_KEYWORD: str = 'fallthrough'
|
||||
|
||||
|
||||
@@ -170,9 +174,8 @@ class Parser(ExprParserMixin):
|
||||
return self._ParseFunctionDef()
|
||||
if v == 'class':
|
||||
return self._ParseClassDef()
|
||||
# struct 纯数据类型定义(软关键字):仅"语句起始 + 后随标识符"时判定,
|
||||
# 故 `struct = 5` / `x.struct` 等作标识符的写法逐字不变。
|
||||
if v == _STRUCT_KEYWORD and self._Peek(1).Type == NAME:
|
||||
# struct 纯数据类型定义(硬关键字):语句起始一律按 struct 定义解析。
|
||||
if v == _STRUCT_KEYWORD:
|
||||
return self._ParseStructDef()
|
||||
if v == 'if':
|
||||
return self._ParseIf()
|
||||
@@ -184,19 +187,17 @@ class Parser(ExprParserMixin):
|
||||
return self._ParseTry()
|
||||
if v == 'with':
|
||||
return self._ParseWith()
|
||||
# match 多模式匹配(硬关键字):语句起始一律按 match 语句解析。
|
||||
if v == 'match':
|
||||
# 软关键字 match:尝试 match 语句,失败回退为表达式
|
||||
save: int = self.Idx
|
||||
try:
|
||||
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._ParseMatch()
|
||||
# switch 多分支(硬关键字,C switch 语义):desugar 为 is_switch=True 的 Match。
|
||||
if v == _SWITCH_KEYWORD:
|
||||
return self._ParseSwitch()
|
||||
# fallthrough 关键字(硬):仅在 switch 的 case 体内合法,脱糖为
|
||||
# `c.NoBreak()` 标记调用,由 switch 生成侧消费(case 体因此不自动
|
||||
# 跳出 switch)。合法位置由生成侧判定:写进 match 或 switch 之外
|
||||
# 一律 FailFastError。
|
||||
if v == _FALLTHROUGH_KEYWORD:
|
||||
return self._ParseFallThrough()
|
||||
if v == 'return':
|
||||
return self._ParseReturn()
|
||||
@@ -237,21 +238,14 @@ class Parser(ExprParserMixin):
|
||||
return self._ParseSimpleStmt()
|
||||
|
||||
# ==================================================================
|
||||
# fallthrough 软关键字(语句级)
|
||||
# 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 标记)"""
|
||||
"""`fallthrough` 脱糖为 `c.NoBreak()`(switch 生成侧识别的 fallthrough 标记)
|
||||
|
||||
合法位置只有 switch 的 case 体;写进 match 或 switch 之外由生成侧
|
||||
(HandlesMatch / HandlesBody)fail-fast。
|
||||
"""
|
||||
tok: Token = self._Peek()
|
||||
self._Next()
|
||||
marker: N.Call = N.Call(
|
||||
@@ -1309,7 +1303,7 @@ class Parser(ExprParserMixin):
|
||||
return node
|
||||
|
||||
# ==================================================================
|
||||
# match 语句(PEP 634)
|
||||
# match 语句(PEP 634)与 switch 语句(C switch 语义)
|
||||
# ==================================================================
|
||||
def _ParseMatch(self) -> N.Match:
|
||||
start: Token = self._Next() # match
|
||||
@@ -1333,10 +1327,83 @@ class Parser(ExprParserMixin):
|
||||
cases.append(mc)
|
||||
self._SkipNEWLINE()
|
||||
self._Expect(DEDENT)
|
||||
node: N.Match = N.Match(subject=subject, cases=cases)
|
||||
node: N.Match = N.Match(subject=subject, cases=cases, is_switch=False)
|
||||
self._FixLoc(node, start)
|
||||
return node
|
||||
|
||||
def _ParseSwitch(self) -> N.Match:
|
||||
"""switch 语句(C switch 语义):脱糖为 is_switch=True 的 N.Match。
|
||||
|
||||
switch <expr>:
|
||||
case <常量 | 枚举变体>: # 可 `case 1 | 2:` 多值
|
||||
...
|
||||
default:
|
||||
...
|
||||
|
||||
- case 默认不穿透,case 体内写 `fallthrough` 才落到下一个 case;
|
||||
- case 标签只支持常量字面量 / 枚举变体 / 多值或模式,不接受序列、
|
||||
映射、捕获等结构化模式;不支持 if 守卫。
|
||||
"""
|
||||
start: Token = self._Next() # switch
|
||||
subject: N.expr = self.ParseExpr()
|
||||
self._ExpectOp(':')
|
||||
self._ExpectNEWLINE()
|
||||
self._Expect(INDENT)
|
||||
cases: list[N.match_case] = []
|
||||
self._SkipNEWLINE()
|
||||
while True:
|
||||
if self._AtName('case'):
|
||||
case_start: Token = self._Next() # case
|
||||
pattern: N.pattern = self._ParsePattern()
|
||||
self._CheckSwitchCasePattern(pattern)
|
||||
if self._AtName('if'):
|
||||
raise SyntaxError("switch 的 case 不支持 if 守卫")
|
||||
self._ExpectOp(':')
|
||||
case_body: list[N.stmt] = self._ParseSuite()
|
||||
mc: N.match_case = N.match_case(pattern=pattern, guard=None, body=case_body)
|
||||
self._FixLoc(mc, case_start)
|
||||
cases.append(mc)
|
||||
elif self._AtName('default') and self._PeekIsOp(':'):
|
||||
def_start: Token = self._Next() # default
|
||||
self._ExpectOp(':')
|
||||
body: list[N.stmt] = self._ParseSuite()
|
||||
# default: 等价 `case _`(MatchAs 通配)
|
||||
wild: N.pattern = N.MatchAs(pattern=None, name=None)
|
||||
self._FixLoc(wild, def_start)
|
||||
mc = N.match_case(pattern=wild, guard=None, body=body)
|
||||
self._FixLoc(mc, def_start)
|
||||
cases.append(mc)
|
||||
else:
|
||||
break
|
||||
self._SkipNEWLINE()
|
||||
self._Expect(DEDENT)
|
||||
node: N.Match = N.Match(subject=subject, cases=cases, is_switch=True)
|
||||
self._FixLoc(node, start)
|
||||
return node
|
||||
|
||||
def _PeekIsOp(self, value: str) -> bool:
|
||||
"""下一个 token 是否为运算符 `value`(不消费)"""
|
||||
nxt: Token = self._Peek(1)
|
||||
return nxt.Type == OP and nxt.Value == value
|
||||
|
||||
def _CheckSwitchCasePattern(self, pattern: N.pattern) -> None:
|
||||
"""校验 switch 的 case 标签:只允许常量 / 枚举变体 / 多值或模式。
|
||||
|
||||
与其他语法错误一致地 fail-fast(SyntaxError 向上冒泡终止编译),
|
||||
绝不静默接受结构化模式。
|
||||
"""
|
||||
def _IsAllowed(p: N.pattern) -> bool:
|
||||
if isinstance(p, (N.MatchValue, N.MatchSingleton, N.MatchClass)):
|
||||
return True
|
||||
if isinstance(p, N.MatchOr):
|
||||
return len(p.patterns) > 0 and all(_IsAllowed(x) for x in p.patterns)
|
||||
return False
|
||||
if not _IsAllowed(pattern):
|
||||
raise SyntaxError(
|
||||
"switch 的 case 标签非法:只支持常量字面量或枚举变体"
|
||||
"(可多值 `case 1 | 2:`),不接受序列/映射/捕获等结构化模式。"
|
||||
"正确示例:case 1: / case \"a\": / case Color.Red: / case Opt.Some():")
|
||||
|
||||
def _ParsePattern(self) -> N.pattern:
|
||||
"""解析 match 模式(顶层,处理 | 或模式)。"""
|
||||
start: Token = self._Peek()
|
||||
|
||||
Reference in new issue
Block a user