两种写法同源,最终都产出与手写 @t.CStatic / x: t.CStatic|T 逐字段同形的标记节点,故 TransPyV 侧零新增解析判据: - static def f(...):消费 static 后合成 @t.CStatic 装饰器(与 inline def 同一套 pending_decorators 通道,见 _ParseStaticModified)。 - static x: int = 0:置 Parser.pending_static,由 _parse_type_ann 在描述符序列首位插入 t.CStatic(= 手写 t.CStatic | int),随即清零只作用于紧随的一个注解。 - 软关键字边界:只在语句起始、且后随 def 或 Name: 时命中(_IsStaticModifier);作标识符的位置(属性名/形参名/表达式内)不经过该判据。 语义(禁止跨模块使用)由 TransPyV 侧消费。
4378 lines
185 KiB
Plaintext
4378 lines
185 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import memhub
|
||
import string
|
||
import stdio
|
||
import stdlib
|
||
from .tokens import (Token, TokenType, Keyword, TokOp)
|
||
from .base import (
|
||
AST, ASTCtx, ASTKind, OpKind, ASTFlag,
|
||
_set_pos, _binop_from_op, _augop_from_op, _cmpop_from_op, _copy_str, _inherit_pos,
|
||
_init_ast,
|
||
CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR,
|
||
FLAG_IS_ASYNC, FLAG_IS_EXTERN, FLAG_IS_EXPORT, FLAG_IS_DEFINE,
|
||
)
|
||
from .exprs import (
|
||
BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set,
|
||
ListComp, SetComp, DictComp, GeneratorExp,
|
||
Await, Yield, YieldFrom, FormattedValue, JoinedStr,
|
||
Constant, NamedExpr, Attribute, Subscript, Starred, Name,
|
||
List, Tuple, Slice, Call, Compare, OpNode,
|
||
)
|
||
from .stmts import (
|
||
Module, Expression, Interactive, FunctionType,
|
||
FunctionDef, ClassDef, Return, Delete, Assign, AugAssign, AnnAssign,
|
||
For, While, If, With, Raise, Try, Assert, Global, Nonlocal,
|
||
Pass, Break, Continue, Expr, Import, ImportFrom, Match,
|
||
)
|
||
from .astaux import (
|
||
ExceptHandler, Arguments, Arg, Keyword as KwNode, Alias, WithItem, Comprehension,
|
||
)
|
||
from .match import (
|
||
MatchCase, MatchValue, MatchSingleton, MatchSequence,
|
||
MatchMapping, MatchClass, MatchStar, MatchAs, MatchOr,
|
||
)
|
||
from .lexer import (
|
||
Lexer, _lexer_init, tokenize, new_lexer,
|
||
get_lex_err_line, get_lex_err_col, get_lex_err_msg,
|
||
_lex_int_neg_exceeds_i64,
|
||
)
|
||
|
||
|
||
# ============================================================
|
||
# Parser State
|
||
# ============================================================
|
||
|
||
# token 链表长度上限(成环检测的硬闸):正常源码的 token 数远低于此(200KB 源文件
|
||
# 约数万 token);走到这个数还没到尾即判为链表成环(见 parse_tokens)。
|
||
define PARSER_MAX_TOKENS = 8000000
|
||
|
||
|
||
class Parser:
|
||
tokens: rawptr[Token] # token linked list head
|
||
cur: rawptr[Token] # current token
|
||
prev: rawptr[Token] # 上一个被消费的 token(供 `T*(x)` 紧邻判据)
|
||
pool: rawptr[memhub.MemManager] # memory pool
|
||
error_count: int # error count
|
||
pending_decorators: rawptr[list[rawptr[AST]]] # pending decorators list
|
||
# `static` 修饰符(软关键字)挂起标记:语句起始消费 `static`(后随变量声明
|
||
# `Name :`)后置 1,由紧随的 `_parse_type_ann` 在类型联合最左插入 t.CStatic
|
||
# 标记(`static x: int = 0` → `x: t.CStatic | int = 0`),插入后立即清零,
|
||
# 只作用于紧随其后的这一个注解。函数分支不经此通道(改走合成 @t.CStatic
|
||
# 装饰器,与 inline def 同构,见 _ParseStaticModified)。
|
||
pending_static: int
|
||
# 当前正在解析的套件的**直接拥有者**是否 class(1=类体,0=其它)。
|
||
# 由 _parse_suite_into_children 按 owner_is_class 维护,供 virtual/novirtual
|
||
# 修饰符判"方法级修饰符是否直接写在类体内"(见 _ParseVirtualModified)。
|
||
in_class_body: int
|
||
# 步数预算(_advance 计数 / parse_tokens 预算):语句主循环一旦超预算即
|
||
# fail-fast 报错返回,绝不无限循环(见 parse_tokens 的成环检测)。
|
||
step_count: sizet
|
||
step_limit: sizet
|
||
# 看门狗:对"当前 token 查询"的计数与收束标志(见 _tick)。
|
||
# tick_count 超 step_limit ⇒ aborted=1,解析各入口改返回收束值,
|
||
# 所有 while 循环常数轮内结束(任何原地打转都不可能存活)。
|
||
tick_count: sizet
|
||
aborted: int
|
||
|
||
def __new__(self, pool: rawptr[memhub.MemManager]):
|
||
ptr: rawptr[Parser] = pool.alloc(Parser.__sizeof__())
|
||
if ptr: string.memset(ptr, 0, Parser.__sizeof__())
|
||
return ptr
|
||
|
||
|
||
def new_parser(pool: rawptr[memhub.MemManager]) -> rawptr[Parser]:
|
||
"""Allocate from the pool and return the Parser (cross-module safe wrapper)"""
|
||
return Parser(pool)
|
||
|
||
|
||
def _parser_init(ps: rawptr[Parser], tokens: rawptr[Token],
|
||
pool: rawptr[memhub.MemManager]):
|
||
if ps is None: return
|
||
ps.tokens = tokens
|
||
ps.cur = tokens
|
||
ps.prev = None
|
||
ps.pool = pool
|
||
ps.error_count = 0
|
||
ps.pending_decorators = None
|
||
ps.pending_static = 0
|
||
ps.in_class_body = 0
|
||
ps.step_count = 0
|
||
# 默认预算取上限(parse_tokens 会按实际 token 数收紧):保证不经过 parse_tokens
|
||
# 的入口(parse_expression 等)不会因预算为 0 而误报;同时仍有硬上界防死循环。
|
||
ps.step_limit = PARSER_MAX_TOKENS
|
||
ps.tick_count = 0
|
||
ps.aborted = 0
|
||
|
||
|
||
# ============================================================
|
||
# 解析看门狗(静态收口:杜绝"无输出卡死")
|
||
#
|
||
# 任何"原地打转、一个 token 也不消费"的解析循环,都必然反复查询当前 token;因此把
|
||
# 「查询当前 token」的每个入口(_cur_type / _cur_op / _cur_kw / _match_op /
|
||
# _accept_op / _advance)都记一次 tick,超出预算即置 aborted + 记录解析错误,并让
|
||
# 这些入口返回**收束值**:
|
||
# _cur_type → EndMarker(结束 `while _cur_type != X` 类循环)
|
||
# _cur_op / _cur_kw → 0(结束 `while True` + 运算符不匹配则 break 类循环)
|
||
# _match_op / _accept_op → 还有 token 则返回 1 并一次性跳到末尾(结束
|
||
# `while not _match_op(...)` 与 `while _match_op(...)` 两类循环,≤2 轮)
|
||
# _advance → 直接跳到末尾
|
||
# 于是 parse_tokens 以 error_count > 0 返回 None → 调用方按 SyntaxError 报
|
||
# 文件:行:列 终止编译,**不再存在任何静默卡死路径**。
|
||
# ============================================================
|
||
|
||
def _tick(ps: rawptr[Parser]):
|
||
"""推进看门狗;超预算即置 aborted 并记录首个解析错误(含当前位置)"""
|
||
if ps is None: return
|
||
if ps.aborted != 0: return
|
||
ps.tick_count += 1
|
||
if ps.tick_count > ps.step_limit:
|
||
ps.aborted = 1
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
|
||
|
||
# ============================================================
|
||
# Token Navigation Helpers
|
||
# ============================================================
|
||
|
||
def _advance(ps: rawptr[Parser]):
|
||
"""Advance to the next token"""
|
||
_tick(ps)
|
||
if ps.aborted != 0:
|
||
ps.cur = None
|
||
return
|
||
if ps.cur is None: return
|
||
if ps.cur.next is not None:
|
||
ps.prev = ps.cur
|
||
ps.cur = ps.cur.next
|
||
ps.step_count += 1
|
||
|
||
|
||
def _cur_type(ps: rawptr[Parser]) -> int:
|
||
_tick(ps)
|
||
if ps.aborted != 0: return TokenType.EndMarker
|
||
if ps.cur is None: return TokenType.EndMarker
|
||
return ps.cur.type
|
||
|
||
|
||
def _cur_op(ps: rawptr[Parser]) -> int:
|
||
_tick(ps)
|
||
if ps.aborted != 0: return 0
|
||
if ps.cur is None: return 0
|
||
return ps.cur.op_subtype
|
||
|
||
|
||
def _cur_kw(ps: rawptr[Parser]) -> int:
|
||
_tick(ps)
|
||
if ps.aborted != 0: return 0
|
||
if ps.cur is None: return 0
|
||
return ps.cur.kw_subtype
|
||
|
||
|
||
def _is_funcdef_kw(ps: rawptr[Parser], kw: int) -> int:
|
||
"""判断当前 token 是否为函数定义关键字(def 及其别名 fn/fun/func/function,
|
||
以及 extern/export 声明关键字)
|
||
|
||
def 是硬关键字直接判定;fn/fun/func/function/extern/export 是软关键字,
|
||
仅当下一个 token 是 Name 且再下一个是 '('(或泛型 '[T]' 前的 '[')时
|
||
判定为函数定义。extern 还允许 'extern def A(...)' 形式(下一个是 def),
|
||
export 同样允许 'export def A(...)' 形式。
|
||
否则按普通标识符处理,兼容既有代码把 func 用作
|
||
字段/参数名(如 AST 节点 func 字段、Call.func 注解 `func: rawptr[AST]`)。
|
||
"""
|
||
if kw == Keyword.Def:
|
||
return 1
|
||
if kw == Keyword.Extern or kw == Keyword.Export:
|
||
# extern/export def A(...) 或 extern/export A(...)
|
||
# (extern/export 后是 def 或 Name+'(/'[')
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
if nxt is None: return 0
|
||
if nxt.kw_subtype == Keyword.Def: return 1
|
||
if nxt.type != TokenType.Name: return 0
|
||
nxt2: rawptr[Token] = nxt.next
|
||
if nxt2 is None: return 0
|
||
if nxt2.type == TokenType.Op and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb):
|
||
return 1
|
||
return 0
|
||
if kw == Keyword.Fn or kw == Keyword.Fun \
|
||
or kw == Keyword.Func or kw == Keyword.Function \
|
||
or kw == Keyword.Define:
|
||
# 软关键字 lookahead: 下个是 Name,再下个是 '(' 或 '[T...]' 泛型参数(PEP 695)
|
||
# (define 宏函数与 def 语义一致,返回注解自动追加 t.CDefine)
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
if nxt is None:
|
||
return 0
|
||
if nxt.type != TokenType.Name:
|
||
return 0
|
||
nxt2: rawptr[Token] = nxt.next
|
||
if nxt2 is None:
|
||
return 0
|
||
if nxt2.type == TokenType.Op \
|
||
and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb):
|
||
return 1
|
||
return 0
|
||
return 0
|
||
|
||
|
||
def _is_define_const(ps: rawptr[Parser]) -> int:
|
||
"""define 常量声明的软关键字 lookahead(1=是,0=否)。
|
||
|
||
`define NAME[: T] = <字面量>`:下一个 token 是 Name,再下一个是 '='(无注解)
|
||
或 ':'(带注解)。
|
||
- `define NAME(...)`(宏函数,`_is_funcdef_kw` 的既有形态)第二个 token 是
|
||
'(' / '[' ⇒ 此处不命中,宏函数语义逐字不变;
|
||
- 把 define 当标识符的写法(`define = 5` / `define(x)`)同样不命中;
|
||
- `define NAME: T`(**缺 `=`**)会命中(下一个是 ':'),随后在
|
||
`_parse_define_const` 里 `_expect_op(Equal)` 失败 ⇒ 响亮失败(非静默)。
|
||
"""
|
||
if _cur_kw(ps) != Keyword.Define: return 0
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
if nxt is None or nxt.type != TokenType.Name: return 0
|
||
nxt2: rawptr[Token] = nxt.next
|
||
if nxt2 is None or nxt2.type != TokenType.Op: return 0
|
||
if nxt2.op_subtype == TokOp.Equal or nxt2.op_subtype == TokOp.Colon: return 1
|
||
return 0
|
||
|
||
|
||
def _is_typedef(ps: rawptr[Parser]) -> int:
|
||
"""typedef 类型别名声明的软关键字判定(1=是,0=否)。
|
||
|
||
`typedef T1 = T2` 与 `T1: t.CTypedef = T2` **完全等价**(同族同风格于
|
||
`enum`/`define`:展开为既有形态,落到同一条 typedef 注册/查找路径)。
|
||
|
||
`typedef` 是**语句级保留字**:全仓冲突普查确认它只在注释/文档字符串中出现,
|
||
没有任何变量/参数/字段/函数/类/模块以其命名 ⇒ 语句起始出现 `typedef` 即按
|
||
本语法处理;非法形态(`typedef = 1` / `typedef A` / `typedef A = ` 缺 RHS)
|
||
在 `_parse_typedef` 内 `_record_parse_err` **响亮失败**(非静默)。
|
||
"""
|
||
if _cur_kw(ps) != Keyword.Typedef:
|
||
return 0
|
||
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
|
||
|
||
|
||
# 解析错误位置与消息(fail-fast 仿 Python SyntaxError 报告,首次错误记录)
|
||
_g_parse_err_line: int = 0
|
||
_g_parse_err_col: int = 0
|
||
_g_parse_err_msg: str = None
|
||
|
||
|
||
def _record_parse_err(ps: rawptr[Parser]):
|
||
"""记录首个解析错误的位置与消息(只记录第一次,后续错误不覆盖)"""
|
||
global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg
|
||
if _g_parse_err_line != 0:
|
||
return
|
||
_g_parse_err_line = _cur_lineno(ps)
|
||
_g_parse_err_col = _cur_col(ps)
|
||
err_s: str = _cur_str(ps)
|
||
if err_s is None:
|
||
err_s = "(none)"
|
||
# 消息文本用 stdlib 拷贝(token 的 str_val 在 pool 中,生命周期覆盖本次编译但保险起见独立存储)
|
||
buf_m: str = stdlib.malloc(128)
|
||
if buf_m is not None:
|
||
stdio.snprintf(buf_m, 128, "意外的 token '%s'", err_s)
|
||
_g_parse_err_msg = buf_m
|
||
else:
|
||
_g_parse_err_msg = err_s
|
||
|
||
|
||
def _record_parse_err_ctx(ps: rawptr[Parser], tok_type: int,
|
||
pv_str: str, pv_line: int, pv_col: int,
|
||
nx_str: str, nx_line: int, nx_col: int):
|
||
"""记录首个解析错误,并把 token 类型 + 前/后 token 作为附注并入消息
|
||
|
||
与 `_record_parse_err` 同源(只记首次、同位置口径),差别只在消息更全:
|
||
意外的 token 'x'(type=4)
|
||
附注: prev='union' (line 420 col 0) | next=':' (line 420 col 8)
|
||
`文件:行:列` + 源码行 + caret + `SyntaxError: <消息>` 由调用方的
|
||
dump_parse_traceback 统一补全(仿 fatal_error),解析器不再自拼单行报文。
|
||
"""
|
||
global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg
|
||
if _g_parse_err_line != 0:
|
||
return
|
||
_g_parse_err_line = _cur_lineno(ps)
|
||
_g_parse_err_col = _cur_col(ps)
|
||
err_s: str = _cur_str(ps)
|
||
if err_s is None:
|
||
err_s = "(none)"
|
||
buf_m: str = stdlib.malloc(512)
|
||
if buf_m is None:
|
||
_g_parse_err_msg = err_s
|
||
return
|
||
stdio.snprintf(buf_m, 512,
|
||
"意外的 token '%s'(type=%d)\n 附注: prev='%s' (line %d col %d) | next='%s' (line %d col %d)",
|
||
err_s, tok_type, pv_str, pv_line, pv_col, nx_str, nx_line, nx_col)
|
||
_g_parse_err_msg = buf_m
|
||
|
||
|
||
def _record_neg_literal_overflow(lit_text: str, lineno: int, col: int):
|
||
"""记录"一元减 + 整数字面量"的联合越界(fail-fast;只记首次)。
|
||
|
||
`-` 是独立的一元运算符、不属于字面量,故幅值合法(≤ 2^64-1)的字面量在取负
|
||
后可能 < -2^63。报文含:**字面量原文(含负号)** + 允许范围 [-2^63, 2^64-1]
|
||
+ 修法;`文件:行:列` 与源码行由 parse_tokens → dump_parse_traceback 补全
|
||
(与词法层 `_lex_record_overflow` 同一上报路径)。
|
||
"""
|
||
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,
|
||
"[TPV] 负数整数字面量越界: 一元减作用于整数字面量后的结果超出 64 位"
|
||
"有符号可表示范围(允许范围 -9223372036854775808..18446744073709551615"
|
||
" = [-2^63, 2^64-1];负数结果必须 >= -2^63)。字面量原文: '-%s'。"
|
||
"禁止静默回绕/截断,已终止编译。"
|
||
"修法: 把该负数改到 -9223372036854775808..-1 的范围内(本语言不存在"
|
||
"更宽的整数类型,如需更大数值请拆分为多个 64 位字表达)。", lit_text)
|
||
_g_parse_err_msg = buf
|
||
else:
|
||
_g_parse_err_msg = lit_text
|
||
|
||
|
||
# ============================================================
|
||
# 类型描述符 const / volatile(软关键字,仅注解位置生效)
|
||
# ============================================================
|
||
def _IsDescriptorKeyword(ps: rawptr[Parser]) -> int:
|
||
"""当前 token 是否为类型描述符关键字(const / volatile),1=是 / 0=否。
|
||
|
||
const/volatile 是**软关键字**:只在**注解位置**(`:` 后的参数/AnnAssign 注解、
|
||
`->` 后的返回注解)由 `_parse_type_ann` 消费;作标识符的写法
|
||
(`t.attr.const()`,TransPyV/Test/App/attr_test.vp:142)不在注解位置,
|
||
属性名/调用名解析只看 TokenType.Name,不看 kw_subtype,故不受影响。
|
||
"""
|
||
if _cur_type(ps) != TokenType.Name:
|
||
return 0
|
||
if _cur_kw(ps) == Keyword.Const or _cur_kw(ps) == Keyword.Volatile:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _IsMagicAttributeCall(ps: rawptr[Parser]) -> int:
|
||
"""注解位置的 `__attribute__(...)` 前缀判定,1=是 / 0=否
|
||
|
||
`__attribute__` 是软关键字(不进关键字表):只在**注解位置**、且紧跟 '(' 时
|
||
由本处消费;作普通标识符/装饰器名(`@__attribute__(...)` 走装饰器通道)都不受影响。
|
||
语义与 const / volatile 描述符同族:顺序自由、可混写。
|
||
"""
|
||
if _cur_type(ps) != TokenType.Name:
|
||
return 0
|
||
s: str = _cur_str(ps)
|
||
if s is None or string.strcmp(s, "__attribute__") != 0:
|
||
return 0
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
if nxt is None or nxt.type != TokenType.Op or nxt.op_subtype != TokOp.LPar:
|
||
return 0
|
||
return 1
|
||
|
||
|
||
def _MakeDescriptorMarker(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""由当前描述符关键字构造标记节点 Attribute(Name('t'), 'CConst'/'CVolatile')。
|
||
|
||
与 TPC 侧 `SymbolUtils._ExpandTypeDescriptors` 的文本展开产物
|
||
(`t.CConst` / `t.CVolatile`)**逐字段同形**:两侧落到同一条既有识别路径
|
||
(TPC:HandlesTypeMerge/TypeAnnotationResolver 的 t 模块类型限定符分支 →
|
||
DataConst/DataVolatile;TPV:注解解析的 t 模块属性分支),不新造第二套表示。
|
||
"""
|
||
attr_name: str = "CConst"
|
||
if _cur_kw(ps) == Keyword.Volatile:
|
||
attr_name = "CVolatile"
|
||
marker: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||
attr_name, ASTCtx.Load)
|
||
_set_pos(marker, _cur_lineno(ps), _cur_col(ps), 0, 0)
|
||
return marker
|
||
|
||
|
||
def _PrependDescriptorMarkers(ps: rawptr[Parser],
|
||
base: rawptr[AST],
|
||
mods: rawptr[list[rawptr[AST]]],
|
||
mod_count: int) -> rawptr[AST]:
|
||
"""把描述符标记贴到类型联合的**最左操作数**位置(与 TPC 文本展开同形)。
|
||
|
||
TPC 把 `const volatile int | t.CPtr` 展开为文本
|
||
`t.CConst | t.CVolatile | int | t.CPtr`,其解析树是左结合
|
||
`(((t.CConst | t.CVolatile) | int) | t.CPtr)`。本函数据此把描述符串整体
|
||
贴到 `base` 最左操作数的左侧,产出**同一棵树**(而非
|
||
`t.CConst | (t.CVolatile | int | t.CPtr)`——那会让两侧 AST 不同形)。
|
||
"""
|
||
slot_parent: rawptr[BinOp] = None # 最左 BitOr 节点:其 left 即"最左操作数"
|
||
slot_node: rawptr[AST] = base
|
||
cur: rawptr[AST] = base
|
||
while cur is not None and cur.kind() == ASTKind.BinOp:
|
||
b: rawptr[BinOp] = (rawptr[BinOp])(cur)
|
||
if b.op != OpKind.BitOr or b.left is None:
|
||
break
|
||
nxt: rawptr[AST] = b.left
|
||
if nxt.kind() == ASTKind.BinOp:
|
||
nb: rawptr[BinOp] = (rawptr[BinOp])(nxt)
|
||
if nb.op == OpKind.BitOr and nb.left is not None:
|
||
cur = nxt
|
||
continue
|
||
slot_parent = b
|
||
slot_node = nxt
|
||
break
|
||
# 描述符串按源码顺序左结合:`const volatile` → (t.CConst | t.CVolatile)
|
||
chain: rawptr[AST] = mods.get(0)
|
||
i: int = 1
|
||
while i < mod_count:
|
||
chain = BinOp(ps.pool, chain, OpKind.BitOr, mods.get(i))
|
||
i += 1
|
||
newnode: rawptr[AST] = BinOp(ps.pool, chain, OpKind.BitOr, slot_node)
|
||
if slot_parent is None:
|
||
return newnode
|
||
slot_parent.left = newnode
|
||
return base
|
||
|
||
|
||
def _RecordDescriptorErr(detail: str, lineno: int, col: int):
|
||
"""记录类型描述符非法形态(fail-fast;只记首次)。
|
||
|
||
报文含**期望形态 + 正确示例**;`文件:行:列` 与源码行 + `^` 列标记由
|
||
parse_tokens → dump_parse_traceback 补全(与 `_record_neg_literal_overflow`
|
||
同一上报路径)。描述符**绝不静默忽略**:缺类型 / 后随非类型 / 裸用都终止编译。
|
||
"""
|
||
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(1024)
|
||
if buf is not None:
|
||
stdio.snprintf(buf, 1024,
|
||
"类型描述符非法(%s)。const/volatile 是只在类型位置生效的软关键字,"
|
||
"必须写成 `名字: const 类型` / `名字: volatile 类型`"
|
||
"(可组合 `名字: const volatile 类型`)。正确示例:a: const int / "
|
||
"a: volatile int / a: const volatile t.CInt32T / "
|
||
"def f(p: const rawptr[Foo]) -> const int。"
|
||
"(描述符不会被静默忽略:缺类型 / 后随非类型 / 裸用一律终止编译)", detail)
|
||
_g_parse_err_msg = buf
|
||
else:
|
||
_g_parse_err_msg = detail
|
||
|
||
|
||
# ============================================================
|
||
# virtual / novirtual 修饰符(软关键字,仅"语句起始 + 紧跟 class/def"生效)
|
||
# ============================================================
|
||
# 语法:`virtual class C:` / `novirtual class C:` / `virtual def m(...)` /
|
||
# `novirtual def m(...)`(修饰符写在 class/def **之前**)。
|
||
#
|
||
# 语义:与**手写装饰器同一套表示**——virtual → `@t.CVTable`,novirtual → `@t.NoVTable`:
|
||
# - 类级:CVTable 强制有虚表 / NoVTable 强制无虚表
|
||
# (ClassGenBase._detect_vtable_status)
|
||
# - 方法级:CVTable 进虚表槽 / NoVTable 不进虚表槽
|
||
# (ClassGenBase._should_method_be_virtual)
|
||
# 本层只"合成同名装饰器",下游零新增判据,不新造第二套表示;位置/报文与 TPC 端
|
||
# (lib/vast/Parser.py:_ParseVirtualModified)逐条对齐。
|
||
#
|
||
# 软关键字边界(非保留字):只在**语句起始**判定;作标识符的写法
|
||
# (`x = d.virtual` / 形参名 / for 目标 / 字典键)token 仍是 Name,那些位置只看
|
||
# TokenType.Name、不看 kw_subtype,故语义逐字不变。
|
||
#
|
||
# 非法形态**响亮失败**(不静默丢修饰符):
|
||
# - 后随非 class/def;
|
||
# - `virtual def` / `novirtual def` 不直接写在类体内(模块级 / 函数体 / 分支体)。
|
||
# 组合与重复形态(由"后随必须是 class/def"这一条唯一判据统一挡住):
|
||
# `virtual novirtual class` / `virtual virtual class` / `novirtual virtual def`
|
||
# 都因"修饰符后随的是另一个修饰符"而响亮失败,不会被静默折叠或忽略。
|
||
# `novirtual class` 内写 `virtual def` **合法**:与既有"@t.NoVTable 类 + 方法级
|
||
# @t.CVTable"语义完全一致(_detect_vtable_status → _has_any_func_cvtable ⇒ 该类
|
||
# 仍生成虚表,且槽里只有被标记的方法)。
|
||
def _IsVirtualModifier(ps: rawptr[Parser]) -> int:
|
||
"""当前 token 是否为 virtual / novirtual 修饰符(1=是 / 0=否)。
|
||
|
||
只在**语句起始**(_parse_statement_into 分派处)调用;作标识符的位置不经过
|
||
本判据,故 `x = d.virtual` / 形参名 / for 目标 / 字典键不受影响。
|
||
"""
|
||
if _cur_type(ps) != TokenType.Name:
|
||
return 0
|
||
if _cur_kw(ps) == Keyword.Virtual or _cur_kw(ps) == Keyword.Novirtual:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _MakeVirtualMarker(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""由当前 virtual/novirtual token 构造标记节点 Attribute(Name('t'), 'CVTable'|'NoVTable')。
|
||
|
||
与**手写** `@t.CVTable` / `@t.NoVTable` 装饰器逐字段同形(_parse_decorators 的
|
||
产物形态),故下游 `ClassGenBase._has_decorator` 零新增判据。
|
||
"""
|
||
attr_name: str = "CVTable"
|
||
if _cur_kw(ps) == Keyword.Novirtual:
|
||
attr_name = "NoVTable"
|
||
marker: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||
attr_name, ASTCtx.Load)
|
||
_set_pos(marker, _cur_lineno(ps), _cur_col(ps), 0, 0)
|
||
return marker
|
||
|
||
|
||
def _RecordVirtualErr(detail: str, lineno: int, col: int, extra: str):
|
||
"""记录 virtual/novirtual 修饰符非法形态(fail-fast;只记首次)。
|
||
|
||
报文含**允许出现的位置 + 正确示例 + 修法**;`文件:行:列` 与源码行 + `^` 列标记
|
||
由 parse_tokens → dump_parse_traceback 补全(与 _RecordDescriptorErr 同一上报
|
||
路径)。修饰符**绝不静默忽略**:后随非 class/def / 方法级不在类体内都终止编译。
|
||
|
||
两个入参(detail / extra)由调用方用 **f-string 插值**构建:本语言的 str 是
|
||
i8*,**没有** `str + str` 的拼接语义,`"a" + x` 会退化成裸指针 add ⇒ 非法 IR
|
||
(opt: invalid operand type for instruction)。故本文件所有含变量的报文一律
|
||
走 f-string / snprintf,禁止对 str 用 `+` 拼接。
|
||
"""
|
||
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(1536)
|
||
if buf is not None:
|
||
stdio.snprintf(buf, 1536,
|
||
"[TPV] virtual/novirtual 修饰符非法(%s)。可写位置:类级 "
|
||
"`virtual class C:` / `novirtual class C:`;方法级 "
|
||
"`virtual def m(self): ...` / `novirtual def m(self): ...`"
|
||
"(只能直接写在类体内:virtual class / class / novirtual class 的类体)。"
|
||
"正确示例:virtual class Shape: / novirtual class Node: / "
|
||
"class A: virtual def run(self) -> int: ... / "
|
||
"novirtual class B: novirtual def helper(self) -> int: ...。%s"
|
||
"(修饰符不会被静默忽略:非法形态一律终止编译)", detail, extra)
|
||
_g_parse_err_msg = buf
|
||
else:
|
||
_g_parse_err_msg = detail
|
||
|
||
|
||
def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]):
|
||
"""`virtual class` / `novirtual class` / `virtual def` / `novirtual def`。
|
||
|
||
合成 `@t.CVTable` / `@t.NoVTable` 装饰器(与用户既有装饰器合并,标记追加在末尾;
|
||
位置无关——`_has_decorator` 按名判定)后交给 _parse_classdef / _parse_funcdef,
|
||
产出与**手写同名装饰器**逐字段同形的 AST,故下游零新增分支。
|
||
"""
|
||
mod_lineno: int = _cur_lineno(ps)
|
||
mod_col: int = _cur_col(ps)
|
||
mod_name: str = "virtual"
|
||
if _cur_kw(ps) == Keyword.Novirtual:
|
||
mod_name = "novirtual"
|
||
marker: rawptr[AST] = _MakeVirtualMarker(ps)
|
||
_advance(ps) # 消费修饰符
|
||
# 修饰符后必须是 class / def(def 的别名 fn/fun/func/function 同样接受:
|
||
# 与 _is_funcdef_kw 口径一致,也与 TPC 端逐条对齐——TPC 的 fn→def 文本展开
|
||
# 只命中行首 fn,`virtual fn m()` 不先展开,故两端都在本层显式接受别名)
|
||
kw2: int = _cur_kw(ps)
|
||
is_class: int = 0
|
||
is_def: int = 0
|
||
if kw2 == Keyword.Class:
|
||
is_class = 1
|
||
elif kw2 == Keyword.Def or kw2 == Keyword.Fn or kw2 == Keyword.Fun \
|
||
or kw2 == Keyword.Func or kw2 == Keyword.Function:
|
||
is_def = 1
|
||
if is_class == 0 and is_def == 0:
|
||
nxt_s: str = _cur_str(ps)
|
||
if nxt_s is None:
|
||
nxt_s = "(none)"
|
||
_RecordVirtualErr(f"`{mod_name}` 后随 '{nxt_s}'(非 class/def)",
|
||
mod_lineno, mod_col,
|
||
f"`{mod_name}` 只在 class/def 前生效;"
|
||
f"若此处本意是普通标识符名,请改名"
|
||
f"(该词在语句起始会被判为修饰符)。")
|
||
ps.error_count += 1
|
||
# 余下 token 交给通用语句分派消费(不制造死循环,也不覆盖首次错误)。
|
||
_parse_simple_stmt_into(ps, parent)
|
||
return
|
||
if is_def != 0 and ps.in_class_body == 0:
|
||
_RecordVirtualErr(f"{mod_name} def 出现在类体之外", mod_lineno, mod_col,
|
||
f"{mod_name} def 描述的是「类体内方法的虚/非虚」,"
|
||
f"模块级 / 函数体 / 分支体内没有可归属的类。")
|
||
ps.error_count += 1
|
||
# 合并既有装饰器(`@deco` + virtual class/def 组合)后交给既有 class/def 解析
|
||
merged: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
if merged is None:
|
||
return
|
||
if ps.pending_decorators is not None:
|
||
dn: sizet = len(ps.pending_decorators)
|
||
di: sizet = 0
|
||
while di < dn:
|
||
merged.append(ps.pending_decorators.get(di))
|
||
di += 1
|
||
merged.append(marker)
|
||
ps.pending_decorators = merged
|
||
stmt: rawptr[AST] = None
|
||
if is_class != 0:
|
||
stmt = _parse_classdef(ps)
|
||
else:
|
||
stmt = _parse_funcdef(ps, 0)
|
||
if stmt is not None:
|
||
parent.append(stmt)
|
||
|
||
|
||
# ============================================================
|
||
# inline 修饰符(软关键字):`inline def f(...)` ⇒ 合成 @t.attr.always_inline
|
||
#
|
||
# 与 virtual/novirtual 完全同构:只在「语句起始 + inline + 紧跟函数定义」时生效,
|
||
# 其余位置的 inline 仍是普通标识符(故 `inline = 5` / 字段名 inline 等既有写法不受
|
||
# 影响)。合成的装饰器与**手写** `@t.attr.always_inline` 逐字段同形,下游
|
||
# FuncGenBase.extract_func_attrs → _append_llvm_attr 的 "always_inline" → LLVM
|
||
# "alwaysinline" 映射原样复用;StubGen 跨模块 alwaysinline 合并规则同样原样生效。
|
||
#
|
||
# 为什么不进关键字表:`inline` 在 TPV/TPC 既有源码里可能是普通标识符,进表会把
|
||
# 全局词法口径改掉;此处按软关键字文本判定,作用域只限"语句起始后随函数定义"。
|
||
# ============================================================
|
||
def _IsInlineModifier(ps: rawptr[Parser]) -> int:
|
||
"""当前 token 是否为 `inline`(软关键字,且后随函数定义),1=是 / 0=否"""
|
||
if _cur_type(ps) != TokenType.Name:
|
||
return 0
|
||
s: str = _cur_str(ps)
|
||
if s is None or string.strcmp(s, "inline") != 0:
|
||
return 0
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
if nxt is None or nxt.type != TokenType.Name:
|
||
return 0
|
||
nxt2: rawptr[Token] = nxt.next
|
||
if nxt2 is None:
|
||
return 0
|
||
# 后随 def(硬关键字):`inline def f(...)`
|
||
if nxt.kw_subtype == Keyword.Def:
|
||
return 1
|
||
# 后随函数定义软关键字别名(fn/fun/func/function):与 _is_funcdef_kw 同口径
|
||
# ——必须是 Name + '(' 或 '[T]',避免把 `inline func` 这种普通标识符组合误判。
|
||
if nxt.kw_subtype == Keyword.Fn or nxt.kw_subtype == Keyword.Fun \
|
||
or nxt.kw_subtype == Keyword.Func or nxt.kw_subtype == Keyword.Function:
|
||
if nxt2.type == TokenType.Op \
|
||
and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb):
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _MakeInlineMarker(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""由当前 inline token 构造标记节点 Attribute(Attribute(Name('t'),'attr'), 'always_inline')
|
||
|
||
形态与**手写** `@t.attr.always_inline` 装饰器逐字段同形。取属性名的既有逻辑
|
||
(FuncGenBase._get_attr_name_from_node)对 Attribute 节点取最外层 attr,
|
||
故 t.attr.always_inline → "always_inline" → _append_llvm_attr 映射为 LLVM
|
||
"alwaysinline"。**刻意不用** `@c.Attribute(t.attr.x)` 形态:该形态的提取器
|
||
要求本源文件 `import c`,而 includes/asm.vp 之类文件没有 import,会静默不内联。
|
||
"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
t_name: rawptr[AST] = Name(ps.pool, "t", ASTCtx.Load)
|
||
attr_at: rawptr[AST] = Attribute(ps.pool, t_name, "attr", ASTCtx.Load)
|
||
marker: rawptr[AST] = Attribute(ps.pool, attr_at, "always_inline", ASTCtx.Load)
|
||
_set_pos(marker, lineno, col, 0, 0)
|
||
return marker
|
||
|
||
|
||
def _ParseInlineModified(ps: rawptr[Parser], parent: rawptr[AST]):
|
||
"""`inline def f(...)`:消费 inline,合成 @t.attr.always_inline 后交给 _parse_funcdef"""
|
||
marker: rawptr[AST] = _MakeInlineMarker(ps)
|
||
if marker is None:
|
||
return
|
||
_advance(ps) # 消费 inline 修饰符
|
||
# 合并既有装饰器(`@deco` + inline def 组合),标记追加在末尾;位置无关
|
||
# (下游按装饰器名判定)。与 _ParseVirtualModified 同一套 pending_decorators 通道。
|
||
merged: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
if merged is None:
|
||
return
|
||
if ps.pending_decorators is not None:
|
||
dn: sizet = len(ps.pending_decorators)
|
||
di: sizet = 0
|
||
while di < dn:
|
||
merged.append(ps.pending_decorators.get(di))
|
||
di += 1
|
||
merged.append(marker)
|
||
ps.pending_decorators = merged
|
||
stmt: rawptr[AST] = _parse_funcdef(ps, 0)
|
||
if stmt is not None:
|
||
parent.append(stmt)
|
||
|
||
|
||
def _IsStaticModifier(ps: rawptr[Parser]) -> int:
|
||
"""当前 token 是否为 `static` 修饰符(软关键字),1=是 / 0=否。
|
||
|
||
只在**语句起始**(_parse_statement_into 分派处)调用;后随
|
||
① 函数定义(`def`,或 fn/fun/func/function 别名 + '(' / '[')
|
||
② 变量声明(`Name :`,即 AnnAssign)
|
||
时命中。作标识符的位置(属性名 / 形参名 / 字典键 / 表达式内)不经过本判据,
|
||
故 `x = d.static`、`f(static=1)` 等写法不受影响(软关键字边界)。
|
||
|
||
语义(禁止跨模块使用)由 TransPyV 侧消费;本层只负责把两种写法归一,
|
||
且与手写 `@t.CStatic` / `x: t.CStatic | T` 的标记逐字段同形。
|
||
"""
|
||
if _cur_type(ps) != TokenType.Name:
|
||
return 0
|
||
s: str = _cur_str(ps)
|
||
if s is None or string.strcmp(s, "static") != 0:
|
||
return 0
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
if nxt is None or nxt.type != TokenType.Name:
|
||
return 0
|
||
# ① 后随 def(硬关键字):`static def f(...)`
|
||
if nxt.kw_subtype == Keyword.Def:
|
||
return 1
|
||
nxt2: rawptr[Token] = nxt.next
|
||
# ①' 后随函数定义软关键字别名:必须是 Name + '(' 或 '[T]'
|
||
#(与 _IsInlineModifier / _is_funcdef_kw 同口径,避免 `static func` 这类
|
||
# 普通标识符组合被误判)
|
||
if nxt.kw_subtype == Keyword.Fn or nxt.kw_subtype == Keyword.Fun \
|
||
or nxt.kw_subtype == Keyword.Func or nxt.kw_subtype == Keyword.Function:
|
||
if nxt2 is not None and nxt2.type == TokenType.Op \
|
||
and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb):
|
||
return 1
|
||
# ② 后随变量声明 `Name :`(AnnAssign)
|
||
if nxt2 is not None and nxt2.type == TokenType.Op and nxt2.op_subtype == TokOp.Colon:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _MakeStaticMarker(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""构造标记节点 Attribute(Name('t'), 'CStatic')
|
||
|
||
与**手写** `@t.CStatic` 装饰器 / `x: t.CStatic | T` 注解里的标记逐字段同形,
|
||
故下游(FuncGenDef 的存储类标记识别、TypeGenResolve 的存储类限定符判定)
|
||
零新增判据;两种写法同源。
|
||
"""
|
||
marker: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||
"CStatic", ASTCtx.Load)
|
||
_set_pos(marker, _cur_lineno(ps), _cur_col(ps), 0, 0)
|
||
return marker
|
||
|
||
|
||
def _ParseStaticModified(ps: rawptr[Parser], parent: rawptr[AST]) -> int:
|
||
"""`static` 修饰符分派:1=已完全处理(函数定义,调用方直接 return);
|
||
0=仅置位(变量声明,调用方继续走通用语句分派)。
|
||
|
||
函数:消费 static 后合成 @t.CStatic 装饰器,交给 _parse_funcdef
|
||
(与 inline def 同一套 pending_decorators 通道)。
|
||
变量:置 pending_static 后消费 static,由紧随的 _parse_type_ann 把
|
||
t.CStatic 插到类型联合最左(= 手写 `x: t.CStatic | int`)。
|
||
"""
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
is_func: int = 0
|
||
if nxt is not None and nxt.type == TokenType.Name:
|
||
if nxt.kw_subtype == Keyword.Def:
|
||
is_func = 1
|
||
elif nxt.kw_subtype == Keyword.Fn or nxt.kw_subtype == Keyword.Fun \
|
||
or nxt.kw_subtype == Keyword.Func or nxt.kw_subtype == Keyword.Function:
|
||
nxt2: rawptr[Token] = nxt.next
|
||
if nxt2 is not None and nxt2.type == TokenType.Op \
|
||
and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb):
|
||
is_func = 1
|
||
if is_func == 0:
|
||
# 变量分支:仅置位 + 消费 static,交调用方继续通用分派(AnnAssign 解析
|
||
# 会在 _parse_type_ann 里读取 pending_static)。
|
||
ps.pending_static = 1
|
||
_advance(ps)
|
||
return 0
|
||
marker: rawptr[AST] = _MakeStaticMarker(ps)
|
||
if marker is None:
|
||
return 1
|
||
_advance(ps) # 消费 static 修饰符
|
||
# 合并既有装饰器(`@deco` + `static def` 组合),标记追加在末尾;位置无关
|
||
# (下游按装饰器名判定)。与 _ParseInlineModified 同一套 pending_decorators 通道。
|
||
merged: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
if merged is None:
|
||
return 1
|
||
if ps.pending_decorators is not None:
|
||
dn: sizet = len(ps.pending_decorators)
|
||
di: sizet = 0
|
||
while di < dn:
|
||
merged.append(ps.pending_decorators.get(di))
|
||
di += 1
|
||
merged.append(marker)
|
||
ps.pending_decorators = merged
|
||
stmt: rawptr[AST] = _parse_funcdef(ps, 0)
|
||
if stmt is not None:
|
||
parent.append(stmt)
|
||
return 1
|
||
|
||
|
||
def _IsBareDescriptor(ps: rawptr[Parser]) -> int:
|
||
"""裸用描述符判定:整条语句只有一个描述符串(const[: volatile]),1=是 / 0=否。
|
||
|
||
只在"描述符串后直接换行 / 文件尾 / 分号"时命中;作普通标识符的写法
|
||
(`const = 5` / `const(x)` / `t.attr.const()`)不命中(软关键字边界)。
|
||
"""
|
||
if _IsDescriptorKeyword(ps) == 0:
|
||
return 0
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
while nxt is not None and nxt.type == TokenType.Name \
|
||
and (nxt.kw_subtype == Keyword.Const or nxt.kw_subtype == Keyword.Volatile):
|
||
nxt = nxt.next
|
||
if nxt is None:
|
||
return 1
|
||
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
|
||
|
||
|
||
def get_parse_err_line() -> int:
|
||
"""获取最近一次解析错误的行号(0=无错误)"""
|
||
return _g_parse_err_line
|
||
|
||
|
||
def get_parse_err_col() -> int:
|
||
"""获取最近一次解析错误的列号(0=无错误)"""
|
||
return _g_parse_err_col
|
||
|
||
|
||
def get_parse_err_msg() -> str:
|
||
"""获取最近一次解析错误的错误消息(仿 Python SyntaxError 消息文本)"""
|
||
return _g_parse_err_msg
|
||
|
||
|
||
def _cur_str(ps: rawptr[Parser]) -> str:
|
||
if ps.cur is None: return None
|
||
return ps.cur.str_val
|
||
|
||
|
||
def _cur_lineno(ps: rawptr[Parser]) -> int:
|
||
if ps.cur is None: return 0
|
||
return ps.cur.lineno
|
||
|
||
|
||
def _cur_col(ps: rawptr[Parser]) -> int:
|
||
if ps.cur is None: return 0
|
||
return ps.cur.col_offset
|
||
|
||
|
||
def _skip_nl(ps: rawptr[Parser]):
|
||
"""Skip TokenType.Nl token"""
|
||
while ps.cur is not None and ps.cur.type == TokenType.Nl:
|
||
_advance(ps)
|
||
|
||
|
||
def _skip_nls_and_newlines(ps: rawptr[Parser]):
|
||
"""Skip TokenType.Nl and TokenType.NewLine (used for line continuation)"""
|
||
while ps.cur is not None:
|
||
if ps.cur.type == TokenType.Nl or ps.cur.type == TokenType.NewLine:
|
||
_advance(ps)
|
||
else:
|
||
break
|
||
|
||
|
||
def _expect_type(ps: rawptr[Parser], ttype: int) -> int:
|
||
"""Expect the current token to be of the specified type, advance and return 1, otherwise return 0"""
|
||
if _cur_type(ps) == ttype:
|
||
_advance(ps)
|
||
return 1
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
return 0
|
||
|
||
|
||
def _expect_op(ps: rawptr[Parser], op_id: int) -> int:
|
||
"""Expecting the current one to be some operator"""
|
||
if _cur_type(ps) == TokenType.Op and _cur_op(ps) == op_id:
|
||
_advance(ps)
|
||
return 1
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
return 0
|
||
|
||
|
||
def _match_op(ps: rawptr[Parser], op_id: int) -> int:
|
||
"""Check but not consume the current token"""
|
||
_tick(ps)
|
||
if ps.aborted != 0:
|
||
# 收束:仍有 token 时"匹配"并一次性跳到末尾,使
|
||
# `while not _match_op(...)` / `while _match_op(...)` 均在 ≤2 轮结束
|
||
if ps.cur is not None:
|
||
ps.cur = None
|
||
return 1
|
||
return 0
|
||
if _cur_type(ps) == TokenType.Op and _cur_op(ps) == op_id:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _accept_op(ps: rawptr[Parser], op_id: int) -> int:
|
||
"""If match, consume and return 1"""
|
||
if _match_op(ps, op_id):
|
||
_advance(ps)
|
||
return 1
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# Constant Construction Helper (New API: Constant directly accepts lineno/col)
|
||
# ============================================================
|
||
|
||
def _new_const_int(pool: rawptr[memhub.MemManager], val: int64,
|
||
lineno: int, col: int) -> rawptr[AST]:
|
||
return Constant(pool, CONST_INT, val, 0.0, None, lineno, col)
|
||
|
||
|
||
def _new_const_float(pool: rawptr[memhub.MemManager], val: double,
|
||
lineno: int, col: int) -> rawptr[AST]:
|
||
global _g_dbg_pts, _g_dbg_vals, _g_dbg_cnt
|
||
new_c: rawptr[Constant] = Constant(pool, CONST_FLOAT, 0, val, None, lineno, col)
|
||
# 把数字文本存到 str_val(stdlib 分配,绕开 pool 对 float_val 字段的覆写)
|
||
# REG-SRC 用 string.atof(str_val) 恢复原始值
|
||
lit_txt: str = stdlib.malloc(64)
|
||
if lit_txt is not None:
|
||
stdio.snprintf(lit_txt, 64, "%.17g", val)
|
||
new_c.str_val = lit_txt
|
||
# 记录浮点节点 ptr→val 到 stdlib 表(构造时值正确,供 REG-SRC 查表绕开 pool 覆写)
|
||
# 表满时重置:旧记录对应已翻译完的文件,从当前节点重新记录
|
||
if _g_dbg_pts is None:
|
||
_g_dbg_pts = stdlib.malloc(4096 * 8)
|
||
_g_dbg_vals = stdlib.malloc(4096 * 8)
|
||
_g_dbg_cnt = 0
|
||
elif _g_dbg_cnt >= 4096:
|
||
_g_dbg_cnt = 0
|
||
if _g_dbg_pts is not None and _g_dbg_vals is not None:
|
||
_g_dbg_pts[_g_dbg_cnt] = uint64(new_c)
|
||
_g_dbg_vals[_g_dbg_cnt] = val
|
||
_g_dbg_cnt += 1
|
||
return new_c
|
||
|
||
|
||
# 浮点节点构造值表(stdlib 分配,避免污染 pool)
|
||
_g_dbg_pts: rawptr[uint64] = None
|
||
_g_dbg_vals: rawptr[double] = None
|
||
_g_dbg_cnt: int = 0
|
||
# 最近一次 _dbg_lookup_lit_val 是否命中
|
||
_g_dbg_lit_found: int = 0
|
||
|
||
|
||
def _dbg_lookup_lit_val(ptr: uint64) -> double:
|
||
"""查浮点节点构造值表:返回 ptr 节点的原始构造值(未命中返回 0 并置 found=0)
|
||
|
||
用于绕开 pool 分配对 AST float_val 字段的覆写(构造时值正确,读取时可能被覆写)。
|
||
"""
|
||
global _g_dbg_lit_found
|
||
_g_dbg_lit_found = 0
|
||
if _g_dbg_pts is None or _g_dbg_vals is None:
|
||
return 0.0
|
||
i: sizet = 0
|
||
while i < _g_dbg_cnt:
|
||
if _g_dbg_pts[i] == ptr:
|
||
_g_dbg_lit_found = 1
|
||
return _g_dbg_vals[i]
|
||
i += 1
|
||
return 0.0
|
||
|
||
|
||
def _dbg_lit_found() -> int:
|
||
"""检查最近一次 _dbg_lookup_lit_val 是否命中"""
|
||
return _g_dbg_lit_found
|
||
|
||
|
||
def _new_const_str(pool: rawptr[memhub.MemManager], val: str,
|
||
lineno: int, col: int,
|
||
is_dquote: int = 1,
|
||
is_triple: int = 0,
|
||
is_char: int = 0) -> rawptr[AST]:
|
||
"""Create a string/character constant node
|
||
|
||
is_dquote: 0 = single quote, non-0 = double quote (default double quote)
|
||
is_triple: non-0 = triple-quoted string
|
||
is_char: non-0 = single-character candidate (lexer has confirmed that after escaping it's exactly 1 byte, including '0')
|
||
Semantic distinctions:
|
||
- 'x' (single quote, single character, not triple-quoted) → CONST_CHAR (i8 value)
|
||
- "x" (double quote, includes 0 terminator) → CONST_STR (i8* pointer, i8 x 2)
|
||
- 'ab' (single quotes, multiple characters) → CONST_STR (i8* pointer)
|
||
- '''x''' / \"\"\"x\"\"\" (triple quotes) → CONST_STR (i8* pointer)
|
||
- '' / "" (empty string) → CONST_STR (i8* pointer)
|
||
"""
|
||
# Single quotes, non-triple quotes, single-character candidates marked by lexer → CONST_CHAR
|
||
# Mark with is_char instead of strlen(val), because '0' becomes a NUL byte after escaping, strlen would return 0
|
||
if is_dquote == 0 and is_triple == 0 and is_char != 0:
|
||
# Single quote # Single character → CONST_CHAR, int_val stores the character's ASCII value
|
||
# val[0] is a legal value of 0 even if it's NUL
|
||
ch_val: int64 = val[0]
|
||
return Constant(pool, CONST_CHAR, ch_val, 0.0, val, lineno, col)
|
||
# Other situations → CONST_STR
|
||
return Constant(pool, CONST_STR, 0, 0.0, val, lineno, col)
|
||
|
||
|
||
def _new_const_bool(pool: rawptr[memhub.MemManager], val: int,
|
||
lineno: int, col: int) -> rawptr[AST]:
|
||
return Constant(pool, CONST_BOOL, val, 0.0, None, lineno, col)
|
||
|
||
|
||
def _new_const_none(pool: rawptr[memhub.MemManager],
|
||
lineno: int, col: int) -> rawptr[AST]:
|
||
return Constant(pool, CONST_NONE, 0, 0.0, None, lineno, col)
|
||
|
||
|
||
# ============================================================
|
||
# ctx Helper (Recursive Fixing Store Context)
|
||
# ============================================================
|
||
|
||
def _set_ctx(node: rawptr[AST], ctx: int):
|
||
"""Set ctx field of node (based on kind conversion)"""
|
||
if node is None: return
|
||
k: int = node.kind()
|
||
if k == ASTKind.Name:
|
||
nm: rawptr[Name] = (rawptr[Name])(node)
|
||
nm.ctx = ctx
|
||
elif k == ASTKind.Attribute:
|
||
at: rawptr[Attribute] = (rawptr[Attribute])(node)
|
||
at.ctx = ctx
|
||
elif k == ASTKind.Subscript:
|
||
sb: rawptr[Subscript] = (rawptr[Subscript])(node)
|
||
sb.ctx = ctx
|
||
elif k == ASTKind.Tuple:
|
||
tp: rawptr[Tuple] = (rawptr[Tuple])(node)
|
||
tp.ctx = ctx
|
||
elif k == ASTKind.List:
|
||
ls: rawptr[List] = (rawptr[List])(node)
|
||
ls.ctx = ctx
|
||
elif k == ASTKind.Starred:
|
||
st: rawptr[Starred] = (rawptr[Starred])(node)
|
||
st.ctx = ctx
|
||
|
||
|
||
def _fix_store_ctx(node: rawptr[AST]):
|
||
"""Fix ctx of store targets to be Store"""
|
||
if node is None: return
|
||
_set_ctx(node, ASTCtx.Store)
|
||
k: int = node.kind()
|
||
if k == ASTKind.Tuple or k == ASTKind.List:
|
||
tp_elts: rawptr[list[rawptr[AST]]] = None
|
||
if k == ASTKind.Tuple:
|
||
tp: rawptr[Tuple] = (rawptr[Tuple])(node)
|
||
tp_elts = tp.elts
|
||
else:
|
||
ls: rawptr[List] = (rawptr[List])(node)
|
||
tp_elts = ls.elts
|
||
if tp_elts is not None:
|
||
n: sizet = tp_elts.__len__()
|
||
i: sizet = 0
|
||
while i < n:
|
||
cur: rawptr[AST] = tp_elts.get(i)
|
||
_fix_store_ctx(cur)
|
||
i += 1
|
||
elif k == ASTKind.Starred:
|
||
st2: rawptr[Starred] = (rawptr[Starred])(node)
|
||
_fix_store_ctx(st2.value)
|
||
|
||
|
||
# ============================================================
|
||
# Expression Parser
|
||
# ============================================================
|
||
|
||
def _parse_expr(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""expr: yield_expr | namedexpr_test"""
|
||
if _cur_kw(ps) == Keyword.Yield:
|
||
return _parse_yield(ps)
|
||
return _parse_namedexpr(ps)
|
||
|
||
|
||
def _parse_namedexpr(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""namedexpr_test: test [':=' test]"""
|
||
left: rawptr[AST] = _parse_test(ps)
|
||
if left is None: return None
|
||
if _match_op(ps, TokOp.ColonEq):
|
||
_advance(ps)
|
||
right: rawptr[AST] = _parse_test(ps)
|
||
node: rawptr[AST] = NamedExpr(ps.pool, left, right)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
return node
|
||
return left
|
||
|
||
|
||
def _IsVolatileCallPrefix(ps: rawptr[Parser]) -> int:
|
||
"""值位置的 `volatile 函数()` / `volatile 结构体()` 前缀判定,1=是 / 0=否
|
||
|
||
`volatile` 是软关键字:只在"值位置 + 紧跟 调用(Name 后随 '(')"时消费,
|
||
作注解描述符(`a: volatile int`)与作标识符的写法都不受影响。
|
||
"""
|
||
if _cur_kw(ps) != Keyword.Volatile:
|
||
return 0
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
if nxt is None or nxt.type != TokenType.Name:
|
||
return 0
|
||
nxt2: rawptr[Token] = nxt.next
|
||
if nxt2 is None or nxt2.type != TokenType.Op or nxt2.op_subtype != TokOp.LPar:
|
||
return 0
|
||
return 1
|
||
|
||
|
||
def _parse_test(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""test: or_test ['if' or_test 'else' test] | lambdef"""
|
||
if _cur_kw(ps) == Keyword.Lambda:
|
||
return _parse_lambda(ps)
|
||
# 值位置 volatile 调用前缀:`a: int = volatile f()` / `volatile S()`
|
||
# ⇒ 消费 volatile 并把调用节点标记为 volatile(结果槽按 volatile 存取;
|
||
# 结构体构造的写入亦带 LLVM volatile)。LLVM 的 call 无 volatile 限定符,
|
||
# 故语义只落在内存存取上(见 Call.IsVolatile 注释)。
|
||
if _IsVolatileCallPrefix(ps) != 0:
|
||
_advance(ps) # 消费 volatile
|
||
vinner: rawptr[AST] = _parse_test(ps)
|
||
if vinner is not None and vinner.kind() == ast.ASTKind.Call:
|
||
(ast.Call*(vinner)).IsVolatile = 1
|
||
return vinner
|
||
left: rawptr[AST] = _parse_or_test(ps)
|
||
if left is None: return None
|
||
if _cur_kw(ps) == Keyword.If:
|
||
_advance(ps)
|
||
test: rawptr[AST] = _parse_or_test(ps)
|
||
if _cur_kw(ps) == Keyword.Else:
|
||
_advance(ps)
|
||
orelse: rawptr[AST] = _parse_test(ps)
|
||
node: rawptr[AST] = IfExp(ps.pool, test, left, orelse)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
return node
|
||
ps.error_count += 1
|
||
return left
|
||
return left
|
||
|
||
|
||
def _parse_lambda(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""lambdef: 'lambda' [varargslist] ':' test"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # lambda
|
||
args: rawptr[AST] = None
|
||
if not _match_op(ps, TokOp.Colon):
|
||
args = _parse_varargslist(ps, 1)
|
||
if not _expect_op(ps, TokOp.Colon):
|
||
return None
|
||
body: rawptr[AST] = _parse_test(ps)
|
||
node: rawptr[AST] = Lambda(ps.pool, args, body)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_or_test(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""or_test: and_test ('or' and_test)*"""
|
||
left: rawptr[AST] = _parse_and_test(ps)
|
||
if left is None: return None
|
||
values: rawptr[list[rawptr[AST]]] = None
|
||
while True:
|
||
# 括号内行首 or((A\n or B))与行尾 or(A or\n B)都支持:
|
||
# 每轮先 _skip_nl 再检查 or,否则 A or B\n or C 在 B 后停在 Nl 上
|
||
# 循环退出,上层把续行 or 当非法 token(对齐 and_test / vast 实现)。
|
||
_skip_nl(ps)
|
||
if _cur_kw(ps) != Keyword.Or:
|
||
break
|
||
if values is None:
|
||
values = list[rawptr[AST]](ps.pool, 8)
|
||
values.append(left)
|
||
_advance(ps)
|
||
_skip_nl(ps) # 括号内跨行: a or\n b
|
||
right: rawptr[AST] = _parse_and_test(ps)
|
||
if right is None: break
|
||
values.append(right)
|
||
if values is not None:
|
||
node: rawptr[AST] = BoolOp(ps.pool, OpKind.Or, values)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
return node
|
||
return left
|
||
|
||
|
||
def _parse_and_test(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""and_test: not_test ('and' not_test)*"""
|
||
left: rawptr[AST] = _parse_not_test(ps)
|
||
if left is None: return None
|
||
values: rawptr[list[rawptr[AST]]] = None
|
||
while True:
|
||
# 括号内行首 and((A\n and B))与行尾 and(A and\n B)都支持:
|
||
# 每轮先 _skip_nl 再检查 and,否则 A and B\n and C 在 B 后停在
|
||
# Nl 上循环退出,上层把续行 and 当非法 token
|
||
# (ThreadingTest check 多行 and 链解析失败根因)。
|
||
_skip_nl(ps)
|
||
if _cur_kw(ps) != Keyword.And:
|
||
break
|
||
if values is None:
|
||
values = list[rawptr[AST]](ps.pool, 8)
|
||
values.append(left)
|
||
_advance(ps)
|
||
_skip_nl(ps) # 括号内跨行: a and\n b
|
||
right: rawptr[AST] = _parse_not_test(ps)
|
||
if right is None: break
|
||
values.append(right)
|
||
if values is not None:
|
||
node: rawptr[AST] = BoolOp(ps.pool, OpKind.And, values)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
return node
|
||
return left
|
||
|
||
|
||
def _parse_not_test(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""not_test: 'not' not_test | comparison"""
|
||
if _cur_kw(ps) == Keyword.Not:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
operand: rawptr[AST] = _parse_not_test(ps)
|
||
node: rawptr[AST] = UnaryOp(ps.pool, OpKind.Not, operand)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
return _parse_comparison(ps)
|
||
|
||
|
||
def _parse_comparison(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""comparison: bitor_expr (comp_op bitor_expr)*
|
||
|
||
Single comparison design: a < b → Compare(left=a, op=Lt, right=b)
|
||
Chained comparison a < b < c → BoolOp(And, [Compare(a,<,b), Compare(b,<,c)])
|
||
"""
|
||
left: rawptr[AST] = _parse_bitor(ps)
|
||
if left is None: return None
|
||
|
||
_skip_nl(ps) # 括号内行首比较运算符: (a\n < b)
|
||
op_id: int = _match_comp_op(ps)
|
||
if op_id == OpKind.NoneOp:
|
||
return left
|
||
_advance_comp_op(ps)
|
||
_skip_nl(ps) # 括号内跨行: a ==\n b
|
||
right: rawptr[AST] = _parse_bitor(ps)
|
||
# Single comparison: construct ops and comparators list
|
||
ops: rawptr[list[int]] = list[int](ps.pool, 8)
|
||
ops.append(op_id)
|
||
comparators: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
comparators.append(right)
|
||
first_cmp: rawptr[AST] = Compare(ps.pool, left, ops, comparators)
|
||
_set_pos(first_cmp, left.lineno, left.col_offset, 0, 0)
|
||
|
||
# Check chained comparison
|
||
_skip_nl(ps) # 括号内行首链式比较: (a < b\n < c)
|
||
next_op: int = _match_comp_op(ps)
|
||
if next_op == OpKind.NoneOp:
|
||
return first_cmp
|
||
|
||
# Single comparison with chained conditions: a < b < c → BoolOp(And, [Compare(a,<,b), Compare(b,<,c), ...])
|
||
values: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
values.append(first_cmp)
|
||
cur_left: rawptr[AST] = right
|
||
while next_op != OpKind.NoneOp:
|
||
_advance_comp_op(ps)
|
||
_skip_nl(ps) # 括号内跨行链式比较
|
||
cur_right: rawptr[AST] = _parse_bitor(ps)
|
||
ops2: rawptr[list[int]] = list[int](ps.pool, 8)
|
||
ops2.append(next_op)
|
||
comparators2: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
comparators2.append(cur_right)
|
||
cmp: rawptr[AST] = Compare(ps.pool, cur_left, ops2, comparators2)
|
||
_set_pos(cmp, cur_left.lineno, cur_left.col_offset, 0, 0)
|
||
values.append(cmp)
|
||
cur_left = cur_right
|
||
_skip_nl(ps) # 括号内行首链式比较: (a < b\n < c)
|
||
next_op = _match_comp_op(ps)
|
||
node: rawptr[AST] = BoolOp(ps.pool, OpKind.And, values)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
return node
|
||
|
||
|
||
def _match_comp_op(ps: rawptr[Parser]) -> int:
|
||
"""Check if the current one is a comparison operator, return OP_* or OpKind.NoneOp"""
|
||
if _cur_type(ps) == TokenType.Op:
|
||
op_id: int = _cur_op(ps)
|
||
if op_id == TokOp.EqEq: return OpKind.Eq
|
||
if op_id == TokOp.ExclaimEq: return OpKind.Ne
|
||
if op_id == TokOp.Less: return OpKind.Lt
|
||
if op_id == TokOp.LessEq: return OpKind.Le
|
||
if op_id == TokOp.Greater: return OpKind.Gt
|
||
if op_id == TokOp.GreaterEq: return OpKind.Ge
|
||
if _cur_kw(ps) == Keyword.Is:
|
||
if ps.cur.next is not None and ps.cur.next.type == TokenType.Name and ps.cur.next.kw_subtype == Keyword.Not:
|
||
return OpKind.IsNot
|
||
return OpKind.Is
|
||
if _cur_kw(ps) == Keyword.Not:
|
||
if ps.cur.next is not None and ps.cur.next.kw_subtype == Keyword.In:
|
||
return OpKind.NotIn
|
||
if _cur_kw(ps) == Keyword.In:
|
||
return OpKind.In
|
||
return OpKind.NoneOp
|
||
|
||
|
||
def _advance_comp_op(ps: rawptr[Parser]):
|
||
"""Consume comparison operator token"""
|
||
if _cur_type(ps) == TokenType.Op:
|
||
_advance(ps)
|
||
return
|
||
if _cur_kw(ps) == Keyword.Is:
|
||
_advance(ps)
|
||
if _cur_kw(ps) == Keyword.Not:
|
||
_advance(ps)
|
||
return
|
||
if _cur_kw(ps) == Keyword.Not:
|
||
_advance(ps)
|
||
if _cur_kw(ps) == Keyword.In:
|
||
_advance(ps)
|
||
return
|
||
if _cur_kw(ps) == Keyword.In:
|
||
_advance(ps)
|
||
|
||
|
||
def _parse_bitor(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
left: rawptr[AST] = _parse_bitxor(ps)
|
||
if left is None: return None
|
||
while True:
|
||
_skip_nl(ps) # 括号内行首 '|': (a\n | b)
|
||
if _match_op(ps, TokOp.VBar) == 0:
|
||
break
|
||
_advance(ps)
|
||
_skip_nl(ps) # 括号内跨行: a |\n b
|
||
right: rawptr[AST] = _parse_bitxor(ps)
|
||
node: rawptr[AST] = BinOp(ps.pool, left, OpKind.BitOr, right)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
left = node
|
||
return left
|
||
|
||
|
||
def _parse_bitxor(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
left: rawptr[AST] = _parse_bitand(ps)
|
||
if left is None: return None
|
||
while True:
|
||
_skip_nl(ps) # 括号内行首 '^': (a\n ^ b)
|
||
if _match_op(ps, TokOp.Caret) == 0:
|
||
break
|
||
_advance(ps)
|
||
_skip_nl(ps) # 括号内跨行: a ^\n b
|
||
right: rawptr[AST] = _parse_bitand(ps)
|
||
node: rawptr[AST] = BinOp(ps.pool, left, OpKind.BitXor, right)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
left = node
|
||
return left
|
||
|
||
|
||
def _parse_bitand(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
left: rawptr[AST] = _parse_shift(ps)
|
||
if left is None: return None
|
||
while True:
|
||
_skip_nl(ps) # 括号内行首 '&': (a\n & b)
|
||
if _match_op(ps, TokOp.Amp) == 0:
|
||
break
|
||
_advance(ps)
|
||
_skip_nl(ps) # 括号内跨行: a &\n b
|
||
right: rawptr[AST] = _parse_shift(ps)
|
||
node: rawptr[AST] = BinOp(ps.pool, left, OpKind.BitAnd, right)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
left = node
|
||
return left
|
||
|
||
|
||
def _parse_shift(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
left: rawptr[AST] = _parse_arith(ps)
|
||
if left is None: return None
|
||
while True:
|
||
_skip_nl(ps) # 括号内行首 '<<'/'>>': (a\n << b)
|
||
if _match_op(ps, TokOp.LtLt) == 0 and _match_op(ps, TokOp.GtGt) == 0:
|
||
break
|
||
op_token: int = _cur_op(ps)
|
||
_advance(ps)
|
||
_skip_nl(ps) # 括号内跨行: a <<\n b
|
||
right: rawptr[AST] = _parse_arith(ps)
|
||
ast_op: int = OpKind.LShift
|
||
if op_token == TokOp.GtGt:
|
||
ast_op = OpKind.RShift
|
||
node: rawptr[AST] = BinOp(ps.pool, left, ast_op, right)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
left = node
|
||
return left
|
||
|
||
|
||
def _parse_arith(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
left: rawptr[AST] = _parse_term(ps)
|
||
if left is None: return None
|
||
while True:
|
||
_skip_nl(ps) # 括号内行首 '+'/'-': (a\n + b)
|
||
if _match_op(ps, TokOp.Plus) == 0 and _match_op(ps, TokOp.Minus) == 0:
|
||
break
|
||
op_token: int = _cur_op(ps)
|
||
_advance(ps)
|
||
_skip_nl(ps) # 括号内跨行: a +\n b
|
||
right: rawptr[AST] = _parse_term(ps)
|
||
ast_op: int = OpKind.Add
|
||
if op_token == TokOp.Minus:
|
||
ast_op = OpKind.Sub
|
||
node: rawptr[AST] = BinOp(ps.pool, left, ast_op, right)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
left = node
|
||
return left
|
||
|
||
|
||
# ============================================================
|
||
# C 风格指针后缀('*' / '**')—— 与 TransPyC lib/vast 的 _AtPtrSuffix 同源
|
||
# ============================================================
|
||
# Start position (line/col) of the last consumed C-style pointer suffix ('*' / '**').
|
||
# _parse_paren matches it against ps.prev to tell the paren-less cast `(T*)x` from a
|
||
# plain group like `(a | b)`. Recorded as ints, not as a token pointer: comparing two
|
||
# token pointers whose declared pointer levels differ is rejected by the compare check.
|
||
_g_ptr_suffix_line: int = 0
|
||
_g_ptr_suffix_col: int = 0
|
||
|
||
|
||
def _ptr_suffix_end_op(op: int) -> int:
|
||
"""后缀末尾 token 是否为“无右操作数”位置(']' ',' ')' ':' '=')。
|
||
|
||
这些位置后接 '*'/'**' 时,乘法/幂本就缺右操作数(原语法错误),
|
||
故按指针后缀处理不会改变任何合法表达式的语义(x[a * b] / a ** b 等不受影响)。
|
||
"""
|
||
if op == TokOp.Rsqb or op == TokOp.Comma or op == TokOp.RPar \
|
||
or op == TokOp.Colon or op == TokOp.Equal:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _at_ptr_suffix(ps: rawptr[Parser], base: rawptr[AST]) -> int:
|
||
"""判定当前 '*'/'**' 串是否为作用于 base 的 C 风格指针后缀。
|
||
|
||
判据(与 vast 一致):base 是类型原子(Name/Attribute/Subscript),
|
||
且从当前 '*'/'**' 起跨过连续 '*'/'**' 与 Nl 后,紧跟 ']' / ',' / ')' / ':' / '='。
|
||
该判据同时覆盖注解与表达式上下文(如 list[ast.AST*] / list[ast.AST*]*)。
|
||
"""
|
||
if base is None:
|
||
return 0
|
||
k: int = base.kind()
|
||
if k != ASTKind.Name and k != ASTKind.Attribute and k != ASTKind.Subscript:
|
||
return 0
|
||
nxt: rawptr[Token] = ps.cur
|
||
stars: int = 0
|
||
while nxt is not None:
|
||
if nxt.type == TokenType.Op \
|
||
and (nxt.op_subtype == TokOp.Star or nxt.op_subtype == TokOp.StarStar):
|
||
stars += 1
|
||
nxt = nxt.next
|
||
elif nxt.type == TokenType.Nl:
|
||
nxt = nxt.next
|
||
else:
|
||
break
|
||
if stars == 0 or nxt is None or nxt.type != TokenType.Op:
|
||
return 0
|
||
return _ptr_suffix_end_op(nxt.op_subtype)
|
||
|
||
|
||
def _make_ptr_type(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]:
|
||
"""构造一层指针包装:base | t.CPtr。"""
|
||
cptr: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||
"CPtr", ASTCtx.Load)
|
||
node: rawptr[AST] = BinOp(ps.pool, base, OpKind.BitOr, cptr)
|
||
_set_pos(node, base.lineno, base.col_offset, 0, 0)
|
||
return node
|
||
|
||
|
||
def _consume_ptr_suffix(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]:
|
||
"""消费连续的 C 风格指针后缀('*' 一层 / '**' 两层),展开为 base | t.CPtr 链。"""
|
||
global _g_ptr_suffix_line, _g_ptr_suffix_col
|
||
while True:
|
||
_skip_nl(ps)
|
||
if _match_op(ps, TokOp.StarStar):
|
||
_advance(ps)
|
||
_g_ptr_suffix_line = ps.prev.lineno
|
||
_g_ptr_suffix_col = ps.prev.col_offset
|
||
base = _make_ptr_type(ps, base)
|
||
base = _make_ptr_type(ps, base)
|
||
continue
|
||
if _match_op(ps, TokOp.Star):
|
||
_advance(ps)
|
||
_g_ptr_suffix_line = ps.prev.lineno
|
||
_g_ptr_suffix_col = ps.prev.col_offset
|
||
base = _make_ptr_type(ps, base)
|
||
continue
|
||
break
|
||
return base
|
||
|
||
|
||
def _at_cast_operand(ps: rawptr[Parser]) -> int:
|
||
"""Whether the current token may start the operand of a paren-less cast `(T*)x`.
|
||
|
||
'(' is deliberately absent: `(T*)(x)` is already handled by the trailer loop as a
|
||
cast call, and keeping it there leaves that path's AST untouched. Nl is not
|
||
skipped either: a line break after ')' starts a new statement, not an operand.
|
||
"""
|
||
if _cur_type(ps) == TokenType.Name:
|
||
return 1
|
||
if _cur_type(ps) == TokenType.Number:
|
||
return 1
|
||
if _cur_type(ps) == TokenType.String:
|
||
return 1
|
||
op: int = _cur_op(ps)
|
||
if op == TokOp.Lsqb or op == TokOp.LBrace or op == TokOp.Star or op == TokOp.Amp or op == TokOp.Minus or op == TokOp.Plus or op == TokOp.Tilde:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _at_ptr_suffix_call(ps: rawptr[Parser], base: rawptr[AST]) -> int:
|
||
"""判定当前 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用**。
|
||
|
||
判据(与 TPC lib/vast 的 _AtPtrSuffixCall 同源):
|
||
1. base 是类型原子(Name/Attribute/Subscript);
|
||
2. base 与 '*'/'**' 串**紧贴**(token 行列相邻、同行,无空白/换行);
|
||
3. '*'/'**' 串(串内逐个 token 紧贴)之后**紧贴**一个 '('。
|
||
|
||
即整段写作**无空白**的 `T*(x)`(C 风格强转写法)。为什么 '(' 不进
|
||
_ptr_suffix_end_op 表:`a * (b)` 是合法乘法('(' 可作乘法右操作数起点),
|
||
无条件计入会误伤乘法。故叠加"前驱是类型原子 + 两侧紧贴";`a * (b + c)` /
|
||
`n * (m - 1)` 等有空格者仍按乘法解析。
|
||
"""
|
||
if base is None:
|
||
return 0
|
||
k: int = base.kind()
|
||
if k != ASTKind.Name and k != ASTKind.Attribute and k != ASTKind.Subscript:
|
||
return 0
|
||
nxt: rawptr[Token] = ps.cur
|
||
pv: rawptr[Token] = ps.prev
|
||
if pv is None or nxt is None:
|
||
return 0
|
||
if pv.type == TokenType.Nl or pv.type == TokenType.NewLine:
|
||
return 0
|
||
if pv.end_lineno != nxt.lineno or pv.end_col_offset != nxt.col_offset:
|
||
return 0 # base 与 '*'/'**' 之间有空白/换行:非 C 风格强转写法
|
||
stars: int = 0
|
||
prev: rawptr[Token] = None
|
||
last: rawptr[Token] = None
|
||
while nxt is not None:
|
||
if nxt.type == TokenType.Op \
|
||
and (nxt.op_subtype == TokOp.Star or nxt.op_subtype == TokOp.StarStar):
|
||
if prev is not None \
|
||
and (nxt.lineno != prev.end_lineno or nxt.col_offset != prev.end_col_offset):
|
||
return 0 # 星串内部出现空白:非紧邻,不判为强转
|
||
stars += 1
|
||
prev = nxt
|
||
last = nxt
|
||
nxt = nxt.next
|
||
else:
|
||
break
|
||
if stars == 0 or last is None or nxt is None:
|
||
return 0
|
||
if nxt.type != TokenType.Op or nxt.op_subtype != TokOp.LPar:
|
||
return 0
|
||
if nxt.lineno != last.end_lineno or nxt.col_offset != last.end_col_offset:
|
||
return 0
|
||
return 1
|
||
|
||
|
||
def _consume_ptr_suffix_call(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]:
|
||
"""把已认定为 `T*(...)` 的指针后缀强转解析为**调用**。
|
||
|
||
产物与既有强转形态 `(T | t.CPtr)(...)` 逐字同形:同为 Call
|
||
(func = base 展开的 `base | t.CPtr` 链,args/keywords = 括号实参),
|
||
不新造第二套 cast 表示。
|
||
"""
|
||
func: rawptr[AST] = _consume_ptr_suffix(ps, base)
|
||
return _parse_call(ps, func)
|
||
|
||
|
||
def _parse_term(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
left: rawptr[AST] = _parse_factor(ps)
|
||
if left is None: return None
|
||
while True:
|
||
_skip_nl(ps) # 括号内行首 '*'/'/'/'//'/'%'/'@': (a\n * b)
|
||
if _match_op(ps, TokOp.Star) and _at_ptr_suffix(ps, left) != 0:
|
||
left = _consume_ptr_suffix(ps, left) # C 风格指针后缀,不按乘法处理
|
||
continue
|
||
op_token: int = 0
|
||
if _match_op(ps, TokOp.Star): op_token = TokOp.Star
|
||
elif _match_op(ps, TokOp.Slash): op_token = TokOp.Slash
|
||
elif _match_op(ps, TokOp.DSlash): op_token = TokOp.DSlash
|
||
elif _match_op(ps, TokOp.Percent): op_token = TokOp.Percent
|
||
elif _match_op(ps, TokOp.At): op_token = TokOp.At
|
||
if op_token == 0: break
|
||
_advance(ps)
|
||
_skip_nl(ps) # 括号内跨行: a *\n b
|
||
right: rawptr[AST] = _parse_factor(ps)
|
||
ast_op: int = _binop_from_op(op_token)
|
||
node: rawptr[AST] = BinOp(ps.pool, left, ast_op, right)
|
||
_set_pos(node, left.lineno, left.col_offset, 0, 0)
|
||
left = node
|
||
return left
|
||
|
||
|
||
def _parse_factor(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""factor: '+' factor | '-' factor | '~' factor | '*' factor | '&' factor | power"""
|
||
if (_match_op(ps, TokOp.Plus) or _match_op(ps, TokOp.Minus) or _match_op(ps, TokOp.Tilde)
|
||
or _match_op(ps, TokOp.Star) or _match_op(ps, TokOp.Amp)):
|
||
op_token: int = _cur_op(ps)
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
# 一元减 + 整数字面量的**联合越界判定**:`-` 是一元运算符、不属于字面量,
|
||
# 故幅值合法(≤ 2^64-1,词法层已放行)的字面量取负后可能 < -2^63。
|
||
# 只有"直接作用于整数常量"的一元减在此判定:`1 - 2` / `a - b` 是二元减
|
||
# (经 _parse_arith,不进入本分支);`-x`(变量)操作数不是常量,不判定。
|
||
if op_token == TokOp.Minus and _cur_type(ps) == TokenType.Number \
|
||
and ps.cur.is_float == 0 \
|
||
and _lex_int_neg_exceeds_i64(_cur_str(ps)) != 0:
|
||
_record_neg_literal_overflow(_cur_str(ps), lineno, col)
|
||
ps.error_count += 1
|
||
return None
|
||
operand: rawptr[AST] = _parse_factor(ps)
|
||
ast_op: int = OpKind.UAdd
|
||
if op_token == TokOp.Minus:
|
||
ast_op = OpKind.USub
|
||
elif op_token == TokOp.Tilde:
|
||
ast_op = OpKind.Invert
|
||
elif op_token == TokOp.Star:
|
||
ast_op = OpKind.Deref
|
||
elif op_token == TokOp.Amp:
|
||
ast_op = OpKind.AddrOf
|
||
node: rawptr[AST] = UnaryOp(ps.pool, ast_op, operand)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
return _parse_power(ps)
|
||
|
||
|
||
def _parse_power(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""power: await_expr ['**' factor] | atom_expr ['**' factor]"""
|
||
base: rawptr[AST] = _parse_atom_expr(ps)
|
||
if base is None: return None
|
||
if _match_op(ps, TokOp.StarStar) and _at_ptr_suffix(ps, base) != 0:
|
||
return _consume_ptr_suffix(ps, base) # C 风格指针后缀('**' 计 2 层)
|
||
if _match_op(ps, TokOp.StarStar):
|
||
_advance(ps)
|
||
exp: rawptr[AST] = _parse_factor(ps)
|
||
node: rawptr[AST] = BinOp(ps.pool, base, OpKind.Pow, exp)
|
||
_set_pos(node, base.lineno, base.col_offset, 0, 0)
|
||
return node
|
||
return base
|
||
|
||
|
||
def _parse_atom_expr(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""atom_expr: ['await'] atom trailer*"""
|
||
if _cur_kw(ps) == Keyword.Await:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
inner: rawptr[AST] = _parse_atom_expr(ps)
|
||
node: rawptr[AST] = Await(ps.pool, inner)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
base: rawptr[AST] = _parse_atom(ps)
|
||
if base is None: return None
|
||
# trailer loop
|
||
while True:
|
||
if (_match_op(ps, TokOp.Star) or _match_op(ps, TokOp.StarStar)) \
|
||
and _at_ptr_suffix_call(ps, base) != 0:
|
||
# `T*(x)` / `T**(x)` 指针后缀强转:解析为 cast 调用(与
|
||
# `(T | t.CPtr)(x)` 同形)。在 trailer 循环里消费,故其后的
|
||
# `.attr` / `[i]` / `(…)` 仍由本循环继续挂接。
|
||
base = _consume_ptr_suffix_call(ps, base)
|
||
elif _match_op(ps, TokOp.Dot):
|
||
_advance(ps)
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
break
|
||
attr_name: str = _cur_str(ps)
|
||
attr_lineno: int = _cur_lineno(ps)
|
||
attr_col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
ctx: int = ASTCtx.Load
|
||
if _match_op(ps, TokOp.Equal):
|
||
ctx = ASTCtx.Store
|
||
elif _match_op(ps, TokOp.PlusEq) or _match_op(ps, TokOp.MinusEq):
|
||
ctx = ASTCtx.Store
|
||
base = Attribute(ps.pool, base, attr_name, ctx)
|
||
_set_pos(base, attr_lineno, attr_col, 0, 0)
|
||
elif _match_op(ps, TokOp.LPar):
|
||
base = _parse_call(ps, base)
|
||
elif _match_op(ps, TokOp.Lsqb):
|
||
base = _parse_subscript(ps, base)
|
||
else:
|
||
break
|
||
return base
|
||
|
||
|
||
def _parse_call(ps: rawptr[Parser], func: rawptr[AST]) -> rawptr[AST]:
|
||
"""Parse function call trailer. Both args and keywords are lists."""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # (
|
||
_skip_nls_and_newlines(ps) # Skip the line break after '('
|
||
args: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
keywords: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
|
||
if not _match_op(ps, TokOp.RPar):
|
||
while True:
|
||
# **expr
|
||
if _match_op(ps, TokOp.StarStar):
|
||
_advance(ps)
|
||
_skip_nls_and_newlines(ps)
|
||
val: rawptr[AST] = _parse_expr(ps)
|
||
kw: rawptr[AST] = KwNode(ps.pool, None, val)
|
||
_set_pos(kw, _cur_lineno(ps), _cur_col(ps), 0, 0)
|
||
keywords.append(kw)
|
||
# *expr
|
||
elif _match_op(ps, TokOp.Star):
|
||
_advance(ps)
|
||
_skip_nls_and_newlines(ps)
|
||
val = _parse_expr(ps)
|
||
star: rawptr[AST] = Starred(ps.pool, val, ASTCtx.Load)
|
||
_set_pos(star, val.lineno, val.col_offset, 0, 0)
|
||
args.append(star)
|
||
# keyword = expr
|
||
elif _cur_type(ps) == TokenType.Name:
|
||
if ps.cur.next is not None and ps.cur.next.type == TokenType.Op and ps.cur.next.op_subtype == TokOp.Equal:
|
||
kw_name: str = _cur_str(ps)
|
||
kw_lineno: int = _cur_lineno(ps)
|
||
kw_col: int = _cur_col(ps)
|
||
_advance(ps) # name
|
||
_advance(ps) # =
|
||
_skip_nls_and_newlines(ps)
|
||
val = _parse_expr(ps)
|
||
kw = KwNode(ps.pool, kw_name, val)
|
||
_set_pos(kw, kw_lineno, kw_col, 0, 0)
|
||
keywords.append(kw)
|
||
else:
|
||
val = _parse_expr(ps)
|
||
args.append(val)
|
||
else:
|
||
val = _parse_expr(ps)
|
||
args.append(val)
|
||
_skip_nls_and_newlines(ps) # Skip the line break after the parameter
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_skip_nls_and_newlines(ps) # Skip the line break after the comma
|
||
_skip_nls_and_newlines(ps) # Skip the line break before ')'
|
||
_expect_op(ps, TokOp.RPar)
|
||
node: rawptr[AST] = Call(ps.pool, func, args, keywords)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_subscript(ps: rawptr[Parser], value: rawptr[AST]) -> rawptr[AST]:
|
||
"""Parse subscript trailer."""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # [
|
||
_skip_nl(ps)
|
||
slice_node: rawptr[AST] = _parse_slice(ps)
|
||
_expect_op(ps, TokOp.Rsqb)
|
||
ctx: int = ASTCtx.Load
|
||
if _match_op(ps, TokOp.Equal):
|
||
ctx = ASTCtx.Store
|
||
node: rawptr[AST] = Subscript(ps.pool, value, slice_node, ctx)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_slice(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""Parse slice: [expr] ':' [expr] [':' [expr]] | expr | expr, expr, ..."""
|
||
lower: rawptr[AST] = None
|
||
# 空下标(`T[]`,类型注解位置的无签名函数指针 t.Callable[]):`[` 后直接 `]`
|
||
# ⇒ slice 为 None(由类型解析侧按"空参数表"处理),不能调 _parse_expr,
|
||
# 否则报"意外的 token ']'"。
|
||
if not _match_op(ps, TokOp.Colon) and not _match_op(ps, TokOp.Rsqb):
|
||
lower = _parse_expr(ps)
|
||
|
||
# Check if there’s a comma (tuple syntax: a[x, y, ...])
|
||
if _match_op(ps, TokOp.Comma):
|
||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
if lower is not None:
|
||
elts.append(lower)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
if _match_op(ps, TokOp.Rsqb):
|
||
break # Trailing comma
|
||
elem: rawptr[AST] = _parse_expr(ps)
|
||
if elem is not None:
|
||
elts.append(elem)
|
||
lineno: int = 0
|
||
if lower is not None:
|
||
lineno = lower.lineno
|
||
node: rawptr[AST] = Tuple(ps.pool, elts, ASTCtx.Load)
|
||
_set_pos(node, lineno, 0, 0, 0)
|
||
return node
|
||
|
||
if _match_op(ps, TokOp.Colon):
|
||
_advance(ps)
|
||
upper: rawptr[AST] = None
|
||
if not _match_op(ps, TokOp.Colon) and not _match_op(ps, TokOp.Rsqb):
|
||
upper = _parse_expr(ps)
|
||
step: rawptr[AST] = None
|
||
if _accept_op(ps, TokOp.Colon):
|
||
if not _match_op(ps, TokOp.Rsqb):
|
||
step = _parse_expr(ps)
|
||
lineno: int = 0
|
||
if lower is not None:
|
||
lineno = lower.lineno
|
||
elif upper is not None:
|
||
lineno = upper.lineno
|
||
else:
|
||
lineno = _cur_lineno(ps)
|
||
node: rawptr[AST] = Slice(ps.pool, lower, upper, step)
|
||
_set_pos(node, lineno, 0, 0, 0)
|
||
return node
|
||
return lower
|
||
|
||
|
||
def _parse_atom(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""atom: the smallest unit of expression"""
|
||
ttype: int = _cur_type(ps)
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
|
||
if ttype == TokenType.Name:
|
||
kw: int = _cur_kw(ps)
|
||
if kw == Keyword.True_:
|
||
_advance(ps)
|
||
return _new_const_bool(ps.pool, 1, lineno, col)
|
||
if kw == Keyword.False_:
|
||
_advance(ps)
|
||
return _new_const_bool(ps.pool, 0, lineno, col)
|
||
if kw == Keyword.None_:
|
||
_advance(ps)
|
||
return _new_const_none(ps.pool, lineno, col)
|
||
name_str: str = _cur_str(ps)
|
||
_advance(ps)
|
||
node: rawptr[AST] = Name(ps.pool, name_str, ASTCtx.Load)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
if ttype == TokenType.Number:
|
||
tok: rawptr[Token] = ps.cur
|
||
_advance(ps)
|
||
if tok.is_float:
|
||
return _new_const_float(ps.pool, tok.float_val, lineno, col)
|
||
return _new_const_int(ps.pool, tok.int_val, lineno, col)
|
||
|
||
if ttype == TokenType.String:
|
||
sval: str = _cur_str(ps)
|
||
is_fstr: int = ps.cur.kw_subtype & 4
|
||
is_triple: int = ps.cur.kw_subtype & 8
|
||
is_dquote: int = ps.cur.kw_subtype & 16
|
||
is_char: int = ps.cur.kw_subtype & 32
|
||
_advance(ps)
|
||
node: rawptr[AST]
|
||
if is_fstr:
|
||
node = _parse_fstring(ps, sval, lineno, col)
|
||
else:
|
||
node = _new_const_str(ps.pool, sval, lineno, col, is_dquote, is_triple, is_char)
|
||
# Python 相邻字符串字面量隐式拼接:"a" "b" → "ab"
|
||
# Nl 是括号内换行 token(允许括号内跨行拼接);语句级换行是 NewLine,不跳过
|
||
while True:
|
||
_skip_nl(ps)
|
||
if _cur_type(ps) != TokenType.String:
|
||
break
|
||
n_sval: str = _cur_str(ps)
|
||
n_fstr: int = ps.cur.kw_subtype & 4
|
||
n_triple: int = ps.cur.kw_subtype & 8
|
||
n_dquote: int = ps.cur.kw_subtype & 16
|
||
n_char: int = ps.cur.kw_subtype & 32
|
||
_advance(ps)
|
||
n_node: rawptr[AST]
|
||
if n_fstr:
|
||
n_node = _parse_fstring(ps, n_sval, lineno, col)
|
||
else:
|
||
n_node = _new_const_str(ps.pool, n_sval, lineno, col, n_dquote, n_triple, n_char)
|
||
node = _concat_str_nodes(ps.pool, node, n_node)
|
||
return node
|
||
|
||
if _match_op(ps, TokOp.Ellipsis):
|
||
_advance(ps)
|
||
return _new_const_none(ps.pool, lineno, col)
|
||
|
||
if _match_op(ps, TokOp.LPar):
|
||
return _parse_paren(ps)
|
||
if _match_op(ps, TokOp.Lsqb):
|
||
return _parse_list(ps)
|
||
if _match_op(ps, TokOp.LBrace):
|
||
return _parse_brace(ps)
|
||
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
return None
|
||
|
||
|
||
def _parse_paren(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""Parenthesis expressions: () | (expr) | (expr,) | (expr, ...) | (yield)"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # (
|
||
_skip_nl(ps) # Skip the line break after '(' (multi-line tuple)
|
||
if _match_op(ps, TokOp.RPar):
|
||
_advance(ps)
|
||
# Empty tuple
|
||
empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
node: rawptr[AST] = Tuple(ps.pool, empty, ASTCtx.Load)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
# yield
|
||
if _cur_kw(ps) == Keyword.Yield:
|
||
y: rawptr[AST] = _parse_yield(ps)
|
||
_expect_op(ps, TokOp.RPar)
|
||
return y
|
||
first: rawptr[AST] = _parse_expr(ps)
|
||
if _match_op(ps, TokOp.Comma):
|
||
# Tuple
|
||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
elts.append(first)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
_skip_nl(ps)
|
||
if _match_op(ps, TokOp.RPar):
|
||
break
|
||
e: rawptr[AST] = _parse_expr(ps)
|
||
elts.append(e)
|
||
_skip_nl(ps) # 末元素无尾逗号时收尾(多行元组)
|
||
_expect_op(ps, TokOp.RPar)
|
||
node = Tuple(ps.pool, elts, ASTCtx.Load)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
# Paren-less C cast `(T*)x`: the inner expression ends with a pointer suffix
|
||
# (ps.prev is that suffix's '*'/'**' token) and the operand follows ')' directly.
|
||
# Same product as `(T*)(x)` / `T*(x)`: a Call whose func is `T | t.CPtr`.
|
||
is_ptr_cast: int = 0
|
||
if _g_ptr_suffix_line > 0 and _match_op(ps, TokOp.RPar) and ps.prev.lineno == _g_ptr_suffix_line and ps.prev.col_offset == _g_ptr_suffix_col:
|
||
is_ptr_cast = 1
|
||
_skip_nl(ps) # (expr\n) 单表达式括号跨行收尾
|
||
_expect_op(ps, TokOp.RPar)
|
||
# Check for generator expression
|
||
if _cur_kw(ps) == Keyword.For:
|
||
return _parse_comprehension(ps, first, ASTKind.GeneratorExp, lineno, col)
|
||
if is_ptr_cast != 0 and _at_cast_operand(ps) != 0:
|
||
operand: rawptr[AST] = _parse_factor(ps)
|
||
cast_args: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
cast_args.append(operand)
|
||
cast_kws: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
cast_node: rawptr[AST] = Call(ps.pool, first, cast_args, cast_kws)
|
||
_set_pos(cast_node, first.lineno, first.col_offset, 0, 0)
|
||
return cast_node
|
||
return first
|
||
|
||
|
||
def _parse_list(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""List expressions: [] | [expr_list] | [expr_list comp_for]"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # [
|
||
_skip_nl(ps) # Skip the line break after '[' (multi-line list)
|
||
if _match_op(ps, TokOp.Rsqb):
|
||
_advance(ps)
|
||
empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
node: rawptr[AST] = List(ps.pool, empty, ASTCtx.Load)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
first: rawptr[AST] = _parse_expr(ps)
|
||
if _cur_kw(ps) == Keyword.For:
|
||
node = _parse_comprehension(ps, first, ASTKind.ListComp, lineno, col)
|
||
_expect_op(ps, TokOp.Rsqb)
|
||
return node
|
||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
elts.append(first)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
_skip_nl(ps)
|
||
if _match_op(ps, TokOp.Rsqb):
|
||
break
|
||
e: rawptr[AST] = _parse_expr(ps)
|
||
elts.append(e)
|
||
_skip_nl(ps) # 末元素无尾逗号时收尾(base64.vp 多行列表)
|
||
_expect_op(ps, TokOp.Rsqb)
|
||
node = List(ps.pool, elts, ASTCtx.Load)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_brace(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""Brace expressions: {} | {set_items} | {dict_items} | {comp_for}"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # {
|
||
_skip_nl(ps) # Skip the line break after '{' (multi-line dict)
|
||
if _match_op(ps, TokOp.RBrace):
|
||
_advance(ps)
|
||
empty_k: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
empty_v: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
node: rawptr[AST] = Dict(ps.pool, empty_k, empty_v)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
first: rawptr[AST] = _parse_expr(ps)
|
||
# dict: key: value
|
||
if _match_op(ps, TokOp.Colon):
|
||
_advance(ps)
|
||
val: rawptr[AST] = _parse_expr(ps)
|
||
if _cur_kw(ps) == Keyword.For:
|
||
# dict comprehension
|
||
gens: rawptr[list[rawptr[AST]]] = _parse_comp_for(ps)
|
||
node = DictComp(ps.pool, first, val, gens)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
_expect_op(ps, TokOp.RBrace)
|
||
return node
|
||
keys: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
values: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
keys.append(first)
|
||
values.append(val)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
_skip_nl(ps)
|
||
if _match_op(ps, TokOp.RBrace):
|
||
break
|
||
k: rawptr[AST] = _parse_expr(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
v: rawptr[AST] = _parse_expr(ps)
|
||
keys.append(k)
|
||
values.append(v)
|
||
_skip_nl(ps) # 末元素无尾逗号时收尾
|
||
_expect_op(ps, TokOp.RBrace)
|
||
node = Dict(ps.pool, keys, values)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
# set
|
||
if _cur_kw(ps) == Keyword.For:
|
||
node = _parse_comprehension(ps, first, ASTKind.SetComp, lineno, col)
|
||
_expect_op(ps, TokOp.RBrace)
|
||
return node
|
||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
elts.append(first)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
_skip_nl(ps) # 括号内跨行集合字面量
|
||
if _match_op(ps, TokOp.RBrace):
|
||
break
|
||
e: rawptr[AST] = _parse_expr(ps)
|
||
elts.append(e)
|
||
_skip_nl(ps) # 末元素无尾逗号时收尾
|
||
_expect_op(ps, TokOp.RBrace)
|
||
node = Set(ps.pool, elts)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
# ============================================================
|
||
# f-string parsing
|
||
# ============================================================
|
||
|
||
def _parse_sub_expr(ps: rawptr[Parser], src: str) -> rawptr[AST]:
|
||
"""Create a sub-lexer and parser to parse expressions (used for {expr} inside f-strings)"""
|
||
pool: rawptr[memhub.MemManager] = ps.pool
|
||
lx: rawptr[Lexer] = new_lexer(pool)
|
||
if lx is None: return None
|
||
_lexer_init(lx, src, pool)
|
||
tokens: rawptr[Token] = tokenize(lx)
|
||
if tokens is None: return None
|
||
sub_ps: rawptr[Parser] = new_parser(pool)
|
||
if sub_ps is None: return None
|
||
_parser_init(sub_ps, tokens, pool)
|
||
return _parse_test(sub_ps)
|
||
|
||
|
||
def _append_str_value(pool: rawptr[memhub.MemManager],
|
||
values: rawptr[list[rawptr[AST]]],
|
||
node: rawptr[AST]):
|
||
"""将字符串节点展开加入 values(JoinedStr 展开其 values,其余直接追加)"""
|
||
if node is None:
|
||
return
|
||
if node.kind() == ASTKind.JoinedStr:
|
||
js_val: rawptr[JoinedStr] = (rawptr[JoinedStr])(node)
|
||
if js_val.values is not None:
|
||
n_v: sizet = js_val.values.__len__()
|
||
i_v: sizet
|
||
for i_v in range(n_v):
|
||
values.append(js_val.values.get(i_v))
|
||
else:
|
||
values.append(node)
|
||
|
||
|
||
def _concat_str_nodes(pool: rawptr[memhub.MemManager],
|
||
a: rawptr[AST], b: rawptr[AST]) -> rawptr[AST]:
|
||
"""合并相邻字符串字面量节点(Python 隐式拼接 "a" "b" → "ab")
|
||
|
||
纯常量字符串直接拼接文本;涉及 f-string(JoinedStr/FormattedValue)时
|
||
合并到 JoinedStr 的 values 列表。
|
||
"""
|
||
if a is None:
|
||
return b
|
||
if b is None:
|
||
return a
|
||
# 两个都是常量字符串 → 直接拼接文本
|
||
if a.kind() == ASTKind.Constant and b.kind() == ASTKind.Constant:
|
||
ca_c: rawptr[Constant] = (rawptr[Constant])(a)
|
||
cb_c: rawptr[Constant] = (rawptr[Constant])(b)
|
||
if ca_c.const_kind == CONST_STR and cb_c.const_kind == CONST_STR \
|
||
and ca_c.str_val is not None and cb_c.str_val is not None:
|
||
la_c: sizet = string.strlen(ca_c.str_val)
|
||
lb_c: sizet = string.strlen(cb_c.str_val)
|
||
buf_c: str = pool.alloc(la_c + lb_c + 1)
|
||
if buf_c is not None:
|
||
string.memcpy(buf_c, ca_c.str_val, la_c)
|
||
string.memcpy(buf_c + la_c, cb_c.str_val, lb_c + 1)
|
||
return _new_const_str(pool, buf_c, a.lineno, a.col_offset, 0, 0, 0)
|
||
# 涉及 f-string → 合并到 JoinedStr
|
||
values_c: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](pool, 8)
|
||
_append_str_value(pool, values_c, a)
|
||
_append_str_value(pool, values_c, b)
|
||
if values_c.__len__() == 1:
|
||
return values_c.get(0)
|
||
node_c: rawptr[AST] = JoinedStr(pool, values_c)
|
||
_set_pos(node_c, a.lineno, a.col_offset, 0, 0)
|
||
return node_c
|
||
|
||
|
||
def _parse_fstring(ps: rawptr[Parser], content: str,
|
||
lineno: int, col: int) -> rawptr[AST]:
|
||
"""Parse f-string content, building JoinedStr or FormattedValue nodes"""
|
||
pool: rawptr[memhub.MemManager] = ps.pool
|
||
values: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](pool, 8)
|
||
i: sizet = 0
|
||
content_len: sizet = string.strlen(content)
|
||
# Literal text buffer for f-string content
|
||
lit_buf: str = pool.alloc(content_len + 1)
|
||
if lit_buf is None: return None
|
||
lit_pos: sizet = 0
|
||
|
||
while i < content_len:
|
||
ch: char = content[i]
|
||
if ch == '{':
|
||
# {{ → Literal {
|
||
if i + 1 < content_len and content[i + 1] == '{':
|
||
lit_buf[lit_pos] = '{'
|
||
lit_pos += 1
|
||
i += 2
|
||
continue
|
||
# Refresh literal buffer
|
||
if lit_pos > 0:
|
||
lit_buf[lit_pos] = '\0'
|
||
lit_node: rawptr[AST] = _new_const_str(pool, lit_buf, lineno, col)
|
||
values.append(lit_node)
|
||
lit_buf = pool.alloc(content_len + 1)
|
||
if lit_buf is None: break
|
||
lit_pos = 0
|
||
# Find matching closing '}' while tracking nested '{}' and string literals inside the f-string
|
||
j: sizet = i + 1
|
||
depth: int = 1
|
||
conv_pos: sizet = 0
|
||
fmt_pos: sizet = 0
|
||
while j < content_len and depth > 0:
|
||
c: char = content[j]
|
||
if c == '\'' or c == '"':
|
||
# 跳过表达式块内的嵌套字符串字面量(PEP 701 同引号嵌套),
|
||
# 避免其内部字符干扰 {} 深度/转换/格式分隔符定位
|
||
nq: char = c
|
||
j += 1
|
||
while j < content_len:
|
||
c2: char = content[j]
|
||
if c2 == '\\':
|
||
j += 2
|
||
continue
|
||
if c2 == nq:
|
||
j += 1
|
||
break
|
||
j += 1
|
||
continue
|
||
if c == '{':
|
||
depth += 1
|
||
elif c == '}':
|
||
depth -= 1
|
||
if depth == 0:
|
||
break
|
||
elif depth == 1:
|
||
if c == '!' and conv_pos == 0 and fmt_pos == 0:
|
||
if j + 1 < content_len:
|
||
nc: char = content[j + 1]
|
||
if nc == 's' or nc == 'r' or nc == 'a':
|
||
if j + 2 < content_len:
|
||
after: char = content[j + 2]
|
||
if after == ':' or after == '}':
|
||
conv_pos = j
|
||
elif j + 2 == content_len:
|
||
conv_pos = j
|
||
elif c == ':' and fmt_pos == 0:
|
||
fmt_pos = j
|
||
j += 1
|
||
if depth != 0:
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
break
|
||
# Extract expression text
|
||
expr_end: sizet = j
|
||
if conv_pos > 0:
|
||
expr_end = conv_pos
|
||
elif fmt_pos > 0:
|
||
expr_end = fmt_pos
|
||
expr_len: sizet = expr_end - i - 1
|
||
if expr_len == 0:
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
i = j + 1
|
||
continue
|
||
expr_buf: str = pool.alloc(expr_len + 1)
|
||
if expr_buf is None: break
|
||
string.memcpy(expr_buf, content + i + 1, expr_len)
|
||
expr_buf[expr_len] = '\0'
|
||
# Parse expression text
|
||
expr_node: rawptr[AST] = _parse_sub_expr(ps, expr_buf)
|
||
# Conversion specifier
|
||
conversion: int = -1
|
||
if conv_pos > 0:
|
||
conv_char: char = content[conv_pos + 1]
|
||
if conv_char == 's':
|
||
conversion = 115
|
||
elif conv_char == 'r':
|
||
conversion = 114
|
||
elif conv_char == 'a':
|
||
conversion = 97
|
||
# Format specifier
|
||
format_spec: rawptr[AST] = None
|
||
if fmt_pos > 0:
|
||
spec_len: sizet = j - fmt_pos - 1
|
||
if spec_len > 0:
|
||
spec_buf: str = pool.alloc(spec_len + 1)
|
||
if spec_buf is not None:
|
||
string.memcpy(spec_buf, content + fmt_pos + 1, spec_len)
|
||
spec_buf[spec_len] = '\0'
|
||
format_spec = _new_const_str(pool, spec_buf, lineno, col)
|
||
fv: rawptr[AST] = FormattedValue(pool, expr_node, conversion, format_spec)
|
||
_set_pos(fv, lineno, col, 0, 0)
|
||
values.append(fv)
|
||
i = j + 1
|
||
elif ch == '}':
|
||
if i + 1 < content_len and content[i + 1] == '}':
|
||
lit_buf[lit_pos] = '}'
|
||
lit_pos += 1
|
||
i += 2
|
||
else:
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
i += 1
|
||
else:
|
||
lit_buf[lit_pos] = ch
|
||
lit_pos += 1
|
||
i += 1
|
||
|
||
# Refresh remaining literal buffer
|
||
if lit_pos > 0:
|
||
lit_buf[lit_pos] = '\0'
|
||
lit_node2: rawptr[AST] = _new_const_str(pool, lit_buf, lineno, col)
|
||
values.append(lit_node2)
|
||
|
||
# If no content, return empty string
|
||
if values.__len__() == 0:
|
||
return _new_const_str(pool, "", lineno, col)
|
||
# If only one FormattedValue and no literal, return FormattedValue
|
||
if values.__len__() == 1:
|
||
first: rawptr[AST] = values.get(0)
|
||
if first.kind() == ASTKind.FormattedValue:
|
||
return first
|
||
node: rawptr[AST] = JoinedStr(pool, values)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_comprehension(ps: rawptr[Parser], elt: rawptr[AST],
|
||
vtype: int, lineno: int, col: int) -> rawptr[AST]:
|
||
"""General comprehension: elt comp_for"""
|
||
gens: rawptr[list[rawptr[AST]]] = _parse_comp_for(ps)
|
||
node: rawptr[AST] = None
|
||
if vtype == ASTKind.ListComp:
|
||
node = ListComp(ps.pool, elt, gens)
|
||
elif vtype == ASTKind.SetComp:
|
||
node = SetComp(ps.pool, elt, gens)
|
||
else:
|
||
node = GeneratorExp(ps.pool, elt, gens)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_comp_for(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]:
|
||
"""comp_for: ['async'] 'for' exprlist 'in' or_test [comp_if] (comp_for)*
|
||
Returns generators list。"""
|
||
gens: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
while True:
|
||
is_async: int = 0
|
||
if _cur_kw(ps) == Keyword.Async:
|
||
is_async = 1
|
||
_advance(ps)
|
||
if _cur_kw(ps) != Keyword.For:
|
||
break
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
target: rawptr[AST] = _parse_target_list(ps)
|
||
if _cur_kw(ps) != Keyword.In:
|
||
ps.error_count += 1
|
||
break
|
||
_advance(ps)
|
||
iter_: rawptr[AST] = _parse_or_test(ps)
|
||
# Collect ifs to list
|
||
ifs: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
while _cur_kw(ps) == Keyword.If:
|
||
_advance(ps)
|
||
cond: rawptr[AST] = _parse_or_test(ps)
|
||
ifs.append(cond)
|
||
comp: rawptr[AST] = Comprehension(ps.pool, target, iter_, ifs, is_async)
|
||
_set_pos(comp, lineno, col, 0, 0)
|
||
gens.append(comp)
|
||
return gens
|
||
|
||
|
||
def _parse_target_list(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""Parse assignment target list (comma-separated)"""
|
||
first: rawptr[AST] = _parse_target(ps)
|
||
if _match_op(ps, TokOp.Comma):
|
||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
elts.append(first)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
if _cur_kw(ps) == Keyword.In:
|
||
break
|
||
e: rawptr[AST] = _parse_target(ps)
|
||
elts.append(e)
|
||
node: rawptr[AST] = Tuple(ps.pool, elts, ASTCtx.Store)
|
||
_set_pos(node, first.lineno, first.col_offset, 0, 0)
|
||
return node
|
||
return first
|
||
|
||
|
||
def _parse_target(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""Parse assignment target"""
|
||
if _match_op(ps, TokOp.Star):
|
||
_advance(ps)
|
||
inner: rawptr[AST] = _parse_atom_expr(ps)
|
||
node: rawptr[AST] = Starred(ps.pool, inner, ASTCtx.Store)
|
||
_set_pos(node, inner.lineno, inner.col_offset, 0, 0)
|
||
return node
|
||
if _match_op(ps, TokOp.LPar):
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
t_: rawptr[AST] = _parse_target_list(ps)
|
||
_skip_nl(ps)
|
||
_expect_op(ps, TokOp.RPar)
|
||
return t_
|
||
if _match_op(ps, TokOp.Lsqb):
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
first: rawptr[AST] = _parse_target(ps)
|
||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
elts.append(first)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
_skip_nl(ps)
|
||
if _match_op(ps, TokOp.Rsqb):
|
||
break
|
||
e: rawptr[AST] = _parse_target(ps)
|
||
elts.append(e)
|
||
_expect_op(ps, TokOp.Rsqb)
|
||
node = List(ps.pool, elts, ASTCtx.Store)
|
||
_set_pos(node, first.lineno, first.col_offset, 0, 0)
|
||
return node
|
||
# Normal atom_expr, ctx=Store
|
||
node = _parse_atom_expr(ps)
|
||
if node is not None:
|
||
_set_ctx(node, ASTCtx.Store)
|
||
return node
|
||
|
||
|
||
def _parse_yield(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # yield
|
||
if _cur_kw(ps) == Keyword.From:
|
||
_advance(ps)
|
||
val: rawptr[AST] = _parse_expr(ps)
|
||
node: rawptr[AST] = YieldFrom(ps.pool, val)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
val2: rawptr[AST] = None
|
||
if not _match_op(ps, TokOp.RPar) and _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker:
|
||
if _cur_type(ps) == TokenType.Op and (_cur_op(ps) == TokOp.Comma or _cur_op(ps) == TokOp.Rsqb):
|
||
pass
|
||
else:
|
||
val2 = _parse_test(ps)
|
||
node = Yield(ps.pool, val2)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_varargslist(ps: rawptr[Parser], is_lambda: int = 0) -> rawptr[AST]:
|
||
"""Parse function parameter list
|
||
New API: args/defaults/kw_defaults are lists, vararg/kwarg are fields."""
|
||
args: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
vararg: rawptr[AST] = None
|
||
kwarg: rawptr[AST] = None
|
||
defaults: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
kw_defaults: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
|
||
while True:
|
||
_skip_nl(ps)
|
||
if _match_op(ps, TokOp.RPar):
|
||
break
|
||
# *args 或 * (kwonly separated)
|
||
if _match_op(ps, TokOp.Star):
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
if _cur_type(ps) == TokenType.Name:
|
||
vararg = _parse_arg_def(ps, is_lambda)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_skip_nl(ps)
|
||
continue
|
||
# **kwargs
|
||
if _match_op(ps, TokOp.StarStar):
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
kwarg = _parse_arg_def(ps, is_lambda)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_skip_nl(ps)
|
||
continue
|
||
# / Delimiter (posonly end)— New API: posonly/regular are not distinguished
|
||
if _match_op(ps, TokOp.Slash):
|
||
_advance(ps)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_skip_nl(ps)
|
||
continue
|
||
# Normal parameter
|
||
a: rawptr[AST] = _parse_arg_def(ps, is_lambda)
|
||
if a is None: break
|
||
args.append(a)
|
||
if _match_op(ps, TokOp.Equal):
|
||
_advance(ps)
|
||
d: rawptr[AST] = _parse_test(ps)
|
||
defaults.append(d)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_skip_nl(ps)
|
||
return Arguments(ps.pool, args, vararg, kwarg, defaults, kw_defaults)
|
||
|
||
|
||
def _parse_array_prefix_declarator(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""C 风格前置数组声明符 `[N]T` / `[]T` → `t.CArray[T, N]` / `t.CArray[T, None]`
|
||
|
||
只在注解位置(_parse_type_ann)消费;当前 token 不是 '[' 时返回 None。
|
||
脱糖目标与 TPC / StubGen / DeclarationGenerator 的规范数组形态同形
|
||
(`t.CArray[E, N]`;无长度按 `t.CArray[E, None]` = 指针模式),即把
|
||
`coeffs: const [8]double` 就地等价成文档给出的修法
|
||
`coeffs: const t.CArray[double, 8]`(Debugger/type-descriptors.md §3 第 6 例)。
|
||
|
||
必须在 `_parse_test` 之前调用:否则 `[N]` 会被解析成 list 字面量,随后在元素
|
||
类型标识符处报"意外的 token"(includes/binascii.vp `HEX_VALS: [256]int` 现象)。
|
||
"""
|
||
if _match_op(ps, TokOp.Lsqb) == 0:
|
||
return None
|
||
lb_lineno: int = _cur_lineno(ps)
|
||
lb_col: int = _cur_col(ps)
|
||
_advance(ps) # 消费 '['
|
||
count_node: rawptr[AST] = None
|
||
if _match_op(ps, TokOp.Rsqb) == 0:
|
||
# 长度表达式(常量/常量表达式;非法长度由下游 ArrayDims 判据拒绝)
|
||
count_node = _parse_test(ps)
|
||
if count_node is None:
|
||
_record_parse_err(ps)
|
||
return None
|
||
if _match_op(ps, TokOp.Rsqb) == 0:
|
||
_record_parse_err(ps) # 缺少 ']'
|
||
ps.error_count += 1
|
||
return None
|
||
_advance(ps) # 消费 ']'
|
||
# 元素类型:递归 _parse_type_ann,故 `[N]const int` / `[N]int*` 走同一规则源
|
||
elem: rawptr[AST] = _parse_type_ann(ps)
|
||
if elem is None:
|
||
return None
|
||
if count_node is None:
|
||
count_node = _new_const_none(ps.pool, lb_lineno, lb_col)
|
||
# 元素大小必须显式传 8(list[T](pool, elem_size) 按字节步长寻址;
|
||
# 传 2 会让 8 字节指针按 2 字节步长写入 → 堆越界,ASan 在 _set_parent_list
|
||
# 抛 access-violation)。与文件内其它 list[rawptr[AST]] 站点同参。
|
||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
elts.append(elem)
|
||
elts.append(count_node)
|
||
carray: rawptr[AST] = Subscript(ps.pool,
|
||
Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||
"CArray", ASTCtx.Load),
|
||
Tuple(ps.pool, elts, ASTCtx.Load),
|
||
ASTCtx.Load)
|
||
_set_pos(carray, lb_lineno, lb_col, 0, 0)
|
||
return carray
|
||
|
||
|
||
def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""解析类型注解,支持类型描述符前缀(const / volatile)与 C 风格指针后缀(* / **)。
|
||
|
||
类型描述符(软关键字,只在注解位置生效;与 TPC 侧
|
||
`SymbolUtils._ExpandTypeDescriptors` 的展开产物逐字段同形):
|
||
const int → t.CConst | int
|
||
volatile int → t.CVolatile | int
|
||
const volatile int → t.CConst | t.CVolatile | int(顺序任意,两限定符都累积)
|
||
const t.CInt32T → t.CConst | t.CInt32T
|
||
const rawptr[Foo] → t.CConst | rawptr[Foo]
|
||
const int | t.CPtr → t.CConst | int | t.CPtr(粘贴在联合的最左操作数:与
|
||
TPC 文本展开 `t.CConst | int | t.CPtr` 的解析树同形)
|
||
非法形态**响亮失败**(非静默,不丢描述符):缺类型(`a: const`)/
|
||
后随非类型(`a: const 1`)/ 裸用(语句只有 `const`,见 _is_bare_descriptor)。
|
||
|
||
C 风格指针后缀:
|
||
int* → rawptr[int] (i32*)
|
||
int** → (rawptr[int]) | t.CPtr (i32**)
|
||
int****** → 6 层嵌套 (i32******)
|
||
|
||
_parse_test 会把后缀 '*' 消费为乘法(Mult)、'**' 消费为幂(Pow),
|
||
右操作数解析失败时产生 BinOp(op, right=None) 垃圾节点并 error_count+1
|
||
('int***' 形态为 BinOp(Pow, UnaryOp(Deref, None)),StarStar+Star)。
|
||
本函数回收该形态并恢复 error_count,再按层数嵌套构造 BinOp(BitOr, t.CPtr)。
|
||
"""
|
||
# 类型描述符前缀:收集 const / volatile 标记(软关键字,仅本位置消费)
|
||
mods: rawptr[list[rawptr[AST]]] = None
|
||
mod_count: int = 0
|
||
mod_lineno: int = _cur_lineno(ps)
|
||
mod_col: int = _cur_col(ps)
|
||
# `static` 修饰符(语句起始消费,`static x: int = 0`):把 t.CStatic 标记
|
||
# 追加为描述符序列的**第一个**(后续由 _PrependDescriptorMarkers 贴到类型
|
||
# 联合最左),与手写 `x: t.CStatic | int` 逐字段同形;随即清零,只作用于
|
||
# 紧随其后的这一个注解(下一个注解不会继承)。
|
||
if ps.pending_static != 0:
|
||
ps.pending_static = 0
|
||
if mods is None:
|
||
mods = list[rawptr[AST]](ps.pool, 8)
|
||
st_mk: rawptr[AST] = _MakeStaticMarker(ps)
|
||
if st_mk is not None:
|
||
mods.append(st_mk)
|
||
mod_count += 1
|
||
while _IsDescriptorKeyword(ps) != 0 or _IsMagicAttributeCall(ps) != 0:
|
||
if mods is None:
|
||
# 元素大小显式传 8:list[T](pool, elem_size) 按字节步长寻址,传 4 会让
|
||
# 两个及以上描述符(如 `const volatile`)的第 2 个 8 字节指针越位写入。
|
||
mods = list[rawptr[AST]](ps.pool, 8)
|
||
if _IsMagicAttributeCall(ps) != 0:
|
||
# C 风格 `a: __attribute__(section(".text.wo"), aligned(16)) const int = ...`:
|
||
# 与 const / volatile 同位置、同顺序自由、可混写;每个实参 desugar 成
|
||
# `t.attr.<name>`(带调用则保留实参)后并入同一 markers 列表,由
|
||
# _PrependDescriptorMarkers 贴到类型联合最左(与 TPC 文本展开同形)。
|
||
ac_node: rawptr[AST] = _parse_test(ps)
|
||
if ac_node is None:
|
||
break
|
||
if ac_node.kind() != ast.ASTKind.Call:
|
||
break
|
||
ac: ast.Call* = ast.Call*(ac_node)
|
||
if ac.args is not None:
|
||
acn: sizet = len(ac.args)
|
||
aci: sizet = 0
|
||
while aci < acn:
|
||
mk: rawptr[AST] = _MakeTAttrMarker(ps, ac.args.get(aci))
|
||
if mk is not None:
|
||
mods.append(mk)
|
||
mod_count += 1
|
||
aci += 1
|
||
continue
|
||
mods.append(_MakeDescriptorMarker(ps))
|
||
mod_count += 1
|
||
_advance(ps)
|
||
# C 风格前置数组声明符 `[N]T` / `[]T`(含 `const [N]T`):就地脱糖为
|
||
# `t.CArray[T, N]` / `t.CArray[T, None]` 后直接返回。描述符此时已收集完毕,
|
||
# 就地贴到数组节点上(`t.CConst | t.CArray[T, N]`,与文档修法同形)。
|
||
# 该形态后不接指针后缀;全仓用法只有 `[N]T` / `[]T` 两种。
|
||
if _match_op(ps, TokOp.Lsqb) != 0:
|
||
arr: rawptr[AST] = _parse_array_prefix_declarator(ps)
|
||
if arr is None:
|
||
return None
|
||
if mod_count > 0:
|
||
arr = _PrependDescriptorMarkers(ps, arr, mods, mod_count)
|
||
return arr
|
||
if mod_count > 0 and _cur_type(ps) != TokenType.Name:
|
||
# 描述符后缺类型(`a: const` / `a: const = 1` / `def f(a: const)`):
|
||
# 响亮失败——**不**把描述符静默丢掉。
|
||
_RecordDescriptorErr("描述符后缺少类型", mod_lineno, mod_col)
|
||
ps.error_count += 1
|
||
return None
|
||
before_ec: int = ps.error_count
|
||
base: rawptr[AST] = _parse_test(ps)
|
||
if base is None:
|
||
if mod_count > 0:
|
||
# 描述符已消费但类型解析失败(如 `a: const = 1`):响亮失败保描述符报错。
|
||
_RecordDescriptorErr("描述符后缺少类型(类型解析失败)", mod_lineno, mod_col)
|
||
_record_parse_err(ps) # 注解解析失败:记录具体位置(error_count 已在 _parse_test 内增加)
|
||
return None
|
||
if mod_count > 0 and base.kind() == ASTKind.Constant:
|
||
# 后随非类型(`a: const 1` / `a: const "x"` / `a: const True`):响亮失败
|
||
# (描述符只修饰类型,常量不是类型——不得静默降级/丢弃)。
|
||
_RecordDescriptorErr("描述符后随非类型(描述符只修饰类型)", mod_lineno, mod_col)
|
||
ps.error_count += 1
|
||
return None
|
||
ptr_level: int = 0
|
||
# 回收垃圾节点(_parse_test 对后缀 * / ** 的消费产物)
|
||
if base.kind() == ASTKind.BinOp:
|
||
gb: rawptr[BinOp] = (rawptr[BinOp])(base)
|
||
if gb.op == OpKind.Mult and gb.right is None:
|
||
ptr_level += 1
|
||
base = gb.left
|
||
elif gb.op == OpKind.Pow:
|
||
if gb.right is None:
|
||
ptr_level += 2
|
||
base = gb.left
|
||
elif gb.right.kind() == ASTKind.UnaryOp:
|
||
gu: rawptr[UnaryOp] = (rawptr[UnaryOp])(gb.right)
|
||
if gu.op == OpKind.Deref and gu.operand is None:
|
||
ptr_level += 3
|
||
base = gb.left
|
||
if ptr_level > 0:
|
||
ps.error_count = before_ec # 回收成功,恢复错误计数避免整文件解析失败
|
||
# 循环消费残留后缀('**' 是词法单 token 计 2 层,'*' 计 1 层)
|
||
while _match_op(ps, TokOp.StarStar):
|
||
_advance(ps)
|
||
ptr_level += 2
|
||
while _match_op(ps, TokOp.Star):
|
||
_advance(ps)
|
||
ptr_level += 1
|
||
# 嵌套构造 rawptr[base](n 层)
|
||
li_an: int = 0
|
||
while li_an < ptr_level:
|
||
cptr_an: rawptr[AST] = Attribute(ps.pool,
|
||
Name(ps.pool, "t", ASTCtx.Load),
|
||
"CPtr", ASTCtx.Load)
|
||
base = BinOp(ps.pool, base, OpKind.BitOr, cptr_an)
|
||
li_an += 1
|
||
# 描述符最后粘贴:与 TPC 文本展开同形(见 _PrependDescriptorMarkers)
|
||
if mod_count > 0:
|
||
base = _PrependDescriptorMarkers(ps, base, mods, mod_count)
|
||
return base
|
||
|
||
|
||
def _parse_arg_def(ps: rawptr[Parser], is_lambda: int = 0) -> rawptr[AST]:
|
||
"""arg: TokenType.Name [':' annotation]"""
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
return None
|
||
name_str: str = _cur_str(ps)
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
annotation: rawptr[AST] = None
|
||
if is_lambda == 0:
|
||
if _accept_op(ps, TokOp.Colon):
|
||
annotation = _parse_type_ann(ps)
|
||
result: rawptr[AST] = Arg(ps.pool, name_str, annotation)
|
||
_set_pos(result, lineno, col, 0, 0)
|
||
return result
|
||
|
||
|
||
# ============================================================
|
||
# Statement parsing (Design: directly append to parent, do not return a list)
|
||
# ============================================================
|
||
|
||
def _parse_suite_into_children(ps: rawptr[Parser], parent: rawptr[AST],
|
||
owner_is_class: int = 0):
|
||
"""Parse statement suite, directly append to parent's children.
|
||
suite: TokenType.NewLine TokenType.Indent stmt+ TokenType.Dedent | simple_stmt
|
||
|
||
owner_is_class:本套件的**直接拥有者**是否 ClassDef。套件解析期间置
|
||
ps.in_class_body,供 _ParseVirtualModified 判 `virtual def`/`novirtual def`
|
||
是否"直接写在类体内"(函数体 / 分支体 / 循环体都不算)。默认 0 ⇒ 除类体外的
|
||
所有套件拥有者天然正确,无需逐个改动调用点。
|
||
"""
|
||
prev_in_class: int = ps.in_class_body
|
||
ps.in_class_body = owner_is_class
|
||
_skip_nl(ps)
|
||
if _cur_type(ps) == TokenType.NewLine:
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
if _cur_type(ps) != TokenType.Indent:
|
||
ps.error_count += 1
|
||
ps.in_class_body = prev_in_class
|
||
return
|
||
_advance(ps)
|
||
sidx: int = 0
|
||
while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker:
|
||
sidx += 1
|
||
# fail-fast 防卡死:步数超预算(token 链表成环 / 游标原地打转)立即报错返回
|
||
if ps.step_count > ps.step_limit:
|
||
_record_parse_err(ps)
|
||
return
|
||
# Decorator (class body methods can have decorators)
|
||
if _match_op(ps, TokOp.At):
|
||
ps.pending_decorators = _parse_decorators(ps)
|
||
before_cur: rawptr[Token] = ps.cur
|
||
_parse_statement_into(ps, parent)
|
||
# Defensive: if parse did not advance token, force advance to avoid infinite loop
|
||
if ps.cur is before_cur:
|
||
ps.error_count += 1
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
_expect_type(ps, TokenType.Dedent)
|
||
ps.in_class_body = prev_in_class
|
||
return
|
||
# Single-line suite
|
||
_parse_simple_stmt_into(ps, parent)
|
||
ps.in_class_body = prev_in_class
|
||
|
||
|
||
def _parse_statement_into(ps: rawptr[Parser], parent: rawptr[AST]):
|
||
"""Parse statement (compound or simple), append to the parent."""
|
||
ttype: int = _cur_type(ps)
|
||
kw: int = _cur_kw(ps)
|
||
if ttype == TokenType.Name:
|
||
# define 常量声明:`define NAME[: T] = <字面量>`(**无括号**)。
|
||
# 必须早于 _parse_simple_stmt_into 分派——否则 `define` 被当普通标识符
|
||
# 解析成表达式语句,随后的 NAME 沦为残留 token(虽响亮失败但定位错误)。
|
||
# `define NAME(...)`(宏函数)被 _is_define_const 的 lookahead 排除,
|
||
# 仍走下方 _is_funcdef_kw 分支,语义逐字不变。
|
||
if kw == Keyword.Define and _is_define_const(ps) != 0:
|
||
dc_stmt: rawptr[AST] = _parse_define_const(ps)
|
||
if dc_stmt is not None:
|
||
parent.append(dc_stmt)
|
||
return
|
||
# typedef 类型别名声明:`typedef T1 = T2`(语句级保留字,无 lookahead——
|
||
# typedef 不在任何既有源码中作标识符)。非法形态在 _parse_typedef 内
|
||
# 响亮失败(_record_parse_err),不静默。
|
||
if _is_typedef(ps) != 0:
|
||
td_stmt: rawptr[AST] = _parse_typedef(ps)
|
||
if td_stmt is not None:
|
||
parent.append(td_stmt)
|
||
return
|
||
# virtual / novirtual 修饰符(软关键字):只在"语句起始 + 紧跟 class/def"
|
||
# 时生效;合成既有装饰器 @t.CVTable / @t.NoVTable 后照常走 class/def 解析
|
||
# (见 _ParseVirtualModified)。放在 class/def 分派之前:修饰符必须先被消费,
|
||
# 后面的 class/def 才能落到 _is_funcdef_kw / Keyword.Class 分支。
|
||
# 与软关键字 fn→def 的关系:两种写法都接受(def 的别名 fn/fun/func/function),
|
||
# 与 TPC 端(先做 fn→def 文本展开、再判修饰符)结果一致。
|
||
if _IsVirtualModifier(ps) != 0:
|
||
_ParseVirtualModified(ps, parent)
|
||
return
|
||
# inline 修饰符(软关键字):只在"语句起始 + inline + 紧跟函数定义"时生效,
|
||
# 合成既有装饰器 @t.always_inline 后照常走 def 解析(见 _ParseInlineModified)。
|
||
# 与 virtual/novirtual 同一位置、同一套 pending_decorators 通道。
|
||
if _IsInlineModifier(ps) != 0:
|
||
_ParseInlineModified(ps, parent)
|
||
return
|
||
# static 修饰符(软关键字):语句起始 + static + (def | `Name :`)。
|
||
# 函数分支消费 static 并合成 @t.CStatic 装饰器(同 inline,已处理→return);
|
||
# 变量分支仅消费 static 并置 pending_static(返回 0),继续走下方通用分派
|
||
# ——AnnAssign 解析经 _parse_type_ann 把 t.CStatic 插到类型联合最左。
|
||
# 语义(禁止跨模块使用)由 TransPyV 侧消费;此处只归一两种书写形态。
|
||
if _IsStaticModifier(ps) != 0:
|
||
if _ParseStaticModified(ps, parent) != 0:
|
||
return
|
||
# 类型描述符裸用:整条语句只有 `const` / `volatile`(缺类型)⇒ 响亮失败。
|
||
# 只在"描述符串后直接换行/文件尾/分号"时命中(软关键字边界),故
|
||
# `const = 5`(作标识符的赋值)/ `t.attr.const()` 等写法不命中、语义不变。
|
||
# 记录自定义报文后**继续走通用 simple_stmt 分派**:既消费 token(不制造
|
||
# 死循环),也不覆盖首次错误(_record_parse_err 只记首次错误)。
|
||
if _IsBareDescriptor(ps) != 0:
|
||
_RecordDescriptorErr("裸用描述符(整条语句只有描述符,缺类型)",
|
||
_cur_lineno(ps), _cur_col(ps))
|
||
ps.error_count += 1
|
||
if kw == Keyword.If or kw == Keyword.While or kw == Keyword.For or kw == Keyword.Try:
|
||
stmt: rawptr[AST] = _parse_compound(ps)
|
||
if stmt is not None:
|
||
parent.append(stmt)
|
||
return
|
||
if kw == Keyword.With or _is_funcdef_kw(ps, kw) != 0 or kw == Keyword.Class \
|
||
or kw == Keyword.Enum or kw == Keyword.Union:
|
||
stmt = _parse_compound(ps)
|
||
if stmt is not None:
|
||
parent.append(stmt)
|
||
return
|
||
if (kw == Keyword.Match and _is_match_stmt(ps) != 0) or kw == Keyword.Async:
|
||
stmt = _parse_compound(ps)
|
||
if stmt is not None:
|
||
parent.append(stmt)
|
||
return
|
||
_parse_simple_stmt_into(ps, parent)
|
||
|
||
|
||
def _parse_simple_stmt_into(ps: rawptr[Parser], parent: rawptr[AST]):
|
||
"""simple_stmt: small_stmt (';' small_stmt)* [';'] TokenType.NewLine"""
|
||
first: rawptr[AST] = _parse_small_stmt(ps)
|
||
if first is not None:
|
||
parent.append(first)
|
||
while _accept_op(ps, TokOp.Semi):
|
||
if _cur_type(ps) == TokenType.NewLine or _cur_type(ps) == TokenType.EndMarker:
|
||
break
|
||
s: rawptr[AST] = _parse_small_stmt(ps)
|
||
if s is None: break
|
||
parent.append(s)
|
||
# 先跳过 Nl(括号内换行,如多行函数调用参数间的换行,属合法 token)
|
||
_skip_nl(ps)
|
||
# 语句结束检查:simple_stmt 必须以换行/文件尾结束。
|
||
# 残留 token(如相邻表达式 `func12344444 avg3(...)` 被静默拆成多条语句、
|
||
# `def` 缺失的关键字函数定义残留)是语法错误(fail-fast),禁止静默,
|
||
# 并仿 Python 报告错误位置(SyntaxError at line/col)。
|
||
if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker:
|
||
# 残留 token(如相邻表达式 `func12344444 avg3(...)` 被静默拆成多条语句、
|
||
# `def` 缺失的关键字函数定义残留)是语法错误(fail-fast),禁止静默。
|
||
# 报文**不再由解析器自拼单行**:位置与消息走解析错误通道,前/后 token
|
||
# 作为附注并入消息,由调用方 dump_parse_traceback 以 Python 风格统一输出
|
||
# (Traceback + File/行 + 源码行 + caret + SyntaxError)。
|
||
# 定位:从链表头找 cur,记录前一个 token 与下一个 token 的上下文
|
||
prev_tok: rawptr[Token] = None
|
||
walk_tok: rawptr[Token] = ps.tokens
|
||
while walk_tok is not None and walk_tok is not ps.cur:
|
||
prev_tok = walk_tok
|
||
walk_tok = walk_tok.next
|
||
pv_str: str = "(head)"
|
||
pv_line: int = 0
|
||
pv_col: int = 0
|
||
if prev_tok is not None:
|
||
pv_str = prev_tok.str_val
|
||
pv_line = prev_tok.lineno
|
||
pv_col = prev_tok.col_offset
|
||
nxt_tok: rawptr[Token] = ps.cur.next
|
||
nxt_str: str = "(none)"
|
||
nxt_line: int = 0
|
||
nxt_col: int = 0
|
||
if nxt_tok is not None:
|
||
nxt_str = nxt_tok.str_val
|
||
nxt_line = nxt_tok.lineno
|
||
nxt_col = nxt_tok.col_offset
|
||
_record_parse_err_ctx(ps, _cur_type(ps), pv_str, pv_line, pv_col,
|
||
nxt_str, nxt_line, nxt_col)
|
||
ps.error_count += 1
|
||
return
|
||
if _cur_type(ps) == TokenType.NewLine:
|
||
_advance(ps)
|
||
|
||
|
||
def _parse_small_stmt(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""small_stmt: various simple statements"""
|
||
kw: int = _cur_kw(ps)
|
||
if kw == Keyword.Pass:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
node: rawptr[AST] = Pass(ps.pool)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
if kw == Keyword.Break:
|
||
lineno = _cur_lineno(ps)
|
||
col = _cur_col(ps)
|
||
_advance(ps)
|
||
node = Break(ps.pool)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
if kw == Keyword.Continue:
|
||
lineno = _cur_lineno(ps)
|
||
col = _cur_col(ps)
|
||
_advance(ps)
|
||
node = Continue(ps.pool)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
if kw == Keyword.Return:
|
||
return _parse_return(ps)
|
||
if kw == Keyword.Raise:
|
||
return _parse_raise(ps)
|
||
if kw == Keyword.Global:
|
||
return _parse_global(ps)
|
||
if kw == Keyword.Nonlocal:
|
||
return _parse_nonlocal(ps)
|
||
if kw == Keyword.Import:
|
||
return _parse_import(ps)
|
||
if kw == Keyword.From:
|
||
return _parse_from_import(ps)
|
||
if kw == Keyword.Del:
|
||
return _parse_del(ps)
|
||
if kw == Keyword.Assert:
|
||
return _parse_assert(ps)
|
||
if kw == Keyword.Yield:
|
||
return _parse_expr_stmt(ps, _parse_yield(ps))
|
||
# Expression statement / assignment / augassign / annassign
|
||
return _parse_expr_stmt(ps, None)
|
||
|
||
|
||
def _maybe_tuple(ps: rawptr[Parser], first: rawptr[AST]) -> rawptr[AST]:
|
||
"""If there is a comma after first, collect the comma-separated expressions into a Tuple."""
|
||
if first is None: return None
|
||
if not _match_op(ps, TokOp.Comma):
|
||
return first
|
||
lineno: int = first.lineno
|
||
col: int = first.col_offset
|
||
elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
elts.append(first)
|
||
_accept_op(ps, TokOp.Comma)
|
||
while True:
|
||
tt: int = _cur_type(ps)
|
||
if tt == TokenType.NewLine or tt == TokenType.EndMarker:
|
||
break
|
||
if tt == TokenType.Op:
|
||
op: int = _cur_op(ps)
|
||
if op == TokOp.Equal or op == TokOp.Semi:
|
||
break
|
||
e: rawptr[AST] = _parse_expr(ps)
|
||
if e is None: break
|
||
elts.append(e)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
node: rawptr[AST] = Tuple(ps.pool, elts, ASTCtx.Load)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_testlist(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""Parse a comma-separated list of expressions, if there’s more than one, form a tuple."""
|
||
first: rawptr[AST] = _parse_expr(ps)
|
||
return _maybe_tuple(ps, first)
|
||
|
||
|
||
def _parse_expr_stmt(ps: rawptr[Parser], first: rawptr[AST]) -> rawptr[AST]:
|
||
"""Expression statement, handle assignment/augassign/annassign"""
|
||
if first is None:
|
||
first = _parse_testlist(ps)
|
||
else:
|
||
first = _maybe_tuple(ps, first)
|
||
if first is None:
|
||
return None
|
||
lineno: int = first.lineno
|
||
col: int = first.col_offset
|
||
# Assignment
|
||
if _match_op(ps, TokOp.Equal):
|
||
targets: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
targets.append(first)
|
||
_fix_store_ctx(first)
|
||
value: rawptr[AST] = None
|
||
accept_result: int = _accept_op(ps, TokOp.Equal)
|
||
while accept_result:
|
||
v: rawptr[AST] = _parse_testlist(ps)
|
||
if v is None: break
|
||
if value is not None:
|
||
targets.append(value)
|
||
_fix_store_ctx(value)
|
||
value = v
|
||
accept_result = _accept_op(ps, TokOp.Equal)
|
||
node: rawptr[AST] = Assign(ps.pool, targets, value)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
# augassign
|
||
if _cur_type(ps) == TokenType.Op:
|
||
op_id: int = _cur_op(ps)
|
||
aug_op: int = _augop_from_op(op_id)
|
||
if aug_op != OpKind.NoneOp:
|
||
_advance(ps)
|
||
value2: rawptr[AST] = _parse_testlist(ps)
|
||
_fix_store_ctx(first)
|
||
node2: rawptr[AST] = AugAssign(ps.pool, first, aug_op, value2)
|
||
_set_pos(node2, lineno, col, 0, 0)
|
||
return node2
|
||
# annassign: target ':' annotation [= value]
|
||
if _match_op(ps, TokOp.Colon):
|
||
_advance(ps)
|
||
annotation: rawptr[AST] = _parse_type_ann(ps)
|
||
value3: rawptr[AST] = None
|
||
if _accept_op(ps, TokOp.Equal):
|
||
value3 = _parse_testlist(ps)
|
||
if ps.pool is None:
|
||
return None
|
||
_fix_store_ctx(first)
|
||
if ps.pool is None:
|
||
return None
|
||
node3: rawptr[AST] = AnnAssign(ps.pool, first, annotation, value3, 1)
|
||
_set_pos(node3, lineno, col, 0, 0)
|
||
return node3
|
||
# Expression statement
|
||
node4: rawptr[AST] = Expr(ps.pool, first)
|
||
_set_pos(node4, lineno, col, 0, 0)
|
||
return node4
|
||
|
||
|
||
def _parse_return(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
val: rawptr[AST] = None
|
||
if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker and _cur_type(ps) != TokenType.Op:
|
||
val = _parse_testlist(ps)
|
||
elif _cur_type(ps) == TokenType.Op and _cur_op(ps) != TokOp.Semi:
|
||
val = _parse_testlist(ps)
|
||
node: rawptr[AST] = Return(ps.pool, val)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_raise(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
exc: rawptr[AST] = None
|
||
cause: rawptr[AST] = None
|
||
if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker:
|
||
exc = _parse_test(ps)
|
||
if _cur_kw(ps) == Keyword.From:
|
||
_advance(ps)
|
||
cause = _parse_test(ps)
|
||
node: rawptr[AST] = Raise(ps.pool, exc, cause)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_global(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
names: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
if _cur_type(ps) == TokenType.Name:
|
||
n: rawptr[AST] = Name(ps.pool, _cur_str(ps), ASTCtx.Store)
|
||
_set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0)
|
||
names.append(n)
|
||
_advance(ps)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
if _cur_type(ps) != TokenType.Name: break
|
||
n = Name(ps.pool, _cur_str(ps), ASTCtx.Store)
|
||
_set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0)
|
||
names.append(n)
|
||
_advance(ps)
|
||
node: rawptr[AST] = Global(ps.pool, names)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_nonlocal(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
names: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
if _cur_type(ps) == TokenType.Name:
|
||
n: rawptr[AST] = Name(ps.pool, _cur_str(ps), ASTCtx.Store)
|
||
_set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0)
|
||
names.append(n)
|
||
_advance(ps)
|
||
while _accept_op(ps, TokOp.Comma):
|
||
if _cur_type(ps) != TokenType.Name: break
|
||
n = Name(ps.pool, _cur_str(ps), ASTCtx.Store)
|
||
_set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0)
|
||
names.append(n)
|
||
_advance(ps)
|
||
node: rawptr[AST] = Nonlocal(ps.pool, names)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_import(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
names: rawptr[list[rawptr[AST]]] = _parse_alias_list(ps)
|
||
node: rawptr[AST] = Import(ps.pool, names)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_from_import(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
# Parse module (possibly with dots)
|
||
level: int = 0
|
||
while _match_op(ps, TokOp.Dot):
|
||
level += 1
|
||
_advance(ps)
|
||
while _match_op(ps, TokOp.Ellipsis):
|
||
level += 3
|
||
_advance(ps)
|
||
module_name: str = None
|
||
# The token type for keywords (like import) is also Name, so they must be excluded using kw_subtype,
|
||
# otherwise the `import` in `from . import` would be mistakenly treated as a module_name.
|
||
# Note: only `import` itself should be excluded. Module names can be keywords (like in `from .match import ...`,
|
||
# where match is a Viper match statement keyword), requiring NotKeyword would cause
|
||
# `from .match import` to fail parsing and the dependency scan would miss that module.
|
||
if _cur_type(ps) == TokenType.Name and _cur_kw(ps) != Keyword.Import:
|
||
module_name = _cur_str(ps)
|
||
_advance(ps)
|
||
while _match_op(ps, TokOp.Dot):
|
||
_advance(ps)
|
||
if _cur_type(ps) == TokenType.Name and _cur_kw(ps) == Keyword.NotKeyword:
|
||
next_name: str = _cur_str(ps)
|
||
mlen: int = string.strlen(module_name)
|
||
nlen: int = string.strlen(next_name)
|
||
new_mod: str = ps.pool.alloc(mlen + 1 + nlen + 1)
|
||
if new_mod is not None:
|
||
string.strcpy(new_mod, module_name)
|
||
new_mod[mlen] = '.'
|
||
string.strcpy(new_mod + mlen + 1, next_name)
|
||
module_name = new_mod
|
||
_advance(ps)
|
||
if _cur_kw(ps) != Keyword.Import:
|
||
ps.error_count += 1
|
||
return None
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
# import *
|
||
if _match_op(ps, TokOp.Star):
|
||
_advance(ps)
|
||
star_alias: rawptr[AST] = Alias(ps.pool, "*", None)
|
||
_set_pos(star_alias, lineno, col, 0, 0)
|
||
star_names: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
star_names.append(star_alias)
|
||
node: rawptr[AST] = ImportFrom(ps.pool, module_name, star_names, level)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
# ( names ) or names
|
||
if _match_op(ps, TokOp.LPar):
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
names: rawptr[list[rawptr[AST]]] = _parse_alias_list(ps)
|
||
_skip_nl(ps)
|
||
_expect_op(ps, TokOp.RPar)
|
||
else:
|
||
names = _parse_alias_list(ps)
|
||
node: rawptr[AST] = ImportFrom(ps.pool, module_name, names, level)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_alias_list(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]:
|
||
"""Parse alias list."""
|
||
names: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
while True:
|
||
if _cur_type(ps) != TokenType.Name:
|
||
break
|
||
name_str: str = _cur_str(ps)
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
# dotted name
|
||
while _match_op(ps, TokOp.Dot):
|
||
_advance(ps)
|
||
if _cur_type(ps) == TokenType.Name:
|
||
next_n: str = _cur_str(ps)
|
||
nmlen: int = string.strlen(name_str)
|
||
nxtlen: int = string.strlen(next_n)
|
||
new_name: str = ps.pool.alloc(nmlen + 1 + nxtlen + 1)
|
||
if new_name is not None:
|
||
string.strcpy(new_name, name_str)
|
||
new_name[nmlen] = '.'
|
||
string.strcpy(new_name + nmlen + 1, next_n)
|
||
name_str = new_name
|
||
_advance(ps)
|
||
asname: str = None
|
||
if _cur_kw(ps) == Keyword.As:
|
||
_advance(ps)
|
||
if _cur_type(ps) == TokenType.Name:
|
||
asname = _cur_str(ps)
|
||
_advance(ps)
|
||
a: rawptr[AST] = Alias(ps.pool, name_str, asname)
|
||
_set_pos(a, lineno, col, 0, 0)
|
||
names.append(a)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_skip_nl(ps)
|
||
return names
|
||
|
||
|
||
def _parse_del(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
targets: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
while True:
|
||
t_: rawptr[AST] = _parse_atom_expr(ps)
|
||
if t_ is None: break
|
||
_set_ctx(t_, ASTCtx.Del)
|
||
targets.append(t_)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
node: rawptr[AST] = Delete(ps.pool, targets)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_assert(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
test: rawptr[AST] = _parse_test(ps)
|
||
msg: rawptr[AST] = None
|
||
if _accept_op(ps, TokOp.Comma):
|
||
msg = _parse_test(ps)
|
||
node: rawptr[AST] = Assert(ps.pool, test, msg)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
# ============================================================
|
||
# Compound statements
|
||
# ============================================================
|
||
|
||
def _parse_compound(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
kw: int = _cur_kw(ps)
|
||
if kw == Keyword.If:
|
||
return _parse_if(ps)
|
||
if kw == Keyword.While:
|
||
return _parse_while(ps)
|
||
if kw == Keyword.For:
|
||
return _parse_for(ps, 0)
|
||
if kw == Keyword.Try:
|
||
return _parse_try(ps)
|
||
if kw == Keyword.With:
|
||
return _parse_with(ps, 0)
|
||
if _is_funcdef_kw(ps, kw) != 0:
|
||
return _parse_funcdef(ps, 0)
|
||
if kw == Keyword.Class:
|
||
return _parse_classdef(ps)
|
||
if kw == Keyword.Enum:
|
||
return _parse_enumdef(ps)
|
||
if kw == Keyword.Union:
|
||
return _parse_uniondef(ps)
|
||
if kw == Keyword.Async:
|
||
_advance(ps)
|
||
if _is_funcdef_kw(ps, _cur_kw(ps)) != 0:
|
||
return _parse_funcdef(ps, 1)
|
||
if _cur_kw(ps) == Keyword.For:
|
||
return _parse_for(ps, 1)
|
||
if _cur_kw(ps) == Keyword.With:
|
||
return _parse_with(ps, 1)
|
||
ps.error_count += 1
|
||
return None
|
||
if kw == Keyword.Match and _is_match_stmt(ps) != 0:
|
||
return _parse_match(ps)
|
||
ps.error_count += 1
|
||
return None
|
||
|
||
|
||
def _parse_if(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # if
|
||
test: rawptr[AST] = _parse_test(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
# First construct with empty orelse, then fill it in later
|
||
empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
node: rawptr[If] = If(ps.pool, test, empty)
|
||
_parse_suite_into_children(ps, node)
|
||
orelse: rawptr[list[rawptr[AST]]] = _parse_if_tail(ps)
|
||
node.orelse = orelse
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_if_tail(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]:
|
||
"""Parse elif/else chain and return orelse list.
|
||
|
||
elif: orelse = list containing single If node (nested)
|
||
else: orelse = statement list
|
||
"""
|
||
if _cur_kw(ps) == Keyword.Elif:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
elif_test: rawptr[AST] = _parse_test(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
elif_node: rawptr[If] = If(ps.pool, elif_test, empty)
|
||
_parse_suite_into_children(ps, elif_node)
|
||
elif_orelse: rawptr[list[rawptr[AST]]] = _parse_if_tail(ps)
|
||
elif_node.orelse = elif_orelse
|
||
_set_pos(elif_node, lineno, col, 0, 0)
|
||
orelse: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
orelse.append(elif_node)
|
||
return orelse
|
||
if _cur_kw(ps) == Keyword.Else:
|
||
_advance(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
scratch: rawptr[Module] = Module(ps.pool)
|
||
_parse_suite_into_children(ps, scratch)
|
||
return scratch.children
|
||
return list[rawptr[AST]](ps.pool, 8) # 空 list
|
||
|
||
|
||
# ============================================================
|
||
# Parsing Compound Statements (Continued)
|
||
# ============================================================
|
||
|
||
def _parse_while(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # while
|
||
test: rawptr[AST] = _parse_test(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
node: rawptr[While] = While(ps.pool, test, empty)
|
||
_parse_suite_into_children(ps, node)
|
||
if _cur_kw(ps) == Keyword.Else:
|
||
_advance(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
scratch: rawptr[Module] = Module(ps.pool)
|
||
_parse_suite_into_children(ps, scratch)
|
||
node.orelse = scratch.children
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_for(ps: rawptr[Parser], is_async: int) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # for
|
||
target: rawptr[AST] = _parse_target_list(ps)
|
||
if _cur_kw(ps) != Keyword.In:
|
||
ps.error_count += 1
|
||
return None
|
||
_advance(ps)
|
||
iter_: rawptr[AST] = _parse_or_test(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
node: rawptr[For] = For(ps.pool, target, iter_, empty, is_async)
|
||
_fix_store_ctx(target)
|
||
_parse_suite_into_children(ps, node)
|
||
if _cur_kw(ps) == Keyword.Else:
|
||
_advance(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
scratch: rawptr[Module] = Module(ps.pool)
|
||
_parse_suite_into_children(ps, scratch)
|
||
node.orelse = scratch.children
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_try(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # try
|
||
_expect_op(ps, TokOp.Colon)
|
||
empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
node: rawptr[Try] = Try(ps.pool, empty, empty, empty)
|
||
_parse_suite_into_children(ps, node)
|
||
# handlers (except)
|
||
handlers: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
while _cur_kw(ps) == Keyword.Except:
|
||
h: rawptr[AST] = _parse_except_handler(ps)
|
||
if h is not None:
|
||
handlers.append(h)
|
||
node.handlers = handlers
|
||
# else
|
||
if _cur_kw(ps) == Keyword.Else:
|
||
_advance(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
scratch: rawptr[Module] = Module(ps.pool)
|
||
_parse_suite_into_children(ps, scratch)
|
||
node.orelse = scratch.children
|
||
# finally
|
||
if _cur_kw(ps) == Keyword.Finally:
|
||
_advance(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
scratch2: rawptr[Module] = Module(ps.pool)
|
||
_parse_suite_into_children(ps, scratch2)
|
||
node.finalbody = scratch2.children
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_except_handler(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # except
|
||
type_: rawptr[AST] = None
|
||
name: str = None
|
||
if _cur_op(ps) != TokOp.Colon:
|
||
type_ = _parse_test(ps)
|
||
if _cur_kw(ps) == Keyword.As:
|
||
_advance(ps)
|
||
if _cur_type(ps) == TokenType.Name:
|
||
name = _cur_str(ps)
|
||
_advance(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
node: rawptr[AST] = ExceptHandler(ps.pool, type_, name)
|
||
_parse_suite_into_children(ps, node)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_with(ps: rawptr[Parser], is_async: int) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # with
|
||
items: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
# items
|
||
while True:
|
||
item: rawptr[AST] = _parse_with_item(ps)
|
||
if item is None: break
|
||
items.append(item)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
# Check if we can continue with a newline (with (...): form)
|
||
if _cur_type(ps) == TokenType.NewLine or _cur_type(ps) == TokenType.Nl:
|
||
break
|
||
_expect_op(ps, TokOp.Colon)
|
||
node: rawptr[With] = With(ps.pool, items, is_async)
|
||
_parse_suite_into_children(ps, node)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_with_item(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""with_item: test ['as' expr]"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
context_expr: rawptr[AST] = _parse_or_test(ps)
|
||
if context_expr is None: return None
|
||
optional_vars: rawptr[AST] = None
|
||
if _cur_kw(ps) == Keyword.As:
|
||
_advance(ps)
|
||
optional_vars = _parse_target(ps)
|
||
_fix_store_ctx(optional_vars)
|
||
node: rawptr[AST] = WithItem(ps.pool, context_expr, optional_vars)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
is_extern_fd: int = 0
|
||
is_export_fd: int = 0
|
||
is_define_fd: int = 0
|
||
if _cur_kw(ps) == Keyword.Extern:
|
||
# extern 声明:extern [def] A(...) [-> T] [": pass" 可选]
|
||
is_extern_fd = 1
|
||
_advance(ps) # extern
|
||
if _cur_kw(ps) == Keyword.Def:
|
||
_advance(ps)
|
||
elif _cur_kw(ps) == Keyword.Export:
|
||
# export 声明:export [def] A(...) [-> T] [": pass" 可选]
|
||
is_export_fd = 1
|
||
_advance(ps) # export
|
||
if _cur_kw(ps) == Keyword.Def:
|
||
_advance(ps)
|
||
elif _cur_kw(ps) == Keyword.Define:
|
||
# define 宏函数:语法与 def 完全一致,返回注解自动追加 t.CDefine
|
||
# 标记为宏函数(StubGen 保留原样 → C #define / alwaysinline 内联)
|
||
is_define_fd = 1
|
||
_advance(ps) # define
|
||
else:
|
||
_advance(ps) # def / fn / ...
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
return None
|
||
name: str = _cur_str(ps)
|
||
_advance(ps)
|
||
# PEP 695 泛型函数类型参数: def add[T](a: T, b: T) — 解析并存入 type_params。
|
||
# (与 class 解析器一致;之前缺失此处理导致 _expect_op(Colon) 失败、函数体被
|
||
# 误提升到模块级——b = Box(42) 被当作模块级全局注册,函数内赋值误报
|
||
# "需用 global 声明"。翻译时 type_params 映射到 int 兜底。)
|
||
type_params_fd: rawptr[list[str]] = None
|
||
if _accept_op(ps, TokOp.Lsqb):
|
||
type_params_fd = list[str](ps.pool, 8)
|
||
while not _match_op(ps, TokOp.Rsqb):
|
||
if _cur_type(ps) == TokenType.EndMarker:
|
||
break
|
||
if _cur_type(ps) == TokenType.Name:
|
||
tp_name_fd: str = _cur_str(ps)
|
||
_advance(ps)
|
||
# 跳过可选 bound/约束: [T: bound] 或 [T: (A, B)]
|
||
if _match_op(ps, TokOp.Colon):
|
||
_advance(ps)
|
||
_parse_test(ps)
|
||
type_params_fd.append(tp_name_fd)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_expect_op(ps, TokOp.Rsqb)
|
||
args: rawptr[AST] = None
|
||
if _accept_op(ps, TokOp.LPar):
|
||
args = _parse_varargslist(ps, 0)
|
||
_expect_op(ps, TokOp.RPar)
|
||
returns: rawptr[AST] = None
|
||
if _accept_op(ps, TokOp.RArrow):
|
||
returns = _parse_type_ann(ps)
|
||
if is_extern_fd:
|
||
# extern 声明:返回注解改写为 <原类型> | t.State,复用既有
|
||
# State/外部 C 函数识别路径(vpi 注解含 "State" → 定义端裸名 + 不生成函数体)
|
||
state_attr_fd: rawptr[AST] = Attribute(ps.pool,
|
||
Name(ps.pool, "t", ASTCtx.Load),
|
||
"State", ASTCtx.Load)
|
||
if returns != None:
|
||
returns = BinOp(ps.pool, returns, OpKind.BitOr, state_attr_fd)
|
||
else:
|
||
returns = state_attr_fd
|
||
elif is_export_fd:
|
||
# export 声明:返回注解改写为 <原类型> | t.CExport,复用既有
|
||
# CExport 识别路径(vpi 注解含 "CExport" → 定义端裸名 + 不加模块 SHA1 头)
|
||
export_attr_fd: rawptr[AST] = Attribute(ps.pool,
|
||
Name(ps.pool, "t", ASTCtx.Load),
|
||
"CExport", ASTCtx.Load)
|
||
if returns != None:
|
||
returns = BinOp(ps.pool, returns, OpKind.BitOr, export_attr_fd)
|
||
else:
|
||
returns = export_attr_fd
|
||
elif is_define_fd:
|
||
# define 宏函数:返回注解改写为 <原类型> | t.CDefine,复用既有
|
||
# CDefine 宏函数识别路径(StubGen 保留原样 → C #define / alwaysinline)
|
||
define_attr_fd: rawptr[AST] = Attribute(ps.pool,
|
||
Name(ps.pool, "t", ASTCtx.Load),
|
||
"CDefine", ASTCtx.Load)
|
||
if returns != None:
|
||
returns = BinOp(ps.pool, returns, OpKind.BitOr, define_attr_fd)
|
||
else:
|
||
returns = define_attr_fd
|
||
dec_list: rawptr[list[rawptr[AST]]] = ps.pending_decorators
|
||
ps.pending_decorators = None
|
||
node: rawptr[FunctionDef] = FunctionDef(ps.pool, name, args, dec_list, returns, is_async, type_params_fd)
|
||
if is_extern_fd:
|
||
node.flags = node.flags | FLAG_IS_EXTERN
|
||
if is_export_fd:
|
||
node.flags = node.flags | FLAG_IS_EXPORT
|
||
if is_define_fd:
|
||
node.flags = node.flags | FLAG_IS_DEFINE
|
||
if (is_extern_fd == 0 and is_export_fd == 0 and is_define_fd == 0) or _match_op(ps, TokOp.Colon):
|
||
_expect_op(ps, TokOp.Colon)
|
||
_parse_suite_into_children(ps, node)
|
||
else:
|
||
# extern/export 无 ': pass' 形式(extern A(...) -> int):body 为空,仅声明。
|
||
# 必须消费本行 NewLine:模块级/类体语句循环只 _skip_nl(Nl),
|
||
# 残留 NewLine 会被下一轮当表达式解析(_parse_atom → error_count++)
|
||
# → parse_tokens 返回 None(TPV 编译 extern 文件报 7 个扫描失败的根因)
|
||
if _cur_type(ps) == TokenType.NewLine:
|
||
_advance(ps)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_classdef(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # class
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
return None
|
||
name: str = _cur_str(ps)
|
||
_advance(ps)
|
||
# Parse type parameters [T] or [T, U] (PEP 695 generic class syntax)
|
||
type_params: rawptr[list[str]] = None
|
||
if _accept_op(ps, TokOp.Lsqb):
|
||
type_params = list[str](ps.pool, 8)
|
||
while not _match_op(ps, TokOp.Rsqb):
|
||
if _cur_type(ps) == TokenType.EndMarker: break
|
||
if _cur_type(ps) == TokenType.Name:
|
||
tp_name: str = _cur_str(ps)
|
||
_advance(ps)
|
||
# Skip optional bound/constraint: [T: bound] or [T: (A, B)]
|
||
if _match_op(ps, TokOp.Colon):
|
||
_advance(ps)
|
||
_parse_test(ps)
|
||
type_params.append(tp_name)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_expect_op(ps, TokOp.Rsqb)
|
||
bases: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
keywords: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
# bases / keywords
|
||
if _accept_op(ps, TokOp.LPar):
|
||
while not _match_op(ps, TokOp.RPar):
|
||
# 基类括号内跨行(class X(A,\n B):):逗号后换行在此跳过,
|
||
# 否则 cur=NL 非 Name/Comma → break → _expect_op(RPar) 报
|
||
# "意外的 token"(Generator.vp L34 多继承跨行根因)
|
||
_skip_nl(ps)
|
||
if _cur_type(ps) == TokenType.EndMarker: break
|
||
# base 或 keyword=value
|
||
if _cur_type(ps) == TokenType.Name:
|
||
# Parse as expression, if followed by '=', then as keyword=value
|
||
e: rawptr[AST] = _parse_or_test(ps)
|
||
if _match_op(ps, TokOp.Equal):
|
||
_advance(ps)
|
||
v: rawptr[AST] = _parse_test(ps)
|
||
kw_node: rawptr[AST] = KwNode(ps.pool, e, v)
|
||
keywords.append(kw_node)
|
||
else:
|
||
bases.append(e)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_skip_nl(ps) # 末基类后换行收尾(class X(A,\n B):)
|
||
_expect_op(ps, TokOp.RPar)
|
||
_expect_op(ps, TokOp.Colon)
|
||
dec_list: rawptr[list[rawptr[AST]]] = ps.pending_decorators
|
||
ps.pending_decorators = None
|
||
node: rawptr[ClassDef] = ClassDef(ps.pool, name, bases, keywords, dec_list, type_params)
|
||
# owner_is_class=1:类体期间 ps.in_class_body=1,供 virtual/novirtual 修饰符判
|
||
# "方法级修饰符是否直接写在类体内"(见 _ParseVirtualModified / _parse_suite_into_children)
|
||
_parse_suite_into_children(ps, node, 1)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
# ============================================================
|
||
# enum 语法糖(与 class X(t.CEnum): 完全等价)
|
||
#
|
||
# 规格:`enum Name:` 头行 + 成员行(`A` / `A: T` / `A = 0`)。
|
||
# 语义与手写 `class Name(t.CEnum):` **完全等价**——本函数合成的 ClassDef
|
||
# 与 _parse_classdef 的产物逐字段同形,故下游(ClassGenDef 分派 →
|
||
# ClassGenEnum._translate_enum_def → EnumTable 编译期常量)只有**同一条**
|
||
# 实现路径,不存在第二套枚举实现。
|
||
# ============================================================
|
||
|
||
def _enum_member_annassign(ps: rawptr[Parser], owner: rawptr[AST],
|
||
name_node: rawptr[AST],
|
||
lineno: int, col: int,
|
||
end_lineno: int, end_col: int) -> rawptr[AST]:
|
||
"""把单个成员名节点改写为 `Name: t.State` 的 AnnAssign(enum 成员统一形态)。
|
||
|
||
target=Name(id, Store)、annotation=Attribute(Name('t'), 'State')、
|
||
value=None、simple=1 —— 与手写 `class X(t.CEnum): A: t.State` 逐字段同形。
|
||
"""
|
||
ann: rawptr[AST] = Attribute(ps.pool,
|
||
Name(ps.pool, "t", ASTCtx.Load),
|
||
"State", ASTCtx.Load)
|
||
_set_pos(ann, lineno, col, end_lineno, end_col)
|
||
aa: rawptr[AST] = AnnAssign(ps.pool, name_node, ann, None, 1)
|
||
_fix_store_ctx(name_node)
|
||
_set_pos(aa, lineno, col, end_lineno, end_col)
|
||
aa.parent = owner # 与 AST.append 一致地维护父指针
|
||
return aa
|
||
|
||
|
||
def _convert_enum_bare_members(ps: rawptr[Parser], node: rawptr[ClassDef]):
|
||
"""把 enum 体里的裸名 / 逗号分组成员行就地改写为 `Name: t.State` 的 AnnAssign。
|
||
|
||
成员收集(ClassGenEnum._translate_enum_def 只认 Assign / AnnAssign 的 Name
|
||
目标;TPC 侧 HandlesClassDef._RegisterEnumMembers 同口径)**不认**裸名语句
|
||
(表达式语句 `Expr(Name)`)与逗号分组语句(表达式语句 `Expr(Tuple)`)——
|
||
两者此前都会被**静默漏掉**(成员凭空消失,直到访问处 NameError / 别处误报)。
|
||
|
||
两种成员**行**形态(与 TPC 侧 SymbolUtils._ExpandEnumBlocks 同口径):
|
||
- `A` → `A: t.State`(整条语句替换为一个 AnnAssign)
|
||
- `A, B, C` → `A: t.State` / `B: t.State` / `C: t.State`(三条 AnnAssign)
|
||
逗号组要求"全是简单标识符且 ≥2 个"(尾随逗号在 AST 上与正常组不可区分,按
|
||
正常组处理);带值逗号组 `A, B = 1`、带注解逗号组 `A, B: T`、单名尾随逗号
|
||
`A,` 与其余语句**不**在此改写——它们由注册处的"源码可见成员名数 ≠ 实际登记
|
||
成员数"fail-fast 拦住(ClassGenEnum._translate_enum_def),不再静默丢成员。
|
||
缩进/空行/注释的容忍由 _parse_suite_into_children 承担(本函数只看语句种类)。
|
||
"""
|
||
children: rawptr[list[rawptr[AST]]] = node.children
|
||
if children is None:
|
||
return
|
||
cn: sizet = len(children)
|
||
# 重建子表:逗号组一行 → 多条 AnnAssign。list 无 insert,故整体重建;赋值形态
|
||
# 与 AST.append(base.vp:358 `self.children = list[rawptr[AST]](...)`)逐字同形。
|
||
node.children = list[rawptr[AST]](ps.pool, 8)
|
||
out: rawptr[list[rawptr[AST]]] = node.children
|
||
i: sizet = 0
|
||
while i < cn:
|
||
stmt: rawptr[AST] = children.get(i)
|
||
handled: int = 0
|
||
if stmt is not None and stmt.kind() == ASTKind.Expr:
|
||
ex: rawptr[Expr] = (rawptr[Expr])(stmt)
|
||
if ex.value is not None and ex.value.kind() == ASTKind.Name:
|
||
out.append(_enum_member_annassign(ps, node, ex.value,
|
||
ex.lineno, ex.col_offset,
|
||
ex.end_lineno, ex.end_col_offset))
|
||
handled = 1
|
||
elif ex.value is not None and ex.value.kind() == ASTKind.Tuple:
|
||
tup: rawptr[Tuple] = (rawptr[Tuple])(ex.value)
|
||
if tup.elts is not None and len(tup.elts) >= 2:
|
||
all_names: int = 1
|
||
k: sizet = 0
|
||
while k < len(tup.elts):
|
||
el: rawptr[AST] = tup.elts.get(k)
|
||
if el is None or el.kind() != ASTKind.Name:
|
||
all_names = 0
|
||
break
|
||
k += 1
|
||
if all_names == 1:
|
||
k = 0
|
||
while k < len(tup.elts):
|
||
el2: rawptr[AST] = tup.elts.get(k)
|
||
out.append(_enum_member_annassign(ps, node, el2,
|
||
el2.lineno, el2.col_offset,
|
||
el2.end_lineno, el2.end_col_offset))
|
||
k += 1
|
||
handled = 1
|
||
if handled == 0 and stmt is not None:
|
||
out.append(stmt)
|
||
i += 1
|
||
|
||
|
||
def _parse_define_const(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""`define NAME[: T] = <字面量>` → 与 `NAME: [T |] t.CDefine = <字面量>`
|
||
逐字段同形的 AnnAssign(define 常量声明语法糖)。
|
||
|
||
- 与手写 `NAME: t.CDefine = 值` 落到**同一条** CDefine 注册路径
|
||
(AssignGen 的 is_cdefine_annotation 分支);不新增第二套常量实现。
|
||
- 带类型时 T 写在联合**前面**(`T | t.CDefine`):与 TPC 侧
|
||
`SymbolUtils._ExpandDefineConsts` 的展开产物同形,使"值必须能放进 T"
|
||
这条判据有据可依(TPV 的 is_cdefine_annotation 与顺序无关,
|
||
仅取"含 CDefine"这一事实)。
|
||
- 行号/列号取自 define 关键字所在位置,报错回显定位到真实源码行。
|
||
- 语句必须换行/文件尾结束(与 _parse_simple_stmt_into 同口径);
|
||
残留 token 属语法错误(_record_parse_err,fail-fast,不静默)。
|
||
"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # define
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
_record_parse_err(ps)
|
||
return None
|
||
name: str = _cur_str(ps)
|
||
_advance(ps)
|
||
explicit_ann: rawptr[AST] = None
|
||
if _accept_op(ps, TokOp.Colon):
|
||
explicit_ann = _parse_type_ann(ps)
|
||
if explicit_ann is None:
|
||
return None
|
||
if _expect_op(ps, TokOp.Equal) == 0:
|
||
return None
|
||
value: rawptr[AST] = _parse_test(ps)
|
||
if value is None:
|
||
ps.error_count += 1
|
||
_record_parse_err(ps)
|
||
return None
|
||
cdefine_marker: rawptr[AST] = Attribute(ps.pool,
|
||
Name(ps.pool, "t", ASTCtx.Load),
|
||
"CDefine", ASTCtx.Load)
|
||
annotation: rawptr[AST] = cdefine_marker
|
||
if explicit_ann is not None:
|
||
annotation = BinOp(ps.pool, explicit_ann, OpKind.BitOr, cdefine_marker)
|
||
target: rawptr[AST] = Name(ps.pool, name, ASTCtx.Store)
|
||
_set_pos(target, lineno, col, 0, 0)
|
||
node: rawptr[AST] = AnnAssign(ps.pool, target, annotation, value, 1)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
_skip_nl(ps)
|
||
if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker:
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
return None
|
||
if _cur_type(ps) == TokenType.NewLine:
|
||
_advance(ps)
|
||
return node
|
||
|
||
|
||
def _parse_typedef(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""`typedef T1 = T2` → 与 `T1: t.CTypedef = T2` 逐字段同形的 AnnAssign。
|
||
|
||
- 与手写 `T1: t.CTypedef = T2` 落到**同一条** typedef 注册/查找路径
|
||
(AssignGenMixin.translate_annassign 的 CTypedef 分支 → ScanCTypedefDeclarations
|
||
/ TypeGenCType.register_ctypedef);**不新增第二套 typedef 实现**。
|
||
- 注解为 `Attribute(Name('t'), 'CTypedef')`,与 TPC 侧展开产物
|
||
`SymbolUtils._ExpandTypedefs` 同形(`t.CTypedef`)。
|
||
- RHS 即"类型表达式"(写在值位,按 `_parse_test` 解析):裸名 `int32` /
|
||
`t.X` / 并集 `char | t.CPtr` / 泛型 `array[E, N]` / 链式 `A = B`。
|
||
- 行号/列号取自 typedef 关键字所在位置,报错回显定位到真实源码行。
|
||
- 非法形态(缺名字 / 缺 `=` / 缺 RHS)⇒ `_record_parse_err` **响亮失败**
|
||
(fail-fast,不静默);语句必须换行/文件尾结束(与 _parse_define_const 同口径)。
|
||
"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # typedef
|
||
if _cur_type(ps) != TokenType.Name:
|
||
# 缺别名(如 `typedef = 1` / `typedef : x`):响亮失败。
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
return None
|
||
name: str = _cur_str(ps)
|
||
_advance(ps)
|
||
if _expect_op(ps, TokOp.Equal) == 0:
|
||
# 缺 `=`(如 `typedef A`):响亮失败。
|
||
return None
|
||
value: rawptr[AST] = _parse_test(ps)
|
||
if value is None:
|
||
# 缺 RHS(如 `typedef A = `):响亮失败。
|
||
ps.error_count += 1
|
||
_record_parse_err(ps)
|
||
return None
|
||
ctypedef_marker: rawptr[AST] = Attribute(ps.pool,
|
||
Name(ps.pool, "t", ASTCtx.Load),
|
||
"CTypedef", ASTCtx.Load)
|
||
target: rawptr[AST] = Name(ps.pool, name, ASTCtx.Store)
|
||
_set_pos(target, lineno, col, 0, 0)
|
||
node: rawptr[AST] = AnnAssign(ps.pool, target, ctypedef_marker, value, 1)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
_skip_nl(ps)
|
||
if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker:
|
||
_record_parse_err(ps)
|
||
ps.error_count += 1
|
||
return None
|
||
if _cur_type(ps) == TokenType.NewLine:
|
||
_advance(ps)
|
||
return node
|
||
|
||
|
||
def _parse_enumdef(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""enum NAME: 语法糖 → 与 `class NAME(t.CEnum):` 逐字段同形的 ClassDef。
|
||
|
||
成员书写形态(与 TPC 侧 SymbolUtils._ExpandEnumBlocks 同口径):
|
||
- 裸名 `A` → `A: t.State`(默认编号 0..N,宽度默认 i32)
|
||
- 逗号组 `A, B` → 同行拆成多条 `A: t.State` / `B: t.State`(≥2 个简单标识符)
|
||
- 带注解 `A: T` → 原样(自定义具体类型,参与"最宽成员类型"宽度推导)
|
||
- 带值 `A = 0` → 原样(显式编号)
|
||
|
||
基类固定为 Attribute(Name('t'), 'CEnum'),与手写 `class X(t.CEnum):` 的
|
||
bases[0] 逐字段同形(同一 _parse_classdef 基类解析产物)。行号/列号原样继承
|
||
自各成员语句,故报错回显(如"枚举成员 'P' 不能是指针类型")定位到真实源码行。
|
||
"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # enum
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
return None
|
||
name: str = _cur_str(ps)
|
||
_advance(ps)
|
||
bases: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
bases.append(Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||
"CEnum", ASTCtx.Load))
|
||
keywords: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
dec_list: rawptr[list[rawptr[AST]]] = ps.pending_decorators
|
||
ps.pending_decorators = None
|
||
_expect_op(ps, TokOp.Colon)
|
||
node: rawptr[ClassDef] = ClassDef(ps.pool, name, bases, keywords, dec_list, None)
|
||
_parse_suite_into_children(ps, node)
|
||
_convert_enum_bare_members(ps, node)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_uniondef(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""union NAME: 语法糖 → 与 `class NAME(t.CUnion):` 逐字段同形的 ClassDef。
|
||
|
||
成员按普通类体(AnnAssign)解析:`d: double` / `u: uint64`。基类固定为
|
||
Attribute(Name('t'), 'CUnion'),与手写 `class X(t.CUnion):` 的 bases[0]
|
||
逐字段同形(同一 _parse_classdef 基类解析产物)——TPC 侧正是靠
|
||
`issubclass(cls, t.CUnion)` 判 union(HandlesClassDef._ClassifyBase),
|
||
故两侧落到同一条既有的 union 语义路径(成员偏移 0、尺寸取最宽成员)。
|
||
行号/列号继承自语句本身,报错回显定位到真实源码行。
|
||
|
||
与 `enum NAME:` 的差异:不做裸名成员转换(union 成员必须带注解/类型)。
|
||
"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # union
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
return None
|
||
name: str = _cur_str(ps)
|
||
_advance(ps)
|
||
bases: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
bases.append(Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||
"CUnion", ASTCtx.Load))
|
||
keywords: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
dec_list: rawptr[list[rawptr[AST]]] = ps.pending_decorators
|
||
ps.pending_decorators = None
|
||
_expect_op(ps, TokOp.Colon)
|
||
node: rawptr[ClassDef] = ClassDef(ps.pool, name, bases, keywords, dec_list, None)
|
||
_parse_suite_into_children(ps, node)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_match(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""match statement: match subject ':' NewLine Indent case+ Dedent"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # match
|
||
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)
|
||
# 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)
|
||
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_match_case(ps)
|
||
if case_node is not None:
|
||
cases.append(case_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
|
||
# ============================================================
|
||
|
||
def _make_name_ast(pool: rawptr[memhub.MemManager], node: rawptr[Name]) -> rawptr[AST]:
|
||
# Analysis: "Convert Name* to AST* (Solving the TransPyC Type Inference Problem)"
|
||
return node
|
||
|
||
|
||
def _parse_pattern(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""pattern: or_pattern ['as' NAME]
|
||
or_pattern: closed_pattern ('|' closed_pattern)*"""
|
||
pat: rawptr[AST] = _parse_closed_pattern(ps)
|
||
if pat is None: return None
|
||
# OR pattern
|
||
if _match_op(ps, TokOp.VBar):
|
||
patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
patterns.append(pat)
|
||
while _accept_op(ps, TokOp.VBar):
|
||
p: rawptr[AST] = _parse_closed_pattern(ps)
|
||
if p is not None:
|
||
patterns.append(p)
|
||
or_node: rawptr[AST] = MatchOr(ps.pool, patterns)
|
||
_set_pos(or_node, pat.lineno, pat.col_offset, 0, 0)
|
||
pat = or_node
|
||
# AS pattern
|
||
if _cur_kw(ps) == Keyword.As:
|
||
_advance(ps)
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
return pat
|
||
name_str: str = _cur_str(ps)
|
||
_advance(ps)
|
||
as_node: rawptr[AST] = MatchAs(ps.pool, pat, name_str)
|
||
_set_pos(as_node, pat.lineno, pat.col_offset, 0, 0)
|
||
return as_node
|
||
return pat
|
||
|
||
|
||
def _parse_closed_pattern(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""closed_pattern: literal | capture | wildcard | value | group | sequence | mapping | class"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
ttype: int = _cur_type(ps)
|
||
val: rawptr[AST]
|
||
node: rawptr[AST]
|
||
tok: rawptr[Token]
|
||
sval: str
|
||
|
||
# Number literal pattern
|
||
if ttype == TokenType.Number:
|
||
tok = ps.cur
|
||
_advance(ps)
|
||
if tok.is_float:
|
||
val = _new_const_float(ps.pool, tok.float_val, lineno, col)
|
||
else:
|
||
val = _new_const_int(ps.pool, tok.int_val, lineno, col)
|
||
node = MatchValue(ps.pool, val)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
# String literal pattern
|
||
if ttype == TokenType.String:
|
||
sval = _cur_str(ps)
|
||
is_dquote_m: int = ps.cur.kw_subtype & 16
|
||
_advance(ps)
|
||
val = _new_const_str(ps.pool, sval, lineno, col, is_dquote_m)
|
||
node = MatchValue(ps.pool, val)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
# Negative number literal pattern: -1, -1.5
|
||
if _match_op(ps, TokOp.Minus):
|
||
_advance(ps)
|
||
if _cur_type(ps) == TokenType.Number:
|
||
tok = ps.cur
|
||
# 一元减 + 整数字面量的联合越界判定(与 _parse_factor 同口径):
|
||
# 幅值 > 2^63 的负数字面量(如 -18446744073709551615、-9223372036854775809)
|
||
# 结果 < -2^63 ⇒ fail-fast,不静默回绕。
|
||
if tok.is_float == 0 and _lex_int_neg_exceeds_i64(_cur_str(ps)) != 0:
|
||
_record_neg_literal_overflow(_cur_str(ps), lineno, col)
|
||
ps.error_count += 1
|
||
return None
|
||
_advance(ps)
|
||
if tok.is_float:
|
||
val = _new_const_float(ps.pool, -tok.float_val, lineno, col)
|
||
else:
|
||
val = _new_const_int(ps.pool, -tok.int_val, lineno, col)
|
||
node = MatchValue(ps.pool, val)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
ps.error_count += 1
|
||
return None
|
||
|
||
# Name: None/True/False/_ /capture/value/class
|
||
if ttype == TokenType.Name:
|
||
kw: int = _cur_kw(ps)
|
||
# None/True/False → MatchSingleton
|
||
if kw == Keyword.None_ or kw == Keyword.True_ or kw == Keyword.False_:
|
||
_advance(ps)
|
||
if kw == Keyword.None_:
|
||
val = _new_const_none(ps.pool, lineno, col)
|
||
elif kw == Keyword.True_:
|
||
val = _new_const_bool(ps.pool, 1, lineno, col)
|
||
else:
|
||
val = _new_const_bool(ps.pool, 0, lineno, col)
|
||
node = MatchSingleton(ps.pool, val)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
name_str: str = _cur_str(ps)
|
||
# Wildcard pattern: _
|
||
if string.strcmp(name_str, "_") == 0:
|
||
_advance(ps)
|
||
node = MatchAs(ps.pool, None, None)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
_advance(ps)
|
||
# Check dotted name(value pattern 或 class pattern)
|
||
if _match_op(ps, TokOp.Dot):
|
||
name_node: rawptr[Name] = Name(ps.pool, name_str, ASTCtx.Load)
|
||
_set_pos(name_node, lineno, col, 0, 0)
|
||
base: rawptr[AST] = _make_name_ast(ps.pool, name_node)
|
||
while _match_op(ps, TokOp.Dot):
|
||
_advance(ps)
|
||
if _cur_type(ps) != TokenType.Name:
|
||
ps.error_count += 1
|
||
break
|
||
attr_name: str = _cur_str(ps)
|
||
attr_lineno: int = _cur_lineno(ps)
|
||
attr_col: int = _cur_col(ps)
|
||
_advance(ps)
|
||
base = Attribute(ps.pool, base, attr_name, ASTCtx.Load)
|
||
_set_pos(base, attr_lineno, attr_col, 0, 0)
|
||
# class pattern: dotted.name(...)
|
||
if _match_op(ps, TokOp.LPar):
|
||
return _parse_class_pattern(ps, base, lineno, col)
|
||
# value pattern: dotted.name
|
||
node = MatchValue(ps.pool, base)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
# class pattern: Name(...)
|
||
if _match_op(ps, TokOp.LPar):
|
||
cls_node: rawptr[Name] = Name(ps.pool, name_str, ASTCtx.Load)
|
||
_set_pos(cls_node, lineno, col, 0, 0)
|
||
cls: rawptr[AST] = _make_name_ast(ps.pool, cls_node)
|
||
return _parse_class_pattern(ps, cls, lineno, col)
|
||
# capture pattern: NAME
|
||
node = MatchAs(ps.pool, None, name_str)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
# group pattern / sequence pattern (tuple): (...)
|
||
if _match_op(ps, TokOp.LPar):
|
||
_advance(ps)
|
||
inner: rawptr[AST] = _parse_pattern(ps)
|
||
if _accept_op(ps, TokOp.Comma):
|
||
# sequence pattern (tuple)
|
||
patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
if inner is not None:
|
||
patterns.append(inner)
|
||
while not _match_op(ps, TokOp.RPar) and _cur_type(ps) != TokenType.EndMarker:
|
||
p: rawptr[AST] = _parse_pattern(ps)
|
||
if p is not None:
|
||
patterns.append(p)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_expect_op(ps, TokOp.RPar)
|
||
node = MatchSequence(ps.pool, patterns)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
_expect_op(ps, TokOp.RPar)
|
||
return inner # group pattern
|
||
|
||
# sequence pattern (list): [...]
|
||
if _match_op(ps, TokOp.Lsqb):
|
||
return _parse_sequence_pattern(ps, TokOp.Rsqb, lineno, col)
|
||
|
||
# mapping pattern: {...}
|
||
if _match_op(ps, TokOp.LBrace):
|
||
return _parse_mapping_pattern(ps, lineno, col)
|
||
|
||
ps.error_count += 1
|
||
return None
|
||
|
||
|
||
def _parse_sequence_pattern(ps: rawptr[Parser], close_op: int,
|
||
lineno: int, col: int) -> rawptr[AST]:
|
||
"""sequence_pattern: '[' [pattern_list] ']'"""
|
||
patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
_advance(ps) # [
|
||
while not _match_op(ps, close_op) and _cur_type(ps) != TokenType.EndMarker:
|
||
# *name (star pattern)
|
||
if _match_op(ps, TokOp.Star):
|
||
_advance(ps)
|
||
if _cur_type(ps) == TokenType.Name:
|
||
star_name: str = _cur_str(ps)
|
||
_advance(ps)
|
||
star_node: rawptr[AST]
|
||
if string.strcmp(star_name, "_") == 0:
|
||
star_node = MatchStar(ps.pool, None)
|
||
else:
|
||
star_node = MatchStar(ps.pool, star_name)
|
||
_set_pos(star_node, lineno, col, 0, 0)
|
||
patterns.append(star_node)
|
||
else:
|
||
p: rawptr[AST] = _parse_pattern(ps)
|
||
if p is not None:
|
||
patterns.append(p)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_expect_op(ps, close_op)
|
||
node: rawptr[AST] = MatchSequence(ps.pool, patterns)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_mapping_pattern(ps: rawptr[Parser],
|
||
lineno: int, col: int) -> rawptr[AST]:
|
||
"""mapping_pattern: '{' [items] '}'
|
||
items: key ':' pattern (',' key ':' pattern)* [',' '**' NAME] | '**' NAME"""
|
||
keys: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
rest: str = None
|
||
node: rawptr[AST]
|
||
key: rawptr[AST]
|
||
val_pat: rawptr[AST]
|
||
_advance(ps) # {
|
||
if _match_op(ps, TokOp.RBrace):
|
||
_advance(ps)
|
||
node = MatchMapping(ps.pool, keys, patterns, rest)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
while _cur_type(ps) != TokenType.EndMarker:
|
||
if _match_op(ps, TokOp.StarStar):
|
||
_advance(ps)
|
||
if _cur_type(ps) == TokenType.Name:
|
||
rest = _cur_str(ps)
|
||
_advance(ps)
|
||
break
|
||
key = _parse_expr(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
val_pat = _parse_pattern(ps)
|
||
if key is not None:
|
||
keys.append(key)
|
||
if val_pat is not None:
|
||
patterns.append(val_pat)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_expect_op(ps, TokOp.RBrace)
|
||
node = MatchMapping(ps.pool, keys, patterns, rest)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_class_pattern(ps: rawptr[Parser], cls: rawptr[AST],
|
||
lineno: int, col: int) -> rawptr[AST]:
|
||
"""class_pattern: cls '(' [pattern_list] ')'
|
||
pattern_list: positional_pattern (',' positional_pattern)*
|
||
[',' keyword_pattern]* | keyword_pattern (',' keyword_pattern)*
|
||
keyword_pattern: NAME '=' pattern"""
|
||
patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
kwd_attrs: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
kwd_patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
p: rawptr[AST]
|
||
_advance(ps) # (
|
||
if not _match_op(ps, TokOp.RPar):
|
||
while _cur_type(ps) != TokenType.EndMarker:
|
||
# keyword pattern: NAME '=' pattern
|
||
if _cur_type(ps) == TokenType.Name and ps.cur is not None:
|
||
nxt: rawptr[Token] = ps.cur.next
|
||
if nxt is not None and nxt.type == TokenType.Op and nxt.op_subtype == TokOp.Equal:
|
||
attr_name: str = _cur_str(ps)
|
||
_advance(ps) # NAME
|
||
_advance(ps) # =
|
||
kwd_pat: rawptr[AST] = _parse_pattern(ps)
|
||
attr_n: rawptr[Name] = Name(ps.pool, attr_name, ASTCtx.Load)
|
||
_set_pos(attr_n, lineno, col, 0, 0)
|
||
attr_node: rawptr[AST] = _make_name_ast(ps.pool, attr_n)
|
||
kwd_attrs.append(attr_node)
|
||
if kwd_pat is not None:
|
||
kwd_patterns.append(kwd_pat)
|
||
else:
|
||
p = _parse_pattern(ps)
|
||
if p is not None:
|
||
patterns.append(p)
|
||
else:
|
||
p = _parse_pattern(ps)
|
||
if p is not None:
|
||
patterns.append(p)
|
||
if not _accept_op(ps, TokOp.Comma):
|
||
break
|
||
_expect_op(ps, TokOp.RPar)
|
||
node: rawptr[AST] = MatchClass(ps.pool, cls, patterns, kwd_attrs, kwd_patterns)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
def _parse_match_case(ps: rawptr[Parser]) -> rawptr[AST]:
|
||
"""case pattern [if guard] ':' suite"""
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # case
|
||
guard: rawptr[AST] = None
|
||
pattern: rawptr[AST] = _parse_pattern(ps)
|
||
if _cur_kw(ps) == Keyword.If:
|
||
_advance(ps)
|
||
guard = _parse_or_test(ps)
|
||
_expect_op(ps, TokOp.Colon)
|
||
node: rawptr[AST] = MatchCase(ps.pool, pattern, guard)
|
||
_parse_suite_into_children(ps, node)
|
||
_set_pos(node, lineno, col, 0, 0)
|
||
return node
|
||
|
||
|
||
# ============================================================
|
||
# __attribute__ 装饰器 desugar(C 风格属性书写法)
|
||
#
|
||
# @__attribute__(section(".text.wo"), always_inline)
|
||
# def f(): ...
|
||
#
|
||
# 等价于手写 `@c.Attribute(t.attr.section(".text.wo"), t.attr.always_inline)`:
|
||
# 实参用**无前缀**属性名,逐个 desugar 为 `t.attr.<name>`(带调用时保留实参),
|
||
# 外层换成 `c.Attribute(...)`。产出的装饰器与手写形态逐字段同形 ⇒
|
||
# ClassGenBase._has_decorator / FuncGenBase.extract_func_attrs / _append_llvm_attr
|
||
# 原样识别(零新增判据);也即同时适用于 class 与 def(装饰器链共用本函数)。
|
||
# 容错:实参已是 `t.attr.X` / `c.Attribute(...)` 形态时原样保留(允许混写)。
|
||
# ============================================================
|
||
def _IsMagicAttributeCallee(node: ast.AST*) -> int:
|
||
"""节点是否为 `__attribute__`(Name 形态,或 X.__attribute__ 的尾段形态)"""
|
||
if node is None:
|
||
return 0
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(node)
|
||
if nm.id is not None and string.strcmp(nm.id, "__attribute__") == 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(node)
|
||
if at.attr is not None and string.strcmp(at.attr, "__attribute__") == 0:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def _MakeTAttrMarker(ps: rawptr[Parser], arg: ast.AST*) -> ast.AST*:
|
||
"""把一个 __attribute__ 实参 desugar 为 `t.attr.<name>`(带调用则保留实参)
|
||
|
||
- `always_inline`(Name) → `t.attr.always_inline`
|
||
- `section(".text.wo")`(Call)→ `t.attr.section(".text.wo")`
|
||
- 已含 `t.attr.X` 形态(value 也是 Attribute)→ 原样返回
|
||
"""
|
||
if arg is None:
|
||
return None
|
||
k: int = arg.kind()
|
||
if k == ast.ASTKind.Attribute:
|
||
at0: ast.Attribute* = ast.Attribute*(arg)
|
||
if at0.value is not None and at0.value.kind() == ast.ASTKind.Attribute:
|
||
return arg
|
||
return arg
|
||
attr_root: ast.AST* = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||
"attr", ASTCtx.Load)
|
||
if k == ast.ASTKind.Name:
|
||
nm2: ast.Name* = ast.Name*(arg)
|
||
if nm2.id is None:
|
||
return arg
|
||
mk: ast.AST* = Attribute(ps.pool, attr_root, nm2.id, ASTCtx.Load)
|
||
_set_pos(mk, nm2.lineno, nm2.col_offset, 0, 0)
|
||
return mk
|
||
if k == ast.ASTKind.Call:
|
||
cl2: ast.Call* = ast.Call*(arg)
|
||
if cl2.func is None or cl2.func.kind() != ast.ASTKind.Name:
|
||
return arg
|
||
fnm: ast.Name* = ast.Name*(cl2.func)
|
||
if fnm.id is None:
|
||
return arg
|
||
inner: ast.AST* = Attribute(ps.pool, attr_root, fnm.id, ASTCtx.Load)
|
||
_set_pos(inner, fnm.lineno, fnm.col_offset, 0, 0)
|
||
mk2: ast.AST* = Call(ps.pool, inner, cl2.args, cl2.keywords)
|
||
_set_pos(mk2, cl2.lineno, cl2.col_offset, 0, 0)
|
||
return mk2
|
||
return arg
|
||
|
||
|
||
def _DesugarMagicAttribute(ps: rawptr[Parser], expr: ast.AST*) -> ast.AST*:
|
||
"""`@__attribute__(...)` → `@c.Attribute(t.attr.x, ...)`;其它装饰器原样返回"""
|
||
if expr is None or expr.kind() != ast.ASTKind.Call:
|
||
return expr
|
||
cl: ast.Call* = ast.Call*(expr)
|
||
if _IsMagicAttributeCallee(cl.func) == 0:
|
||
return expr
|
||
c_func: ast.AST* = Attribute(ps.pool, Name(ps.pool, "c", ASTCtx.Load),
|
||
"Attribute", ASTCtx.Load)
|
||
new_args: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
if new_args is None:
|
||
return expr
|
||
if cl.args is not None:
|
||
an: sizet = len(cl.args)
|
||
ai: sizet = 0
|
||
while ai < an:
|
||
mk: ast.AST* = _MakeTAttrMarker(ps, cl.args.get(ai))
|
||
if mk is not None:
|
||
new_args.append(mk)
|
||
ai += 1
|
||
new_call: ast.AST* = Call(ps.pool, c_func, new_args, None)
|
||
_set_pos(new_call, cl.lineno, cl.col_offset, 0, 0)
|
||
return new_call
|
||
|
||
|
||
def _parse_decorators(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]:
|
||
"""解析装饰器链,返回 list。"""
|
||
decs: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||
while _match_op(ps, TokOp.At):
|
||
lineno: int = _cur_lineno(ps)
|
||
col: int = _cur_col(ps)
|
||
_advance(ps) # @
|
||
expr: rawptr[AST] = _parse_namedexpr(ps)
|
||
# C 风格 `@__attribute__(...)` desugar 成既有的 `@c.Attribute(t.attr.<x>...)`
|
||
expr = _DesugarMagicAttribute(ps, expr)
|
||
_skip_nl(ps)
|
||
if _cur_type(ps) == TokenType.NewLine:
|
||
_advance(ps)
|
||
decs.append(expr)
|
||
return decs
|
||
|
||
|
||
# ============================================================
|
||
# Module entry
|
||
# ============================================================
|
||
|
||
def _parse_module_body(ps: rawptr[Parser], module_node: rawptr[AST]):
|
||
"""解析模块体,append 到 module_node。"""
|
||
_skip_nl(ps)
|
||
stmt_idx: int = 0
|
||
while _cur_type(ps) != TokenType.EndMarker:
|
||
stmt_idx += 1
|
||
# fail-fast 防卡死:步数超预算(token 链表成环 / 游标原地打转)立即报错返回
|
||
if ps.step_count > ps.step_limit:
|
||
_record_parse_err(ps)
|
||
return
|
||
# Decorator
|
||
if _match_op(ps, TokOp.At):
|
||
ps.pending_decorators = _parse_decorators(ps)
|
||
# Record current token pointer to check for infinite loop
|
||
before_cur: rawptr[Token] = ps.cur
|
||
_parse_statement_into(ps, module_node)
|
||
# Defense: If parsing doesn't advance the token (like when _parse_atom encounters an unrecognized token and returns None),
|
||
# force it to move forward to avoid an infinite loop in the main loop; accumulate error_count for the caller to diagnose
|
||
if ps.cur is before_cur:
|
||
ps.error_count += 1
|
||
_advance(ps)
|
||
_skip_nl(ps)
|
||
|
||
|
||
def parse_tokens(pool: rawptr[memhub.MemManager],
|
||
tokens: rawptr[Token]) -> rawptr[AST]:
|
||
"""Parse an AST Module from a token list."""
|
||
if pool is None or tokens is None:
|
||
return None
|
||
global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg
|
||
_g_parse_err_line = 0
|
||
_g_parse_err_col = 0
|
||
_g_parse_err_msg = None
|
||
# 词法层 fail-fast 优先上报:tokenize 期间记录的溢出(如整数字面量 > 2^64-1)在此
|
||
# 转写到解析错误通道,复用 dump_parse_traceback 的 "文件:行:列 + 源码行 +
|
||
# SyntaxError: 消息" 报告路径(并终止编译)。lexer 无独立 fatal 出口,这是它
|
||
# 唯一的对外上报点。
|
||
lex_line: int = get_lex_err_line()
|
||
if lex_line != 0:
|
||
_g_parse_err_line = lex_line
|
||
_g_parse_err_col = get_lex_err_col()
|
||
_g_parse_err_msg = get_lex_err_msg()
|
||
return None
|
||
ps: rawptr[Parser] = new_parser(pool)
|
||
if ps is None: return None
|
||
_parser_init(ps, tokens, pool)
|
||
# fail-fast 防卡死:先统计 token 链表长度,顺带检测成环。
|
||
# 背景:lexer 的 token 链表若出现环(_emit 追加了同一个 Token 指针两次,或分配
|
||
# 别名把两个 token 落在同一地址),语句主循环永远等不到 EndMarker → 编译进程
|
||
# 静默卡死(无输出、无报错)。此处 walk 到尾得到 token 数作为步数预算;超过上限
|
||
# 仍未到尾即判为成环,立即报错返回(绝不进入无限解析)。
|
||
tok_count: sizet = 0
|
||
walk_t: rawptr[Token] = tokens
|
||
while walk_t is not None:
|
||
walk_t = walk_t.next
|
||
tok_count += 1
|
||
if tok_count > PARSER_MAX_TOKENS:
|
||
_g_parse_err_line = _cur_lineno(ps)
|
||
_g_parse_err_col = _cur_col(ps)
|
||
_g_parse_err_msg = "token 链表成环:lexer 输出的 token 链表无终止(_emit 重复追加同一 Token 或分配别名),已终止解析"
|
||
return None
|
||
# 预算:看门狗按"当前 token 查询"计数(合法解析每 token 的查询次数远小于 256,
|
||
# 不可能触顶),成环/原地打转必触发;它同时是 _advance 步数与语句循环的上界。
|
||
ps.step_limit = tok_count * 256 + 262144
|
||
module_node: rawptr[Module] = Module(pool)
|
||
_parse_module_body(ps, module_node)
|
||
# fail-fast: 解析出错(error_count > 0)时返回 None,由调用方(TPV 等)报错终止,
|
||
# 避免错误代码静默编译(如 `func12344444 avg3(...)` 缺 def 关键字的函数定义残留)。
|
||
if ps.error_count > 0: return None
|
||
return module_node
|