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:
1 parent
59378e6a2a
commit
305242aa8a
6 files changed
+195
-15
No files matched your search
@@ -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
@@ -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]:
|
||||
# 描述符串跨行:本层只支持同行(不增删换行的前提),响亮失败不静默丢
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user