feat(ast): 新增 switch 关键字与 switch 语句,match 完全禁止 fallthrough
- tokens.vp:Keyword 表尾追加 Switch / FallThrough,并 intern "switch"/"fallthrough"(既有编号不变) - stmts.vp:Match 增加 is_switch 字段(switch 语法糖 desugar 的标记);FallThrough 注释改为仅 switch 合法 - parser.vp:match / fallthrough 改为硬关键字分派(去掉软关键字 lookahead);新增 _parse_switch(case 常量/枚举变体/多值 + default:) - lexer.vp:局部变量 match 改名 Matched(match 已为硬关键字)
This commit is contained in:
1 parent
b85f49ab35
commit
2c5b39456b
4 files changed
+181
-74
No files matched your search
+3
-3
@@ -1003,14 +1003,14 @@ def _read_operator(lx: rawptr[Lexer]):
|
||||
while cur != None:
|
||||
if cur.length <= int(remaining):
|
||||
# Compare cur.name with src[pos:pos+cur.length]
|
||||
match: int = 1
|
||||
Matched: int = 1
|
||||
i: sizet = 0
|
||||
while i < sizet(cur.length):
|
||||
if lx.src[lx.pos + i] != cur.name[i]:
|
||||
match = 0
|
||||
Matched = 0
|
||||
break
|
||||
i += 1
|
||||
if match:
|
||||
if Matched != 0:
|
||||
if cur.length > best_len:
|
||||
best_len = cur.length
|
||||
best_entry = cur
|
||||
|
||||
+147
-63
@@ -285,60 +285,6 @@ def _is_export_extern_var(ps: rawptr[Parser]) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
def _is_match_stmt(ps: rawptr[Parser]) -> int:
|
||||
"""match 是软关键字(CPython 3.10+):lookahead 判断是否为 match 语句
|
||||
|
||||
match 后直接跟 ':'(变量注解,如 lexer.vp 的 `match: int = 1`)、
|
||||
'='(赋值,如 `match = 0`)、换行/文件尾 → 按普通标识符;
|
||||
否则扫描至行尾,在括号深度 0 处出现 ':' → match 语句(subject 后必须冒号)。
|
||||
case/type 同理为软关键字(case 仅在 match 套件内消费,type 无语句分发)。
|
||||
"""
|
||||
nxt: rawptr[Token] = ps.cur.next
|
||||
if nxt is None: return 0
|
||||
if nxt.type == TokenType.NewLine or nxt.type == TokenType.EndMarker: return 0
|
||||
if nxt.type == TokenType.Op:
|
||||
if nxt.op_subtype == TokOp.Colon: return 0
|
||||
if nxt.op_subtype == TokOp.Equal: return 0
|
||||
walk: rawptr[Token] = nxt
|
||||
depth: int = 0
|
||||
while walk is not None:
|
||||
if walk.type == TokenType.NewLine or walk.type == TokenType.EndMarker:
|
||||
return 0
|
||||
if walk.type == TokenType.Op:
|
||||
wop: int = walk.op_subtype
|
||||
if wop == TokOp.LPar or wop == TokOp.Lsqb or wop == TokOp.LBrace:
|
||||
depth += 1
|
||||
elif wop == TokOp.RPar or wop == TokOp.Rsqb or wop == TokOp.RBrace:
|
||||
if depth > 0:
|
||||
depth -= 1
|
||||
elif wop == TokOp.Colon and depth == 0:
|
||||
return 1
|
||||
walk = walk.next
|
||||
return 0
|
||||
|
||||
|
||||
def _is_fallthrough_kw(ps: rawptr[Parser]) -> int:
|
||||
"""fallthrough 是软关键字(语句级):lookahead 判断整条语句是否为 `fallthrough`
|
||||
|
||||
仅当当前 Name 恰为 `fallthrough` 且后随 换行 / ';' / 文件尾 时命中。
|
||||
作标识符/属性的写法(`fallthrough = 1` 赋值、`fallthrough(x)` 调用、
|
||||
`d.fallthrough` 属性)都不命中,语义不受影响。
|
||||
"""
|
||||
if _cur_type(ps) != TokenType.Name:
|
||||
return 0
|
||||
s: str = _cur_str(ps)
|
||||
if s is None or string.strcmp(s, "fallthrough") != 0:
|
||||
return 0
|
||||
nxt: rawptr[Token] = ps.cur.next
|
||||
if nxt is None:
|
||||
return 0
|
||||
if nxt.type == TokenType.NewLine or nxt.type == TokenType.EndMarker:
|
||||
return 1
|
||||
if nxt.type == TokenType.Op and nxt.op_subtype == TokOp.Semi:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
# 解析错误位置与消息(fail-fast 仿 Python SyntaxError 报告,首次错误记录)
|
||||
_g_parse_err_line: int = 0
|
||||
_g_parse_err_col: int = 0
|
||||
@@ -2902,7 +2848,7 @@ def _parse_statement_into(ps: rawptr[Parser], parent: rawptr[AST]):
|
||||
if stmt is not None:
|
||||
parent.append(stmt)
|
||||
return
|
||||
if (kw == Keyword.Match and _is_match_stmt(ps) != 0) or kw == Keyword.Async:
|
||||
if kw == Keyword.Match or kw == Keyword.Switch or kw == Keyword.Async:
|
||||
stmt = _parse_compound(ps)
|
||||
if stmt is not None:
|
||||
parent.append(stmt)
|
||||
@@ -2965,12 +2911,10 @@ def _parse_simple_stmt_into(ps: rawptr[Parser], parent: rawptr[AST]):
|
||||
def _parse_small_stmt(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""small_stmt: various simple statements"""
|
||||
kw: int = _cur_kw(ps)
|
||||
# fallthrough 软关键字(语句级):仅当整条 small_stmt 就是 `fallthrough`
|
||||
# (后随 换行 / ';' / 文件尾)时产出 FallThrough 节点。作标识符的写法
|
||||
# (`fallthrough = 1` / `fallthrough(x)` / `d.fallthrough`)不命中,
|
||||
# 仍走下方通用表达式分派(软关键字边界)。语义由 match 生成侧消费:
|
||||
# case 体含该标记时不自动 break,落到下一个 case 体。
|
||||
if _is_fallthrough_kw(ps) != 0:
|
||||
# fallthrough 关键字(硬):仅在 switch 的 case 体内合法,语义是"落到下一个
|
||||
# case 体"。合法位置由生成侧(StmtGenDeclMixin.translate_fallthrough)判定,
|
||||
# 在 match 内或 switch 外写出即 fail-fast。
|
||||
if kw == Keyword.FallThrough:
|
||||
ft_lineno: int = _cur_lineno(ps)
|
||||
ft_col: int = _cur_col(ps)
|
||||
_advance(ps)
|
||||
@@ -3366,8 +3310,10 @@ def _parse_compound(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
return _parse_with(ps, 1)
|
||||
ps.error_count += 1
|
||||
return None
|
||||
if kw == Keyword.Match and _is_match_stmt(ps) != 0:
|
||||
if kw == Keyword.Match:
|
||||
return _parse_match(ps)
|
||||
if kw == Keyword.Switch:
|
||||
return _parse_switch(ps)
|
||||
ps.error_count += 1
|
||||
return None
|
||||
|
||||
@@ -4244,7 +4190,7 @@ def _parse_match(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
subject: rawptr[AST] = _parse_test(ps)
|
||||
_expect_op(ps, TokOp.Colon)
|
||||
cases: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
node: rawptr[AST] = Match(ps.pool, subject, cases)
|
||||
node: rawptr[AST] = Match(ps.pool, subject, cases, 0)
|
||||
# Parsing body: Same NewLine/Indent/Dedent pattern as _parse_suite_into_children,
|
||||
# but parses case clauses into a cases list instead of normal statements into children
|
||||
_skip_nl(ps)
|
||||
@@ -4265,6 +4211,144 @@ def _parse_match(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
return node
|
||||
|
||||
|
||||
# ============================================================
|
||||
# switch statement(C switch 语义,desugar 为 Match(is_switch=1))
|
||||
#
|
||||
# switch <expr>:
|
||||
# case <常量 | 枚举变体>: # 可 `case 1 | 2:` 多值
|
||||
# ...
|
||||
# default:
|
||||
# ...
|
||||
#
|
||||
# 规则与 C switch 一致:case 默认**不穿透**(每个 case 体末尾自动跳出 switch),
|
||||
# 只有 case 体内写 `fallthrough` 才落到下一个 case。case 标签只支持常量字面量 /
|
||||
# 枚举变体(不支持序列、映射等结构化模式),与 match 的丰富模式区分开。
|
||||
# ============================================================
|
||||
|
||||
def _is_switch_default_kw(ps: rawptr[Parser]) -> int:
|
||||
"""`default:` 是 switch 套件内的上下文关键字:仅当 `default` 后紧跟 ':' 时命中"""
|
||||
if _cur_type(ps) != TokenType.Name:
|
||||
return 0
|
||||
s: str = _cur_str(ps)
|
||||
if s is None or string.strcmp(s, "default") != 0:
|
||||
return 0
|
||||
nxt: rawptr[Token] = ps.cur.next
|
||||
if nxt is None:
|
||||
return 0
|
||||
if nxt.type == TokenType.Op and nxt.op_subtype == TokOp.Colon:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _RecordSwitchCaseErr(detail: str, lineno: int, col: int):
|
||||
"""记录 switch 的 case 标签非法(fail-fast;只记首次)"""
|
||||
global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg
|
||||
if _g_parse_err_line != 0:
|
||||
return
|
||||
_g_parse_err_line = lineno
|
||||
_g_parse_err_col = col
|
||||
buf: str = stdlib.malloc(512)
|
||||
if buf is not None:
|
||||
stdio.snprintf(buf, 512,
|
||||
"switch 的 case 标签非法(%s)。switch 只支持常量字面量或枚举变体标签"
|
||||
"(可多值 `case 1 | 2:`),不接受序列/映射/捕获等结构化模式。"
|
||||
"正确示例:case 1: / case \"a\": / case Color.Red: / case Opt.Some():", detail)
|
||||
_g_parse_err_msg = buf
|
||||
else:
|
||||
_g_parse_err_msg = "switch 的 case 标签非法"
|
||||
|
||||
|
||||
def _CheckSwitchCasePattern(ps: rawptr[Parser], pat: rawptr[AST]) -> int:
|
||||
"""校验 switch 的 case 标签是否合法(只允许常量 / 枚举变体 / 多值或模式)"""
|
||||
if pat is None:
|
||||
return 0
|
||||
k: int = pat.kind()
|
||||
if k == ASTKind.MatchValue or k == ASTKind.MatchSingleton or k == ASTKind.MatchClass:
|
||||
return 1
|
||||
if k == ASTKind.MatchOr:
|
||||
mo: rawptr[MatchOr] = rawptr[MatchOr](pat)
|
||||
pats: rawptr[list[rawptr[AST]]] = mo.patterns
|
||||
if pats is None or len(pats) == 0:
|
||||
return 0
|
||||
i: sizet = 0
|
||||
n: sizet = len(pats)
|
||||
while i < n:
|
||||
sub: rawptr[AST] = pats.get(i)
|
||||
if sub is None:
|
||||
return 0
|
||||
sk: int = sub.kind()
|
||||
if sk != ASTKind.MatchValue and sk != ASTKind.MatchSingleton \
|
||||
and sk != ASTKind.MatchClass:
|
||||
return 0
|
||||
i = i + 1
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _parse_switch_case(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""switch 的 `case <常量|枚举变体> ':' suite`(不支持 guard)"""
|
||||
lineno: int = _cur_lineno(ps)
|
||||
col: int = _cur_col(ps)
|
||||
_advance(ps) # case
|
||||
pattern: rawptr[AST] = _parse_pattern(ps)
|
||||
if _CheckSwitchCasePattern(ps, pattern) == 0:
|
||||
_RecordSwitchCaseErr("case 标签不是常量/枚举变体", lineno, col)
|
||||
ps.error_count += 1
|
||||
if _cur_kw(ps) == Keyword.If:
|
||||
_RecordSwitchCaseErr("switch 的 case 不支持 if 守卫", _cur_lineno(ps), _cur_col(ps))
|
||||
ps.error_count += 1
|
||||
_expect_op(ps, TokOp.Colon)
|
||||
node: rawptr[AST] = MatchCase(ps.pool, pattern, None)
|
||||
_parse_suite_into_children(ps, node)
|
||||
_set_pos(node, lineno, col, 0, 0)
|
||||
return node
|
||||
|
||||
|
||||
def _parse_switch_default(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""switch 的 `default ':' suite` —— desugar 为 `case _`(MatchAs 通配)"""
|
||||
lineno: int = _cur_lineno(ps)
|
||||
col: int = _cur_col(ps)
|
||||
_advance(ps) # default
|
||||
_expect_op(ps, TokOp.Colon)
|
||||
wild: rawptr[AST] = MatchAs(ps.pool, None, None)
|
||||
_set_pos(wild, lineno, col, 0, 0)
|
||||
node: rawptr[AST] = MatchCase(ps.pool, wild, None)
|
||||
_parse_suite_into_children(ps, node)
|
||||
_set_pos(node, lineno, col, 0, 0)
|
||||
return node
|
||||
|
||||
|
||||
def _parse_switch(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""switch statement: switch subject ':' NewLine Indent (case|default)+ Dedent"""
|
||||
lineno: int = _cur_lineno(ps)
|
||||
col: int = _cur_col(ps)
|
||||
_advance(ps) # switch
|
||||
subject: rawptr[AST] = _parse_test(ps)
|
||||
_expect_op(ps, TokOp.Colon)
|
||||
cases: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
node: rawptr[AST] = Match(ps.pool, subject, cases, 1)
|
||||
_skip_nl(ps)
|
||||
if _cur_type(ps) == TokenType.NewLine:
|
||||
_advance(ps)
|
||||
_skip_nl(ps)
|
||||
if _cur_type(ps) == TokenType.Indent:
|
||||
_advance(ps)
|
||||
while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker:
|
||||
if _cur_kw(ps) == Keyword.Case:
|
||||
case_node: rawptr[AST] = _parse_switch_case(ps)
|
||||
if case_node is not None:
|
||||
cases.append(case_node)
|
||||
elif _is_switch_default_kw(ps) != 0:
|
||||
def_node: rawptr[AST] = _parse_switch_default(ps)
|
||||
if def_node is not None:
|
||||
cases.append(def_node)
|
||||
else:
|
||||
_advance(ps) # Skip non-case token to avoid infinite loop
|
||||
_expect_type(ps, TokenType.Dedent)
|
||||
_set_pos(node, lineno, col, 0, 0)
|
||||
return node
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Match pattern parsing
|
||||
# ============================================================
|
||||
|
||||
+19
-7
@@ -982,11 +982,11 @@ class Continue(Stmt):
|
||||
|
||||
|
||||
# ============================================================
|
||||
# FallThrough Node(软关键字 `fallthrough`,仅 match-case 体内合法)
|
||||
# FallThrough Node(关键字 `fallthrough`,仅 switch 的 case 体内合法)
|
||||
# ============================================================
|
||||
|
||||
class FallThrough(Stmt):
|
||||
"""FallThrough() —— case 体不自动 break,落到下一个 case 体"""
|
||||
"""FallThrough() —— switch 的 case 体不自动 break,落到下一个 case 体"""
|
||||
|
||||
def __new__(self, pool: rawptr[memhub.MemManager]):
|
||||
ptr: rawptr[FallThrough] = pool.alloc(FallThrough.__sizeof__())
|
||||
@@ -1090,23 +1090,33 @@ class ImportFrom(Stmt):
|
||||
# ============================================================
|
||||
|
||||
class Match(Stmt):
|
||||
"""Match(subject: AST, cases: list[rawptr[AST]])"""
|
||||
subject: rawptr[AST]
|
||||
cases: rawptr[list[rawptr[AST]]]
|
||||
"""Match(subject: AST, cases: list[rawptr[AST]], is_switch: int)
|
||||
|
||||
is_switch=1 表示本节点由 `switch <expr>:` 语法糖 desugar 而来(见 parser.vp 的
|
||||
_parse_switch):语义是 C 的 switch —— case 默认不穿透,case 体内写
|
||||
`fallthrough` 才落到下一个 case。is_switch=0 为真正的 `match` 语句,
|
||||
**完全禁止 fallthrough**(写出即 fail-fast)。
|
||||
"""
|
||||
subject: rawptr[AST]
|
||||
cases: rawptr[list[rawptr[AST]]]
|
||||
is_switch: int
|
||||
|
||||
def __new__(self, pool: rawptr[memhub.MemManager],
|
||||
subject: rawptr[AST],
|
||||
cases: rawptr[list[rawptr[AST]]]):
|
||||
cases: rawptr[list[rawptr[AST]]],
|
||||
is_switch: int):
|
||||
ptr: rawptr[Match] = pool.alloc(Match.__sizeof__())
|
||||
if ptr: string.memset(ptr, 0, Match.__sizeof__())
|
||||
return ptr
|
||||
|
||||
def __init__(self, pool: rawptr[memhub.MemManager],
|
||||
subject: rawptr[AST],
|
||||
cases: rawptr[list[rawptr[AST]]]):
|
||||
cases: rawptr[list[rawptr[AST]]],
|
||||
is_switch: int):
|
||||
_init_ast(self, pool, 0, 0)
|
||||
self.subject = subject
|
||||
self.cases = cases
|
||||
self.is_switch = is_switch
|
||||
_set_parent_list(cases, self)
|
||||
|
||||
def kind(self) -> int:
|
||||
@@ -1123,5 +1133,7 @@ class Match(Stmt):
|
||||
pos = _emit(buf, size, pos, "None")
|
||||
pos = _emit(buf, size, pos, ", cases=")
|
||||
pos = _dump_list(self.cases, buf, size, pos)
|
||||
pos = _emit(buf, size, pos, ", is_switch=")
|
||||
pos = _emit_int(buf, size, pos, int64(self.is_switch))
|
||||
pos = _emit(buf, size, pos, ")")
|
||||
return pos
|
||||
+12
-1
@@ -142,6 +142,14 @@ class Keyword(t.CEnum):
|
||||
# 全仓 .vp 源码中 `struct` 不作标识符使用(仅出现在注释/字符串与
|
||||
# build_gep_struct 之类复合标识符里)。
|
||||
Struct: t.State
|
||||
# switch 多分支语句关键字(`switch <expr>:` + `case <常量>: ` / `default:`,
|
||||
# 见 parser.vp 的 _parse_switch)。C switch 语义:默认**不穿透**,case 体内
|
||||
# 写 `fallthrough` 才落到下一个 case。同样追加在**表尾**:既有关键字编号
|
||||
# (kw_subtype)逐字不变;全仓 .vp 源码中 `switch` 不作标识符使用。
|
||||
Switch: t.State
|
||||
# fallthrough 语句关键字(仅在 switch 的 case 体内合法;match 内使用报错)。
|
||||
# 追加在**表尾**:既有关键字编号(kw_subtype)逐字不变。
|
||||
FallThrough: t.State
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -419,7 +427,10 @@ def _init_keywords(pool: rawptr[memhub.MemManager]):
|
||||
_kw_intern(pool, "renum", Keyword.Renum)
|
||||
# struct 纯数据类型定义关键字(语法糖,同 enum/union/renum,见 parser.vp 的
|
||||
# _parse_structdef:基类 t.CStruct + 伪装饰器 @t.NoVTable,禁 virtual/novirtual、禁 def)
|
||||
_kw_intern(pool, "struct", Keyword.Struct)
|
||||
_kw_intern(pool, "struct", Keyword.Struct)
|
||||
# switch / fallthrough 关键字(见 parser.vp 的 _parse_switch 与 _parse_small_stmt)
|
||||
_kw_intern(pool, "switch", Keyword.Switch)
|
||||
_kw_intern(pool, "fallthrough", Keyword.FallThrough)
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
Reference in new issue
Block a user