feat(parse): typedef 右值纳入类型位,支持 const T / T*

X: t.CTypedef = const str / = const uint16* 现按类型位解析(含 typedef X = T 语法糖):

- 文本层:_ExpandTypeDescriptors 新增 _IsTypedefAnnotationRhs(= 右侧且语句含 CTypedef);

- 解析层:vast.Parser._TryParseAnnAssign 对 CTypedef 注解的右值走 _ParseTypeAnnotation,vast_c.c 同步并重建 _parser.pyd;

- 文本回退:TypeAnnotationResolver._NormalizeTypedefRhsText 把 const T / T* 归一为 | 联合;

- 值解析:LLVMTypeMapper.resolve_typedef_value_type 把 CConst/CVolatile 限定符标记视为无类型(_TYPEDEF_VALUE_QUALIFIERS),修 const str 被误解析成 CConst * 导致 LPCSTR 登记失败。
This commit is contained in:
TermiNexus committed 2026-10-05 20:36:29 +08:00
1 parent 59378e6a2a
commit 305242aa8a
6 files changed
+195 -15

No files matched your search

+29 -8
View File
@@ -26,6 +26,13 @@ from lib.includes import t as _t
from lib.includes.t import CTypeRegistry
# typedef 值位置的类型限定符标记:它们自身不是类型(名下有 const/volatile 语义,
# 由注解解析器累积),在 typedef 值里只修饰 `|` 另一侧。历史写法
# `t.CConst | char | t.CPtr` 恰好被 `char` 一侧掩盖;新语法 `const str`
# (= `t.CConst | str`)暴露了这一点,故显式判为"无类型信息"。
_TYPEDEF_VALUE_QUALIFIERS: frozenset[str] = frozenset({'CConst', 'CVolatile'})
class TypeNameUnresolvedError(NameError):
"""类型注解名无法解析(fail-fast,取代旧的"warn + 回退 i32")。
@@ -208,6 +215,12 @@ class LLVMTypeMapper:
# 直接判断属性名是否为 CPtr(消除对 CName=='*' 的依赖)
if node.attr == 'CPtr':
return 'ptr'
if node.attr in _TYPEDEF_VALUE_QUALIFIERS:
# 类型限定符标记(const/volatile)本身不是类型:它只修饰 `|` 另一侧。
# 旧写法 `t.CConst | char | t.CPtr` 靠 char 一侧掩盖了这一点;一旦写成
# `t.CConst | str`(`const str`),把 'CConst' 当基类型会得到 'CConst *',
# 于是别名登记失败、所有使用处报「裸名 … 无法解析为类型」。
return ''
CName: str | None = CTypeHelper.GetCName(node.attr)
if CName == 'CPtr':
return 'ptr'
@@ -217,15 +230,23 @@ class LLVMTypeMapper:
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
left_str: str = self.resolve_typedef_value_type(node.left)
right_str: str = self.resolve_typedef_value_type(node.right)
if right_str == 'ptr' or (right_str and right_str.endswith('*')):
base: str = left_str if left_str and left_str != 'ptr' else ''
return base + ' *' if base else 'void *'
if left_str == 'ptr' or (left_str and left_str.endswith('*')):
base: str = right_str if right_str and right_str != 'ptr' else ''
return base + ' *' if base else 'void *'
if right_str and right_str not in ('ptr', 'void', 'i32', 'i64'):
# 限定符侧解析为空串 ⇒ 另一侧就是完整类型(见 _TYPEDEF_VALUE_QUALIFIERS)。
# 只在两侧都有类型信息时才按"基类型 + 指针标记"合并。
if not left_str:
return 'void *' if right_str == 'ptr' else right_str
if not right_str:
return 'void *' if left_str == 'ptr' else left_str
if right_str == 'ptr' or right_str.endswith('*'):
if left_str != 'ptr':
return left_str + ' *'
return right_str if right_str.endswith('*') else 'void *'
if left_str == 'ptr' or left_str.endswith('*'):
if right_str != 'ptr':
return right_str + ' *'
return left_str if left_str.endswith('*') else 'void *'
if right_str not in ('ptr', 'void', 'i32', 'i64'):
return right_str
if left_str and left_str not in ('ptr', 'void', 'i32', 'i64'):
if left_str not in ('ptr', 'void', 'i32', 'i64'):
return left_str
return left_str if left_str else right_str
return ''
+54 -1
View File
@@ -449,6 +449,54 @@ def _StaticPositionSyntaxError(Lineno: int, Col: int, Snippet: str) -> SyntaxErr
)
_TYPEDEF_RHS_SCAN_LIMIT: int = 256
def _IsTypedefAnnotationRhs(Tokens: list, EqIdx: int) -> bool:
"""`EqIdx` 处的 `=` 是否是 `名字: <含 CTypedef 的注解> = <类型>` 的赋值号。
用途:把 `t.CTypedef` 注解的**右值**也纳入类型位置,使
`LPCSTR: t.CTypedef = const str` / `= const uint16*` 里的描述符被展开
(与注解位同一展开规则;右值的 `T*` 后缀由 vast 的 _ParseTypeAnnotation 处理)。
判据:从 `=` 反向扫描到**深度 0 的 `:`**(跳过注释/NL,遇换行/`;` 即视为非声明行),
再确认 `:` 与 `=` 之间出现 NAME `CTypedef`。故 `x: int = const`(值恰为标识符 const)、
`d = {"k": const}`、`f = lambda x: t.CTypedef` 等一律不受影响。
"""
if EqIdx < 0 or EqIdx >= len(Tokens):
return False
depth: int = 0
colon_idx: int = -1
j: int = EqIdx - 1
steps: int = 0
while j >= 0 and steps < _TYPEDEF_RHS_SCAN_LIMIT:
steps += 1
tok = Tokens[j]
if tok.type in (tokenize.NL, tokenize.COMMENT):
j -= 1
continue
if tok.type in (tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT) or tok.string == ';':
return False
if tok.type == tokenize.OP:
if tok.string in (')', ']', '}'):
depth += 1
elif tok.string in ('(', '[', '{'):
if depth == 0:
return False
depth -= 1
elif tok.string == ':' and depth == 0:
colon_idx = j
break
j -= 1
if colon_idx < 0:
return False
for k in range(colon_idx + 1, EqIdx):
tk = Tokens[k]
if tk.type == tokenize.NAME and tk.string == 'CTypedef':
return True
return False
def _InStaticOldAnnotationPosition(Tokens: list, Index: int) -> bool:
"""`Index` 处的 `static` 是否处于**旧**注解位置(`名字: static 类型` / `-> static 类型`)。
@@ -1103,7 +1151,8 @@ def _ExpandTypeDescriptors(Code: str) -> str:
"""把 `const T` / `volatile T`(含组合、任意顺序)展开为 `t.CConst | T` 等形态(行号不变)。
- 词法级切分(`tokenize`):注释/字符串内容天然不参与,不会被误改写。
- 只在"注解位置"(前一个 token 是 `:` 且再前一个是 Name,或前一个 token 是 `->`)
- 只在"注解位置"(前一个 token 是 `:` 且再前一个是 Name,或前一个 token 是 `->`,
或 `名字: t.CTypedef =` 的赋值号右侧,见 _IsTypedefAnnotationRhs)
识别描述符串;非类型位置原样保留为普通标识符(软关键字边界)。
- **`static` 不再属于本展开器**(已迁出注解位置):注解位置出现 `static`
(如 `memory_regions: static memory_region* = None`)一律**响亮失败**
@@ -1155,6 +1204,10 @@ def _ExpandTypeDescriptors(Code: str) -> str:
BeforePrevIdx: int = _PrevSignificantTokenIndex(Tokens, PrevIdx)
if BeforePrevIdx >= 0 and Tokens[BeforePrevIdx].type == tokenize.NAME:
InAnnotationPosition = True
elif Prev.string == '=' and _IsTypedefAnnotationRhs(Tokens, PrevIdx):
# `LPCSTR: t.CTypedef = const str`:typedef 的右值也是类型位置
# (与 TPV 的 _parse_type_ann 右值分支同口径)。
InAnnotationPosition = True
if InAnnotationPosition:
if Run[0].start[0] != Run[-1].end[0]:
# 描述符串跨行:本层只支持同行(不增删换行的前提),响亮失败不静默丢
+33 -2
View File
@@ -149,15 +149,46 @@ class TypeAnnotationResolver:
_TYPEDEF_SUGAR_RE: re.Pattern = re.compile(
r'^typedef\s+([A-Za-z_]\w*)\s*=(?!=)\s*(.+?)\s*$')
@staticmethod
def _NormalizeTypedefRhsText(text: str) -> str:
"""把 typedef 右值的 C 式写法归一成规范 `|` 联合文本(供**文本回退路径**使用)。
磁盘原文里 `X: t.CTypedef = const str` / `= const uint16*` 的右值带描述符与指针
后缀,而本路径(_parse_typedef_line → _resolve_builtins_type_str)按表达式文本解析,
故先归一:
- 前导 `const` / `volatile`(可组合) → `t.CConst` / `t.CVolatile` 前缀;
- 末尾 `*` / `**` 串 → 逐层 `| t.CPtr`。
不含上述形态的文本原样返回(幂等)。无算术语义风险:typedef 右值不含乘法。
"""
s: str = text.strip()
if not s:
return text
markers: list[str] = []
while True:
m: re.Match | None = re.match(r'^(const|volatile)\s+', s)
if m is None:
break
markers.append('t.CConst' if m.group(1) == 'const' else 't.CVolatile')
s = s[m.end():].lstrip()
ptrs: int = 0
while s.endswith('*'):
s = s[:-1].rstrip()
ptrs += 1
if not markers and ptrs == 0:
return text
parts: list[str] = markers + [s] + ['t.CPtr'] * ptrs
return ' | '.join(p for p in parts if p)
@staticmethod
def _parse_typedef_line(stripped: str) -> tuple[str, str] | None:
"""单行源码文本 → typedef 声明 (名字, 类型值);非 typedef 行返回 None
兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。
右值的 C 式写法(`const T` / `T*`)经 _NormalizeTypedefRhsText 归一为 `|` 联合。
"""
m: re.Match | None = TypeAnnotationResolver._TYPEDEF_SUGAR_RE.match(stripped)
if m is not None:
return m.group(1), m.group(2)
return m.group(1), TypeAnnotationResolver._NormalizeTypedefRhsText(m.group(2))
if ':' not in stripped or 't.CTypedef' not in stripped:
return None
name, _, rest = stripped.partition(':')
@@ -167,7 +198,7 @@ class TypeAnnotationResolver:
name = name.strip()
if not name or not value:
return None
return name, value
return name, TypeAnnotationResolver._NormalizeTypedefRhsText(value)
@staticmethod
def _builtins_include_dirs() -> list[str]:
+31 -2
View File
@@ -373,11 +373,34 @@ class Parser(ExprParserMixin):
self._SetStoreContext(expr)
return expr
@staticmethod
def _IsCTypedefAnnotation(annotation: N.expr | None) -> bool:
"""注解是否为 typedef 标记(裸 `CTypedef` / `t.CTypedef` / 含它的 `|` 联合)。
用于把 typedef 的右值按**类型位置**解析(`X: t.CTypedef = const uint16*`
里的 `*` 后缀由 _ParseTypeAnnotation 消费,与注解位同一规则;描述符
`const`/`volatile` 已由 SymbolUtils._ExpandTypeDescriptors 在文本层展开)。
仅按标记名判定,不依赖 import 别名表(与 TPV `_IsCTypedefAnnotation` 同口径)。
"""
if annotation is None:
return False
if isinstance(annotation, N.Attribute):
return getattr(annotation, 'attr', None) == 'CTypedef'
if isinstance(annotation, N.Name):
return getattr(annotation, 'id', None) == 'CTypedef'
if isinstance(annotation, N.BinOp):
return (Parser._IsCTypedefAnnotation(getattr(annotation, 'left', None))
or Parser._IsCTypedefAnnotation(getattr(annotation, 'right', None)))
return False
def _TryParseAnnAssign(self) -> N.AnnAssign | None:
"""尝试解析带注解赋值:x: T [= v] 或 x: T(目标为 Name/Attribute/Subscript)。
注意:仅当目标为简单 Name/Attribute/Subscript 且后随 ':' 时匹配;
元组/列表解包不做注解(返回 None 走普通路径)。
`t.CTypedef` 注解的右值是**类型**而非一般表达式(`const str` / `uint16*`),
故走 `_ParseTypeAnnotation`(与注解位同一路径,支持 C 式指针后缀)。
"""
start: Token = self._Peek()
# 名称
@@ -388,7 +411,10 @@ class Parser(ExprParserMixin):
value: N.expr | None = None
if self._AtOp('='):
self._Next()
value = self.ParseExpr()
if self._IsCTypedefAnnotation(annotation):
value = self._ParseTypeAnnotation()
else:
value = self.ParseExpr()
target: N.Name = N.Name(id=name_tok.Value, ctx=N.Store())
self._FixLoc(target, name_tok)
node: N.AnnAssign = N.AnnAssign(target=target, annotation=annotation,
@@ -409,7 +435,10 @@ class Parser(ExprParserMixin):
value = None
if self._AtOp('='):
self._Next()
value = self.ParseExpr()
if self._IsCTypedefAnnotation(annotation):
value = self._ParseTypeAnnotation()
else:
value = self.ParseExpr()
self._SetStoreContext(expr)
simple: int = 1 if isinstance(expr, N.Name) else 0
node = N.AnnAssign(target=expr, annotation=annotation, value=value, simple=simple)
Binary file not shown.
+48 -2
View File
@@ -4651,6 +4651,50 @@ static PyObject *ParseSimpleStmt(Parser *P) {
return Node;
}
/* 注解是否为 typedef 标记(裸 CTypedef / t.CTypedef / 含它的 `|` 联合)。
* 用于把 typedef 的右值按类型位置解析(`X: t.CTypedef = const uint16*` 的 `*` 后缀
* 由 ParseTypeAnnotation 消费),与 Python vast Parser._IsCTypedefAnnotation 同口径。 */
static int IsCTypedefAnnotation(PyObject *Ann) {
if (Ann == NULL || Ann == Py_None) {
return 0;
}
if (NodeIs(Ann, "Name") || NodeIs(Ann, "Attribute")) {
const char *Field = NodeIs(Ann, "Name") ? "id" : "attr";
PyObject *V = PyObject_GetAttrString(Ann, Field);
int Hit = 0;
if (V != NULL) {
if (PyUnicode_Check(V) && PyUnicode_CompareWithASCIIString(V, "CTypedef") == 0) {
Hit = 1;
}
Py_DECREF(V);
} else {
PyErr_Clear();
}
return Hit;
}
if (NodeIs(Ann, "BinOp")) {
int Hit = 0;
PyObject *L = PyObject_GetAttrString(Ann, "left");
if (L != NULL) {
Hit = IsCTypedefAnnotation(L);
Py_DECREF(L);
} else {
PyErr_Clear();
}
if (!Hit) {
PyObject *R = PyObject_GetAttrString(Ann, "right");
if (R != NULL) {
Hit = IsCTypedefAnnotation(R);
Py_DECREF(R);
} else {
PyErr_Clear();
}
}
return Hit;
}
return 0;
}
/* 尝试解析带注解赋值(对齐 vast _TryParseAnnAssign) */
static PyObject *TryParseAnnAssign(Parser *P) {
Token *Start = PeekTok(P, 0);
@@ -4666,7 +4710,8 @@ static PyObject *TryParseAnnAssign(Parser *P) {
PyObject *Value = NULL;
if (AtOp(P, "=")) {
NextTok(P);
Value = ParseExpr(P);
/* A typedef's RHS is a type (`const str` / `uint16*`), not a general expression. */
Value = IsCTypedefAnnotation(Annotation) ? ParseTypeAnnotation(P) : ParseExpr(P);
if (Value == NULL) {
Py_DECREF(Annotation);
return NULL;
@@ -4712,7 +4757,8 @@ static PyObject *TryParseAnnAssign(Parser *P) {
PyObject *Value = NULL;
if (AtOp(P, "=")) {
NextTok(P);
Value = ParseExpr(P);
/* A typedef's RHS is a type (`const str` / `uint16*`). */
Value = IsCTypedefAnnotation(Annotation) ? ParseTypeAnnotation(P) : ParseExpr(P);
if (Value == NULL) {
Py_DECREF(Annotation);
Py_DECREF(Expr);