fix(vast_c): C 解析器补齐 match 的 is_switch,并实现 switch / fallthrough
- ParseMatch:Match 由 2 字段改为 3 字段(subject, cases, is_switch=False), 修运行期 AttributeError: 'Match' object has no attribute 'is_switch' - 新增 ParseSwitch:switch 脱糖为 is_switch=True 的 Match,支持 case 常量/枚举 变体/多值或模式与 default:(标签非法或不支持 if 守卫时 fail-fast) - 新增 ParseFallThrough:fallthrough 脱糖为 c.NoBreak() 标记调用 - 语句分派:match / struct 改为硬关键字,新增 switch / fallthrough 分派
This commit is contained in:
1 parent
74cfaeb917
commit
5de9cb6527
1 file changed
+228
-16
+228
-16
@@ -4329,6 +4329,9 @@ static PyObject *ParseFor(Parser *P, int IsAsync);
|
||||
static PyObject *ParseTry(Parser *P);
|
||||
static PyObject *ParseWith(Parser *P, int IsAsync);
|
||||
static PyObject *ParseMatch(Parser *P);
|
||||
static PyObject *ParseSwitch(Parser *P);
|
||||
static PyObject *ParseFallThrough(Parser *P);
|
||||
static int SwitchPatAllowed(PyObject *Pat);
|
||||
static PyObject *ParseFunctionDef(Parser *P, int IsAsync, PyObject *Decorators);
|
||||
static PyObject *ParseClassDef(Parser *P, PyObject *Decorators);
|
||||
static PyObject *ParseExprList(Parser *P);
|
||||
@@ -4430,9 +4433,8 @@ static PyObject *ParseStatement(Parser *P) {
|
||||
if (TokIsName(T, "class")) {
|
||||
return ParseClassDef(P, NULL);
|
||||
}
|
||||
/* struct 纯数据类型定义(软关键字:仅"语句起始 + 后随标识符"生效,
|
||||
* 故 `struct = 5` / `x.struct` 作标识符的写法不受影响) */
|
||||
if (TokIsName(T, "struct") && PeekTok(P, 1)->Kind == TK_NAME) {
|
||||
/* struct 纯数据类型定义(硬关键字:语句起始一律按 struct 定义解析) */
|
||||
if (TokIsName(T, "struct")) {
|
||||
return ParseStructDef(P, NULL);
|
||||
}
|
||||
if (TokIsName(T, "if")) {
|
||||
@@ -4450,18 +4452,18 @@ static PyObject *ParseStatement(Parser *P) {
|
||||
if (TokIsName(T, "with")) {
|
||||
return ParseWith(P, 0);
|
||||
}
|
||||
/* match 多模式匹配(硬关键字:语句起始一律按 match 语句解析) */
|
||||
if (TokIsName(T, "match")) {
|
||||
int SaveIdx = P->Idx;
|
||||
PyObject *M = ParseMatch(P);
|
||||
if (M != NULL) {
|
||||
return M;
|
||||
}
|
||||
if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
P->Idx = SaveIdx;
|
||||
P->Error = 0;
|
||||
/* 回退为表达式语句 */
|
||||
return ParseMatch(P);
|
||||
}
|
||||
/* switch 多分支(硬关键字,C switch 语义):desugar 为 is_switch=True 的 Match */
|
||||
if (TokIsName(T, "switch")) {
|
||||
return ParseSwitch(P);
|
||||
}
|
||||
/* fallthrough 关键字(硬):仅在 switch 的 case 体内合法,脱糖为
|
||||
* `c.NoBreak()` 标记调用,由 switch 生成侧消费 */
|
||||
if (TokIsName(T, "fallthrough")) {
|
||||
return ParseFallThrough(P);
|
||||
}
|
||||
if (TokIsName(T, "return")) {
|
||||
Token *Start = NextTok(P);
|
||||
@@ -6433,8 +6435,8 @@ static PyObject *ParseMatch(Parser *P) {
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *Argv[2] = { Subject, Cases };
|
||||
PyObject *Node = NewNode(P, "Match", Argv, 2);
|
||||
PyObject *Argv[3] = { Subject, Cases, Py_False };
|
||||
PyObject *Node = NewNode(P, "Match", Argv, 3);
|
||||
Py_DECREF(Subject);
|
||||
Py_DECREF(Cases);
|
||||
if (Node != NULL) {
|
||||
@@ -6443,6 +6445,216 @@ static PyObject *ParseMatch(Parser *P) {
|
||||
return Node;
|
||||
}
|
||||
|
||||
/* switch 的 case 标签是否合法:只允许常量 / 枚举变体 / 多值或模式
|
||||
* (MatchValue / MatchSingleton / MatchClass / MatchOr<上述>) */
|
||||
static int SwitchPatAllowed(PyObject *Pat) {
|
||||
if (Pat == NULL) {
|
||||
return 0;
|
||||
}
|
||||
const char *N = Py_TYPE(Pat)->tp_name;
|
||||
if (strcmp(N, "MatchValue") == 0 || strcmp(N, "MatchSingleton") == 0
|
||||
|| strcmp(N, "MatchClass") == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(N, "MatchOr") == 0) {
|
||||
PyObject *Pats = PyObject_GetAttrString(Pat, "patterns");
|
||||
if (Pats == NULL) {
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
if (!PyList_Check(Pats)) {
|
||||
Py_DECREF(Pats);
|
||||
return 0;
|
||||
}
|
||||
Py_ssize_t Count = PyList_Size(Pats);
|
||||
int Ok = (Count > 0);
|
||||
for (Py_ssize_t I = 0; I < Count && Ok; I++) {
|
||||
if (!SwitchPatAllowed(PyList_GetItem(Pats, I))) {
|
||||
Ok = 0;
|
||||
}
|
||||
}
|
||||
Py_DECREF(Pats);
|
||||
return Ok;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* switch 语句(C switch 语义):脱糖为 is_switch=True 的 Match
|
||||
*
|
||||
* switch <expr>:
|
||||
* case <常量 | 枚举变体>: # 可 `case 1 | 2:` 多值
|
||||
* default:
|
||||
*
|
||||
* case 标签只支持常量 / 枚举变体 / 多值或模式,不支持 if 守卫。 */
|
||||
static PyObject *ParseSwitch(Parser *P) {
|
||||
Token *Start = NextTok(P); /* switch */
|
||||
PyObject *Subject = ParseExpr(P);
|
||||
if (Subject == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (!ExpectOp(P, ":")) {
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
if (!ExpectNEWLINE(P)) {
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
Token *IndentTok = NextTok(P);
|
||||
if (IndentTok->Kind != TK_INDENT) {
|
||||
SyntaxErrorAt(P, IndentTok, "expected INDENT");
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *Cases = NewList();
|
||||
if (Cases == NULL) {
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
SkipNEWLINE(P);
|
||||
while (1) {
|
||||
PyObject *Pattern = NULL;
|
||||
Token *CaseStart = NULL;
|
||||
if (AtName(P, "case")) {
|
||||
CaseStart = NextTok(P); /* case */
|
||||
Pattern = ParsePattern(P);
|
||||
if (Pattern == NULL) {
|
||||
Py_DECREF(Cases);
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
if (!SwitchPatAllowed(Pattern)) {
|
||||
SyntaxErrorAt(P, CaseStart,
|
||||
"switch case label must be a constant or enum variant");
|
||||
Py_DECREF(Pattern);
|
||||
Py_DECREF(Cases);
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
if (AtName(P, "if")) {
|
||||
SyntaxErrorAt(P, PeekTok(P, 0), "switch case does not support if guard");
|
||||
Py_DECREF(Pattern);
|
||||
Py_DECREF(Cases);
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
} else if (AtName(P, "default") && TokIsOp(PeekTok(P, 1), ":")) {
|
||||
CaseStart = NextTok(P); /* default */
|
||||
PyObject *WildArgv[2] = { Py_None, Py_None };
|
||||
Pattern = NewNode(P, "MatchAs", WildArgv, 2);
|
||||
if (Pattern == NULL) {
|
||||
Py_DECREF(Cases);
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
FixLoc(P, Pattern, CaseStart);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if (!ExpectOp(P, ":")) {
|
||||
Py_DECREF(Pattern);
|
||||
Py_DECREF(Cases);
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *CaseBody = ParseSuite(P);
|
||||
if (CaseBody == NULL) {
|
||||
Py_DECREF(Pattern);
|
||||
Py_DECREF(Cases);
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *McArgv[3] = { Pattern, Py_None, CaseBody };
|
||||
PyObject *Mc = NewNode(P, "match_case", McArgv, 3);
|
||||
Py_DECREF(Pattern);
|
||||
Py_DECREF(CaseBody);
|
||||
if (Mc == NULL) {
|
||||
Py_DECREF(Cases);
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
PyList_Append(Cases, Mc);
|
||||
Py_DECREF(Mc);
|
||||
SkipNEWLINE(P);
|
||||
}
|
||||
Token *DedentTok = NextTok(P);
|
||||
if (DedentTok->Kind != TK_DEDENT) {
|
||||
SyntaxErrorAt(P, DedentTok, "expected DEDENT");
|
||||
Py_DECREF(Cases);
|
||||
Py_DECREF(Subject);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *Argv[3] = { Subject, Cases, Py_True };
|
||||
PyObject *Node = NewNode(P, "Match", Argv, 3);
|
||||
Py_DECREF(Subject);
|
||||
Py_DECREF(Cases);
|
||||
if (Node != NULL) {
|
||||
FixLoc(P, Node, Start);
|
||||
}
|
||||
return Node;
|
||||
}
|
||||
|
||||
/* `fallthrough` 脱糖为 `c.NoBreak()`(switch 生成侧识别的 fallthrough 标记) */
|
||||
static PyObject *ParseFallThrough(Parser *P) {
|
||||
Token *Start = NextTok(P); /* fallthrough */
|
||||
PyObject *Load1 = MakeCtx(P, "Load");
|
||||
PyObject *IdC = Load1 ? PyUnicode_FromString("c") : NULL;
|
||||
if (Load1 == NULL || IdC == NULL) {
|
||||
Py_XDECREF(Load1);
|
||||
Py_XDECREF(IdC);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *NameArgv[2] = { IdC, Load1 };
|
||||
PyObject *NameNode = NewNode(P, "Name", NameArgv, 2);
|
||||
Py_DECREF(IdC);
|
||||
Py_DECREF(Load1);
|
||||
if (NameNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
FixLoc(P, NameNode, Start);
|
||||
PyObject *Load2 = MakeCtx(P, "Load");
|
||||
PyObject *AttrStr = Load2 ? PyUnicode_FromString("NoBreak") : NULL;
|
||||
if (Load2 == NULL || AttrStr == NULL) {
|
||||
Py_XDECREF(Load2);
|
||||
Py_XDECREF(AttrStr);
|
||||
Py_DECREF(NameNode);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *AttrArgv[3] = { NameNode, AttrStr, Load2 };
|
||||
PyObject *AttrNode = NewNode(P, "Attribute", AttrArgv, 3);
|
||||
Py_DECREF(AttrStr);
|
||||
Py_DECREF(Load2);
|
||||
Py_DECREF(NameNode);
|
||||
if (AttrNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
FixLoc(P, AttrNode, Start);
|
||||
PyObject *Args = NewList();
|
||||
PyObject *Keywords = NewList();
|
||||
if (Args == NULL || Keywords == NULL) {
|
||||
Py_XDECREF(Args);
|
||||
Py_XDECREF(Keywords);
|
||||
Py_DECREF(AttrNode);
|
||||
return NULL;
|
||||
}
|
||||
PyObject *CallArgv[3] = { AttrNode, Args, Keywords };
|
||||
PyObject *CallNode = NewNode(P, "Call", CallArgv, 3);
|
||||
Py_DECREF(Args);
|
||||
Py_DECREF(Keywords);
|
||||
Py_DECREF(AttrNode);
|
||||
if (CallNode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
FixLoc(P, CallNode, Start);
|
||||
PyObject *ExprArgv[1] = { CallNode };
|
||||
PyObject *ExprNode = NewNode(P, "Expr", ExprArgv, 1);
|
||||
Py_DECREF(CallNode);
|
||||
if (ExprNode != NULL) {
|
||||
FixLoc(P, ExprNode, Start);
|
||||
}
|
||||
return ExprNode;
|
||||
}
|
||||
|
||||
/* 模式(顶层,处理 | 或模式) */
|
||||
static PyObject *ParsePattern(Parser *P) {
|
||||
Token *Start = PeekTok(P, 0);
|
||||
|
||||
Reference in new issue
Block a user