feat(lang): 同步 cif/celif/celse、值引用注解与 assert 改译 testcheck.check

vast 双路径(Python Parser/ExprParser 与 C 扩展 vast_c.c)新增 cif-celif-celse 语句族与 &T / &temp T 参数注解解析,_parser.pyd 已同步重编。HandlesIf 改认 @CIf 标记并做编译期常量求值,含运行时变量一律 fail-fast,同时删除 c.CIf 家族(c.py)。HandlesAssert 合成 testcheck.check(cond, None, msg) 调用,非字符串字面量消息报 TypeError。FuncGen 调用点按形参模式自动取址(ref 仅接受左值,右值 fail-fast;temp 物化临时栈),SymbolUtils/TypeAnnotationResolver 提供模式识别;t.py 删除 CNeedPtr。
This commit is contained in:
TermiNexus committed 2026-10-07 19:03:29 +08:00
1 parent 64f3d79b64
commit 2514cee4c9
24 files changed
+1139 -246

No files matched your search

+5
View File
@@ -1274,6 +1274,11 @@ class DeclarationGenerator:
return f'{sname}*'
elif isinstance(annotation, ast.Attribute):
attr_name: str = annotation.attr if hasattr(annotation, 'attr') else ''
# 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`,非真实类):按 t.CPtr
# 同形的「一层指针 = i8*」参与联合合并(`T | t.CRef` → T*),保证跨模块
# .vpi 声明端类型与定义端 _ctype_to_llvm 一致。
if attr_name in ('CRef', 'CTempRef'):
return 'i8*'
module_name: str = ''
if hasattr(annotation, 'value'):
if isinstance(annotation.value, ast.Name):
+16
View File
@@ -152,6 +152,22 @@ class Phase1Generator:
deps: set[str] = get_file_dependencies(src_path, self.src_root)
imported_from_src.update(deps)
# 检测 assert 使用,自动添加 testcheck 依赖:assert 译为
# testcheck.check(...),该 include 须纳入 .vpi/.stub 生成与后续编译链接,
# 否则断言所在模块解析不到 testcheck.check(跨模块符号缺失)。
# 独立成一次 walk:下面的 dict/list 检测循环带 break,会漏掉其后的 assert。
for src_path in reachable_source_files:
try:
_, tree = parse_python_file(src_path)
if tree is None:
continue
for node in ast.walk(tree):
if isinstance(node, ast.Assert):
imported_from_src.add('testcheck')
break
except Exception:
pass
# 检测 dict/list 容器使用,自动添加 _dict/_list/json 依赖
for src_path in reachable_source_files:
try:
+21 -3
View File
@@ -1547,6 +1547,10 @@ class Phase2Translator:
elif isinstance(node, ast.ImportFrom):
if node.module:
self.used_includes.add(node.module.split('.')[0])
# 检测 assert 使用,自动添加 testcheck 到 used_includes:
# assert 译为 testcheck.check(...),该库须被发现、编译并链接。
if isinstance(node, ast.Assert):
self.used_includes.add('testcheck')
# 检测 list/dict 容器使用,自动添加 _list/_dict/json 到 used_includes
# 以便 _scan_include_libraries 能发现并编译这些库文件
if isinstance(node, ast.Subscript) and isinstance(node.value, ast.Name):
@@ -2144,11 +2148,14 @@ class Phase2Translator:
def _inject_auto_imports(self, code: str, src_path: str) -> str:
"""检测源码中对 list/dict 容器的使用,自动注入 import _list / import _dict。
当使用 dict 时,同时注入 import json (用于 dict 的 JSON 处理功能)。
检测到 assert 语句时自动注入 import testcheck(assert 译为
testcheck.check(...),需该模块的跨模块符号声明)。
检测模式:
- list[T] / dict[V] 作为类型注解 (ast.Subscript, value=Name(id='list'/'dict'))
- list[T](...) / dict[V](...) 作为构造函数调用 (ast.Call, func=Subscript)
- list(...) / dict(...) 作为构造函数调用 (ast.Call, func=Name)
- assert 语句 (ast.Assert)
"""
try:
# `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析:
@@ -2158,10 +2165,11 @@ class Phase2Translator:
except SyntaxError:
return code
# 检查是否已经导入了 _list / _dict / json
# 检查是否已经导入了 _list / _dict / json / testcheck
has_list_import: bool = False
has_dict_import: bool = False
has_json_import: bool = False
has_testcheck_import: bool = False
for node in ast.iter_child_nodes(tree):
if isinstance(node, ast.Import):
for alias in node.names:
@@ -2171,6 +2179,8 @@ class Phase2Translator:
has_dict_import = True
elif alias.name == 'json':
has_json_import = True
elif alias.name == 'testcheck':
has_testcheck_import = True
elif isinstance(node, ast.ImportFrom):
if node.module == '_list':
has_list_import = True
@@ -2178,13 +2188,18 @@ class Phase2Translator:
has_dict_import = True
elif node.module == 'json':
has_json_import = True
if has_list_import and has_dict_import and has_json_import:
elif node.module == 'testcheck':
has_testcheck_import = True
if has_list_import and has_dict_import and has_json_import and has_testcheck_import:
break
# 检查是否使用了 list / dict 容器
# 检查是否使用了 list / dict 容器,以及是否含 assert 语句
uses_list: bool = False
uses_dict: bool = False
uses_assert: bool = False
for node in ast.walk(tree):
if isinstance(node, ast.Assert):
uses_assert = True
if isinstance(node, ast.Subscript):
if isinstance(node.value, ast.Name):
if node.value.id == 'list':
@@ -2211,6 +2226,9 @@ class Phase2Translator:
prefix += 'import _dict\n'
if uses_dict and not has_json_import:
prefix += 'import json\n'
# 含 assert 的模块等价于在文件顶部写下 import testcheck(已导入则不重复)
if uses_assert and not has_testcheck_import:
prefix += 'import testcheck\n'
if prefix:
return prefix + code
return code
+126 -60
View File
@@ -220,14 +220,14 @@ class PythonToStubConverter:
StubLines.append('')
StubLines.append(ImportStr)
elif isinstance(node, ast.Expr):
# 处理模块级别的宏调用,如 c.CIf(...), c.CEndif()
# 处理模块级别的宏调用,如 c.CEndif()
ExprStr: str = PythonToStubConverter._GetExprString(node.value)
if ExprStr:
CurrentType = 'macro'
StubLines.append(ExprStr)
StubLines.append('') # 宏后空行
elif isinstance(node, ast.If):
# 处理模块级别的 if 宏条件,如 if c.CIf(FF_MULTI_PARTITION):
# 处理模块级别的 cif 宏条件,如 `cif FF_MULTI_PARTITION:`
IfStr: str = PythonToStubConverter._GetModuleIfMacroString(node)
if IfStr:
CurrentType = 'macro'
@@ -440,12 +440,12 @@ class PythonToStubConverter:
FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent+1, SourceLines=SourceLines, class_name=node.name)
lines.extend(FuncStub)
elif isinstance(item, ast.Expr):
# 处理宏调用,如 c.CIf(...), c.CEndif(), c.CElif(), c.CElse()
# 处理宏调用,如 c.CEndif()
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
if ExprStr:
lines.append(f'{IndentStr} {ExprStr}')
elif isinstance(item, ast.If):
# 处理 if c.CIf(...): 形式的宏条件
# 处理 cif 宏条件(`cif ...:` / `celif ...:` / `celse:`)
IfStr: str = PythonToStubConverter._get_if_macro_string(item, indent=indent+1)
if IfStr:
lines.append(IfStr)
@@ -710,6 +710,21 @@ class PythonToStubConverter:
left: ast.AST = annotation.left
right: ast.AST = annotation.right
# 值引用参数标记:联合形态 `T | t.CRef`(ref)/ `T | t.CTempRef`(temp)
# 还原为源码写法 `&T` / `&temp T`,使跨模块 .vpi 往返保留取址模式。
if isinstance(annotation.op, ast.BitOr):
_ref_kind: str | None = None
_type_side: ast.AST | None = None
if isinstance(right, ast.Attribute) and right.attr in ('CRef', 'CTempRef'):
_ref_kind = right.attr
_type_side = left
elif isinstance(left, ast.Attribute) and left.attr in ('CRef', 'CTempRef'):
_ref_kind = left.attr
_type_side = right
if _ref_kind is not None and _type_side is not None:
_inner: str = PythonToStubConverter._GetTypeString(_type_side)
return f'&temp {_inner}' if _ref_kind == 'CTempRef' else f'&{_inner}'
# 检查右侧是否是 __set_default__ 调用
if isinstance(right, ast.Call) and isinstance(right.func, ast.Attribute) and right.func.attr in ('__set_default__', '__set_default'):
# 去除 __set_default__,保留左侧,右侧替换为函数名部分
@@ -973,81 +988,132 @@ class PythonToStubConverter:
def _GetExprString(expr: ast.AST) -> str:
"""获取表达式字符串(用于宏调用)"""
if isinstance(expr, ast.Call):
# 处理函数调用,如 c.CIf(...), c.CEndif(), c.CUndef(...), c.CPragma(...)
# 处理函数调用,如 c.CEndif(), c.CUndef(...), c.CPragma(...)
# (旧的 c.CIf/c.CElif/c.CElse 已删除:cif/celif/celse 由 _ProcessIfMacro 渲染)
FuncStr: str = PythonToStubConverter._GetDecoratorString(expr.func)
if FuncStr and ('CIf' in FuncStr or 'CEndif' in FuncStr or 'CElif' in FuncStr or 'CElse' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr):
if FuncStr and ('CEndif' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr):
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in expr.args])
return f'{FuncStr}({ArgsStr})' if ArgsStr else f'{FuncStr}()'
return ''
@staticmethod
def _RestoreDefinedMarkers(node: ast.AST) -> ast.AST:
"""把内部标记 c.CIfdef(N) 递归还原为 defined(N) 调用(供 stub 文本渲染)。
cif/celif 条件的 desugar 会把 defined(NAME) 变成 c.@CIfdef(NAME) 标记调用;
生成 .vpi 文本时必须还原为用户可写的 defined(NAME),否则存根不可再解析。
"""
if isinstance(node, ast.Call):
F: str = PythonToStubConverter._GetDecoratorString(node.func)
short: str = F.rsplit('.', 1)[-1] if F else ''
if short in ('@CIfdef', '@CIfndef') and len(node.args) == 1:
inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(node.args[0])
call: ast.Call = ast.Call(
func=ast.Name(id='defined', ctx=ast.Load()),
args=[inner], keywords=[])
if short == '@CIfndef':
return ast.UnaryOp(op=ast.Not(), operand=call)
return call
for field, value in ast.iter_fields(node):
if isinstance(value, list):
for i, item in enumerate(value):
if isinstance(item, ast.AST):
value[i] = PythonToStubConverter._RestoreDefinedMarkers(item)
elif isinstance(value, ast.AST):
setattr(node, field, PythonToStubConverter._RestoreDefinedMarkers(value))
return node
@staticmethod
def _CifConditionText(test: ast.AST) -> str | None:
"""cif/celif 条件文本:内部标记 c.@CIf(EXPR) → EXPR;c.@CIfdef(N) → defined(N)。
返回 None 表示 test 不是 cif 族标记(普通 if 的运行时条件)。
"""
if isinstance(test, ast.Call):
F: str = PythonToStubConverter._GetDecoratorString(test.func)
short: str = F.rsplit('.', 1)[-1] if F else ''
if short == '@CIf' and len(test.args) == 1:
inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(test.args[0])
return PythonToStubConverter._GetTypeString(inner)
if short in ('@CIfdef', '@CIfndef') and len(test.args) == 1:
return PythonToStubConverter._GetTypeString(
PythonToStubConverter._RestoreDefinedMarkers(test))
return None
@staticmethod
def _get_if_macro_string(node: ast.If, indent: int = 1) -> str:
"""获取类内部的 if 宏字符串(如 if c.CIf(...):)"""
"""获取类内部的 cif 宏字符串(如 `cif <cond>:`,含 celif/celse 链)。"""
IndentStr: str = ' ' * indent
InnerIndent: str = ' ' * (indent + 1)
# 检查条件是否是宏调用
if isinstance(node.test, ast.Call):
FuncStr: str = PythonToStubConverter._GetDecoratorString(node.test.func)
if FuncStr and 'CIf' in FuncStr:
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in node.test.args])
result: list[str] = [f'{IndentStr}if {FuncStr}({ArgsStr}):' if ArgsStr else f'{IndentStr}if {FuncStr}():']
# 处理 if 体内的语句
for item in node.body:
if isinstance(item, ast.AnnAssign):
TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation)
result.append(f'{InnerIndent}{item.target.id}: {TypeStr}')
elif isinstance(item, ast.Expr):
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
if ExprStr:
result.append(f'{InnerIndent}{ExprStr}')
return '\n'.join(result)
return ''
cond: str | None = PythonToStubConverter._CifConditionText(node.test)
if cond is None:
return ''
def _body_lines(body: list, ind: str) -> list[str]:
out: list[str] = []
for item in body:
if isinstance(item, ast.AnnAssign):
TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation)
out.append(f'{ind}{item.target.id}: {TypeStr}')
elif isinstance(item, ast.Expr):
ExprStr: str = PythonToStubConverter._GetExprString(item.value)
if ExprStr:
out.append(f'{ind}{ExprStr}')
return out
result: list[str] = [f'{IndentStr}cif {cond}:']
result.extend(_body_lines(node.body, InnerIndent))
current: ast.If = node
while current.orelse:
if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If):
elif_node: ast.If = current.orelse[0]
econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test)
if econd is None:
break
result.append(f'{IndentStr}celif {econd}:')
result.extend(_body_lines(elif_node.body, InnerIndent))
current = elif_node
continue
result.append(f'{IndentStr}celse:')
result.extend(_body_lines(current.orelse, InnerIndent))
break
return '\n'.join(result)
@staticmethod
def _GetModuleIfMacroString(node: ast.If) -> str:
"""获取模块级别的 if 宏字符串(如 if c.CIf(FF_MULTI_PARTITION):)"""
"""获取模块级别的 cif 宏字符串(如 `cif FF_MULTI_PARTITION:`)。"""
return PythonToStubConverter._ProcessIfMacro(node, indent=0)
@staticmethod
def _ProcessIfMacro(node: ast.If, indent: int = 0) -> str:
"""处理 if 宏节点,支持 elif 和 else"""
"""处理 cif 宏节点,支持 celif / celse 链。"""
IndentStr: str = ' ' * indent
cond: str | None = PythonToStubConverter._CifConditionText(node.test)
if cond is None:
return ''
result: list[str] = [f'{IndentStr}cif {cond}:']
for item in node.body:
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
# 检查条件是否是宏调用
if isinstance(node.test, ast.Call):
FuncStr: str = PythonToStubConverter._GetDecoratorString(node.test.func)
if FuncStr and 'CIf' in FuncStr:
ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in node.test.args])
result: list[str] = [f'{IndentStr}if {FuncStr}({ArgsStr}):' if ArgsStr else f'{IndentStr}if {FuncStr}():']
# 处理 if 体内的语句
for item in node.body:
current: ast.If = node
while current.orelse:
if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If):
# celif 情况
elif_node: ast.If = current.orelse[0]
econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test)
if econd is None:
break
result.append(f'{IndentStr}celif {econd}:')
for item in elif_node.body:
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
# 处理 elif 和 else
current: ast.If = node
while current.orelse:
if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If):
# elif 情况
elif_node: ast.If = current.orelse[0]
if isinstance(elif_node.test, ast.Call):
ElifFuncStr: str = PythonToStubConverter._GetDecoratorString(elif_node.test.func)
if ElifFuncStr and 'CIf' in ElifFuncStr:
ElifArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in elif_node.test.args])
result.append(f'{IndentStr}elif {ElifFuncStr}({ElifArgsStr}):' if ElifArgsStr else f'{IndentStr}elif {ElifFuncStr}():')
for item in elif_node.body:
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
current = elif_node
continue
break
else:
# else 情况
result.append(f'{IndentStr}else:')
for item in current.orelse:
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
break
return '\n'.join(result)
return ''
current = elif_node
continue
# celse 情况
result.append(f'{IndentStr}celse:')
for item in current.orelse:
result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1))
break
return '\n'.join(result)
@staticmethod
def _ProcessMacroBodyItem(item: ast.AST, indent: int = 0) -> list[str]:
+66 -1
View File
@@ -222,6 +222,28 @@ class ConstEvaluator:
elif isinstance(func, ast.Name):
func_name = func.id
# c.@CIfdef(NAME) / c.@CIfndef(NAME):defined(NAME) 的解析期 desugar 标记,编译期返回 0/1。
# 仅出现在 cif/celif 条件内(解析器保证),故此处只做宏定义判定。
if func_name == '@CIfdef':
if node.args:
macro_name: str | None = ConstEvaluator._resolve_macro_ref(node.args[0], ctx)
if macro_name is not None:
return 1 if ctx.is_macro_defined(macro_name) else 0
return 0
if func_name == '@CIfndef':
if node.args:
macro_name = ConstEvaluator._resolve_macro_ref(node.args[0], ctx)
if macro_name is not None:
return 0 if ctx.is_macro_defined(macro_name) else 1
return 1
# defined(NAME) 谓词(未 desugar 形态 / TPV 口径):编译期返回 0/1。
if func_name == 'defined' and module_name is None:
if node.args:
macro_name = ConstEvaluator._resolve_macro_ref(node.args[0], ctx)
if macro_name is not None:
return 1 if ctx.is_macro_defined(macro_name) else 0
return 0
# ctraits.isptr(x) — 带模块前缀
# isptr(x) — 通过 from ctraits import isptr 导入
is_isptr: bool = False
@@ -434,6 +456,17 @@ class ConstEvaluator:
pass
return None
@staticmethod
def _resolve_macro_ref(node: ast.AST, ctx: 'EvalContext') -> Optional[str]:
"""从 defined(...) 实参节点解析宏名:Name→id;Constant(str)→值;Attribute→点分全名。"""
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Constant) and isinstance(node.value, str):
return node.value
if isinstance(node, ast.Attribute):
return EvalContext._get_attr_full_name(node)
return None
@staticmethod
def _eval_attribute_define(node: ast.Attribute, symtab: Any) -> Optional[Any]:
"""从 SymbolTable 查找 Attribute 形式的 define 值"""
@@ -532,7 +565,39 @@ class EvalContext:
return None
def lookup_attribute(self, node: ast.Attribute) -> Optional[Any]:
def is_macro_defined(self, name: str) -> bool:
"""判定宏 NAME 是否已定义(供 defined(NAME) / c.CIfdef 标记使用)。
来源与 HandlesIf._is_macro_defined 同口径:Gen._define_constants /
_DefineConstants / SymbolTable.is_define / 平台宏。
"""
if not name:
return False
if self.Gen and name in getattr(self.Gen, '_define_constants', {}):
return True
if self.Gen and hasattr(self.Gen, '_DefineConstants') and name in self.Gen._DefineConstants:
return True
if self.symtab:
is_define = getattr(self.symtab, 'is_define', None)
if callable(is_define) and is_define(name):
return True
if hasattr(self.symtab, '__contains__') and name in self.symtab:
info: CTypeInfo = self.symtab[name]
if getattr(info, 'IsDefine', False):
return True
macros: dict = self._get_platform_macros()
if name in macros:
return True
# Attribute 形式(如 platmacro.IS_WINDOWS):退化为短名匹配。
if '.' in name:
short: str = name.rsplit('.', 1)[-1]
if short in macros:
return True
if self.Gen and short in getattr(self.Gen, '_define_constants', {}):
return True
return False
def lookup_attribute(self, node: ast.Attribute) -> Optional[Any]:
"""查找 Attribute 节点的值"""
module_name: str | None = node.value.id if isinstance(node.value, ast.Name) else None
attr_name: str = node.attr
+11 -1
View File
@@ -12,6 +12,7 @@ from lib.includes import t
from lib.core.SymbolUtils import (
IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext,
AnnotationContainsName, IsCPtrNode, RequireDefineConstStorable,
FindAutoAddrMode,
)
@@ -38,7 +39,8 @@ def _IsGenericTpName(name: str) -> bool:
_PTR_MARKER_CLASS_NAMES: frozenset[str] = frozenset({
'rawptr', 't.rawptr', 'c.rawptr',
'CPtr', 't.CPtr', 'c.CPtr',
'CNeedPtr', 't.CNeedPtr', 'CAutoPtr', 't.CAutoPtr',
'CAutoPtr', 't.CAutoPtr',
'CRef', 't.CRef', 'CTempRef', 't.CTempRef',
})
@@ -501,6 +503,14 @@ class AnnAssignHandle(BaseHandle):
def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
# 值引用标记(`&T` / `&temp T`)仅用于函数参数位:局部/全局变量注解出现即 fail-fast。
_ref_mode: str | None = FindAutoAddrMode(Node.annotation)
if _ref_mode is not None:
from lib.core.LLVMCG.FuncGen import FailFastError
_ann_text: str = ast.unparse(Node.annotation)
raise FailFastError(
f"变量注解不支持值引用(`&T` / `&temp T`):'{_ann_text}'。"
f"值引用仅用于函数参数位;局部变量请改用显式指针类型(如 `T*`)。")
# 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列):
# 局部变量/self 字段注解不经过 ResolveAnnotationType,故在此显式校验。
self._CheckFixedArrayKeyword(Node.annotation)
+79 -60
View File
@@ -1,60 +1,79 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
import vast as ast
import llvmlite.ir as ir
class AssertHandle(BaseHandle):
def _HandleAssertLlvm(self, Node: ast.Assert) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
cond_val: ir.Value | None = self.HandleExprLlvm(Node.test)
if not cond_val:
return
if not isinstance(cond_val.type, ir.IntType) or cond_val.type.width != 1:
zero: ir.Constant = ir.Constant(cond_val.type, 0)
cond_val = Gen.builder.icmp_signed('!=', cond_val, zero, name="assert_cond")
AssertFailBB: ir.Block = Gen.func.append_basic_block(name="assert.fail")
AssertOkBB: ir.Block = Gen.func.append_basic_block(name="assert.ok")
Gen.builder.cbranch(cond_val, AssertOkBB, AssertFailBB)
Gen.builder.position_at_start(AssertFailBB)
exc_code: int = 9
exc_msg: ir.Value | None = None
if Node.msg:
if isinstance(Node.msg, ast.Constant) and isinstance(Node.msg.value, str):
exc_msg = self.HandleExprLlvm(Node.msg)
elif isinstance(Node.msg, ast.Call) and isinstance(Node.msg.func, ast.Name):
exc_code = EXCEPTION_CODE_MAP.get(Node.msg.func.id, 99)
if Node.msg.args:
first_arg: ast.expr = Node.msg.args[0]
if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str):
exc_msg = self.HandleExprLlvm(first_arg)
elif isinstance(Node.msg, ast.Name):
exc_code = EXCEPTION_CODE_MAP.get(Node.msg.id, 99)
else:
exc_msg = self.HandleExprLlvm(Node.msg)
if Gen.eh_except_block_stack:
ExceptBB: ir.Block = Gen.eh_except_block_stack[-1][0]
EndBB: ir.Block | None = Gen.eh_except_block_stack[-1][1]
exception_code: ir.AllocaInstr = Gen.eh_except_block_stack[-1][2]
eh_message: ir.AllocaInstr = Gen.eh_except_block_stack[-1][3]
Gen._store(ir.Constant(ir.IntType(32), exc_code), exception_code)
if exc_msg:
Gen._store(exc_msg, eh_message)
else:
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen._store(null_ptr, eh_message)
Gen.builder.branch(ExceptBB)
else:
printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
if exc_msg:
fmt: ir.Value = Gen.emit_constant("AssertionError: %s\n", 'string')
Gen.builder.call(printf_func, [fmt, exc_msg], name="assert_fail_print")
else:
fmt: ir.Value = Gen.emit_constant("AssertionError\n", 'string')
Gen.builder.call(printf_func, [fmt], name="assert_fail_print")
exit_func: ir.Function = Gen.get_or_declare_c_func('exit', ir.FunctionType(ir.VoidType(), [ir.IntType(32)]))
Gen.builder.call(exit_func, [ir.Constant(ir.IntType(32), 1)], name="call_exit")
Gen.builder.unreachable()
Gen.builder.position_at_start(AssertOkBB)
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
import vast as ast
# 无信息 assert 使用的默认 fail_msg;须与 testcheck.check 的 fail_msg: str 形参一致
_ASSERT_DEFAULT_FAIL_MSG: str = "assertion failed"
# assert 自动依赖的检查库模块名与入口函数(见 includes/testcheck.vp)
_ASSERT_CHECK_MODULE: str = "testcheck"
_ASSERT_CHECK_FUNC: str = "check"
class AssertHandle(BaseHandle):
def _HandleAssertLlvm(self, Node: ast.Assert) -> None:
"""把 `assert 条件[, 信息]` 译为一次普通函数调用 testcheck.check(条件, None, 信息)。
语义:条件为真 → ok_msg 传 None(静默,不打印 PASS);条件为假 → fail(信息)。
不再中止程序(旧实现的 printf AssertionError + exit(1) 已移除),也不再走
try/except 抛异常码的路径。
"""
FailMsgNode: ast.expr = self._ResolveAssertFailMsgNode(Node)
CallNode: ast.Call = ast.Call(
func=ast.Attribute(
value=ast.Name(id=_ASSERT_CHECK_MODULE, ctx=ast.Load()),
attr=_ASSERT_CHECK_FUNC,
ctx=ast.Load()),
args=[Node.test, ast.Constant(value=None, kind=None), FailMsgNode],
keywords=[],
)
ast.copy_location(CallNode, Node)
# 复用既有跨模块调用生成路径(含符号解析、实参按形参类型收窄/扩充与 fail-fast 校验)
self.HandleExprLlvm(CallNode)
def _ResolveAssertFailMsgNode(self, Node: ast.Assert) -> ast.expr:
"""解析 assert 的信息表达式,并强制其为 str(fail-fast,禁止静默降级)。
- 无信息 → 字符串字面量 "assertion failed"
- 有信息 → 原节点(须为 str 形态:字面量 / f-string / str 变量或属性)
"""
MsgNode: ast.expr | None = Node.msg
if MsgNode is None:
DefaultMsg: ast.Constant = ast.Constant(value=_ASSERT_DEFAULT_FAIL_MSG, kind=None)
ast.copy_location(DefaultMsg, Node)
return DefaultMsg
# 旧实现允许把异常类名当信息(映射为异常码、不打印)。check() 的 fail_msg 形参
# 是 str,这类写法已无对应语义:显式报错,不静默降级。
_ExcName: str | None = None
if isinstance(MsgNode, ast.Name):
_ExcName = MsgNode.id
elif isinstance(MsgNode, ast.Call) and isinstance(MsgNode.func, ast.Name):
_ExcName = MsgNode.func.id
if _ExcName is not None and _ExcName in EXCEPTION_CODE_MAP:
self._RaiseAssertMsgTypeError(
f"assert 的信息必须是字符串(str),不能是异常类 '{_ExcName}'")
if not self._IsStrAssertMsg(MsgNode):
self._RaiseAssertMsgTypeError(
"assert 的信息必须是字符串(str):仅支持字符串字面量、f-string 或 str 变量/属性"
"(如需拼接请先写入 str 变量)")
return MsgNode
def _IsStrAssertMsg(self, MsgNode: ast.expr) -> bool:
"""静态判定 assert 信息是否为 str 形态。
变量/属性(Name/Attribute)无法在此静态证伪为 str,放行并由调用点的
`fail_msg: str` 形参做类型收口;其余非常量、非 f-string 的形态一律拒绝。
"""
if isinstance(MsgNode, ast.Constant):
return isinstance(MsgNode.value, str)
if isinstance(MsgNode, ast.JoinedStr):
return True
if isinstance(MsgNode, (ast.Name, ast.Attribute)):
return True
return False
def _RaiseAssertMsgTypeError(self, Message: str) -> None:
from lib.core.LLVMCG.FuncGen import FailFastError
Gen = self.Trans.LlvmGen
raise FailFastError(Message + (Gen._get_node_info() if Gen else ''))
+6 -1
View File
@@ -16,7 +16,7 @@ from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES
from lib.core.SymbolUtils import (
IsPtrSubscriptNode, IsListAnnotation, IsCPtrMarkerNode,
FlattenUnionOperands, IsI8DoublePtrType, IsClassRefPtrAnnotation,
FixedArrayKeywordAnnotationError)
FixedArrayKeywordAnnotationError, FindAutoAddrMode)
from lib.core.VLogger import get_logger as _vlog
@@ -1247,6 +1247,11 @@ class BaseHandle:
return None
VarName: str = item.target.id
AnnotationText: str = ast.unparse(item.annotation)
# 值引用标记(`&T` / `&temp T`)仅用于函数参数位:结构体字段/成员注解出现即 fail-fast。
if FindAutoAddrMode(item.annotation) is not None:
raise ValueError(
f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}':结构体字段/成员不支持"
f"值引用(`&T` / `&temp T`)——值引用仅用于函数参数位,请改用显式指针类型(如 `T*`)")
try:
TypeInfo: CTypeInfo | None = self.ResolveAnnotationType(item.annotation)
except RecursionError:
+5 -4
View File
@@ -19,11 +19,12 @@ from lib.constants.config import mode as _config_mode
from lib.includes import t
from lib.includes.t import CTypeRegistry
# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CNeedPtr/CAutoPtr,后两者继承
# CPtr)、内建 rawptr(= t.CPtr)、str/bytes(= i8*)。枚举值是整数常量,
# 指针成员语义不成立,命中即明确报错(不静默当 int)。
# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CAutoPtr,后者继承
# CPtr)、值引用标记(CRef/CTempRef,`&T`/`&temp T`)、内建 rawptr(= t.CPtr)、
# str/bytes(= i8*)。枚举值是整数常量,指针成员语义不成立,命中即明确报错
# (不静默当 int)。
_ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({
'CPtr', 'CNeedPtr', 'CAutoPtr', 'rawptr', 'str', 'bytes',
'CPtr', 'CAutoPtr', 'CRef', 'CTempRef', 'rawptr', 'str', 'bytes',
})
+21 -18
View File
@@ -27,6 +27,14 @@ _LEN_SUPPORTED_HINT: str = (
"或改用 t.CPtr[元素] 并显式维护元素计数"
)
# 已删除的用户可见 CIf 族(c.CIf / c.CIfdef / c.CIfndef / c.CElif / c.CElse / c.CEndif):
# 用户再写即在调用点 FailFast,指向原生 cif/celif/celse 与 defined(NAME)(英文报文,
# 与 TransPyV CallGenDispatch.vp 同口径)。内部标记 c.@CIf 族不受影响。
_REMOVED_CIF_CALL_MSG: str = (
"c.CIf / c.CIfdef / c.CIfndef were removed; use the cif / celif / celse statements "
"together with the defined(NAME) predicate instead "
"(e.g. `cif defined(NAME):` ... `celif ...:` ... `celse:` ...)")
# PROF 热点:_EmitLLVMIRInstruction 对每条 IR 模板都要做多次 re.match / re.split,
# 未预编译的模块级入口每次都要「模式串哈希 → re 缓存查找 → 未命中就重新编译」,
@@ -938,8 +946,9 @@ class ExprCallHandle(BaseHandle):
return self._HandleCDerefAsLlvm(Node)
elif FuncAttr == 'PtrToInt':
return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node)
elif FuncAttr in ('CIf', 'CElif'):
return self.Trans.CSpecialCallHandle._HandleCIfLlvm(Node)
elif FuncAttr in ('CIf', 'CIfdef', 'CIfndef', 'CElif', 'CElse', 'CEndif'):
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(_REMOVED_CIF_CALL_MSG)
elif FuncAttr == 'CError':
msg = "compile-time error"
if Node.args:
@@ -2003,8 +2012,9 @@ class ExprCallHandle(BaseHandle):
return self._HandleCDerefAsLlvm(Node)
elif func_name == 'PtrToInt':
return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node)
elif func_name in ('CIf', 'CElif'):
return self.Trans.CSpecialCallHandle._HandleCIfLlvm(Node)
elif func_name in ('CIf', 'CIfdef', 'CIfndef', 'CElif', 'CElse', 'CEndif'):
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(_REMOVED_CIF_CALL_MSG)
elif func_name == 'CError':
msg = "compile-time error"
if Node.args:
@@ -3355,19 +3365,9 @@ class ExprCallHandle(BaseHandle):
return self._HandleCDerefAsLlvm(Node)
elif MethodName == 'PtrToInt':
return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node)
elif MethodName in ('CIf', 'CElif'):
if Node.args:
ArgVal = self.HandleExprLlvm(Node.args[0])
if ArgVal:
if isinstance(ArgVal, ir.Constant) and isinstance(ArgVal.type, ir.IntType):
return ir.Constant(ir.IntType(1), 1 if ArgVal.constant != 0 else 0)
if isinstance(ArgVal.type, ir.IntType):
return Gen.builder.icmp_signed('!=', ArgVal, Gen._ZeroConst(ArgVal.type), name="cif_cond")
if isinstance(ArgVal.type, ir.PointerType):
return Gen.builder.icmp_signed('!=', ArgVal, Gen._ZeroConst(ArgVal.type), name="cif_cond")
return ir.Constant(ir.IntType(1), 0)
elif MethodName in ('CIfdef', 'CIfndef'):
return ir.Constant(ir.IntType(1), 1)
elif MethodName in ('CIf', 'CIfdef', 'CIfndef', 'CElif'):
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(_REMOVED_CIF_CALL_MSG)
elif MethodName == 'CError':
msg = "compile-time error"
if Node.args:
@@ -3389,7 +3389,10 @@ class ExprCallHandle(BaseHandle):
if _line_offset > 0 and lineno > _line_offset:
lineno -= _line_offset
raise Exception(f"#error: {msg} (line {lineno})")
elif MethodName in ('CElse', 'CEndif', 'CPragma', 'CUndef'):
elif MethodName in ('CElse', 'CEndif'):
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(_REMOVED_CIF_CALL_MSG)
elif MethodName in ('CPragma', 'CUndef'):
return ir.Constant(ir.IntType(1), 1)
elif MethodName in ('LLVMIR', 'LInp', 'LOut'):
return None
+27 -16
View File
@@ -10,7 +10,7 @@ import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.SymbolUtils import (
IsTModule, FindStructNameInAnnotationWithContext, AnnotationContainsName,
IsPtrSubscriptNode, DesugarPtrSubscriptToUnion)
IsPtrSubscriptNode, DesugarPtrSubscriptToUnion, FindAutoAddrMode)
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
from lib.includes import t
@@ -35,6 +35,21 @@ def _RejectCReturnDecorator(node: ast.AST, decorators: list[ast.AST]) -> None:
raise SyntaxError(f"函数 '{func_name}':{_CRETURN_REMOVED_HINT}")
def _RejectRefReturnAnnotation(node: ast.AST) -> None:
"""返回注解出现值引用标记(`&T` / `&temp T`)即 fail-fast。
值引用仅用于函数参数位;返回位一律拒绝,禁止静默按指针降级。
"""
if getattr(node, 'returns', None) is None:
return
if FindAutoAddrMode(node.returns) is not None:
from lib.core.LLVMCG.FuncGen import FailFastError
func_name: str = getattr(node, 'name', '<unknown>')
raise FailFastError(
f"返回注解不支持值引用 `&T` / `&temp T`(函数 '{func_name}'):"
f"值引用仅用于函数参数位;返回位请改用显式指针类型(如 `T*`)。")
def _IsVoidOrDeclAnnotation(Annot: ast.AST) -> bool:
"""判断函数返回注解是否为 void 或 extern/export 声明型辅助符号。
@@ -505,6 +520,7 @@ class FunctionHandle(BaseHandle):
FuncName, RawFuncName, func_meta = self._compute_func_name_meta(Node, ClassName)
if Node.decorator_list:
_RejectCReturnDecorator(Node, Node.decorator_list)
_RejectRefReturnAnnotation(Node)
MultiRetTypes: list[ast.AST] = []
if Node.returns:
llvm_attrs: list[str] = self._ExtractLLVMAttrs(Node.returns)
@@ -762,16 +778,14 @@ class FunctionHandle(BaseHandle):
ParamTypes.append(ParamType)
ParamNames.append(Arg.arg)
ParamTypeStrs.append(ParamTypeStr)
# 检测 t.CNeedPtr / t.CAutoPtr 参数注解
auto_mode: str | None = None
if ParamTypeStr is not None and hasattr(ParamTypeStr, 'BaseType') and ParamTypeStr.BaseType is not None:
if isinstance(ParamTypeStr.BaseType, t.CAutoPtr):
auto_mode = 'always'
elif isinstance(ParamTypeStr.BaseType, t.CNeedPtr):
auto_mode = 'need'
# 自动取址模式(由注解读出,不依赖 BaseType 类型):
# 'ref' = &T、'temp' = &temp T(合成联合 T | t.CRef / t.CTempRef),
# 'always' = t.CAutoPtr。经 Gen._auto_addr_params 供调用点分流。
auto_mode: str | None = FindAutoAddrMode(Arg.annotation)
auto_addr_modes.append(auto_mode)
if self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen):
ParamTypeStrs.insert(0, f"struct {ResolvedClassName}")
auto_addr_modes.insert(0, None)
IsVariadic: bool = Node.args.vararg is not None
if extra_params:
for var_name, ptr_type in extra_params:
@@ -1366,6 +1380,7 @@ class FunctionHandle(BaseHandle):
if self._is_generic_function(Node):
return
FuncName, RawFuncName, func_meta = self._compute_func_name_meta(Node, ClassName)
_RejectRefReturnAnnotation(Node)
# 保存父级 _CurrentCpythonObjectClass,防止嵌套函数发射污染父方法类上下文
# (例如 HandleImport body 内触发 list[ast.ASTptr] 方法发射,结束后会清空 _CurrentCpythonObjectClass)
saved_cpython_class: str | None = self.Trans._CurrentCpythonObjectClass
@@ -1667,13 +1682,8 @@ class FunctionHandle(BaseHandle):
ParamTypes.append(ParamType)
ParamNames.append(Arg.arg)
ParamTypeStrs.append(ParamTypeInfo)
# 检测 t.CNeedPtr / t.CAutoPtr 参数注解
auto_mode_emit: str | None = None
if ParamTypeInfo is not None and hasattr(ParamTypeInfo, 'BaseType') and ParamTypeInfo.BaseType is not None:
if isinstance(ParamTypeInfo.BaseType, t.CAutoPtr):
auto_mode_emit = 'always'
elif isinstance(ParamTypeInfo.BaseType, t.CNeedPtr):
auto_mode_emit = 'need'
# 自动取址模式(由注解读出):'ref' = &T、'temp' = &temp T、'always' = t.CAutoPtr
auto_mode_emit: str | None = FindAutoAddrMode(Arg.annotation)
auto_addr_modes_emit.append(auto_mode_emit)
Gen._record_var_signedness(Arg.arg, ParamIsUnsigned)
self.Trans.VarScopes[-1][Arg.arg] = ParamTypeInfo
@@ -1706,7 +1716,8 @@ class FunctionHandle(BaseHandle):
self.Trans.CurrentMultiRetTypes = MultiRetTypes
else:
self.Trans.CurrentMultiRetTypes = None
self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen)
if self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen):
auto_addr_modes_emit.insert(0, None)
IsVariadic = Node.args.vararg is not None
if extra_params:
for var_name, ptr_type in extra_params:
+35 -14
View File
@@ -23,12 +23,16 @@ class IfHandle(BaseHandle):
return None
def _is_cif_call(self, node: ast.expr) -> bool:
"""判定 If.test 是否为 cif/celif 的内部标记调用(c.@CIf 族,解析期合成)。
标记属性名带前导 '@'(词法器不可产出),故用户写的 c.CIf(...) 不会命中。
"""
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute):
if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c':
return node.func.attr in ('CIf', 'CIfdef', 'CIfndef')
return node.func.attr in ('@CIf', '@CIfdef', '@CIfndef')
if isinstance(node.func, ast.Name):
return node.func.id in ('CIf', 'CIfdef', 'CIfndef')
return node.func.id in ('@CIf', '@CIfdef', '@CIfndef')
return False
def _resolve_macro_name(self, arg: ast.expr) -> str | None:
@@ -55,17 +59,17 @@ class IfHandle(BaseHandle):
kind = node.func.id
else:
return None
if kind == 'CIfdef':
if kind == '@CIfdef':
name: str | None = self._resolve_macro_name(args[0])
if name is None:
return 0
return 1 if self._is_macro_defined(name) else 0
elif kind == 'CIfndef':
elif kind == '@CIfndef':
name: str | None = self._resolve_macro_name(args[0])
if name is None:
return 1
return 0 if self._is_macro_defined(name) else 1
elif kind == 'CIf':
elif kind == '@CIf':
return self._eval_const_expr(args[0])
return None
@@ -154,19 +158,36 @@ class IfHandle(BaseHandle):
return 1 if result != 0.0 else 0
return result
def _CifExprText(self, node: ast.expr) -> str:
"""cif/celif 标记调用内层条件表达式的源码文本(供 FailFast 报文,失败取占位)。"""
try:
if isinstance(node, ast.Call) and node.args:
return ast.unparse(node.args[0])
return ast.unparse(node)
except Exception:
return '<condition>'
def _HandleIfLlvm(self, Node: ast.If) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if self._is_cif_call(Node.test):
compile_time_val: int | None = self._evaluate_cif_condition(Node.test)
if compile_time_val is not None:
if compile_time_val:
self.Trans.BodyHandler.HandleBodyLlvm(Node.body)
elif Node.orelse:
if isinstance(Node.orelse, list) and len(Node.orelse) == 1 and isinstance(Node.orelse[0], ast.If):
self._HandleIfLlvm(Node.orelse[0])
else:
self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse)
return
if compile_time_val is None:
# cif/celif 条件必须是编译期常量:求不出 ⇒ FailFast(不再回退运行时分支)。
from lib.core.LLVMCG.FuncGen import FailFastError
expr_text: str = self._CifExprText(Node.test)
Message: str = (
f"'cif' / 'celif' condition is not a compile-time constant: {expr_text}. "
f"Only CDefine constants, literals, their arithmetic/comparison/boolean/"
f"bitwise operators and the 'defined(NAME)' predicate are allowed.")
raise FailFastError(Message + (Gen._get_node_info() if Gen else ''))
if compile_time_val:
self.Trans.BodyHandler.HandleBodyLlvm(Node.body)
elif Node.orelse:
if isinstance(Node.orelse, list) and len(Node.orelse) == 1 and isinstance(Node.orelse[0], ast.If):
self._HandleIfLlvm(Node.orelse[0])
else:
self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse)
return
Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test)
if not Cond:
return
+8 -1
View File
@@ -14,7 +14,7 @@ from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
from lib.includes import t
from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext
from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext, FindAutoAddrMode
from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords
from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod
@@ -1870,6 +1870,13 @@ class ImportHandle(BaseHandle):
func = Gen._get_or_declare_function(MangledName, FuncType)
Gen.functions[MangledName] = func
Gen.functions[FuncName] = func
# 跨模块自动取址模式登记(ref/temp/always):调用点 Gen._apply_auto_addr 按函数名
# 查表,命中才做取址适配。ParamTypes 与 Node.args.args 逐位对齐(含方法 self)。
_auto_modes: list[str | None] = [
FindAutoAddrMode(a.annotation) for a in Node.args.args]
if any(m is not None for m in _auto_modes):
Gen._auto_addr_params[MangledName] = _auto_modes
Gen._auto_addr_params[FuncName] = _auto_modes
func_meta = FuncMeta.NONE
if Node.decorator_list:
for d in Node.decorator_list:
+7
View File
@@ -207,6 +207,13 @@ class HandlesTypeMerge(BaseHandle):
return Info
if ModulePath == 't' or (ModulePath and ModulePath.startswith('t.')):
# 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`,非真实类):与
# t.CPtr 同形的「一层指针」,使 `T | t.CRef` 合并为 T*(pointer 层 +1)。
if TypeName in ('CRef', 'CTempRef'):
Info = CTypeInfo()
Info.PtrCount = 1
Info.BaseType = t.CVoid
return Info
if self._IsTModuleType(TypeName):
TypeClass = CTypeHelper.GetTModuleCType(TypeName) or getattr(t, TypeName, None)
if TypeClass == t.CArrayPtr:
+3 -2
View File
@@ -378,8 +378,9 @@ class BaseGenMixin:
# 编译期 with 上下文栈:记录当前嵌套 with 链中所有可用的 provides 字段名
# 用于 __require_must__ 的纯编译期静态可达性检查
self._with_provides_stack: list[list[str]] = []
# 函数参数自动取地址模式: func_name -> [None, None, 'need', 'always', ...]
# 'need': t.CNeedPtr — 仅对非指针值取地址
# 函数参数自动取地址模式: func_name -> [None, None, 'ref', 'temp', 'always', ...]
# 'ref': &T — 实参非指针左值取址,右值 fail-fast
# 'temp': &temp T — 实参非指针时在调用方建同型临时再取址
# 'always': t.CAutoPtr — 无条件取地址
self._auto_addr_params: dict[str, list[str | None]] = {}
self.global_struct_class: dict[str, str] = {}
+28 -7
View File
@@ -94,11 +94,17 @@ class FuncGenMixin:
self.class_methods[ClassName].append(MethodName)
def _apply_auto_addr(self, func_name: str, args: list[ir.Value]) -> list[ir.Value]:
"""对函数参数应用自动取地址逻辑(t.CNeedPtr / t.CAutoPtr)。
在调用前对标记为 auto-addr 的参数自动取地址:
- 'need' (t.CNeedPtr): 仅对非指针值取地址(alloca + store + get pointer)
- 'always' (t.CAutoPtr): 无条件取地址
"""对函数参数应用自动取址逻辑(ref / temp / always 三种模式)。
在调用前对标记为 auto-addr 的实参做地址适配(模式由 HandlesFunctions 从形参注解
读出并写入 Gen._auto_addr_params[func_name]):
- 'ref'(&T):实参已是指针 → 原样传;非指针左值 → 传该左值地址;
非指针且非左值(字面量/表达式结果)→ fail-fast(禁止静默降级)。
- 'temp'(&temp T):实参已是指针 → 原样传;否则在 entry 块建同型临时 + store + 传地址。
- 'always'(t.CAutoPtr):无条件取址(保持既有语义)。
非指针左值的地址取自实参 load 指令的指针操作数(`load(p)` ⇒ p 即左值地址),
因为调用点实参已按 rvalue 求值(标量变量的 load 结果)。
"""
modes: list[str | None] | None = self._auto_addr_params.get(func_name)
if not modes:
@@ -109,8 +115,23 @@ class FuncGenMixin:
continue
arg: ir.Value = new_args[i]
is_ptr: bool = isinstance(arg.type, ir.PointerType)
if mode == 'always' or (mode == 'need' and not is_ptr):
# alloca + store + get pointer
if mode == 'ref':
if is_ptr:
continue
if isinstance(arg, ir.LoadInstr) and len(arg.operands) == 1:
# load(p) 的 p 即该左值的地址(该实参是变量/字段/下标的取值)
new_args[i] = arg.operands[0]
continue
raise FailFastError(
f"&T 形参只接受左值,右值请用具名临时变量或改用 &temp T"
f"(调用 {func_name} 第 {i} 个实参类型 {arg.type})")
if mode == 'temp':
if is_ptr:
continue
slot: ir.Value = self._allocaEntry(arg.type, name=f"autoaddr_tmp_{i}")
self.builder.store(arg, slot)
new_args[i] = slot
elif mode == 'always':
slot: ir.Value = self.builder.alloca(arg.type)
self.builder.store(arg, slot)
new_args[i] = slot
+32
View File
@@ -2386,6 +2386,38 @@ def IsCPtrNode(node: ast.AST) -> bool:
and node.attr == 'CPtr')
# 值引用参数标记名 → 自动取址模式(仅注解 AST 内按名字识别,非真实类):
# 'CRef' → 'ref' (&T :实参非指针左值取址,右值 fail-fast)
# 'CTempRef' → 'temp' (&temp T :实参非指针时在调用方建同型临时再取址)
# 'CAutoPtr' → 'always' (t.CAutoPtr:无条件取址,保持既有语义)
_AUTO_ADDR_MARKER_MODES: dict[str, str] = {
'CRef': 'ref', 'CTempRef': 'temp', 'CAutoPtr': 'always',
}
def FindAutoAddrMode(node: ast.AST | None) -> str | None:
"""注解 AST 内查找自动取址模式标记,返回 'ref' / 'temp' / 'always';无则 None。
只按名字识别(Attribute.attr 或 Name.id),递归联合/嵌套注解;CRef/CTempRef
非真实类,不出现在 lib/includes/t.py。
"""
if node is None:
return None
if isinstance(node, ast.Attribute):
mode = _AUTO_ADDR_MARKER_MODES.get(node.attr)
if mode is not None:
return mode
elif isinstance(node, ast.Name):
mode = _AUTO_ADDR_MARKER_MODES.get(node.id)
if mode is not None:
return mode
for child in ast.iter_child_nodes(node):
mode = FindAutoAddrMode(child)
if mode is not None:
return mode
return None
def IsCPtrSubscriptNode(node: ast.AST) -> bool:
"""检查节点是否为 t.CPtr[T] 下标指针类型(非裸标记,裸标记见 IsCPtrNode)。
+8
View File
@@ -1131,6 +1131,14 @@ class TypeAnnotationResolver:
ModulePath = resolved_name
if ModulePath and IsTModulePath(ModulePath):
# 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`):非真实类,仅按名字
# 识别为「一层指针」(与 t.CPtr 同形),使联合形态 `T | t.CRef` 解析为 T*、
# `void | t.CRef` 解析为 void*(i8*)。模式语义(ref/temp)由注解侧识别。
if TypeName in ('CRef', 'CTempRef'):
Info: CTypeInfo = CTypeInfo()
Info.PtrCount = 1
Info.BaseType = t.CVoid
return Info
TypeClass: type | None = CTypeHelper.GetTModuleCType(TypeName)
if TypeClass is None:
TypeClass = getattr(t, TypeName, None)
-43
View File
@@ -454,32 +454,6 @@ class CDefine:
return []
class CIfndef:
"""#ifndef 条件编译"""
def __init__(self, condition: Any) -> None:
self.condition: Any = condition
@staticmethod
def HandleCall(translator: Any, args: Any, keywords: Any) -> list[str]:
"""处理 c.CIfndef() 调用"""
if args:
condition: Any = translator.HandleExpr(args[0])[0]
return ['#ifndef ' + _to_str(condition)]
return ['#if 0']
class CIfdef:
"""#ifdef 条件编译"""
def __init__(self, condition: Any) -> None:
self.condition: Any = condition
@staticmethod
def HandleCall(translator: Any, args: Any, keywords: Any) -> list[str]:
"""处理 c.CIfdef() 调用"""
if args:
condition: Any = translator.HandleExpr(args[0])[0]
return ['#ifdef ' + _to_str(condition)]
return ['#if 0']
class CError:
"""#error 错误信息"""
def __init__(self, condition: Any) -> None:
@@ -536,20 +510,6 @@ class TokenPast:
return ['']
class CIf:
"""#if 条件编译"""
def __init__(self, condition: Any) -> None:
self.condition: Any = condition
@staticmethod
def HandleCall(translator: Any, args: Any, keywords: Any) -> list[str]:
"""处理 c.CIf() 调用"""
if args:
condition: Any = translator.HandleExpr(args[0])[0]
return ['#if ' + _to_str(condition)]
return ['#if 0']
class CElif:
"""#elif 条件编译"""
def __init__(self, condition: Any) -> None:
@@ -688,9 +648,6 @@ Library_C = {
'AsmInp': AsmInp,
'AsmOut': AsmOut,
'CDefine': CDefine,
'CIfndef': CIfndef,
'CIfdef': CIfdef,
'CIf': CIf,
'CElif': CElif,
'CElse': CElse,
'CEndif': CEndif,
-9
View File
@@ -530,15 +530,6 @@ class CPtr(CType, Generic[T]):
def __init__(self, value: Any = None) -> None:
super().__init__(value)
class CNeedPtr(CPtr):
"""函数签名注解:自动取地址(仅对非指针值)。
用作参数类型时,编译器在调用处自动将非指针值取地址为指针。
若参数已经是指针,则不做操作。"""
position: ClassVar[frozenset[str]] = frozenset({CType.POINTER})
auto_addr_mode: ClassVar[str] = 'need'
def __init__(self, value: Any = None) -> None:
super().__init__(value)
class CAutoPtr(CPtr):
"""函数签名注解:无条件取地址。
用作参数类型时,编译器在调用处无条件将值取地址为指针,
+90 -1
View File
@@ -40,6 +40,12 @@ def _JoinConstParts(parts: list[object]) -> str | bytes:
_HEX_ALPHABET: str = '0123456789abcdefABCDEF'
# 已删除的用户可见 CIf 族名:用户再写 c.CIf / c.CIfdef / c.CIfndef ⇒ FailFast,
# 提示改用原生 cif/celif/celse 与 defined() 谓词(内部标记由解析期合成,不走此处)。
_REMOVED_CIF_NAMES: frozenset[str] = frozenset({
'CIf', 'CIfdef', 'CIfndef', 'c.CIf', 'c.CIfdef', 'c.CIfndef',
})
def _HexValOf(ch: str) -> int:
"""单个字符的十六进制位值;非十六进制字符返回 0(与 C 解析器 vast_c.HexVal 同口径)。"""
@@ -581,6 +587,44 @@ class ExprParserMixin(TokenStream):
self._ExpectOp(')')
node: N.Call = N.Call(func=func, args=args, keywords=keywords)
self._FixLoc(node, start)
return self._DesugarSpecialCall(node, start)
def _CallCalleeName(self, func: N.expr) -> str:
"""被调用者的限定名:Name('X')→'X';Attribute(c,'X')→'c.X';否则空串。"""
if isinstance(func, N.Name):
return func.id
if isinstance(func, N.Attribute) and isinstance(func.value, N.Name):
return f'{func.value.id}.{func.attr}'
return ''
def _DesugarSpecialCall(self, node: N.Call, start: Token) -> N.expr:
"""调用点的特殊 desugar / fail-fast。
- `defined(NAME)`:仅 cif/celif 条件内合法(`self._InCIfCond`),
desugar 为内部标记调用 `c.CIfdef(NAME)`;其余位置 FailFast。
- `c.CIf` / `c.CIfdef` / `c.CIfndef`(及裸名):已删除,FailFast 指向新语法。
"""
callee: str = self._CallCalleeName(node.func)
if callee in _REMOVED_CIF_NAMES:
raise SyntaxError(
f"'{callee}(...)' has been removed; use the native 'cif' / 'celif' / "
f"'celse' statements together with the 'defined(NAME)' predicate "
f"instead, at line {start.Line}")
if callee == 'defined':
if not self._InCIfCond:
raise SyntaxError(
"'defined(NAME)' is only allowed inside a 'cif' / 'celif' "
f"condition, at line {start.Line}")
if len(node.args) != 1 or node.keywords:
raise SyntaxError(
f"'defined' expects exactly one macro name (e.g. defined(MAX_SIZE)), "
f"at line {start.Line}")
marker: N.Call = N.Call(
func=N.Attribute(value=N.Name(id='c', ctx=N.Load()),
attr='@CIfdef', ctx=N.Load()),
args=[node.args[0]], keywords=[])
self._FixLoc(marker, start)
return marker
return node
def _ParseSubscriptTail(self, value: N.expr, start: Token) -> N.Subscript:
@@ -1567,7 +1611,33 @@ class ExprParserMixin(TokenStream):
正确写法示例),绝不静默丢弃:注解末尾若仍遗留未被 `_AtPtrSuffix` 接受的
'*'/'**'(如 `a: int * 2` / `a: int* | t.CPtr`),判据拒绝消费即终止编译
(历史事故:静默退化会产出错误的指针层数)。
值引用参数注解(仅形参位有效):前导 `&` 为 ref 模式、`&temp` 为 temp 模式,
合成联合形态 `T | t.CRef` / `T | t.CTempRef` 承载(CRef/CTempRef 非真实类,
仅按名字识别;语义侧见 HandlesFunctions / FuncGen)。表达式位的 `&x` 不受影响。
"""
ref_mode: str | None = None
ref_tok: Token = self._Peek()
if self._AtOp('&'):
self._Next()
if self._AtName('temp'):
self._Next()
ref_mode = 'temp'
else:
ref_mode = 'ref'
# `&` / `&temp` 之后必须紧跟类型;缺失即响亮失败(禁止静默丢弃)
end_tok: Token = self._Peek()
if end_tok.Type in (NEWLINE, NL, ENDMARKER) or (
end_tok.Type == OP and end_tok.Value in (')', ',', ':', '=', ']', '}', ';')):
snippet: str = ''
try:
snippet = (self.Source.splitlines()[end_tok.Line - 1] or '').strip()
except Exception:
snippet = ''
raise SyntaxError(
f"[vast] 值引用参数注解缺少类型:第 {end_tok.Line} 行第 {end_tok.Col} 列的 "
f"`&` 后必须紧跟类型 T(ref:`&T`;temp:`&temp T`)。"
f"源码片段 {snippet!r}。正确示例:`def f(p: &int)` / `def f(p: &temp void)`。")
saved_flag: bool = self._InTypeAnnotation
self._InTypeAnnotation = True
try:
@@ -1575,9 +1645,13 @@ class ExprParserMixin(TokenStream):
finally:
self._InTypeAnnotation = saved_flag
if expr is None:
if ref_mode is not None:
raise SyntaxError(
"[vast] 值引用参数注解缺少类型:`&` 后必须紧跟类型 T"
"(ref:`&T`;temp:`&temp T`)。")
return None
if self._AtPtrSuffix(expr):
return self._ConsumePtrSuffix(expr)
expr = self._ConsumePtrSuffix(expr)
tok: Token = self._Peek()
if tok.Type == OP and tok.Value in ('*', '**'):
snippet: str = ''
@@ -1595,8 +1669,23 @@ class ExprParserMixin(TokenStream):
f"正确示例:`a: t.CPtr*` / `a: rawptr*` / `a: t.X**` / "
f"`a: rawptr[ast.AST*]` / `def f(p: int*) -> int**`。"
)
if ref_mode is not None:
expr = self._MakeRefMarker(expr, ref_mode, ref_tok)
return expr
def _MakeRefMarker(self, base: N.expr, mode: str, tok: Token) -> N.expr:
"""构造值引用标记联合:`base | t.CRef`(ref)/ `base | t.CTempRef`(temp)。
CRef / CTempRef 非真实类,仅作为注解 AST 内的名字标记(语义侧按名字识别)。
"""
marker: N.expr = N.Attribute(
value=N.Name(id='t', ctx=N.Load()),
attr='CRef' if mode == 'ref' else 'CTempRef',
ctx=N.Load())
node: N.BinOp = N.BinOp(left=base, op=N.BitOr(), right=marker)
self._FixLoc(node, tok)
return node
# 类型结束后缀:'_AtPtrSuffix' 的判据表
# - 全局(任意表达式上下文):'*'/'**' 串后紧跟这些 operator 时,乘法/幂本就缺右操作数
# (即原语法错误),故按指针后缀处理不会改变任何合法表达式的语义;
+121
View File
@@ -91,6 +91,32 @@ _SWITCH_KEYWORD: str = 'switch'
# 标记调用(switch 生成侧据此让 case 体不自动跳出 switch,落到下一个 case)。
_FALLTHROUGH_KEYWORD: str = 'fallthrough'
# ======================================================================
# cif / celif / celse 编译期条件编译(软关键字)
# ======================================================================
# 语法:`cif <编译期常量表达式>:` / `celif <...>:` / `celse:`
# 语义:只有 cif 能开链;celif/celse 必须属于同一条 cif 链。
# 解析期 desugar 为既有「If + CIf 族标记调用」形状(与 Viper 侧口径一致):
# cif E: → If(test=c.@CIf(E), body, orelse)
# celif E: → 同链下一个 If(test=c.@CIf(E), ...)(挂在上一支 orelse)
# celse: → 普通 orelse
# defined(N) → c.@CIfdef(N) 标记调用
# 标记(c.@CIf / c.@CIfdef)是内部表示:用户写 c.CIf(...) 在 ExprParser 处报错。
_CIF_KEYWORD: str = 'cif'
_CELIF_KEYWORD: str = 'celif'
_CELSE_KEYWORD: str = 'celse'
# 内部 CIf 族标记:desugar 产出的调用名(用户不可写)。
# 与 Viper 侧解析器(includes/ast/parser.vp 的 _MakeCifMarker)同拼写:属性名带前导 '@',
# 词法器无法产出该拼写,故用户写的 c.CIf(...) 永远不会命中此标记(会被 ExprParser 拒绝)。
_CIF_MARKER: str = '@CIf'
_CIFDEF_MARKER: str = '@CIfdef'
# cif 语句起始判定:语句起始 NAME('cif') 后紧跟这些运算符时按普通语句(赋值/注解)
# 处理,避免 `cif = 5` / `cif: int = 5` 被误判为 cif 语句。
_CIF_NON_STATEMENT_OPS: frozenset[str] = frozenset({
'=', ':', '+=', '-=', '*=', '/=', '//=', '%=', '**=',
'<<=', '>>=', '&=', '|=', '^=', '@=',
})
class Parser(ExprParserMixin):
"""Viper/Python 完整语法分析器。"""
@@ -101,6 +127,10 @@ class Parser(ExprParserMixin):
# "virtual def / novirtual def 只能直接写在类体内"。
_InClassBody: bool = False
# _InCIfCond:当前是否正在解析 cif/celif 的条件表达式。用于判定 `defined(NAME)`
# 谓词的合法位置(仅 cif/celif 条件内合法,其余位置 FailFast)。
_InCIfCond: bool = False
# ==================================================================
# 模块级入口
# ==================================================================
@@ -177,6 +207,15 @@ class Parser(ExprParserMixin):
# struct 纯数据类型定义(硬关键字):语句起始一律按 struct 定义解析。
if v == _STRUCT_KEYWORD:
return self._ParseStructDef()
# cif 编译期条件编译(软关键字):语句起始 + 非赋值/注解形态。
if v == _CIF_KEYWORD and self._IsCIfStatementStart():
return self._ParseCIf()
# celif / celse 必须属于某条 cif 链(由 _ParseCIf 直接消费);
# 语句起始独立出现 ⇒ FailFast。
if v == _CELIF_KEYWORD or v == _CELSE_KEYWORD:
raise SyntaxError(
f"'{v}' must belong to a 'cif' chain (no matching 'cif') "
f"at line {tok.Line}")
if v == 'if':
return self._ParseIf()
if v == 'while':
@@ -1215,6 +1254,88 @@ class Parser(ExprParserMixin):
self._FixLoc(node, start)
return node
# ==================================================================
# cif / celif / celse 编译期条件编译(软关键字,desugar 到 c.CIf 标记)
# ==================================================================
def _IsCIfStatementStart(self) -> bool:
"""语句起始 NAME('cif') 是否按 cif 语句解析。
排除赋值/注解形态(`cif = 5` / `cif: int = 5` / `cif += 1`):
第二种 token 为 '=' ':' 或增强赋值运算符时按普通语句处理。
"""
nxt: Token = self._Peek(1)
if nxt.Type == OP and nxt.Value in _CIF_NON_STATEMENT_OPS:
return False
return True
def _MakeCIfMarkerCall(self, attr: str, expr: N.expr, start: Token) -> N.Call:
"""构造内部标记调用 `c.<attr>(expr)`(用户不可写;cif/defined 的 desugar)。"""
func: N.Attribute = N.Attribute(
value=N.Name(id='c', ctx=N.Load()), attr=attr, ctx=N.Load())
self._FixLoc(func, start)
call: N.Call = N.Call(func=func, args=[expr], keywords=[])
self._FixLoc(call, start)
return call
def _ParseCIf(self) -> N.If:
"""`cif <expr>:`(+ 可选 celif/celse 链)。"""
start: Token = self._Next() # cif
test: N.expr = self._ParseCIfCondition()
self._ExpectOp(':')
body: list[N.stmt] = self._ParseSuite()
orelse: list[N.stmt] = self._ParseCIfChainTail()
node: N.If = N.If(test=self._MakeCIfMarkerCall(_CIF_MARKER, test, start),
body=body, orelse=orelse)
self._FixLoc(node, start)
return node
def _ParseCIfCondition(self) -> N.expr:
"""解析 cif/celif 条件表达式,期间允许 `defined(NAME)` 谓词。"""
prev: bool = self._InCIfCond
self._InCIfCond = True
try:
return self.ParseExpr()
finally:
self._InCIfCond = prev
def _ParseCIfChainTail(self) -> list[N.stmt]:
"""解析 cif 链尾部(celif/celse),返回 orelse 列表。
- 无 celif/celse:返回空(cif 链正常收尾)。
- 出现普通 elif/else:FailFast(cif 链必须以 celse 收尾)。
"""
if self._AtName(_CELIF_KEYWORD):
return [self._ParseCelif()]
if self._AtName(_CELSE_KEYWORD):
self._Next() # celse
self._ExpectOp(':')
body: list[N.stmt] = self._ParseSuite()
# celse 之后不得再出现 celif / celse(同缩进层级会落在本方法可见处)。
if self._AtName(_CELIF_KEYWORD) or self._AtName(_CELSE_KEYWORD):
bad_tok: Token = self._Peek()
raise SyntaxError(
f"'{bad_tok.Value}' cannot follow 'celse' (the 'celse' branch must be "
f"the last of the 'cif' chain) at line {bad_tok.Line}")
return body
if self._AtName('elif') or self._AtName('else'):
kind: str = self._Peek().Value
raise SyntaxError(
f"a 'cif' chain must be closed with 'celse', not '{kind}', "
f"at line {self._Peek().Line}")
return []
def _ParseCelif(self) -> N.If:
"""`celif <expr>:`(cif 链的后续分支)。"""
start: Token = self._Next() # celif
test: N.expr = self._ParseCIfCondition()
self._ExpectOp(':')
body: list[N.stmt] = self._ParseSuite()
orelse: list[N.stmt] = self._ParseCIfChainTail()
node: N.If = N.If(test=self._MakeCIfMarkerCall(_CIF_MARKER, test, start),
body=body, orelse=orelse)
self._FixLoc(node, start)
return node
def _ParseWhile(self) -> N.While:
start: Token = self._Next() # while
test: N.expr = self.ParseExpr()
Binary file not shown.
+424 -5
View File
@@ -101,6 +101,7 @@ typedef struct {
int FStringExprEnd; /* f-string 表达式段结束位置(body 内偏移) */
int InTypeAnnotation; /* 类型注解模式:'*'/'**' 允许按 C 风格指针后缀判定 */
int InClassBody; /* 当前套件的直接拥有者是否为 ClassDef(virtual def 判据) */
int InCIfCond; /* 是否正在解析 cif/celif 条件(defined(NAME) 合法位置判据) */
Token *PtrSuffixTail; /* 最近消费的 C 指针后缀尾 token(识别 (T*)x 无括号强转) */
} Parser;
@@ -2652,6 +2653,8 @@ static PyObject *ConsumePtrSuffix(Parser *P, PyObject *Node) {
}
static PyObject *ParseCallTail(Parser *P, PyObject *Func, Token *Start);
static PyObject *DesugarSpecialCall(Parser *P, PyObject *Node, Token *Start);
static PyObject *MakeCifMarkerCall(Parser *P, const char *AttrName, PyObject *Expr, Token *Start);
/* 判定当前位置的 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用**
* (对齐 Python ExprParser._AtPtrSuffixCall):
@@ -2709,13 +2712,64 @@ static PyObject *ConsumePtrSuffixCall(Parser *P, PyObject *Base, Token *Start) {
return Call;
}
/* 类型注解入口:置注解模式标志(对齐 Python ExprParser._ParseTypeAnnotation)。 */
/* 类型注解入口:置注解模式标志(对齐 Python ExprParser._ParseTypeAnnotation)。
*
* 值引用参数注解(仅形参位有效,对齐纯 Python 端):
* 前导 '&' + 可选名 'temp',合成联合形态 base | t.CRef(ref)/ base | t.CTempRef(temp)。
* CRef/CTempRef 非真实类,仅作注解 AST 内的名字标记(语义侧按名字识别)。
* '&'/'&temp' 之后缺类型(行尾 / ')' / ',' / ':' / '=' / ']' / '}' / ';')时响亮失败。 */
static PyObject *ParseTypeAnnotation(Parser *P) {
int RefTemp = 0; /* 0=非值引用 1=ref 2=temp */
Token *RefTok = PeekTok(P, 0);
if (AtOp(P, "&")) {
NextTok(P);
if (AtName(P, "temp")) {
NextTok(P);
RefTemp = 2;
} else {
RefTemp = 1;
}
Token *End = PeekTok(P, 0);
if (End->Kind == TK_NEWLINE || End->Kind == TK_NL
|| End->Kind == TK_ENDMARKER
|| TokIsOp(End, ")") || TokIsOp(End, ",") || TokIsOp(End, ":")
|| TokIsOp(End, "=") || TokIsOp(End, "]") || TokIsOp(End, "}")
|| TokIsOp(End, ";")) {
SyntaxErrorAt(P, End,
"值引用参数注解缺少类型:`&` 后必须紧跟类型 T(ref:`&T`;temp:`&temp T`)");
return NULL;
}
}
int Saved = P->InTypeAnnotation;
P->InTypeAnnotation = 1;
PyObject *Node = ParseExpr(P);
P->InTypeAnnotation = Saved;
return Node;
if (Node == NULL) {
return NULL;
}
if (RefTemp == 0) {
return Node;
}
PyObject *Marker = MakeTAttrNode(P, RefTemp == 2 ? "CTempRef" : "CRef");
if (Marker == NULL) {
Py_DECREF(Node);
return NULL;
}
PyObject *Op = MakeSingleton(P, "BitOr");
if (Op == NULL) {
Py_DECREF(Node);
Py_DECREF(Marker);
return NULL;
}
PyObject *Argv[3] = { Node, Op, Marker };
PyObject *Wrap = NewNode(P, "BinOp", Argv, 3);
Py_DECREF(Op);
Py_DECREF(Marker);
Py_DECREF(Node);
if (Wrap != NULL) {
FixLoc(P, Wrap, RefTok);
}
return Wrap;
}
/* term:factor (*|/|//|%|@ factor)* */
@@ -3008,10 +3062,127 @@ static PyObject *ParseCallTail(Parser *P, PyObject *Func, Token *Start) {
Py_DECREF(Keywords);
if (Node != NULL) {
FixLoc(P, Node, Start);
Node = DesugarSpecialCall(P, Node, Start);
}
return Node;
}
/* 被调用者限定名:Name('X')→'X';Attribute(Name('c'),'X')→'c.X';否则 NULL(无异常)。 */
static PyObject *MakeCifMarkerCall(Parser *P, const char *AttrName, PyObject *Expr, Token *Start);
static PyObject *CalleeQualName(PyObject *Func) {
if (NodeIs(Func, "Name")) {
return PyObject_GetAttrString(Func, "id");
}
if (NodeIs(Func, "Attribute")) {
PyObject *Value = PyObject_GetAttrString(Func, "value");
PyObject *Attr = PyObject_GetAttrString(Func, "attr");
PyObject *Result = NULL;
if (Value != NULL && Attr != NULL && NodeIs(Value, "Name")) {
PyObject *Id = PyObject_GetAttrString(Value, "id");
if (Id != NULL) {
Result = PyUnicode_FromFormat("%U.%U", Id, Attr);
Py_DECREF(Id);
}
}
Py_XDECREF(Value);
Py_XDECREF(Attr);
return Result;
}
return NULL;
}
static int StrIsOneOf(PyObject *S, const char *const *Names) {
for (int I = 0; Names[I] != NULL; I++) {
if (PyUnicode_CompareWithASCIIString(S, Names[I]) == 0) {
return 1;
}
}
return 0;
}
/* 调用点特殊 desugar / fail-fast:接管 Node 的引用,返回新引用(失败返回 NULL)。
* - defined(NAME):仅 cif/celif 条件内合法(P->InCIfCond)⇒ desugar 为 c.@CIfdef(NAME);
* 其余位置 ⇒ FailFast。
* - c.CIf / c.CIfdef / c.CIfndef(及裸名):已删除 ⇒ FailFast,指向原生新语法。 */
static PyObject *DesugarSpecialCall(Parser *P, PyObject *Node, Token *Start) {
PyObject *Func = PyObject_GetAttrString(Node, "func");
if (Func == NULL) {
PyErr_Clear();
return Node;
}
/* 廉价预筛:只看标识符短名(Name.id / Attribute.attr),与 cif 无关的调用一律
* 立即返回,避免在解析热路径上为每个调用构造限定名并分配字符串。 */
PyObject *Short = NULL;
if (NodeIs(Func, "Name")) {
Short = PyObject_GetAttrString(Func, "id");
} else if (NodeIs(Func, "Attribute")) {
Short = PyObject_GetAttrString(Func, "attr");
}
if (Short == NULL) {
PyErr_Clear();
Py_DECREF(Func);
return Node;
}
static const char *Interesting[] = {"CIf", "CIfdef", "CIfndef", "defined", NULL};
int Hit = StrIsOneOf(Short, Interesting);
Py_DECREF(Short);
if (!Hit) {
Py_DECREF(Func);
return Node;
}
PyObject *Callee = CalleeQualName(Func);
Py_DECREF(Func);
if (Callee == NULL) {
PyErr_Clear();
return Node;
}
static const char *Removed[] = {
"CIf", "CIfdef", "CIfndef", "c.CIf", "c.CIfdef", "c.CIfndef", NULL
};
if (StrIsOneOf(Callee, Removed)) {
PyErr_Format(PyExc_SyntaxError,
"'%U(...)' has been removed; use the native 'cif' / 'celif' / 'celse' "
"statements together with the 'defined(NAME)' predicate instead, at line %d",
Callee, Start->Line);
Py_DECREF(Callee);
Py_DECREF(Node);
return NULL;
}
if (PyUnicode_CompareWithASCIIString(Callee, "defined") == 0) {
Py_DECREF(Callee);
if (!P->InCIfCond) {
PyErr_Format(PyExc_SyntaxError,
"'defined(NAME)' is only allowed inside a 'cif' / 'celif' condition, at line %d",
Start->Line);
Py_DECREF(Node);
return NULL;
}
PyObject *Args = PyObject_GetAttrString(Node, "args");
PyObject *Kw = PyObject_GetAttrString(Node, "keywords");
PyObject *Arg0 = NULL;
if (Args != NULL && Kw != NULL && PyList_Check(Args) && PyList_Check(Kw)
&& PyList_GET_SIZE(Args) == 1 && PyList_GET_SIZE(Kw) == 0) {
Arg0 = PyList_GetItem(Args, 0); /* borrowed */
}
if (Arg0 == NULL) {
PyErr_Format(PyExc_SyntaxError,
"'defined' expects exactly one macro name (e.g. defined(MAX_SIZE)), at line %d",
Start->Line);
Py_XDECREF(Args);
Py_XDECREF(Kw);
Py_DECREF(Node);
return NULL;
}
PyObject *Marker = MakeCifMarkerCall(P, "@CIfdef", Arg0, Start);
Py_XDECREF(Args);
Py_XDECREF(Kw);
Py_DECREF(Node);
return Marker;
}
Py_DECREF(Callee);
return Node;
}
static PyObject *ParseSliceList(Parser *P);
static PyObject *ParseSliceItem(Parser *P);
@@ -4324,6 +4495,12 @@ static PyObject *ParseSimpleStmt(Parser *P);
static PyObject *ParseSuite(Parser *P);
static PyObject *ParseSuiteOwned(Parser *P, int OwnerIsClass);
static PyObject *ParseIf(Parser *P);
static PyObject *ParseCIf(Parser *P);
static PyObject *ParseCelif(Parser *P);
static PyObject *ParseCIfChainTail(Parser *P);
static PyObject *MakeCifMarkerCall(Parser *P, const char *AttrName, PyObject *Expr, Token *Start);
static int IsCIfStatementStart(Parser *P);
static PyObject *DesugarSpecialCall(Parser *P, PyObject *Node, Token *Start);
static PyObject *ParseWhile(Parser *P);
static PyObject *ParseFor(Parser *P, int IsAsync);
static PyObject *ParseTry(Parser *P);
@@ -4437,6 +4614,22 @@ static PyObject *ParseStatement(Parser *P) {
if (TokIsName(T, "struct")) {
return ParseStructDef(P, NULL);
}
/* cif 编译期条件编译(软关键字):语句起始 + 非赋值/注解形态 */
if (TokIsName(T, "cif") && IsCIfStatementStart(P)) {
return ParseCIf(P);
}
/* celif / celse 必须属于某条 cif 链(由 ParseCIf 直接消费);
* 语句起始独立出现 ⇒ FailFast */
if (TokIsName(T, "celif")) {
SyntaxErrorAt(P, T,
"'celif' must belong to a 'cif' chain (no matching 'cif')");
return NULL;
}
if (TokIsName(T, "celse")) {
SyntaxErrorAt(P, T,
"'celse' must belong to a 'cif' chain (no matching 'cif')");
return NULL;
}
if (TokIsName(T, "if")) {
return ParseIf(P);
}
@@ -6023,6 +6216,227 @@ static PyObject *ParseIf(Parser *P) {
return Node;
}
/* ===================== cif / celif / celse 编译期条件编译 =====================
* 软关键字:语句起始 NAME('cif') 后非赋值/注解形态 ⇒ cif 语句。
* 解析期 desugar 为既有「If + c.@CIf 标记调用」形状(与纯 Python / Viper 侧一致):
* cif E: → If(test=c.@CIf(E), body, orelse)
* celif E: → 同链下一个 If(test=c.@CIf(E), ...)(挂在上一支 orelse)
* celse: → 普通 orelse
* 标记 c.@CIf / c.@CIfdef 由本解析器合成(属性名前导 '@' 词法器不可产出),
* 用户写 c.CIf 在表达式处 FailFast。 */
/* cif 语句起始判定:第二 token 为 '=' ':' 或增强赋值 ⇒ 非 cif 语句(cif = 5)。 */
static int IsCIfStatementStart(Parser *P) {
Token *N = PeekTok(P, 1);
if (N->Kind != TK_OP) {
return 1;
}
static const char *Ops[] = {
"=", ":", "+=", "-=", "*=", "/=", "//=", "%=", "**=",
"<<=", ">>=", "&=", "|=", "^=", "@=", NULL
};
for (int I = 0; Ops[I] != NULL; I++) {
if (TokIsOp(N, Ops[I])) {
return 0;
}
}
return 1;
}
/* 构造内部标记调用 `c.<AttrName>(Expr)`(用户不可写)。 */
static PyObject *MakeCifMarkerCall(Parser *P, const char *AttrName, PyObject *Expr, Token *Start) {
PyObject *Load1 = MakeCtx(P, "Load");
PyObject *IdC = Load1 ? PyUnicode_FromString("c") : NULL;
if (Load1 == NULL || IdC == NULL) {
Py_XDECREF(Load1);
Py_XDECREF(IdC);
return NULL;
}
PyObject *NameArgv[2] = { IdC, Load1 };
PyObject *NameNode = NewNode(P, "Name", NameArgv, 2);
Py_DECREF(IdC);
Py_DECREF(Load1);
if (NameNode == NULL) {
return NULL;
}
FixLoc(P, NameNode, Start);
PyObject *Load2 = MakeCtx(P, "Load");
PyObject *AttrStr = Load2 ? PyUnicode_FromString(AttrName) : NULL;
if (Load2 == NULL || AttrStr == NULL) {
Py_XDECREF(Load2);
Py_XDECREF(AttrStr);
Py_DECREF(NameNode);
return NULL;
}
PyObject *AttrArgv[3] = { NameNode, AttrStr, Load2 };
PyObject *AttrNode = NewNode(P, "Attribute", AttrArgv, 3);
Py_DECREF(AttrStr);
Py_DECREF(Load2);
Py_DECREF(NameNode);
if (AttrNode == NULL) {
return NULL;
}
FixLoc(P, AttrNode, Start);
PyObject *Args = NewList();
PyObject *Kw = NewList();
if (Args == NULL || Kw == NULL) {
Py_XDECREF(Args);
Py_XDECREF(Kw);
Py_DECREF(AttrNode);
return NULL;
}
PyList_Append(Args, Expr);
PyObject *CallArgv[3] = { AttrNode, Args, Kw };
PyObject *CallNode = NewNode(P, "Call", CallArgv, 3);
Py_DECREF(Args);
Py_DECREF(Kw);
Py_DECREF(AttrNode);
if (CallNode == NULL) {
return NULL;
}
FixLoc(P, CallNode, Start);
return CallNode;
}
/* 解析 cif/celif 条件表达式:期间允许 defined(NAME) 谓词。 */
static PyObject *ParseCIfCondition(Parser *P) {
int Prev = P->InCIfCond;
P->InCIfCond = 1;
PyObject *E = ParseExpr(P);
P->InCIfCond = Prev;
return E;
}
/* `cif <expr>:` + 可选 celif/celse 链 */
static PyObject *ParseCIf(Parser *P) {
Token *Start = NextTok(P); /* cif */
PyObject *Test = ParseCIfCondition(P);
if (Test == NULL) {
return NULL;
}
if (!ExpectOp(P, ":")) {
Py_DECREF(Test);
return NULL;
}
PyObject *Body = ParseSuite(P);
if (Body == NULL) {
Py_DECREF(Test);
return NULL;
}
PyObject *Marker = MakeCifMarkerCall(P, "@CIf", Test, Start);
Py_DECREF(Test);
if (Marker == NULL) {
Py_DECREF(Body);
return NULL;
}
PyObject *Orelse = ParseCIfChainTail(P);
if (Orelse == NULL) {
Py_DECREF(Marker);
Py_DECREF(Body);
return NULL;
}
PyObject *Argv[3] = { Marker, Body, Orelse };
PyObject *Node = NewNode(P, "If", Argv, 3);
Py_DECREF(Marker);
Py_DECREF(Body);
Py_DECREF(Orelse);
if (Node != NULL) {
FixLoc(P, Node, Start);
}
return Node;
}
/* `celif <expr>:`(cif 链的后续分支) */
static PyObject *ParseCelif(Parser *P) {
Token *Start = NextTok(P); /* celif */
PyObject *Test = ParseCIfCondition(P);
if (Test == NULL) {
return NULL;
}
if (!ExpectOp(P, ":")) {
Py_DECREF(Test);
return NULL;
}
PyObject *Body = ParseSuite(P);
if (Body == NULL) {
Py_DECREF(Test);
return NULL;
}
PyObject *Marker = MakeCifMarkerCall(P, "@CIf", Test, Start);
Py_DECREF(Test);
if (Marker == NULL) {
Py_DECREF(Body);
return NULL;
}
PyObject *Orelse = ParseCIfChainTail(P);
if (Orelse == NULL) {
Py_DECREF(Marker);
Py_DECREF(Body);
return NULL;
}
PyObject *Argv[3] = { Marker, Body, Orelse };
PyObject *Node = NewNode(P, "If", Argv, 3);
Py_DECREF(Marker);
Py_DECREF(Body);
Py_DECREF(Orelse);
if (Node != NULL) {
FixLoc(P, Node, Start);
}
return Node;
}
/* cif 链尾部(celif/celse)→ orelse 列表;错配 ⇒ FailFast(静态报文)。 */
static PyObject *ParseCIfChainTail(Parser *P) {
if (AtName(P, "celif")) {
PyObject *Nested = ParseCelif(P);
if (Nested == NULL) {
return NULL;
}
PyObject *L = NewList();
if (L == NULL) {
Py_DECREF(Nested);
return NULL;
}
PyList_Append(L, Nested);
Py_DECREF(Nested);
return L;
}
if (AtName(P, "celse")) {
NextTok(P);
if (!ExpectOp(P, ":")) {
return NULL;
}
PyObject *Body = ParseSuite(P);
if (Body == NULL) {
return NULL;
}
if (AtName(P, "celif")) {
SyntaxErrorAt(P, PeekTok(P, 0),
"'celif' cannot follow 'celse' (the 'celse' branch must be the last of the 'cif' chain)");
Py_DECREF(Body);
return NULL;
}
if (AtName(P, "celse")) {
SyntaxErrorAt(P, PeekTok(P, 0),
"'celse' cannot follow 'celse' (the 'celse' branch must be the last of the 'cif' chain)");
Py_DECREF(Body);
return NULL;
}
return Body;
}
if (AtName(P, "elif")) {
SyntaxErrorAt(P, PeekTok(P, 0),
"a 'cif' chain must be closed with 'celse', not 'elif'");
return NULL;
}
if (AtName(P, "else")) {
SyntaxErrorAt(P, PeekTok(P, 0),
"a 'cif' chain must be closed with 'celse', not 'else'");
return NULL;
}
return NewList();
}
/* while / else */
static PyObject *ParseWhile(Parser *P) {
Token *Start = NextTok(P); /* while */
@@ -7265,15 +7679,16 @@ static PyObject *VastC_Parse(PyObject *Self, PyObject *Args, PyObject *Kwargs) {
P.FStringExprEnd = 0;
P.InTypeAnnotation = 0;
P.InClassBody = 0;
P.InCIfCond = 0;
P.PtrSuffixTail = NULL;
PyObject *Tree = ParseModule(&P);
PyMem_Free(Tokens);
PyMem_Free(LineStarts);
if (Tree == NULL) {
if (P.Error) {
/* 报错必须自足可定位:带上 类型名 + token 原文 + 行号,措辞与
* Python ExprParser 的 `unexpected token OP('*') at line N` 一致,
* 这样 `[vast] C 解析器失败,回退纯 Python:<文件>: …` 一行即可定位。 */
* 这样 `[vast] C 解析器失败,回退纯 Python:<文件>: …` 一行即可定位。
* 注意:P.ErrTok 指向 Tokens 数组内部,必须在 PyMem_Free 之前读取
* (否则读到 free 链表指针,首个 token 会被写成 ENDMARKER(''))。 */
char TokBuf[64];
const char *TokName = "ENDMARKER";
TokBuf[0] = '\0';
@@ -7296,8 +7711,12 @@ static PyObject *VastC_Parse(PyObject *Self, PyObject *Args, PyObject *Kwargs) {
P.ErrMsg ? P.ErrMsg : "vast_c: syntax error",
TokName, TokBuf, P.ErrLine);
}
PyMem_Free(Tokens);
PyMem_Free(LineStarts);
return NULL;
}
PyMem_Free(Tokens);
PyMem_Free(LineStarts);
if (strcmp(Mode, "exec") == 0) {
return Tree;
}