diff --git a/_dict.vp b/_dict.vp index fa221a2..bd7256b 100644 --- a/_dict.vp +++ b/_dict.vp @@ -29,7 +29,7 @@ class dict: d: dict = dict(mb) d.set_int("x", 42) p: t.CPtr = d["x"] - v: int = c.Deref((int*)(p)) # int 值按 i32 存,读回须给 int* 视图 + v: int = c.Deref((int*)(p)) # int value stored as i32; read it back with an int* view # Nested dict inner: dict = dict(mb) @@ -60,11 +60,8 @@ class dict: def _find(self, key: str) -> sizet: """Find index of target key; returns __count__ if not found""" for i in range(self.__count__): - # 元素地址:`T*(base + byte_offset)`。裸指针 rawptr 即 i8*,其上的 `+` - # 按 **1 字节**缩放 ⇒ 与原先 `uint64(base) + n` 再强转回指针逐字等价, - # 故去掉整数往返(与 _list.vp 同口径)。 - # 下面 __getitem__/_set_variant_raw/get/get_vtype/__next__/to_json 同此式, - # 不再重复注释。 + # Element address: `T*(base + byte_offset)`. rawptr is i8* and `+` scales by 1 byte, byte-identical to the old uint64(base)+n form (no integer round-trip). + # Same form below (__getitem__/_set_variant_raw/get/get_vtype/__next__/to_json), not annotated again. key_ptr: str* = str*(self.__keys__ + i * 8) if string.strcmp(key_ptr[0], key) == 0: return i return self.__count__ diff --git a/_list.vp b/_list.vp index 0c33723..1688cc9 100644 --- a/_list.vp +++ b/_list.vp @@ -47,14 +47,8 @@ class list[T]: string.memcpy(new_data, self.__data__, self.__count__ * self.__elem_size__) self.__data__ = new_data self.__capacity__ = new_cap - # 元素地址:`T*(self.__data__ + i * elem_size)`。 - # · rawptr 就是 i8*,i8* 上的 `+` 按 **1 字节**缩放(ExprGenOps 的指针算术分流 - # → ExprGenBase.translate_ptr_arith 按 pointee 大小缩放)⇒ 与原先 - # `uint64(ptr) + n` 再强转回指针**逐字等价**,故去掉整数往返。 - # · 步长仍由 __elem_size__ **显式**给出,不能改成下标访问(`__data__[i]` 会按 - # sizeof(T) 缩放):list[int](pool, 8) / list[str](pool, 64) 这类"按显式步长 - # 当字节缓冲"的用法依赖它。 - # 下面 get/set/__getitem__/__setitem__/pop/__next__ 同此式,不再重复注释。 + # Element address: `T*(self.__data__ + i * elem_size)`. rawptr is i8* and `+` scales by 1 byte, byte-identical to the old uint64(ptr)+n form (no integer round-trip). + # The stride is explicit via __elem_size__; do not use indexing (`__data__[i]` scales by sizeof(T)) since list[int](pool, 8) / list[str](pool, 64) rely on the explicit byte stride. Same form below (get/set/__getitem__/__setitem__/pop/__next__). elem_ptr: T* = T*(self.__data__ + self.__count__ * self.__elem_size__) *elem_ptr = item self.__count__ += 1 diff --git a/_variant.vp b/_variant.vp index 9c425c0..7e20366 100644 --- a/_variant.vp +++ b/_variant.vp @@ -23,7 +23,7 @@ class Variant: # Retrieve value ptr: rawptr = v.ptr - i: int = *((int*)(ptr)) # int 值按 i32 存,读回须给 int* 视图 + i: int = *((int*)(ptr)) # int values are stored as i32, so read them back through an int* view """ vtype: int ptr: rawptr diff --git a/_withcontent.vp b/_withcontent.vp index f8dbd5c..7783d6f 100644 --- a/_withcontent.vp +++ b/_withcontent.vp @@ -27,9 +27,8 @@ _tls_index: ulong = 0 def _init_tls(): """Initialize TLS index (executes once; subsequent calls are skipped).""" global _tls_index - # 双保险:TPV 已正式实现 @t.TLS(函数级一次性守卫,首次执行后置 - # __tls_init_done 标志,后续调用直接返回),此判重守卫保留以兼容 - # 尚未重建的旧 TPV.exe。_tls_index 初始为 0(TlsAlloc 成功不会返回 0)。 + # Double guard: TPV now implements @t.TLS (a per-function one-shot guard setting __tls_init_done), but this re-check is kept for compatibility with older TPV.exe builds. + # _tls_index starts at 0 (TlsAlloc never returns 0 on success). if _tls_index == 0: _tls_index = TlsAlloc() @@ -67,11 +66,8 @@ def _find_provider(field_name: str) -> rawptr: """Search for matching field_name starting from stack top. Return associated pointer, or NULL if no match found.""" stack: _ProviderStack* = _get_stack() - # 上界显式转 t.CInt(有符号):本循环是「递减 + stop 为 -1」形态,若上界 - # 被当作无符号(stack.top 是 uint64 字段),比较谓词会退化为 ugt,而 -1 在 - # 无符号域是 UINT32_MAX ⇒ 条件恒假、循环体不可达、本函数恒返回 NULL(with 的 - # __mbuddy__ 注入全部失效,_str.upper 对 NULL 取 vtable 崩溃)。显式转换让 - # 符号域与递减语义一致,与编译器侧「stop 为负字面量时退回有符号谓词」互为双保险。 + # Upper bound explicitly cast to t.CInt (signed): the loop is "decrement, stop at -1". If the bound were unsigned (stack.top is a uint64 field), the predicate would degrade to ugt and -1 (= UINT32_MAX unsigned) would make it always false, so the loop body is unreachable and this function always returns NULL (all with-injections break, and _str.upper crashes taking a vtable from NULL). + # The explicit cast keeps the signed domain consistent with the decrement semantics. for i in range(int(stack.top) - 1, -1, -1): # Content comparison: every string literal gets its own constant, so the # push/find pointers differ and pointer equality never matches. diff --git a/argparse.vp b/argparse.vp index 5f46d9e..3a48287 100644 --- a/argparse.vp +++ b/argparse.vp @@ -284,9 +284,8 @@ class ArgumentParser: arg: Argument* = None value: str = None - # argv 是 char**(`str | t.CPtr`),argv[idx] 直接给出该位置的 char*。 - # 不要写成 `str*(uint64(argv) + idx*8)`:那样右值是 i8**,TPC 的赋值路径 - # 会对"指针的指针"右值再解引用一次(多 load 一层)→ 取到野指针。 + # argv is char** (`str | t.CPtr`); argv[idx] directly gives the char* at that position. + # Do NOT write `str*(uint64(argv) + idx*8)`: the right-hand side would be i8**, and TPC's assignment path dereferences a "pointer to pointer" rvalue one extra time (an extra load) -> a wild pointer. while idx < argc: token = argv[idx] if token is None: @@ -434,9 +433,8 @@ def release(args: rawptr[ParsedArgs]): if args is None: return if args.__mbuddy__ is None: return if args._ht is not None: - # 用临时变量拆分链式属性访问,避免 args._ht.__slots__ 跨模块类型推断失败 - # 注意:类型注解必须用 hashtable.HashTable(带模块前缀), - # 否则编译器无法解析 HashTable 类型,导致 ht.__slots__ 属性访问失败 + # Split the chained attribute access with a temp variable to avoid cross-module type inference failure for args._ht.__slots__. + # Note: the type annotation must be hashtable.HashTable (module-prefixed), otherwise the compiler cannot resolve HashTable and ht.__slots__ access fails. ht: hashtable.HashTable* = args._ht if ht.__slots__ is not None: args.__mbuddy__.free(ht.__slots__) diff --git a/ast/__init__.vp b/ast/__init__.vp index 43db1fd..5124876 100644 --- a/ast/__init__.vp +++ b/ast/__init__.vp @@ -98,7 +98,7 @@ def parse_expression(src: str, pool: rawptr[memhub.MemManager]) -> rawptr[Expres pool: memhub.MemManager instance, to allocate AST nodes Returns: - Expression node (包装节点,body 字段为实际表达式), or None + Expression node (wrapper; body holds the actual expression), or None """ if src is None or pool is None: return None _init_tables(pool) diff --git a/ast/base.vp b/ast/base.vp index 380c918..01f740e 100644 --- a/ast/base.vp +++ b/ast/base.vp @@ -42,7 +42,7 @@ enum ASTKind: MatchCase, MatchValue MatchSingleton, MatchSequence, MatchMapping MatchClass, MatchStar, MatchAs, MatchOr - # Soft keyword statement(append 在末尾以保持既有枚举值不变) + # Soft keyword statement (appended last to keep existing enum values unchanged) FallThrough @@ -364,26 +364,23 @@ virtual class AST: # ============================================================ # Stmt - Statement Base Class # -# 所有语句节点(Assign/If/For/While/Return/...)继承 Stmt。 -# isinstance(node, ast.Stmt) 可在运行时区分"语句"与"表达式" -# (依赖 TPV isinstance 子类集合判定 + 三层 vtable 继承)。 -# 不 override kind()(继承 AST 的 0),具体节点各自返回 ASTKind 值。 +# All statement nodes (Assign/If/For/While/Return/...) inherit Stmt. +# isinstance(node, ast.Stmt) distinguishes statements from expressions at runtime. +# Does not override kind() (inherits AST's 0); concrete nodes return their ASTKind. # ============================================================ virtual class Stmt(AST): - """语句节点基类(Assign/If/For/While/Return/...)""" + """Statement node base class (Assign/If/For/While/Return/...)""" # ============================================================ # ExprNode - Expression Base Class # -# 所有表达式节点(Name/Constant/Call/BinOp/...)继承 ExprNode。 -# isinstance(node, ast.ExprNode) 可在运行时区分"表达式"与"语句"。 -# -# 命名说明:不能叫 Expr——ASTKind.Expr(base.vp L23)与 stmts.vp 的 -# Expr 类已是"表达式语句"(Expression Statement)的既有语义。 +# All expression nodes (Name/Constant/Call/BinOp/...) inherit ExprNode. +# isinstance(node, ast.ExprNode) distinguishes expressions from statements at runtime. +# Not named Expr: ASTKind.Expr and the stmts.vp Expr class already mean expression statement. # ============================================================ virtual class ExprNode(AST): - """表达式节点基类(Name/Constant/Call/BinOp/...)""" + """Expression node base class (Name/Constant/Call/BinOp/...)""" # ============================================================ diff --git a/ast/exprs.vp b/ast/exprs.vp index 84f263b..1b7aa32 100644 --- a/ast/exprs.vp +++ b/ast/exprs.vp @@ -51,25 +51,25 @@ class Name(ExprNode): def name_get_id(node: rawptr[Name]) -> str: - """获取 Name.id 字段(跨模块访问器,stub 字段访问陷阱防护)""" + """Get Name.id field (cross-module accessor; guards against stub field-access pitfalls)""" if node is None: return None return node.id def constant_get_str_val(node: rawptr[Constant]) -> str: - """获取 Constant.str_val 字段(跨模块访问器)""" + """Get Constant.str_val field (cross-module accessor)""" if node is None: return None return node.str_val def binop_get_left(node: rawptr[BinOp]) -> rawptr[AST]: - """获取 BinOp.left 字段(跨模块访问器)""" + """Get BinOp.left field (cross-module accessor)""" if node is None: return None return node.left def binop_get_right(node: rawptr[BinOp]) -> rawptr[AST]: - """获取 BinOp.right 字段(跨模块访问器)""" + """Get BinOp.right field (cross-module accessor)""" if node is None: return None return node.right @@ -284,9 +284,8 @@ class Call(ExprNode): func: rawptr[AST] args: rawptr[list[rawptr[AST]]] keywords: rawptr[list[rawptr[AST]]] - # 值位置 `volatile f()` / `volatile S()` 前缀标记(见 parser.vp _IsVolatileCallPrefix): - # 1=本次调用的结果槽(赋值目标/临时槽)按 volatile 存取,结构体构造的写入亦带 - # LLVM volatile。追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入。 + # Value-position `volatile f()` / `volatile S()` prefix marker (see _IsVolatileCallPrefix): + # 1 = result/temp slot accessed volatile (struct writes too). Appended last: offsets unchanged. IsVolatile: int def __new__(self, pool: rawptr[memhub.MemManager], @@ -931,20 +930,17 @@ class Attribute(ExprNode): # ============================================================ -# 字段访问器(跨模块 stub 字段访问陷阱防护) -# -# 其他模块(如 TypeGenCType)直接访问 ast.Attribute 的字段时, -# TPC 可能按 stub 类型生成错误 GEP(读垃圾地址 → 0xC0000005 崩溃)。 -# 本模块是完整类型,字段偏移正确;跨模块调用方用这些访问器。 +# Field accessors (guard against cross-module stub field-access pitfalls): other modules reading +# ast.Attribute fields directly may get a wrong stub GEP and crash, so they use these accessors. # ============================================================ def attribute_get_value(node: rawptr[Attribute]) -> rawptr[AST]: - """获取 Attribute.value 字段(跨模块访问器)""" + """Get Attribute.value field (cross-module accessor)""" if node is None: return None return node.value def attribute_get_attr(node: rawptr[Attribute]) -> str: - """获取 Attribute.attr 字段(跨模块访问器)""" + """Get Attribute.attr field (cross-module accessor)""" if node is None: return None return node.attr diff --git a/ast/lexer.vp b/ast/lexer.vp index fd9d032..b57f34b 100644 --- a/ast/lexer.vp +++ b/ast/lexer.vp @@ -37,25 +37,25 @@ _g_lex_err_msg: str = None def get_lex_err_line() -> int: - """最近一次词法层 fail-fast 的行号(0=无) + """Line number of the latest lexer fail-fast (0 = none). - 供 parser.parse_tokens 取出后转写解析错误通道(_g_parse_err_*)。 + Consumed by parser.parse_tokens and forwarded to the parse error channel. """ return _g_lex_err_line def get_lex_err_col() -> int: - """最近一次词法层 fail-fast 的列号(0-based;-1 表示无列信息)""" + """Column of the latest lexer fail-fast (0-based; -1 = no column info)""" return _g_lex_err_col def get_lex_err_msg() -> str: - """最近一次词法层 fail-fast 的消息(含字面量原文 / 超出的位宽 / 上限 / 修法建议)""" + """Message of the latest lexer fail-fast (literal / required width / limit / fix hint)""" return _g_lex_err_msg def _lex_reset_err(): - """清空词法层 fail-fast 三元组(每次 tokenize 入口调用,避免跨文件串用旧错误)""" + """Clear the lexer fail-fast triple (called at each tokenize entry to avoid stale errors)""" global _g_lex_err_line, _g_lex_err_col, _g_lex_err_msg _g_lex_err_line = 0 _g_lex_err_col = 0 @@ -63,10 +63,8 @@ def _lex_reset_err(): def _lex_record_overflow(text: str, lineno: int, col: int): - """记录整数字面量溢出(fail-fast;只记首次)。 - - 报文含:字面量原文 + **超出的位宽(至少 65 位,上限 2^64-1)** + 修法建议; - 文件:行:列 与源码行由调用方(parser.parse_tokens → dump_parse_traceback)补全。 + """Record an integer literal overflow (fail-fast; first record only); message carries literal + + required width (>= 65 bits, limit 2^64-1) + fix hint (file:line:col filled later by parse_tokens). """ global _g_lex_err_line, _g_lex_err_col, _g_lex_err_msg if _g_lex_err_line != 0: @@ -88,25 +86,16 @@ def _lex_record_overflow(text: str, lineno: int, col: int): def _lex_int_exceeds_u64(text: str) -> int: - """整数字面量文本的幅值是否超出 64 位位模式上限 2^64-1(1=超出,0=未超出)。 - - 纯文本判定(与 AssignGenMixin._int_text_exceeds_64 同口径;**不落进任何定宽 - 容器、不回绕后再比较**)。只处理整数字面量(调用方保证 is_float==0);字面量 - 文本不含正负号(一元加/减是独立 token,不属于字面量本身)。 - - 十进制:去 '_' 与前导零后,与 '18446744073709551615'(2^64-1,20 位)按 - 长度 + 字典序比较(同位数时字典序 = 数值序)。 - 0x/0X :有效位数 > 16 ⇒ 超出(16 位十六进制的最大值为 2^64-1)。 - 0o/0O :有效位数 > 22 ⇒ 超出;== 22 位且首位 > '1' ⇒ 超出 - (8^21 = 2^63 ⇒ 22 位以 '1' 开头恰为 2^64-1 为上限)。 - 0b/0B :有效位数 > 64 ⇒ 超出。 + """True if the integer literal magnitude exceeds the 64-bit limit 2^64-1 (1 = yes, 0 = no). + Pure text compare, no wrap-around: decimal length+lexicographic vs "18446744073709551615", + 0x > 16, 0o > 22 (or 22 leading > '1'), 0b > 64 (literal text carries no sign). """ if text is None: return 0 tl: sizet = string.strlen(text) if tl == 0: return 0 - # 进制判定(前缀大小写皆可) + # Base detection (prefix letter case-insensitive) base: int = 10 start: sizet = 0 if tl >= 2 and text[0] == '0': @@ -120,7 +109,7 @@ def _lex_int_exceeds_u64(text: str) -> int: elif pfx == 'b' or pfx == 'B': base = 2 start = 2 - # 统计有效位数(跳过 '_' 与前导零),并记下首个有效位 + # Count significant digits (skip '_' and leading zeros) and remember the first one sig: int = 0 first: char = '0' j: sizet = start @@ -146,11 +135,11 @@ def _lex_int_exceeds_u64(text: str) -> int: break if sig == 0: if ch == '0': - continue # 前导零不计 + continue # leading zeros not counted first = ch sig += 1 if sig == 0: - return 0 # 值 0 + return 0 # value 0 if base == 16: return 1 if sig > 16 else 0 if base == 2: @@ -161,13 +150,13 @@ def _lex_int_exceeds_u64(text: str) -> int: if sig == 22 and first > '1': return 1 return 0 - # 十进制:有效位数与 2^64-1 的文本长度比较 + # Decimal: compare significant-digit count against the 2^64-1 text length mlen: sizet = 20 if sig > mlen: return 1 if sig < mlen: return 0 - # 等长(20 位):逐位与 '18446744073709551615' 字典序比较 + # Equal length (20 digits): lexicographic compare against '18446744073709551615' max_text: str = "18446744073709551615" d: sizet = 0 k: sizet = start @@ -193,17 +182,15 @@ def _lex_int_exceeds_u64(text: str) -> int: def _lex_mag_digit_val(ch: char, base: int) -> int: - """数字字符 → 位值(十六进制含 a-f/A-F;其余进制为 '0'..基-1)。""" + """Digit char -> positional value (hex accepts a-f/A-F; other bases are '0'..base-1).""" if base == 16: return _hex_val(ch) return int(ch) - 48 def _lex_mag_gt_text(text: str, base: int, start: sizet, sig: int, max_text: str) -> int: - """整数字面量文本的有效数字是否 > 同位数上限文本(1=大于,0=不大于)。 - - 两侧均跳过 '_' 与前导零,逐位按**位值**比较(十六进制用 _hex_val,其余进制 - 字符序即位值序);纯文本判定,不落定宽容器、不回绕。 + """True if the literal's significant digits exceed the same-length max text (1 = yes). + Both sides skip '_'/leading zeros, compared by positional value (hex via _hex_val); pure text. """ d: sizet = 0 k: sizet = start @@ -240,16 +227,9 @@ def _lex_mag_gt_text(text: str, base: int, start: sizet, sig: int, max_text: str def _lex_int_neg_exceeds_i64(text: str) -> int: - """整数字面量幅值是否**严格大于** 2^63(1=超出,0=未超出)。 - - 用于"一元减 + 整数字面量"的联合越界判定:`-` 是独立的一元运算符、不属于 - 字面量,故幅值合法的字面量(≤ 2^64-1,`_lex_int_exceeds_u64` 已放行)在取负 - 后可能 < -2^63。幅值恰为 2^63 时一元减结果 = INT64_MIN = -9223372036854775808 - (合法),故本判据用**严格大于**。 - - 纯文本判定(与 _lex_int_exceeds_u64 同口径,不落定宽容器、不回绕)。 - 2^63 的幅值文本:十进制 19 位 `9223372036854775808`;0x 16 位 - `8000000000000000`;0o 22 位 `1000000000000000000000`;0b 64 位(1 + 63 个 0)。 + """True if the magnitude is strictly greater than 2^63 (1 = yes). + For unary-minus + literal overflow: a magnitude-valid literal may become < -2^63 after + negation; exactly 2^63 is legal (INT64_MIN). Pure text compare, no wrap-around. """ if text is None: return 0 @@ -292,10 +272,10 @@ def _lex_int_neg_exceeds_i64(text: str) -> int: if ok_digit == 0: break if sig == 0 and ch == '0': - continue # 前导零不计 + continue # leading zeros not counted sig += 1 if sig == 0: - return 0 # 值 0 + return 0 # value 0 if base == 16: if sig > 16: return 1 if sig < 16: return 0 @@ -339,7 +319,7 @@ class Lexer: def __new__(self, pool: rawptr[memhub.MemManager]): ptr: rawptr[Lexer] = pool.alloc(Lexer.__sizeof__()) if ptr == None: - # 池耗尽时不再静默返回 NULL(下游会把 NULL 当有效 Lexer 用 → 崩在任意位置) + # Pool exhausted: fail-fast instead of returning NULL (downstream would treat NULL as a valid Lexer) stdio.printf("[AST] Lexer 分配失败:内存池已耗尽(need=%zu 字节)\n", sizet(Lexer.__sizeof__())) stdio.fflush(None) @@ -437,7 +417,7 @@ def _is_hex(ch: char) -> int: def _hex_val(ch: char) -> int: - """十六进制字符转数值 0-15(供 \xNN 转义解析)""" + """Hex char -> value 0-15 (for \xNN escape parsing)""" if ch >= '0' and ch <= '9': return int(ch) - 48 if ch >= 'a' and ch <= 'f': return int(ch) - 87 if ch >= 'A' and ch <= 'F': return int(ch) - 55 @@ -723,8 +703,8 @@ def _emit_number(lx: rawptr[Lexer], start: sizet, if is_float: tok.float_val = string.atof(tok.str_val) else: - # 整数字面量幅值 > 2^64-1:词法层 fail-fast(纯文本判定,不静默回绕)。 - # 合法区间 0..2^64-1 仍按 64 位位模式存入 int64(产物形态不变)。 + # Integer literal magnitude > 2^64-1: lexer fail-fast (pure text, no silent wrap). + # Range 0..2^64-1 is still stored into int64 as a 64-bit bit pattern (unchanged shape). if _lex_int_exceeds_u64(tok.str_val) != 0: _lex_record_overflow(tok.str_val, start_lineno, start_col) tok.int_val = string.atoll(tok.str_val) @@ -794,7 +774,7 @@ def _read_string(lx: rawptr[Lexer]): # Read string content content_buf_start: sizet = lx.pos - # f-string {} 表达式块嵌套深度(PEP 701 同引号嵌套字符串支持用) + # f-string {} expression block nesting depth (for PEP 701 same-quote nested strings) fstr_depth: int = 0 while lx.pos < lx.len: ch = lx.src[lx.pos] @@ -822,7 +802,7 @@ def _read_string(lx: rawptr[Lexer]): lx.col += 1 else: if is_fstring: - # {{ 与 }} 是转义字面量,不参与深度计数 + # {{ and }} are escaped literals, not counted in depth if ch == '{' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '{': lx.pos += 2 lx.col += 2 @@ -843,8 +823,8 @@ def _read_string(lx: rawptr[Lexer]): lx.col += 1 continue if fstr_depth > 0 and (ch == '\'' or ch == '"'): - # {} 表达式块内允许嵌套字符串字面量(PEP 701 同引号嵌套): - # 跳过到匹配引号,块内引号不终止外层 f-string。 + # Nested string literals allowed inside {} blocks (PEP 701 same-quote nesting): + # skip to the matching quote; inner quotes do not terminate the outer f-string. nq: char = ch lx.pos += 1 lx.col += 1 @@ -859,7 +839,7 @@ def _read_string(lx: rawptr[Lexer]): lx.col += 1 break if c2 == '\n': - # 单行字符串不能跨行 + # single-line string cannot span lines lx.error_count += 1 return lx.pos += 1 @@ -938,10 +918,8 @@ def _read_string(lx: rawptr[Lexer]): elif next_ch == '0': conv_buf[wpos] = '\0' elif next_ch == 'x' and rpos + 2 < content_len: - # \xNN 十六进制转义(对齐 Python):'\\x1b' → 单字节 0x1B。 - # 此前当作未知转义保留('\\'+'x' 2 字节)→ esc_len≠1 → - # '\x1b' 被判为 CONST_STR 而非 CONST_CHAR(27) → 字符数组 - # 元素提取失败(desktop _ps2_set1 索引 1 恒 0 根因)。 + # \xNN hex escape (matches Python): '\\x1b' -> single byte 0x1B. + # Must yield esc_len == 1 so '\xNN' becomes CONST_CHAR, not CONST_STR. h1: char = lx.src[content_buf_start + rpos + 1] h2: char = lx.src[content_buf_start + rpos + 2] if _is_hex(h1) != 0 and _is_hex(h2) != 0: @@ -950,7 +928,7 @@ def _read_string(lx: rawptr[Lexer]): wpos += 1 rpos += 3 continue - # 非法十六进制('\x' 后不足 2 位 hex):保留原样 + # Invalid hex (\x followed by fewer than 2 hex digits): keep as-is conv_buf[wpos] = '\\' wpos += 1 conv_buf[wpos] = next_ch @@ -1072,9 +1050,8 @@ def tokenize(lx: rawptr[Lexer]) -> rawptr[Token]: continue # Backslash continuation line - # 支持 LF 与 CRLF(Windows 文件行尾):CRLF 下 `\` 后是 '\r'(非 '\n'), - # 只查 '\n' 会失效 → `\` 被当未知字符跳过、'\n' 产生 NEWLINE → 表达式 - # 中断(VpiReader.vp L324 `or \` 行尾续行根因,CRLF 文件)。 + # Support LF and CRLF line endings: under CRLF `\` is followed by '\r' (not '\n'), + # so checking only '\n' fails and the backslash continuation breaks. if ch == '\\' and lx.pos + 1 < lx.len: if lx.src[lx.pos + 1] == '\n': lx.pos += 2 @@ -1082,7 +1059,7 @@ def tokenize(lx: rawptr[Lexer]) -> rawptr[Token]: lx.col = 0 continue if lx.src[lx.pos + 1] == '\r': - # \ + \r\n:跳 \、\r、\n + # \ + \r\n: skip \, \r, \n lx.pos += 2 if lx.pos < lx.len and lx.src[lx.pos] == '\n': lx.pos += 1 diff --git a/ast/parser.vp b/ast/parser.vp index b11c7d4..7e77dd5 100644 --- a/ast/parser.vp +++ b/ast/parser.vp @@ -43,37 +43,32 @@ from .lexer import ( # Parser State # ============================================================ -# token 链表长度上限(成环检测的硬闸):正常源码的 token 数远低于此(200KB 源文件 -# 约数万 token);走到这个数还没到尾即判为链表成环(见 parse_tokens)。 +# Hard cap on the token linked-list length (cycle detection): normal sources have far fewer +# tokens, so reaching this count without the tail means the list is cyclic (see 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)` 紧邻判据) + prev: rawptr[Token] # previous consumed token (for the `T*(x)` adjacency test) pool: rawptr[memhub.MemManager] # memory pool error_count: int # error count pending_decorators: rawptr[list[rawptr[AST]]] # pending decorators list - # 实参位前导 `*` 的已解析左操作数挂起槽(见 _parse_call / _parse_power)。 + # Pending parsed left operand for a leading `*` in argument position (see _parse_call / _parse_power). starred_seed: rawptr[AST] - # `static` 修饰符(软关键字)挂起标记:语句起始消费 `static`(后随变量声明 - # `Name :`)后置 1,由紧随的 `_parse_type_ann` 在类型联合最左插入 t.CStatic - # 标记(`static x: int = 0` → `x: t.CStatic | int = 0`),插入后立即清零, - # 只作用于紧随其后的这一个注解。函数分支不经此通道(改走合成 @t.CStatic - # 装饰器,与 inline def 同构,见 _ParseStaticModified)。 + # `static` modifier (soft keyword) pending flag: set after consuming `static` before a `Name :` + # declaration; the next _parse_type_ann prepends t.CStatic and clears it (see _ParseStaticModified). pending_static: int - # 当前正在解析的套件的**直接拥有者**是否 class(1=类体,0=其它)。 - # 由 _parse_suite_into_children 按 owner_is_class 维护,供 virtual/novirtual - # 修饰符判"方法级修饰符是否直接写在类体内"(见 _ParseVirtualModified)。 + # Whether the suite currently being parsed is directly owned by a class (1 = class body, 0 = other), + # maintained by _parse_suite_into_children for the virtual/novirtual class-body check. in_class_body: int - # 步数预算(_advance 计数 / parse_tokens 预算):语句主循环一旦超预算即 - # fail-fast 报错返回,绝不无限循环(见 parse_tokens 的成环检测)。 + # Step budget (_advance count / parse_tokens budget): the statement loop fails fast once exceeded, + # never looping forever (see the cycle detection in parse_tokens). step_count: sizet step_limit: sizet - # 看门狗:对"当前 token 查询"的计数与收束标志(见 _tick)。 - # tick_count 超 step_limit ⇒ aborted=1,解析各入口改返回收束值, - # 所有 while 循环常数轮内结束(任何原地打转都不可能存活)。 + # Watchdog: counter and convergence flag for "query current token" (see _tick). When tick_count + # exceeds step_limit, aborted=1, entries return converged values and every loop ends in bounded rounds. tick_count: sizet aborted: int @@ -101,31 +96,26 @@ def _parser_init(ps: rawptr[Parser], tokens: rawptr[Token], ps.pending_static = 0 ps.in_class_body = 0 ps.step_count = 0 - # 默认预算取上限(parse_tokens 会按实际 token 数收紧):保证不经过 parse_tokens - # 的入口(parse_expression 等)不会因预算为 0 而误报;同时仍有硬上界防死循环。 + # Default budget is the cap (parse_tokens later tightens it to the real token count) so entries that + # bypass parse_tokens (parse_expression etc.) do not misreport at 0, while a hard bound still prevents loops. ps.step_limit = PARSER_MAX_TOKENS ps.tick_count = 0 ps.aborted = 0 # ============================================================ -# 解析看门狗(静态收口:杜绝"无输出卡死") +# Parser watchdog (static convergence: no silent hang) # -# 任何"原地打转、一个 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 报 -# 文件:行:列 终止编译,**不再存在任何静默卡死路径**。 +# Any loop that spins without consuming a token must repeatedly query the current token, so every query +# entry (_cur_type / _cur_op / _cur_kw / _match_op / _accept_op / _advance) ticks the watchdog; once the +# budget is exceeded it sets aborted, records a parse error and returns a converged value: +# _cur_type -> EndMarker, _cur_op / _cur_kw -> 0, _match_op / _accept_op -> 1 (jump to end), +# _advance -> jump to end. parse_tokens then returns None with error_count > 0 and the caller reports +# file:line:col SyntaxError; there is no silent-hang path left. # ============================================================ def _tick(ps: rawptr[Parser]): - """推进看门狗;超预算即置 aborted 并记录首个解析错误(含当前位置)""" + """Tick the watchdog; over budget sets aborted and records the first parse error (with position)""" if ps is None: return if ps.aborted != 0: return ps.tick_count += 1 @@ -174,21 +164,18 @@ def _cur_kw(ps: rawptr[Parser]) -> int: def _is_funcdef_kw(ps: rawptr[Parser], kw: int) -> int: - """判断当前 token 是否为函数定义关键字(def 及其别名 fn/fun/func/function, - 以及 extern/export 声明关键字) + """Whether the current token is a function-definition keyword (def and its aliases + fn/fun/func/function, plus 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]`)。 + def is a hard keyword. fn/fun/func/function/extern/export are soft: they count only when the next + token is a Name followed by '(' or a generic '['; extern/export also accept ` def A(...)`. + Otherwise they are ordinary identifiers (e.g. the AST `func` field or `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+'(/'[') + # extern/export def A(...) or extern/export A(...) + # (after extern/export comes def or Name + '(' / '[') nxt: rawptr[Token] = ps.cur.next if nxt is None: return 0 if nxt.kw_subtype == Keyword.Def: return 1 @@ -201,8 +188,8 @@ def _is_funcdef_kw(ps: rawptr[Parser], kw: int) -> int: 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) + # Soft-keyword lookahead: next is a Name, the one after is '(' or a generic '[T...]' (PEP 695) + # (the define macro function matches def, with t.CDefine appended to the return annotation) nxt: rawptr[Token] = ps.cur.next if nxt is None: return 0 @@ -219,15 +206,11 @@ def _is_funcdef_kw(ps: rawptr[Parser], kw: int) -> int: def _is_define_const(ps: rawptr[Parser]) -> int: - """define 常量声明的软关键字 lookahead(1=是,0=否)。 + """Soft-keyword lookahead for a define constant declaration (1 = yes, 0 = no). - `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)` 失败 ⇒ 响亮失败(非静默)。 + `define NAME[: T] = `: next is a Name, then '=' (no annotation) or ':' (annotated). + `define NAME(...)` and `define` used as an identifier do not match. `define NAME: T` (missing '=') + does match and then fails loudly in _parse_define_const's _expect_op(Equal). """ if _cur_kw(ps) != Keyword.Define: return 0 nxt: rawptr[Token] = ps.cur.next @@ -239,15 +222,10 @@ def _is_define_const(ps: rawptr[Parser]) -> int: def _is_typedef(ps: rawptr[Parser]) -> int: - """typedef 类型别名声明的软关键字判定(1=是,0=否)。 + """Soft-keyword test for a typedef alias declaration (1 = yes, 0 = no). - `typedef T1 = T2` 与 `T1: t.CTypedef = T2` **完全等价**(同族同风格于 - `enum`/`define`:展开为既有形态,落到同一条 typedef 注册/查找路径)。 - - `typedef` 是**语句级保留字**:全仓冲突普查确认它只在注释/文档字符串中出现, - 没有任何变量/参数/字段/函数/类/模块以其命名 ⇒ 语句起始出现 `typedef` 即按 - 本语法处理;非法形态(`typedef = 1` / `typedef A` / `typedef A = ` 缺 RHS) - 在 `_parse_typedef` 内 `_record_parse_err` **响亮失败**(非静默)。 + `typedef T1 = T2` is fully equivalent to `T1: t.CTypedef = T2`. `typedef` is a statement-level + reserved word (never an identifier in the repo); invalid forms fail loudly in _parse_typedef. """ if _cur_kw(ps) != Keyword.Typedef: return 0 @@ -255,17 +233,10 @@ def _is_typedef(ps: rawptr[Parser]) -> int: def _is_export_extern_var(ps: rawptr[Parser]) -> int: - """`export NAME :` / `extern NAME :` 变量声明的软关键字判定(1=是,0=否)。 + """Soft-keyword test for `export NAME :` / `extern NAME :` variable declarations (1 = yes, 0 = no). - `export` / `extern` 既可用于**函数**声明(`export def f()` / `export f()` / - `extern def f()`,见 `_is_funcdef_kw`),也可用于**变量**声明 - (`export gdt: [7]gdt_entry = []`;`extern a: int`)。二者由第三个 token 区分: - 函数是 `(`(参数表)或 `[`(泛型),变量是 `:`(类型注解)。 - - - `export name(` / `extern def f(`(函数)⇒ 本判据不命中,函数语义逐字不变; - - `export = 5` / `export(x)` / `export.attr`(把 export 当标识符)⇒ 不命中; - - `export NAME:` 后缺类型等非法形态在 `_parse_export_extern_var` 内 - `_record_parse_err` **响亮失败**(非静默)。 + export/extern also serve function declarations (see _is_funcdef_kw); the third token distinguishes + them: '(' or '[' is a function, ':' is a variable. Other forms do not match; invalid forms fail loudly. """ kw: int = _cur_kw(ps) if kw != Keyword.Export and kw != Keyword.Extern: @@ -275,7 +246,7 @@ def _is_export_extern_var(ps: rawptr[Parser]) -> int: return 0 if nxt.type != TokenType.Name: return 0 - # 名字必须是**非关键字**标识符(排除 `export def f()` 之类把关键字当名字的形态) + # The name must be a non-keyword identifier (excludes forms like `export def f()`) if nxt.kw_subtype != Keyword.NotKeyword: return 0 nxt2: rawptr[Token] = nxt.next @@ -288,14 +259,14 @@ def _is_export_extern_var(ps: rawptr[Parser]) -> int: return 1 -# 解析错误位置与消息(fail-fast 仿 Python SyntaxError 报告,首次错误记录) +# Parse error position and message (fail-fast, Python SyntaxError style, first error only) _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]): - """记录首个解析错误的位置与消息(只记录第一次,后续错误不覆盖)""" + """Record the position and message of the first parse error (later errors do not overwrite it)""" global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg if _g_parse_err_line != 0: return @@ -304,7 +275,7 @@ def _record_parse_err(ps: rawptr[Parser]): err_s: str = _cur_str(ps) if err_s is None: err_s = "(none)" - # 消息文本用 stdlib 拷贝(token 的 str_val 在 pool 中,生命周期覆盖本次编译但保险起见独立存储) + # Copy the message text via stdlib (token.str_val lives in the pool; stored independently to be safe) buf_m: str = stdlib.malloc(128) if buf_m is not None: stdio.snprintf(buf_m, 128, "意外的 token '%s'", err_s) @@ -316,13 +287,10 @@ def _record_parse_err(ps: rawptr[Parser]): 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 the first parse error with token type and prev/next tokens appended to the message. - 与 `_record_parse_err` 同源(只记首次、同位置口径),差别只在消息更全: - 意外的 token 'x'(type=4) | 附注: prev='union' (line 420 col 0) | next=':' (line 420 col 8) - (**单行**:同一消息会被 [Phase A-pre-vpi] 等日志行内联显示,含换行会把日志撑成多行。) - `文件:行:列` + 源码行 + caret + `SyntaxError: <消息>` 由调用方的 - dump_parse_traceback 统一补全(仿 fatal_error),解析器不再自拼单行报文。 + Same source as _record_parse_err (first only); the message must stay a single line since logs + inline it. file:line:col and the caret are filled later by the caller's dump_parse_traceback. """ global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg if _g_parse_err_line != 0: @@ -343,12 +311,10 @@ def _record_parse_err_ctx(ps: rawptr[Parser], tok_type: int, def _record_neg_literal_overflow(lit_text: str, lineno: int, col: int): - """记录"一元减 + 整数字面量"的联合越界(fail-fast;只记首次)。 + """Record a combined "unary minus + integer literal" overflow (fail-fast; first only). - `-` 是独立的一元运算符、不属于字面量,故幅值合法(≤ 2^64-1)的字面量在取负 - 后可能 < -2^63。报文含:**字面量原文(含负号)** + 允许范围 [-2^63, 2^64-1] - + 修法;`文件:行:列` 与源码行由 parse_tokens → dump_parse_traceback 补全 - (与词法层 `_lex_record_overflow` 同一上报路径)。 + `-` is a separate unary operator, so a magnitude-valid literal (<= 2^64-1) may still be < -2^63 + after negation. Message carries the literal text + allowed range [-2^63, 2^64-1] + fix hint. """ global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg if _g_parse_err_line != 0: @@ -370,15 +336,13 @@ def _record_neg_literal_overflow(lit_text: str, lineno: int, col: int): # ============================================================ -# 类型描述符 const / volatile(软关键字,仅注解位置生效) +# Type descriptors const / volatile (soft keywords, annotation position only) # ============================================================ def _IsDescriptorKeyword(ps: rawptr[Parser]) -> int: - """当前 token 是否为类型描述符关键字(const / volatile),1=是 / 0=否。 + """Whether the current token is a type-descriptor keyword (const / volatile), 1 = yes / 0 = no. - const/volatile 是**软关键字**:只在**注解位置**(`:` 后的参数/AnnAssign 注解、 - `->` 后的返回注解)由 `_parse_type_ann` 消费;作标识符的写法 - (`t.attr.const()`,TransPyV/Test/App/attr_test.vp:142)不在注解位置, - 属性名/调用名解析只看 TokenType.Name,不看 kw_subtype,故不受影响。 + Soft keywords consumed by _parse_type_ann only in annotation position; `t.attr.const()` as an + identifier is unaffected since attribute/call resolution looks at TokenType.Name, not kw_subtype. """ if _cur_type(ps) != TokenType.Name: return 0 @@ -388,11 +352,10 @@ def _IsDescriptorKeyword(ps: rawptr[Parser]) -> int: def _IsMagicAttributeCall(ps: rawptr[Parser]) -> int: - """注解位置的 `__attribute__(...)` 前缀判定,1=是 / 0=否 + """Test for an annotation-position `__attribute__(...)` prefix, 1 = yes / 0 = no. - `__attribute__` 是软关键字(不进关键字表):只在**注解位置**、且紧跟 '(' 时 - 由本处消费;作普通标识符/装饰器名(`@__attribute__(...)` 走装饰器通道)都不受影响。 - 语义与 const / volatile 描述符同族:顺序自由、可混写。 + `__attribute__` is a soft keyword (not in the keyword table): consumed here only in annotation + position immediately before '('. Same family as const/volatile: order-free and mixable. """ if _cur_type(ps) != TokenType.Name: return 0 @@ -406,12 +369,10 @@ def _IsMagicAttributeCall(ps: rawptr[Parser]) -> int: def _MakeDescriptorMarker(ps: rawptr[Parser]) -> rawptr[AST]: - """由当前描述符关键字构造标记节点 Attribute(Name('t'), 'CConst'/'CVolatile')。 + """Build the marker node Attribute(Name('t'), 'CConst'/'CVolatile') from the current descriptor keyword. - 与 TPC 侧 `SymbolUtils._ExpandTypeDescriptors` 的文本展开产物 - (`t.CConst` / `t.CVolatile`)**逐字段同形**:两侧落到同一条既有识别路径 - (TPC:HandlesTypeMerge/TypeAnnotationResolver 的 t 模块类型限定符分支 → - DataConst/DataVolatile;TPV:注解解析的 t 模块属性分支),不新造第二套表示。 + Field-identical to TPC's _ExpandTypeDescriptors text expansion (t.CConst / t.CVolatile) so both + sides share one recognition path; no second representation is introduced. """ attr_name: str = "CConst" if _cur_kw(ps) == Keyword.Volatile: @@ -426,15 +387,12 @@ def _PrependDescriptorMarkers(ps: rawptr[Parser], base: rawptr[AST], mods: rawptr[list[rawptr[AST]]], mod_count: int) -> rawptr[AST]: - """把描述符标记贴到类型联合的**最左操作数**位置(与 TPC 文本展开同形)。 + """Prepend descriptor markers to the leftmost operand of the type union (same shape as TPC text). - 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 不同形)。 + TPC expands `const volatile int | t.CPtr` to the left-associative `(((t.CConst | t.CVolatile) | int) | t.CPtr)`; + this function produces the identical tree rather than a differently-shaped one. """ - slot_parent: rawptr[BinOp] = None # 最左 BitOr 节点:其 left 即"最左操作数" + slot_parent: rawptr[BinOp] = None # leftmost BitOr node: its left is the leftmost operand slot_node: rawptr[AST] = base cur: rawptr[AST] = base while cur is not None and cur.kind() == ASTKind.BinOp: @@ -450,7 +408,7 @@ def _PrependDescriptorMarkers(ps: rawptr[Parser], slot_parent = b slot_node = nxt break - # 描述符串按源码顺序左结合:`const volatile` → (t.CConst | t.CVolatile) + # Descriptor chain left-associates in source order: `const volatile` -> (t.CConst | t.CVolatile) chain: rawptr[AST] = mods.get(0) i: int = 1 while i < mod_count: @@ -464,11 +422,10 @@ def _PrependDescriptorMarkers(ps: rawptr[Parser], def _RecordDescriptorErr(detail: str, lineno: int, col: int): - """记录类型描述符非法形态(fail-fast;只记首次)。 + """Record an invalid type-descriptor form (fail-fast; first only). - 报文含**期望形态 + 正确示例**;`文件:行:列` 与源码行 + `^` 列标记由 - parse_tokens → dump_parse_traceback 补全(与 `_record_neg_literal_overflow` - 同一上报路径)。描述符**绝不静默忽略**:缺类型 / 后随非类型 / 裸用都终止编译。 + Message carries the expected form + a correct example; file:line:col is filled later. Descriptors + are never silently ignored: missing type / non-type follower / bare use all abort compilation. """ global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg if _g_parse_err_line != 0: @@ -490,37 +447,13 @@ def _RecordDescriptorErr(detail: str, lineno: int, col: int): # ============================================================ -# 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 ⇒ 该类 -# 仍生成虚表,且槽里只有被标记的方法)。 +# virtual / novirtual modifier (soft keyword; statement start before class/def only), same +# representation as @t.CVTable / @t.NoVTable. Invalid forms fail loudly; `x = d.virtual` unaffected. def _IsVirtualModifier(ps: rawptr[Parser]) -> int: - """当前 token 是否为 virtual / novirtual 修饰符(1=是 / 0=否)。 + """Whether the current token is a virtual / novirtual modifier (1 = yes / 0 = no). - 只在**语句起始**(_parse_statement_into 分派处)调用;作标识符的位置不经过 - 本判据,故 `x = d.virtual` / 形参名 / for 目标 / 字典键不受影响。 + Called only at statement start; identifier positions never reach this test, so `x = d.virtual`, + parameter names, for targets and dict keys are unaffected. """ if _cur_type(ps) != TokenType.Name: return 0 @@ -530,10 +463,9 @@ def _IsVirtualModifier(ps: rawptr[Parser]) -> int: def _MakeVirtualMarker(ps: rawptr[Parser]) -> rawptr[AST]: - """由当前 virtual/novirtual token 构造标记节点 Attribute(Name('t'), 'CVTable'|'NoVTable')。 + """Build the marker node Attribute(Name('t'), 'CVTable'|'NoVTable') from the current virtual/novirtual token. - 与**手写** `@t.CVTable` / `@t.NoVTable` 装饰器逐字段同形(_parse_decorators 的 - 产物形态),故下游 `ClassGenBase._has_decorator` 零新增判据。 + Field-identical to hand-written @t.CVTable / @t.NoVTable, so ClassGenBase._has_decorator needs no new test. """ attr_name: str = "CVTable" if _cur_kw(ps) == Keyword.Novirtual: @@ -545,16 +477,11 @@ def _MakeVirtualMarker(ps: rawptr[Parser]) -> rawptr[AST]: def _RecordVirtualErr(detail: str, lineno: int, col: int, extra: str): - """记录 virtual/novirtual 修饰符非法形态(fail-fast;只记首次)。 + """Record an invalid virtual/novirtual form (fail-fast; first only); message carries allowed + positions + a correct example. Modifiers are never silently ignored. - 报文含**允许出现的位置 + 正确示例 + 修法**;`文件:行:列` 与源码行 + `^` 列标记 - 由 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 用 `+` 拼接。 + Callers build detail/extra with f-string interpolation: this language's str is i8*, so `str + str` + degrades to raw pointer add and illegal IR; never concatenate strings with `+` in this file. """ global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg if _g_parse_err_line != 0: @@ -578,11 +505,10 @@ def _RecordVirtualErr(detail: str, lineno: int, col: int, extra: str): def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]): - """`virtual class` / `novirtual class` / `virtual def` / `novirtual def`。 + """`virtual class` / `novirtual class` / `virtual def` / `novirtual def`. - 合成 `@t.CVTable` / `@t.NoVTable` 装饰器(与用户既有装饰器合并,标记追加在末尾; - 位置无关——`_has_decorator` 按名判定)后交给 _parse_classdef / _parse_funcdef, - 产出与**手写同名装饰器**逐字段同形的 AST,故下游零新增分支。 + Synthesizes a @t.CVTable / @t.NoVTable decorator (merged with existing decorators, appended last; + position-independent since _has_decorator matches by name) and hands off to _parse_classdef / _parse_funcdef. """ mod_lineno: int = _cur_lineno(ps) mod_col: int = _cur_col(ps) @@ -590,10 +516,9 @@ def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]): 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()` 不先展开,故两端都在本层显式接受别名) + _advance(ps) # consume the modifier + # The modifier must be followed by class / def (the def aliases fn/fun/func/function are accepted too, + # matching _is_funcdef_kw and TPC, whose fn->def expansion only hits a leading fn). kw2: int = _cur_kw(ps) is_class: int = 0 is_def: int = 0 @@ -607,9 +532,8 @@ def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]): if nxt_s is None: nxt_s = "(none)" if kw2 == Keyword.Struct: - # struct 是纯数据类型:禁止 virtual / novirtual 修饰(用户 2026-10-01 定)。 - # 单独报文(而不是落到通用"后随非 class/def")以便直接指出修法: - # struct 解析期已内置 @t.NoVTable,不需要也无法再显式声明虚/非虚。 + # struct is a pure data type: no virtual / novirtual modifier allowed. A dedicated message + # (not the generic "follower not class/def") points at the fix: struct already implies @t.NoVTable. _RecordStructErr(f"`{mod_name} struct` 非法:struct 禁止 virtual/novirtual 修饰", mod_lineno, mod_col, "struct 天然无虚表(解析期已合成 @t.NoVTable)," @@ -621,7 +545,7 @@ def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]): f"若此处本意是普通标识符名,请改名" f"(该词在语句起始会被判为修饰符)。") ps.error_count += 1 - # 余下 token 交给通用语句分派消费(不制造死循环,也不覆盖首次错误)。 + # Dispatch the remaining tokens to the generic statement path (no infinite loop, no error overwrite). _parse_simple_stmt_into(ps, parent) return if is_def != 0 and ps.in_class_body == 0: @@ -629,7 +553,7 @@ def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]): f"{mod_name} def 描述的是「类体内方法的虚/非虚」," f"模块级 / 函数体 / 分支体内没有可归属的类。") ps.error_count += 1 - # 合并既有装饰器(`@deco` + virtual class/def 组合)后交给既有 class/def 解析 + # Merge existing decorators (@deco + virtual class/def) then hand off to the existing class/def parser merged: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) if merged is None: return @@ -649,19 +573,11 @@ def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]): # ============================================================ -# 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 既有源码里可能是普通标识符,进表会把 -# 全局词法口径改掉;此处按软关键字文本判定,作用域只限"语句起始后随函数定义"。 +# inline modifier (soft keyword): `inline def f(...)` -> synthesizes @t.attr.always_inline, same shape as +# virtual/novirtual (statement start before a function definition only); kept out of the keyword table. # ============================================================ def _IsInlineModifier(ps: rawptr[Parser]) -> int: - """当前 token 是否为 `inline`(软关键字,且后随函数定义),1=是 / 0=否""" + """Whether the current token is `inline` (soft keyword followed by a function definition), 1 = yes / 0 = no""" if _cur_type(ps) != TokenType.Name: return 0 s: str = _cur_str(ps) @@ -673,11 +589,11 @@ def _IsInlineModifier(ps: rawptr[Parser]) -> int: nxt2: rawptr[Token] = nxt.next if nxt2 is None: return 0 - # 后随 def(硬关键字):`inline def f(...)` + # Followed by def (hard keyword): `inline def f(...)` if nxt.kw_subtype == Keyword.Def: return 1 - # 后随函数定义软关键字别名(fn/fun/func/function):与 _is_funcdef_kw 同口径 - # ——必须是 Name + '(' 或 '[T]',避免把 `inline func` 这种普通标识符组合误判。 + # Followed by a function-definition soft-keyword alias (fn/fun/func/function): same rule as + # _is_funcdef_kw -- must be Name + '(' or '[T]', so `inline func` is not misread. 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 \ @@ -687,13 +603,10 @@ def _IsInlineModifier(ps: rawptr[Parser]) -> int: def _MakeInlineMarker(ps: rawptr[Parser]) -> rawptr[AST]: - """由当前 inline token 构造标记节点 Attribute(Attribute(Name('t'),'attr'), 'always_inline') + """Build the marker Attribute(Attribute(Name('t'),'attr'), 'always_inline') from the current inline token. - 形态与**手写** `@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,会静默不内联。 + Field-identical to a hand-written @t.attr.always_inline. Deliberately not @c.Attribute(t.attr.x), + whose extractor requires `import c` and would silently skip inlining in files lacking it. """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -705,13 +618,13 @@ def _MakeInlineMarker(ps: rawptr[Parser]) -> rawptr[AST]: def _ParseInlineModified(ps: rawptr[Parser], parent: rawptr[AST]): - """`inline def f(...)`:消费 inline,合成 @t.attr.always_inline 后交给 _parse_funcdef""" + """`inline def f(...)`: consume inline, synthesize @t.attr.always_inline and hand off to _parse_funcdef""" marker: rawptr[AST] = _MakeInlineMarker(ps) if marker is None: return - _advance(ps) # 消费 inline 修饰符 - # 合并既有装饰器(`@deco` + inline def 组合),标记追加在末尾;位置无关 - # (下游按装饰器名判定)。与 _ParseVirtualModified 同一套 pending_decorators 通道。 + _advance(ps) # consume the inline modifier + # Merge existing decorators (@deco + inline def), appending the marker last; position-independent + # (matched by name downstream). Same pending_decorators channel as _ParseVirtualModified. merged: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) if merged is None: return @@ -727,16 +640,11 @@ def _ParseInlineModified(ps: rawptr[Parser], parent: rawptr[AST]): def _IsStaticModifier(ps: rawptr[Parser]) -> int: - """当前 token 是否为 `static` 修饰符(软关键字),1=是 / 0=否。 + """Whether the current token is a `static` modifier (soft keyword), 1 = yes / 0 = no. - 只在**语句起始**(_parse_statement_into 分派处)调用;后随 - ① 函数定义(`def`,或 fn/fun/func/function 别名 + '(' / '[') - ② 变量声明(`Name :`,即 AnnAssign) - 时命中。作标识符的位置(属性名 / 形参名 / 字典键 / 表达式内)不经过本判据, - 故 `x = d.static`、`f(static=1)` 等写法不受影响(软关键字边界)。 - - 语义(禁止跨模块使用)由 TransPyV 侧消费;本层只负责把两种写法归一, - 且与手写 `@t.CStatic` / `x: t.CStatic | T` 的标记逐字段同形。 + Called only at statement start; matches when followed by (1) a function definition (`def` or the + fn/fun/func/function alias + '(' / '[') or (2) a variable declaration (`Name :`, i.e. AnnAssign). + Identifier positions never reach this test, so `x = d.static` / `f(static=1)` are unaffected. """ if _cur_type(ps) != TokenType.Name: return 0 @@ -746,37 +654,30 @@ def _IsStaticModifier(ps: rawptr[Parser]) -> int: nxt: rawptr[Token] = ps.cur.next if nxt is None or nxt.type != TokenType.Name: return 0 - # ① 后随 def(硬关键字):`static def f(...)` + # (1) followed by def (hard keyword): `static def f(...)` if nxt.kw_subtype == Keyword.Def: return 1 nxt2: rawptr[Token] = nxt.next - # ①' 后随函数定义软关键字别名:必须是 Name + '(' 或 '[T]' - #(与 _IsInlineModifier / _is_funcdef_kw 同口径,避免 `static func` 这类 - # 普通标识符组合被误判) + # (1') followed by a function-definition alias: must be Name + '(' or '[T]', same rule as + # _IsInlineModifier / _is_funcdef_kw, so `static func` is not misread. 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) + # (2) followed by a variable declaration `Name :` (AnnAssign) if nxt2 is not None and nxt2.type == TokenType.Op and nxt2.op_subtype == TokOp.Colon: return 1 return 0 def _IsStaticDescriptorKeyword(ps: rawptr[Parser]) -> int: - """当前 token 是否为本位置的 `static`(软关键字),1=是 / 0=否 + """Whether the current token is an annotation-position `static` (soft keyword), 1 = yes / 0 = no. - 本位置的 `static` 即**注解里的 static**(`名字: static 类型` / 形参 / `->` - 返回注解),它**已不再支持**:`_parse_type_ann` 只把它当"旧位置误用"识别出来 - 并响亮报错(提示前置形式 `static 名字: 类型`),不再脱糖成 t.CStatic。 - 合法的两种写法(都与手写 `t.CStatic | T` 同源): - * 语句起始 `static 名字: 类型`(pending_static 通道,见 _ParseStaticModified); - * 装饰器 `@t.CStatic`。 - 之所以必须报错而不是忽略:不消费时 `static` 会退化成一个叫 static 的"类名", - 被建成 opaque 结构体(`@a = global %".static" zeroinitializer`, - llvm-link 报 invalid type for null constant),报错点远离根因。 - 本判定只在注解位置生效(由 `_parse_type_ann` 调用),标识符位置不受影响。 + This position (`name: static T`) is no longer supported: _parse_type_ann only detects it as misuse + and fails loudly, pointing at the prefix form `static name: T`; it is no longer desugared to t.CStatic. + Valid forms are statement-start `static name: T` and the `@t.CStatic` decorator. Reporting (not ignoring) + is mandatory: unconsumed, `static` degrades into an opaque "class" named static and errors far from the cause. """ if _cur_type(ps) != TokenType.Name: return 0 @@ -787,11 +688,10 @@ def _IsStaticDescriptorKeyword(ps: rawptr[Parser]) -> int: def _MakeStaticMarker(ps: rawptr[Parser]) -> rawptr[AST]: - """构造标记节点 Attribute(Name('t'), 'CStatic') + """Build the marker node Attribute(Name('t'), 'CStatic'). - 与**手写** `@t.CStatic` 装饰器 / `x: t.CStatic | T` 注解里的标记逐字段同形, - 故下游(FuncGenDef 的存储类标记识别、TypeGenResolve 的存储类限定符判定) - 零新增判据;两种写法同源。 + Field-identical to the hand-written @t.CStatic decorator / `x: t.CStatic | T` marker, so downstream + (FuncGenDef storage-class detection, TypeGenResolve qualifier resolution) needs no new test. """ marker: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), "CStatic", ASTCtx.Load) @@ -800,13 +700,11 @@ def _MakeStaticMarker(ps: rawptr[Parser]) -> rawptr[AST]: def _ParseStaticModified(ps: rawptr[Parser], parent: rawptr[AST]) -> int: - """`static` 修饰符分派:1=已完全处理(函数定义,调用方直接 return); - 0=仅置位(变量声明,调用方继续走通用语句分派)。 + """Dispatch the `static` modifier: 1 = fully handled (function definition), 0 = only flagged + (variable declaration, caller continues with the generic statement dispatch). - 函数:消费 static 后合成 @t.CStatic 装饰器,交给 _parse_funcdef - (与 inline def 同一套 pending_decorators 通道)。 - 变量:置 pending_static 后消费 static,由紧随的 _parse_type_ann 把 - t.CStatic 插到类型联合最左(= 手写 `x: t.CStatic | int`)。 + Function: consume static and synthesize an @t.CStatic decorator, handed to _parse_funcdef. + Variable: set pending_static and consume static; the next _parse_type_ann prepends t.CStatic. """ nxt: rawptr[Token] = ps.cur.next is_func: int = 0 @@ -820,17 +718,17 @@ def _ParseStaticModified(ps: rawptr[Parser], parent: rawptr[AST]) -> int: 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)。 + # Variable branch: only flag + consume static, then let the caller continue generic dispatch + # (AnnAssign parsing reads pending_static in _parse_type_ann). 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 通道。 + _advance(ps) # consume the static modifier + # Merge existing decorators (@deco + static def), appending the marker last; position-independent + # (matched by name downstream). Same pending_decorators channel as _ParseInlineModified. merged: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) if merged is None: return 1 @@ -847,10 +745,10 @@ def _ParseStaticModified(ps: rawptr[Parser], parent: rawptr[AST]) -> int: def _IsBareDescriptor(ps: rawptr[Parser]) -> int: - """裸用描述符判定:整条语句只有一个描述符串(const[: volatile]),1=是 / 0=否。 + """Bare-descriptor test: the whole statement is a single descriptor run (const[: volatile]), 1 = yes / 0 = no. - 只在"描述符串后直接换行 / 文件尾 / 分号"时命中;作普通标识符的写法 - (`const = 5` / `const(x)` / `t.attr.const()`)不命中(软关键字边界)。 + Matches only when the descriptor run is followed directly by newline / EOF / semicolon; identifiers + (`const = 5` / `const(x)` / `t.attr.const()`) do not match (soft-keyword boundary). """ if _IsDescriptorKeyword(ps) == 0: return 0 @@ -868,17 +766,17 @@ def _IsBareDescriptor(ps: rawptr[Parser]) -> int: def get_parse_err_line() -> int: - """获取最近一次解析错误的行号(0=无错误)""" + """Line number of the latest parse error (0 = none)""" return _g_parse_err_line def get_parse_err_col() -> int: - """获取最近一次解析错误的列号(0=无错误)""" + """Column of the latest parse error (0 = none)""" return _g_parse_err_col def get_parse_err_msg() -> str: - """获取最近一次解析错误的错误消息(仿 Python SyntaxError 消息文本)""" + """Message of the latest parse error (Python SyntaxError style)""" return _g_parse_err_msg @@ -936,8 +834,8 @@ 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 轮结束 + # Convergence: if tokens remain, "match" and jump to the end so both + # `while not _match_op(...)` and `while _match_op(...)` end within <= 2 rounds if ps.cur is not None: ps.cur = None return 1 @@ -968,14 +866,14 @@ 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) 恢复原始值 + # Store the numeric text in str_val (stdlib allocation, bypassing the pool overwrite of float_val); + # REG-SRC recovers the original value with 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 覆写) - # 表满时重置:旧记录对应已翻译完的文件,从当前节点重新记录 + # Record float node ptr->val into a stdlib table (value correct at construction; REG-SRC uses the + # table to bypass the pool overwrite). Reset when full: old entries belong to finished files. if _g_dbg_pts is None: _g_dbg_pts = stdlib.malloc(4096 * 8) _g_dbg_vals = stdlib.malloc(4096 * 8) @@ -989,18 +887,18 @@ def _new_const_float(pool: rawptr[memhub.MemManager], val: double, return new_c -# 浮点节点构造值表(stdlib 分配,避免污染 pool) +# Float-node construction-value table (stdlib allocated to avoid polluting the pool) _g_dbg_pts: rawptr[uint64] = None _g_dbg_vals: rawptr[double] = None _g_dbg_cnt: int = 0 -# 最近一次 _dbg_lookup_lit_val 是否命中 +# Whether the latest _dbg_lookup_lit_val hit _g_dbg_lit_found: int = 0 def _dbg_lookup_lit_val(ptr: uint64) -> double: - """查浮点节点构造值表:返回 ptr 节点的原始构造值(未命中返回 0 并置 found=0) + """Look up the float-node construction-value table: return the original value (miss returns 0, found=0). - 用于绕开 pool 分配对 AST float_val 字段的覆写(构造时值正确,读取时可能被覆写)。 + Used to bypass the pool allocation overwriting the AST float_val field (correct at construction). """ global _g_dbg_lit_found _g_dbg_lit_found = 0 @@ -1014,7 +912,7 @@ def _dbg_lookup_lit_val(ptr: uint64) -> double: def _dbg_lit_found() -> int: - """检查最近一次 _dbg_lookup_lit_val 是否命中""" + """Check whether the latest _dbg_lookup_lit_val hit""" return _g_dbg_lit_found @@ -1132,10 +1030,10 @@ def _parse_namedexpr(ps: rawptr[Parser]) -> rawptr[AST]: def _IsVolatileCallPrefix(ps: rawptr[Parser]) -> int: - """值位置的 `volatile 函数()` / `volatile 结构体()` 前缀判定,1=是 / 0=否 + """Test for a value-position `volatile f()` / `volatile S()` prefix, 1 = yes / 0 = no. - `volatile` 是软关键字:只在"值位置 + 紧跟 调用(Name 后随 '(')"时消费, - 作注解描述符(`a: volatile int`)与作标识符的写法都不受影响。 + `volatile` is a soft keyword consumed only in value position before a call (Name followed by '('); + its use as an annotation descriptor (`a: volatile int`) or an identifier is unaffected. """ if _cur_kw(ps) != Keyword.Volatile: return 0 @@ -1152,12 +1050,10 @@ 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 注释)。 + # Value-position volatile call prefix: `a: int = volatile f()` / `volatile S()` -> consume volatile and + # mark the call node (result slot accessed volatile, struct writes too; LLVM call has no volatile qualifier). if _IsVolatileCallPrefix(ps) != 0: - _advance(ps) # 消费 volatile + _advance(ps) # consume volatile vinner: rawptr[AST] = _parse_test(ps) if vinner is not None and vinner.kind() == ASTKind.Call: (Call*(vinner)).IsVolatile = 1 @@ -1200,9 +1096,8 @@ def _parse_or_test(ps: rawptr[Parser]) -> rawptr[AST]: 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 实现)。 + # Supports both leading `or` ((A\n or B)) and trailing `or` (A or\n B) inside parens: + # each round skips Nl before checking, otherwise A or B\n or C stops on Nl after B. _skip_nl(ps) if _cur_kw(ps) != Keyword.Or: break @@ -1210,7 +1105,7 @@ def _parse_or_test(ps: rawptr[Parser]) -> rawptr[AST]: values = list[rawptr[AST]](ps.pool, 8) values.append(left) _advance(ps) - _skip_nl(ps) # 括号内跨行: a or\n b + _skip_nl(ps) # cross-line inside parens: a or\n b right: rawptr[AST] = _parse_and_test(ps) if right is None: break values.append(right) @@ -1227,10 +1122,8 @@ def _parse_and_test(ps: rawptr[Parser]) -> rawptr[AST]: 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 链解析失败根因)。 + # Supports both leading `and` ((A\n and B)) and trailing `and` (A and\n B) inside parens: + # each round skips Nl before checking, otherwise A and B\n and C stops on Nl after B. _skip_nl(ps) if _cur_kw(ps) != Keyword.And: break @@ -1238,7 +1131,7 @@ def _parse_and_test(ps: rawptr[Parser]) -> rawptr[AST]: values = list[rawptr[AST]](ps.pool, 8) values.append(left) _advance(ps) - _skip_nl(ps) # 括号内跨行: a and\n b + _skip_nl(ps) # cross-line inside parens: a and\n b right: rawptr[AST] = _parse_not_test(ps) if right is None: break values.append(right) @@ -1271,12 +1164,12 @@ def _parse_comparison(ps: rawptr[Parser]) -> rawptr[AST]: left: rawptr[AST] = _parse_bitor(ps) if left is None: return None - _skip_nl(ps) # 括号内行首比较运算符: (a\n < b) + _skip_nl(ps) # leading comparison operator inside parens: (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 + _skip_nl(ps) # cross-line inside parens: 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) @@ -1287,7 +1180,7 @@ def _parse_comparison(ps: rawptr[Parser]) -> rawptr[AST]: _set_pos(first_cmp, left.lineno, left.col_offset, 0, 0) # Check chained comparison - _skip_nl(ps) # 括号内行首链式比较: (a < b\n < c) + _skip_nl(ps) # leading chained comparison inside parens: (a < b\n < c) next_op: int = _match_comp_op(ps) if next_op == OpKind.NoneOp: return first_cmp @@ -1298,7 +1191,7 @@ def _parse_comparison(ps: rawptr[Parser]) -> rawptr[AST]: cur_left: rawptr[AST] = right while next_op != OpKind.NoneOp: _advance_comp_op(ps) - _skip_nl(ps) # 括号内跨行链式比较 + _skip_nl(ps) # cross-line chained comparison inside parens cur_right: rawptr[AST] = _parse_bitor(ps) ops2: rawptr[list[int]] = list[int](ps.pool, 8) ops2.append(next_op) @@ -1308,7 +1201,7 @@ def _parse_comparison(ps: rawptr[Parser]) -> rawptr[AST]: _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) + _skip_nl(ps) # leading chained comparison inside parens: (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) @@ -1360,11 +1253,11 @@ 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) + _skip_nl(ps) # leading '|' inside parens: (a\n | b) if _match_op(ps, TokOp.VBar) == 0: break _advance(ps) - _skip_nl(ps) # 括号内跨行: a |\n b + _skip_nl(ps) # cross-line inside parens: 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) @@ -1376,11 +1269,11 @@ 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) + _skip_nl(ps) # leading '^' inside parens: (a\n ^ b) if _match_op(ps, TokOp.Caret) == 0: break _advance(ps) - _skip_nl(ps) # 括号内跨行: a ^\n b + _skip_nl(ps) # cross-line inside parens: 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) @@ -1392,11 +1285,11 @@ 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) + _skip_nl(ps) # leading '&' inside parens: (a\n & b) if _match_op(ps, TokOp.Amp) == 0: break _advance(ps) - _skip_nl(ps) # 括号内跨行: a &\n b + _skip_nl(ps) # cross-line inside parens: 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) @@ -1408,12 +1301,12 @@ 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) + _skip_nl(ps) # leading '<<'/'>>' inside parens: (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 + _skip_nl(ps) # cross-line inside parens: a <<\n b right: rawptr[AST] = _parse_arith(ps) ast_op: int = OpKind.LShift if op_token == TokOp.GtGt: @@ -1428,12 +1321,12 @@ 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) + _skip_nl(ps) # leading '+'/'-' inside parens: (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 + _skip_nl(ps) # cross-line inside parens: a +\n b right: rawptr[AST] = _parse_term(ps) ast_op: int = OpKind.Add if op_token == TokOp.Minus: @@ -1445,7 +1338,7 @@ def _parse_arith(ps: rawptr[Parser]) -> rawptr[AST]: # ============================================================ -# C 风格指针后缀('*' / '**')—— 与 TransPyC lib/vast 的 _AtPtrSuffix 同源 +# C-style pointer suffix ('*' / '**') -- same source as 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 @@ -1456,10 +1349,10 @@ _g_ptr_suffix_col: int = 0 def _ptr_suffix_end_op(op: int) -> int: - """后缀末尾 token 是否为“无右操作数”位置(']' ',' ')' ':' '=')。 + """Whether the suffix-end token is a "no right operand" position (']' ',' ')' ':' '='). - 这些位置后接 '*'/'**' 时,乘法/幂本就缺右操作数(原语法错误), - 故按指针后缀处理不会改变任何合法表达式的语义(x[a * b] / a ** b 等不受影响)。 + Multiplication/power would lack a right operand there anyway (a pre-existing syntax error), so + treating '*'/'**' as a pointer suffix changes no valid expression (x[a * b] / a ** b unaffected). """ if op == TokOp.Rsqb or op == TokOp.Comma or op == TokOp.RPar \ or op == TokOp.Colon or op == TokOp.Equal: @@ -1468,11 +1361,10 @@ def _ptr_suffix_end_op(op: int) -> int: def _at_ptr_suffix(ps: rawptr[Parser], base: rawptr[AST]) -> int: - """判定当前 '*'/'**' 串是否为作用于 base 的 C 风格指针后缀。 + """Whether the current '*'/'**' run is a C-style pointer suffix applied to base. - 判据(与 vast 一致):base 是类型原子(Name/Attribute/Subscript), - 且从当前 '*'/'**' 起跨过连续 '*'/'**' 与 Nl 后,紧跟 ']' / ',' / ')' / ':' / '='。 - 该判据同时覆盖注解与表达式上下文(如 list[AST*] / list[AST*]*)。 + Rule (same as vast): base is a type atom (Name/Attribute/Subscript) and, skipping consecutive + '*'/'**' and Nl, the next token is ']' / ',' / ')' / ':' / '='. Covers annotation and expression contexts. """ if base is None: return 0 @@ -1496,7 +1388,7 @@ def _at_ptr_suffix(ps: rawptr[Parser], base: rawptr[AST]) -> int: def _make_ptr_type(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]: - """构造一层指针包装:base | t.CPtr。""" + """Build one pointer wrapper layer: 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) @@ -1505,7 +1397,7 @@ def _make_ptr_type(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]: def _consume_ptr_suffix(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]: - """消费连续的 C 风格指针后缀('*' 一层 / '**' 两层),展开为 base | t.CPtr 链。""" + """Consume consecutive C-style pointer suffixes ('*' = 1 layer, '**' = 2), expanding into a base | t.CPtr chain.""" global _g_ptr_suffix_line, _g_ptr_suffix_col while True: _skip_nl(ps) @@ -1546,17 +1438,11 @@ def _at_cast_operand(ps: rawptr[Parser]) -> int: def _at_ptr_suffix_call(ps: rawptr[Parser], base: rawptr[AST]) -> int: - """判定当前 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用**。 + """Whether the current '*'/'**' run is a `T*(x)` pointer-suffix cast call. - 判据(与 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)` 等有空格者仍按乘法解析。 + Rule (same as TPC lib/vast _AtPtrSuffixCall): (1) base is a type atom; (2) base and the star run are + adjacent (same line, no whitespace/newline); (3) the run, internally adjacent, is immediately followed + by '('. Required because `a * (b)` is valid multiplication, which a bare star rule would misread. """ if base is None: return 0 @@ -1570,7 +1456,7 @@ def _at_ptr_suffix_call(ps: rawptr[Parser], base: rawptr[AST]) -> int: 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 风格强转写法 + return 0 # whitespace/newline between base and '*': not a C-style cast stars: int = 0 prev: rawptr[Token] = None last: rawptr[Token] = None @@ -1579,7 +1465,7 @@ def _at_ptr_suffix_call(ps: rawptr[Parser], base: rawptr[AST]) -> int: 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 # 星串内部出现空白:非紧邻,不判为强转 + return 0 # whitespace inside the star run: not adjacent, not a cast stars += 1 prev = nxt last = nxt @@ -1596,11 +1482,10 @@ def _at_ptr_suffix_call(ps: rawptr[Parser], base: rawptr[AST]) -> int: def _consume_ptr_suffix_call(ps: rawptr[Parser], base: rawptr[AST]) -> rawptr[AST]: - """把已认定为 `T*(...)` 的指针后缀强转解析为**调用**。 + """Parse a recognized `T*(...)` pointer-suffix cast as a call. - 产物与既有强转形态 `(T | t.CPtr)(...)` 逐字同形:同为 Call - (func = base 展开的 `base | t.CPtr` 链,args/keywords = 括号实参), - 不新造第二套 cast 表示。 + Identical to the existing cast form `(T | t.CPtr)(...)`: a Call (func = the expanded base | t.CPtr + chain, args/keywords = the parenthesized arguments); no second cast representation is introduced. """ func: rawptr[AST] = _consume_ptr_suffix(ps, base) return _parse_call(ps, func) @@ -1610,9 +1495,9 @@ 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) + _skip_nl(ps) # leading '*'/'/'/'//'/'%'/'@' inside parens: (a\n * b) if _match_op(ps, TokOp.Star) and _at_ptr_suffix(ps, left) != 0: - left = _consume_ptr_suffix(ps, left) # C 风格指针后缀,不按乘法处理 + left = _consume_ptr_suffix(ps, left) # C-style pointer suffix, not multiplication continue op_token: int = 0 if _match_op(ps, TokOp.Star): op_token = TokOp.Star @@ -1622,7 +1507,7 @@ def _parse_term(ps: rawptr[Parser]) -> rawptr[AST]: elif _match_op(ps, TokOp.At): op_token = TokOp.At if op_token == 0: break _advance(ps) - _skip_nl(ps) # 括号内跨行: a *\n b + _skip_nl(ps) # cross-line inside parens: 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) @@ -1633,9 +1518,8 @@ def _parse_term(ps: rawptr[Parser]) -> rawptr[AST]: def _parse_factor(ps: rawptr[Parser]) -> rawptr[AST]: """factor: '+' factor | '-' factor | '~' factor | '*' factor | '&' factor | power""" - # 实参位前导 `*` 已由 _parse_call 解析出紧绑定的 Starred 左操作数,直接作为本层 - # 因子返回(必须先于下面的一元判定,否则其后的二元 `+`/`-` 会被误判为一元)。 - # 上层各层随即以该左操作数续接二元运算:`*p + n` ⇒ `(*p) + n`,而非 `*(p + n)`。 + # A leading `*` in argument position was already parsed by _parse_call as a tightly bound Starred + # operand; return it as this level's factor, before the unary test (so `*p + n` => `(*p) + n`). if ps.starred_seed is not None: seed: rawptr[AST] = ps.starred_seed ps.starred_seed = None @@ -1646,10 +1530,8 @@ def _parse_factor(ps: rawptr[Parser]) -> rawptr[AST]: lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) _advance(ps) - # 一元减 + 整数字面量的**联合越界判定**:`-` 是一元运算符、不属于字面量, - # 故幅值合法(≤ 2^64-1,词法层已放行)的字面量取负后可能 < -2^63。 - # 只有"直接作用于整数常量"的一元减在此判定:`1 - 2` / `a - b` 是二元减 - # (经 _parse_arith,不进入本分支);`-x`(变量)操作数不是常量,不判定。 + # Combined unary-minus + integer-literal overflow check: `-` is unary, so a magnitude-valid literal + # may become < -2^63 after negation. Only a unary minus directly on an integer constant is checked here. 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: @@ -1677,7 +1559,7 @@ def _parse_power(ps: rawptr[Parser]) -> rawptr[AST]: 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 层) + return _consume_ptr_suffix(ps, base) # C-style pointer suffix ('**' counts as 2 layers) if _match_op(ps, TokOp.StarStar): _advance(ps) exp: rawptr[AST] = _parse_factor(ps) @@ -1703,9 +1585,9 @@ def _parse_atom_expr(ps: rawptr[Parser]) -> rawptr[AST]: 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]` / `(…)` 仍由本循环继续挂接。 + # `T*(x)` / `T**(x)` pointer-suffix cast: parsed as a cast call (same shape as + # `(T | t.CPtr)(x)`). Consumed in the trailer loop, so subsequent `.attr` / `[i]` / `(…)` + # are still attached by this same loop. base = _consume_ptr_suffix_call(ps, base) elif _match_op(ps, TokOp.Dot): _advance(ps) @@ -1755,10 +1637,8 @@ def _parse_call(ps: rawptr[Parser], func: rawptr[AST]) -> rawptr[AST]: elif _match_op(ps, TokOp.Star): _advance(ps) _skip_nls_and_newlines(ps) - # 只按一元因子取下界(保持 `*p[0]` = `*(p[0])` 的 C 结合),再挂到 - # starred_seed 上,交由 _parse_expr 续接其后二元运算:`*p + n` ⇒ - # `(*p) + n`。若整体用 _parse_expr,前导 `*` 会吞掉整条 `p + n` - # 而错解为 `*(p + n)`(zlib append_bytes 的 out 参数不回写根因)。 + # Bind only a unary factor (preserving `*p[0]` = `*(p[0])`), put it on starred_seed and let + # _parse_expr continue the binary ops: `*p + n` => `(*p) + n`, not `*(p + n)`. seed: rawptr[AST] = _parse_factor(ps) if seed is None: ps.error_count += 1 @@ -1817,9 +1697,8 @@ def _parse_subscript(ps: rawptr[Parser], value: rawptr[AST]) -> rawptr[AST]: 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 ']'"。 + # Empty subscript (`T[]`): `[` directly followed by `]` => slice is None (the type side treats it as + # an empty param list); calling _parse_expr here would report an unexpected token ']'. if not _match_op(ps, TokOp.Colon) and not _match_op(ps, TokOp.Rsqb): lower = _parse_expr(ps) @@ -1905,8 +1784,8 @@ def _parse_atom(ps: rawptr[Parser]) -> rawptr[AST]: 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,不跳过 + # Python implicit adjacent string literal concatenation: "a" "b" -> "ab". + # Nl is the in-paren newline token (allowed across lines); statement NewLine is not skipped. while True: _skip_nl(ps) if _cur_type(ps) != TokenType.String: @@ -1970,7 +1849,7 @@ def _parse_paren(ps: rawptr[Parser]) -> rawptr[AST]: break e: rawptr[AST] = _parse_expr(ps) elts.append(e) - _skip_nl(ps) # 末元素无尾逗号时收尾(多行元组) + _skip_nl(ps) # finish when the last element has no trailing comma (multi-line tuple) _expect_op(ps, TokOp.RPar) node = Tuple(ps.pool, elts, ASTCtx.Load) _set_pos(node, lineno, col, 0, 0) @@ -1981,7 +1860,7 @@ def _parse_paren(ps: rawptr[Parser]) -> rawptr[AST]: 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) 单表达式括号跨行收尾 + _skip_nl(ps) # (expr\n) close a single-expression paren spanning lines _expect_op(ps, TokOp.RPar) # Check for generator expression if _cur_kw(ps) == Keyword.For: @@ -2022,7 +1901,7 @@ def _parse_list(ps: rawptr[Parser]) -> rawptr[AST]: break e: rawptr[AST] = _parse_expr(ps) elts.append(e) - _skip_nl(ps) # 末元素无尾逗号时收尾(base64.vp 多行列表) + _skip_nl(ps) # finish when the last element has no trailing comma (multi-line list) _expect_op(ps, TokOp.Rsqb) node = List(ps.pool, elts, ASTCtx.Load) _set_pos(node, lineno, col, 0, 0) @@ -2067,7 +1946,7 @@ def _parse_brace(ps: rawptr[Parser]) -> rawptr[AST]: v: rawptr[AST] = _parse_expr(ps) keys.append(k) values.append(v) - _skip_nl(ps) # 末元素无尾逗号时收尾 + _skip_nl(ps) # finish when the last element has no trailing comma _expect_op(ps, TokOp.RBrace) node = Dict(ps.pool, keys, values) _set_pos(node, lineno, col, 0, 0) @@ -2080,12 +1959,12 @@ def _parse_brace(ps: rawptr[Parser]) -> rawptr[AST]: elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) elts.append(first) while _accept_op(ps, TokOp.Comma): - _skip_nl(ps) # 括号内跨行集合字面量 + _skip_nl(ps) # cross-line set literal inside braces if _match_op(ps, TokOp.RBrace): break e: rawptr[AST] = _parse_expr(ps) elts.append(e) - _skip_nl(ps) # 末元素无尾逗号时收尾 + _skip_nl(ps) # finish when the last element has no trailing comma _expect_op(ps, TokOp.RBrace) node = Set(ps.pool, elts) _set_pos(node, lineno, col, 0, 0) @@ -2113,7 +1992,7 @@ def _parse_sub_expr(ps: rawptr[Parser], src: str) -> rawptr[AST]: def _append_str_value(pool: rawptr[memhub.MemManager], values: rawptr[list[rawptr[AST]]], node: rawptr[AST]): - """将字符串节点展开加入 values(JoinedStr 展开其 values,其余直接追加)""" + """Flatten a string node into values (JoinedStr expands its values, everything else is appended)""" if node is None: return if node.kind() == ASTKind.JoinedStr: @@ -2129,16 +2008,16 @@ def _append_str_value(pool: rawptr[memhub.MemManager], def _concat_str_nodes(pool: rawptr[memhub.MemManager], a: rawptr[AST], b: rawptr[AST]) -> rawptr[AST]: - """合并相邻字符串字面量节点(Python 隐式拼接 "a" "b" → "ab") + """Merge adjacent string literal nodes (Python implicit concatenation "a" "b" -> "ab"). - 纯常量字符串直接拼接文本;涉及 f-string(JoinedStr/FormattedValue)时 - 合并到 JoinedStr 的 values 列表。 + Pure constant strings are concatenated as text; f-strings (JoinedStr/FormattedValue) are merged + into a JoinedStr values list. """ if a is None: return b if b is None: return a - # 两个都是常量字符串 → 直接拼接文本 + # Both are constant strings -> concatenate the text directly if a.kind() == ASTKind.Constant and b.kind() == ASTKind.Constant: ca_c: rawptr[Constant] = (rawptr[Constant])(a) cb_c: rawptr[Constant] = (rawptr[Constant])(b) @@ -2151,7 +2030,7 @@ def _concat_str_nodes(pool: rawptr[memhub.MemManager], 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 + # Involves an f-string -> merge into a 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) @@ -2199,8 +2078,8 @@ def _parse_fstring(ps: rawptr[Parser], content: str, while j < content_len and depth > 0: c: char = content[j] if c == '\'' or c == '"': - # 跳过表达式块内的嵌套字符串字面量(PEP 701 同引号嵌套), - # 避免其内部字符干扰 {} 深度/转换/格式分隔符定位 + # Skip nested string literals inside an expression block (PEP 701 same-quote nesting) + # so their characters do not confuse the {} depth / conversion / format delimiter scan nq: char = c j += 1 while j < content_len: @@ -2329,7 +2208,7 @@ def _parse_comprehension(ps: rawptr[Parser], elt: rawptr[AST], 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。""" + Returns generators list.""" gens: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) while True: is_async: int = 0 @@ -2448,7 +2327,7 @@ def _parse_varargslist(ps: rawptr[Parser], is_lambda: int = 0) -> rawptr[AST]: _skip_nl(ps) if _match_op(ps, TokOp.RPar): break - # *args 或 * (kwonly separated) + # *args or * (kwonly separated) if _match_op(ps, TokOp.Star): _advance(ps) _skip_nl(ps) @@ -2489,43 +2368,37 @@ def _parse_varargslist(ps: rawptr[Parser], is_lambda: int = 0) -> rawptr[AST]: def _parse_array_prefix_declarator(ps: rawptr[Parser]) -> rawptr[AST]: - """C 风格前置数组声明符 `[N]T` / `[]T` → `t.CArray[T, N]` / `t.CArray[T, None]` + """C-style leading array declarator `[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` 现象)。 + Consumed only in annotation position (_parse_type_ann); returns None if the current token is not '['. + Desugars to the canonical array form shared with TPC / StubGen / DeclarationGenerator. Must be called + before _parse_test, otherwise `[N]` is parsed as a list literal and the element type then errors. """ if _match_op(ps, TokOp.Lsqb) == 0: return None lb_lineno: int = _cur_lineno(ps) lb_col: int = _cur_col(ps) - _advance(ps) # 消费 '[' + _advance(ps) # consume '[' count_node: rawptr[AST] = None if _match_op(ps, TokOp.Rsqb) == 0: - # 长度表达式(常量/常量表达式;非法长度由下游 ArrayDims 判据拒绝) + # Length expression (constant/constant expression; invalid lengths are rejected downstream) 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) # 缺少 ']' + _record_parse_err(ps) # missing ']' ps.error_count += 1 return None - _advance(ps) # 消费 ']' - # 元素类型:递归 _parse_type_ann,故 `[N]const int` / `[N]int*` 走同一规则源 + _advance(ps) # consume ']' + # Element type: recurse into _parse_type_ann, so `[N]const int` / `[N]int*` share one rule source 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]] 站点同参。 + # Element size must be explicitly 8 (list[T](pool, elem_size) addresses by byte stride; passing 2 + # would write 8-byte pointers with a 2-byte stride -> heap overflow). Same arg as other list sites. elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) elts.append(elem) elts.append(count_node) @@ -2539,44 +2412,17 @@ def _parse_array_prefix_declarator(ps: rawptr[Parser]) -> rawptr[AST]: def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]: - """解析类型注解,支持类型描述符前缀(const / volatile / __attribute__)与 C 风格指针后缀(* / **)。 - - 类型描述符(软关键字,只在注解位置生效;与 TPC 侧 - `SymbolUtils._ExpandTypeDescriptors` / `_ExpandAttributeDescriptors` 的展开产物逐字段同形): - const int → t.CConst | int - volatile int → t.CVolatile | int - const volatile int → t.CConst | t.CVolatile | int(顺序任意,两限定符都累积) - __attribute__(x, y(a)) → t.attr.x | t.attr.y(a)(顺序自由、可与上述混写) - 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` 的解析树同形) - `static` **不在本位置**:存储类标记一律前置(`static 名字: 类型` → 由语句起始的 - pending_static 通道把 t.CStatic 贴到类型联合最左,与手写 `t.CStatic | 类型`、 - `@t.CStatic` 同源)。注解位置的 `static`(`名字: static 类型`)已不再支持, - 会在此**响亮失败**并提示改成前置形式(见 _IsStaticDescriptorKeyword)。 - 非法形态**响亮失败**(非静默,不丢描述符):缺类型(`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)。 + """Parse a type annotation: descriptor prefixes (const / volatile / __attribute__) and C-style pointer + suffixes (* / **), field-identical to TPC's expansion. `static` is invalid here; missing type / non-type + follower / bare use fail loudly. Reclaims the Mult/Pow junk _parse_test leaves and rebuilds t.CPtr chains. """ - # 类型描述符前缀:收集 const / volatile 标记(软关键字,仅本位置消费) + # Descriptor prefix: collect const / volatile markers (soft keywords, consumed only here) 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` 逐字段同形;随即清零,只作用于 - # 紧随其后的这一个注解(下一个注解不会继承)。 + # `static` modifier (consumed at statement start): append t.CStatic as the first descriptor marker + # (pasted on the union's left later), then clear it so it applies only to this annotation. if ps.pending_static != 0: ps.pending_static = 0 if mods is None: @@ -2588,43 +2434,30 @@ def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]: while _IsDescriptorKeyword(ps) != 0 or _IsMagicAttributeCall(ps) != 0 \ or _IsStaticDescriptorKeyword(ps) != 0: if _IsStaticDescriptorKeyword(ps) != 0: - # 注解位置的 `static`(`名字: static 类型`)**已不再支持**:存储类标记一律 - # 前置(`static 名字: 类型`,或 `@t.CStatic`)。此处响亮失败而不是当类型名—— - # 放开会退化成"叫 static 的类",被建成 opaque 结构体 - # (`@a = global %".static" zeroinitializer`,llvm-link 报 invalid type - # for null constant),报错点远离根因。报错后消费该 token 继续解析类型,让本次 - # 注解仍被完整分析(错误由 _g_parse_err_* 通道统一上报)。 + # Annotation-position `static` (`name: static T`) is no longer supported: storage-class markers + # are always prefixed. Fail loudly here (otherwise `static` becomes an opaque "static" class). _RecordDescriptorErr("注解位置的 static 已不再支持:请写成前置形式" " `static 名字: 类型 = 值`(等价 `名字: t.CStatic | 类型`)", _cur_lineno(ps), _cur_col(ps)) _advance(ps) continue if mods is None: - # 元素大小显式传 8:list[T](pool, elem_size) 按字节步长寻址,传 4 会让 - # 两个及以上描述符(如 `const volatile`)的第 2 个 8 字节指针越位写入。 + # Element size is explicitly 8: list[T](pool, elem_size) addresses by byte stride, so passing 4 + # would make the 2nd 8-byte pointer (e.g. for `const volatile`) write out of bounds. 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.`(带调用则保留实参)后并入同一 markers 列表,由 - # _PrependDescriptorMarkers 贴到类型联合最左(与 TPC 文本展开同形)。 - # - # ⚠ 只用 _parse_call 解析**这个调用本身**,绝不能用 _parse_test:后者是 - # 通用表达式解析器,解析完 `__attribute__(...)` 会继续吃 postfix trailer, - # 把紧随的 C 风格前置数组 `[]T` 的 `[]` 当作**合法空下标**吞掉 - # (_parse_slice 显式支持空下标 `T[]`,见其注释)——全程**零报错**, - # 但 ac_node 已不是 Call ⇒ 本循环 break、markers 一个都没收集,且 - # `_match_op(Lsqb)` 此时看到的是元素类型 ⇒ 数组分支被跳过 ⇒ 注解整体 - # 退化成裸元素类型。现场:main.vp:44 - # V_TPV_VERSION: __attribute__(section(".PRVERSN")) []char = "…" - # 被解析成 `char`(stub 记 `V_TPV_VERSION: char`、IR 出 - # `global i8 ` 非法、段名 ".PRVERSN" 丢失)。 + # C-style `a: __attribute__(section(".text.wo"), aligned(16)) const int = ...`: same position + # and order-free mixing as const / volatile; each argument desugars to `t.attr.` and joins + # the same markers list. + # Use only _parse_call for this call itself, never _parse_test: the latter is a general expression + # parser and would swallow the following `[]T` as a legal empty subscript, silently degrading the + # annotation to a bare element type with zero errors reported. mka_lineno: int = _cur_lineno(ps) mka_col: int = _cur_col(ps) mka_fn: rawptr[AST] = Name(ps.pool, "__attribute__", ASTCtx.Load) _set_pos(mka_fn, mka_lineno, mka_col, 0, 0) - _advance(ps) # 消费 `__attribute__` - ac_node: rawptr[AST] = _parse_call(ps, mka_fn) # 只解析 `( 实参表 )` + _advance(ps) # consume `__attribute__` + ac_node: rawptr[AST] = _parse_call(ps, mka_fn) # parse only `( args )` if ac_node is None: break if ac_node.kind() != ASTKind.Call: @@ -2641,10 +2474,8 @@ def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]: 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` 两种。 + # C-style leading array declarator `[N]T` / `[]T` (incl. `const [N]T`): desugar in place to + # `t.CArray[T, N]` / `t.CArray[T, None]` and paste the already-collected descriptors onto it. if _match_op(ps, TokOp.Lsqb) != 0: arr: rawptr[AST] = _parse_array_prefix_declarator(ps) if arr is None: @@ -2653,8 +2484,8 @@ def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]: 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)`): - # 响亮失败——**不**把描述符静默丢掉。 + # Missing type after a descriptor (`a: const` / `a: const = 1` / `def f(a: const)`): + # fail loudly -- never silently drop the descriptor. _RecordDescriptorErr("描述符后缺少类型", mod_lineno, mod_col) ps.error_count += 1 return None @@ -2662,18 +2493,18 @@ def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]: base: rawptr[AST] = _parse_test(ps) if base is None: if mod_count > 0: - # 描述符已消费但类型解析失败(如 `a: const = 1`):响亮失败保描述符报错。 + # Descriptor consumed but type parsing failed (e.g. `a: const = 1`): fail loudly keeping the descriptor error. _RecordDescriptorErr("描述符后缺少类型(类型解析失败)", mod_lineno, mod_col) - _record_parse_err(ps) # 注解解析失败:记录具体位置(error_count 已在 _parse_test 内增加) + _record_parse_err(ps) # annotation parse failed: record position (error_count already bumped in _parse_test) return None if mod_count > 0 and base.kind() == ASTKind.Constant: - # 后随非类型(`a: const 1` / `a: const "x"` / `a: const True`):响亮失败 - # (描述符只修饰类型,常量不是类型——不得静默降级/丢弃)。 + # Non-type follower (`a: const 1` / `a: const "x"` / `a: const True`): fail loudly, since + # descriptors modify types only and constants are not types -- never silently degrade or drop. _RecordDescriptorErr("描述符后随非类型(描述符只修饰类型)", mod_lineno, mod_col) ps.error_count += 1 return None ptr_level: int = 0 - # 回收垃圾节点(_parse_test 对后缀 * / ** 的消费产物) + # Reclaim the junk nodes left by _parse_test consuming the * / ** suffixes if base.kind() == ASTKind.BinOp: gb: rawptr[BinOp] = (rawptr[BinOp])(base) if gb.op == OpKind.Mult and gb.right is None: @@ -2689,21 +2520,21 @@ def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]: ptr_level += 3 base = gb.left if ptr_level > 0: - ps.error_count = before_ec # 回收成功,恢复错误计数避免整文件解析失败 - # 循环消费残留后缀('**' 是词法单 token 计 2 层,'*' 计 1 层) + ps.error_count = before_ec # reclaim succeeded: restore error count to avoid whole-file parse failure + # Consume remaining suffixes ('**' is one token counting as 2 layers, '*' as 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 层) + # Build n nested base | t.CPtr layers for li_an in range(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) - # 描述符最后粘贴:与 TPC 文本展开同形(见 _PrependDescriptorMarkers) + # Paste descriptors last (same shape as TPC text expansion; see _PrependDescriptorMarkers) if mod_count > 0: base = _PrependDescriptorMarkers(ps, base, mods, mod_count) return base @@ -2736,10 +2567,9 @@ def _parse_suite_into_children(ps: rawptr[Parser], parent: rawptr[AST], """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 ⇒ 除类体外的 - 所有套件拥有者天然正确,无需逐个改动调用点。 + owner_is_class: whether this suite's direct owner is a ClassDef. During suite parsing ps.in_class_body + is set so _ParseVirtualModified can tell if `virtual def`/`novirtual def` is directly in a class body + (function/branch/loop bodies do not count). Default 0 keeps all non-class owners correct. """ prev_in_class: int = ps.in_class_body ps.in_class_body = owner_is_class @@ -2755,7 +2585,7 @@ def _parse_suite_into_children(ps: rawptr[Parser], parent: rawptr[AST], sidx: int = 0 while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker: sidx += 1 - # fail-fast 防卡死:步数超预算(token 链表成环 / 游标原地打转)立即报错返回 + # fail-fast anti-hang: over-budget steps (cyclic token list / cursor spinning) report and return if ps.step_count > ps.step_limit: _record_parse_err(ps) return @@ -2782,61 +2612,46 @@ def _parse_statement_into(ps: rawptr[Parser], parent: rawptr[AST]): 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 分支,语义逐字不变。 + # define constant declaration: `define NAME[: T] = ` (no parentheses). Must run before + # _parse_simple_stmt_into, otherwise `define` is parsed as an identifier expression and NAME is left + # as a stray token. `define NAME(...)` (macro function) is excluded by _is_define_const's lookahead. 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),不静默。 + # typedef alias declaration: `typedef T1 = T2` (statement-level reserved word, no lookahead since + # typedef is never an identifier in existing sources). Invalid forms fail loudly in _parse_typedef. if _is_typedef(ps) != 0: td_stmt: rawptr[AST] = _parse_typedef(ps) if td_stmt is not None: parent.append(td_stmt) return - # export / extern 变量声明(软关键字):`export NAME: T [= v]` / - # `extern NAME: T`(**不带**初始值)→ 带存储类标记的模块级 AnnAssign - # (见 _parse_export_extern_var)。函数形态(`export NAME(` / - # `extern def NAME(`)被 lookahead 排除,语义逐字不变。 + # export / extern variable declaration (soft keyword): `export NAME: T [= v]` / `extern NAME: T` + # -> a module-level AnnAssign with storage-class markers; function forms are excluded by lookahead. if _is_export_extern_var(ps) != 0: ee_stmt: rawptr[AST] = _parse_export_extern_var(ps) if ee_stmt is not None: parent.append(ee_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 文本展开、再判修饰符)结果一致。 + # virtual / novirtual modifier (soft keyword): only at statement start before class/def; synthesizes + # @t.CVTable / @t.NoVTable then proceeds with class/def parsing. Placed before the class/def dispatch. if _IsVirtualModifier(ps) != 0: _ParseVirtualModified(ps, parent) return - # inline 修饰符(软关键字):只在"语句起始 + inline + 紧跟函数定义"时生效, - # 合成既有装饰器 @t.always_inline 后照常走 def 解析(见 _ParseInlineModified)。 - # 与 virtual/novirtual 同一位置、同一套 pending_decorators 通道。 + # inline modifier (soft keyword): only at statement start before a function definition; synthesizes + # @t.always_inline then proceeds with def parsing (same pending_decorators channel as virtual). 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 侧消费;此处只归一两种书写形态。 + # static modifier (soft keyword): statement start + static + (def | `Name :`). The function branch + # synthesizes @t.CStatic (returns); the variable branch flags pending_static for _parse_type_ann. if _IsStaticModifier(ps) != 0: if _ParseStaticModified(ps, parent) != 0: return - # 类型描述符裸用:整条语句只有 `const` / `volatile`(缺类型)⇒ 响亮失败。 - # 只在"描述符串后直接换行/文件尾/分号"时命中(软关键字边界),故 - # `const = 5`(作标识符的赋值)/ `t.attr.const()` 等写法不命中、语义不变。 - # 记录自定义报文后**继续走通用 simple_stmt 分派**:既消费 token(不制造 - # 死循环),也不覆盖首次错误(_record_parse_err 只记首次错误)。 + # Bare descriptor use: the whole statement is only `const` / `volatile` (missing type) -> fail loudly. + # Matches only when followed by newline/EOF/semicolon (soft-keyword boundary), so `const = 5` / + # `t.attr.const()` do not match. After recording, continue the generic simple_stmt dispatch. if _IsBareDescriptor(ps) != 0: _RecordDescriptorErr("裸用描述符(整条语句只有描述符,缺类型)", _cur_lineno(ps), _cur_col(ps)) @@ -2872,19 +2687,17 @@ def _parse_simple_stmt_into(ps: rawptr[Parser], parent: rawptr[AST]): s: rawptr[AST] = _parse_small_stmt(ps) if s is None: break parent.append(s) - # 先跳过 Nl(括号内换行,如多行函数调用参数间的换行,属合法 token) + # First skip Nl (in-paren newline, e.g. between multi-line call args; a legal token) _skip_nl(ps) - # 语句结束检查:simple_stmt 必须以换行/文件尾结束。 - # 残留 token(如相邻表达式 `func12344444 avg3(...)` 被静默拆成多条语句、 - # `def` 缺失的关键字函数定义残留)是语法错误(fail-fast),禁止静默, - # 并仿 Python 报告错误位置(SyntaxError at line/col)。 + # Statement-end check: simple_stmt must end with a newline/EOF. Stray tokens (e.g. adjacent + # expressions split into multiple statements, or a def missing its keyword) are a fail-fast syntax + # error, reported Python-style (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 的上下文 + # Stray tokens (e.g. adjacent expressions split into statements, or a def missing its keyword) are a + # fail-fast syntax error. The message is no longer self-assembled: position/message go through the + # parse error channel with prev/next tokens appended, output Python-style by the caller's + # dump_parse_traceback (Traceback + File/line + source line + caret + SyntaxError). + # Locate: walk the list from the head to find cur, recording prev and next token context. prev_tok: rawptr[Token] = None walk_tok: rawptr[Token] = ps.tokens while walk_tok is not None and walk_tok is not ps.cur: @@ -2916,9 +2729,8 @@ def _parse_simple_stmt_into(ps: rawptr[Parser], parent: rawptr[AST]): def _parse_small_stmt(ps: rawptr[Parser]) -> rawptr[AST]: """small_stmt: various simple statements""" kw: int = _cur_kw(ps) - # fallthrough 关键字(硬):仅在 switch 的 case 体内合法,语义是"落到下一个 - # case 体"。合法位置由生成侧(StmtGenDeclMixin.translate_fallthrough)判定, - # 在 match 内或 switch 外写出即 fail-fast。 + # fallthrough keyword (hard): legal only inside a switch case body ("fall to the next case"). + # Legality is checked by the generator; writing it in a match or outside a switch is fail-fast. if kw == Keyword.FallThrough: ft_lineno: int = _cur_lineno(ps) ft_col: int = _cur_col(ps) @@ -3366,7 +3178,7 @@ def _parse_if_tail(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]: scratch: rawptr[Module] = Module(ps.pool) _parse_suite_into_children(ps, scratch) return scratch.children - return list[rawptr[AST]](ps.pool, 8) # 空 list + return list[rawptr[AST]](ps.pool, 8) # empty list # ============================================================ @@ -3515,20 +3327,20 @@ def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: is_export_fd: int = 0 is_define_fd: int = 0 if _cur_kw(ps) == Keyword.Extern: - # extern 声明:extern [def] A(...) [-> T] [": pass" 可选] + # extern declaration: extern [def] A(...) [-> T] [optional ": 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" 可选] + # export declaration: export [def] A(...) [-> T] [optional ": 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 内联) + # define macro function: syntax identical to def; t.CDefine is appended to the return annotation + # (StubGen keeps it as-is -> C #define / alwaysinline) is_define_fd = 1 _advance(ps) # define else: @@ -3538,10 +3350,8 @@ def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: 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 兜底。) + # PEP 695 generic function type parameters: def add[T](a: T, b: T) -- parsed and stored into + # type_params, consistent with the class parser. type_params maps to int as a fallback during translation. type_params_fd: rawptr[list[str]] = None if _accept_op(ps, TokOp.Lsqb): type_params_fd = list[str](ps.pool, 8) @@ -3551,7 +3361,7 @@ def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: if _cur_type(ps) == TokenType.Name: tp_name_fd: str = _cur_str(ps) _advance(ps) - # 跳过可选 bound/约束: [T: bound] 或 [T: (A, B)] + # Skip the optional bound/constraint: [T: bound] or [T: (A, B)] if _match_op(ps, TokOp.Colon): _advance(ps) _parse_test(ps) @@ -3567,8 +3377,8 @@ def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: if _accept_op(ps, TokOp.RArrow): returns = _parse_type_ann(ps) if is_extern_fd: - # extern 声明:返回注解改写为 <原类型> | t.State,复用既有 - # State/外部 C 函数识别路径(vpi 注解含 "State" → 定义端裸名 + 不生成函数体) + # extern declaration: rewrite the return annotation to | t.State, reusing the existing + # State/external-C-function path (vpi annotation containing "State" -> bare name, no function body) state_attr_fd: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), "State", ASTCtx.Load) @@ -3577,8 +3387,8 @@ def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: else: returns = state_attr_fd elif is_export_fd: - # export 声明:返回注解改写为 <原类型> | t.CExport,复用既有 - # CExport 识别路径(vpi 注解含 "CExport" → 定义端裸名 + 不加模块 SHA1 头) + # export declaration: rewrite the return annotation to | t.CExport, reusing the existing + # CExport path (vpi annotation containing "CExport" -> bare name, no module SHA1 header) export_attr_fd: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), "CExport", ASTCtx.Load) @@ -3587,8 +3397,8 @@ def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: else: returns = export_attr_fd elif is_define_fd: - # define 宏函数:返回注解改写为 <原类型> | t.CDefine,复用既有 - # CDefine 宏函数识别路径(StubGen 保留原样 → C #define / alwaysinline) + # define macro function: rewrite the return annotation to | t.CDefine, reusing the + # existing CDefine path (StubGen keeps it as-is -> C #define / alwaysinline) define_attr_fd: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), "CDefine", ASTCtx.Load) @@ -3609,10 +3419,9 @@ def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: _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 个扫描失败的根因) + # extern/export without ': pass' (extern A(...) -> int): empty body, declaration only. The NewLine + # must be consumed here: the module/class statement loop only skips Nl, so a leftover NewLine would be + # parsed as an expression next round and fail parse_tokens. if _cur_type(ps) == TokenType.NewLine: _advance(ps) _set_pos(node, lineno, col, 0, 0) @@ -3650,12 +3459,11 @@ def _parse_classdef(ps: rawptr[Parser]) -> rawptr[AST]: # 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 多继承跨行根因) + # Cross-line base list inside parens (class X(A,\n B):): skip the newline after a comma, + # otherwise cur=NL is not Name/Comma -> break -> _expect_op(RPar) reports an unexpected token. _skip_nl(ps) if _cur_type(ps) == TokenType.EndMarker: break - # base 或 keyword=value + # base or 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) @@ -3668,37 +3476,33 @@ def _parse_classdef(ps: rawptr[Parser]) -> rawptr[AST]: bases.append(e) if not _accept_op(ps, TokOp.Comma): break - _skip_nl(ps) # 末基类后换行收尾(class X(A,\n B):) + _skip_nl(ps) # finish the newline after the last base (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) + # owner_is_class=1: ps.in_class_body=1 during the class body, so virtual/novirtual modifiers can tell + # whether a method-level modifier is directly in the class body (see _ParseVirtualModified). _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 编译期常量)只有**同一条** -# 实现路径,不存在第二套枚举实现。 +# enum sugar (fully equivalent to `class X(t.CEnum):`): `enum Name:` header + member lines +# (`A` / `A: T` / `A = 0`). The synthesized ClassDef is field-identical to _parse_classdef output, so +# downstream (ClassGenEnum._translate_enum_def -> EnumTable) has a single implementation path. # ============================================================ 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 成员统一形态)。 + """Rewrite a single member-name node into a `Name: t.State` AnnAssign (the uniform enum member form). - target=Name(id, Store)、annotation=Attribute(Name('t'), 'State')、 - value=None、simple=1 —— 与手写 `class X(t.CEnum): A: t.State` 逐字段同形。 + target=Name(id, Store), annotation=Attribute(Name('t'), 'State'), value=None, simple=1 -- + field-identical to a hand-written `class X(t.CEnum): A: t.State`. """ ann: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), @@ -3707,33 +3511,23 @@ def _enum_member_annassign(ps: rawptr[Parser], owner: rawptr[AST], 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 一致地维护父指针 + aa.parent = owner # maintain the parent pointer like AST.append does return aa def _convert_enum_bare_members(ps: rawptr[Parser], node: rawptr[ClassDef]): - """把 enum 体里的裸名 / 逗号分组成员行就地改写为 `Name: t.State` 的 AnnAssign。 + """Rewrite bare-name / comma-grouped member lines in an enum body into `Name: t.State` AnnAssigns. - 成员收集(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 承担(本函数只看语句种类)。 + Member collection only recognizes Assign / AnnAssign Name targets, so bare names and comma groups + (`A, B, C`) were silently dropped; now `A` -> `A: t.State` and `A, B, C` -> three AnnAssigns. Valued + or annotated groups are not rewritten here and are caught by the registration-site fail-fast. """ 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]](...)`)逐字同形。 + # Rebuild the child list: one comma-group line -> several AnnAssigns. list has no insert, so rebuild + # entirely; the assignment form matches AST.append in base.vp. node.children = list[rawptr[AST]](ps.pool, 8) out: rawptr[list[rawptr[AST]]] = node.children for i in range(cn): @@ -3771,18 +3565,10 @@ def _convert_enum_bare_members(ps: rawptr[Parser], node: rawptr[ClassDef]): def _parse_define_const(ps: rawptr[Parser]) -> rawptr[AST]: - """`define NAME[: T] = <字面量>` → 与 `NAME: [T |] t.CDefine = <字面量>` - 逐字段同形的 AnnAssign(define 常量声明语法糖)。 + """`define NAME[: T] = ` -> an AnnAssign field-identical to `NAME: [T |] t.CDefine = `. - - 与手写 `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,不静默)。 + Same CDefine registration path as hand-written `NAME: t.CDefine = value`. With a type, T goes before + the union (`T | t.CDefine`), matching TPC's expansion. Must end with newline/EOF (fail-fast otherwise). """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -3826,35 +3612,27 @@ def _parse_define_const(ps: rawptr[Parser]) -> rawptr[AST]: def _parse_typedef(ps: rawptr[Parser]) -> rawptr[AST]: - """`typedef T1 = T2` → 与 `T1: t.CTypedef = T2` 逐字段同形的 AnnAssign。 + """`typedef T1 = T2` -> an AnnAssign field-identical to `T1: t.CTypedef = T2`. - - 与手写 `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 同口径)。 + Same typedef registration/lookup path as the hand-written form (CTypedef branch -> register_ctypedef); + no second implementation. Invalid forms (missing name / '=' / RHS) fail loudly; must end with newline/EOF. """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) _advance(ps) # typedef if _cur_type(ps) != TokenType.Name: - # 缺别名(如 `typedef = 1` / `typedef : x`):响亮失败。 + # Missing alias (e.g. `typedef = 1` / `typedef : x`): fail loudly. _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`):响亮失败。 + # Missing '=' (e.g. `typedef A`): fail loudly. return None value: rawptr[AST] = _parse_test(ps) if value is None: - # 缺 RHS(如 `typedef A = `):响亮失败。 + # Missing RHS (e.g. `typedef A = `): fail loudly. ps.error_count += 1 _record_parse_err(ps) return None @@ -3876,17 +3654,11 @@ def _parse_typedef(ps: rawptr[Parser]) -> rawptr[AST]: def _parse_export_extern_var(ps: rawptr[Parser]) -> rawptr[AST]: - """`export NAME: T [= v]` / `extern NAME: T` → 带存储类标记的模块级 AnnAssign。 + """`export NAME: T [= v]` / `extern NAME: T` -> a module-level AnnAssign with storage-class markers. - - **export**:注解改写为 `T | t.CExport`,与 `export def` **同一标记、同一消费 - 路径**(定义端裸名,不加模块 SHA1 头——内联汇编 / C 符号对接必需)。 - 允许带初始值;`export tss_buf: [104]uint8`(无初始值)生成零初始化全局定义。 - - **extern**:注解改写为 `T | t.State`(= t.CExtern | t.CExport:仅声明 + 裸名), - 与 `extern def` 同口径。**不允许初始值**(符号定义在外部), - `extern a: int = 1` → `_record_parse_err` **响亮失败**(非静默)。 - - 行号/列号取自 `export`/`extern` 关键字位置,报错回显定位到真实源码行; - 语句必须换行/文件尾结束(与 _parse_typedef / _parse_define_const 同口径)。 + export rewrites the annotation to `T | t.CExport` (same marker/path as `export def`: bare name, no + module SHA1 header) and allows an initializer; extern rewrites to `T | t.State` (declaration + bare + name) and forbids an initializer (`extern a: int = 1` fails loudly). Must end with newline/EOF. """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -3895,21 +3667,21 @@ def _parse_export_extern_var(ps: rawptr[Parser]) -> rawptr[AST]: is_extern = 1 _advance(ps) # export / extern if _cur_type(ps) != TokenType.Name: - # 缺变量名(如 `export : int`):响亮失败。 + # Missing variable name (e.g. `export : int`): fail loudly. _record_parse_err(ps) ps.error_count += 1 return None name: str = _cur_str(ps) _advance(ps) if _expect_op(ps, TokOp.Colon) == 0: - # 缺 `:`(如 `export x`):响亮失败。 + # Missing ':' (e.g. `export x`): fail loudly. return None ann: rawptr[AST] = _parse_type_ann(ps) if ann is None: ps.error_count += 1 _record_parse_err(ps) return None - # 存储类标记:export → t.CExport(裸名导出);extern → t.State(声明 + 裸名) + # Storage-class marker: export -> t.CExport (bare-name export); extern -> t.State (declaration + bare name) storage_attr: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), "State" if is_extern != 0 else "CExport", @@ -3918,7 +3690,7 @@ def _parse_export_extern_var(ps: rawptr[Parser]) -> rawptr[AST]: value: rawptr[AST] = None if _match_op(ps, TokOp.Equal): if is_extern != 0: - # extern 变量是外部定义:本模块不允许带初始值。 + # An extern variable is externally defined: no initializer allowed in this module. _RecordDescriptorErr("extern 变量声明不能带初始值(符号定义在外部)", lineno, col) ps.error_count += 1 @@ -3944,17 +3716,12 @@ def _parse_export_extern_var(ps: rawptr[Parser]) -> rawptr[AST]: def _parse_enumdef(ps: rawptr[Parser]) -> rawptr[AST]: - """enum NAME: 语法糖 → 与 `class NAME(t.CEnum):` 逐字段同形的 ClassDef。 + """enum NAME: sugar -> a ClassDef field-identical to `class NAME(t.CEnum):`. - 成员书写形态(与 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' 不能是指针类型")定位到真实源码行。 + Member forms (same as TPC's _ExpandEnumBlocks): bare `A` -> `A: t.State` (auto-numbered, i32 width); + comma group `A, B` -> several `A: t.State` / `B: t.State`; annotated `A: T` and valued `A = 0` as-is. + Base is always Attribute(Name('t'), 'CEnum'), field-identical to `class X(t.CEnum):`. Line/col inherited + from each member statement. """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -3979,16 +3746,11 @@ def _parse_enumdef(ps: rawptr[Parser]) -> rawptr[AST]: def _parse_uniondef(ps: rawptr[Parser]) -> rawptr[AST]: - """union NAME: 语法糖 → 与 `class NAME(t.CUnion):` 逐字段同形的 ClassDef。 + """union NAME: sugar -> a ClassDef field-identical to `class NAME(t.CUnion):`. - 成员按普通类体(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 成员必须带注解/类型)。 + Members are parsed as a normal class body (AnnAssign). Base is always Attribute(Name('t'), 'CUnion'), + field-identical to `class X(t.CUnion):`; TPC detects unions via `issubclass(cls, t.CUnion)`, so both + sides share the existing union path (offset 0, size = widest member). No bare-name conversion. """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -4012,18 +3774,11 @@ def _parse_uniondef(ps: rawptr[Parser]) -> rawptr[AST]: def _parse_renumdef(ps: rawptr[Parser]) -> rawptr[AST]: - """renum NAME: 语法糖 → 与 `class NAME(t.REnum):` 逐字段同形的 ClassDef。 + """renum NAME: sugar -> a ClassDef field-identical to `class NAME(t.REnum):`. - 成员按普通类体解析:变体为**嵌套 ClassDef**(`class Some:` + 字段注解), - 无载荷变体为普通赋值(`None_ = 0` / `Empty = 1`),与手写 - `class Option(t.REnum):`(Test/EnumTest/App/enumtest.vp:13)逐字同形。 - 基类固定为 Attribute(Name('t'), 'REnum'),与手写写法 bases[0] 逐字段同形 - ——TPC 侧靠 `issubclass(cls, t.REnum)` 判 REnum(HandlesClassDef._ClassifyBase), - 故两侧落到同一条既有的 REnum 语义路径(tag + 变体 payload)。 - 行号/列号继承自语句本身,报错回显定位到真实源码行。 - - 与 `enum NAME:` 的差异:不做裸名成员转换(enum 的 `A` → `A: t.State`); - 与 `union NAME:` 的差异:基类为 t.REnum 而非 t.CUnion。 + Members are parsed as a normal class body: a variant is a nested ClassDef (`class Some:` + field + annotations) and a payload-less variant is a plain assignment (`None_ = 0`). Base is always + Attribute(Name('t'), 'REnum'); TPC detects REnum via `issubclass(cls, t.REnum)`. No bare-name conversion. """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -4047,11 +3802,10 @@ def _parse_renumdef(ps: rawptr[Parser]) -> rawptr[AST]: def _RecordStructErr(detail: str, lineno: int, col: int, extra: str): - """记录 struct 的非法形态(fail-fast;只记首次)。 + """Record an invalid struct form (fail-fast; first only), same reporting path as _RecordVirtualErr. - 与 _RecordVirtualErr 同一上报路径(parse_tokens → dump_parse_traceback 补全 - `文件:行:列` 与源码行 + `^` 列标记)。报文一律用 f-string 构建:本语言的 str - 是 i8*,没有 `str + str` 的语义,`"a" + x` 会退化成裸指针 add(非法 IR)。 + Messages always use f-string interpolation: this language's str is i8*, so `str + str` degrades to + raw pointer add and illegal IR. """ global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg if _g_parse_err_line != 0: @@ -4073,26 +3827,12 @@ def _RecordStructErr(detail: str, lineno: int, col: int, extra: str): def _parse_structdef(ps: rawptr[Parser]) -> rawptr[AST]: - """struct NAME: / struct NAME(Base): 语法糖 → 与 - `@t.NoVTable class NAME(t.CStruct):` 逐字段同形的 ClassDef。 + """struct NAME: / struct NAME(Base): sugar -> a ClassDef field-identical to + `@t.NoVTable class NAME(t.CStruct):`. - 语义(用户 2026-10-01 定):与 class 同形,但只能表示数据类型: - · 强制无虚表:合成伪装饰器 @t.NoVTable(与手写 @t.NoVTable 逐字段同形, - ClassGenBase._has_decorator / TPC _resolve_class_flags 零新增判据)。 - 即使继承自带虚表的父类,本类也不生成虚表(_detect_vtable_status 被 - 类级 NoVTable 关闭),与"C 的 struct 没有虚表"一致。 - · 数据类标记:基类列表**追加** Attribute(Name('t'), 'CStruct'),追加在用户 - 基类**之后**(不移位真实父类的解析顺序)。TPV ClassGenBase._is_marker_base - 与 TPC lib/includes/t.py 的 _MARKER_BASES 都认 CStruct(都不当真实父类), - 两侧对称;于是"纯数据类(无方法无虚表无 __new__)"的模块级变量保持 - **值实例**(TranslatorGenMixin 既有口径)。 - · 禁止类体内定义 def:解析完类体后检查直接子节点,遇 FunctionDef 即 - fail-fast(_RecordStructErr)。嵌套 struct 由它自己的解析负责自检。 - · 禁止 virtual/novirtual struct:见 _ParseVirtualModified。 - 允许出现在 REnum 内部(与 class 一样:REnum 的变体本就是嵌套 ClassDef), - 也允许 struct 内再写 struct —— 与 class 内写 class **完全同口径**(语法层不禁止; - 内层是否发射为独立结构体由类体 ClassDef 子节点的既有路径决定)。 - 行号/列号继承自语句本身,报错回显定位到真实源码行。 + Data-only: forces no vtable via the pseudo decorator @t.NoVTable, appends Attribute(Name('t'), 'CStruct') + after the user bases as a data-class marker, and forbids def in the body (fail-fast via _RecordStructErr). + Nestable in REnum and in another struct. """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -4104,7 +3844,7 @@ def _parse_structdef(ps: rawptr[Parser]) -> rawptr[AST]: return None name: str = _cur_str(ps) _advance(ps) - # 类型参数 [T] / [T, U](与 class 同口径,PEP 695 泛型) + # Type parameters [T] / [T, U] (same as class, PEP 695 generics) type_params: rawptr[list[str]] = None if _accept_op(ps, TokOp.Lsqb): type_params = list[str](ps.pool, 8) @@ -4122,7 +3862,7 @@ def _parse_structdef(ps: rawptr[Parser]) -> rawptr[AST]: _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(与 _parse_classdef 同一解析路径,含跨行与 keyword=value) + # bases / keywords (same parse path as _parse_classdef, including cross-line and keyword=value) if _accept_op(ps, TokOp.LPar): while not _match_op(ps, TokOp.RPar): _skip_nl(ps) @@ -4140,11 +3880,11 @@ def _parse_structdef(ps: rawptr[Parser]) -> rawptr[AST]: break _skip_nl(ps) _expect_op(ps, TokOp.RPar) - # 数据类标记基类:追加在用户基类之后(真实父类仍在 bases[0],解析顺序不变) + # Data-class marker base: appended after the user bases (real parent stays at bases[0]) bases.append(Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), "CStruct", ASTCtx.Load)) _expect_op(ps, TokOp.Colon) - # 伪装饰器 @t.NoVTable:追加在用户装饰器之后(与 _ParseVirtualModified 同款合并) + # Pseudo decorator @t.NoVTable: appended after the user decorators (same merge as _ParseVirtualModified) dec_list: rawptr[list[rawptr[AST]]] = ps.pending_decorators ps.pending_decorators = None marker: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load), @@ -4158,9 +3898,9 @@ def _parse_structdef(ps: rawptr[Parser]) -> rawptr[AST]: merged.append(dec_list.get(di_s)) merged.append(marker) node: rawptr[ClassDef] = ClassDef(ps.pool, name, bases, keywords, merged, type_params) - # owner_is_class=1:与 class 同口径(类体期间 ps.in_class_body=1) + # owner_is_class=1: same as class (ps.in_class_body=1 during the class body) _parse_suite_into_children(ps, node, 1) - # struct 禁止 def:检查直接子节点 + # struct forbids def: check the direct children chk: rawptr[list[rawptr[AST]]] = node.children if chk is not None: cn_s: sizet = len(chk) @@ -4211,23 +3951,15 @@ def _parse_match(ps: rawptr[Parser]) -> rawptr[AST]: # ============================================================ -# switch statement(C switch 语义,desugar 为 Match(is_switch=1)) -# -# switch : -# case <常量 | 枚举变体>: # 可 `case 1 | 2:` 多值 -# ... -# default: -# ... -# -# 规则与 C switch 一致:case 默认**不穿透**(每个 case 体末尾自动跳出 switch), -# 只有 case 体内写 `fallthrough` 才落到下一个 case。case 标签只支持常量字面量 / -# 枚举变体(不支持序列、映射等结构化模式),与 match 的丰富模式区分开。 +# switch statement (C switch semantics, desugared to Match(is_switch=1)): `switch :` with +# `case :` / `default:` arms. C switch rules: no fall-through unless +# `fallthrough` is written in the case body. Case labels allow only constants / enum variants / +# multi-value `case 1 | 2:`, not structured patterns. # ============================================================ def _is_switch_default_kw(ps: rawptr[Parser]) -> int: - """`default` 是 switch 套件内的上下文关键字:命中 `default:` 或 - `default if :`(后者支持 guard,与 `case _ if ` 一致; - guard 由 _parse_switch_default 解析)""" + """`default` is a context keyword inside a switch suite: matches `default:` or + `default if :` (the latter supports a guard like `case _ if `).""" if _cur_type(ps) != TokenType.Name: return 0 s: str = _cur_str(ps) @@ -4244,7 +3976,7 @@ def _is_switch_default_kw(ps: rawptr[Parser]) -> int: def _RecordSwitchCaseErr(detail: str, lineno: int, col: int): - """记录 switch 的 case 标签非法(fail-fast;只记首次)""" + """Record an invalid switch case label (fail-fast; first only)""" global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg if _g_parse_err_line != 0: return @@ -4262,14 +3994,14 @@ def _RecordSwitchCaseErr(detail: str, lineno: int, col: int): def _CheckSwitchCasePattern(ps: rawptr[Parser], pat: rawptr[AST]) -> int: - """校验 switch 的 case 标签是否合法(只允许常量 / 枚举变体 / 多值或模式)""" + """Validate a switch case label (only constants / enum variants / multi-value patterns allowed)""" if pat is None: return 0 k: int = pat.kind() if k == ASTKind.MatchValue or k == ASTKind.MatchSingleton or k == ASTKind.MatchClass: return 1 if k == ASTKind.MatchAs: - # 仅通配(`case _`,等价 default)合法;带子模式的 as 绑定不合法 + # Only the wildcard (`case _`, equivalent to default) is legal; as-bindings with a subpattern are not ma_w: rawptr[MatchAs] = rawptr[MatchAs](pat) if ma_w.pattern is None: return 1 @@ -4295,14 +4027,11 @@ def _CheckSwitchCasePattern(ps: rawptr[Parser], pat: rawptr[AST]) -> int: def _parse_switch_case(ps: rawptr[Parser]) -> rawptr[AST]: - """switch 的 `case <常量|枚举变体> [if ] ':' suite`(支持 guard) + """switch `case [if ] ':' suite` (guard supported). - 裸名在 match 里是**捕获模式**(MatchAs),在 switch 里应作**值引用**处理 - (对齐 C 的 `case NAME:`),故此处把 `MatchAs(pattern=None, name=X)` 归一为 - `MatchValue(Name('X'))`;`_` 仍是通配,保持不变。 - - guard 与 `_parse_match_case` 同款解析(`case pattern [if guard] ':'`): - `case <值> if ` 的条件约束原样传入 MatchCase.guard。 + A bare name is a capture pattern (MatchAs) in match but a value reference in switch (like C's + `case NAME:`), so `MatchAs(pattern=None, name=X)` is normalized to `MatchValue(Name('X'))`; `_` + stays a wildcard. The guard is parsed like _parse_match_case and passed to MatchCase.guard. """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -4319,7 +4048,7 @@ def _parse_switch_case(ps: rawptr[Parser]) -> rawptr[AST]: if _CheckSwitchCasePattern(ps, pattern) == 0: _RecordSwitchCaseErr("case 标签不是常量/枚举变体", lineno, col) ps.error_count += 1 - # guard 守卫:`case <值> if ':'`(与 _parse_match_case 同款写法) + # guard: `case if ':'` (same form as _parse_match_case) guard: rawptr[AST] = None if _cur_kw(ps) == Keyword.If: _advance(ps) @@ -4332,11 +4061,10 @@ def _parse_switch_case(ps: rawptr[Parser]) -> rawptr[AST]: def _parse_switch_default(ps: rawptr[Parser]) -> rawptr[AST]: - """switch 的 `default [if ] ':' suite` —— desugar 为 `case []`(MatchAs 通配) + """switch `default [if ] ':' suite` -- desugared to `case []` (MatchAs wildcard). - `default` 即 switch 拼写的 `case _`;现与 `case _ if ` 一致地支持 guard - (`default if :`),解析出的 guard 原样传入 MatchCase;无 if 时保持原行为 - (guard=None,即唯一无 guard 通配臂)。 + `default` is switch's spelling of `case _`; it supports a guard like `case _ if `, passed + to MatchCase. Without if the behavior is unchanged (guard=None). """ lineno: int = _cur_lineno(ps) col: int = _cur_col(ps) @@ -4461,9 +4189,8 @@ def _parse_closed_pattern(ps: rawptr[Parser]) -> rawptr[AST]: _advance(ps) if _cur_type(ps) == TokenType.Number: tok = ps.cur - # 一元减 + 整数字面量的联合越界判定(与 _parse_factor 同口径): - # 幅值 > 2^63 的负数字面量(如 -18446744073709551615、-9223372036854775809) - # 结果 < -2^63 ⇒ fail-fast,不静默回绕。 + # Combined unary-minus + integer-literal overflow check (same rule as _parse_factor): + # a negative literal with magnitude > 2^63 yields < -2^63 -> fail-fast, no silent wrap. 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 @@ -4502,7 +4229,7 @@ def _parse_closed_pattern(ps: rawptr[Parser]) -> rawptr[AST]: _set_pos(node, lineno, col, 0, 0) return node _advance(ps) - # Check dotted name(value pattern 或 class pattern) + # Check dotted name (value pattern or 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) @@ -4700,20 +4427,14 @@ def _parse_match_case(ps: rawptr[Parser]) -> rawptr[AST]: # ============================================================ -# __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.`(带调用时保留实参), -# 外层换成 `c.Attribute(...)`。产出的装饰器与手写形态逐字段同形 ⇒ -# ClassGenBase._has_decorator / FuncGenBase.extract_func_attrs / _append_llvm_attr -# 原样识别(零新增判据);也即同时适用于 class 与 def(装饰器链共用本函数)。 -# 容错:实参已是 `t.attr.X` / `c.Attribute(...)` 形态时原样保留(允许混写)。 +# __attribute__ decorator desugar (C-style attribute syntax): `@__attribute__(section(".text.wo"), +# always_inline)` equals hand-written `@c.Attribute(t.attr.section(...), t.attr.always_inline)`. +# Arguments use unprefixed names, each desugared to `t.attr.` (calls keep their args) with the +# outer call becoming `c.Attribute(...)`, so the result is field-identical and recognized as-is. +# Works for class and def; arguments already in `t.attr.X` / `c.Attribute(...)` form are kept. # ============================================================ def _IsMagicAttributeCallee(node: AST*) -> int: - """节点是否为 `__attribute__`(Name 形态,或 X.__attribute__ 的尾段形态)""" + """Whether the node is `__attribute__` (Name form, or the trailing X.__attribute__ form)""" if node is None: return 0 k: int = node.kind() @@ -4730,11 +4451,10 @@ def _IsMagicAttributeCallee(node: AST*) -> int: def _MakeTAttrMarker(ps: rawptr[Parser], arg: AST*) -> AST*: - """把一个 __attribute__ 实参 desugar 为 `t.attr.`(带调用则保留实参) + """Desugar one __attribute__ argument into `t.attr.` (calls keep their arguments). - - `always_inline`(Name) → `t.attr.always_inline` - - `section(".text.wo")`(Call)→ `t.attr.section(".text.wo")` - - 已含 `t.attr.X` 形态(value 也是 Attribute)→ 原样返回 + `always_inline` (Name) -> `t.attr.always_inline`; `section(".text.wo")` (Call) -> `t.attr.section(...)`; + an argument already in `t.attr.X` form is returned as-is. """ if arg is None: return None @@ -4769,7 +4489,7 @@ def _MakeTAttrMarker(ps: rawptr[Parser], arg: AST*) -> AST*: def _DesugarMagicAttribute(ps: rawptr[Parser], expr: AST*) -> AST*: - """`@__attribute__(...)` → `@c.Attribute(t.attr.x, ...)`;其它装饰器原样返回""" + """`@__attribute__(...)` -> `@c.Attribute(t.attr.x, ...)`; other decorators are returned as-is""" if expr is None or expr.kind() != ASTKind.Call: return expr cl: Call* = Call*(expr) @@ -4792,14 +4512,14 @@ def _DesugarMagicAttribute(ps: rawptr[Parser], expr: AST*) -> AST*: def _parse_decorators(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]: - """解析装饰器链,返回 list。""" + """Parse a decorator chain, returning a 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....)` + # C-style `@__attribute__(...)` desugars into the existing `@c.Attribute(t.attr....)` expr = _DesugarMagicAttribute(ps, expr) _skip_nl(ps) if _cur_type(ps) == TokenType.NewLine: @@ -4813,12 +4533,12 @@ def _parse_decorators(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]: # ============================================================ def _parse_module_body(ps: rawptr[Parser], module_node: rawptr[AST]): - """解析模块体,append 到 module_node。""" + """Parse the module body, appending to module_node.""" _skip_nl(ps) stmt_idx: int = 0 while _cur_type(ps) != TokenType.EndMarker: stmt_idx += 1 - # fail-fast 防卡死:步数超预算(token 链表成环 / 游标原地打转)立即报错返回 + # fail-fast anti-hang: over-budget steps (cyclic token list / cursor spinning) report and return if ps.step_count > ps.step_limit: _record_parse_err(ps) return @@ -4845,10 +4565,8 @@ def parse_tokens(pool: rawptr[memhub.MemManager], _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 出口,这是它 - # 唯一的对外上报点。 + # Lexer fail-fast takes priority: overflows recorded during tokenize (e.g. integer literal > 2^64-1) + # are forwarded to the parse error channel here; lexer has no independent fatal exit. lex_line: int = get_lex_err_line() if lex_line != 0: _g_parse_err_line = lex_line @@ -4858,11 +4576,8 @@ def parse_tokens(pool: rawptr[memhub.MemManager], 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 数作为步数预算;超过上限 - # 仍未到尾即判为成环,立即报错返回(绝不进入无限解析)。 + # fail-fast anti-hang: count the token list first, detecting a cycle. A cyclic list (Token appended + # twice or aliased) would hang the statement loop; exceeding the cap before the tail means a cycle. tok_count: sizet = 0 walk_t: rawptr[Token] = tokens while walk_t is not None: @@ -4873,19 +4588,18 @@ def parse_tokens(pool: rawptr[memhub.MemManager], _g_parse_err_col = _cur_col(ps) _g_parse_err_msg = "token 链表成环:lexer 输出的 token 链表无终止(_emit 重复追加同一 Token 或分配别名),已终止解析" return None - # 预算:看门狗按"当前 token 查询"计数(合法解析每 token 的查询次数远小于 256, - # 不可能触顶),成环/原地打转必触发;它同时是 _advance 步数与语句循环的上界。 + # Budget: the watchdog counts "query current token" calls (well below 256 per token in valid parses, + # so it cannot trip falsely) while catching cycles/spins; it also bounds _advance and statement loops. 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 关键字的函数定义残留)。 + # fail-fast: on a parse error (error_count > 0) return None so the caller aborts, avoiding silently + # compiling broken code (e.g. a function definition left without its def keyword). if ps.error_count > 0: - # 兜底补记位置与原因:解析器有 40+ 处 `ps.error_count += 1`,并非每处都调用 - # _record_*(存在只自增不记录的路径)。缺记录时 dump_parse_traceback 只能打出 - # `File "..."` + `SyntaxError: 语法错误`,**既无行号也无原因** - # (用户报 "[Phase A-pre-vpi] … AST 解析失败 不显示失败原因")。此处用当前 - # token 位置补一条可定位的报文,保证上报路径永远有 文件:行:列 + 原因。 + # Fallback position/reason: the parser has 40+ `ps.error_count += 1` sites and not all call + # _record_*. Without a record, dump_parse_traceback can only print `File "..."` + + # `SyntaxError` with no line or reason. Fill in a locatable message from the current token so the + # report always has file:line:col + reason. if _g_parse_err_line == 0: _g_parse_err_line = _cur_lineno(ps) _g_parse_err_col = _cur_col(ps) diff --git a/ast/stmts.vp b/ast/stmts.vp index 62ff11d..e368e81 100644 --- a/ast/stmts.vp +++ b/ast/stmts.vp @@ -294,8 +294,8 @@ class FunctionDef(Stmt): decorator_list: list[rawptr[AST]], returns: AST, flags: CInt, type_params: rawptr[list[str]] = None) - type_params: 泛型函数类型参数名列表(PEP 695 def f[T](...): 中的 [T]), - None 表示非泛型函数。""" + type_params: generic function type-parameter name list (the [T] in PEP 695 def f[T](...)), + None means a non-generic function.""" name: str args: rawptr[AST] decorator_list: rawptr[list[rawptr[AST]]] @@ -474,8 +474,8 @@ class ClassDef(Stmt): """ClassDef(name: str, bases: list[rawptr[AST]], keywords: list[rawptr[AST]], decorator_list: list[rawptr[AST]], type_params: rawptr[list[str]]) - type_params: 泛型类型参数名列表(PEP 695 class list[T]: 中的 [T]), - None 表示非泛型类。""" + type_params: generic type-parameter name list (the [T] in PEP 695 class list[T]:), + None means a non-generic class.""" name: str bases: rawptr[list[rawptr[AST]]] keywords: rawptr[list[rawptr[AST]]] @@ -982,11 +982,11 @@ class Continue(Stmt): # ============================================================ -# FallThrough Node(关键字 `fallthrough`,仅 switch 的 case 体内合法) +# FallThrough Node (keyword `fallthrough`, legal only inside a switch case body) # ============================================================ class FallThrough(Stmt): - """FallThrough() —— switch 的 case 体不自动 break,落到下一个 case 体""" + """FallThrough() - a switch case body does not auto-break, so it falls through to the next case""" def __new__(self, pool: rawptr[memhub.MemManager]): ptr: rawptr[FallThrough] = pool.alloc(FallThrough.__sizeof__()) @@ -1092,10 +1092,8 @@ class ImportFrom(Stmt): class Match(Stmt): """Match(subject: AST, cases: list[rawptr[AST]], is_switch: int) - is_switch=1 表示本节点由 `switch :` 语法糖 desugar 而来(见 parser.vp 的 - _parse_switch):语义是 C 的 switch —— case 默认不穿透,case 体内写 - `fallthrough` 才落到下一个 case。is_switch=0 为真正的 `match` 语句, - **完全禁止 fallthrough**(写出即 fail-fast)。 + is_switch=1: from `switch :` sugar (see _parse_switch), C semantics (no fall-through without + `fallthrough`). is_switch=0: real `match`, fallthrough is completely forbidden (fail-fast). """ subject: rawptr[AST] cases: rawptr[list[rawptr[AST]]] diff --git a/ast/tokens.vp b/ast/tokens.vp index 87b223c..c691e21 100644 --- a/ast/tokens.vp +++ b/ast/tokens.vp @@ -7,12 +7,8 @@ import stdlib def _ast_pool_fatal(what: str, need: sizet): - """池分配失败(alloc 返回 NULL)时的统一 fail-fast。 - - 此前这些 alloc 失败只是静默返回,留下 str_val=NULL / 哑 token;后续 - `_kw_lookup(tok.str_val)` → `_kw_hash_str` 在 NULL 上取字节,崩成 - ASAN "access-violation on unknown address 0x0",把真因(内存池耗尽) - 掩盖成空指针崩溃。这里直接报真因并终止。 + """Unified fail-fast when a pool alloc fails (returns NULL). + Reports the real cause (pool exhausted) and terminates instead of leaving NULL str_val / dummy tokens. """ stdio.printf("[AST] %s 分配失败:内存池已耗尽(alloc 返回 NULL,need=%zu 字节)" "——上游有块被写穿/丢失,见 [MEM] 报告\n", what, need) @@ -88,67 +84,46 @@ enum Keyword: Match Case Type - # def 的别名关键字(fn/fun/func/function,语义与 def 完全相同) + # def alias keywords (fn/fun/func/function, identical semantics to def) Fn Fun Func Function - # extern 函数声明关键字(FFI,等价 t.State 标记;软判定见 _is_funcdef_kw) + # extern function declaration keyword (FFI, equivalent to t.State; soft test in _is_funcdef_kw) Extern - # export 函数声明关键字(等价 t.CExport,裸名导出不加 SHA1 头;软判定见 _is_funcdef_kw) + # export function declaration keyword (equivalent to t.CExport; soft test in _is_funcdef_kw) Export - # define 宏函数关键字(与 def 语义一致,返回注解自动追加 t.CDefine 标记 - # 为宏函数;软判定见 _is_funcdef_kw) + # define macro-function keyword (same semantics as def, return annotation gets t.CDefine; + # soft test in _is_funcdef_kw) Define - # enum 枚举定义关键字(语法糖:`enum X:` 与 `class X(t.CEnum):` 等价, - # 见 parser.vp 的 _parse_enumdef)。追加在**表尾**而非插在 Class 之后: - # 本枚举成员的值按声明序递增,插在中间会平移 Class 之后所有关键字的编号 - # (kw_subtype 数值),追加则既有编号逐字不变。 + # enum definition keyword (sugar: `enum X:` == `class X(t.CEnum):`, see _parse_enumdef). + # Appended at the enum tail so existing kw_subtype values stay unchanged. Enum - # typedef 类型别名关键字(语法糖:`typedef T1 = T2` 与 - # `T1: t.CTypedef = T2` 等价,见 parser.vp 的 _parse_typedef)。同样追加在 - # **表尾**:既有关键字编号(kw_subtype)逐字不变。 + # typedef alias keyword (sugar: `typedef T1 = T2` == `T1: t.CTypedef = T2`, see _parse_typedef). + # Appended at the enum tail: existing kw_subtype values unchanged. Typedef - # 类型描述符关键字 const / volatile(语法糖:描述符写在类型前, - # `a: const int` 与 `a: t.CConst | int` 等价,见 parser.vp 的 _parse_type_ann)。 - # 同样追加在**表尾**:既有关键字编号(kw_subtype)逐字不变。 - # 二者是**软关键字**——只在注解位置(`:` 后 / `->` 后)生效;作标识符的写法 - # (t.attr.const(),TransPyV/Test/App/attr_test.vp:142)不受影响: - # 该处 token 类型仍是 Name,属性名解析只看 TokenType.Name,不看 kw_subtype。 + # const/volatile descriptor keywords (sugar: `a: const int` == `a: t.CConst | int`, _parse_type_ann), + # appended at the enum tail. Soft keywords: only in annotation position; `t.attr.const()` unaffected. Const Volatile - # virtual / novirtual 修饰符关键字(语法糖:`virtual class C:` / - # `novirtual class C:` / `virtual def m(...)` / `novirtual def m(...)`, - # 与手写类/方法装饰器 `@t.CVTable` / `@t.NoVTable` **同一套表示**, - # 见 parser.vp 的 _ParseVirtualModified)。同样追加在**表尾**:既有关键字 - # 编号(kw_subtype)逐字不变。 - # 二者是**软关键字**——只在"语句起始 + 紧跟 class/def"时生效;作标识符的写法 - # (`x = d.virtual` / 形参名 / for 目标 / 字典键)不受影响:那些位置按 - # TokenType.Name 解析,不看 kw_subtype。 + # virtual/novirtual modifier keywords (sugar for @t.CVTable / @t.NoVTable, _ParseVirtualModified), + # appended at the enum tail. Soft keywords: only at statement start before class/def; identifiers unaffected. Virtual Novirtual - # union 类型定义关键字(语法糖:`union X:` 与 `class X(t.CUnion):` 等价, - # 见 parser.vp 的 _parse_uniondef)。同样追加在**表尾**:既有关键字编号 - # (kw_subtype)逐字不变;全仓 .vp 源码中 `union` 不作标识符使用。 + # union definition keyword (sugar: `union X:` == `class X(t.CUnion):`, see _parse_uniondef), + # appended at the enum tail; `union` is never used as an identifier in the repo. Union - # renum 变体类型定义关键字(语法糖:`renum X:` 与 `class X(t.REnum):` 等价, - # 见 parser.vp 的 _parse_renumdef)。同样追加在**表尾**:既有关键字编号 - # (kw_subtype)逐字不变;全仓 .vp 源码中 `renum` 不作标识符使用。 + # renum variant-type keyword (sugar: `renum X:` == `class X(t.REnum):`, see _parse_renumdef), + # appended at the enum tail; `renum` is never used as an identifier in the repo. Renum - # struct 纯数据类型定义关键字(语法糖:`struct X:` 与 - # `class X(t.CStruct):` + `@t.NoVTable` 等价,见 parser.vp 的 _parse_structdef): - # 与 class 同形,但**只能表示数据类型**——禁止 virtual/novirtual 修饰、禁止类体内 - # 定义 def。同样追加在**表尾**:既有关键字编号(kw_subtype)逐字不变; - # 全仓 .vp 源码中 `struct` 不作标识符使用(仅出现在注释/字符串与 - # build_gep_struct 之类复合标识符里)。 + # struct data-type keyword (sugar: `struct X:` == `class X(t.CStruct):` + @t.NoVTable, _parse_structdef), + # appended at the enum tail. Data-only: no virtual/novirtual, no def in body; never an identifier. Struct - # switch 多分支语句关键字(`switch :` + `case <常量>: ` / `default:`, - # 见 parser.vp 的 _parse_switch)。C switch 语义:默认**不穿透**,case 体内 - # 写 `fallthrough` 才落到下一个 case。同样追加在**表尾**:既有关键字编号 - # (kw_subtype)逐字不变;全仓 .vp 源码中 `switch` 不作标识符使用。 + # switch statement keyword (`switch :` + `case`/`default`, see _parse_switch), appended + # at the enum tail. C switch semantics: no fall-through unless `fallthrough` is written. Switch - # fallthrough 语句关键字(仅在 switch 的 case 体内合法;match 内使用报错)。 - # 追加在**表尾**:既有关键字编号(kw_subtype)逐字不变。 + # fallthrough statement keyword (legal only inside a switch case body; error inside match), + # appended at the enum tail. FallThrough @@ -257,21 +232,18 @@ class KwEntry: _kw_head: rawptr[KwEntry] = None _kw_pool: rawptr[memhub.MemManager] = None -# 关键字哈希索引(开放寻址 | 线性探测):槽为 u64,0=空槽,否则存 KwEntry 地址。 -# 目的:把 _kw_lookup 的 46 项链表线性扫描降为期望 O(1)(不改链表本身)。索引与 -# 链表同源——在 _init_keywords 里由同一批 _kw_intern 派生,不另抄关键字列表。 -# 关键字为静态集合且一次性注册完毕 ⇒ 索引是"完整"的:查找未命中等价于原扫描 -# 返回 0(无需回退链表扫描);回退仅用于索引未建成(分配失败)时,保证正确性。 +# Keyword hash index (open addressing, linear probing): slot u64, 0 = empty else KwEntry address; +# expected O(1) lookups, falling back to the list only when not built (alloc failure). Miss returns 0. _kw_index: sizet* = None -# 索引槽数(2 的幂且 > 2*46,负载 < 0.5,探测必然终止) +# Index slot count (power of two > 2*46, load < 0.5 so probing always terminates) define _KW_IDX_CAP = 128 -# FNV-1a 64 位常量(与 hashtable.vp 同款) +# FNV-1a 64-bit constants (same as hashtable.vp) _KW_FNV_OFFSET: uint64 = 0xcbf29ce484222325 _KW_FNV_PRIME: uint64 = 0x100000001b3 def _kw_hash_str(key: str) -> uint64: - """关键字名 FNV-1a 64 位哈希(同一键必得同一值;仅用于槽定位)""" + """FNV-1a 64-bit hash of a keyword name (same key -> same value; used only for slot location)""" h: uint64 = _KW_FNV_OFFSET k: str = key while k[0] != 0: @@ -282,11 +254,8 @@ def _kw_hash_str(key: str) -> uint64: def _kw_index_put(entry: rawptr[KwEntry]): - """把关键字条目写入哈希索引(索引未建成时静默跳过) - - 同键只保留首注册者(与查找"命中即返回"语义一致);冲突逐键 strcmp 区分, - 不能只比哈希——否则不同关键字撞槽会返回错误 kw_id。 - """ + """Write a keyword entry into the hash index (silently skipped if not built); a duplicate key keeps + the first registrant, and collisions are separated by strcmp (hash alone could return a wrong kw_id).""" if _kw_index == None: return h: uint64 = _kw_hash_str(entry.name) mask: sizet = sizet(_KW_IDX_CAP - 1) @@ -316,18 +285,17 @@ def _kw_intern(pool: rawptr[memhub.MemManager], name: str, kw_id: int): entry.kw_id = kw_id entry.next = _kw_head _kw_head = entry - # 同步写入哈希索引(关键字集合的唯一真源仍是上面的链表) + # Also write to the hash index (the linked list above is still the single source of truth) _kw_index_put(entry) def _kw_lookup(name: str) -> int: - """Look up keyword name, return the kw_id (0 if not keyword) + """Look up keyword name, return the kw_id (0 if not a keyword). - 快速路径:查哈希索引(期望 O(1),不分配内存);索引未建成时回退原链表扫描。 + Fast path: hash index (expected O(1), no allocation); falls back to the linked-list scan if not built. """ if name == None: - # 防御:str_val 为 NULL 时 _kw_hash_str 会在 NULL 上取字节,崩成 - # ASAN "access-violation on unknown address 0x0",真因(池耗尽)被掩盖。 + # Defensive: a NULL str_val would crash _kw_hash_str and mask the real cause (pool exhausted). stdio.printf("[AST] _kw_lookup 收到 NULL 名称:token.str_val 未分配成功(内存池耗尽 / 未 set)\n") stdio.fflush(None) stdlib.exit(1) @@ -358,7 +326,7 @@ def _init_keywords(pool: rawptr[memhub.MemManager]): global _kw_head, _kw_pool, _kw_index if _kw_head != None: return _kw_pool = pool - # 建哈希索引槽数组(一次性分配;分配失败则 _kw_index 保持 None → 查找回退链表扫描) + # Allocate the index slot array once; on failure _kw_index stays None -> lookups fall back to the list idx_buf: sizet* = pool.alloc(_KW_IDX_CAP * 8) if idx_buf != None: string.memset(idx_buf, 0, _KW_IDX_CAP * 8) @@ -401,7 +369,7 @@ def _init_keywords(pool: rawptr[memhub.MemManager]): _kw_intern(pool, "match", Keyword.Match) _kw_intern(pool, "case", Keyword.Case) _kw_intern(pool, "type", Keyword.Type) - # def 的别名关键字(fn/fun/func/function,语义与 def 完全相同) + # def alias keywords (fn/fun/func/function, identical semantics to def) _kw_intern(pool, "fn", Keyword.Fn) _kw_intern(pool, "fun", Keyword.Fun) _kw_intern(pool, "func", Keyword.Func) @@ -411,20 +379,20 @@ def _init_keywords(pool: rawptr[memhub.MemManager]): _kw_intern(pool, "define", Keyword.Define) _kw_intern(pool, "enum", Keyword.Enum) _kw_intern(pool, "typedef", Keyword.Typedef) - # 类型描述符软关键字(仅注解位置生效,见 parser.vp 的 _parse_type_ann) + # Type-descriptor soft keywords (annotation position only, see _parse_type_ann) _kw_intern(pool, "const", Keyword.Const) _kw_intern(pool, "volatile", Keyword.Volatile) - # virtual / novirtual 修饰符软关键字(仅"语句起始 + 紧跟 class/def"生效, - # 见 parser.vp 的 _ParseVirtualModified) + # virtual/novirtual modifier soft keywords (statement start before class/def only, + # see _ParseVirtualModified) _kw_intern(pool, "virtual", Keyword.Virtual) _kw_intern(pool, "novirtual", Keyword.Novirtual) _kw_intern(pool, "union", Keyword.Union) - # renum 变体类型定义关键字(语法糖,同 enum/union,见 parser.vp 的 _parse_renumdef) + # renum variant-type keyword (sugar, same as enum/union; see _parse_renumdef) _kw_intern(pool, "renum", Keyword.Renum) - # struct 纯数据类型定义关键字(语法糖,同 enum/union/renum,见 parser.vp 的 - # _parse_structdef:基类 t.CStruct + 伪装饰器 @t.NoVTable,禁 virtual/novirtual、禁 def) + # struct data-type keyword (sugar, same as enum/union/renum; see _parse_structdef: + # base t.CStruct + pseudo decorator @t.NoVTable, no virtual/novirtual and no def) _kw_intern(pool, "struct", Keyword.Struct) - # switch / fallthrough 关键字(见 parser.vp 的 _parse_switch 与 _parse_small_stmt) + # switch / fallthrough keywords (see _parse_switch and _parse_small_stmt) _kw_intern(pool, "switch", Keyword.Switch) _kw_intern(pool, "fallthrough", Keyword.FallThrough) diff --git a/base64.vp b/base64.vp index 33ff535..9a86327 100644 --- a/base64.vp +++ b/base64.vp @@ -7,7 +7,7 @@ import t, c # Base64 encoding lookup table b64_tab: []char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -# Base64 反向解码表,映射字符到索引值 +# Base64 reverse decode table mapping characters to index values b64_dec_tab: [80]int8 = [ 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, diff --git a/binascii.vp b/binascii.vp index 9c84735..065a9c6 100644 --- a/binascii.vp +++ b/binascii.vp @@ -102,9 +102,8 @@ def init_binascii(): HEX_VALS[int(HEX_CHARS[i] - 32)] = i for i in range(64): B64_VALS[int(B64_CHARS[i])] = i - # HQX_VALS 由标准 BinHex 字母表(见 HQX_CHARS 静态初值,RFC 1741)反查建立。 - # 注意:此处【不得】再重写 HQX_CHARS——此前用 `chr(32 + i)` 覆盖 0..63, - # 会把标准 BinHex 表整张冲掉,导致 b2a_hqx/a2b_hqx 与 RFC 1741 不兼容。 + # HQX_VALS is reverse-mapped from the standard BinHex alphabet (HQX_CHARS, RFC 1741). + # Never overwrite HQX_CHARS here: it would break RFC 1741 compatibility of b2a_hqx/a2b_hqx. for i in range(64): HQX_VALS[uchar(HQX_CHARS[i])] = i @@ -296,14 +295,14 @@ def b2a_hqx(data: str, length: int, crc: ushort = 0) -> tuple[str, ushort]: out: str = str(stdlib.malloc(5)) if not out: return None, crc - # 3字节 → 4字节 HQX + # Pack 3 source bytes into 4 HQX characters. m: uint = (uchar(data[0]) << 16) | (uchar(data[1]) << 8) | uchar(data[2]) out[0] = HQX_CHARS[(m >> 18) & 0x3F] out[1] = HQX_CHARS[(m >> 12) & 0x3F] out[2] = HQX_CHARS[(m >> 6) & 0x3F] out[3] = HQX_CHARS[m & 0x3F] out[4] = '\0' - # CRC 初始化 + 正确计算 + # Seed the CRC with the caller's value, then fold in the 3 source bytes. s: ushort = crc for i in range(3): byte: uchar = uchar(data[i]) @@ -314,12 +313,12 @@ def b2a_hqx(data: str, length: int, crc: ushort = 0) -> tuple[str, ushort]: def a2b_hqx(data: str, crc: ushort = 0) -> tuple[str, ushort]: if not data: return None, crc - # 检查字符是否有效 + # Reject the input as soon as any character is not a valid HQX symbol. for i in range(4): cchar: uchar = uchar(data[i]) if HQX_VALS[cchar] < 0: return None, crc - # 解码 + # Unpack 4 HQX characters back into 3 bytes. m: uint = 0 for i in range(4): m = (m << 6) | uint(HQX_VALS[uchar(data[i])]) @@ -330,7 +329,7 @@ def a2b_hqx(data: str, crc: ushort = 0) -> tuple[str, ushort]: out[1] = char((m >> 8) & 0xFF) out[2] = char(m & 0xFF) out[3] = '\0' - # CRC 初始化 + 正确计算 + # Seed the CRC with the caller's value, then fold in the 3 decoded bytes. s: ushort = crc for i in range(3): byte: uchar = uchar(out[i]) diff --git a/coro.vp b/coro.vp index 9aaadd8..20278a1 100644 --- a/coro.vp +++ b/coro.vp @@ -4,49 +4,46 @@ import stdlib novirtual class Coroutine: - """协程帧句柄(M1:__ptr__ 持有 create_() 返回的帧指针)。 - 纯句柄语义:从不实例化/多态分派,编译器全拦截 coro.* 调用; - 用户代码句柄统一用 t.CPtr。""" + """Coroutine frame handle (M1: __ptr__ holds the frame pointer returned by create_()). + Pure handle semantics: never instantiated or polymorphically dispatched, the compiler intercepts all coro.* calls; user code uses t.CPtr as the handle.""" __ptr__: rawptr def coro_resume(c: Coroutine) -> int: - # M1 占位:实际由编译器将 coro.coro_run 内联翻译为 resume_ 循环, - # 本函数不会被真正调用(仅保证模块可编译)。 + # M1 placeholder: the compiler inlines coro.coro_run into a resume_ loop; this function is never actually called (it only keeps the module compilable). return 0 def coro_result(c: Coroutine) -> int: - # M1 占位:coro_run 内联翻译直接从帧 ret 槽取结果。 + # M1 placeholder: coro_run's inline translation reads the result directly from the frame ret slot. return 0 def coro_run(c: Coroutine) -> int: - # M1 占位实现:编译器在调用点对 coro.coro_run 做内联翻译(resume 循环)。 + # M1 placeholder: the compiler inlines coro.coro_run at the call site (resume loop). s: int = 0 while s == 0: s = coro_resume(c) return coro_result(c) def coro_sleep(ms: int) -> int: - # M3 占位:编译器将 await coro_sleep(ms) 内联翻译为帧 wake 槽挂起, - # 本函数不会被真正调用(仅保证模块可编译)。 + # M3 placeholder: the compiler inlines await coro_sleep(ms) into a frame wake-slot suspend; this function is never actually called (it only keeps the module compilable). return 0 def coro_destroy(c: Coroutine): - # M3 占位:编译器将 coro.coro_destroy(帧) 内联翻译为 free。 + # M3 placeholder: the compiler inlines coro.coro_destroy(frame) into free. return def coro_exception(c: Coroutine) -> int: - # M4 占位:编译器将 coro.coro_exception(帧) 内联翻译为帧 exc 槽读取。 + # M4 placeholder: the compiler inlines coro.coro_exception(frame) into a frame exc-slot read. return 0 def coro_gather(a: Coroutine) -> int: - # M4 占位:编译器将 coro.coro_gather(帧...) 内联翻译为并发推进循环。 + # M4 placeholder: the compiler inlines coro.coro_gather(frames...) into a concurrent-advance loop. return 0 def coro_exception_msg(c: Coroutine) -> t.CPtr: - # M7 占位:编译器将 coro.coro_exception_msg(帧) 内联翻译为帧 msg 槽读取。 - # 注意:TPV(Viper)不支持裸 null,用 t.CPtr(0) 构造空指针(两编译器兼容)。 + # M7 placeholder: the compiler inlines coro.coro_exception_msg(frame) into a frame msg-slot read. + # Note: TPV (Viper) does not support bare null; use t.CPtr(0) to build a null pointer (compatible with both compilers). return None diff --git a/fltk/__init__.vp b/fltk/__init__.vp index 428622a..9e0f6e1 100644 --- a/fltk/__init__.vp +++ b/fltk/__init__.vp @@ -54,10 +54,10 @@ from fltk._cfl_theme import * # ============================================================ -# Fl — FLTK 全局静态类(事件循环 / 事件 / 颜色) +# Fl — FLTK global static class (event loop / events / colors) # ============================================================ novirtual class Fl: - """FLTK 全局函数集合(静态方法)""" + """FLTK global function collection (static methods).""" @staticmethod def run() -> int: @@ -129,11 +129,10 @@ novirtual class Fl: @staticmethod def set_scheme(scheme: str) -> int: - """切换全局风格 scheme。 + """Switch the global style scheme. - 内置风格: 'base' / 'plastic' / 'gtk+' / 'gleam' / 'oxy'。 - 调用后 FLTK 自动重载并重绘所有窗口。 - 用法: fltk.Fl.set_scheme("plastic")""" + Built-in: 'base' / 'plastic' / 'gtk+' / 'gleam' / 'oxy'; FLTK reloads and + redraws all windows afterwards. Usage: fltk.Fl.set_scheme("plastic")""" return Fl_set_scheme(scheme) @staticmethod @@ -182,77 +181,77 @@ novirtual class Fl: @staticmethod def scheme_string() -> str: - """当前风格名('base'/'plastic'/'gtk+'/'gleam'/'oxy')。""" + """Current scheme name ('base'/'plastic'/'gtk+'/'gleam'/'oxy').""" return Fl_scheme_string() @staticmethod def reload_scheme() -> int: - """重载当前风格(修改背景/前景色后调用生效)。""" + """Reload the current scheme (call after changing background/foreground colors).""" return Fl_reload_scheme() @staticmethod def set_background(r: int, g: int, b: int): - """设置全局背景色(0~255),之后需 Fl.reload_scheme()。""" + """Set the global background color (0~255); call Fl.reload_scheme() afterwards.""" Fl_background(r, g, b) @staticmethod def set_background2(r: int, g: int, b: int): - """设置全局次级背景色(0~255)。""" + """Set the global secondary background color (0~255).""" Fl_background2(r, g, b) @staticmethod def set_foreground(r: int, g: int, b: int): - """设置全局前景色(文字,0~255)。""" + """Set the global foreground (text) color (0~255).""" Fl_foreground(r, g, b) - # ----- Fl_Theme 主题(C++ 主题库,libfltk_theme.a) ----- + # ----- Fl_Theme theme (C++ theme library, libfltk_theme.a) ----- @staticmethod def set_theme_dark(): - """应用 Fl_Theme 深色主题(flat 控件 + BLACK 色板 + 深灰背景/白前景)。""" + """Apply the Fl_Theme dark theme (flat widgets + BLACK palette + dark-gray bg / white fg).""" Fl_Theme_apply_dark() @staticmethod def set_theme_metro(): - """应用 Fl_Theme Metro 主题(metro 控件 + 蓝灰配色)。""" + """Apply the Fl_Theme Metro theme (metro widgets + blue-gray palette).""" Fl_Theme_apply_metro() @staticmethod def set_border_radius(r: int): - """设置 flat 控件圆角半径(默认 5;需在应用主题前调用)。""" + """Set the flat widget corner radius (default 5; call before applying a theme).""" Fl_Theme_set_border_radius(r) @staticmethod def apply_flat_scheme(): - """仅应用 flat 控件绘制方案(不改颜色)。""" + """Apply only the flat widget drawing scheme (colors unchanged).""" Fl_Theme_apply_flat_scheme() @staticmethod def apply_metro_scheme(): - """仅应用 metro 控件绘制方案(不改颜色)。""" + """Apply only the metro widget drawing scheme (colors unchanged).""" Fl_Theme_apply_metro_scheme() - # ----- 定时器 / 线程唤醒 ----- + # ----- Timers / thread wakeup ----- @staticmethod def add_timeout(seconds: double, cb: bytes, data: bytes = 0): - """注册一次性定时回调(seconds 秒后触发)。回调签名: def cb(w: bytes, data: bytes)""" + """Register a one-shot timer callback (fires after seconds). cb(w: bytes, data: bytes).""" Fl_add_timeout(seconds, cb, data) @staticmethod def repeat_timeout(seconds: double, cb: bytes, data: bytes = 0): - """注册重复定时回调(每 seconds 秒触发,回调内需再调 repeat_timeout 保持)。""" + """Register a repeating timer callback (every seconds; call repeat_timeout again inside to keep it).""" Fl_repeat_timeout(seconds, cb, data) @staticmethod def awake(): - """唤醒另一线程的 Fl.run/Fl.wait 事件循环。""" + """Wake up the Fl.run/Fl.wait event loop in another thread.""" Fl_awake() @staticmethod def awake_callback(handler: bytes, data: bytes = 0) -> int: - """注册跨线程唤醒回调(另一线程 Fl.awake() 后主循环调用 handler)。""" + """Register a cross-thread wakeup callback (the main loop calls handler after another thread's Fl.awake()).""" return Fl_awake_callback(handler, data) - # ----- 补全事件查询 ----- + # ----- Additional event queries ----- @staticmethod def event_original_key() -> int: return Fl_event_original_key() @@ -287,10 +286,10 @@ novirtual class Fl: # ============================================================ -# Fl_Widget — 控件基类包装 +# Fl_Widget — widget base class wrapper # ============================================================ novirtual class Fl_Widget: - """FLTK 控件基类(不透明指针包装)""" + """FLTK widget base class (opaque pointer wrapper).""" _ptr: Fl_WidgetPtr def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): @@ -371,36 +370,36 @@ novirtual class Fl_Widget: def delete(self): Fl_Widget_delete(self._ptr) - # ----- 回调 ----- + # ----- Callbacks ----- def set_callback(self, cb: bytes, data: bytes = 0): - """注册控件回调(如点击/变更触发)。cb 传 Viper 模块级函数名,自动转函数指针。 - 回调签名: def cb(w: bytes, data: bytes): ...(w=控件指针,可转回本类指针用)。 - 触发时机由 set_when 控制(默认 Fl_WHEN_RELEASE)。""" + """Register a widget callback (e.g. click/change). cb is a Viper module-level + function name auto-cast to a function pointer: cb(w: bytes, data: bytes). + Trigger time is controlled by set_when (default Fl_WHEN_RELEASE).""" Fl_Widget_set_callback(self._ptr, cb, data) def callback(self) -> bytes: return Fl_Widget_callback(self._ptr) def do_callback(self): - """手动触发本控件回调(不经过事件循环)。""" + """Manually invoke this widget's callback (bypassing the event loop).""" Fl_Widget_do_callback(self._ptr) def set_when(self, when: int): - """设置回调触发条件(Fl_WHEN_* 常量见 _enums.vp)。""" + """Set the callback trigger condition (Fl_WHEN_* constants in _enums.vp).""" Fl_Widget_set_when(self._ptr, when) def when(self) -> int: return Fl_Widget_when(self._ptr) def set_user_data(self, data: bytes): - """设置回调用户数据(回调的第二个参数 data)。""" + """Set the callback user data (the callback's second argument data).""" Fl_Widget_set_user_data(self._ptr, data) def user_data(self) -> bytes: return Fl_Widget_user_data(self._ptr) def set_image(self, img: bytes): - """设置控件图标(传 Fl_PNG_Image 等图片对象的 _ptr)。""" + """Set the widget image (pass the _ptr of an image object such as Fl_PNG_Image).""" Fl_Widget_set_image(self._ptr, img) def image(self) -> bytes: @@ -408,10 +407,10 @@ novirtual class Fl_Widget: # ============================================================ -# Fl_Window — 窗口包装 +# Fl_Window — window wrapper # ============================================================ novirtual class Fl_Window(Fl_Widget): - """FLTK 窗口""" + """FLTK window.""" def __init__(self, w: int, h: int, title: str = ""): self._ptr = Fl_Window_new(0, 0, w, h, title) @@ -466,20 +465,20 @@ novirtual class Fl_Window(Fl_Widget): # ============================================================ -# Fl_Box — 静态文本/装饰框 +# Fl_Box — static text/decorative box # ============================================================ novirtual class Fl_Box(Fl_Widget): - """FLTK 文本框/装饰框""" + """FLTK text/decorative box.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Box_new(x, y, w, h, label) # ============================================================ -# Fl_Button — 按钮包装 +# Fl_Button — button wrapper # ============================================================ novirtual class Fl_Button(Fl_Widget): - """FLTK 按钮""" + """FLTK button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Button_new(x, y, w, h, label) @@ -510,7 +509,7 @@ novirtual class Fl_Button(Fl_Widget): novirtual class Fl_Check_Button(Fl_Button): - """FLTK 复选框""" + """FLTK check button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Check_Button_new(x, y, w, h, label) @@ -523,7 +522,7 @@ novirtual class Fl_Check_Button(Fl_Button): novirtual class Fl_Radio_Button(Fl_Button): - """FLTK 单选按钮""" + """FLTK radio button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Radio_Button_new(x, y, w, h, label) @@ -536,21 +535,21 @@ novirtual class Fl_Radio_Button(Fl_Button): novirtual class Fl_Repeat_Button(Fl_Button): - """FLTK 重复触发按钮""" + """FLTK repeat button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Repeat_Button_new(x, y, w, h, label) novirtual class Fl_Return_Button(Fl_Button): - """FLTK 回车按钮""" + """FLTK return button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Return_Button_new(x, y, w, h, label) novirtual class Fl_Light_Button(Fl_Button): - """FLTK 指示灯按钮""" + """FLTK light button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Light_Button_new(x, y, w, h, label) @@ -563,10 +562,10 @@ novirtual class Fl_Light_Button(Fl_Button): # ============================================================ -# Fl_Input / Fl_Output — 文本输入输出 +# Fl_Input / Fl_Output — text input/output # ============================================================ novirtual class Fl_Input(Fl_Widget): - """FLTK 单行文本输入框""" + """FLTK single-line text input.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Input_new(x, y, w, h, label) @@ -591,7 +590,7 @@ novirtual class Fl_Input(Fl_Widget): novirtual class Fl_Output(Fl_Widget): - """FLTK 单行文本输出框""" + """FLTK single-line text output.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Output_new(x, y, w, h, label) @@ -614,10 +613,10 @@ novirtual class Fl_Float_Input(Fl_Input): # ============================================================ -# Fl_Text_Buffer / Fl_Text_Display — 多行文本 +# Fl_Text_Buffer / Fl_Text_Display — multi-line text # ============================================================ novirtual class Fl_Text_Buffer: - """FLTK 文本缓冲""" + """FLTK text buffer.""" _ptr: Fl_Text_BufferPtr @@ -653,7 +652,7 @@ novirtual class Fl_Text_Buffer: novirtual class Fl_Text_Display(Fl_Widget): - """FLTK 多行文本显示""" + """FLTK multi-line text display.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Text_Display_new(x, y, w, h, label) @@ -675,10 +674,10 @@ novirtual class Fl_Text_Display(Fl_Widget): # ============================================================ -# Fl_Group — 容器包装 +# Fl_Group — container wrapper # ============================================================ novirtual class Fl_Group(Fl_Widget): - """FLTK 控件组""" + """FLTK widget group.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Group_new(x, y, w, h, label) @@ -709,7 +708,7 @@ novirtual class Fl_Group(Fl_Widget): novirtual class Fl_Scroll(Fl_Group): - """FLTK 滚动容器""" + """FLTK scroll container.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Scroll_new(x, y, w, h, label) @@ -731,7 +730,7 @@ novirtual class Fl_Scroll(Fl_Group): novirtual class Fl_Tabs(Fl_Group): - """FLTK 选项卡容器""" + """FLTK tab container.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Tabs_new(x, y, w, h, label) @@ -744,10 +743,10 @@ novirtual class Fl_Tabs(Fl_Group): # ============================================================ -# Fl_Slider / Fl_Value_Input / Fl_Value_Output — 数值控件 +# Fl_Slider / Fl_Value_Input / Fl_Value_Output — numeric widgets # ============================================================ novirtual class Fl_Slider(Fl_Widget): - """FLTK 滑动条""" + """FLTK slider.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Slider_new(x, y, w, h, label) @@ -781,7 +780,7 @@ novirtual class Fl_Slider(Fl_Widget): novirtual class Fl_Value_Input(Fl_Widget): - """FLTK 数值输入框""" + """FLTK numeric input.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Value_Input_new(x, y, w, h, label) @@ -797,7 +796,7 @@ novirtual class Fl_Value_Input(Fl_Widget): novirtual class Fl_Value_Output(Fl_Widget): - """FLTK 数值输出框""" + """FLTK numeric output.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Value_Output_new(x, y, w, h, label) @@ -810,7 +809,7 @@ novirtual class Fl_Value_Output(Fl_Widget): novirtual class Fl_Progress(Fl_Widget): - """FLTK 进度条""" + """FLTK progress bar.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Progress_new(x, y, w, h, label) @@ -829,10 +828,10 @@ novirtual class Fl_Progress(Fl_Widget): # ============================================================ -# Fl_Choice — 下拉选择 +# Fl_Choice — drop-down choice # ============================================================ novirtual class Fl_Choice(Fl_Widget): - """FLTK 下拉选择框""" + """FLTK drop-down choice box.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Choice_new(x, y, w, h, label) @@ -851,14 +850,14 @@ novirtual class Fl_Choice(Fl_Widget): # ============================================================ -# Fl_Menu_Bar — 菜单栏 +# Fl_Menu_Bar — menu bar # ============================================================ novirtual class Fl_Menu_Bar(Fl_Widget): - """FLTK 菜单栏 + """FLTK menu bar. - add 走 cfltk 完整 6 参签名(name/shortcut/回调/数据/flags), - cb 可传 Viper 模块级函数名(回调签名: def cb(w: bytes, data: bytes)); - 不传回调(默认)时菜单项无动作。简单下拉建议用 add_choice。""" + add uses the full cfltk 6-arg signature (name/shortcut/cb/data/flags); cb may be + a Viper module-level function name (cb(w: bytes, data: bytes)). Default: no action. + For simple drop-downs prefer add_choice.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Menu_Bar_new(x, y, w, h, label) @@ -871,14 +870,13 @@ novirtual class Fl_Menu_Bar(Fl_Widget): # ============================================================ -# Fl_Native_File_Chooser — 系统文件对话框 +# Fl_Native_File_Chooser — native file dialog # ============================================================ novirtual class Fl_Native_File_Chooser: - """FLTK 原生文件选择对话框 + """FLTK native file dialog. - 构造需传对话框类型常量(如 Fl_Native_File_Chooser_BROWSE_FILE / - BROWSE_DIRECTORY / BROWSE_MULTI_FILE / BROWSE_MULTI_DIRECTORY / - BROWSE_SAVE_FILE,见 _enums.vp)。""" + The constructor takes a dialog-type constant (BROWSE_FILE / BROWSE_DIRECTORY / + BROWSE_MULTI_FILE / BROWSE_MULTI_DIRECTORY / BROWSE_SAVE_FILE; see _enums.vp).""" _ptr: Fl_Native_File_ChooserPtr def __init__(self, type_: int): @@ -934,77 +932,77 @@ novirtual class Fl_Native_File_Chooser: # ============================================================ -# 常用别名 +# Common aliases # ============================================================ def fltk_init(): - """初始化 FLTK(多线程环境需先 Fl_lock 再 init_all)。""" + """Initialize FLTK (in multithreaded environments call Fl_lock before init_all).""" Fl_init_all() # ============================================================ -# 补充按钮类 +# Additional button classes # ============================================================ novirtual class Fl_Toggle_Button(Fl_Button): - """FLTK 切换按钮(按下保持)""" + """FLTK toggle button (stays pressed).""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Toggle_Button_new(x, y, w, h, label) novirtual class Fl_Round_Button(Fl_Button): - """FLTK 圆形按钮""" + """FLTK round button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Round_Button_new(x, y, w, h, label) novirtual class Fl_Radio_Round_Button(Fl_Button): - """FLTK 圆形单选按钮""" + """FLTK round radio button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Radio_Round_Button_new(x, y, w, h, label) novirtual class Fl_Radio_Light_Button(Fl_Button): - """FLTK 指示灯单选按钮""" + """FLTK radio light button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Radio_Light_Button_new(x, y, w, h, label) novirtual class Fl_Shortcut_Button(Fl_Button): - """FLTK 快捷键录入按钮""" + """FLTK shortcut button.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Shortcut_Button_new(x, y, w, h, label) # ============================================================ -# 补充输入类 +# Additional input classes # ============================================================ novirtual class Fl_Multiline_Input(Fl_Input): - """FLTK 多行文本输入框""" + """FLTK multi-line text input.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Multiline_Input_new(x, y, w, h, label) novirtual class Fl_Secret_Input(Fl_Input): - """FLTK 密码输入框(显示为 *)""" + """FLTK password input (shown as *).""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Secret_Input_new(x, y, w, h, label) novirtual class Fl_File_Input(Fl_Input): - """FLTK 文件路径输入框""" + """FLTK file-path input.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_File_Input_new(x, y, w, h, label) novirtual class Fl_Multiline_Output(Fl_Output): - """FLTK 多行文本输出框""" + """FLTK multi-line text output.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Multiline_Output_new(x, y, w, h, label) @@ -1014,14 +1012,14 @@ novirtual class Fl_Multiline_Output(Fl_Output): # Fl_Nice_Slider / Fl_Spinner / Fl_Clock # ============================================================ novirtual class Fl_Nice_Slider(Fl_Slider): - """FLTK 美观滑块""" + """FLTK nice slider.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Nice_Slider_new(x, y, w, h, label) novirtual class Fl_Spinner(Fl_Widget): - """FLTK 数字微调框""" + """FLTK spinner.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Spinner_new(x, y, w, h, label) @@ -1052,41 +1050,41 @@ novirtual class Fl_Spinner(Fl_Widget): novirtual class Fl_Clock(Fl_Widget): - """FLTK 时钟(模拟表盘)""" + """FLTK clock (analog dial).""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Clock_new(x, y, w, h, label) # ============================================================ -# 补充容器类 +# Additional container classes # ============================================================ novirtual class Fl_Pack(Fl_Group): - """FLTK 自动布局容器(子控件按序排列)""" + """FLTK auto-layout container (children laid out in order).""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Pack_new(x, y, w, h, label) novirtual class Fl_Tile(Fl_Group): - """FLTK 可拖动分隔容器""" + """FLTK resizable-splitter container.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Tile_new(x, y, w, h, label) novirtual class Fl_Flex(Fl_Group): - """FLTK 弹性布局容器""" + """FLTK flex layout container.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Flex_new(x, y, w, h, label) # ============================================================ -# Fl_Menu_Button — 弹出菜单按钮 +# Fl_Menu_Button — popup menu button # ============================================================ novirtual class Fl_Menu_Button(Fl_Widget): - """FLTK 弹出菜单按钮(右键/长按弹出)""" + """FLTK popup menu button (shown on right-click / long-press).""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Menu_Button_new(x, y, w, h, label) @@ -1096,16 +1094,16 @@ novirtual class Fl_Menu_Button(Fl_Widget): # ============================================================ -# Fl_Browser — 列表浏览 +# Fl_Browser — list browser # ============================================================ novirtual class Fl_Browser(Fl_Widget): - """FLTK 列表浏览框""" + """FLTK list browser.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Browser_new(x, y, w, h, label) def value(self) -> int: - """当前选中行号(1 起;0=未选中)""" + """Current selected line number (1-based; 0=unselected).""" return Fl_Browser_value(self._ptr) def add(self, newtext: str): @@ -1119,16 +1117,16 @@ novirtual class Fl_Browser(Fl_Widget): # ============================================================ -# Fl_Tree — 树形控件 +# Fl_Tree — tree widget # ============================================================ novirtual class Fl_Tree(Fl_Widget): - """FLTK 树形控件""" + """FLTK tree widget.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Tree_new(x, y, w, h, label) def add(self, name: str) -> Fl_Tree_ItemPtr: - """添加节点('a/b/c' 路径自动建父子层级)""" + """Add a node ('a/b/c' path builds parent/child levels automatically).""" return Fl_Tree_add(self._ptr, name) def clear(self): @@ -1139,22 +1137,22 @@ novirtual class Fl_Tree(Fl_Widget): # Fl_Text_Editor / Fl_Terminal # ============================================================ novirtual class Fl_Text_Editor(Fl_Text_Display): - """FLTK 文本编辑器""" + """FLTK text editor.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Text_Editor_new(x, y, w, h, label) def insert(self, text: str): - """在光标处插入文本""" + """Insert text at the cursor.""" Fl_Text_Editor_insert(self._ptr, text) def overstrike(self, text: str): - """在光标处覆盖插入文本""" + """Overwrite text at the cursor.""" Fl_Text_Editor_overstrike(self._ptr, text) novirtual class Fl_Terminal(Fl_Widget): - """FLTK 终端模拟控件(ANSI 转义支持)""" + """FLTK terminal emulation widget (ANSI escape support).""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Terminal_new(x, y, w, h, label) @@ -1164,13 +1162,13 @@ novirtual class Fl_Terminal(Fl_Widget): # ============================================================ -# Fl_File_Chooser — 文件选择对话框 +# Fl_File_Chooser — file dialog # ============================================================ novirtual class Fl_File_Chooser: - """FLTK 文件选择对话框(cfltk 封装,非原生) + """FLTK file dialog (cfltk wrapper, not native). - new(directory, pattern, type, title);type 用 Fl_File_Chooser_* 常量 - (见 _enums.vp:SINGLE/MULTI/DIRECTORY/CREATE/SAVE 等)。""" + new(directory, pattern, type, title); type uses Fl_File_Chooser_* constants + (see _enums.vp: SINGLE/MULTI/DIRECTORY/CREATE/SAVE, etc.).""" _ptr: Fl_File_ChooserPtr @@ -1220,7 +1218,7 @@ novirtual class Fl_File_Chooser: Fl_File_Chooser_hide(self._ptr) def value(self, f: int) -> str: - """选中的第 f 个文件名(0 起;多选时用 count 遍历)""" + """Selected filename at index f (0-based; iterate with count for multi-select).""" return Fl_File_Chooser_value(self._ptr, f) def delete(self): @@ -1228,10 +1226,10 @@ novirtual class Fl_File_Chooser: # ============================================================ -# Fl_Help_Dialog — HTML 帮助对话框 +# Fl_Help_Dialog — HTML help dialog # ============================================================ novirtual class Fl_Help_Dialog: - """FLTK HTML 帮助对话框""" + """FLTK HTML help dialog.""" _ptr: Fl_Help_DialogPtr @@ -1239,11 +1237,11 @@ novirtual class Fl_Help_Dialog: self._ptr = Fl_Help_Dialog_new() def load(self, f: str) -> int: - """加载 HTML 文件""" + """Load an HTML file.""" return Fl_Help_Dialog_load(self._ptr, f) def set_value(self, f: str): - """直接设置 HTML 内容""" + """Set HTML content directly.""" Fl_Help_Dialog_set_value(self._ptr, f) def value(self) -> str: @@ -1266,10 +1264,10 @@ novirtual class Fl_Help_Dialog: # ============================================================ -# Fl_Input_Choice — 输入+下拉组合 +# Fl_Input_Choice — input + drop-down combo # ============================================================ novirtual class Fl_Input_Choice(Fl_Group): - """FLTK 文本输入 + 下拉选择组合控件""" + """FLTK text input + drop-down choice combo widget.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Input_Choice_new(x, y, w, h, label) @@ -1278,21 +1276,21 @@ novirtual class Fl_Input_Choice(Fl_Group): Fl_Input_Choice_add(self._ptr, s) def value(self) -> str: - """当前输入框文本""" + """Current input box text.""" return Fl_Input_Choice_value(self._ptr) # ============================================================ -# Fl_Color_Chooser — 颜色选择 +# Fl_Color_Chooser — color chooser # ============================================================ novirtual class Fl_Color_Chooser(Fl_Group): - """FLTK 颜色选择器""" + """FLTK color chooser.""" def __init__(self, x: int, y: int, w: int, h: int, label: str = ""): self._ptr = Fl_Color_Chooser_new(x, y, w, h, label) def set_rgb(self, r: double, g: double, b: double) -> int: - """设置 RGB 值(0.0~1.0),返回 1=值变更""" + """Set RGB values (0.0~1.0); returns 1 if the value changed.""" return Fl_Color_Chooser_set_rgb(self._ptr, r, g, b) def show(self): @@ -1300,10 +1298,10 @@ novirtual class Fl_Color_Chooser(Fl_Group): # ============================================================ -# 图片类 +# Image classes # ============================================================ novirtual class Fl_PNG_Image: - """FLTK PNG 图片(从文件加载)""" + """FLTK PNG image (loaded from a file).""" _ptr: Fl_PNG_ImagePtr @@ -1312,7 +1310,7 @@ novirtual class Fl_PNG_Image: novirtual class Fl_JPEG_Image: - """FLTK JPEG 图片""" + """FLTK JPEG image.""" _ptr: Fl_JPEG_ImagePtr @@ -1321,7 +1319,7 @@ novirtual class Fl_JPEG_Image: novirtual class Fl_GIF_Image: - """FLTK GIF 图片""" + """FLTK GIF image.""" _ptr: Fl_GIF_ImagePtr diff --git a/fltk/_cfl_theme.vp b/fltk/_cfl_theme.vp index 4ae0879..92ad7b5 100644 --- a/fltk/_cfl_theme.vp +++ b/fltk/_cfl_theme.vp @@ -7,15 +7,15 @@ # ============================================================ import t, c -# 应用深色主题(flat 控件 + BLACK 色板) +# Apply the dark theme (flat widgets + BLACK palette) extern def Fl_Theme_apply_dark() -# 应用 Metro 主题(metro 控件 + 蓝灰配色) +# Apply the Metro theme (metro widgets + blue-gray palette) extern def Fl_Theme_apply_metro() -# 设置 flat 控件圆角半径(默认 5;应用主题前调用) +# Set the flat widget corner radius (default 5; call before applying a theme) extern def Fl_Theme_set_border_radius(radius: int) -# 仅应用 flat / metro 控件绘制方案(不改颜色) +# Apply only the flat / metro widget drawing schemes (colors unchanged) extern def Fl_Theme_apply_flat_scheme() extern def Fl_Theme_apply_metro_scheme() diff --git a/gzip.vp b/gzip.vp index 62ed58f..d6b71fc 100644 --- a/gzip.vp +++ b/gzip.vp @@ -25,13 +25,13 @@ from stdint import * # GzipFile # ============================================================ class GzipFile: - ok: int # 1 = 可正常使用 - err: int # 错误码(0 无, <0 出错) - src: bytes # 整个 .gz 文件的内存副本(构造时读入, close 释放) + ok: int # 1 = usable + err: int # error code (0 = none, <0 = error) + src: bytes # in-memory copy of whole .gz file (read in ctor, freed by close) src_len: sizet - dst: bytes # 解压输出缓冲(read_all 生成, close 释放) - dst_len: sizet # 解压结果长度 - _need_close_src: int # 是否持有 src(读失败时为 0) + dst: bytes # decompressed output buffer (created by read_all, freed by close) + dst_len: sizet # decompressed length + _need_close_src: int # whether src is owned (0 if read failed) def __init__(self, path: str): self.ok = 0 @@ -46,15 +46,15 @@ class GzipFile: return f: fileio.File = fileio.File(path, fileio.MODE.R) if f.closed: - self.err = -2 # 打不开文件 + self.err = -2 # cannot open file return sz: longlong = f.size() if sz < 18: f.close() - self.err = -3 # 文件过小 + self.err = -3 # file too small return self.src_len = sizet(sz) - # read_all 契约: 最多读 max_count-1 字节并补 NUL; 故按 src_len+1 申请与请求 + # read_all contract: reads up to max_count-1 bytes and NUL-terminates; request src_len+1. buf: bytes = stdlib.malloc(self.src_len + 1) if buf is None: f.close() @@ -64,9 +64,9 @@ class GzipFile: f.close() if got <= 0 or sizet(got) != self.src_len: stdlib.free(buf) - self.err = -5 # 读取失败或被截断 + self.err = -5 # read failed or truncated return - # gzip 魔数校验 + # gzip magic-number check ub: uchar* = buf if ub[0] != 0x1F or ub[1] != 0x8B or ub[2] != 8: stdlib.free(buf) @@ -77,18 +77,18 @@ class GzipFile: self.ok = 1 def read_all(self) -> bytes: - """解压整个成员。返回内部持有的输出缓冲(或 None); 长度见 self.dst_len。""" + """Decompress the whole member. Returns internal output buffer (or None); length in self.dst_len.""" if self.ok == 0 or self.src is None: return None if self.dst is not None: - return self.dst # 已解压过 + return self.dst # already decompressed want: uint32 = gzip_isize(self.src, self.src_len) cap: sizet = sizet(want) if cap == 0: - cap = self.src_len * 64 # 无 ISIZE 时保守初值 + cap = self.src_len * 64 # conservative initial value when no ISIZE if cap < 1024: cap = 1024 - # 保险起见: ISIZE 与实际不符时报"目标缓冲不足", 就翻倍重试(正常情况下一次成功) + # If ISIZE mismatches actual size (buffer-too-small), double cap and retry (normally one pass). out: BYTEPTR = None n: LONG = 0 while True: @@ -108,7 +108,7 @@ class GzipFile: break if n <= 0: stdlib.free(out) - self.err = int(n) # n 为负错误码, 原样保存 + self.err = int(n) # n is a negative error code; store as-is return None self.dst = out self.dst_len = sizet(n) @@ -127,7 +127,7 @@ class GzipFile: self.ok = 0 # ============================================================ -# 位流状态(模块级, 单线程; 每次 _gzip_decode 重置) +# Bit-stream state (module-level, single-thread; reset per _gzip_decode) # ============================================================ _g_bits: uint64 = 0 _g_bitcnt: int = 0 @@ -137,12 +137,12 @@ _g_src_len: sizet = 0 def gzip_isize(src: t.CPtr, src_len: sizet) -> uint32: - """gzip 尾部 ISIZE(小端 u32), 即原始长度提示""" + """gzip trailing ISIZE (little-endian u32): original length hint.""" if src is None or src_len < 4: return 0 ub: uchar* = src p: sizet = src_len - 4 - # 小端 u32 拼装(64 位中间量后收窄) + # Assemble little-endian u32 (build in 64-bit then narrow) v64: uint64 = uint64(ub[p]) \ + uint64(ub[p + 1]) * 256 \ + uint64(ub[p + 2]) * 65536 \ @@ -313,7 +313,7 @@ def _cl_order_set(arr: int*, idx: int, val: int): # ============================================================ -# 核心解压(模块级私有) +# Core decompression (module-private) # ============================================================ def _gzip_decode(src: t.CPtr, src_len: sizet, dst: t.CPtr, dst_cap: sizet) -> LONG: @@ -371,7 +371,7 @@ def _gzip_decode(src: t.CPtr, src_len: sizet, or cl_sorted is None or pos_tmp is None: return -4 - # 全部表缓冲清零(初始 malloc 未清零; 防个别路径读到垃圾表导致提前/错乱收尾) + # Zero all table buffers (malloc is uninitialized; avoids garbage tables causing early/bad finish). string.memset(lit_len, 0, 288 * 4) string.memset(lit_cnt, 0, 17 * 4) string.memset(lit_first, 0, 17 * 4) @@ -606,5 +606,5 @@ def _gzip_decode(src: t.CPtr, src_len: sizet, stdlib.free(pos_tmp) if err != 0: - return LONG(err) # err 本身为负错误码, 不能再取负(否则被上层当成"长度>0 成功") + return LONG(err) # err is already a negative code; do not negate it (else callers treat >0 as success) return LONG(wpos) diff --git a/hashtable.vp b/hashtable.vp index be47213..d3f6f69 100644 --- a/hashtable.vp +++ b/hashtable.vp @@ -45,7 +45,7 @@ class HashTable: ht: HashTable = HashTable(mb) ht.set_int("x", 42) p: t.CPtr = ht["x"] - v: int = c.Deref((int | t.CPtr)(p)) # int 值按 i32 存,读回须给 int* 视图 + v: int = c.Deref((int | t.CPtr)(p)) # int value stored as i32; read it back with an int* view """ __slots__: t.CPtr # Base address of slot array __count__: sizet = 0 # Number of occupied entries @@ -79,10 +79,8 @@ class HashTable: def _slot_value(self, idx: sizet) -> t.CPtr: """Read value pointer from target slot.""" - # value 槽存的是指针,注解须为「指针的指针」:X|t.CPtr 的合并规则是 Ptr(resolve(X)), - # 故 t.CUInt64T|t.CPtr|t.CPtr = Ptr(Ptr(i64)) = i64**,取下标得 i64*(指针), - # 与返回类型 t.CPtr(i8*)同级指针可隐式 bitcast。若只写 t.CUInt64T|t.CPtr - # (= i64*,存哈希整数用),取下标得标量 i64 → 撞「整数隐式转指针禁止」。 + # The value slot holds a pointer, so the annotation must be "pointer to pointer": X|t.CPtr merges as Ptr(resolve(X)), so t.CUInt64T|t.CPtr|t.CPtr = Ptr(Ptr(i64)) = i64**, and subscripting yields i64* (a pointer). + # Writing only t.CUInt64T|t.CPtr (= i64*, for hash integers) would make subscripting yield a scalar i64, hitting "implicit int-to-pointer conversion forbidden". ptr: uint64** = (uint64**)(uint64(self.__slots__) + idx * _HT_SLOT_SIZE + 16) return ptr[0] @@ -92,7 +90,7 @@ class HashTable: h_ptr[0] = h k_ptr: str* = (str*)(uint64(self.__slots__) + idx * _HT_SLOT_SIZE + 8) k_ptr[0] = key - # 同 _slot_value:value 槽按「指针的指针」(i64**) 注解,元素才是指针 + # Same as _slot_value: the value slot is annotated as "pointer to pointer" (i64**) so the element is a pointer v_ptr: uint64** = (uint64**)(uint64(self.__slots__) + idx * _HT_SLOT_SIZE + 16) v_ptr[0] = val @@ -140,7 +138,7 @@ class HashTable: h: uint64 = old_h_ptr[0] if h != _HT_EMPTY and h != _HT_TOMBSTONE: old_k_ptr: str* = (str*)(uint64(old_slots) + i * _HT_SLOT_SIZE + 8) - # 同 _slot_value:value 槽按「指针的指针」(i64**) 注解,元素才是指针 + # Same as _slot_value: the value slot is annotated as "pointer to pointer" (i64**) so the element is a pointer old_v_ptr: uint64** = (uint64**)(uint64(old_slots) + i * _HT_SLOT_SIZE + 16) k: str = old_k_ptr[0] v: t.CPtr = old_v_ptr[0] diff --git a/json/__init__.vp b/json/__init__.vp index 6c1300d..1c6f205 100644 --- a/json/__init__.vp +++ b/json/__init__.vp @@ -17,8 +17,8 @@ define JSON_OBJECT = 6 # ============================================================ -# JsonValue - JSON 值节点 -# 使用 mpool 分配:JsonValue(pool) 从池中分配内存 +# JsonValue - JSON value node. +# Allocated from mpool: JsonValue(pool) allocates from the pool. # ============================================================ class JsonValue: vtype: int @@ -32,7 +32,7 @@ class JsonValue: child_count: sizet def __new__(self, pool: rawptr[memhub.MemManager]): - """从 mpool 分配 JsonValue 结构体内存并零初始化""" + """Allocate and zero-initialize a JsonValue struct from mpool.""" ptr: JsonValue* = pool.alloc(JsonValue.__sizeof__()) if ptr: string.memset(ptr, 0, JsonValue.__sizeof__()) @@ -78,7 +78,7 @@ class JsonValue: return self.child_count def __getitem__(self, key: str) -> 'JsonValue' | t.CPtr: - """Python风格取值:v["key"] 取对象字段""" + """Python-style get: v["key"] returns an object field.""" if self.vtype != JSON_OBJECT: return None if key == None: @@ -92,7 +92,7 @@ class JsonValue: return None def __setitem__(self, key: str, val: 'JsonValue' | t.CPtr): - """Python风格赋值:v["key"] = val""" + """Python-style set: v["key"] = val.""" if self.vtype != JSON_OBJECT or key == None or val == None: return cur: JsonValue* = self.child @@ -108,7 +108,7 @@ class JsonValue: cur.child_count = val.child_count return cur = cur.next - # key 不存在,追加新子节点 + # key absent: append a new child node klen: sizet = string.strlen(key) kbuf: str = self._pool_alloc(klen + 1) if kbuf != None: @@ -117,7 +117,7 @@ class JsonValue: self._append_child(val) def get_item(self, index: sizet) -> 'JsonValue' | t.CPtr: - """按索引取数组元素:v.get_item(0)""" + """Get an array element by index: v.get_item(0).""" if self.vtype != JSON_ARRAY and self.vtype != JSON_OBJECT: return None cur: JsonValue* = self.child @@ -130,7 +130,7 @@ class JsonValue: return None def _pool_alloc(self, size: sizet) -> str: - """内部方法:从当前对象的 mpool 分配内存(预留,暂返回 None)""" + """Internal: allocate from current object's mpool (reserved; currently returns None).""" return None def _append_child(self, child: 'JsonValue' | t.CPtr): @@ -145,7 +145,7 @@ class JsonValue: # ============================================================ -# 构造函数:创建各类 JsonValue(从 mpool 分配) +# Constructors: create each kind of JsonValue (allocated from mpool) # ============================================================ def null(pool: rawptr[memhub.MemManager]) -> JsonValue*: @@ -199,13 +199,13 @@ def object(pool: rawptr[memhub.MemManager]) -> JsonValue*: v.vtype = JSON_OBJECT return v -# 向数组追加元素 +# Append an element to an array def array_append(pool: rawptr[memhub.MemManager], arr: JsonValue*, item: JsonValue*): if arr == None or item == None: return if arr.vtype != JSON_ARRAY: return arr._append_child(item) -# 向对象设置键值对 +# Set a key/value pair on an object def object_set(pool: rawptr[memhub.MemManager], obj: JsonValue*, key: str, val: JsonValue*): if obj == None or key == None or val == None: return if obj.vtype != JSON_OBJECT: return @@ -231,7 +231,7 @@ def object_set(pool: rawptr[memhub.MemManager], obj: JsonValue*, key: str, val: # ============================================================ -# 导入子模块 +# Import submodules # ============================================================ from .__parser import parse from .__writer import write diff --git a/json/__parser.vp b/json/__parser.vp index fd5b151..e63a870 100644 --- a/json/__parser.vp +++ b/json/__parser.vp @@ -206,9 +206,8 @@ def _parse_array(s: rawptr[ParseState]) -> rawptr[JsonValue]: return null(s.pool) arr: rawptr[JsonValue] = array(s.pool) - # 池耗尽 ⇒ array() 返回 None(JsonValue.__new__/null 的既有约定:分配失败返回 None)。 - # 若继续走到 arr._append_child(...) 就是空指针解引用 ⇒ 这里向上返回 None - # (与 json.parse 可能返回 None 的约定一致,调用方本就必须判 None)。 + # Pool exhausted => array() returns None; continuing to arr._append_child(...) would deref + # a null pointer, so return None here (json.parse may return None; callers must check). if arr == None: return null(s.pool) @@ -218,8 +217,8 @@ def _parse_array(s: rawptr[ParseState]) -> rawptr[JsonValue]: return arr item: rawptr[JsonValue] = _parse_value(s) - # item == None ⟺ _parse_value 内部某处分配失败(_parse_value 对语法错误返回的是 - # 合法的 JSON_NULL 节点,只有池耗尽才是 None)⇒ 向上返回 None,不追加半条。 + # item == None iff allocation failed somewhere inside _parse_value (syntax errors yield + # a valid JSON_NULL node; only pool exhaustion yields None) => return None, append nothing. if item == None: return null(s.pool) arr._append_child(item) @@ -245,7 +244,7 @@ def _parse_object(s: rawptr[ParseState]) -> rawptr[JsonValue]: return null(s.pool) obj: rawptr[JsonValue] = object(s.pool) - # 同 _parse_array:池耗尽时 obj 为 None,不能对 None 调 _append_child。 + # Same as _parse_array: obj is None on pool exhaustion; do not call _append_child on None. if obj == None: return null(s.pool) @@ -290,7 +289,7 @@ def _parse_object(s: rawptr[ParseState]) -> rawptr[JsonValue]: def parse(pool: rawptr[memhub.MemManager], src: str) -> rawptr[JsonValue]: - """解析 JSON 字符串,返回 JsonValue 树""" + """Parse a JSON string, returning a JsonValue tree.""" if src == None: return null(pool) s: rawptr[ParseState] = ParseState(src, pool) diff --git a/json/__writer.vp b/json/__writer.vp index f10b096..4ab0cd2 100644 --- a/json/__writer.vp +++ b/json/__writer.vp @@ -21,25 +21,25 @@ from . import ( # cap 的合法区间是 [WRITE_CAP_MIN, WRITE_CAP_MAX]:越界一律返回 None, # 非法容量不进入 pool.alloc(不会按可疑值去申请内存)。 # ============================================================ -define WRITE_CAP_MIN = 64# 下限:最短 JSON 文档(如 {"id":""})含结尾 NUL 也在 64 字节内 -define WRITE_CAP_MAX = 67108864# 上限:64 MiB,不小于调用方可能申请的最大输出缓冲 -define WRITE_CAP_DEFAULT = 4096# write 三参入口的固定容量(与旧实现 pool.alloc(4096) 一致) +define WRITE_CAP_MIN = 64# lower bound: shortest JSON doc (e.g. {"id":""}) incl NUL fits in 64 bytes +define WRITE_CAP_MAX = 67108864# upper bound: 64 MiB, not less than the largest output buffer a caller may request +define WRITE_CAP_DEFAULT = 4096# fixed capacity for the 3-arg write entry def _write_char(buf: str, pos: sizet, cap: sizet, ch: char) -> sizet: if pos + 1 >= cap: if pos >= cap: - return pos # 已带溢出标志:原样返回,不再写 - return cap # 留不出结尾 NUL 位 ⇒ 置溢出标志 + return pos # overflow flag already set: return as-is, do not write + return cap # cannot reserve trailing NUL slot => set overflow flag buf[pos] = ch return pos + 1 def _write_str(buf: str, pos: sizet, cap: sizet, s: str) -> sizet: if s == None: return pos if pos >= cap: - return pos # 已带溢出标志:不再写 + return pos # overflow flag already set: stop writing slen: sizet = string.strlen(s) if pos + slen >= cap: - return cap # 留不出结尾 NUL 位 ⇒ 置溢出标志 + return cap # cannot reserve trailing NUL slot => set overflow flag string.memcpy(char(uint64(buf) + pos, t.CPtr), s, slen) return pos + slen @@ -49,12 +49,12 @@ def _write_string_escaped(buf: str, pos: sizet, cap: sizet, s: str) -> sizet: return pos pos = _write_char(buf, pos, cap, '"') if pos >= cap: - return pos # 已溢出:不再写 + return pos # already overflowed: stop writing i: sizet = 0 slen: sizet = string.strlen(s) while i < slen: if pos >= cap: - return pos # 已溢出:提前收敛,不做无意义的逐字符空转 + return pos # already overflowed: converge early, skip pointless per-char looping ch: char = s[i] if ch == '"': pos = _write_str(buf, pos, cap, "\\\"") @@ -76,7 +76,7 @@ def _write_string_escaped(buf: str, pos: sizet, cap: sizet, s: str) -> sizet: def _write_value(buf: str, pos: sizet, cap: sizet, val: rawptr[JsonValue], pool: rawptr[memhub.MemManager]) -> sizet: if pos >= cap: - return pos # 已溢出:不再写(递归在此收敛) + return pos # already overflowed: stop writing (recursion converges here) if val == None: pos = _write_str(buf, pos, cap, "null") return pos @@ -114,7 +114,7 @@ def _write_value(buf: str, pos: sizet, cap: sizet, val: rawptr[JsonValue], first: bool = True while cur != None: if pos >= cap: - return pos # 已溢出:停止遍历,不再写 + return pos # already overflowed: stop iterating, do not write if not first: pos = _write_char(buf, pos, cap, ',') pos = _write_value(buf, pos, cap, cur, pool) @@ -131,7 +131,7 @@ def _write_value(buf: str, pos: sizet, cap: sizet, val: rawptr[JsonValue], first2: bool = True while cur2 != None: if pos >= cap: - return pos # 已溢出:停止遍历,不再写 + return pos # already overflowed: stop iterating, do not write if not first2: pos = _write_char(buf, pos, cap, ',') pos = _write_string_escaped(buf, pos, cap, cur2.key) @@ -144,20 +144,19 @@ def _write_value(buf: str, pos: sizet, cap: sizet, val: rawptr[JsonValue], return pos -# 序列化为 JSON 字符串。cap = 输出缓冲容量(字节,含结尾 NUL),缺省 4096 -# (与旧实现 pool.alloc(4096) 一致 ⇒ 既有 3 参调用点行为不变)。 -# 内容放不下时返回 None(旧实现无边界判断,会写穿池块)。 +# Serialize to a JSON string. cap = output buffer size (bytes, incl trailing NUL), default 4096. +# Returns None when content does not fit (never writes past the buffer). def write(pool: rawptr[memhub.MemManager], val: rawptr[JsonValue], pretty: bool, cap: sizet = 4096) -> str: - """将 JsonValue 序列化为 JSON 字符串""" + """Serialize a JsonValue into a JSON string.""" if cap < 2: - return None # 连结尾 NUL 都放不下 + return None # not enough room even for the trailing NUL buf: str = pool.alloc(cap) if buf == None: return None end_pos: sizet = _write_value(buf, 0, cap, val, pool) if end_pos >= cap: - return None # 溢出标志:内容超出容量 + return None # overflow flag: content exceeds capacity buf[end_pos] = '\0' return buf \ No newline at end of file diff --git a/memhub.vp b/memhub.vp index 12e7641..3655975 100644 --- a/memhub.vp +++ b/memhub.vp @@ -220,8 +220,8 @@ def _alloc_site_current() -> uint64: return _alloc_pending_site -# free 被拒(_is_valid_ptr 判无效)只报一次:这类"静默 no-op"会让块永不回收, -# 历史上正是它把 1 GiB 主池漏满且毫无线索,故首次出现必须留下现场。 +# A rejected free (_is_valid_ptr invalid) is reported only once: this silent no-op leaks the block +# forever, so the first occurrence must leave evidence. _rej_free_reported: int = 0 @@ -592,9 +592,9 @@ class MemBuddy(MemManager): chk_depth: sizet # current record depth chk_base: rawptr # record-stack base (stdlib.malloc, separate from the buddy area) big_alloc_count: sizet # count of allocations >= 8KB (OOM diagnostics) - # mark() 时刻的空闲字节(OOM 诊断):据此区分「本窗口把池吃满」与「进窗口前就已满」 + # Free bytes at mark() time (OOM diagnostics): distinguishes "this window filled the pool" from already-full. mark_free_bytes: sizet = 0 - # mark() 时刻的记录深度:OOM 分段诊断用(前缀 vs 本窗口) + # Record depth at mark() time: for OOM segmented diagnostics (prefix vs this window). mark_depth: sizet = 0 # [DET] Allocation detection (off by default; enable to hunt overflows / live-block overwrites): # detection=1 -> 2-byte 0xCDCD markers + record table + a probe after each alloc @@ -724,8 +724,8 @@ class MemBuddy(MemManager): self.chk_stack = (uint64*)(base) self.chk_cap = cap self.chk_depth = 0 - # OOM 诊断:记下打点时刻的空闲字节与记录深度,失败报告里据此区分 - # 「本窗口把池吃满」与「进窗口前池就已满(历史丢失/常驻占用)」。 + # OOM diagnostics: record free bytes and record depth at this mark, so a failure report can + # distinguish "this window filled the pool" from already-full-before-window. self.mark_free_bytes = self.free_bytes() self.mark_depth = self.chk_depth return self.chk_depth @@ -1410,8 +1410,8 @@ class MemBuddy(MemManager): self._lock() result: rawptr = None if size != 0 and self.usable is not None: - # OOM 诊断:≥8KB 的请求计数。此字段此前从不自增,报告里恒为 big=0, - # 无法据此分辨“多次小块”与“少量大块”。阈值与字段注释口径一致。 + # OOM diagnostics: count requests >= 8KB so the report can tell "many small blocks" from + # "few large blocks"; threshold matches the field comment. if size >= 8192: self.big_alloc_count += 1 # +CANARY: reserve 8 sentinel bytes past the user area for overflow detection. @@ -1448,10 +1448,8 @@ class MemBuddy(MemManager): # it with stdlib.realloc (a full stack would stop recording -> reset misses # blocks and _is_valid_ptr's stack check degrades). # - # 记录栈懒建(首次 mark() 之前也记账):否则"首次 mark() 之前"分配的块不在 - # chk_stack 里,事后 free 它们时 _is_valid_ptr 的活跃集检查必然判无效 → - # free 被永久拒绝 → 这些块再无可能回到空闲链 = **永久漏块**,是池单调漏满的 - # 一个确定性来源(OOM 报告里若出现「块头奇数=1 但在记录窗口=0」即为实锤)。 + # Lazily build the record stack (also records before the first mark()); else blocks + # allocated before it are missing from chk_stack and their free is permanently rejected. if self.chk_stack is None: self.mark() if self.chk_stack is not None: @@ -1585,16 +1583,14 @@ class MemBuddy(MemManager): stdio.printf("[MEM] alloc 失败 size=%zu needed=%zu order=%d max_order=%d used=%zu pool_base=%llu\n", size, needed2, order2, self.max_order, self.used_bytes(), uint64(self.usable)) - # 归属(always-on,无需 --detection):site 来自 alloc_tagged 维护的站点 tag, - # ctx=阶段/当前文件由 TPV 的 SetAllocCtx 维护(未设置则打印 ?)。只在失败路径 - # 打印,不影响正常流程;缺这一行时 [MEM] 只给池基址,无法判断谁吃满了池子。 + # Attribution (always-on, no --detection needed): site comes from the alloc_tagged site + # tag, ctx=stage/current file from TPV's SetAllocCtx (? if unset). Failure path only. stdio.printf("[MEM] 归属 site=%s ctx=%s/%s 池容量=%zu\n", _det_site_name(_alloc_site_current()), _alloc_ctx_stage_str(), _alloc_ctx_detail_str(), self.usable_size) - # 窗口起点空闲:mark() 时刻记下的 free_bytes。 - # ≈池容量 ⇒ 本窗口把池吃满(消费点在当前文件); - # ≈0 ⇒ 进窗口前池就已满(历史丢失/常驻占用,当前文件只是受害者)。 + # Window-start free bytes (recorded at mark()): ~pool size => this window filled the + # pool; ~0 => already full before the window (current file is only a victim). win_start_free: sizet = self.mark_free_bytes win_now_free: sizet = self.free_bytes() win_net: sizet = 0 @@ -1602,7 +1598,7 @@ class MemBuddy(MemManager): win_net = win_start_free - win_now_free stdio.printf("[MEM] 本窗口起点空闲=%zu 当前空闲=%zu(窗口内净分配=%zu)\n", win_start_free, win_now_free, win_net) - # 尺寸画像:池被谁吃满,看存活块的 order 分布(只读既有状态,无需 --detection) + # Size profile: see which orders of live blocks filled the pool (read-only, no --detection). self.dump_alive_orders() # self_check != 0 means the free list structure is damaged (a block header or # next pointer was written through), so alloc finds no block despite free space. @@ -1722,17 +1718,8 @@ class MemBuddy(MemManager): stdio.fflush(None) stdlib.exit(1) else: - # _is_valid_ptr 判无效 = 该块不会被回收到空闲链(静默 no-op 是"池漏满却 - # 无线索"的元凶)。首次出现即打印一行现场,之后不再刷屏。 - # 三个判据逐项打出(mirror _is_valid_ptr),避免"header=0x0"这种歧义: - # in_pool=0 → free() 收到池外指针(stdlib/另一池/野指针) - # aligned=0 → 块地址未按 32 字节对齐(指针不是块首) - # hdr_odd=0 → 块头是偶数(已释放/被越界写穿)——正常 free 之后重复 free - # 或"邻居块头被写坏"都落这里,取两者的区分看「窗口内=1 且 - # 该块状态异常」与后续 canary/账目行 - # in_win=0 → 块头是奇数(存活)却不在 chk_stack[0, chk_depth) 窗口内 - # (分配发生在首次 mark() 之前 ⇒ 记录栈里没有它 ⇒ 该 free - # 被永久拒绝 ⇒ 这块再也不会回到空闲链) + # _is_valid_ptr invalid = block never returns to a free list (silent pool leak); print once. + # Criteria: in_pool=0 not in pool, aligned=0 not 32B-aligned, hdr_odd=0 even header, in_win=0 outside window. global _rej_free_reported if _rej_free_reported == 0: _rej_free_reported = 1 @@ -1760,7 +1747,7 @@ class MemBuddy(MemManager): break e_ci += 1 else: - e_in_win = -1 # 记录栈满(chk_depth==chk_cap)⇒ 该检查被跳过 + e_in_win = -1 # record stack full (chk_depth==chk_cap) -> check skipped stdio.printf("[MEM] free 被拒绝: ptr=%llu block=%llu 在池内=%d 对齐=%d " "块头=0x%llX 块头奇数=%d 在记录窗口=%d chk_depth=%zu " "本池 base=%llu usable=%llu usable_size=%zu\n", @@ -1958,7 +1945,7 @@ class MemBuddy(MemManager): head_val: uint64 = self.free_lists[i] cur: rawptr = rawptr(head_val) if uint64(cur) != 0 and self._fl_block_ok(cur) == 0: - # 链表头本身非法(如未初始化 0xBE…):直接解引用会 AV,这里报告后停止本阶。 + # Free-list head itself invalid (e.g. uninitialized 0xBE...): dereferencing would AV; report and stop this order. stdio.printf("[MEM] free_lists[%d] 头非法(非池内对齐块地址,疑元数据未初始化/被写穿): " "head=%llu chk_depth=%zu usable=%llu usable_size=%zu\n", i, head_val, self.chk_depth, uint64(self.usable), self.usable_size) @@ -1974,8 +1961,8 @@ class MemBuddy(MemManager): count += 1 next_p: rawptr = rawptr(*((uint64*)(cur))) if uint64(next_p) != 0 and self._fl_block_ok(next_p) == 0: - # 头/节点非法(未初始化 0xBE…/非池内对齐地址):继续解引用会直接 AV。 - # 这里只报告并停止本阶计数——"链表头是未初始化内存"本身就是元数据损坏的铁证。 + # Head/node invalid (uninitialized 0xBE... or non-pool-aligned): further dereference + # would AV, so just report and stop counting this order (metadata corruption proof). stdio.printf("[MEM] free_lists[%d] 节点非法(非池内对齐块地址,疑元数据未初始化/被写穿): " "head=%llu cur=%llu next=%llu chk_depth=%zu usable=%llu usable_size=%zu\n", i, head_val, uint64(cur), uint64(next_p), self.chk_depth, @@ -1995,7 +1982,7 @@ class MemBuddy(MemManager): head_val: uint64 = self.free_lists[i] cur: rawptr = rawptr(head_val) if uint64(cur) != 0 and self._fl_block_ok(cur) == 0: - # 链表头本身非法(如未初始化 0xBE…):直接解引用会 AV,这里报告后停止本阶。 + # Free-list head itself invalid (e.g. uninitialized 0xBE...): dereferencing would AV; report and stop this order. stdio.printf("[MEM] free_lists[%d] 头非法(非池内对齐块地址,疑元数据未初始化/被写穿): " "head=%llu chk_depth=%zu usable=%llu usable_size=%zu\n", i, head_val, self.chk_depth, uint64(self.usable), self.usable_size) @@ -2099,8 +2086,8 @@ class MemBuddy(MemManager): stdio.printf("[MEM] 整池账目:镜像存活=%zu 块/%zu 字节 空闲链表=%zu 字节 " "丢失=%zu 字节(丢失=既不在链表也不被持有 ⇒ 空闲链被写穿后截断)\n", live, alive_bytes, fb, lost) - # 分段:mark() 之后分配的那段才是「本窗口」。仅看总数会把历史前缀也算进来 - # (mark1 在 Phase1 1b 只打一次 ⇒ 前缀=1a 的记录,窗口=整段 1b)。 + # Segmentation: only the part allocated after mark() is "this window"; the total would also + # include the historical prefix (mark1 runs once in Phase1 1b => prefix=1a, window=1b). stdio.printf("[MEM] 分段(mark_depth=%zu chk_depth=%zu):前缀[0,mark)=%zu 块/%zu 字节 " "本窗口[mark,end)=%zu 块/%zu 字节\n", self.mark_depth, self.chk_depth, pfx_live, pfx_bytes_a, @@ -2131,12 +2118,11 @@ class MemBuddy(MemManager): hv_p8: uint64 = 0 if raw >= uint64(self.usable) and raw < pool_end: bp: rawptr = rawptr(raw) - # 同址两种读法交叉验证:hv 用直读 `*((uint64*)(p))`,hv2 用 memhub - # 其余路径通用的两步式 `uint64(rawptr(*((uint64*)(p))))`。若两者不一致, - # 说明「块头读作 0」是读法/代码生成问题,而非池真的被清零。 + # Cross-check two reads at the same address: hv direct, hv2 via the two-step memhub + # form; a mismatch means "header read as 0" is a codegen/read issue, not a zeroed pool. hv = *((uint64*)(bp)) hv2 = uint64(rawptr(*((uint64*)(bp)))) - # 邻字探针:整片被零填充 ⇒ 前后邻字同样为 0(一次巨量零填充写的签名) + # Neighbor probe: an all-zero region implies the adjacent words are also 0 (signature of a huge zero-fill write). if raw >= uint64(self.usable) + 8: bp_m: rawptr = rawptr(raw - 8) hv_m8 = *((uint64*)(bp_m)) @@ -2162,7 +2148,7 @@ class MemBuddy(MemManager): head_val: uint64 = self.free_lists[i] cur: rawptr = rawptr(head_val) if uint64(cur) != 0 and self._fl_block_ok(cur) == 0: - # 链表头本身非法(如未初始化 0xBE…):直接解引用会 AV,这里报告后停止本阶。 + # Free-list head itself invalid (e.g. uninitialized 0xBE...): dereferencing would AV; report and stop this order. stdio.printf("[MEM] free_lists[%d] 头非法(非池内对齐块地址,疑元数据未初始化/被写穿): " "head=%llu chk_depth=%zu usable=%llu usable_size=%zu\n", i, head_val, self.chk_depth, uint64(self.usable), self.usable_size) diff --git a/mlirlite/__builder.vp b/mlirlite/__builder.vp index 48088a3..c0f2167 100644 --- a/mlirlite/__builder.vp +++ b/mlirlite/__builder.vp @@ -47,17 +47,17 @@ _TY_TEXT_CAP: sizet = 2048 _LINE_CAP: sizet = 2560 novirtual class IRBuilder: - Pool: memhub.MemBuddy* # 内存池 - Func: rawptr[Function] # 所属函数 - CurBlock: rawptr[BasicBlock] # 当前插入点 - Counter: int # SSA 名计数器 - DbgLoc: str # current DILocation ref(同形保留;MLIR 打印阶段忽略,不发 !dbg 后缀) + Pool: memhub.MemBuddy* # Memory pool + Func: rawptr[Function] # Owning function + CurBlock: rawptr[BasicBlock] # Current insertion point + Counter: int # SSA name counter + DbgLoc: str # current DILocation ref (kept same-shaped; MLIR printing ignores it, no !dbg suffix) # ============================================================ - # 内部辅助 + # Internal helpers # ============================================================ def _alloc_ssa_name(self) -> str: - """分配一个 SSA 名字符串 "%N" 并递增计数器""" + """Allocate an SSA name string "%N" and increment the counter""" pool: memhub.MemBuddy* = self.Pool buf: str = pool.alloc(16) if buf is None: return None @@ -67,22 +67,16 @@ novirtual class IRBuilder: def _type_str(self, ty: rawptr[LLVMType]) -> str: - """获取类型的 MLIR 文本(从 pool 分配 _TY_TEXT_CAP 字节缓冲区) - - MLIR 类型文本内联具名结构体的完整字段体,可达数百字节;超出 - _TY_TEXT_CAP 时 TypePrint 会**响亮报错**(不再静默截断)。 + """Get the MLIR text of a type (allocates a _TY_TEXT_CAP-byte buffer from pool). + MLIR type text inlines full struct bodies; overflow makes TypePrint fail-fast loudly. """ pool: memhub.MemBuddy* = self.Pool buf: str = pool.alloc(_TY_TEXT_CAP) if buf is None: return None buf[0] = '\0' TypePrint(buf, _TY_TEXT_CAP, ty, pool) - # [IR-FAIL] 校验护栏:LLVMType 类型对象可能被 MemBuddy 覆盖/悬空 - # (跨遍 reset 回收后 pool 内存复用),此时 TypePrint 输出首字符 - # 变为垃圾(如 't')或为空。立即 fail-fast 终止,防止生成 - # 垃圾 op 行,避免后续报错难以定位根因。 - # 合法首字符:MLIR 类型('i'/'f'/'!llvm.' 的 '!')与 - # 中间文本里可能出现的 '{'/'['/'"'(聚合/命名类型)等。 + # [IR-FAIL] guard: LLVMType may be overwritten/dangling (pool reuse after reset), making the + # first char garbage/empty; fail-fast. Legal first chars: i/f/!/[/{/%/" and 'n'. c0: int = buf[0] if c0 == 0 or (c0 != 'i' and c0 != 'f' and c0 != '!' and c0 != 'n' and c0 != '[' and c0 != '{' and c0 != '%' and c0 != '"'): @@ -94,38 +88,34 @@ novirtual class IRBuilder: def _emit(self, text: str): - """将指令文本追加到当前块 - - 与 llvmlite 的差异:MLIR 语法没有 `, !dbg !N` 后缀,故本实现**故意不**读取 - DbgLoc(set_debug_location 只记录模型,不改变输出)。其余调用方逻辑一致。 - """ + """Append instruction text to the current block. Unlike llvmlite, MLIR has no ", !dbg !N" + suffix, so DbgLoc is deliberately ignored (set_debug_location only records the model).""" if self is None or self.CurBlock is None: return block_append_text(self.Pool, self.CurBlock, text) def set_debug_location(self, ref: str): - """Set the current DILocation ref(同形保留;MLIR 侧仅记录,不影响输出) - + """Set the current DILocation ref (kept same-shaped; MLIR only records it, no output effect). ref=None clears it. """ if self is None: return self.DbgLoc = ref def clear_debug_location(self): - """Clear the current DILocation ref(同形保留;MLIR 侧仅记录)""" + """Clear the current DILocation ref (kept same-shaped; MLIR only records it)""" if self is None: return self.DbgLoc = None def _mark_terminated(self): - """标记当前块已终止""" + """Mark the current block as terminated""" if self is None or self.CurBlock is None: return block_set_terminated(self.CurBlock, 1) def _build_binop(self, op: str, lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: - """二元运算通用实现""" + """Shared implementation of binary operations""" if self is None or lhs is None or rhs is None: return None pool: memhub.MemBuddy* = self.Pool name: str = self._alloc_ssa_name() @@ -138,14 +128,12 @@ novirtual class IRBuilder: # ============================================================ - # 块定位 + # Block positioning # ============================================================ def position_at_end(self, block: rawptr[BasicBlock]): - """将插入点设为 block 的末尾,并把 block 移到函数块链表末尾。 - - 移动 block 是为了保证 BB 文本输出顺序与控制流(position)顺序一致, - 避免 SSA 名在文本中非单调递增导致 llc 编译失败。 - """ + """Set the insertion point to the end of block and move block to the end of the + function's block list, keeping text order consistent with control-flow order (else + non-monotonic SSA names break llc).""" if self is None: return self.CurBlock = block if self.Func is not None: @@ -153,26 +141,22 @@ novirtual class IRBuilder: def builder_cur_block_is_terminated(self) -> int: - """检查当前块的 IsTerminated 标志 - - 通过 block_get_terminated 辅助函数访问,避免链式属性 - self.CurBlock.IsTerminated 在跨模块场景下 BasicBlock 为 opaque - 导致的 GEP 静默跳过问题。 - """ + """Check the current block's IsTerminated flag via block_get_terminated instead of the + chained self.CurBlock.IsTerminated, which silently skips the GEP when BasicBlock is opaque.""" if self is None or self.CurBlock is None: return 1 return block_get_terminated(self.CurBlock) # ============================================================ - # 内存指令 + # Memory instructions # ============================================================ def build_alloca(self, ty: rawptr[LLVMType]) -> rawptr[Value]: """%N = alloca - 返回类型为 ty* 的 SSA 值(默认对齐,由后端决定)。 - 注意:TPC 并行编译时 include 模块的函数默认参数不会预注册(argN 名回退), - 因此本函数不设默认参数;需要显式对齐请用 build_alloca_aligned。 + Returns an SSA value of type ty* (default alignment, backend-decided). + No default parameter: TPC parallel compilation does not pre-register include defaults + (argN fallback); for explicit alignment use build_alloca_aligned. """ if self is None: return None pool: memhub.MemBuddy* = self.Pool @@ -183,7 +167,7 @@ novirtual class IRBuilder: viperlib.snprintf(line, _LINE_CAP, "%s = alloca %s", name, ty_s) self._emit(line) - # 结果类型是 ty* + # result type is ty* result_ty: rawptr[LLVMType] = LLVMType.Ptr(ty) result_ty_ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if result_ty_ptr is not None: @@ -194,11 +178,8 @@ novirtual class IRBuilder: def build_alloca_aligned(self, ty: rawptr[LLVMType], align: int) -> rawptr[Value]: - """%N = alloca , align N - - 返回类型为 ty* 的 SSA 值;显式指定对齐(如 va_list 24 字节缓冲区需 16 对齐)。 - align 不设默认值(TPC 并行编译时 include 默认参数不预注册),调用方必须显式传入。 - """ + """%N = alloca , align N. Returns an SSA value of type ty* with explicit alignment + (e.g. va_list 24-byte buffer needs 16). align has no default; callers must pass it.""" if self is None: return None pool: memhub.MemBuddy* = self.Pool name: str = self._alloc_ssa_name() @@ -208,7 +189,7 @@ novirtual class IRBuilder: viperlib.snprintf(line, _LINE_CAP, "%s = alloca %s, align %d", name, ty_s, align) self._emit(line) - # 结果类型是 ty* + # result type is ty* result_ty: rawptr[LLVMType] = LLVMType.Ptr(ty) result_ty_ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if result_ty_ptr is not None: @@ -218,11 +199,8 @@ novirtual class IRBuilder: def build_alloca_at_entry(self, ty: rawptr[LLVMType]) -> rawptr[Value]: - """%N = alloca (在函数入口块生成,确保支配性) - - 在入口块的终止指令之前插入 alloca,避免在条件分支内生成 alloca - 导致的支配性违规(Instruction does not dominate all uses)。 - """ + """%N = alloca , emitted in the function entry block to ensure dominance. + Inserts before the entry terminator; alloca inside a conditional branch violates dominance.""" if self is None: return None pool: memhub.MemBuddy* = self.Pool func: rawptr[Function] = self.Func @@ -237,13 +215,10 @@ novirtual class IRBuilder: line_text: str = pool.alloc(_LINE_CAP) if line_text is None: return None viperlib.snprintf(line_text, _LINE_CAP, "%s = alloca %s", name, ty_s) - # 在入口块终止指令之前插入:若入口块已以 br/ret 终止(如嵌套循环内 - # 再分配循环变量),block_append_text 会把 alloca 追加到终止指令之后, - # 生成 "br 之后不可达指令" 的非法 IR(llc: "instruction expected to be - # numbered '%N' or greater")。block_insert_text_before_terminator - # 在块未终止时等同追加,已终止时插到终止指令之前。 + # Insert before the entry terminator: if the entry block already ends in br/ret, appending + # would put alloca after the terminator (invalid IR); insert-before-terminator handles both. block_insert_text_before_terminator(pool, entry_blk, line_text) - # 结果类型是 ty* + # result type is ty* result_ty: rawptr[LLVMType] = LLVMType.Ptr(ty) result_ty_ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if result_ty_ptr is not None: @@ -256,11 +231,8 @@ novirtual class IRBuilder: ptr: rawptr[Value], is_volatile: int = 0) -> rawptr[Value]: """%N = load [volatile ], - - is_volatile=0(默认)逐字输出既有形态;=1 时在 load 后插入 volatile - 限定符(`%N = load volatile i32, i32* %p`),供自旋锁等必须禁止 - 优化器提升/合并重复访问的场景使用。 - """ + is_volatile=0 (default) emits the plain form; =1 adds the volatile qualifier so the + optimizer cannot hoist/merge the access (e.g. spinlocks).""" if self is None or ptr is None: return None pool: memhub.MemBuddy* = self.Pool name: str = self._alloc_ssa_name() @@ -279,10 +251,8 @@ novirtual class IRBuilder: def build_store(self, val: rawptr[Value], ptr: rawptr[Value], is_volatile: int = 0): """store [volatile ] , - - is_volatile=0(默认)逐字输出既有形态;=1 时在 store 后插入 volatile - 限定符(`store volatile i32 %v, i32* %p`)。 - """ + is_volatile=0 (default) emits the plain form; =1 adds the volatile qualifier + (`store volatile i32 %v, i32* %p`).""" if self is None or val is None or ptr is None: return pool: memhub.MemBuddy* = self.Pool val_ty_s: str = self._type_str(val.Ty) @@ -297,7 +267,7 @@ novirtual class IRBuilder: # ============================================================ - # 二元运算指令 + # Binary operation instructions # ============================================================ def build_add(self, lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: @@ -378,7 +348,7 @@ novirtual class IRBuilder: # ============================================================ - # 浮点二元运算指令 + # Float binary operation instructions # ============================================================ def build_fadd(self, lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: @@ -424,14 +394,12 @@ novirtual class IRBuilder: # ============================================================ - # 比较指令 + # Comparison instructions # ============================================================ def build_icmp(self, pred: int, lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: """%N = icmp , - - 返回类型为 i1 的 SSA 值 - """ + Returns an SSA value of type i1.""" if self is None or lhs is None or rhs is None: return None pool: memhub.MemBuddy* = self.Pool name: str = self._alloc_ssa_name() @@ -442,7 +410,7 @@ novirtual class IRBuilder: viperlib.snprintf(line, _LINE_CAP, "%s = icmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) self._emit(line) - # 结果类型是 i1 + # result type is i1 i1_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if i1_ty is not None: string.memset(i1_ty, 0, LLVMType.__sizeof__()) @@ -453,9 +421,7 @@ novirtual class IRBuilder: def build_fcmp(self, pred: int, lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: """%N = fcmp , - - 返回类型为 i1 的 SSA 值 - """ + Returns an SSA value of type i1.""" if self is None or lhs is None or rhs is None: return None pool: memhub.MemBuddy* = self.Pool name: str = self._alloc_ssa_name() @@ -466,7 +432,7 @@ novirtual class IRBuilder: viperlib.snprintf(line, _LINE_CAP, "%s = fcmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) self._emit(line) - # 结果类型是 i1 + # result type is i1 i1_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if i1_ty is not None: string.memset(i1_ty, 0, LLVMType.__sizeof__()) @@ -475,7 +441,7 @@ novirtual class IRBuilder: # ============================================================ - # 终止指令 + # Terminator instructions # ============================================================ def build_br(self, target: rawptr[BasicBlock]): """br label %""" @@ -525,26 +491,23 @@ novirtual class IRBuilder: # ============================================================ - # 调用指令 + # Call instructions # ============================================================ def build_call(self, callee: str, args: rawptr[Value], arg_count: int, ret_ty: rawptr[LLVMType], is_variadic: int = 0) -> rawptr[Value]: """%N = call @() - - args 是 Value 链表头,arg_count 是参数数量。 - 若 ret_ty 是 void,不分配 SSA 名(使用 call 而非 %N = call)。 - is_variadic=1 时发射函数类型签名(如 i32 (i8*, ...) @printf)。 - """ + args is the Value list head, arg_count the argument count. If ret_ty is void no SSA name is + allocated (plain call); is_variadic=1 emits the function type signature (e.g. i32 (i8*, ...)).""" if self is None: return None pool: memhub.MemBuddy* = self.Pool - # --traceback: 记录本次直接调用的调用点(构建期,no-op 除非已启用且处于 - # DebugInfoGen 的 define 上下文内)。详见 __function.vp 的调试调用点记录节。 + # --traceback: record this direct call's call site (build time; no-op unless enabled and + # inside a DebugInfoGen define context). See the debug call-site section in __function.vp. _DebugNoteCall(callee) - # 判断返回类型是否为 void + # check whether the return type is void is_void: int = 0 match ret_ty: case LLVMType.Void(): @@ -558,9 +521,8 @@ novirtual class IRBuilder: ret_ty_s: str = self._type_str(ret_ty) - # 构建参数列表文本 + 变参函数类型签名 - # 实参类型文本内联具名结构体体(单个可达 _TY_TEXT_CAP 字节),故按 - # 实参个数给足容量;固定 1024 字节会在两个以上结构体实参时静默截断。 + # Build the argument list text + variadic function type signature; size by argument count + # (struct type text can be _TY_TEXT_CAP each; a fixed 1024 would silently truncate). arg_cap: sizet = sizet(arg_count + 1) * (_TY_TEXT_CAP + 32) + 32 args_buf: str = pool.alloc(arg_cap) if args_buf is None: return None @@ -587,7 +549,7 @@ novirtual class IRBuilder: _append_cstr(func_ty_buf, arg_cap, ", ") _append_cstr(func_ty_buf, arg_cap, "...") - # 构造带引号的 callee 名字(SHA1 前缀名含 '.' 或以数字开头需要加引号) + # Build the quoted callee name (SHA1-prefixed names with '.' or a leading digit need quotes) callee_buf: str = pool.alloc(_LINE_CAP) if callee_buf is None: return None callee_buf[0] = '\0' @@ -620,17 +582,15 @@ novirtual class IRBuilder: # ============================================================ - # 间接调用指令: call %callee() + # Indirect call instruction: call %callee() # - # callee_val 是函数指针 SSA 值(必须已是函数指针类型,如 i32(i8*)*) + # callee_val is a function-pointer SSA value (must already be a pointer type, e.g. i32(i8*)*) # ============================================================ def build_call_indirect(self, callee_val: rawptr[Value], args: rawptr[Value], arg_count: int, ret_ty: rawptr[LLVMType]) -> rawptr[Value]: """%N = call %callee() - - 间接调用:callee_val 是函数指针 SSA 值。 - """ + Indirect call: callee_val is a function-pointer SSA value.""" if self is None or callee_val is None: return None pool: memhub.MemBuddy* = self.Pool @@ -647,7 +607,7 @@ novirtual class IRBuilder: ret_ty_s: str = self._type_str(ret_ty) - # 构建参数列表文本(容量按实参个数给足,理由同 build_call) + # Build the argument list text (sized by argument count, same reason as build_call) arg_cap2: sizet = sizet(arg_count + 1) * (_TY_TEXT_CAP + 32) + 32 args_buf: str = pool.alloc(arg_cap2) if args_buf is None: return None @@ -681,7 +641,7 @@ novirtual class IRBuilder: # ============================================================ - # 类型转换指令 + # Type conversion instructions # ============================================================ def build_bitcast(self, val: rawptr[Value], to_ty: rawptr[LLVMType]) -> rawptr[Value]: @@ -778,7 +738,7 @@ novirtual class IRBuilder: # ============================================================ - # 浮点转换指令 + # Float conversion instructions # ============================================================ def build_fpext(self, val: rawptr[Value], to_ty: rawptr[LLVMType]) -> rawptr[Value]: @@ -871,18 +831,13 @@ novirtual class IRBuilder: # ============================================================ - # _gep_index_zext - GEP 下标零扩展(窄整数) + # _gep_index_zext - zero-extend narrow integer GEP indices # - # LLVM 对 GEP 的窄整数下标默认按**符号扩展**:i8 0xE2(226) 被当成 -30, - # 于是 ptr[200] 实际读到 ptr[-56](越界读写)。Viper 的下标语义是**非负**, - # 故所有 <32 位的整数下标一律 zext 到 i32;i32/i64 原样透传(`p[-1]` 这类 - # i32 负偏移不受影响),非整数类型原样返回。 - # - # 与 llvmlite 保持同口径(唯一真源):build_gep / build_gep_array 内部 - # 自行处理,调用侧不需要再手动 zext 下标。 + # LLVM sign-extends narrow GEP indices (i8 0xE2 = -30); Viper indices are non-negative, so all + # <32-bit indices are zext'd to i32. build_gep/build_gep_array handle this internally. # ============================================================ def _gep_index_zext(self, idx: rawptr[Value]) -> rawptr[Value]: - """把 <32 位的整数下标零扩展到 i32;其它类型原样返回""" + """Zero-extend a <32-bit integer index to i32; other types are returned unchanged""" if self is None or idx is None or idx.Ty is None: return idx bits: int = 0 @@ -903,16 +858,14 @@ novirtual class IRBuilder: # ============================================================ - # GEP 指令 + # GEP instructions # ============================================================ def build_gep(self, elem_ty: rawptr[LLVMType], ptr: rawptr[Value], idx: rawptr[Value]) -> rawptr[Value]: """%N = getelementptr , , """ if self is None or ptr is None or idx is None: return None - # 防御性检查: ptr.Ty 必须是指针类型,否则 llc 报错 - # "base of getelementptr must be a pointer" - # 当 translate_value 返回非指针值(如 i32 常量 0)时, - # 直接 GEP 会导致 llc 编译失败 + # Defensive check: ptr.Ty must be a pointer type, else llc errors ("base of getelementptr + # must be a pointer") when translate_value returns a non-pointer (e.g. i32 constant 0). if _is_ptr_type(ptr.Ty) == 0: return None idx = self._gep_index_zext(idx) @@ -927,7 +880,7 @@ novirtual class IRBuilder: name, elem_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) self._emit(line) - # GEP 结果类型是 elem_ty* + # GEP result type is elem_ty* result_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if result_ty is not None: string.memset(result_ty, 0, LLVMType.__sizeof__()) @@ -936,15 +889,13 @@ novirtual class IRBuilder: # ============================================================ - # build_gep_array — 数组双索引 GEP: getelementptr [N x ty], [N x ty]* %ptr, i32 0, i32 %idx + # build_gep_array - two-index array GEP: getelementptr [N x ty], [N x ty]* %ptr, i32 0, i32 %idx # ============================================================ def build_gep_array(self, array_ty: rawptr[LLVMType], elem_ty: rawptr[LLVMType], ptr: rawptr[Value], idx: rawptr[Value]) -> rawptr[Value]: """%N = getelementptr [N x elem_ty], [N x elem_ty]* %ptr, i32 0, i32 %idx - - 结果类型是 elem_ty* - """ + Result type is elem_ty*.""" if self is None or ptr is None or idx is None: return None idx = self._gep_index_zext(idx) pool: memhub.MemBuddy* = self.Pool @@ -958,7 +909,7 @@ novirtual class IRBuilder: name, array_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) self._emit(line) - # GEP 结果类型是 elem_ty* + # GEP result type is elem_ty* result_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if result_ty is not None: string.memset(result_ty, 0, LLVMType.__sizeof__()) @@ -967,15 +918,13 @@ novirtual class IRBuilder: # ============================================================ - # build_gep_struct — 结构体字段 GEP: getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx + # build_gep_struct - struct-field GEP: getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx # ============================================================ def build_gep_struct(self, struct_ty: rawptr[LLVMType], field_ty: rawptr[LLVMType], ptr: rawptr[Value], field_idx: int) -> rawptr[Value]: """%N = getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx - - 结果类型是 field_ty* - """ + Result type is field_ty*.""" if self is None or ptr is None: return None pool: memhub.MemBuddy* = self.Pool name: str = self._alloc_ssa_name() @@ -989,7 +938,7 @@ novirtual class IRBuilder: name, struct_ty_s, ptr_ty_s, ptr.Name, field_idx) self._emit(line) - # GEP 结果类型是 field_ty* + # GEP result type is field_ty* result_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if result_ty is None: stdio.printf("[BGS] result_ty alloc None\n") @@ -1003,21 +952,19 @@ novirtual class IRBuilder: # ============================================================ - # Phi 指令 + # Phi instruction # ============================================================ def build_phi(self, ty: rawptr[LLVMType], incoming_head: rawptr[PhiIncoming], count: int) -> rawptr[Value]: """%N = phi [val1, %blk1], [val2, %blk2], ... - - incoming_head 是 PhiIncoming 链表头,count 是入边数量。 - """ + incoming_head is the PhiIncoming list head; count is the incoming-edge count.""" if self is None: return None pool: memhub.MemBuddy* = self.Pool name: str = self._alloc_ssa_name() ty_s: str = self._type_str(ty) - # 构建入边列表文本: [val, %blk], [val, %blk], ... - # 入边可能很多(如 30+ 个 or 链的 bool.merge),需要大缓冲区 + # Build the incoming list text [val, %blk], ...; there can be many edges (e.g. a 30+ or-chain), + # so a large buffer is required. parts: str = pool.alloc(4096) if parts is None: return None parts[0] = '\0' @@ -1027,7 +974,7 @@ novirtual class IRBuilder: if first == 0: _append_cstr(parts, 4096, ", ") _append_cstr(parts, 4096, "[") - # 使用中间变量避免链式结构体成员访问被编译器丢弃 + # use an intermediate var; chained struct member access would be dropped by the compiler inc_val: rawptr[Value] = cur.Val if inc_val is not None: inc_val_name: str = inc_val.Name @@ -1051,7 +998,7 @@ novirtual class IRBuilder: # ============================================================ - # Switch 指令 + # Switch instruction # ============================================================ def build_switch(self, val: rawptr[Value], default_blk: rawptr[BasicBlock], @@ -1061,7 +1008,7 @@ novirtual class IRBuilder: pool: memhub.MemBuddy* = self.Pool ty_s: str = self._type_str(val.Ty) - # 构建主体: switch , label %default + # build body: switch , label %default line: str = pool.alloc(_LINE_CAP) if line is None: return viperlib.snprintf(line, _LINE_CAP, "switch %s %s, label %%%s", @@ -1069,13 +1016,13 @@ novirtual class IRBuilder: _append_cstr(line, _LINE_CAP, " [") _append_cstr(line, _LINE_CAP, "\n") - # 遍历 case 列表 + # iterate the case list cur: rawptr[SwitchCase] = cases_head case_ty_s: str = self._type_str(val.Ty) while cur is not None: case_line: str = pool.alloc(_LINE_CAP) if case_line is not None: - # 使用中间变量避免链式结构体成员访问被编译器丢弃 + # use an intermediate var; chained struct member access would be dropped by the compiler cv: rawptr[Value] = cur.CaseVal cb: rawptr[BasicBlock] = cur.Block if cv is not None and cb is not None: @@ -1093,7 +1040,7 @@ novirtual class IRBuilder: # ============================================================ - # Select 指令 + # Select instruction # ============================================================ def build_select(self, cond: rawptr[Value], true_val: rawptr[Value], false_val: rawptr[Value]) -> rawptr[Value]: @@ -1111,7 +1058,7 @@ novirtual class IRBuilder: # ============================================================ - # Unreachable 指令 + # Unreachable instruction # ============================================================ def build_unreachable(self): """unreachable""" @@ -1125,7 +1072,7 @@ novirtual class IRBuilder: # ============================================================ - # 原子指令 (Phase 3) + # Atomic instructions (Phase 3) # ============================================================ def build_atomicrmw(self, op: int, ptr: rawptr[Value], val: rawptr[Value], @@ -1150,9 +1097,7 @@ novirtual class IRBuilder: cmp_val: rawptr[Value], new_val: rawptr[Value], succ_order: int, fail_order: int) -> rawptr[Value]: """%N = cmpxchg , , - - 返回 {, i1} 类型的 SSA 值(简化为返回 i64 表示的值对) - """ + Returns a {, i1} SSA value (simplified to an i64 representing the pair).""" if self is None or ptr is None or cmp_val is None or new_val is None: return None pool: memhub.MemBuddy* = self.Pool name: str = self._alloc_ssa_name() @@ -1167,12 +1112,12 @@ novirtual class IRBuilder: name, ptr_ty_s, ptr.Name, cmp_ty_s, cmp_val.Name, new_ty_s, new_val.Name, succ_s, fail_s) self._emit(line) - # 返回原始值的类型 + # return the original value's type return SSAValue(pool, new_val.Ty, name) # ============================================================ - # va_arg 指令: 从 va_list 读取下一个变长参数 + # va_arg instruction: read the next variadic argument from va_list # ============================================================ def build_va_arg(self, va_list_ptr: rawptr[Value], @@ -1195,19 +1140,17 @@ novirtual class IRBuilder: # ============================================================ - # 内联汇编 (Phase 3) + # Inline assembly (Phase 3) # ============================================================ def build_inline_asm(self, ret_ty: rawptr[LLVMType], asm_text: str, constraints: str, has_side_effects: int) -> rawptr[Value]: """%N = call asm [sideeffect] "" ""() - - 简化的内联汇编调用,无参数版本。 - """ + Simplified inline-assembly call, no-argument version.""" if self is None: return None pool: memhub.MemBuddy* = self.Pool - # 判断返回类型是否为 void + # check whether the return type is void is_void: int = 0 match ret_ty: case LLVMType.Void(): @@ -1245,13 +1188,13 @@ novirtual class IRBuilder: # ============================================================ -# 工厂函数 +# Factory functions # ============================================================ -# IRBuilder 硬编码大小: Pool(8) + Func(8) + CurBlock(8) + Counter(4) + DbgLoc(8) = 40 字节(与 llvmlite 同布局) +# IRBuilder hardcoded size: Pool(8) + Func(8) + CurBlock(8) + Counter(4) + DbgLoc(8) = 40 bytes (same layout as llvmlite) define BUILDER_SIZE = 40 def new_builder(pool: memhub.MemBuddy*, func: rawptr[Function]) -> rawptr[IRBuilder]: - """创建 IRBuilder,关联到函数 func""" + """Create an IRBuilder associated with function func.""" ptr: rawptr[IRBuilder] = pool.alloc(BUILDER_SIZE) if ptr is None: return None string.memset(ptr, 0, BUILDER_SIZE) @@ -1264,96 +1207,81 @@ def new_builder(pool: memhub.MemBuddy*, func: rawptr[Function]) -> rawptr[IRBuil # ============================================================ -# 块定位 +# Block positioning # ============================================================ def position_at_end(builder: rawptr[IRBuilder], block: rawptr[BasicBlock]): - """将插入点设为 block 的末尾,并把 block 移到函数块链表末尾。 - - 移动 block 是为了保证 BB 文本输出顺序与控制流(position)顺序一致, - 避免 SSA 名在文本中非单调递增导致 llc 编译失败。 - """ + """Set the insertion point to the end of block and move block to the end of the + function's block list, keeping text order consistent with control-flow order (else + non-monotonic SSA names break llc).""" builder.position_at_end(block) # ============================================================ -# 内部辅助 +# Internal helpers # ============================================================ def _alloc_ssa_name(builder: rawptr[IRBuilder]) -> str: - """分配一个 SSA 名字符串 "%N" 并递增计数器""" + """Allocate an SSA name string "%N" and increment the counter""" return builder._alloc_ssa_name() def _type_str(builder: rawptr[IRBuilder], ty: rawptr[LLVMType]) -> str: - """获取类型的 IR 文本(从 pool 分配 512 字节缓冲区) - - 注意:复杂结构体类型(如 13 方法的虚表 {i8*,i8*,...})可能超过 64 字节, - 必须使用足够大的缓冲区,否则 '}' 会被截断导致 IR 语法错误。 - """ + """Get the IR text of a type (allocates a buffer from pool). Complex struct types (e.g. a + 13-method vtable) can exceed 64 bytes; too small a buffer truncates '}' and breaks the IR.""" return builder._type_str(ty) def _emit(builder: rawptr[IRBuilder], text: str): - """将指令文本追加到当前块""" + """Append instruction text to the current block.""" builder._emit(text) def _mark_terminated(builder: rawptr[IRBuilder]): - """标记当前块已终止""" + """Mark the current block as terminated""" builder._mark_terminated() def builder_cur_block_is_terminated(builder: rawptr[IRBuilder]) -> int: - """检查当前块的 IsTerminated 标志 - - 通过 block_get_terminated 辅助函数访问,避免链式属性 - builder.CurBlock.IsTerminated 在跨模块场景下 BasicBlock 为 opaque - 导致的 GEP 静默跳过问题。 - """ + """Check the current block's IsTerminated flag via block_get_terminated instead of the + chained builder.CurBlock.IsTerminated, which silently skips the GEP when BasicBlock is opaque.""" return builder.builder_cur_block_is_terminated() # ============================================================ -# 调试位置(同形保留;MLIR 打印阶段忽略) +# Debug location (kept same-shaped; ignored by MLIR printing) # ============================================================ def builder_set_debug_location(builder: rawptr[IRBuilder], ref: str): - """Set the current DILocation ref(同形保留;MLIR 侧仅记录,不影响输出)""" + """Set the current DILocation ref (kept same-shaped; MLIR only records it, no output effect)""" builder.set_debug_location(ref) def builder_clear_debug_location(builder: rawptr[IRBuilder]): - """Clear the current DILocation ref(同形保留;MLIR 侧仅记录)""" + """Clear the current DILocation ref (kept same-shaped; MLIR only records it)""" builder.clear_debug_location() # ============================================================ -# 内存指令 +# Memory instructions # ============================================================ def build_alloca(builder: rawptr[IRBuilder], ty: rawptr[LLVMType]) -> rawptr[Value]: """%N = alloca - 返回类型为 ty* 的 SSA 值(默认对齐,由后端决定)。 - 注意:TPC 并行编译时 include 模块的函数默认参数不会预注册(argN 名回退), - 因此本函数不设默认参数;需要显式对齐请用 build_alloca_aligned。 + Returns an SSA value of type ty* (default alignment, backend-decided). + No default parameter: TPC parallel compilation does not pre-register include defaults + (argN fallback); for explicit alignment use build_alloca_aligned. """ return builder.build_alloca(ty) def build_alloca_aligned(builder: rawptr[IRBuilder], ty: rawptr[LLVMType], align: int) -> rawptr[Value]: - """%N = alloca , align N - - 返回类型为 ty* 的 SSA 值;显式指定对齐(如 va_list 24 字节缓冲区需 16 对齐)。 - align 不设默认值(TPC 并行编译时 include 默认参数不预注册),调用方必须显式传入。 - """ + """%N = alloca , align N. Returns an SSA value of type ty* with explicit alignment + (e.g. va_list 24-byte buffer needs 16). align has no default; callers must pass it.""" return builder.build_alloca_aligned(ty, align) def build_alloca_at_entry(builder: rawptr[IRBuilder], ty: rawptr[LLVMType]) -> rawptr[Value]: - """%N = alloca (在函数入口块生成,确保支配性) - - 在入口块的终止指令之前插入 alloca,避免在条件分支内生成 alloca - 导致的支配性违规(Instruction does not dominate all uses)。 - """ + """%N = alloca , emitted in the function entry block to ensure dominance. + Inserts before the entry terminator; alloca inside a conditional branch violates dominance.""" return builder.build_alloca_at_entry(ty) @@ -1371,7 +1299,7 @@ def build_store(builder: rawptr[IRBuilder], val: rawptr[Value], ptr: rawptr[Valu # ============================================================ -# 二元运算指令 +# Binary operation instructions # ============================================================ def build_add(builder: rawptr[IRBuilder], lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: @@ -1452,7 +1380,7 @@ def build_ashr(builder: rawptr[IRBuilder], lhs: rawptr[Value], # ============================================================ -# 浮点二元运算指令 +# Float binary operation instructions # ============================================================ def build_fadd(builder: rawptr[IRBuilder], lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: @@ -1491,14 +1419,14 @@ def build_fneg(builder: rawptr[IRBuilder], val: rawptr[Value]) -> rawptr[Value]: def _build_binop(builder: rawptr[IRBuilder], op: str, lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: - """二元运算通用实现""" + """Shared implementation of binary operations""" return builder._build_binop(op, lhs, rhs) # ============================================================ -# 比较指令 +# Comparison instructions # ============================================================ -# ICMP 谓词常量 +# ICMP predicate constants define ICMP_EQ = 0 define ICMP_NE = 1 define ICMP_SGT = 2 @@ -1510,7 +1438,7 @@ define ICMP_UGE = 7 define ICMP_ULT = 8 define ICMP_ULE = 9 -# FCMP 谓词常量 +# FCMP predicate constants define FCMP_FALSE = 0 define FCMP_OEQ = 1 define FCMP_OGT = 2 @@ -1566,23 +1494,19 @@ def _fcmp_pred_str(pred: int) -> str: def build_icmp(builder: rawptr[IRBuilder], pred: int, lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: """%N = icmp , - - 返回类型为 i1 的 SSA 值 - """ + Returns an SSA value of type i1.""" return builder.build_icmp(pred, lhs, rhs) def build_fcmp(builder: rawptr[IRBuilder], pred: int, lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: """%N = fcmp , - - 返回类型为 i1 的 SSA 值 - """ + Returns an SSA value of type i1.""" return builder.build_fcmp(pred, lhs, rhs) # ============================================================ -# 终止指令 +# Terminator instructions # ============================================================ def build_br(builder: rawptr[IRBuilder], target: rawptr[BasicBlock]): """br label %""" @@ -1606,38 +1530,33 @@ def build_ret_void(builder: rawptr[IRBuilder]): # ============================================================ -# 调用指令 +# Call instructions # ============================================================ def build_call(builder: rawptr[IRBuilder], callee: str, args: rawptr[Value], arg_count: int, ret_ty: rawptr[LLVMType], is_variadic: int = 0) -> rawptr[Value]: """%N = call @() - - args 是 Value 链表头,arg_count 是参数数量。 - 若 ret_ty 是 void,不分配 SSA 名(使用 call 而非 %N = call)。 - is_variadic=1 时发射函数类型签名(如 i32 (i8*, ...) @printf)。 - """ + args is the Value list head, arg_count the argument count. If ret_ty is void no SSA name is + allocated (plain call); is_variadic=1 emits the function type signature (e.g. i32 (i8*, ...)).""" return builder.build_call(callee, args, arg_count, ret_ty, is_variadic) # ============================================================ -# 间接调用指令: call %callee() +# Indirect call instruction: call %callee() # -# callee_val 是函数指针 SSA 值(必须已是函数指针类型,如 i32(i8*)*) +# callee_val is a function-pointer SSA value (must already be a pointer type, e.g. i32(i8*)*) # ============================================================ def build_call_indirect(builder: rawptr[IRBuilder], callee_val: rawptr[Value], args: rawptr[Value], arg_count: int, ret_ty: rawptr[LLVMType]) -> rawptr[Value]: """%N = call %callee() - - 间接调用:callee_val 是函数指针 SSA 值。 - """ + Indirect call: callee_val is a function-pointer SSA value.""" return builder.build_call_indirect(callee_val, args, arg_count, ret_ty) # ============================================================ -# 类型转换指令 +# Type conversion instructions # ============================================================ def build_bitcast(builder: rawptr[IRBuilder], val: rawptr[Value], to_ty: rawptr[LLVMType]) -> rawptr[Value]: @@ -1676,7 +1595,7 @@ def build_inttoptr(builder: rawptr[IRBuilder], val: rawptr[Value], # ============================================================ -# 浮点转换指令 +# Float conversion instructions # ============================================================ def build_fpext(builder: rawptr[IRBuilder], val: rawptr[Value], to_ty: rawptr[LLVMType]) -> rawptr[Value]: @@ -1715,10 +1634,10 @@ def build_ui2fp(builder: rawptr[IRBuilder], val: rawptr[Value], # ============================================================ -# GEP 指令 +# GEP instructions # ============================================================ def _is_ptr_type(ty: rawptr[LLVMType]) -> int: - """检查 ty 是否是 Ptr 类型(独立函数,避免嵌套 match 的编译器 BUG)""" + """Check whether ty is a Ptr type (standalone function to avoid the nested-match compiler bug)""" if ty is None: return 0 match ty: @@ -1735,43 +1654,39 @@ def build_gep(builder: rawptr[IRBuilder], elem_ty: rawptr[LLVMType], # ============================================================ -# build_gep_array — 数组双索引 GEP: getelementptr [N x ty], [N x ty]* %ptr, i32 0, i32 %idx +# build_gep_array - two-index array GEP: getelementptr [N x ty], [N x ty]* %ptr, i32 0, i32 %idx # ============================================================ def build_gep_array(builder: rawptr[IRBuilder], array_ty: rawptr[LLVMType], elem_ty: rawptr[LLVMType], ptr: rawptr[Value], idx: rawptr[Value]) -> rawptr[Value]: """%N = getelementptr [N x elem_ty], [N x elem_ty]* %ptr, i32 0, i32 %idx - - 结果类型是 elem_ty* - """ + Result type is elem_ty*.""" return builder.build_gep_array(array_ty, elem_ty, ptr, idx) # ============================================================ -# build_gep_struct — 结构体字段 GEP: getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx +# build_gep_struct - struct-field GEP: getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx # ============================================================ def build_gep_struct(builder: rawptr[IRBuilder], struct_ty: rawptr[LLVMType], field_ty: rawptr[LLVMType], ptr: rawptr[Value], field_idx: int) -> rawptr[Value]: """%N = getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx - - 结果类型是 field_ty* - """ + Result type is field_ty*.""" return builder.build_gep_struct(struct_ty, field_ty, ptr, field_idx) # ============================================================ -# PhiIncoming: phi 节点的入边 (value, block) 对 -# 继承 GSListNode[PhiIncoming] 获取强类型 Next +# PhiIncoming: a phi incoming edge (value, block) pair +# inherit GSListNode[PhiIncoming] for a strongly-typed Next # -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 +# Note: @t.NoVTable is required (rationale in the Line comment in __function.py). # ============================================================ novirtual class PhiIncoming(GSListNode[PhiIncoming]): Val: rawptr[Value] Block: rawptr[BasicBlock] -# PhiIncoming 硬编码大小: Next(8) + Val(8) + Block(8) = 24 字节 +# PhiIncoming hardcoded size: Next(8) + Val(8) + Block(8) = 24 bytes define PHIINC_SIZE = 24 def new_phi_incoming(pool: memhub.MemBuddy*, @@ -1785,28 +1700,26 @@ def new_phi_incoming(pool: memhub.MemBuddy*, # ============================================================ -# Phi 指令 +# Phi instruction # ============================================================ def build_phi(builder: rawptr[IRBuilder], ty: rawptr[LLVMType], incoming_head: rawptr[PhiIncoming], count: int) -> rawptr[Value]: """%N = phi [val1, %blk1], [val2, %blk2], ... - - incoming_head 是 PhiIncoming 链表头,count 是入边数量。 - """ + incoming_head is the PhiIncoming list head; count is the incoming-edge count.""" return builder.build_phi(ty, incoming_head, count) # ============================================================ -# SwitchCase: switch 指令的 case 分支 +# SwitchCase: a case branch of the switch instruction # -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 +# Note: @t.NoVTable is required (rationale in the Line comment in __function.py). # ============================================================ novirtual class SwitchCase(GSListNode[SwitchCase]): CaseVal: rawptr[Value] Block: rawptr[BasicBlock] -# SwitchCase 硬编码大小: Next(8) + CaseVal(8) + Block(8) = 24 字节 +# SwitchCase hardcoded size: Next(8) + CaseVal(8) + Block(8) = 24 bytes define SWCASE_SIZE = 24 def new_switch_case(pool: memhub.MemBuddy*, @@ -1820,7 +1733,7 @@ def new_switch_case(pool: memhub.MemBuddy*, # ============================================================ -# Switch 指令 +# Switch instruction # ============================================================ def build_switch(builder: rawptr[IRBuilder], val: rawptr[Value], default_blk: rawptr[BasicBlock], @@ -1830,7 +1743,7 @@ def build_switch(builder: rawptr[IRBuilder], val: rawptr[Value], # ============================================================ -# Select 指令 +# Select instruction # ============================================================ def build_select(builder: rawptr[IRBuilder], cond: rawptr[Value], true_val: rawptr[Value], false_val: rawptr[Value]) -> rawptr[Value]: @@ -1839,7 +1752,7 @@ def build_select(builder: rawptr[IRBuilder], cond: rawptr[Value], # ============================================================ -# Unreachable 指令 +# Unreachable instruction # ============================================================ def build_unreachable(builder: rawptr[IRBuilder]): """unreachable""" @@ -1847,9 +1760,9 @@ def build_unreachable(builder: rawptr[IRBuilder]): # ============================================================ -# 原子指令 (Phase 3) +# Atomic instructions (Phase 3) # ============================================================ -# AtomicRMW 操作常量 +# AtomicRMW operation constants define ATOMIC_XCHG = 0 define ATOMIC_ADD = 1 define ATOMIC_SUB = 2 @@ -1862,7 +1775,7 @@ define ATOMIC_MIN = 8 define ATOMIC_UMAX = 9 define ATOMIC_UMIN = 10 -# 内存序常量 +# Memory ordering constants define ATOMIC_ORDER_NOTATOMIC = 0 define ATOMIC_ORDER_UNORDERED = 1 define ATOMIC_ORDER_MONOTONIC = 2 @@ -1908,14 +1821,12 @@ def build_cmpxchg(builder: rawptr[IRBuilder], ptr: rawptr[Value], cmp_val: rawptr[Value], new_val: rawptr[Value], succ_order: int, fail_order: int) -> rawptr[Value]: """%N = cmpxchg , , - - 返回 {, i1} 类型的 SSA 值(简化为返回 i64 表示的值对) - """ + Returns a {, i1} SSA value (simplified to an i64 representing the pair).""" return builder.build_cmpxchg(ptr, cmp_val, new_val, succ_order, fail_order) # ============================================================ -# va_arg 指令: 从 va_list 读取下一个变长参数 +# va_arg instruction: read the next variadic argument from va_list # ============================================================ def build_va_arg(builder: rawptr[IRBuilder], va_list_ptr: rawptr[Value], @@ -1925,39 +1836,36 @@ def build_va_arg(builder: rawptr[IRBuilder], # ============================================================ -# 内联汇编 (Phase 3) +# Inline assembly (Phase 3) # ============================================================ def build_inline_asm(builder: rawptr[IRBuilder], ret_ty: rawptr[LLVMType], asm_text: str, constraints: str, has_side_effects: int) -> rawptr[Value]: """%N = call asm [sideeffect] "" ""() - - 简化的内联汇编调用,无参数版本。 - """ + Simplified inline-assembly call, no-argument version.""" return builder.build_inline_asm(ret_ty, asm_text, constraints, has_side_effects) # ============================================================ -# 内部辅助 +# Internal helpers # ============================================================ -# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, -# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 -# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 +# _append_cstr write-position cache: O(1) positioning for repeated appends to the same buffer, +# avoiding O(n^2) strlen rescans (IR serialization hotspot); falls back to strlen on buffer switch/reset. _g_last_dst: uint64 = 0 _g_last_pos: sizet = 0 def _append_cstr(dst: str, dst_size: sizet, src: str): - """将 C 字符串 src 追加到 dst 末尾""" + """Append C string src to the end of dst.""" global _g_last_dst, _g_last_pos if dst is None or src is None: return dlen: sizet = 0 if _g_last_dst == uint64(dst): dlen = _g_last_pos - # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 + # buffer was reset (caller reused the same address after buf[0]=0) -> back to the head if dlen > 0 and dst[0] == '\0': dlen = 0 - # 缓存一致性校验(同 __module/__function 版):缓存 pos 过期会追加到首个 NUL - # 之后使内容不可见,校验失败回退 strlen;正常热路径仍 O(1)。 + # Cache consistency check (same as __module/__function): a stale pos appends past the first + # NUL and hides content; on failure fall back to strlen (hot path stays O(1)). if dlen == 0: if dst[0] != '\0': dlen = string.strlen(dst) @@ -1966,8 +1874,8 @@ def _append_cstr(dst: str, dst_size: sizet, src: str): else: dlen = string.strlen(dst) _g_last_dst = uint64(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + # Unsigned-underflow guard: if dlen >= buffer size (buffer corrupt/dangling) return early, + # else remain = dst_size - dlen underflows and dst[dlen+i] would write past the buffer. if dlen >= dst_size: return slen: sizet = string.strlen(src) diff --git a/mlirlite/__function.vp b/mlirlite/__function.vp index e66e2f6..35c8204 100644 --- a/mlirlite/__function.vp +++ b/mlirlite/__function.vp @@ -12,98 +12,94 @@ from .__types import ( ) -# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast,与 __module.vp 对齐)。 -# 函数/基本块/指令行链的 Next 被越界写破坏成环时,遍历无限循环挂起; -# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位"。 +# dump sentinel: IR list traversal step cap (cycle detection, fail-fast, aligned with __module.vp). +# A broken Next cycle would hang traversal; over the cap it prints the list name and exit(1). define IR_DUMP_CHAIN_LIMIT = 100000 -# 窥孔优化总开关(T2 转发/ret 归零、T3 no-op bitcast 消除)。 -# TPV 命令行 --no-llvmpeephole 会将其置 0:FunctionPrint 序列化时跳过全部本地窥孔, -# 便于与关闭窥孔的 IR 对比调试(RenumberSSA 仍保留,保证输出 SSA 编号合法)。 +# Peephole master switch (T2 forwarding/ret zeroing, T3 no-op bitcast removal). +# --no-llvmpeephole zeroes it so FunctionPrint skips local peepholes for IR diffing (RenumberSSA still runs). G_PeepEnable: bool = True -# 函数文本输出截断标志:_append_cstr 写入被输出缓冲上限截断时置 1, -# FunctionPrint 结束前检查并 MlirFail —— 截断的 MLIR 会让 mlir-opt 报难以定位的 -# 语法错(且此前是**静默**丢内容),必须响亮失败。 +# Function-text truncation flag: _append_cstr sets it to 1 when output hits the buffer cap. +# Checked before FunctionPrint returns and MlirFail'd: truncated MLIR must fail loudly, never silently. _MlirOutTrunc: int = 0 # ============================================================ -# Line: 指令文本行 +# Line: instruction text line # -# 每条 LLVM IR 指令序列化为一行文本,通过链表组织。 -# 继承 GSListNode[Line] 获取强类型 Next: Line|CPtr +# One LLVM IR instruction serializes to one text line, held in a linked list. +# Extends GSListNode[Line] for typed Next: Line|CPtr # -# 注意: 必须加 @t.NoVTable。否则 TransPyC 为子类添加 vtable 指针(T*), -# 但完全丢失父类 GSListNode[T] 的 Next 字段,导致 struct 布局错位, -# 所有字段访问读取垃圾值(见续 XV 会话根因分析)。 +# NOTE: must carry @t.NoVTable, else TransPyC adds a vtable ptr (T*) but drops +# the parent GSListNode[T] Next field, misaligning the struct so field reads return garbage. # ============================================================ novirtual class Line(GSListNode[Line]): - Buf: str # 行文本(NUL 结尾) - Len: sizet # 文本长度(不含 NUL) + Buf: str # line text (NUL-terminated) + Len: sizet # text length (excluding NUL) # ============================================================ -# BasicBlock: 基本块 +# BasicBlock: basic block # -# 持有名称 + 指令行链表 + 终止指令标志。 -# 继承 GSListNode[BasicBlock] 获取强类型 Next: BasicBlock|CPtr -# Lines 为 GSList[Line] 指针,提供 O(1) 追加。 +# Holds name + instruction-line list + terminator flag. +# Extends GSListNode[BasicBlock] for typed Next: BasicBlock|CPtr +# Lines is a GSList[Line] pointer providing O(1) append. # -# 注意: 必须加 @t.NoVTable(理由同 Line)。 +# NOTE: must carry @t.NoVTable (same reason as Line). # ============================================================ novirtual class BasicBlock(GSListNode[BasicBlock]): - Name: str # 块名(如 "entry"/"if.then") - Lines: rawptr[GSList[Line]] # 指令行链表容器(Head/Tail/Count) - IsTerminated: int # 1=已有终止指令(br/ret) + Name: str # block name (e.g. "entry"/"if.then") + Lines: rawptr[GSList[Line]] # instruction-line list container (Head/Tail/Count) + IsTerminated: int # 1=terminator present (br/ret) # ============================================================ -# Param: 函数参数 +# Param: function parameter # -# 继承 GSListNode[Param] 获取强类型 Next: Param|CPtr -# Attrs 存储参数属性文本(如 "noundef"/"nocapture"/"readonly"),None=无属性 +# Extends GSListNode[Param] for typed Next: Param|CPtr +# Attrs holds parameter attribute text (e.g. "noundef"/"nocapture"/"readonly"), None=none. # -# 注意: 必须加 @t.NoVTable(理由同 Line)。 +# NOTE: must carry @t.NoVTable (same reason as Line). # ============================================================ novirtual class Param(GSListNode[Param]): - Ty: rawptr[LLVMType] # 参数类型 - Name: str # 参数名(如 "%0"/"%a") - Attrs: str # 参数属性文本(None=无属性) + Ty: rawptr[LLVMType] # parameter type + Name: str # parameter name (e.g. "%0"/"%a") + Attrs: str # parameter attribute text (None=none) # ============================================================ -# Function: 函数 +# Function: function # -# 持有名称 + 返回类型 + 参数链表 + 基本块链表。 -# 继承 GSListNode[Function] 获取强类型 Next: Function|CPtr -# Params/Blocks 为 GSList 指针,提供 O(1) 追加。 -# Attrs 存储函数级属性文本(如 "nounwind"/"noinline"),None=无属性 +# Holds name + return type + parameter list + basic-block list. +# Extends GSListNode[Function] for typed Next: Function|CPtr +# Params/Blocks are GSList pointers providing O(1) append. +# Attrs holds function-level attribute text (e.g. "nounwind"/"noinline"), None=none. # -# 注意: 必须加 @t.NoVTable(理由同 Line)。 +# NOTE: must carry @t.NoVTable (same reason as Line). # ============================================================ novirtual class Function(GSListNode[Function]): - Name: str # 函数名 - RetTy: rawptr[LLVMType] # 返回类型 - Params: rawptr[GSList[Param]] # 参数链表容器 - Blocks: rawptr[GSList[BasicBlock]] # 基本块链表容器 - IsDeclared: int # 1=仅声明(declare), 0=定义(define) - IsVarArg: int # 1=变参函数(...) - Attrs: str # 函数属性文本(None=无属性) - AttrGroup: str # 字符串属性组内容(如 '"section"=".text.startup"',None=无) - AttrGroupId: int # 属性组编号(>0 时函数行输出 #N,模块尾输出 attributes #N) - NameLen: sizet # 函数名长度(创建时一次算好,恒等于 strlen(Name);追加在末尾,既有字段偏移不变) - DbgRef: str # DISubprogram ref(同形保留;MLIR 打印阶段忽略,如 "!3") + Name: str # function name + RetTy: rawptr[LLVMType] # return type + Params: rawptr[GSList[Param]] # parameter list container + Blocks: rawptr[GSList[BasicBlock]] # basic-block list container + IsDeclared: int # 1=declaration only (declare), 0=definition (define) + IsVarArg: int # 1=variadic function (...) + Attrs: str # function attribute text (None=none) + AttrGroup: str # string attribute group content (e.g. '"section"=".text.startup"', None=none) + AttrGroupId: int # attribute group id (>0 emits #N on the function line and attributes #N at module end) + NameLen: sizet # function name length (computed once at creation, always == strlen(Name); appended last so existing field offsets stay) + DbgRef: str # DISubprogram ref (kept as-is; ignored by MLIR printing, e.g. "!3") # ============================================================ -# 工厂函数 +# Factory functions # ============================================================ -# Line 硬编码大小: Next(8) + Buf(8) + Len(8) = 24 字节 +# Line hardcoded size: Next(8) + Buf(8) + Len(8) = 24 bytes define LINE_SIZE = 24 def new_line(pool: memhub.MemBuddy*, text: str) -> rawptr[Line]: - """创建一个指令行,复制 text 到新分配的缓冲区""" + """Create an instruction line, copying text into a newly allocated buffer""" ptr: rawptr[Line] = pool.alloc(LINE_SIZE) if ptr is None: return None string.memset(ptr, 0, LINE_SIZE) @@ -118,18 +114,18 @@ def new_line(pool: memhub.MemBuddy*, text: str) -> rawptr[Line]: def _new_gslist(pool: memhub.MemBuddy*, list_size: sizet) -> t.CPtr: - """从 pool 分配并零初始化一个 GSList 容器(Head/Tail/Count 全零)""" + """Allocate and zero-init a GSList container from pool (Head/Tail/Count all zero)""" gsl: t.CPtr = pool.alloc(list_size) if gsl is None: return None string.memset(gsl, 0, list_size) return gsl -# BasicBlock 硬编码大小: Next(8) + Name(8) + Lines(8) + IsTerminated(4) = 32 字节 +# BasicBlock hardcoded size: Next(8) + Name(8) + Lines(8) + IsTerminated(4) = 32 bytes define BLOCK_SIZE = 32 def new_basic_block(pool: memhub.MemBuddy*, name: str) -> rawptr[BasicBlock]: - """创建一个基本块""" + """Create a basic block""" ptr: rawptr[BasicBlock] = pool.alloc(BLOCK_SIZE) if ptr is None: return None string.memset(ptr, 0, BLOCK_SIZE) @@ -139,12 +135,12 @@ def new_basic_block(pool: memhub.MemBuddy*, name: str) -> rawptr[BasicBlock]: return ptr -# Param 硬编码大小: Next(8) + Ty(8) + Name(8) + Attrs(8) = 32 字节 +# Param hardcoded size: Next(8) + Ty(8) + Name(8) + Attrs(8) = 32 bytes define PARAM_SIZE = 32 def new_param(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], name: str) -> rawptr[Param]: - """创建一个函数参数""" + """Create a function parameter""" ptr: rawptr[Param] = pool.alloc(PARAM_SIZE) if ptr is None: return None string.memset(ptr, 0, PARAM_SIZE) @@ -154,12 +150,12 @@ def new_param(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], return ptr -# Function 硬编码大小: Next(8) + Name(8) + RetTy(8) + Params(8) + Blocks(8) + IsDeclared(4) + IsVarArg(4) + Attrs(8) + AttrGroup(8) + AttrGroupId(8) + NameLen(8) + DbgRef(8) = 88 字节(与 llvmlite 同布局) +# Function hardcoded size: Next(8) + Name(8) + RetTy(8) + Params(8) + Blocks(8) + IsDeclared(4) + IsVarArg(4) + Attrs(8) + AttrGroup(8) + AttrGroupId(8) + NameLen(8) + DbgRef(8) = 88 bytes (same layout as llvmlite) define FUNC_SIZE = 88 def new_function(pool: memhub.MemBuddy*, name: str, ret_ty: rawptr[LLVMType]) -> rawptr[Function]: - """创建一个函数""" + """Create a function""" ptr: rawptr[Function] = pool.alloc(FUNC_SIZE) if ptr is None: return None string.memset(ptr, 0, FUNC_SIZE) @@ -171,10 +167,8 @@ def new_function(pool: memhub.MemBuddy*, name: str, ptr.Attrs = None ptr.AttrGroup = None ptr.AttrGroupId = 0 - # NameLen 创建时一次算好:Name 终生不变(本文件仅此处赋值 ptr.Name), - # 故 NameLen 恒等于 strlen(Name),可替代对函数名的反复 strlen 热点。 - # name=None 时置 0,与 new_line 的 "text is not None" 判空约定一致 - # (memset 已归零,此处显式赋值以固定语义、不依赖分配器行为)。 + # NameLen computed once: Name never changes, so NameLen == strlen(Name) (avoids repeated strlen). + # name=None sets 0, matching new_line's null convention (explicit for fixed semantics, not allocator behavior). if name is not None: ptr.NameLen = string.strlen(name) else: @@ -183,21 +177,20 @@ def new_function(pool: memhub.MemBuddy*, name: str, # ============================================================ -# 链表操作(委托 GSList.append) +# List operations (delegating to GSList.append) # ============================================================ def function_add_param(func: rawptr[Function], param: rawptr[Param]): - """将参数追加到函数参数链表""" + """Append a parameter to the function's parameter list""" if func is None or param is None: return - # 注意:必须先用局部变量 + 类型参数(对齐 function_move_block_to_end 注释), - # 链式 func.Params.append() 会被编译器解析成 LinkedNode.append 且 this 前移 - # sizeof(LinkedNode)(兄弟 Mixin 兜底误匹配),写穿容器前 48 字节破坏数据结构。 + # NOTE: use a local variable + type parameter (see function_move_block_to_end): chaining + # func.Params.append() mis-resolves to LinkedNode.append and corrupts memory 48 bytes before the container. params: rawptr[GSList[Param]] = func.Params if params is None: return params.append(param) def function_add_block(func: rawptr[Function], block: rawptr[BasicBlock]): - """将基本块追加到函数块链表""" + """Append a basic block to the function's block list""" if func is None or block is None: return blocks: rawptr[GSList[BasicBlock]] = func.Blocks if blocks is None: return @@ -205,49 +198,47 @@ def function_add_block(func: rawptr[Function], block: rawptr[BasicBlock]): def function_move_block_to_end(func: rawptr[Function], block: rawptr[BasicBlock]): - """将 block 移动到函数块链表末尾(保证 BB 文本顺序与控制流顺序一致)。 + """Move block to the end of the function's block list (BB text order == control-flow order). - 用于解决 SSA 名非单调问题:BB 按创建顺序排列,但指令按控制流顺序生成, - 导致 SSA 编号在文本输出时非单调递增,llc 报错。 - 在 position_at_end 时调用此函数,使 BB 顺序与 position 顺序一致。 - - 注意:必须使用 GSList[BasicBlock] 带类型参数,否则 TransPyC 创建不完整 - struct 类型,导致 blocks.Head/Tail/Count 字段访问读取垃圾值。 + Fixes non-monotonic SSA numbering (BBs are created in order but instructions are emitted in + control-flow order) that makes llc error; called at position_at_end so BB order matches position order. + NOTE: must use the typed GSList[BasicBlock] parameter, else TransPyC builds an incomplete struct + type and blocks.Head/Tail/Count field reads return garbage. """ if func is None or block is None: return blocks: rawptr[GSList[BasicBlock]] = func.Blocks if blocks is None: return - # 已经是 Tail,无需移动 + # Already Tail; nothing to move if blocks.Tail is block: return - # 从链表中移除 block + # Remove block from the list if blocks.Head is block: - # block 是 Head,直接后移 Head + # block is Head; advance Head directly blocks.Head = block.Next else: - # 遍历找前驱 + # Walk to find the predecessor prev: rawptr[BasicBlock] = blocks.Head while prev is not None and prev.Next is not block: prev = prev.Next if prev is None: - # block 不在链表中,直接 append + # block not in list; append directly blocks.append(block) return prev.Next = block.Next - # block.Next 由移除逻辑处理,重置为 None + # block.Next handled by the removal logic; reset to None block.Next = None - # append 到末尾(不调用 blocks.append 以避免 Count 重复递增) + # Append at the end (no blocks.append call to avoid double-incrementing Count) if blocks.Tail is not None: blocks.Tail.Next = block blocks.Tail = block def block_append_line(block: rawptr[BasicBlock], line: rawptr[Line]): - """将指令行追加到基本块""" + """Append an instruction line to the basic block""" if block is None or line is None: return lines: rawptr[GSList[Line]] = block.Lines if lines is None: return @@ -255,13 +246,13 @@ def block_append_line(block: rawptr[BasicBlock], line: rawptr[Line]): def block_set_terminated(block: rawptr[BasicBlock], val: int): - """设置块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)""" + """Set the block terminator flag (cross-module call to avoid opaque chained field access)""" if block is None: return block.IsTerminated = val def block_get_terminated(block: rawptr[BasicBlock]) -> int: - """获取块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)""" + """Get the block terminator flag (cross-module call to avoid opaque chained field access)""" if block is None: return 1 return block.IsTerminated @@ -269,7 +260,7 @@ def block_get_terminated(block: rawptr[BasicBlock]) -> int: def block_append_text(pool: memhub.MemBuddy*, block: rawptr[BasicBlock], text: str): - """创建指令行并追加到基本块""" + """Create an instruction line and append it to the basic block""" if block is None or text is None: return line: rawptr[Line] = new_line(pool, text) if line is not None: @@ -279,50 +270,49 @@ def block_append_text(pool: memhub.MemBuddy*, block: rawptr[BasicBlock], def block_insert_text_before_terminator(pool: memhub.MemBuddy*, block: rawptr[BasicBlock], text: str): - """创建指令行并插入到基本块终止指令之前 + """Create an instruction line and insert it before the block terminator. - 如果块未终止,追加到末尾。 - 如果块已终止(有 br/ret),在终止指令之前插入,确保 alloca 等指令 - 不出现在终止指令之后。 + If the block is not terminated, append at the end; if terminated (br/ret), insert before + the terminator so alloca and similar instructions do not land after it. """ if block is None or text is None: return if block.Lines is None: return line: rawptr[Line] = new_line(pool, text) if line is None: return - # 块未终止:直接追加 + # Block not terminated: append directly if block.IsTerminated == 0: block_append_line(block, line) return - # 块已终止:在 Tail(终止指令)之前插入 + # Block terminated: insert before Tail (the terminator) lines: rawptr[GSList[Line]] = block.Lines if lines.Head is None: block_append_line(block, line) return - # 只有一个节点:在 Head 之前插入 + # Single node: insert before Head if lines.Head is lines.Tail: line.Next = lines.Head lines.Head = line lines.Count += 1 return - # 找到 Tail 的前驱节点 + # Find the predecessor of Tail prev: rawptr[Line] = lines.Head while prev is not None and prev.Next is not lines.Tail: prev = prev.Next if prev is None: - # 找不到前驱,回退到追加 + # No predecessor found; fall back to append block_append_line(block, line) return - # 在 prev 和 Tail 之间插入 line + # Insert line between prev and Tail line.Next = lines.Tail prev.Next = line lines.Count += 1 # ============================================================ -# 属性设置 +# Attribute setters # ============================================================ def function_set_attrs(func: rawptr[Function], attrs: str): - """设置函数级属性文本(如 "nounwind"/"noinline")""" + """Set function-level attribute text (e.g. "nounwind"/"noinline")""" if func is None: return func.Attrs = attrs @@ -330,10 +320,10 @@ def function_set_attrs(func: rawptr[Function], attrs: str): _g_attr_group_counter: int = 0 def function_set_attr_group(func: rawptr[Function], group: str): - """设置函数字符串属性组(如 '"section"=".text.startup"')并分配组编号 #N。 + """Set function string attribute group (e.g. '"section"=".text.startup"') and assign #N. - LLVM 字符串属性(带值的,如 section)不能内联在函数签名行, - 必须用属性组:函数行输出 #N,组定义输出在函数尾。 + LLVM string attributes (with a value, e.g. section) cannot be inlined on the signature + line; they must use a group: the function line emits #N and the group is emitted at the end. """ global _g_attr_group_counter if func is None or group is None: @@ -344,7 +334,7 @@ def function_set_attr_group(func: rawptr[Function], group: str): def param_set_attrs(param: rawptr[Param], attrs: str): - """设置参数属性文本(如 "noundef"/"nocapture"/"readonly")""" + """Set parameter attribute text (e.g. "noundef"/"nocapture"/"readonly")""" if param is None: return param.Attrs = attrs @@ -352,27 +342,27 @@ def param_set_attrs(param: rawptr[Param], attrs: str): def function_set_dbg(func: rawptr[Function], ref: str): """Set the function-level debug ref (DISubprogram node ref, e.g. "!3"). - 同形保留:MLIR 打印阶段忽略该引用(FunctionPrint 不输出 !dbg), - 故本调用只记录模型、不改变输出。ref=None 清除。 + Kept as-is: MLIR printing ignores it (FunctionPrint emits no !dbg), so this only + records the model without changing output. ref=None clears it. """ if func is None: return func.DbgRef = ref # ============================================================ -# _ll_name_needs_quote - 检查符号名在 MLIR 中是否需要 @"…" 引号形式 +# _ll_name_needs_quote - whether a symbol name needs @"..." quoting in MLIR # -# ⚠ 判据与 LLVM **不同**(实测 mlir-opt 22.1.8): -# - 裸标识符**首字符必须是字母或 '_'**:数字、'.'、'$' 开头都必须加引号。 -# ⇒ TPV 的 {sha1}.{name} 全部以十六进制数字开头,**一律需要引号**; -# ⇒ 字符串常量全局 @.str 以 '.' 开头,也需要引号。 -# - 后续字符仅允许 [A-Za-z0-9_$] 与 '.'; -# - '-' 在 MLIR 裸名中**非法**(LLVM 允许),实测报 "expected '('"。 -# - 含 '['/']'(泛型特化名)同样非法。 -# 本判据取保守方向:拿不准就加引号(@"…" 对任意名字都合法)。 +# WARNING: the rule differs from LLVM (measured on mlir-opt 22.1.8): +# - A bare identifier's first char must be a letter or '_': leading digit, '.', '$' must be quoted. +# => TPV's {sha1}.{name} all start with a hex digit, so all need quotes; +# => the string-constant global @.str starts with '.', so it needs quotes too. +# - Later chars allow only [A-Za-z0-9_$] and '.'. +# - '-' is illegal in a bare MLIR name (LLVM allows it); measured error "expected '('". +# - '['/']' (generic specialization names) are likewise illegal. +# This predicate takes the conservative direction: when unsure, quote (@"..." is legal for any name). # ============================================================ def _ll_name_needs_quote(name: str) -> int: - """MLIR 符号名裸写判据:不满足 [A-Za-z_][A-Za-z0-9_$.]* 即返回 1(需引号)""" + """MLIR bare-name predicate: returns 1 (needs quotes) unless it matches [A-Za-z_][A-Za-z0-9_$.]*""" if name is None: return 0 name_len: sizet = string.strlen(name) @@ -392,11 +382,11 @@ def _ll_name_needs_quote(name: str) -> int: # ============================================================ -# _MlirSymRefAppend - 输出符号引用(统一引号口径) +# _MlirSymRefAppend - emit a symbol reference (unified quoting rule) # -# name **不含** 前导 '@';已引号形式(首字符 '"')原样输出。 -# 用于 llvm.mlir.addressof 等"操作数里出现符号"的位置,与 -# _MlirFuncSig / 调用目标 / 全局名 三处共用同一判据。 +# name does NOT include a leading '@'; an already-quoted form (first char '"') is emitted as-is. +# Used where a symbol appears as an operand (e.g. llvm.mlir.addressof), sharing the same +# predicate with _MlirFuncSig / call target / global name. # ============================================================ def _MlirSymRefAppend(out: str, outsz: sizet, name: str): if name is None or name[0] == '\0': @@ -415,34 +405,34 @@ def _MlirSymRefAppend(out: str, outsz: sizet, name: str): # ============================================================ -# RenumberSSA: 重编号函数内数字 SSA 名 +# RenumberSSA: renumber numeric SSA names within a function # -# 解决 build_alloca_at_entry 在入口块终止指令前插入 alloca 导致 -# SSA 名在文本输出中非单调递增的问题(llc 报错: -# "instruction expected to be numbered '%N' or greater")。 +# Fixes the non-monotonic SSA names in text output caused by build_alloca_at_entry +# inserting allocas before the entry block terminator (llc error: +# "instruction expected to be numbered '%N' or greater"). # -# 现象: entry 块原指令 %0-%6 + br,alloca_at_entry 插入 %28/%41 到 br 之前, -# 文本输出顺序为 entry(%0..%6, %28, %41) → if.then(%7..%27), -# 导致 %28 出现在 %7 之前,llc 拒绝编译。 +# Example: entry originally %0-%6 + br; alloca_at_entry inserts %28/%41 before br, +# so text order is entry(%0..%6, %28, %41) -> if.then(%7..%27), +# placing %28 before %7, which llc rejects. # -# 算法: -# 1. 扫描所有 BB 指令行,按文本输出顺序收集数字 SSA 名 %N -# 2. 按首次出现顺序建立 old→new 映射(从 0 递增) -# 3. 替换所有指令行文本中的 %N 为新编号 +# Algorithm: +# 1. Scan all BB instruction lines, collect numeric SSA names %N in text-output order +# 2. Build an old->new map by first-occurrence order (incrementing from 0) +# 3. Replace %N in every instruction line text with the new number # -# 注意: -# - 仅重编号纯数字 SSA 名(%0, %1, ...) -# - 命名 SSA 名(%entry, %.tmp, %self)、块标签、全局符号 @N 不重编号 -# - 仅对 define(有函数体)调用,declare 无函数体无需处理 -# - 当前 TransPyV 参数多为命名(%self/%env/%n),非数字,无需重编号参数 +# NOTE: +# - Only pure numeric SSA names (%0, %1, ...) are renumbered +# - Named SSA names (%entry, %.tmp, %self), block labels and global symbols @N are not +# - Only called for define (with a body); declare has no body to process +# - TransPyV params are mostly named (%self/%env/%n), so no numeric param renumbering needed # ============================================================ -# 判定字符是否为十进制数字 +# Whether ch is a decimal digit def _is_digit_ch(ch: char) -> int: - """判断字符是否是 '0'-'9' + """Return 1 if ch is '0'-'9'. - ch 是值类型 (char = i8),TPC 已修复值类型 is None 处理: - 值类型 is None 恒为 false(生成 i1 0 常量),不会解引用空指针。 + ch is a value type (char = i8); TPC has fixed is-None handling for value types + (always false, generating an i1 0), so no null deref occurs. """ if ch is None: return 0 @@ -452,17 +442,17 @@ def _is_digit_ch(ch: char) -> int: # ============================================================ -# i32 数组的安全访问(显式字节偏移) +# Safe i32 array access (explicit byte offset) # -# TransPyC 的 ptr[i] 指针索引可能被当作字节索引(i8*)而非 i32* 索引, -# 导致内存损坏。必须显式计算字节偏移:addr = base + idx * 4,再转型为 i32*。 -# 参见 HandlesTranslator.py:358 的 t.CUInt64T(ptr) + offset * sizeof 用法。 +# TransPyC's ptr[i] pointer indexing may be treated as byte indexing (i8*) instead of i32* +# indexing, corrupting memory. Must compute the byte offset explicitly: addr = base + idx * 4, +# then cast to i32*. See HandlesTranslator.py:358's t.CUInt64T(ptr) + offset * sizeof usage. # ============================================================ def _map_get_i32(map_arr: str, idx: int) -> int: - """读取 i32 数组 map_arr[idx](显式字节偏移 idx*4) + """Read i32 array map_arr[idx] (explicit byte offset idx*4). - 使用 (rawptr[int])(uint64) 强转将字节偏移转为 i32*,再用 c.Deref 解引用。 - TPC 已修复类型联合强转(_HandleTypeUnionCastLlvm 添加 fallback),此写法可用。 + Casts the byte offset to i32* via (rawptr[int])(uint64), then dereferences with c.Deref. + TPC has fixed type-union casts (_HandleTypeUnionCastLlvm fallback), so this form works. """ if map_arr is None: return -1 @@ -472,10 +462,10 @@ def _map_get_i32(map_arr: str, idx: int) -> int: def _map_set_i32(map_arr: str, idx: int, val: int): - """写入 map_arr[idx] = val(i32 数组,显式字节偏移 idx*4) + """Write map_arr[idx] = val (i32 array, explicit byte offset idx*4). - 使用 (rawptr[int])(uint64) 强转 + c.DerefAs 解引用赋值。 - TPC 已修复类型联合强转,此写法可用。 + Casts via (rawptr[int])(uint64) and assigns through c.DerefAs. + TPC has fixed type-union casts, so this form works. """ if map_arr is None: return @@ -485,9 +475,9 @@ def _map_set_i32(map_arr: str, idx: int, val: int): def _scan_max_ssa_in_text(text: str, cur_max: int) -> int: - """扫描文本,找出 %N(N 为数字)中最大的 N,与 cur_max 比较返回更大值 + """Scan text for the largest numeric %N and return max(cur_max, found). - 仅匹配 '%' 后紧跟数字的情况,跳过命名 SSA 名(%entry 等)。 + Only matches '%' immediately followed by a digit; skips named SSA names (%entry etc.). """ result: int = cur_max p: str = text @@ -503,7 +493,7 @@ def _scan_max_ssa_in_text(text: str, cur_max: int) -> int: def _find_max_ssa_number(func: rawptr[Function]) -> int: - """遍历所有 BB 指令行,找出最大的 %N 数字;无数字 SSA 名返回 -1""" + """Walk all BB instruction lines for the largest %N; returns -1 if no numeric SSA names""" max_n: int = -1 if func is None or func.Blocks is None: return max_n @@ -535,9 +525,9 @@ def _find_max_ssa_number(func: rawptr[Function]) -> int: def _assign_new_numbers(text: str, map_arr: str, max_n: int, next_new: int) -> int: - """扫描文本,对每个 %N(数字),若 map_arr[N]==-1 则分配 next_new + """Scan text; for each numeric %N, assign next_new if map_arr[N]==-1. - 返回更新后的 next_new。按文本首次出现顺序建立映射。 + Returns the updated next_new. Mapping is built in first-occurrence order. """ result: int = next_new p: str = text @@ -558,18 +548,18 @@ def _replace_ssa_numbers(text: str, pool: memhub.MemBuddy*, map_arr: str, max_n: int) -> str: - """构建新文本,将 %N 替换为映射后的新编号 + """Build new text with each %N replaced by its mapped new number. - 逐字符扫描:遇到 %N(数字)查映射表替换,其他字符原样复制。 - 返回新分配的缓冲区(调用方无需释放旧 Buf,pool 统一管理)。 + Char-by-char scan: a numeric %N is looked up and substituted, others copied as-is. + Returns a newly allocated buffer (caller need not free the old Buf; pool manages it). """ src_len: sizet = string.strlen(text) - # 新缓冲区:最坏情况每个数字变长(位数增加),分配 src_len*2 + 64 + # New buffer: worst case each number grows in digits; allocate src_len*2 + 64 new_size: sizet = src_len * 2 + 64 new_buf: str = pool.alloc(new_size) if new_buf is None: return None - # 一次性分配数字缓冲区(循环中复用) + # Allocate the digit buffer once (reused in the loop) num_buf: str = pool.alloc(16) if num_buf is None: new_buf[0] = '\0' @@ -579,21 +569,21 @@ def _replace_ssa_numbers(text: str, src_p: str = text while src_p is not None and src_p[0] != '\0': if dst_idx + 16 >= new_size: - # 缓冲区不足,截断 + # Buffer too small; truncate break if src_p[0] == '%': nxt: str = src_p + 1 if nxt is not None and _is_digit_ch(nxt[0]) != 0: num: int = string.atoi(nxt) - # 先做范围检查再读 map_arr:num 超 max_n 时 map_arr 只有 - # (max_n+1)*4 字节,先读后查会越界读远端内存 + # Range-check before reading map_arr: when num > max_n the array holds only + # (max_n+1)*4 bytes, so a read-before-check would read out of bounds. if num >= 0 and num <= max_n: new_num: int = _map_get_i32(map_arr, num) if new_num != -1: - # 写入 '%' + # Write '%' new_buf[dst_idx] = '%' dst_idx += 1 - # 写入新编号 + # Write the new number num_buf[0] = '\0' viperlib.snprintf(num_buf, 16, "%d", new_num) k: sizet = 0 @@ -601,12 +591,12 @@ def _replace_ssa_numbers(text: str, new_buf[dst_idx] = num_buf[k] dst_idx += 1 k += 1 - # 跳过旧数字字符 + # Skip the old digit chars src_p = src_p + 1 while src_p is not None and _is_digit_ch(src_p[0]) != 0: src_p = src_p + 1 continue - # 普通字符:直接复制 + # Ordinary char: copy directly new_buf[dst_idx] = src_p[0] dst_idx += 1 src_p = src_p + 1 @@ -616,34 +606,32 @@ def _replace_ssa_numbers(text: str, def RenumberSSA(func: rawptr[Function], pool: memhub.MemBuddy*): - """重编号函数内所有数字 SSA 名,确保按文本输出顺序单调递增 + """Renumber all numeric SSA names in the function so text-output order is monotonic. - 在 FunctionPrint 序列化函数体之前调用。 - 仅对 define(IsDeclared==0)且 Blocks 非空 的函数生效。 + Called before FunctionPrint serializes the body; only applies to define (IsDeclared==0) + functions with non-empty Blocks. """ if func is None or pool is None: return if func.IsDeclared == 1: - return # declare 无函数体 + return # declare has no body if func.Blocks is None: return - # 1. 第一遍扫描:找出最大 SSA 编号 + # 1. First pass: find the largest SSA number max_n: int = _find_max_ssa_number(func) if max_n < 0: - return # 无数字 SSA 名 + return # no numeric SSA names - # 2. 分配并初始化映射数组 map_arr[0..max_n] = -1 - # 用 i8* 存储(pool.alloc 返回 i8*),通过 _map_get_i32/_map_set_i32 访问。 - # 初始化为 -1:每个 i32 元素 4 字节,0xFFFFFFFF = -1(有符号 i32)。 - # 用 memset 一次性填充,避免循环内 ptr[i] 指针索引问题。 - map_bytes: sizet = (max_n + 1) * 4 # int = 4 字节 + # 2. Allocate and init map_arr[0..max_n] = -1; stored as i8* (pool.alloc), accessed via + # _map_get_i32/_map_set_i32; memset to 0xFF (-1 per 4-byte i32) to avoid ptr[i] indexing issues. + map_bytes: sizet = (max_n + 1) * 4 # int = 4 bytes map_arr: str = pool.alloc(map_bytes) if map_arr is None: return string.memset(map_arr, 0xFF, map_bytes) # 0xFF * 4 = 0xFFFFFFFF = -1 - # 3. 第二遍扫描:按文本顺序分配新编号 + # 3. Second pass: assign new numbers in text order next_new: int = 0 blocks: rawptr[GSList[BasicBlock]] = func.Blocks blk: rawptr[BasicBlock] = blocks.Head @@ -668,7 +656,7 @@ def RenumberSSA(func: rawptr[Function], pool: memhub.MemBuddy*): line = line.Next blk = blk.Next - # 4. 检查是否有需要重编号的(old != new) + # 4. Check whether any renumbering is needed (old != new) need_renumber: int = 0 i: int = 0 while i <= max_n: @@ -678,9 +666,9 @@ def RenumberSSA(func: rawptr[Function], pool: memhub.MemBuddy*): break i += 1 if need_renumber == 0: - return # 编号已单调,无需替换 + return # already monotonic; no replacement needed - # 5. 第三遍扫描:替换所有指令行文本 + # 5. Third pass: replace every instruction line text blk = blocks.Head blk_steps = 0 while blk is not None: @@ -709,12 +697,13 @@ def RenumberSSA(func: rawptr[Function], pool: memhub.MemBuddy*): # ============================================================ -# 本地窥孔优化(文本层,输出前执行,保持语义) -# T1: ret zeroinitializer → ret 0;ret zeroinitializer → null -# T2: 入口 alloca 槽:唯一一次 store 且值无副作用(形参/%SSA/整数/浮点字面量) -# 时,把 store→load 转发为直接使用该值,删除 alloca/store/load 冗余行 -# ("虚拟寄存器优化",如 printf(i8* %msg) 前不再 alloca+store+load)。 -# 安全性:仅当 alloca 与唯一 store 都在 entry、loads 任意、无其它取址/别名用途。 +# Local peephole optimization (text layer, before output, semantics-preserving) +# T1: ret zeroinitializer -> ret 0; ret zeroinitializer -> null +# T2: when an entry alloca slot has exactly one store with a side-effect-free value +# (param/%SSA/int/float literal), forward store->load to that value and drop +# alloca/store/load ("virtual register optimization"). +# Safety: only when the alloca and its single store are both in entry, loads anywhere, +# and no other address-taking/aliasing use. # ============================================================ def _peep_is_startwith(s: str, pfx: str) -> int: if s is None or pfx is None: @@ -730,20 +719,18 @@ def _peep_is_startwith(s: str, pfx: str) -> int: def _peep_replace_token(pool: memhub.MemBuddy*, line_buf: str, tok: str, repl: str, tlen: sizet, rlen: sizet) -> str: - """将 line_buf 中独立记号 % 替换为 repl,返回新缓冲(无变化返回原指针) + """Replace the standalone token % in line_buf with repl; returns a new buffer (original if unchanged). - tlen/rlen = strlen(tok)/strlen(repl):由调用方在行循环外预先求好(同一 - ltok/safe_val 会作用于本函数处理的所有行),避免每行重复求长。 + tlen/rlen = strlen(tok)/strlen(repl): computed by the caller outside the line loop + (the same ltok/safe_val applies to all lines here) to avoid recomputing lengths per line. """ if line_buf is None or tok is None or repl is None: return line_buf if tlen == 0: return line_buf - # 预扫是否命中:不依赖 strlen,以 NUL 为界单趟扫描(绝大多数调用无命中即返回, - # 从而省去逐行 strlen)。逐输入等价:tok 无 NUL(tlen=strlen(tok)),故 - # strncmp 判等必蕴含记号完整落在串内(i+tlen 处仍非 NUL);凡 i+tlen>ln 的 - # 位置 strncmp 必先遇到 line_buf 的 NUL 而与 tok 的非 NUL 字节不等,故原 - # 上界 i+tlen<=ln 从未排除任一可能命中,去掉后命中判定不变。 + # Pre-scan for a hit: no strlen, single NUL-bounded pass (most calls return with no hit, + # avoiding per-line strlen). Per-input equivalent: tok has no NUL, so strncmp equality + # implies the token fits; dropping the old i+tlen<=ln bound does not change hit detection. hit: int = 0 i: sizet = 0 while line_buf[i] != '\0': @@ -761,9 +748,9 @@ def _peep_replace_token(pool: memhub.MemBuddy*, if hit == 0: return line_buf ln: sizet = string.strlen(line_buf) - # 缓冲大小上界:单行最多 ⌊ln/2⌋ 次替换,每次去掉 ≥2 字节(%+名)换入 rlen 字节; - # 最坏输出 ≤ ln + (ln/2)·rlen + NUL。ln + 1 + rlen·(ln+1) 恒覆盖该上界, - # 避免 K≥2 且替换串更长时把新缓冲写穿(随机内存破坏)。 + # Buffer size bound: at most floor(ln/2) replacements per line, each removing >=2 bytes + # (%+name) and inserting rlen; worst output <= ln + (ln/2)*rlen + NUL. ln + 1 + rlen*(ln+1) + # always covers it, preventing overflow (random memory corruption) when rlen is larger. out: str = pool.alloc(ln + 1 + rlen * (ln + 1)) if out is None: return line_buf @@ -793,14 +780,14 @@ def _peep_replace_token(pool: memhub.MemBuddy*, def _peep_last_ref_eq(sb: str, a_name: str, nl: sizet) -> int: - """行内最后一个 '%name' 记号是否等于 a_name(store/load 的指针操作数在行尾) + """Whether the line's last '%name' token equals a_name (store/load pointer operand is at line end). - nl = strlen(a_name):由调用方在 alloca 槽全量行扫描外预先求好(a_name 在 - 同一次槽扫描中不变),避免每行调用重复 strlen。 + nl = strlen(a_name): computed by the caller outside the alloca-slot scan (a_name is + constant within one slot scan) to avoid repeated strlen per line. """ if sb is None or a_name is None: return 0 - # 单趟定位最后一个 '%'(原实现反复 strchr 逐个跳转,每个 '%' 一次函数调用) + # Single pass to locate the last '%' (avoids repeated strchr calls, one per '%'). r: str = None probe: str = sb while probe[0] != '\0': @@ -809,7 +796,7 @@ def _peep_last_ref_eq(sb: str, a_name: str, nl: sizet) -> int: probe += 1 if r is None: return 0 - # 先比首字符(廉价拒绝),首字符相同才进入 strncmp + # Compare the first char first (cheap reject); only strncmp when it matches if r[1] == a_name[0] and string.strncmp(r + 1, a_name, nl) == 0: nxtc: int = r[1 + nl] if nxtc == 0 or nxtc == 32 or nxtc == 44 or nxtc == 41: @@ -818,7 +805,7 @@ def _peep_last_ref_eq(sb: str, a_name: str, nl: sizet) -> int: def _peep_alloc_name(eb: str, pool: memhub.MemBuddy*) -> str: - """从 '%name = alloca ...' 提取 name(不含 %),失败返回 None""" + """Extract name (without %) from '%name = alloca ...'; returns None on failure""" if eb is None or eb[0] != '%': return None eqpos: str = string.strstr(eb, " = alloca ") @@ -836,21 +823,19 @@ def _peep_alloc_name(eb: str, pool: memhub.MemBuddy*) -> str: def _peep_is_alloc_of(eb: str, a_name: str, nl: sizet) -> int: - """判断 eb 是否为 a_name 的 alloca 定义行,等价于对 _peep_alloc_name(eb) 与 - a_name 做 strcmp 是否为 0(不再分配名字缓冲)。 + """Whether eb is a_name's alloca definition line; equivalent to strcmp(_peep_alloc_name(eb), a_name)==0. - 先比名字(strncmp,名字不同立即拒绝),仅当名字逐字节一致时才做 - " = alloca " 子串定位——把原实现「每一个 '%' 行都调一次 strstr」的 - O(槽数×行数) 次 strstr 降为每槽仅命中行才调用。 - nl = strlen(a_name);nlen==0(名字为空)在原实现返回 None,故此处同样拒绝。 - 等价性:_peep_alloc_name 取第一个 " = alloca " 之前、'%' 之后的字节为名字, - 故名字等长且一致 ⟺ strncmp 相同且该子串恰起于 eb+1+nl。 + Compares the name first (strncmp, reject on mismatch), then locates " = alloca " only when + bytes match, reducing strstr count from O(slots*lines) to only hit lines per slot. + nl = strlen(a_name); nlen==0 (empty name) returned None before, so it is rejected here too. + Equivalence: _peep_alloc_name takes the bytes before the first " = alloca " and after '%', + so equal-length equal name <=> strncmp match and the substring starts at eb+1+nl. """ if eb is None or a_name is None or nl == 0 or eb[0] != '%': return 0 - # 首字符预筛:nl>=1,strncmp 判等必然蕴含首字符相等;首字符不同即可廉价拒绝, - # 避免绝大多数行进入 strncmp 调用。逐输入等价:本分支拒绝当且仅当原 strncmp - # 也必返回非 0(首字节不同)。eb[0]=='%' 非 NUL ⇒ eb[1] 读在串内(最多为 NUL)。 + # First-char prefilter: nl>=1, so strncmp equality implies equal first chars; a mismatch + # cheaply rejects, keeping most lines out of strncmp. Per-input equivalent: this branch + # rejects iff the old strncmp would too. eb[0]=='%' is non-NUL, so eb[1] is in-bounds. if eb[1] != a_name[0]: return 0 if string.strncmp(eb + 1, a_name, nl) != 0: @@ -862,12 +847,12 @@ def _peep_is_alloc_of(eb: str, a_name: str, nl: sizet) -> int: def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): - """文本层本地窥孔:T1 ret zeroinitializer;T2 入口槽 store→load 转发""" + """Text-layer local peephole: T1 ret zeroinitializer; T2 entry-slot store->load forwarding""" if func is None or func.Blocks is None or pool is None: return - # 注:TPC 对 "变量 = 指针.字段.成员" 两级自动解引用链会静默丢弃赋值 - # (编译出的 IR 直接读未初始化栈变量 → 随机栈错误)。必须拆成单级: - # 先取字段到局部 rawptr,再判空后取 .Head(与 FunctionPrint 同一模式)。 + # NOTE: TPC silently drops assignments for a two-level "var = ptr.field.member" auto-deref + # chain (the IR reads an uninitialized stack var -> random stack errors). Split into one level: + # fetch the field into a local rawptr, then null-check before .Head (same pattern as FunctionPrint). func_blocks: rawptr[GSList[BasicBlock]] = func.Blocks blk: rawptr[BasicBlock] = None if func_blocks is not None: @@ -907,7 +892,7 @@ def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): line1 = line1.Next blk_t1 = blk_t1.Next - # ---------- T2: entry alloca 槽转发 ---------- + # ---------- T2: entry alloca slot forwarding ---------- entry_blk: rawptr[BasicBlock] = blk if entry_blk is None: return @@ -920,7 +905,7 @@ def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): if eb is not None: a_name: str = _peep_alloc_name(eb, pool) if a_name is not None: - # a_name 在本槽的全量行扫描中不变,strlen 只求一次(原每行各求一次) + # a_name is constant across the slot's full line scan; strlen computed once (not per line) a_name_len: sizet = string.strlen(a_name) st_val: str = None st_ok: int = 0 @@ -932,7 +917,7 @@ def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): alloca_order: int = -1 order: int = 0 unsafe: int = 0 - # 第一遍:统计本 alloca 的 store/load/其它引用及行序 + # First pass: count this alloca's store/load/other refs and line order blk_s = blk while blk_s is not None: l_s: rawptr[Line] = None @@ -979,18 +964,17 @@ def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): if blk_s == entry_blk and st_line_order < 0: load_entry_order += 1 else: - # lname 分配失败:该 load 不会进 load_ltoks,但删除阶段 - # 仍会按槽名删其 def 行,use 无法改写为 store 值 → 悬空。 - # 保守放弃本 alloca 槽的 T2 转发。 + # lname alloc failed: this load won't enter load_ltoks, but the + # delete phase still removes its def line by slot name, leaving a + # dangling use. Conservatively abandon T2 forwarding for this slot. unsafe = 1 else: - # 本 alloca 的 load 超过记录容量(load_ltoks=8*64,上限 64) - # 或行内无 '%' 无法解析 def 名:超出容量的 load 不会被记录, - # 但删除阶段按槽名删除全部 " = load " def 行 → 未记录 load 的 - # use 未改写为 store 值,将悬空引用已删 def(llc: - # "use of undefined value",见 strcmp 短路链 >64 分支场景)。 - # 只有 load 全集都被记录时才能安全删全量 def,故保守放弃 - # 本 alloca 槽的整个 T2 转发(保留 alloca/store/load,IR 仍合法)。 + # Loads exceed record capacity (load_ltoks=8*64, cap 64) or the line has + # no '%' to parse a def name: unrecorded loads would still have their + # " = load " def lines deleted by slot name, leaving dangling uses + # (llc: "use of undefined value", e.g. strcmp short-circuit >64 branch). + # Only a fully recorded load set allows deleting all defs, so + # conservatively abandon the whole T2 forwarding for this slot (IR stays valid). unsafe = 1 else: unsafe = 1 @@ -998,7 +982,7 @@ def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): order += 1 l_s = l_s.Next blk_s = blk_s.Next - # 判据 + # Criteria safe_val: str = None if unsafe == 0 and st_count == 1 and st_ok != 0 and st_line_order >= 0 \ and load_cnt > 0 and load_entry_order == 0 and st_val is not None: @@ -1013,14 +997,14 @@ def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): if string.strchr(pct, ' ') is None and string.strchr(pct, ',') is None: safe_val = pct if safe_val is not None: - # 第二遍:替换 load 名引用 - # safe_val 在本槽替换阶段不变,strlen 只求一次(原每行各求一次) + # Second pass: replace load-name references + # safe_val is constant during this slot's replacement; strlen computed once per slot safe_len: sizet = string.strlen(safe_val) for li2 in range(load_cnt): slotsp: str* = (str*)(uint64(load_ltoks) + li2 * 8) ltok: str = slotsp[0] if ltok is not None: - # ltok 在该 load 名的全函数行扫描中不变,strlen 只求一次 + # ltok is constant across the load name's function-wide scan; strlen computed once ltok_len: sizet = string.strlen(ltok) blk_r = blk while blk_r is not None: @@ -1036,7 +1020,7 @@ def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): lr.Buf = nb2 lr = lr.Next blk_r = blk_r.Next - # 删除:alloca def / 唯一 store / 全部 load 行 + # Delete: alloca def / the single store / all load lines blk_d = blk while blk_d is not None: ld: rawptr[Line] = None @@ -1060,24 +1044,24 @@ def _peephole_opt(func: rawptr[Function], pool: memhub.MemBuddy*): blk_d = blk_d.Next e_line = e_line.Next - # ---------- T3: no-op bitcast 消除 ---------- + # ---------- T3: no-op bitcast removal ---------- _peep_noop_bitcast(func, pool) def _peep_noop_bitcast(func: rawptr[Function], pool: memhub.MemBuddy*): - """T3: 消除 no-op bitcast(bitcast %x to ,两侧类型相同)。 + """T3: remove no-op bitcast (bitcast %x to , same type on both sides). - 生成端(虚调用/联合位宽转换)会产生同类型恒等 bitcast(如 - `%6 = bitcast i8* %ok_msg to i8*`),纯冗余指令。把 %x 的所有引用 - 替换为源操作数(%SSA/@global/常量 token)后删除该行。 - 保守限制:类型与操作数均为不含空格的简单 token。 + The emitter (virtual calls / union width casts) produces same-type identity bitcasts + (e.g. `%6 = bitcast i8* %ok_msg to i8*`), purely redundant. Replace all uses of %x with + the source operand (%SSA/@global/constant token), then delete the line. + Conservative limit: both type and operand must be simple whitespace-free tokens. """ if func is None or pool is None or func.Blocks is None: return - # 单趟前向扫描:命中合法候选 → 全函数替换引用 + 删行,游标从下一行续扫; - # 遇到首个不满足条件的 bitcast 即停止。与原实现"每删一行后从块头整函数 - # 重扫、遇无效候选即 return"的删除结果逐一等价(替换只改操作数 token, - # 不会把游标之前的非候选行变成候选),只是消除了 O(K·C) 的重扫二次方。 + # Single forward pass: on a valid candidate, replace refs function-wide + delete the line, + # resuming from the next line; stop at the first non-matching bitcast. Equivalent deletion + # result to the old rescan-from-block-head approach (replacement only touches operand tokens), + # but removes the O(K*C) quadratic rescan. blk: rawptr[BasicBlock] = func.Blocks.Head while blk is not None: ln: rawptr[Line] = None @@ -1092,7 +1076,7 @@ def _peep_noop_bitcast(func: rawptr[Function], pool: memhub.MemBuddy*): nm_buf: str = None src_buf: str = None if nlen > 0: - after: str = eqpos + 11 # 跳过 " = bitcast " + after: str = eqpos + 11 # skip " = bitcast " sp1: str = string.strchr(after, ' ') tpos: str = string.strstr(after, " to ") if sp1 is not None and tpos is not None \ @@ -1115,10 +1099,10 @@ def _peep_noop_bitcast(func: rawptr[Function], pool: memhub.MemBuddy*): string.strncpy(src_buf, src_st, src_len) src_buf[src_len] = '\0' if nm_buf is None or src_buf is None: - # 首个不满足条件的 bitcast:停止(与原语义一致) + # First non-matching bitcast: stop (same semantics as before) return - # 全函数把 %nm 的引用替换为源操作数 - # nm_buf/src_buf 在全函数行扫描中不变,strlen 只求一次(原每行各求一次) + # Replace %nm references with the source operand function-wide + # nm_buf/src_buf are constant across the scan; strlen computed once (not per line) nm_buf_len: sizet = string.strlen(nm_buf) src_buf_len: sizet = string.strlen(src_buf) blk_r: rawptr[BasicBlock] = func.Blocks.Head @@ -1140,51 +1124,51 @@ def _peep_noop_bitcast(func: rawptr[Function], pool: memhub.MemBuddy*): # ============================================================ -# MLIR(llvm 方言) 渲染阶段 +# MLIR (llvm dialect) render stage # -# 位置:FunctionPrint 内部。builders 一行不改,仍按 LLVM 语法把指令文本写进 -# Line;本阶段把中间文本重新解析,把 MLIR 特有的三类差异集中在此解决: -# 1) 常量物化 —— MLIR 操作数不能是字面量:非 SSA 操作数(整数/浮点/ -# null/zeroinitializer/@符号)在 entry 块开头生成 -# llvm.mlir.constant / llvm.mlir.zero / llvm.mlir.addressof -# op 并分配 %cN 名(同函数内同 (类型,文本) 只物化一次) -# 2) phi→块参数 —— 块首连续 phi 变成块参数 ^blk(%0: i32),phi 行不再输出; -# 跳转到该块的 br/cond_br 按源块匹配入边追加实参 -# 3) 不透明指针 —— 所有指针统一 !llvm.ptr(TypePrint/MlirTypeText 已收敛) +# Located inside FunctionPrint. Builders stay unchanged and keep writing LLVM-syntax +# instruction text into Line; this stage re-parses that text and handles the three +# MLIR-specific differences: +# 1) constant materialization -- MLIR operands cannot be literals: non-SSA operands +# (int/float/null/zeroinitializer/@symbol) become llvm.mlir.constant / llvm.mlir.zero / +# llvm.mlir.addressof ops at the entry block head, assigned %cN (once per (type,text) per function) +# 2) phi -> block parameters -- leading phis become block params ^blk(%0: i32); phi lines are +# not emitted; br/cond_br into that block get arguments matched by source block +# 3) opaque pointers -- all pointers unify to !llvm.ptr (handled by TypePrint/MlirTypeText) # -# 两遍扫描:pass1 只登记(常量表 / bitcast no-op 别名表)+ 形态校验; -# pass2 才真正写文本(此时常量表已完整,entry 开头的常量 op 可先输出)。 +# Two passes: pass1 only registers (const table / bitcast no-op alias table) + shape checks; +# pass2 writes text (const table is complete, so entry-head const ops can be emitted first). # ============================================================ -# 表格容量 +# Table capacity define MLIR_CONST_CAP = 512 define MLIR_ALIAS_CAP = 512 -# 渲染阶段:1=扫描(登记) 2=发射(写文本) +# Render pass: 1=scan(register) 2=emit(write text) _MLIR_Pass: int = 0 _MLIR_Pool: memhub.MemBuddy* = None _MLIR_Func: rawptr[Function] = None _MLIR_SrcBlk: rawptr[BasicBlock] = None -# 常量表:(MLIR 类型文本, 常量文本) -> 物化出的 SSA 名 +# Constant table: (MLIR type text, constant text) -> materialized SSA name _MLIR_ConstTy: t.CPtr = None _MLIR_ConstVal: t.CPtr = None _MLIR_ConstName: t.CPtr = None _MLIR_ConstCount: int = 0 -# bitcast no-op 别名表 + cmpxchg 结果绑定表:结果名 -> 源操作数 / extractvalue 结果名 -# (cmpxchg 的 extractvalue 结果名由结果名确定性派生,不再用计数器,见 _MlirCxResName) +# bitcast no-op alias table + cmpxchg result binding table: result name -> source operand / extractvalue result name +# (cmpxchg's extractvalue result name is deterministically derived from the result name, not a counter; see _MlirCxResName) _MLIR_AliasFrom: t.CPtr = None _MLIR_AliasTo: t.CPtr = None _MLIR_AliasCount: int = 0 -# 当前正在渲染的中间指令文本行(_MlirRenderLine 入口赋值)。 -# 供类型文本转换失败的报文追加行上下文:MlirFail 是即时终止、拿不到调用栈, -# 故把行文本寄存到 __types.vp(经 MlirSetContextLine),由报错处一并打印。 +# The intermediate instruction text line currently being rendered (set at _MlirRenderLine entry). +# Supplies line context to type-text conversion failures: MlirFail terminates immediately with no +# stack, so the line text is stashed in __types.vp (via MlirSetContextLine) and printed with the error. _MLIR_CurLine: str = None -# 解析期 scratch 缓冲(每次 FunctionPrint 分配一次,全程复用; -# 所有需要跨行存活的文本在入表/入别名表时都会 dup,见 _mlir_dup) +# Parse-time scratch buffers (allocated once per FunctionPrint, reused throughout; +# text that must outlive a line is dup'd on table/alias insertion, see _mlir_dup) _SC_Op: str = None _SC_Tb: str = None _SC_Ty: str = None @@ -1208,12 +1192,12 @@ _SC_Buf: str = None def _mlir_slotp(buf: t.CPtr, idx: int) -> str*: - """表格槽位地址(buf + idx*8)""" + """Table slot address (buf + idx*8)""" return (str*)(uint64(buf) + idx * 8) def _mlir_dup(s: str) -> str: - """把文本复制到池(用于需要跨行存活的表项)""" + """Copy text into the pool (for table entries that must outlive a line)""" if s is None: return None n: sizet = string.strlen(s) @@ -1226,7 +1210,7 @@ def _mlir_dup(s: str) -> str: def _MlirTablesInit(pool: memhub.MemBuddy*): - """初始化两个表和解析用 scratch 缓冲""" + """Initialize the two tables and the parse scratch buffers""" global _MLIR_Pool, _MLIR_Pass, _MLIR_ConstTy, _MLIR_ConstVal, _MLIR_ConstName, _MLIR_ConstCount, _MLIR_AliasFrom, _MLIR_AliasTo, _MLIR_AliasCount, _SC_Op, _SC_Tb, _SC_Ty, _SC_Ty2, _SC_V1, _SC_V2, _SC_V3, _SC_Callee, _SC_Args, _SC_ArgTs, _SC_IdxV, _SC_IdxT, _SC_Mt, _SC_Tmp, _SC_Tmp2, _SC_P1, _SC_P2, _SC_B1, _SC_B2, _SC_Buf if pool is None: MlirFail("MLIR 渲染: 内存池为空") @@ -1276,10 +1260,10 @@ def _MlirTablesInit(pool: memhub.MemBuddy*): # ============================================================ -# 常量表 / 别名表 +# Constant table / alias table # ============================================================ def _MlirAliasLookup(name: str) -> str: - """查别名表,未命中返回 None""" + """Look up the alias table; returns None on miss""" if name is None: return None for i in range(_MLIR_AliasCount): @@ -1291,7 +1275,7 @@ def _MlirAliasLookup(name: str) -> str: def _MlirAliasAdd(from_name: str, to_name: str): - """登记 no-op bitcast 别名(from 由 to 取代)""" + """Register a no-op bitcast alias (from is replaced by to)""" global _MLIR_AliasCount if from_name is None or to_name is None: return @@ -1305,7 +1289,7 @@ def _MlirAliasAdd(from_name: str, to_name: str): def _MlirResolve(name: str) -> str: - """别名链解析(bitcast no-op 的结果名 -> 真实操作数)""" + """Resolve the alias chain (bitcast no-op result name -> real operand)""" if name is None: return None cur: str = name @@ -1318,7 +1302,7 @@ def _MlirResolve(name: str) -> str: def _MlirConstGet(tytext: str, valtext: str) -> str: - """取(或新建)常量的物化名 %cN""" + """Get (or create) the constant's materialized name %cN""" global _MLIR_ConstCount for i in range(_MLIR_ConstCount): sp: str* = _mlir_slotp(_MLIR_ConstTy, i) @@ -1328,7 +1312,7 @@ def _MlirConstGet(tytext: str, valtext: str) -> str: sn: str* = _mlir_slotp(_MLIR_ConstName, i) return sn[0] if _MLIR_Pass == 2: - # pass1 已遍历全部指令行,此处不应出现新常量 + # pass1 already scanned all instruction lines; no new constant should appear here MlirFail("MLIR 渲染: 发射阶段出现未物化常量(扫描遗漏)") if _MLIR_ConstCount >= MLIR_CONST_CAP: MlirFail("MLIR 渲染: 常量表溢出") @@ -1347,14 +1331,14 @@ def _MlirConstGet(tytext: str, valtext: str) -> str: def _MlirCxResName(resname: str, suffix: str) -> str: - """cmpxchg 的 extractvalue 结果名:由 struct 结果名**确定性派生** + """cmpxchg's extractvalue result name: deterministically derived from the struct result name. - MLIR 裸 SSA 名首字符必须是字母或 `_`(mlir-opt 22.1.8 实测:`%0_v` 报 - "expected '=' after SSA name"),故统一在结果名正文前加 `_`: - `%0` → `%_0_v`(旧值 [0])/ `%_0_ok`(成功位 [1]) - `%a` → `%_a_v` / `%_a_ok` - 名字与结果名一一对应、不含计数器 ⇒ pass1(登记别名)与 pass2(发射) - 生成的名字必然逐字相同,引用不会悬空。 + A bare MLIR SSA name's first char must be a letter or `_` (mlir-opt 22.1.8 measured: `%0_v` + errors "expected '=' after SSA name"), so a `_` is prefixed to the result name body: + `%0` -> `%_0_v` (old value [0]) / `%_0_ok` (success bit [1]) + `%a` -> `%_a_v` / `%_a_ok` + Names map one-to-one to result names with no counter, so pass1 (alias registration) and + pass2 (emission) produce identical names and references never dangle. """ nm: str = _MLIR_Pool.alloc(64) if nm is None: @@ -1367,18 +1351,17 @@ def _MlirCxResName(resname: str, suffix: str) -> str: def _MlirFloatLiteral(val: str, mt: str) -> str: - """浮点常量文本 → MLIR 浮点字面量文本 + """Float constant text -> MLIR float literal text. - MLIR 的浮点字面量支持两种形态(Builtin Dialect「FloatAttr」语法: - `float-literal (':' float-type)?` | `hexadecimal-literal ':' float-type`): - - 十进制浮点字面量(如 `3.14 : f64`); - - **十六进制位模式**(如 `0x40091EB851EB851F : f64`,按底层二进制位解释)。 + MLIR float literals have two forms (Builtin Dialect FloatAttr syntax: + `float-literal (':' float-type)?` | `hexadecimal-literal ':' float-type`): + - decimal float literal (e.g. `3.14 : f64`); + - hexadecimal bit pattern (e.g. `0x40091EB851EB851F : f64`, interpreted bitwise). - 故 ConstFloat 给出的 `0x…` 位模式**原样直通**:位模式无精度损失。 - 此前把它转成 `%e`(默认 7 位有效数字)会截断 - (3.141592653589793 → 3.141593e+00),与 __values.vp 选位模式的初衷相悖。 - 位模式的位数必须与类型相符(f16/f32/f64/f128 ↔ 4/8/16/32 位十六进制), - 否则响亮报错 —— MLIR 按类型宽度解释该十六进制值。 + So the `0x...` bit pattern from ConstFloat passes through as-is (no precision loss); + converting it to `%e` (7 significant digits) would truncate and defeat the bit-pattern intent. + The pattern width must match the type (f16/f32/f64/f128 <-> 4/8/16/32 hex digits), else a loud + error, since MLIR interprets the hex value by the type width. """ if val[0] == '0' and (val[1] == 'x' or val[1] == 'X'): want: int = 0 @@ -1402,17 +1385,17 @@ def _MlirFloatLiteral(val: str, mt: str) -> str: if nd != want: MlirFail("MLIR 渲染: 浮点位模式位数与类型不符(f16/f32/f64/f128 依次需 4/8/16/32 位十六进制)") return val - # 已是十进制文本:原样使用 + # Already decimal text: use as-is return val def _MlirEmitOneConst(out: str, outsz: sizet, mt: str, val: str, nm: str): - """发射一条常量物化 op""" + """Emit one constant-materialization op""" _append_cstr(out, outsz, " ") _append_cstr(out, outsz, nm) if val[0] == '@': - # 符号引用(全局/函数地址):走统一引号口径 - # (LLVM 与 MLIR 裸名判据不同:MLIR 不允许数字/'.'开头,也不允许 '-') + # Symbol reference (global/function address): use the unified quoting rule + # (LLVM and MLIR bare-name rules differ: MLIR disallows leading digit/'.' and '-') _append_cstr(out, outsz, " = llvm.mlir.addressof ") _MlirSymRefAppend(out, outsz, val + 1) _append_cstr(out, outsz, " : ") @@ -1428,9 +1411,9 @@ def _MlirEmitOneConst(out: str, outsz: sizet, mt: str, val: str, nm: str): MlirFail("MLIR 渲染: 不支持的常量操作数形态") _append_cstr(out, outsz, " = llvm.mlir.constant(") _append_cstr(out, outsz, lit) - # i1 的 `true`/`false` 是 MLIR 的**裸布尔关键字**,不能再带类型后缀: - # `llvm.mlir.constant(false : i1)` 被 mlir-opt 报 "expected ')'"。 - # 其余标量必须写 `值 : 类型`(不写会被当成无类型 attr,与结果类型不符)。 + # i1's `true`/`false` are MLIR bare boolean keywords, no type suffix allowed: + # `llvm.mlir.constant(false : i1)` makes mlir-opt error "expected ')'". + # Other scalars must be written `value : type` (otherwise treated as an untyped attr, mismatching the result type). if string.strcmp(lit, "true") != 0 and string.strcmp(lit, "false") != 0: _append_cstr(out, outsz, " : ") _append_cstr(out, outsz, mt) @@ -1440,7 +1423,7 @@ def _MlirEmitOneConst(out: str, outsz: sizet, mt: str, val: str, nm: str): def _MlirEmitConsts(out: str, outsz: sizet): - """按登记顺序在 entry 块开头发射全部常量物化 op""" + """Emit all constant-materialization ops at the entry block head in registration order""" for i in range(_MLIR_ConstCount): st: str* = _mlir_slotp(_MLIR_ConstTy, i) sv: str* = _mlir_slotp(_MLIR_ConstVal, i) @@ -1449,10 +1432,10 @@ def _MlirEmitConsts(out: str, outsz: sizet): # ============================================================ -# 中间指令文本的解析原语 +# Parsing primitives for intermediate instruction text # ============================================================ def _mlir_ws(p: str) -> str: - """跳过空白(空格/制表/换行)""" + """Skip whitespace (space/tab/newline)""" if p is None: return None while p[0] == ' ' or p[0] == '\t' or p[0] == '\n' or p[0] == '\r': @@ -1461,7 +1444,7 @@ def _mlir_ws(p: str) -> str: def _mlir_delim(ch: int) -> int: - """判断字符是否结束一个记号""" + """Whether ch ends a token""" if ch == 0 or ch == 32 or ch == 9 or ch == 10 or ch == 13: return 1 if ch == ',' or ch == '(' or ch == ')' or ch == '[' or ch == ']' \ @@ -1471,7 +1454,7 @@ def _mlir_delim(ch: int) -> int: def _mlir_read_value(p: str, out: str, outsz: sizet) -> str: - """读一个值记号(%name / @"name" / %"name" / 字面量),返回新的游标""" + """Read a value token (%name / @"name" / %"name" / literal); returns the new cursor""" p = _mlir_ws(p) n: sizet = 0 if p[0] == '\0': @@ -1509,14 +1492,15 @@ def _mlir_read_value(p: str, out: str, outsz: sizet) -> str: def _mlir_read_type(p: str, out: str, outsz: sizet, nofunc: int = 0) -> str: - """读一个类型记号(MLIR 形态 !llvm.… 或标量 iN/fN,兼容 LLVM 聚合形态) + """Read a type token (MLIR form !llvm... or scalar iN/fN, also LLVM aggregate forms). - 括号平衡覆盖 `[] {} () <>`:MLIR 的 !llvm.array<…>/!llvm.struct<(…)> 里含 - 空格与逗号,必须靠深度计数整体读出;无括号类型(!llvm.ptr)遇到顶层分隔符结束。 + Bracket balancing covers `[] {} () <>`: MLIR's !llvm.array<...>/!llvm.struct<(...)> contain + spaces and commas and must be read whole by depth counting; bracketless types (!llvm.ptr) + end at a top-level delimiter. - nofunc=1 时**不吞并**紧随其后的 ` (…)` 函数类型后缀:call 的返回类型位置 - 上,`(…)`(LLVM 词法)是**被调函数的参数类型表**而不是返回类型的一部分, - 必须留给 _MlirCall 的变参分支处理(见该处注释)。 + With nofunc=1, does NOT absorb the following ` (...)` function-type suffix: in a call's return + type position, `(...)` (LLVM syntax) is the callee's parameter type list, not part of the return + type, so it must be left to _MlirCall's variadic branch (see the comment there). """ p = _mlir_ws(p) n: sizet = 0 @@ -1541,7 +1525,7 @@ def _mlir_read_type(p: str, out: str, outsz: sizet, nofunc: int = 0) -> str: continue elif d == 0 and (ch == ' ' or ch == ',' or ch == '\t' or ch == '\n' or ch == '\r'): - # 顶层分隔符:无括号类型(如 !llvm.ptr)到此结束 + # Top-level delimiter: bracketless types (e.g. !llvm.ptr) end here break if n + 1 < outsz: out[n] = ch @@ -1564,13 +1548,13 @@ def _mlir_read_type(p: str, out: str, outsz: sizet, nofunc: int = 0) -> str: p = p + 1 out[n] = '\0' return p - # 普通词(i32 / i8* / void / double …) + # Ordinary word (i32 / i8* / void / double ...) while p[0] != '\0' and _mlir_delim(p[0]) == 0: if n + 1 < outsz: out[n] = p[0] n += 1 p = p + 1 - # 函数类型后缀 " ( … )"(nofunc=1 时不吞并,见函数 docstring) + # Function-type suffix " ( ... )" (not absorbed when nofunc=1, see the docstring) q: str = _mlir_ws(p) if nofunc == 0 and q[0] == '(': if n + 1 < outsz: @@ -1605,7 +1589,7 @@ def _mlir_read_type(p: str, out: str, outsz: sizet, nofunc: int = 0) -> str: def _mlir_in_set(ob: str, setstr: str) -> int: - """判断 opcode 是否属于 "|a|b|c|" 形式的集合""" + """Whether opcode belongs to a "|a|b|c|"-form set""" tb: str = _SC_Tb tb[0] = '\0' string.strcat(tb, "|") @@ -1617,7 +1601,7 @@ def _mlir_in_set(ob: str, setstr: str) -> int: def _MlirBlkRef(t: str) -> str: - """块引用文本:去掉前导 '%'(MLIR 块用 ^name)""" + """Block reference text: strip the leading '%' (MLIR blocks use ^name)""" if t is None: return "" if t[0] == '%': @@ -1626,7 +1610,7 @@ def _MlirBlkRef(t: str) -> str: def _MlirFindBlock(func: rawptr[Function], name: str) -> rawptr[BasicBlock]: - """按块名(不含 %)在函数内查找基本块""" + """Find a basic block by name (without %) within the function""" if func is None or func.Blocks is None or name is None: return None blocks: rawptr[GSList[BasicBlock]] = func.Blocks @@ -1643,7 +1627,7 @@ def _MlirFindBlock(func: rawptr[Function], name: str) -> rawptr[BasicBlock]: def _MlirIsPhiLine(text: str) -> int: - """判断是否为 phi 行""" + """Whether the line is a phi line""" if text is None: return 0 if string.strstr(text, " = phi ") is not None: @@ -1652,13 +1636,13 @@ def _MlirIsPhiLine(text: str) -> int: # ============================================================ -# 操作数渲染(含常量物化) +# Operand rendering (with constant materialization) # ============================================================ def _MlirOperand(val: str, tyllvm: str, out: str, outsz: sizet) -> str: - """渲染一个操作数,返回其最终文本 + """Render one operand and return its final text. - SSA 值 → 别名解析后的 %name; - 非 SSA(字面量/符号引用)→ 物化后的 %cN(scan 阶段顺带登记)。 + SSA value -> %name after alias resolution; + non-SSA (literal/symbol ref) -> materialized %cN (registered during the scan pass). """ if val is None or val[0] == '\0': MlirFail("MLIR 渲染: 操作数为空") @@ -1675,10 +1659,10 @@ def _MlirOperand(val: str, tyllvm: str, out: str, outsz: sizet) -> str: # ============================================================ -# phi 解析 +# phi parsing # ============================================================ def _MlirPhiScan(text: str): - """扫描 phi 行:登记所有入边常量(scan 阶段专用)""" + """Scan a phi line: register all incoming constants (scan pass only)""" p: str = _mlir_ws(text) tb: str = _SC_Buf p = _mlir_read_value(p, tb, 512) @@ -1713,12 +1697,12 @@ def _MlirPhiScan(text: str): p = _mlir_ws(p) if p[0] == ',': p = p + 1 - # 登记该入边值(常量/符号引用需要物化) + # Register this incoming value (constants/symbol refs need materialization) _MlirOperand(vb, tyb, None, 0) def _MlirPhiParam(text: str) -> int: - """取 phi 的结果名与类型作为块参数;成功写 _SC_B1(名)/_SC_B2(类型) 返回 1""" + """Take a phi's result name and type as block params; on success writes _SC_B1(name)/_SC_B2(type) and returns 1""" p: str = _mlir_ws(text) nm: str = _SC_B1 p = _mlir_read_value(p, nm, 512) @@ -1736,7 +1720,7 @@ def _MlirPhiParam(text: str) -> int: def _MlirPhiFindIncoming(text: str, src: rawptr[BasicBlock]) -> int: - """找与源块匹配的 phi 入边;成功写 _SC_B1(值)/_SC_B2(类型) 返回 1""" + """Find the phi incoming edge matching the source block; on success writes _SC_B1(value)/_SC_B2(type) and returns 1""" p: str = _mlir_ws(text) tb: str = _SC_Buf p = _mlir_read_value(p, tb, 512) @@ -1786,10 +1770,10 @@ def _MlirPhiFindIncoming(text: str, src: rawptr[BasicBlock]) -> int: # ============================================================ -# 块参数 / 块实参 +# Block parameters / block arguments # ============================================================ def _MlirBlockHasPhi(blk: rawptr[BasicBlock]) -> int: - """判断块首是否有 phi(即该块是否带块参数)""" + """Whether the block head has a phi (i.e. the block carries block params)""" if blk is None or blk.Lines is None: return 0 lines: rawptr[GSList[Line]] = blk.Lines @@ -1805,9 +1789,9 @@ def _MlirBlockHasPhi(blk: rawptr[BasicBlock]) -> int: def _MlirBlockOperands(out: str, outsz: sizet, target: rawptr[BasicBlock], src: rawptr[BasicBlock]): - """跳转到 target 时按其块参数生成实参: (%v : ty, …) + """Emit target's block arguments when branching to it: (%v : ty, ...). - 入边按源块匹配 phi;匹配不到 → 响亮报错(绝不静默丢实参)。 + Incoming edges are matched to phis by source block; no match -> loud error (never silently drop args). """ if target is None or src is None: return @@ -1844,7 +1828,7 @@ def _MlirBlockOperands(out: str, outsz: sizet, def _MlirEmitBlockHeader(out: str, outsz: sizet, blk: rawptr[BasicBlock]): - """输出非 entry 块头: ^name(%p: ty, …):""" + """Emit a non-entry block header: ^name(%p: ty, ...):""" _append_cstr(out, outsz, "^") if blk.Name is not None: _append_cstr(out, outsz, blk.Name) @@ -1880,7 +1864,7 @@ def _MlirEmitBlockHeader(out: str, outsz: sizet, blk: rawptr[BasicBlock]): def _MlirEmitBlockLines(out: str, outsz: sizet, blk: rawptr[BasicBlock]): - """输出块内指令(跳过块首连续 phi —— 它们已成为块参数)""" + """Emit in-block instructions (skip leading phis, which became block params)""" if blk is None or blk.Lines is None: return lines: rawptr[GSList[Line]] = blk.Lines @@ -1901,16 +1885,16 @@ def _MlirEmitBlockLines(out: str, outsz: sizet, blk: rawptr[BasicBlock]): def _MlirCheckEntryNoPhi(blk: rawptr[BasicBlock]): - """entry 块不允许 phi(无前驱,无法成为块参数)""" + """entry block must not have phis (no predecessor, cannot become block params)""" if _MlirBlockHasPhi(blk) != 0: MlirFail("MLIR 渲染: entry 块不允许 phi(MLIR entry 块不接受块参数)") # ============================================================ -# 各指令的 MLIR 渲染 +# Per-instruction MLIR rendering # ============================================================ def _MlirEmitRes(out: str, outsz: sizet, resname: str): - """输出 " [%N = ]" 前缀""" + """Emit the " [%N = ]" prefix""" _append_cstr(out, outsz, " ") if resname is not None: _append_cstr(out, outsz, resname) @@ -1918,7 +1902,7 @@ def _MlirEmitRes(out: str, outsz: sizet, resname: str): def _MlirBinop(p: str, resname: str, ob: str, out: str, outsz: sizet): - """整数/浮点二元运算: llvm. %a, %b : ty""" + """Integer/float binary op: llvm. %a, %b : ty""" tyb: str = _SC_Ty p = _mlir_read_type(p, tyb, 512) p = _mlir_ws(p) @@ -1966,10 +1950,10 @@ def _MlirFneg(p: str, resname: str, out: str, outsz: sizet): def _MlirCmp(p: str, resname: str, ob: str, out: str, outsz: sizet): """icmp/fcmp: llvm.icmp "sgt" %a, %b : ty - 中间文本是 LLVM 语法 `icmp <谓词> <类型> , `——**谓词在类型之前**, - 故读取顺序必须是 谓词 → 类型 → 操作数。 - 注意不能先读类型:那样会把谓词(如 `sgt`)当类型文本传给 _MlirTypeAppend, - 直接触发"无法转换为 MLIR 类型文本"而中止(Test 4 首条指令即 icmp 的根因)。 + The intermediate text is LLVM syntax `icmp , ` -- predicate before type, + so the read order must be predicate -> type -> operands. + Do not read the type first: the predicate (e.g. `sgt`) would be passed as type text, triggering + "cannot convert to MLIR type text" and aborting. """ pb: str = _SC_P1 p = _mlir_read_value(p, pb, 512) @@ -2006,17 +1990,14 @@ def _MlirCmp(p: str, resname: str, ob: str, out: str, outsz: sizet): def _MlirAlloca(p: str, resname: str, out: str, outsz: sizet): """alloca: llvm.alloca %cN x ty {alignment = N : i64} : (i32) -> !llvm.ptr - 对齐属性的 MLIR 形态由 mlir-opt 22.1.8 实测确定(**不得凭记忆**): - `{alignment = 16 : i64}` → exit 0 ✓(采用) - `align(16)` / `, align 16`(LLVM 词法) → exit 1(expected ':') - `{alignment = 16 : i32}` → exit 1 - ("attribute 'alignment' failed to satisfy constraint: 64-bit signless - integer attribute"——必须是 **i64**) - 故中间文本 `, align N` 必须转写为 `{alignment = N : i64}`。 + The MLIR form of the alignment attribute was determined by measuring mlir-opt 22.1.8 + (do NOT rely on memory): `{alignment = 16 : i64}` -> exit 0 (adopted); + `align(16)` / `, align 16` (LLVM syntax) -> exit 1 (expected ':'); `{alignment = 16 : i32}` + -> exit 1 (requires i64). So the intermediate `, align N` must be rewritten to `{alignment = N : i64}`. - 对齐值只接受**常量十进制**且为 **2 的幂**:mlir-opt 不校验幂次 - (实测 `{alignment = 3 : i64}` exit 0),但非 2 的幂在 LLVM 语义里非法, - 故本层把关,非常量/非幂/超范围(> 2^30)一律响亮报错。 + Alignment accepts only a decimal constant that is a power of two: mlir-opt does not check + power-of-two (`{alignment = 3 : i64}` exits 0), but non-powers are illegal in LLVM semantics, so this + layer gates it: non-constant/non-power/out-of-range (> 2^30) always errors loudly. """ tyb: str = _SC_Ty p = _mlir_read_type(p, tyb, 512) @@ -2024,7 +2005,7 @@ def _MlirAlloca(p: str, resname: str, out: str, outsz: sizet): aligntxt: str = _SC_P2 aligntxt[0] = '\0' if p[0] == ',': - # ", align N"(build_alloca_aligned 的中间文本形态) + # ", align N" (the intermediate-text form from build_alloca_aligned) p = _mlir_ws(p + 1) if string.strncmp(p, "align", 5) != 0: MlirFail("MLIR 渲染: alloca 只支持 align 属性(未知属性)") @@ -2141,18 +2122,18 @@ def _MlirStore(p: str, out: str, outsz: sizet): def _MlirBr(p: str, out: str, outsz: sizet): - """br:LLVM 语法里条件跳转的 opcode **也是 `br`**,靠首个记号是否为 `label` 分流 + """br: in LLVM syntax the conditional branch opcode is also `br`, dispatched by whether the first token is `label`. - br label %x → llvm.br ^x - br i1 %c, label %a, label %b → llvm.cond_br %c, ^a, ^b + br label %x -> llvm.br ^x + br i1 %c, label %a, label %b -> llvm.cond_br %c, ^a, ^b - 注意:build_cond_br 产出的中间文本是 `br i1 …`(**不是** `cond_br`), - 故条件形态必须在此转交 _MlirCondBr;否则会被当无条件跳转, - 把类型 `i1` 误当块名(产出 `llvm.br ^i1` 且丢掉条件)。 + Note: build_cond_br emits intermediate text `br i1 ...` (NOT `cond_br`), so the conditional + form must be forwarded here to _MlirCondBr; otherwise it is taken as unconditional and the + type `i1` is mistaken for a block name (producing `llvm.br ^i1` and dropping the condition). """ p = _mlir_ws(p) if string.strncmp(p, "label ", 6) != 0: - # 条件跳转:`<类型> , label %a, label %b` + # Conditional branch: ` , label %a, label %b` _MlirCondBr(p, out, outsz) return p = _mlir_ws(p + 6) @@ -2202,7 +2183,7 @@ def _MlirCondBr(p: str, out: str, outsz: sizet): def _MlirRet(p: str, out: str, outsz: sizet): - """ret: llvm.return %v : ty(void 省略操作数与类型)""" + """ret: llvm.return %v : ty (void omits operand and type)""" p = _mlir_ws(p) if string.strncmp(p, "void", 4) == 0 and (p[4] == '\0' or p[4] == ' '): _MlirEmitRes(out, outsz, None) @@ -2230,39 +2211,38 @@ def _MlirUnreachable(out: str, outsz: sizet): def _MlirCall(p: str, resname: str, out: str, outsz: sizet): - """call: llvm.call @f(%a) : (i32) -> i32 / 间接 llvm.call %fp(%a) : !llvm.ptr, (i32) -> i32 + """call: llvm.call @f(%a) : (i32) -> i32 / indirect llvm.call %fp(%a) : !llvm.ptr, (i32) -> i32 - 变参调用(中间文本 `call (…) @f(…)`)追加 `vararg(!llvm.func<…>)`。 + Variadic calls (intermediate text `call (...) @f(...)`) append `vararg(!llvm.func<...>)`. """ p = _mlir_ws(p) variadic: int = 0 vftys: str = _SC_Tmp vftys[0] = '\0' rtyb: str = _SC_Ty - # 返回类型必须用 nofunc=1 读:变参调用的中间文本里,返回类型之后的 `(…)` - # 是**被调函数的参数类型表**(LLVM 词法),不是返回类型的一部分。若按默认 - # 行为把它当"函数类型后缀"吞进 rtyb,则 p 落到 `@`/`%`、下面的变参分支 - # 永远进不去,rtyb 变成 `i32 (!llvm.ptr, i32, ...)` 送进 MlirTypeText → - # _MlirFuncText 按逗号切参数,末片 `...` 无对应形态 → 误报 - # "无法转换为 MLIR 类型文本(v1 未覆盖该形态):'...'"。 + # The return type must be read with nofunc=1: in variadic-call text, the `(...)` after the + # return type is the callee's parameter type list (LLVM syntax), not part of the return type. + # Reading it as a function-type suffix would leave p at `@`/`%`, never entering the variadic + # branch, turning rtyb into `i32 (!llvm.ptr, i32, ...)`, which MlirTypeText splits on commas + # and misreports on the trailing `...`. p = _mlir_read_type(p, rtyb, 512, 1) p = _mlir_ws(p) if string.strncmp(p, "asm", 3) == 0: - # 内联汇编:中间文本是 `[%N = ]call asm [sideeffect] - # [inteldialect] "", ""( %v, …)`(LLVM 词法), - # MLIR 形态由 _MlirAsm 转写。 + # Inline asm: intermediate text is `[%N = ]call asm [sideeffect] + # [inteldialect] "", ""( %v, ...)` (LLVM syntax); + # the MLIR form is rewritten by _MlirAsm. _MlirAsm(p, resname, rtyb, out, outsz) return if p[0] == '(': - # 变参调用:中间文本是 `call (<实参类型…>, ...) @f(<实参>)` - # (builder 的 build_call is_variadic=1 形态)。 - # MLIR 形态(mlir-opt 22.1.8 实测 exit 0): + # Variadic call: intermediate text is `call (, ...) @f()` + # (builder's build_call is_variadic=1 form). + # MLIR form (mlir-opt 22.1.8 measured exit 0): # %0 = llvm.call @printf(%p, %n) vararg(!llvm.func) # : (!llvm.ptr, i32) -> i32 - # `vararg(...)` 里是**被调函数的完整 MLIR 函数类型**(含 `...`); - # void 返回时该处返回类型写 `void`(写 `none` 实测报 - # "expected var_callee_type to return void")。 - # 注:vftys/calb 等缓冲用非 _SC_Ty(rtyb 占着)/非 _SC_Tmp2(实参循环用)。 + # `vararg(...)` holds the callee's full MLIR function type (with `...`); + # for void returns that return type is `void` (writing `none` errors + # "expected var_callee_type to return void"). + # Note: buffers like vftys/calb avoid _SC_Ty (rtyb holds it) and _SC_Tmp2 (used by the arg loop). variadic = 1 p = p + 1 g0: int = 0 @@ -2277,7 +2257,7 @@ def _MlirCall(p: str, resname: str, out: str, outsz: sizet): if vftys[0] != '\0': _append_cstr(vftys, 8192, ", ") if p[0] == '.': - # `...` 变参记号直通(不是类型,MlirTypeText 不认该记号) + # `...` variadic token passes through (not a type; MlirTypeText rejects it) _append_cstr(vftys, 8192, "...") while p[0] == '.': p = p + 1 @@ -2331,13 +2311,11 @@ def _MlirCall(p: str, resname: str, out: str, outsz: sizet): rmt: str = _SC_Mt rmt[0] = '\0' MlirTypeText(rmt, 1024, rtyb, _MLIR_Pool) - # LLVM intrinsic(裸符号 @llvm.xxx,非 llvm.mlir.*)必须走通用 op - # `llvm.call_intrinsic "llvm.xxx"(...) : (...) -> ...`:mlir-translate 会按 - # 操作数类型把 intrinsic 名 mangle(如 llvm.va_start -> llvm.va_start.p0)。 - # 用普通 llvm.call 只会生成未 mangle 的 @llvm.va_start,LLVM 校验报 - # "Intrinsic name not mangled correctly for type arguments!"。 - # (llvmlite 路径能过:LLVM 的 IR **解析器**会自动 upgrade 旧式 intrinsic 名, - # 而 mlir-translate 是程序化构造 IR,不走该 upgrade。) + # LLVM intrinsics (bare symbol @llvm.xxx, not llvm.mlir.*) must use the generic op + # `llvm.call_intrinsic "llvm.xxx"(...) : (...) -> ...`: mlir-translate mangles the intrinsic + # name by operand types (e.g. llvm.va_start -> llvm.va_start.p0). A plain llvm.call would emit + # an unmangled @llvm.va_start and LLVM verification errors + # "Intrinsic name not mangled correctly for type arguments!". if calb[0] == '@' and string.strncmp(calb + 1, "llvm.", 5) == 0 \ and string.strncmp(calb + 1, "llvm.mlir.", 10) != 0: if string.strcmp(rmt, "none") == 0: @@ -2367,21 +2345,21 @@ def _MlirCall(p: str, resname: str, out: str, outsz: sizet): _append_cstr(out, outsz, args) _append_cstr(out, outsz, ")") if variadic != 0: - # 变参调用:vararg(被调函数的完整 MLIR 函数类型)(含 `...`) + # Variadic call: vararg(the callee's full MLIR function type) (includes `...`) _append_cstr(out, outsz, " vararg(!llvm.func<") if string.strcmp(rmt, "none") == 0: - # void 返回的变参函数类型:返回类型写 `void`,不能写 `none` + # Void-returning variadic function type: write `void`, not `none` _append_cstr(out, outsz, "void") else: _append_cstr(out, outsz, rmt) _append_cstr(out, outsz, " (") _append_cstr(out, outsz, vftys) - # 依次闭合三层:参数表 `)`、`!llvm.func<` 的 `>`、`vararg(` 的 `)` - # (此前漏掉 vararg 的 `)`,mlir-opt 22.1.8 逐字报 "expected ')'") + # Close three levels in order: the param list `)`, `!llvm.func<`'s `>`, `vararg(`'s `)` + # (omitting vararg's `)` made mlir-opt 22.1.8 report "expected ')'" verbatim) _append_cstr(out, outsz, ")>)") _append_cstr(out, outsz, " : ") if calb[0] != '@': - # 间接调用:被调用者是 !llvm.ptr 操作数 + # Indirect call: the callee is a !llvm.ptr operand _append_cstr(out, outsz, "!llvm.ptr, ") _append_cstr(out, outsz, "(") _append_cstr(out, outsz, argts) @@ -2394,30 +2372,26 @@ def _MlirCall(p: str, resname: str, out: str, outsz: sizet): def _MlirAsm(p: str, resname: str, rtyb: str, out: str, outsz: sizet): - """内联汇编: llvm.inline_asm [has_side_effects] [asm_dialect = intel] "…", "…" %a, %b : (i32, i32) -> i32 + """Inline asm: llvm.inline_asm [has_side_effects] [asm_dialect = intel] "...", "..." %a, %b : (i32, i32) -> i32 - 中间文本(LLVM 词法)有两条来源: - builders 的 build_inline_asm → `call asm sideeffect "…", "…"()` - TPV CallGenCBuiltin 注入文本 → `call asm sideeffect inteldialect "…", "…"( %v, …)` - MLIR(llvm 方言) 形态由 mlir-opt 22.1.8 实测确定(**不得凭记忆**): - * 方言关键字是 `asm_dialect = intel`:LLVM 的裸 `inteldialect` 报 - "expected attribute value";`asm_dialect = inteldialect` 报 - "expected string or keyword containing one of the following enum values - for attribute 'asm_dialect' [att, intel]"。 - * 输入操作数是**逗号分隔的裸操作数、不带括号**:`"…", "=r,r"(%x) : (i32) -> i32` - 报 "expected ':'"(那正是 LLVM 词法,须改写)。 - * 无返回 ⇒ `: (<操作数类型…>) -> ()`;有返回 ⇒ `-> ty`。 - * **多结果**内联汇编在 MLIR 里是**单结果 + 结构体类型**:TPV 形态 - `%N = call {i32, i32} asm sideeffect "…", "=r,r"()` 渲染为 - `%N = llvm.inline_asm has_side_effects "…", "=r,r" : () -> !llvm.struct<(i32, i32)>` - (mlir-opt / mlir-translate 22.1.8 实测 exit 0,降级得 - `call { i32, i32 } asm sideeffect …`),随后由 `llvm.extractvalue` - 逐字段抽取(见 _MlirExtractValue)。此处无需特殊分支:返回类型文本经 - MlirTypeText(`{…}` → `!llvm.struct<(...)>`)即可,嵌套结构体亦同。 - * 实测通过组合:有/无 has_side_effects × 有/无 asm_dialect = intel × - 0/1/2 个操作数 × 有/无返回值。 - 非法形态一律响亮报错:除 `sideeffect`/`inteldialect` 之外的未知记号、 - 缺约束串、非字符串字面量、实参不成对、有返回值却无结果名。 + Intermediate text (LLVM syntax) has two sources: + builders' build_inline_asm -> `call asm sideeffect "...", "..."()` + TPV CallGenCBuiltin injected -> `call asm sideeffect inteldialect "...", "..."( %v, ...)` + The MLIR (llvm dialect) form was determined by measuring mlir-opt 22.1.8 (do NOT rely on memory): + * The dialect keyword is `asm_dialect = intel`: LLVM's bare `inteldialect` errors + "expected attribute value"; `asm_dialect = inteldialect` errors with the enum list [att, intel]. + * Input operands are comma-separated bare operands without parens: `"...", "=r,r"(%x) : (i32) -> i32` + errors "expected ':'" (that is LLVM syntax and must be rewritten). + * No return => `: () -> ()`; with return => `-> ty`. + * Multi-result inline asm is a single struct-typed result in MLIR: TPV's + `%N = call {i32, i32} asm sideeffect "...", "=r,r"()` renders as + `%N = llvm.inline_asm has_side_effects "...", "=r,r" : () -> !llvm.struct<(i32, i32)>` + (mlir-opt / mlir-translate 22.1.8 measured exit 0), then fields are extracted with + `llvm.extractvalue` (see _MlirExtractValue). No special branch is needed here: the return type + text goes through MlirTypeText (`{...}` -> `!llvm.struct<(...)>`), nested structs included. + * Measured passing combinations: has_side_effects x asm_dialect = intel x 0/1/2 operands x return/no-return. + Illegal forms always error loudly: unknown tokens besides `sideeffect`/`inteldialect`, missing + constraint string, non-string literal, unpaired args, or a return value without a result name. """ p = _mlir_ws(p + 3) side: int = 0 @@ -2434,8 +2408,8 @@ def _MlirAsm(p: str, resname: str, rtyb: str, out: str, outsz: sizet): p = _mlir_ws(p + 12) continue break - # 汇编文本可长达数 KB(TPV 的 ASM_LINE_BUF_SIZE=4096),用 4096 缓冲承载, - # 不落小缓冲以免截断;转义对(\22/\5C/\0A)原样透传——MLIR 词法同样接受。 + # Asm text can reach several KB (TPV's ASM_LINE_BUF_SIZE=4096); use a 4096 buffer, not a + # small one, to avoid truncation; escape pairs (\22/\5C/\0A) pass through as MLIR accepts them. asmb: str = _SC_Tmp p = _mlir_read_value(p, asmb, 4096) if asmb[0] != '"': @@ -2519,7 +2493,7 @@ def _MlirAsm(p: str, resname: str, rtyb: str, out: str, outsz: sizet): def _MlirCast(p: str, resname: str, ob: str, out: str, outsz: sizet): - """转换指令: llvm.zext %v : i32 to i64""" + """Cast instruction: llvm.zext %v : i32 to i64""" ftyb: str = _SC_Ty p = _mlir_read_type(p, ftyb, 512) p = _mlir_ws(p) @@ -2549,7 +2523,7 @@ def _MlirCast(p: str, resname: str, ob: str, out: str, outsz: sizet): def _MlirBitcast(p: str, resname: str, out: str, outsz: sizet): - """bitcast: ptr→ptr 视为 no-op(记录别名);否则 llvm.bitcast %v : T to U""" + """bitcast: ptr->ptr is treated as a no-op (records an alias); otherwise llvm.bitcast %v : T to U""" ftyb: str = _SC_Ty p = _mlir_read_type(p, ftyb, 512) p = _mlir_ws(p) @@ -2561,8 +2535,8 @@ def _MlirBitcast(p: str, resname: str, out: str, outsz: sizet): ttyb: str = _SC_Ty2 _mlir_read_type(p, ttyb, 512) if MlirTypeTextIsPointer(ftyb) != 0 and MlirTypeTextIsPointer(ttyb) != 0: - # MLIR 不透明指针下 ptr→ptr bitcast 无意义 → no-op: - # 把结果名登记为源操作数别名(后续引用自动改写),不输出指令 + # With opaque pointers, a ptr->ptr bitcast is meaningless -> no-op: + # register the result name as an alias of the source operand (later refs auto-rewritten), emit nothing if resname is None: MlirFail("MLIR 渲染: no-op bitcast 缺少结果名") opnd: str = _MlirOperand(vb, ftyb, None, 0) @@ -2658,11 +2632,11 @@ def _MlirGep(p: str, resname: str, out: str, outsz: sizet): p = _mlir_ws(p) ivb: str = _SC_V1 p = _mlir_read_value(p, ivb, 512) - # 常量索引必须写成**整数字面量**(如 `[0, 1]`),不能物化成 SSA 操作数: - # MLIR 的 llvm.getelementptr 要求“索引到结构体”的索引是常量,否则 - # verifier 报 'expected index N indexing a struct to be constant' - # (操作数形式只会进 rawConstantIndices 的动态占位)。仅非常量(SSA - # 值,以 '%' 开头)索引才物化为操作数并占类型位;常量索引不占类型位。 + # Constant indices must be written as integer literals (e.g. `[0, 1]`), not materialized + # as SSA operands: MLIR's llvm.getelementptr requires struct-field indices to be constant, + # else the verifier errors 'expected index N indexing a struct to be constant' + # (operand form only populates rawConstantIndices' dynamic slot). Only non-constant (SSA, + # starting with '%') indices are materialized and take a type slot; constants do not. if nidx > 0: _append_cstr(idxv, 1024, ", ") if ivb[0] == '%': @@ -2690,7 +2664,7 @@ def _MlirGep(p: str, resname: str, out: str, outsz: sizet): _append_cstr(out, outsz, "[") _append_cstr(out, outsz, idxv) _append_cstr(out, outsz, "] : (!llvm.ptr") - # 每个索引一个类型 + # One type per index _MlirAppendCommaTypes(out, outsz, idxt) _append_cstr(out, outsz, ") -> !llvm.ptr, ") _append_cstr(out, outsz, bmt) @@ -2698,7 +2672,7 @@ def _MlirGep(p: str, resname: str, out: str, outsz: sizet): def _MlirAppendCommaTypes(out: str, outsz: sizet, tylist: str): - """把 ", t0, t1" 追加到 out(tylist 是逗号分隔的类型串)""" + """Append ", t0, t1" to out (tylist is a comma-separated type string)""" if tylist is None or tylist[0] == '\0': return _append_cstr(out, outsz, ", ") @@ -2708,18 +2682,18 @@ def _MlirAppendCommaTypes(out: str, outsz: sizet, tylist: str): def _MlirSwitch(p: str, out: str, outsz: sizet): """switch: llvm.switch %v : i32, ^default [0: ^case0, 1: ^case1] - 形态由 mlir-opt 22.1.8 实测确定(设计规范里的 - `llvm.switch %v, ^default [0: ^case0] : i32` **被拒绝**:报 "expected ':'"): - `llvm.switch %v : i32, ^default [0: ^c0, 1: ^c1]` → exit 0 ✓(采用) - `llvm.switch %v : i32, ^default(%r : i32) [ … ]` → exit 0 ✓(default 块实参) - `llvm.switch %v : i32, ^default [0: ^c0(%r : i32)]` → exit 0 ✓(case 块实参) - `llvm.switch %v : i64, ^default [4294967296: ^c0]` → exit 0 ✓(case 值随值类型) - `llvm.switch %v : i32, ^default [-1: ^c0]` → exit 0 ✓ - `llvm.switch %v : i32, ^default []` → exit 0 ✓(空 case 表合法) - 即**值类型紧跟值之后**(`%v : i32,`),块实参写法与 br/cond_br 完全同构, - 故 `_MlirBlockOperands` 的 phi→块实参设施在此原样复用 ⇒ **支持目标块带 phi** - (匹配不到入边会响亮报错,绝不静默丢实参)。 - case 值仍必须是常量:MLIR 里 case 值是属性(case_values),SSA 值不可用。 + The form was determined by measuring mlir-opt 22.1.8 (the design-doc form + `llvm.switch %v, ^default [0: ^case0] : i32` is REJECTED: "expected ':'"): + `llvm.switch %v : i32, ^default [0: ^c0, 1: ^c1]` -> exit 0 (adopted) + `llvm.switch %v : i32, ^default(%r : i32) [ ... ]` -> exit 0 (default block args) + `llvm.switch %v : i32, ^default [0: ^c0(%r : i32)]` -> exit 0 (case block args) + `llvm.switch %v : i64, ^default [4294967296: ^c0]` -> exit 0 (case value follows value type) + `llvm.switch %v : i32, ^default [-1: ^c0]` -> exit 0 + `llvm.switch %v : i32, ^default []` -> exit 0 (empty case table) + i.e. the value type follows the value (`%v : i32,`); block args are written exactly like + br/cond_br, so _MlirBlockOperands' phi->block-arg facility is reused as-is => target blocks may + carry phis (a missing incoming edge errors loudly, never silently dropping args). + Case values must still be constants: in MLIR they are attributes (case_values), not SSA values. """ tyb: str = _SC_Ty p = _mlir_read_type(p, tyb, 512) @@ -2736,8 +2710,8 @@ def _MlirSwitch(p: str, out: str, outsz: sizet): p = _mlir_ws(p) if p[0] == '[': p = p + 1 - # 先物化值操作数与其 MLIR 类型文本:case 循环里的 _MlirBlockOperands 会复用 - # _SC_Ty/_SC_Buf/_SC_P1/_SC_P2 等 scratch,捕获到独立缓冲后才安全。 + # Materialize the value operand and its MLIR type text first: _MlirBlockOperands inside the + # case loop reuses _SC_Ty/_SC_Buf/_SC_P1/_SC_P2 scratch, so capture into a separate buffer first. vop: str = _SC_IdxT vop[0] = '\0' _MlirOperand(vb, tyb, vop, 1024) @@ -2775,7 +2749,7 @@ def _MlirSwitch(p: str, out: str, outsz: sizet): _append_cstr(cases, 4096, cvb) _append_cstr(cases, 4096, ": ^") _append_cstr(cases, 4096, cbr) - # 目标块带 phi ⇒ 追加块实参(与 br/cond_br 同一设施) + # Target block has phis => append block arguments (same facility as br/cond_br) _MlirBlockOperands(cases, 1024, _MlirFindBlock(_MLIR_Func, cbr), _MLIR_SrcBlk) ncase += 1 p = _mlir_ws(p) @@ -2840,22 +2814,22 @@ def _MlirAtomicRmw(p: str, resname: str, out: str, outsz: sizet): def _MlirCmpXchg(p: str, resname: str, resname2: str, out: str, outsz: sizet): - """cmpxchg: MLIR 单结果 struct{T, i1} + 两条 extractvalue 导出旧值/成功位 + """cmpxchg: MLIR single result struct{T, i1} + two extractvalues for old value / success bit. - 中间文本(LLVM 词法,两态:builder 单结果 / 双结果前缀) + Intermediate text (LLVM syntax, two forms: builder single result / two-result prefix) %N = cmpxchg , , - %a, %b = cmpxchg … - MLIR 形态(mlir-opt 22.1.8 实测 exit 0;cmpxchg 在 MLIR 里是**单结果**, - 结果为 struct{T, i1},不是两个 SSA 值): + %a, %b = cmpxchg ... + MLIR form (mlir-opt 22.1.8 measured exit 0; cmpxchg is a single result in MLIR, yielding + struct{T, i1}, not two SSA values): %N = llvm.cmpxchg %p, %c, %n acq_rel monotonic : !llvm.ptr, i32 - %_N_v = llvm.extractvalue %N[0] : !llvm.struct<(i32, i1)> ; 旧值(比较值) - %_N_ok = llvm.extractvalue %N[1] : !llvm.struct<(i32, i1)> ; i1 成功位 - 两条 extractvalue 的结果名由 struct 结果名**确定性派生** - (_MlirCxResName,如 `%0` → `%_0_v`/`%_0_ok`)。 - 两个结果名绑定:`%N`(单结果)/ `%a`(双结果首名)→ 旧值 `[0]`; - 双结果次名 `%b` → 成功位 `[1]`。绑定走 bitcast no-op 同一别名设施 - (_MlirAliasAdd),后续引用由 _MlirResolve 自动改写为 extractvalue 结果名。 - 这样 builder 端 `build_cmpxchg` 返回的「T 类型 Value」语义(旧值)得以保持。 + %_N_v = llvm.extractvalue %N[0] : !llvm.struct<(i32, i1)> ; old value (compared value) + %_N_ok = llvm.extractvalue %N[1] : !llvm.struct<(i32, i1)> ; i1 success bit + The two extractvalue result names are deterministically derived from the struct result name + (_MlirCxResName, e.g. `%0` -> `%_0_v`/`%_0_ok`). + Bindings: `%N` (single result) / `%a` (two-result first name) -> old value `[0]`; + two-result second name `%b` -> success bit `[1]`. Bindings use the same alias facility as + bitcast no-op (_MlirAliasAdd); later references are auto-rewritten by _MlirResolve to the + extractvalue result names. This preserves builder-side build_cmpxchg's "T-typed Value" (old value) semantics. """ ptyb: str = _SC_Ty p = _mlir_read_type(p, ptyb, 512) @@ -2886,13 +2860,13 @@ def _MlirCmpXchg(p: str, resname: str, resname2: str, _mlir_read_value(p, fail, 512) if resname is None: MlirFail("MLIR 渲染: cmpxchg 缺少结果名") - # 两条 extractvalue 的结果名由 struct 结果名确定性派生(两遍必然同名) + # The two extractvalue result names derive deterministically from the struct name (identical across both passes) onm: str = _MlirCxResName(resname, "_v") snm: str = _MlirCxResName(resname, "_ok") _MlirAliasAdd(resname, onm) if resname2 is not None: _MlirAliasAdd(resname2, snm) - # MLIR 类型文本(用非 _SC_Mt 缓冲:_MlirOperand 对字面量会占用 _SC_Mt) + # MLIR type text (use a non-_SC_Mt buffer: _MlirOperand occupies _SC_Mt for literals) pmt: str = _SC_Tmp2 pmt[0] = '\0' MlirTypeText(pmt, 4096, ptyb, _MLIR_Pool) @@ -2935,18 +2909,16 @@ def _MlirCmpXchg(p: str, resname: str, resname2: str, def _MlirVaArg(p: str, resname: str, out: str, outsz: sizet): - """va_arg: 用**通用 op 语法**(刻意选择,见下) + """va_arg: use the generic op syntax (deliberate choice, see below). - 中间文本(LLVM 词法): %N = va_arg , - MLIR 文本: %N = "llvm.va_arg"(%ap) : (!llvm.ptr) -> i32 + Intermediate text (LLVM syntax): %N = va_arg , + MLIR text: %N = "llvm.va_arg"(%ap) : (!llvm.ptr) -> i32 - 为什么必须用 generic 形态(mlir-opt 22.1.8 实测):自定义汇编语法 - `llvm.va_arg %ap, i32` 要求显式给出 va_list 的类型(LLVM 里是 `ptr`), - 而不透明指针下该类型无法表达 —— mlir-opt 报 "expected builtin.function", - 故改用通用 op 语法(generic form),只用操作数类型列表描述。 - 降级验证(mlir-translate --mlir-to-llvmir)实测得到 - %2 = va_arg ptr %0, i32 - 与 LLVM 的 va_arg 完全同形。 + Why generic form is required (mlir-opt 22.1.8 measured): the custom assembly syntax + `llvm.va_arg %ap, i32` requires an explicit va_list type (in LLVM it is `ptr`), which cannot + be expressed with opaque pointers -- mlir-opt errors "expected builtin.function". So the + generic op form is used, described only by operand/result types. Down-level verification + (mlir-translate --mlir-to-llvmir) yields `%2 = va_arg ptr %0, i32`, isomorphic to LLVM's va_arg. """ vtyb: str = _SC_Ty p = _mlir_read_type(p, vtyb, 512) @@ -2960,7 +2932,7 @@ def _MlirVaArg(p: str, resname: str, out: str, outsz: sizet): _mlir_read_type(p, atyb, 512) if resname is None: MlirFail("MLIR 渲染: va_arg 缺少结果名") - # 操作数类型 / 结果类型(_MlirOperand 只用 _SC_Mt,故此处先取独立缓冲) + # Operand/result types (_MlirOperand only uses _SC_Mt, so take separate buffers here) omt: str = _SC_Tmp2 omt[0] = '\0' MlirTypeText(omt, 4096, vtyb, _MLIR_Pool) @@ -2978,22 +2950,21 @@ def _MlirVaArg(p: str, resname: str, out: str, outsz: sizet): def _MlirExtractValue(p: str, resname: str, out: str, outsz: sizet): - """extractvalue: llvm.extractvalue %v[i0, i1] : <聚合类型> + """extractvalue: llvm.extractvalue %v[i0, i1] : - 中间文本是 LLVM 语法(**聚合类型文本在值之前,索引在逗号之后**): + Intermediate text is LLVM syntax (aggregate type BEFORE the value, indices after commas): %r = extractvalue {i32, i32} %v, 0 - %r = extractvalue {i32, {i32, i32}} %v, 1, 0 ; 多级索引 - MLIR 形态(mlir-opt 22.1.8 实测 exit 0): + %r = extractvalue {i32, {i32, i32}} %v, 1, 0 ; multi-level index + MLIR form (mlir-opt 22.1.8 measured exit 0): %r = llvm.extractvalue %v[0] : !llvm.struct<(i32, i32)> %r = llvm.extractvalue %v[1, 0] : !llvm.struct<(i32, !llvm.struct<(i32, i32)>)> - 索引列表在 MLIR 里写在 `[]` 内、以 `, ` 分隔(与 LLVM 的 `<聚合>, i0, i1` - 词法不同,必须转写);聚合类型文本经 MlirTypeText 转成 MLIR 类型 - (匿名 `{…}` → !llvm.struct<(...)>、具名 `%"name"` → 内联具名体、 - 数组 `[N x T]` → !llvm.array,三者均已实测 exit 0)。 - 真实来源:TPV 的**多结果内联汇编**(`%asm_result = call {i32, i32} asm …`) - 之后逐字段抽取(`%asm_out_0 = extractvalue {i32, i32} %asm_result, 0`, - 语料实测均为单索引);多级索引同样经 mlir-opt 实测合法,故一并支持。 - 索引必须是常量字面量(LLVM 词法如此)——SSA 索引 ⇒ 响亮报错。 + The index list is written inside `[]` separated by ", " in MLIR (different from LLVM's + `, i0, i1` syntax, so it must be rewritten); the aggregate type text goes through + MlirTypeText (anonymous `{...}` -> !llvm.struct<(...)>, named `%"name"` -> inlined named body, + array `[N x T]` -> !llvm.array, all measured exit 0). + Real source: TPV's multi-result inline asm (`%asm_result = call {i32, i32} asm ...`) followed by + per-field extraction (`%asm_out_0 = extractvalue {i32, i32} %asm_result, 0`); multi-level indices + are also supported (measured legal). Indices must be constant literals (LLVM syntax) -- SSA index => loud error. """ atyb: str = _SC_Ty p = _mlir_read_type(p, atyb, 512) @@ -3004,8 +2975,8 @@ def _MlirExtractValue(p: str, resname: str, out: str, outsz: sizet): idxs[0] = '\0' nidx: int = 0 g: int = 0 - # 索引列表:LLVM 词法里逗号分隔(`<聚合>, i0, i1`),逐个读出后转写; - # 循环上限与文件内其它解析循环同款(有界,超出即响亮报错)。 + # Index list: comma-separated in LLVM syntax (`, i0, i1`); read each and rewrite. + # Loop bound matches other parse loops in this file (bounded; exceeding errors loudly). while g < 64: p = _mlir_ws(p) if p[0] != ',': @@ -3028,7 +2999,7 @@ def _MlirExtractValue(p: str, resname: str, out: str, outsz: sizet): MlirFail("MLIR 渲染: extractvalue 缺少索引") if p[0] != '\0': MlirFail("MLIR 渲染: extractvalue 索引之后出现多余记号") - # MLIR 聚合类型文本先落独立缓冲(_MlirOperand 只占 _SC_Mt,不冲突) + # Aggregate type text goes into a separate buffer first (_MlirOperand only uses _SC_Mt) amt: str = _SC_Tmp2 amt[0] = '\0' MlirTypeText(amt, 4096, atyb, _MLIR_Pool) @@ -3043,15 +3014,15 @@ def _MlirExtractValue(p: str, resname: str, out: str, outsz: sizet): # ============================================================ -# 指令行 → MLIR 分派 +# Instruction line -> MLIR dispatch # ============================================================ def _MlirRenderLine(text: str, out: str, outsz: sizet): - """解析一条中间指令文本并渲染为 MLIR(out=None 时仅登记/校验)""" + """Parse one intermediate instruction text and render it to MLIR (out=None: register/validate only)""" global _MLIR_Pass, _MLIR_CurLine if text is None: return - # 入口寄存"当前行":类型文本转换失败时随报文打印(两次 pass 都是同一行, - # pass1 失败也能定位到具体中间文本)。text 指向 Line.Buf,生命周期覆盖整次渲染。 + # Stash the "current line" at entry: printed on type-text conversion failure (both passes + # use the same line, so pass1 failures are also locatable). text points to Line.Buf, live for the whole render. _MLIR_CurLine = text MlirSetContextLine(_MLIR_CurLine) p: str = _mlir_ws(text) @@ -3067,8 +3038,8 @@ def _MlirRenderLine(text: str, out: str, outsz: sizet): resname = _mlir_dup(tb) p = _mlir_ws(q2 + 1) elif q2[0] == ',': - # 双结果前缀 `%a, %b = `(仅 cmpxchg):MLIR 的 cmpxchg 是**单结果** - # struct{T, i1},两个名字分别绑定到两条 extractvalue(见 _MlirCmpXchg)。 + # Two-result prefix `%a, %b = ` (cmpxchg only): MLIR's cmpxchg is a single + # struct{T, i1} result; the two names each bind to one extractvalue (see _MlirCmpXchg). resname = _mlir_dup(tb) q3: str = _mlir_ws(q2 + 1) tb2: str = _SC_V3 @@ -3125,7 +3096,7 @@ def _MlirRenderLine(text: str, out: str, outsz: sizet): return if string.strcmp(ob, "phi") == 0: if out is not None: - # 块首 phi 已在 _MlirEmitBlockLines 中跳过;走到这里说明 phi 不在块首 + # Leading phis are skipped in _MlirEmitBlockLines; reaching here means phi is not at block head MlirFail("MLIR 渲染: phi 不在块首(v1 只支持块首连续 phi → 块参数)") _MlirPhiScan(text) return @@ -3151,22 +3122,22 @@ def _MlirRenderLine(text: str, out: str, outsz: sizet): _MlirVaArg(p, resname, out, outsz) return if string.strcmp(ob, "extractvalue") == 0: - # 真实来源:TPV 的多结果内联汇编结果抽取(见 _MlirExtractValue)。 - # 注:`insertvalue` 等其它未支持 opcode 在语料中 0 命中, - # 落到下面的兜底响亮报错(保持"未知指令 opcode"即可)。 + # Real source: TPV's multi-result inline-asm result extraction (see _MlirExtractValue). + # Note: other unsupported opcodes like `insertvalue` never occur in the corpus and fall + # through to the loud fallback error ("unknown instruction opcode"). _MlirExtractValue(p, resname, out, outsz) return - # 注:内联汇编在中间文本里 opcode 是 "call"(call asm …), - # 由 _MlirCall 内部识别并转交 _MlirAsm 渲染(llvm.inline_asm)。 + # Note: inline asm's opcode in intermediate text is "call" (call asm ...); _MlirCall + # recognizes it and forwards to _MlirAsm (llvm.inline_asm). MlirFail("MLIR 渲染: 未知指令 opcode") # ============================================================ -# 函数签名与函数体 +# Function signature and body # ============================================================ def _MlirFuncSig(buf: str, size: sizet, func: rawptr[Function], pool: memhub.MemBuddy*): - """llvm.func @name(%a: i32, %b: i32) -> i32(void 返回省略 -> ty)""" + """llvm.func @name(%a: i32, %b: i32) -> i32 (void return omits -> ty)""" _append_cstr(buf, size, "llvm.func @") if func.Name is not None: if _ll_name_needs_quote(func.Name) != 0: @@ -3199,15 +3170,15 @@ def _MlirFuncSig(buf: str, size: sizet, _append_cstr(buf, size, ty_buf) cur = cur.Next first = 0 - # 变参函数:参数表尾追加 `...`(mlir-opt 22.1.8 实测 exit 0: - # `llvm.func @f(%p: !llvm.ptr, ...) -> i32` / 无固定参数时 `llvm.func @f(...) -> i32`) + # Variadic function: append `...` at the end of the parameter list (mlir-opt 22.1.8 measured exit 0: + # `llvm.func @f(%p: !llvm.ptr, ...) -> i32` / with no fixed params `llvm.func @f(...) -> i32`) if func.IsVarArg == 1: if params is not None and params.Head is not None: _append_cstr(buf, size, ", ...") else: _append_cstr(buf, size, "...") _append_cstr(buf, size, ")") - # 返回类型(Void 省略) + # Return type (Void omitted) is_void: int = 0 match func.RetTy: case LLVMType.Void(): @@ -3224,43 +3195,41 @@ def _MlirFuncSig(buf: str, size: sizet, def FunctionPrint(buf: str, size: sizet, func: rawptr[Function], pool: memhub.MemBuddy*): - """将函数 func 序列化为 MLIR(llvm 方言) 文本**追加**写入 buf + """Serialize function func as MLIR (llvm dialect) text APPENDED to buf. - 注意:与 llvmlite 版一致,本函数为追加写(供模块打印连续拼接多个函数), - 调用方若要单函数文本需自行先置 buf[0]='\\0'。 + NOTE: like the llvmlite version, this appends (so the module printer can concatenate + functions); callers wanting single-function text must set buf[0]='\\0' themselves. """ global _MLIR_Pass, _MLIR_Func, _MLIR_SrcBlk, _MLIR_CurLine, _MlirOutTrunc if func is None or buf is None or size == 0: return _MlirOutTrunc = 0 - # 先本地窥孔(ret zeroinitializer→0/null、入口槽 store→load 转发、 - # no-op bitcast 消除):窥孔按"名称一致性"匹配、删除行置 Buf=None。 - # 窥孔作用在 **builders 生成的中间指令文本** 层,与 MLIR 渲染正交; - # RenumberSSA 内部会跳过 declare 函数(IsDeclared==1)。 + # Local peephole first (ret zeroinitializer->0/null, entry-slot store->load forwarding, + # no-op bitcast removal): peepholes match by name and delete lines by setting Buf=None. + # They act on the builders' intermediate instruction text and are orthogonal to MLIR rendering; + # RenumberSSA internally skips declare functions (IsDeclared==1). if func.IsDeclared == 0 and G_PeepEnable != 0: _peephole_opt(func, pool) RenumberSSA(func, pool) _MlirTablesInit(pool) _MLIR_Func = func - # 清空上一函数的行上下文:签名/declare 等非"行驱动"位置若报类型错, - # 不该带上一个已经无关的旧行,避免诊断误导(行上下文只在 _MlirRenderLine 赋值)。 + # Clear the previous function's line context: signature/declare and other non-line-driven + # positions must not carry an unrelated stale line, avoiding misleading diagnostics. _MLIR_CurLine = None MlirSetContextLine(None) if func.IsDeclared == 1: - # LLVM intrinsic 的声明**不输出**:MLIR 侧 intrinsic 由 llvm.call_intrinsic - # 表达(见 _MlirCall)。若照常输出 `llvm.func @llvm.va_start(!llvm.ptr)`, - # mlir-translate 会把它原样降级为未按类型 mangle 的 - # `declare void @llvm.va_start(ptr)`,LLVM 校验报 - # "Intrinsic name not mangled correctly for type arguments!"。 - # 判定:名字以 "llvm." 开头且非 "llvm.mlir.*"(后者是 MLIR 内部 op 名, - # 不是 LLVM intrinsic,也不会作为函数名出现)。 + # LLVM intrinsic declarations are NOT emitted: on the MLIR side intrinsics are expressed + # via llvm.call_intrinsic (see _MlirCall). Emitting `llvm.func @llvm.va_start(!llvm.ptr)` + # would be down-leveled to an unmangled `declare void @llvm.va_start(ptr)`, and LLVM + # verification errors "Intrinsic name not mangled correctly for type arguments!". + # Rule: name starts with "llvm." and not "llvm.mlir.*" (the latter are MLIR-internal op names). if func.Name is not None and string.strncmp(func.Name, "llvm.", 5) == 0 \ and string.strncmp(func.Name, "llvm.mlir.", 10) != 0: return - # declare(无函数体) + # declare (no body) _MlirFuncSig(buf, size, func, pool) _append_cstr(buf, size, "\n") _MlirOutTruncFail() @@ -3273,7 +3242,7 @@ def FunctionPrint(buf: str, size: sizet, _MlirOutTruncFail() return - # ---- pass1:扫描(登记常量/别名 + 形态校验)---- + # ---- pass1: scan (register constants/aliases + shape checks) ---- _MLIR_Pass = 1 blk: rawptr[BasicBlock] = blocks.Head blk_steps: int = 0 @@ -3295,7 +3264,7 @@ def FunctionPrint(buf: str, size: sizet, ln = ln.Next blk = blk.Next - # ---- pass2:发射 ---- + # ---- pass2: emit ---- _MLIR_Pass = 2 _MlirFuncSig(buf, size, func, pool) _append_cstr(buf, size, " {\n") @@ -3308,8 +3277,8 @@ def FunctionPrint(buf: str, size: sizet, MlirFail("MLIR 渲染: 函数块链疑似成环") _MLIR_SrcBlk = blk if first != 0: - # entry 块不带标签;常量物化 op 统一插在 entry 块开头 - # (entry 支配全函数 → 常量在所有块内可见,且不破坏 phi 的块首约束) + # entry block has no label; constant-materialization ops go at the entry block head + # (entry dominates the function, so constants are visible everywhere without breaking the phi-at-head rule) _MlirCheckEntryNoPhi(blk) _MlirEmitConsts(buf, size) else: @@ -3322,27 +3291,28 @@ def FunctionPrint(buf: str, size: sizet, # ============================================================ -# 内部辅助 +# Internal helpers # ============================================================ -# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, -# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 -# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 +# _append_cstr write-position cache (perf: O(1) when appending into the same buffer, +# avoiding O(n^2) strlen(dst) rescans -- an IR-serialization hot spot). +# Falls back to strlen on buffer switch (different dst) or buffer reset (dst[0]=0 and pos>0). _g_last_dst: uint64 = 0 _g_last_pos: sizet = 0 def _append_cstr(dst: str, dst_size: sizet, src: str): - """将 C 字符串 src 追加到 dst 末尾""" + """Append C string src to the end of dst""" global _g_last_dst, _g_last_pos, _MlirOutTrunc if dst is None or src is None: return dlen: sizet = 0 if _g_last_dst == uint64(dst): dlen = _g_last_pos - # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 + # Buffer reset (caller reuses the same address after buf[0]=0) -> restart from head if dlen > 0 and dst[0] == '\0': dlen = 0 - # 缓存一致性校验:缓存结尾必须仍是当前文本末端(末端 NUL 且前一字符非 NUL)。 - # 多个模块轮流写同一 buf、或 pool 复用地址残留旧内容时缓存 pos 会过期, - # 追加落到首个 NUL 之后内容"看不见"(LLvmLiteTest Test5/9 根因)。失败回退 strlen。 + # Cache-consistency check: the cached end must still be the current text end (NUL at the + # end, non-NUL before it). If several modules write the same buf or pool reuse leaves old + # content, the cached pos goes stale and appends past the first NUL become invisible + # (LLvmLiteTest Test5/9 root cause); on failure fall back to strlen. if dlen == 0: if dst[0] != '\0': dlen = string.strlen(dst) @@ -3351,8 +3321,8 @@ def _append_cstr(dst: str, dst_size: sizet, src: str): else: dlen = string.strlen(dst) _g_last_dst = uint64(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + # Unsigned-underflow guard: if dlen exceeds the buffer size (corrupted/dangling buffer), + # return; otherwise remain = dst_size - dlen underflows to a huge value and writes out of bounds. if dlen >= dst_size: _MlirOutTrunc = 1 return @@ -3372,7 +3342,7 @@ def _append_cstr(dst: str, dst_size: sizet, src: str): def _MlirOutTruncFail(): - """打印结束前的输出截断检查(截断即响亮报错,绝不静默输出半截 MLIR)""" + """Output-truncation check before printing finishes (truncation errors loudly, never emits partial MLIR silently)""" global _MlirOutTrunc if _MlirOutTrunc != 0: _MlirOutTrunc = 0 @@ -3380,48 +3350,47 @@ def _MlirOutTruncFail(): # ============================================================ -# 调试调用点记录(--traceback 专用;关闭时零开销) +# Debug call-site recording (--traceback only; zero overhead when off) # -# MLIR 后端不输出 DWARF(见 README §3.3):最终 exe 里我方 .vp 函数没有任何 -# .debug_line 记录,故运行期 stackmap 无法把「调用方帧的 PC(= 返回地址)」还原 -# 为调用点行号。这里在**构建期**把每个 define(F 记录)与每次**直接**调用(C 记录) -# 记进一段文本缓冲;每模块翻译完后由 TransPyV 取出写 .vpcall 旁文件,最后由 -# BuildPipeline.WriteStackMap 结合 `llvm-objdump -d` 做「返回地址 → (文件, 行, 函数)」 -# 的地址归一。 +# The MLIR backend emits no DWARF (see README 3.3): the final exe has no .debug_line records +# for our .vp functions, so runtime stackmaps cannot map a caller frame's PC (= return address) +# back to a call-site line. Here, at BUILD time, each define (F record) and each DIRECT call +# (C record) is recorded into a text buffer; after each module is translated TransPyV writes it +# to a .vpcall side file, and BuildPipeline.WriteStackMap combines it with +# `llvm-objdump -d` for "return address -> (file, line, function)" address normalization. # -# 记录格式(字段用 \t 分隔,避免 Windows 路径里的空格/反斜杠造成歧义): -# F\t<函数符号>\t<定义行>\t<文件绝对路径>\n -# C\t<调用方符号>\t<被调方符号>\t<调用点行>\t<文件绝对路径>\n +# Record format (fields separated by \t to avoid ambiguity from spaces/backslashes in Windows paths): +# F\t\t\t\n +# C\t\t\t\t\n # -# 函数符号口径 = Function.Name(无 @ 前缀),与 objdump 里的标签一致;被调方符号取 -# build_call 的 callee(同样无 @);LLVM intrinsic(llvm.*)不会成为具名 callq 目标, -# 直接跳过。 +# Function symbol = Function.Name (no @ prefix), matching objdump labels; the callee symbol comes +# from build_call's callee (also no @); LLVM intrinsics (llvm.*) never become named callq targets and are skipped. # ============================================================ -# 总开关:镜像 TransPyV 的 --traceback(经 DebugInfoGen.SetEnabled 转达) +# Master switch: mirrors TransPyV's --traceback (relayed via DebugInfoGen.SetEnabled) _g_dbg_on: int = 0 -# 文本缓冲(stdlib 分配,跨 pool reset 存活;DebugTakeText 交出所有权后置空) +# Text buffer (stdlib-allocated, survives pool reset; cleared after DebugTakeText hands off ownership) _g_dbg_buf: bytes = None _g_dbg_len: sizet = 0 _g_dbg_cap: sizet = 0 -# 当前函数上下文(C 记录从这里取调用方符号/文件/行);嵌套 define(lambda 等) -# 用定长栈 Push/Pop。 +# Current function context (C records pull caller symbol/file/line from here); nested defines +# (lambdas etc.) use a fixed-size Push/Pop stack. define DBGC_STACK_MAX = 256 _g_dbg_func: str = None _g_dbg_file: str = None _g_dbg_defline: int = 0 _g_dbg_line: int = 0 _g_dbg_depth: int = 0 -_g_dbg_stk_func: bytes = None # 每槽 8 字节(char*) -_g_dbg_stk_file: bytes = None # 每槽 8 字节(char*) -_g_dbg_stk_def: bytes = None # 每槽 8 字节(sizet) -_g_dbg_stk_ln: bytes = None # 每槽 8 字节(sizet) +_g_dbg_stk_func: bytes = None # 8 bytes per slot (char*) +_g_dbg_stk_file: bytes = None # 8 bytes per slot (char*) +_g_dbg_stk_def: bytes = None # 8 bytes per slot (sizet) +_g_dbg_stk_ln: bytes = None # 8 bytes per slot (sizet) def DebugSetEnabled(flag: bool) -> int: - """打开/关闭调试调用点记录(由 DebugInfoGen.SetEnabled 转发 --traceback)。""" + """Enable/disable debug call-site recording (relayed from DebugInfoGen.SetEnabled --traceback).""" global _g_dbg_on if flag: _g_dbg_on = 1 @@ -3435,7 +3404,7 @@ def DebugIsEnabled() -> int: def _DbgReserve(extra: sizet) -> int: - """确保文本缓冲至少还能容纳 extra 字节(不含末尾 NUL)。""" + """Ensure the text buffer can hold at least extra more bytes (excluding the trailing NUL).""" global _g_dbg_buf, _g_dbg_cap if _g_dbg_buf is None: cap0: sizet = 262144 @@ -3460,7 +3429,7 @@ def _DbgReserve(extra: sizet) -> int: def _DbgEnsureStack() -> int: - """惰性分配 Push/Pop 上下文栈(仅在启用且确有嵌套时用)。""" + """Lazily allocate the Push/Pop context stack (only when enabled and actual nesting occurs).""" global _g_dbg_stk_func, _g_dbg_stk_file, _g_dbg_stk_def, _g_dbg_stk_ln if _g_dbg_stk_func is not None: return 0 @@ -3478,7 +3447,7 @@ def _DbgEnsureStack() -> int: def DebugNoteFunc(sym: str, file: str, line: int) -> int: - """进入一个 define:压入外层上下文,设为当前函数,并写一条 F 记录。""" + """Enter a define: push the outer context, set as current function, and write an F record.""" global _g_dbg_func, _g_dbg_file, _g_dbg_defline, _g_dbg_line, _g_dbg_depth, _g_dbg_len if _g_dbg_on == 0: return 0 @@ -3511,7 +3480,7 @@ def DebugNoteFunc(sym: str, file: str, line: int) -> int: def DebugClearFunc() -> int: - """离开一个 define:恢复外层上下文(无外层则清空)。""" + """Leave a define: restore the outer context (clear if none).""" global _g_dbg_func, _g_dbg_file, _g_dbg_defline, _g_dbg_line, _g_dbg_depth if _g_dbg_on == 0: return 0 @@ -3534,7 +3503,7 @@ def DebugClearFunc() -> int: def DebugNoteLine(line: int) -> int: - """记录当前语句行(供随后的 build_call 写 C 记录用)。""" + """Record the current statement line (used by the following build_call to write a C record).""" global _g_dbg_line if _g_dbg_on == 0: return 0 @@ -3544,7 +3513,7 @@ def DebugNoteLine(line: int) -> int: def DebugResetFunc() -> int: - """清空当前函数上下文(模块起始兜底:翻译中途失败可能残留未 Pop 的上下文)。""" + """Clear the current function context (module-start fallback: a mid-translation failure may leave an un-Popped context).""" global _g_dbg_func, _g_dbg_file, _g_dbg_defline, _g_dbg_line, _g_dbg_depth if _g_dbg_on == 0: return 0 @@ -3557,7 +3526,7 @@ def DebugResetFunc() -> int: def _DebugNoteCall(callee: str) -> int: - """写一条 C 记录(调用方 = 当前函数,行 = 当前语句行)。""" + """Write a C record (caller = current function, line = current statement line).""" global _g_dbg_len if _g_dbg_on == 0: return 0 @@ -3579,7 +3548,7 @@ def _DebugNoteCall(callee: str) -> int: def DebugTakeText() -> str: - """取出并清空当前累积文本(每模块一次);返回的缓冲所有权交给调用方。""" + """Take and clear the accumulated text (once per module); buffer ownership passes to the caller.""" global _g_dbg_buf, _g_dbg_len, _g_dbg_cap if _g_dbg_buf is None or _g_dbg_len == 0: if _g_dbg_buf is not None: @@ -3594,29 +3563,27 @@ def DebugTakeText() -> str: # ============================================================ -# 字段访问器(供其他模块绕过 stub 类型限制使用) +# Field accessors (let other modules bypass stub type limits) # -# 跨模块编译时,使用方模块只有 Function/Param 的 stub 类型 -# (字段不足且字段类型可能错误),导致 TransPyC 静默跳过 -# 字段访问(不生成 GEP,不报错),字段值永远为 null。 -# -# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问 -# 所有字段。其他模块通过函数调用获取字段值,绕过 stub 限制。 +# Cross-module, the user module only has stub types for Function/Param (missing fields, possibly +# wrong types), so TransPyC silently skips field access (no GEP, no error) and fields stay null. +# These accessors run inside this module where the full type definitions exist, so they can access +# all fields correctly; other modules get field values via function calls, bypassing the stub limits. # ============================================================ def function_get_name(func: rawptr[Function]) -> str: - """获取函数名""" + """Get the function name""" if func is None: return None return func.Name def function_get_name_len(func: rawptr[Function]) -> sizet: - """获取函数名长度(创建时一次算好,恒等于 strlen(Name)) + """Get the function name length (computed once at creation, always == strlen(Name)). - 对齐 function_get_name 的跨模块 stub 绕过模式:使用方模块只有 Function - 的 stub 类型(字段不足且字段类型可能错误),直接读 Name/NameLen 字段会被 - TransPyC 静默跳过;本访问器在拥有完整类型定义的本模块内取字段,跨模块经 - 函数调用取得长度,避免对同一函数名反复 strlen(全程序 strlen 热点)。 + Mirrors function_get_name's cross-module stub-bypass pattern: the user module only has a stub + Function type (missing fields, possibly wrong types), so reading Name/NameLen directly is + silently skipped. This accessor reads fields inside the full-type module and returns the length + via a call, avoiding repeated strlen on the same name (a program-wide strlen hot spot). """ if func is None: return 0 @@ -3624,21 +3591,21 @@ def function_get_name_len(func: rawptr[Function]) -> sizet: def function_get_ret_ty(func: rawptr[Function]) -> rawptr[LLVMType]: - """获取函数返回类型""" + """Get the function return type""" if func is None: return None return func.RetTy def function_get_params(func: rawptr[Function]) -> rawptr[GSList[Param]]: - """获取函数参数链表容器""" + """Get the function parameter list container""" if func is None: return None return func.Params def function_get_param_head(func: rawptr[Function]) -> rawptr[Param]: - """获取函数参数链表头节点(一步到位,避免调用方访问 GSList.Head)""" + """Get the function parameter list head (one step, avoiding GSList.Head access by callers)""" if func is None: return None params: rawptr[GSList[Param]] = func.Params @@ -3648,35 +3615,35 @@ def function_get_param_head(func: rawptr[Function]) -> rawptr[Param]: def function_get_next(func: rawptr[Function]) -> rawptr[Function]: - """获取链表下一个函数""" + """Get the next function in the list""" if func is None: return None return func.Next def function_is_declared(func: rawptr[Function]) -> int: - """获取函数是否为声明(1=declare 仅声明, 0=define 定义)""" + """Get whether the function is a declaration (1=declare, 0=define)""" if func is None: return 0 return func.IsDeclared def param_get_name(param: rawptr[Param]) -> str: - """获取参数名""" + """Get the parameter name""" if param is None: return None return param.Name def param_get_ty(param: rawptr[Param]) -> rawptr[LLVMType]: - """获取参数类型""" + """Get the parameter type""" if param is None: return None return param.Ty def param_get_next(param: rawptr[Param]) -> rawptr[Param]: - """获取链表下一个参数""" + """Get the next parameter in the list""" if param is None: return None return param.Next diff --git a/mlirlite/__init__.vp b/mlirlite/__init__.vp index 74d9b17..a18817f 100644 --- a/mlirlite/__init__.vp +++ b/mlirlite/__init__.vp @@ -16,7 +16,7 @@ import viperlib # * 名字保留 LLVMxxx(同形优先)。语义差异见 README.md。 # ============================================================ -# 导入子模块 +# Import submodules from .__types import ( LLVMType, ParamNode, LLVMTypeCore, Int1, Int8, Int16, Int32, Int64, Ptr, Func, Void, Array, Struct, @@ -95,10 +95,10 @@ from .__verify import ( # ============================================================ -# 便捷工厂: 字符串复制 +# Convenience factory: string copy # ============================================================ def _str_dup(pool: memhub.MemBuddy*, src: str) -> str: - """从 pool 分配并复制一个 C 字符串""" + """Allocate from pool and copy a C string.""" if src is None: return None slen: sizet = string.strlen(src) buf: str = pool.alloc(slen + 1) @@ -108,15 +108,13 @@ def _str_dup(pool: memhub.MemBuddy*, src: str) -> str: # ============================================================ -# 便捷工厂: 创建带名称的基本块 +# Convenience factory: create a named basic block # ============================================================ def create_block(pool: memhub.MemBuddy*, func: rawptr[Function], name: str) -> rawptr[BasicBlock]: - """创建基本块并添加到函数""" - # 块名与函数参数值名冲突防护:LLVM 中块标签 "entry:" 即值 %entry, - # 与参数 %entry 重名 → llc "unable to create block named 'entry'" - # (CallGenCall._field_byte_offset(self, pool, entry, ...) 根因)。 - # 冲突时追加 .1/.2/... 后缀去重(Param.Name 可能带 % 前缀,剥离比较)。 + """Create a basic block and add it to the function.""" + # Block-name/param conflict guard: block label "entry:" is value %entry, so a param %entry + # collides; on collision append .1/.2/... (strip a leading % from Param.Name before comparing). use_name: str = name if func is not None and func.Params is not None and name is not None \ and name[0] != '\0': @@ -150,29 +148,24 @@ def create_block(pool: memhub.MemBuddy*, func: rawptr[Function], # ============================================================ -# 便捷工厂: 创建整数常量值 +# Convenience factory: create an integer constant value # ============================================================ def const_int(pool: memhub.MemBuddy*, bits: int, val: int64) -> rawptr[Value]: - """创建整数常量值(自动创建类型 + 名字文本) - - 参数: - bits: 位宽 (1/8/16/32/64) - val: 整数值 + """Create an integer constant value (auto-creates type + name text). + bits: bit width (1/8/16/32/64); val: integer value. """ - # 创建类型 + # create type ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if ty is None: return None string.memset(ty, 0, LLVMType.__sizeof__()) c.DerefAs(ty, LLVMType.Int(bits)) - # 创建名字文本 + # create name text name: str = pool.alloc(32) if name is None: return None - # LLVM i64 字面量范围不含 INT64_MIN(-9223372036854775808):该值必须 - # 用无符号位模式 9223372036854775808 表示(LLVM 接受),否则 opt 解析 - # "-9223372036854775808" 报 expected value token(二代编 viperlib.vp - # u_num=0x8000000000000000 崩溃根因;gen1/TPC 即输出无符号位模式)。 + # LLVM i64 literals exclude INT64_MIN: represent it as the unsigned bit pattern + # 9223372036854775808 (accepted by LLVM), else opt rejects "-9223372036854775808". if bits == 64 and val == -9223372036854775807 - 1: viperlib.snprintf(name, 32, "9223372036854775808") else: @@ -182,21 +175,21 @@ def const_int(pool: memhub.MemBuddy*, bits: int, def const_int32(pool: memhub.MemBuddy*, val: int64) -> rawptr[Value]: - """便捷: 创建 i32 整数常量""" + """Convenience: create an i32 integer constant.""" return const_int(pool, 32, val) def const_int64(pool: memhub.MemBuddy*, val: int64) -> rawptr[Value]: - """便捷: 创建 i64 整数常量""" + """Convenience: create an i64 integer constant.""" return const_int(pool, 64, val) # ============================================================ -# 便捷工厂: 创建函数并添加到模块 +# Convenience factory: create a function and add it to the module # ============================================================ def create_function(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], name: str, ret_ty: rawptr[LLVMType]) -> rawptr[Function]: - """创建函数并添加到模块""" + """Create a function and add it to the module.""" name_dup: str = _str_dup(pool, name) func: rawptr[Function] = new_function(pool, name_dup, ret_ty) if func is not None: @@ -206,7 +199,7 @@ def create_function(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], def add_param(pool: memhub.MemBuddy*, func: rawptr[Function], ty: rawptr[LLVMType], name: str) -> rawptr[Param]: - """创建参数并添加到函数""" + """Create a parameter and add it to the function.""" name_dup: str = _str_dup(pool, name) param: rawptr[Param] = new_param(pool, ty, name_dup) if param is not None: @@ -215,14 +208,12 @@ def add_param(pool: memhub.MemBuddy*, func: rawptr[Function], # ============================================================ -# 便捷工厂: 创建函数声明(declare) +# Convenience factory: create a function declaration (declare) # ============================================================ def create_declare(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], name: str, ret_ty: rawptr[LLVMType]) -> rawptr[Function]: - """创建函数声明(declare,无函数体)并添加到模块 - - 与 create_function 不同,声明不生成函数体,仅生成: - declare @() + """Create a function declaration (declare, no body) and add it to the module. + Unlike create_function, it emits only: declare @() """ name_dup: str = _str_dup(pool, name) func: rawptr[Function] = new_function(pool, name_dup, ret_ty) @@ -233,51 +224,45 @@ def create_declare(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], # ============================================================ -# 便捷工厂: 创建字符串常量全局变量 +# Convenience factory: create a string-constant global variable # -# 初值以 llvmlite 同形的 **LLVM 语法文本** 存入 gv.Initializer -# (c"contents\00"),MLIR 渲染阶段(__module.vp/_MlirGlobalInit) -# 再转成 MLIR attribute 形态 "contents"。 +# The initializer is stored in gv.Initializer as llvmlite-shaped LLVM syntax text (c"contents\00"); +# the MLIR render stage (__module.vp/_MlirGlobalInit) converts it to the attribute form "contents". # ============================================================ def create_global_string(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], name: str, text: str) -> rawptr[GlobalVariable]: - """创建字符串常量全局变量并添加到模块 - - 参数: - name: 全局变量名(如 ".str") - text: 字符串内容(C 字符串,不含末尾 NUL,NUL 由本函数自动添加) - - 返回: GlobalVariable 指针,其类型为 [len+1 x i8] + """Create a string-constant global variable and add it to the module. + name: global name (e.g. ".str"); text: C string without trailing NUL (added automatically). + Returns: GlobalVariable pointer of type [len+1 x i8]. """ if text is None: return None slen: sizet = string.strlen(text) - count: int = slen + 1 # 含末尾 NUL + count: int = slen + 1 # includes the trailing NUL - # 创建 i8 类型 + # create i8 type i8_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if i8_ty is None: return None string.memset(i8_ty, 0, LLVMType.__sizeof__()) c.DerefAs(i8_ty, LLVMType.Int(8)) - # 创建 [count x i8] 数组类型 + # create [count x i8] array type arr_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if arr_ty is None: return None string.memset(arr_ty, 0, LLVMType.__sizeof__()) c.DerefAs(arr_ty, LLVMType.Array(i8_ty, count)) - # 创建全局变量 + # create global variable name_dup: str = _str_dup(pool, name) gv: rawptr[GlobalVariable] = new_global_variable(pool, name_dup, arr_ty) if gv is None: return None - # 设置属性: private + unnamed_addr + constant + # set attributes: private + unnamed_addr + constant global_set_linkage(gv, "private") global_set_unnamed_addr(gv, 1) global_set_constant(gv, 1) - # 构造初始化值文本: c"contents\00" - # LLVM IR 字符串字面量格式: c"" 其中 " 和 \ 及非打印字符用 \xx 转义 - # 缓冲区大小: 最坏情况每个字符4字节(\xx)+ c"..." + \00 + " + # Build initializer text c"contents\00": LLVM IR string literal c"" with " \ and + # non-printables escaped as \xx; buffer sized for worst case (4 bytes/char) + c"..." + \00 + " init_buf: str = pool.alloc(slen * 4 + 8) if init_buf is None: return None pos: sizet = 0 @@ -288,7 +273,7 @@ def create_global_string(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], for i in range(slen): ch: uint8 = text[i] if ch == 0x22 or ch == 0x5C or ch < 0x20 or ch >= 0x7F: - # 需要转义: " → \22, \ → \5c, 非打印字符 → \xx + # escape: " -> \22, \ -> \5c, non-printable -> \xx init_buf[pos] = '\\' pos += 1 hi: int = (ch >> 4) & 0x0F @@ -306,7 +291,7 @@ def create_global_string(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], else: init_buf[pos] = ch pos += 1 - # 末尾 NUL 字节 + # trailing NUL byte init_buf[pos] = '\\' pos += 1 init_buf[pos] = '0' @@ -323,16 +308,14 @@ def create_global_string(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], # ============================================================ -# 便捷工厂: 创建带初始化值的整数全局变量 +# Convenience factory: create an integer global with an initializer # ============================================================ def create_global_int(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], name: str, bits: int, val: int64) -> rawptr[GlobalVariable]: - """创建整数全局变量并添加到模块 - - MLIR 渲染结果: - llvm.mlir.global @( : i) : i + """Create an integer global variable and add it to the module. + MLIR output: llvm.mlir.global @( : i) : i """ - # 创建整数类型 + # create integer type ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) if ty is None: return None string.memset(ty, 0, LLVMType.__sizeof__()) @@ -342,7 +325,7 @@ def create_global_int(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], gv: rawptr[GlobalVariable] = new_global_variable(pool, name_dup, ty) if gv is None: return None - # 构造初始化值文本: "42" (类型由全局渲染阶段单独打印) + # build initializer text "42" (the type is printed separately by the global render stage) init_buf: str = pool.alloc(48) if init_buf is None: return None viperlib.snprintf(init_buf, 48, "%lld", val) @@ -353,9 +336,9 @@ def create_global_int(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], # ============================================================ -# LLVMModuleCore: 模块/函数/值创建核心,封装 pool 避免重复传参 +# LLVMModuleCore: module/function/value creation core; wraps pool to avoid passing it repeatedly # -# 用法: +# Usage: # mcore: rawptr[LLVMModuleCore] = LLVMModuleCore(pool) # mod: rawptr[LLVMModule] = mcore.NewModule("test") # func: rawptr[Function] = mcore.CreateFunction(mod, "add", ty_i32) @@ -363,13 +346,13 @@ def create_global_int(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], # ============================================================ @t.NoVTable class LLVMModuleCore: - """模块/函数/值创建核心,封装 pool 避免每次创建都传 pool""" + """Module/function/value creation core; wraps pool so callers need not pass it.""" Pool: memhub.MemBuddy* def __init__(self, pool: memhub.MemBuddy*): self.Pool = pool - # ---- 模块操作 ---- + # ---- module operations ---- def NewModule(self, name: str) -> rawptr[LLVMModule]: return new_module(self.Pool, name) @@ -379,7 +362,7 @@ class LLVMModuleCore: def CreateDeclare(self, mod: rawptr[LLVMModule], name: str, ret_ty: rawptr[LLVMType]) -> rawptr[Function]: - """创建函数声明(declare,无函数体)""" + """Create a function declaration (declare, no body).""" return create_declare(self.Pool, mod, name, ret_ty) def AddParam(self, func: rawptr[Function], ty: rawptr[LLVMType], @@ -387,11 +370,11 @@ class LLVMModuleCore: return add_param(self.Pool, func, ty, name) def FunctionSetAttrs(self, func: rawptr[Function], attrs: str): - """设置函数级属性文本""" + """Set the function-level attribute text.""" function_set_attrs(func, attrs) def ParamSetAttrs(self, param: rawptr[Param], attrs: str): - """设置参数属性文本""" + """Set the parameter attribute text.""" param_set_attrs(param, attrs) def CreateBlock(self, func: rawptr[Function], @@ -401,10 +384,10 @@ class LLVMModuleCore: def NewBuilder(self, func: rawptr[Function]) -> rawptr[IRBuilder]: return new_builder(self.Pool, func) - # ---- 全局变量 ---- + # ---- globals ---- def NewGlobal(self, mod: rawptr[LLVMModule], name: str, ty: rawptr[LLVMType]) -> rawptr[GlobalVariable]: - """创建全局变量并添加到模块""" + """Create a global variable and add it to the module.""" name_dup: str = _str_dup(self.Pool, name) gv: rawptr[GlobalVariable] = new_global_variable(self.Pool, name_dup, ty) if gv is not None: @@ -413,12 +396,12 @@ class LLVMModuleCore: def CreateGlobalString(self, mod: rawptr[LLVMModule], name: str, text: str) -> rawptr[GlobalVariable]: - """创建字符串常量全局变量""" + """Create a string-constant global variable.""" return create_global_string(self.Pool, mod, name, text) def CreateGlobalInt(self, mod: rawptr[LLVMModule], name: str, bits: int, val: int64) -> rawptr[GlobalVariable]: - """创建带初始化值的整数全局变量""" + """Create an integer global with an initializer.""" return create_global_int(self.Pool, mod, name, bits, val) def GlobalSetConstant(self, gv: rawptr[GlobalVariable], is_const: int): @@ -433,7 +416,7 @@ class LLVMModuleCore: def GlobalSetSection(self, gv: rawptr[GlobalVariable], section: str): global_set_section(gv, section) - # ---- 值创建 ---- + # ---- value creation ---- def ConstInt(self, bits: int, val: int64) -> rawptr[Value]: return const_int(self.Pool, bits, val) @@ -452,7 +435,7 @@ class LLVMModuleCore: def ConstNull(self, ty: rawptr[LLVMType], name: str) -> rawptr[Value]: return ConstNull(self.Pool, ty, name) - # ---- 打印 ---- + # ---- printing ---- def PrintValue(self, buf: str, size: sizet, val: rawptr[Value]): ValuePrint(buf, size, val, self.Pool) @@ -462,17 +445,17 @@ class LLVMModuleCore: def PrintModule(self, buf: str, size: sizet, mod: rawptr[LLVMModule]): LLVMModulePrint(buf, size, mod, self.Pool, OUTPUT_FULL) - # ---- SSA 验证 ---- + # ---- SSA verification ---- def NewVerifyResult(self, buf_size: sizet) -> rawptr[VerifyResult]: - """创建验证结果对象(buf_size 为错误消息缓冲区大小)""" + """Create a verify-result object (buf_size = error message buffer size).""" return _new_verify_result(self.Pool, buf_size) def VerifyFunction(self, func: rawptr[Function], result: rawptr[VerifyResult]) -> int: - """验证单个函数的 SSA 正确性,返回错误数(0=通过)""" + """Verify SSA correctness of one function; returns error count (0 = pass).""" return verify_function(self.Pool, func, result) def VerifyModule(self, mod: rawptr[LLVMModule], result: rawptr[VerifyResult]) -> int: - """验证模块中所有函数的 SSA 正确性,返回错误数(0=通过)""" + """Verify SSA correctness of all functions in the module; returns error count (0 = pass).""" return verify_module(self.Pool, mod, result) diff --git a/mlirlite/__module.vp b/mlirlite/__module.vp index b9d561b..85f1a65 100644 --- a/mlirlite/__module.vp +++ b/mlirlite/__module.vp @@ -19,94 +19,73 @@ from .__function import ( ) -# ============================================================ -# GlobalVariable: LLVM IR 全局变量 -# -# 持有名称 + 类型 + 初始化值 + 常量标志 + 链接类型。 -# 继承 GSListNode[GlobalVariable] 获取强类型 Next。 -# ============================================================ +# GlobalVariable: LLVM IR global variable holding name, type, initializer, constant flag +# and linkage; strong-typed Next via GSListNode[GlobalVariable]. novirtual class GlobalVariable(GSListNode[GlobalVariable]): - Name: str # 全局变量名(如 ".str" / "my_global") - Ty: rawptr[LLVMType] # 变量类型 - Initializer: rawptr # 初始化值 Value*(None = 无初始化) + Name: str # Global name (e.g. ".str" / "my_global") + Ty: rawptr[LLVMType] # Variable type + Initializer: rawptr # Initializer Value* (None = no initializer) IsConstant: int # 1=constant, 0=variable - Linkage: str # 链接类型("private"/"internal"/"external"/...) - Section: str # 段名(None = 默认) + Linkage: str # Linkage ("private"/"internal"/"external"/...) + Section: str # Section name (None = default) UnnamedAddr: int # 1=unnamed_addr, 2=local_unnamed_addr, 0=none - Align: int # 对齐字节数(0=默认对齐,如 @c.Attribute(t.attr.aligned(16))) + Align: int # Alignment in bytes (0 = default; e.g. @c.Attribute(t.attr.aligned(16))) -# ============================================================ -# NamedTypeNode: 命名结构体类型链表节点 -# -# 持有 LLVMType* 指针(必须是命名 Struct 类型)。 -# 继承 GSListNode[NamedTypeNode] 获取强类型 Next。 -# ============================================================ +# NamedTypeNode: named-struct type list node holding an LLVMType* (must be a named Struct); +# strong-typed Next via GSListNode[NamedTypeNode]. @t.NoVTable class NamedTypeNode(GSListNode[NamedTypeNode]): - Ty: rawptr[LLVMType] # 命名结构体类型指针 - IsPacked: int # 1=packed 结构体(MLIR: !llvm.struct<"name", packed (…)>,无字段填充) + Ty: rawptr[LLVMType] # Named-struct type pointer + IsPacked: int # 1 = packed struct (MLIR !llvm.struct<"name", packed (…)>; no field padding) -# ============================================================ -# DINode: DI metadata node(同形保留;MLIR 打印阶段忽略) -# -# 与 includes/llvmlite 逐名同形:Debug Info 在 MLIR 后端无对应概念,故本库 -# 仅记录模型(供 TransPyV 的 DebugInfoGen 在 llvmlite.→mlirlite. 改名后仍可 -# 编译),LLVMModulePrint **不输出**这些节点(输出会破坏 MLIR 语法)。 -# ============================================================ +# DINode: DI metadata node (model kept in sync; ignored at MLIR print time). Debug Info has no +# MLIR-backend equivalent, so this only records the model (keeping DebugInfoGen compiling) and +# LLVMModulePrint never emits these nodes (emitting them would break the MLIR syntax). @t.NoVTable class DINode(GSListNode[DINode]): Text: str # node body, e.g. "!DIFile(filename: \"a.vp\", directory: \"/x\")" -# ============================================================ -# DINamedMeta: named metadata(同形保留;MLIR 打印阶段忽略) -# ============================================================ +# DINamedMeta: named metadata (kept in sync; ignored at MLIR print time) @t.NoVTable class DINamedMeta(GSListNode[DINamedMeta]): Name: str # e.g. "llvm.dbg.cu" / "llvm.module.flags" Refs: str # reference list text, e.g. "!1" -# ============================================================ -# LLVMModule: LLVM IR 模块容器 -# -# 使用嵌入式 Head/Tail/Count 字段代替 GSList 指针, -# 避免泛型 GSList 的 __sizeof__ 返回 0 和 vtable 偏移不一致问题。 -# LLVMModulePrint 输出完整 .ll 文件内容。 -# 注意: 类名必须为 LLVMModule 而非 Module,避免与 ast.Module 命名冲突 -# ============================================================ +# LLVMModule: LLVM IR module container. Uses embedded Head/Tail/Count fields instead of a GSList +# pointer, avoiding the generic GSList's __sizeof__==0 and vtable-offset mismatch. LLVMModulePrint +# emits the full .ll content; the class must be LLVMModule (not Module) to avoid clashing with ast.Module. @t.NoVTable class LLVMModule: - Name: str # 模块名 - FuncHead: rawptr[Function] # 函数链表头 - FuncTail: rawptr[Function] # 函数链表尾 - FuncCount: sizet # 函数计数 - GlobalHead: rawptr[GlobalVariable] # 全局变量链表头 - GlobalTail: rawptr[GlobalVariable] # 全局变量链表尾 - GlobalCount: sizet # 全局变量计数 - TargetTriple: str # 目标三元组(如 "x86_64-pc-linux-gnu") - DataLayout: str # 数据布局(如 "e-m:e-p270:32:32-...") - NamedTypeHead: rawptr[NamedTypeNode] # 命名结构体类型链表头 - NamedTypeTail: rawptr[NamedTypeNode] # 命名结构体类型链表尾 - NamedTypeCount: sizet # 命名结构体类型计数 - DIMetaHead: rawptr[DINode] # DI metadata node list head(同形保留;MLIR 不打印) + Name: str # Module name + FuncHead: rawptr[Function] # Function list head + FuncTail: rawptr[Function] # Function list tail + FuncCount: sizet # Function count + GlobalHead: rawptr[GlobalVariable] # Global list head + GlobalTail: rawptr[GlobalVariable] # Global list tail + GlobalCount: sizet # Global count + TargetTriple: str # Target triple (e.g. "x86_64-pc-linux-gnu") + DataLayout: str # Data layout (e.g. "e-m:e-p270:32:32-...") + NamedTypeHead: rawptr[NamedTypeNode] # Named-struct type list head + NamedTypeTail: rawptr[NamedTypeNode] # Named-struct type list tail + NamedTypeCount: sizet # Named-struct type count + DIMetaHead: rawptr[DINode] # DI metadata node list head (kept in sync; not printed by MLIR) DIMetaTail: rawptr[DINode] # DI metadata node list tail DIMetaCount: sizet # DI node count (= next !N id) DINamedHead: rawptr[DINamedMeta] # named metadata list head DINamedTail: rawptr[DINamedMeta] # named metadata list tail DINamedCount: sizet # named metadata count - # 总计: 18 字段 * 8 = 144 字节(无 vtable;与 includes/llvmlite 同布局) + # Total: 18 fields * 8 = 144 bytes (no vtable; same layout as includes/llvmlite) - # ============================================================ - # 实例方法(对象化重构:原 module_* 顶层函数改写为方法, - # 原顶层函数保留为一行委托) - # ============================================================ + # Instance methods (objectified: the old module_* top-level functions became methods, + # kept as one-line delegates). - # ---- 链表操作 ---- + # ---- List operations ---- def add_function(self, func: rawptr[Function]): - """将函数追加到模块函数链表(O(1))""" + """Append a function to the module's function list (O(1)).""" if self is None or func is None: return func.Next = None if self.FuncHead is None: @@ -117,7 +96,7 @@ class LLVMModule: self.FuncCount += 1 def add_global(self, gv: rawptr[GlobalVariable]): - """将全局变量追加到模块全局变量链表(O(1))""" + """Append a global to the module's global list (O(1)).""" if self is None or gv is None: return gv.Next = None if self.GlobalHead is None: @@ -127,24 +106,24 @@ class LLVMModule: self.GlobalTail = gv self.GlobalCount += 1 - # ---- 目标属性 ---- + # ---- Target attributes ---- def set_target(self, triple: str): - """设置目标三元组""" + """Set the target triple.""" if self is None: return self.TargetTriple = triple def set_datalayout(self, layout: str): - """设置数据布局""" + """Set the data layout.""" if self is None: return self.DataLayout = layout - # ---- DI debug metadata(同形保留;MLIR 打印阶段忽略,仅记录模型)---- + # ---- DI debug metadata (kept in sync; ignored at MLIR print time, model only) ---- def add_di_node(self, pool: memhub.MemBuddy*, text: str) -> int: """Append a DI metadata node; return its !N id (0-based, unique), -1 on failure. - text is the node body incl. !M references built from earlier ids. - MLIR 后端无 DI 概念:节点仅登记在模型里(LLVMModulePrint 不输出), - 从而 TransPyV 的 DebugInfoGen 在改名后仍可编译、且不影响 MLIR 输出。 + text is the node body incl. !M references built from earlier ids. MLIR has no DI + concept, so the node is model-only (LLVMModulePrint does not emit it), keeping + DebugInfoGen compiling without affecting MLIR output. """ if self is None or pool is None or text is None: return -1 @@ -172,7 +151,7 @@ class LLVMModule: """Register named metadata "! = !{ }"; merge into an existing entry with the same name (refs is the brace body, e.g. "!3"). - MLIR 侧仅记录模型,不产生输出(LLVMModulePrint 不输出 DI)。 + MLIR side keeps the model only, no output (LLVMModulePrint does not emit DI). """ if self is None or pool is None or name is None: return @@ -221,28 +200,27 @@ class LLVMModule: self.DINamedTail = entry self.DINamedCount += 1 - # ---- 命名结构体类型管理 ---- + # ---- Named-struct type management ---- def add_named_type(self, pool: memhub.MemBuddy*, ty: rawptr[LLVMType]): - """登记命名结构体类型到模块(MLIR 侧仅保留模型,不产生输出) + """Register a named struct type in the module (MLIR keeps the model only, no output). - 非命名结构体(Name 为 None 或非 Struct 类型)会被忽略。 - 按 Name 去重;同名时 opaque 声明可被完整定义替换(供跨模块值类型 alloca)。 - MLIR 无独立类型定义语法:结构体类型在使用处内联为 !llvm.struct<(…)>, - 故本方法不再产生输出文本,命名类型链表(NamedTypeHead)仅作为模型保留, - 供 module_set_named_type_packed / ensure_opaque_for_type 等原有 API 使用。 + Non-named structs (None Name or non-Struct) are ignored. Dedup by Name; a full definition + replaces an opaque declaration of the same name (for cross-module value-type alloca). + MLIR has no standalone type definition: struct types are inlined at the use site, so this + emits nothing and NamedTypeHead is model-only, used by the existing APIs. """ if self is None or pool is None or ty is None: return name: str = get_struct_name(ty) if name is None: return - # 去重检查(opaque 声明可被完整定义替换) + # Dedup check (a full definition may replace an opaque declaration) cur: rawptr[NamedTypeNode] = self.NamedTypeHead cnt_dedup: int = 0 while cur is not None: - # 步数守卫:NamedTypeHead 链表成环(内存损坏/插入 bug)会导致 - # 无限遍历挂起(G3 编译 LinkedNode.prepend 挂起根因)。 - # 正常链表节点数远小于此上限,超限即链表损坏 → 立即报错终止。 + # Step guard: a cyclic NamedTypeHead list (corruption/insert bug) would hang the + # traversal forever. Normal lists are far below this cap; exceeding it means the + # list is corrupt → report and terminate immediately. cnt_dedup += 1 if cnt_dedup > 100000: stdio.printf("[ANT-LOOP] NamedTypeHead 链表疑似成环, name=%s, 遍历节点=%d, 强制终止\n", @@ -252,27 +230,26 @@ class LLVMModule: existing_name: str = get_struct_name(cur.Ty) if existing_name is not None: if string.strcmp(existing_name, name) == 0: - # 已有完整定义则无需替换 + # Already a full definition: no replacement needed if _is_opaque_struct(cur.Ty) == 0: return - # 已有 opaque,新类型也是 opaque 则不重复 + # Existing is opaque and the new type is opaque too: skip if _is_opaque_struct(ty) != 0: return - # 用完整定义替换 opaque 声明(原地替换 Ty 指针) + # Replace the opaque declaration with the full definition (patch Ty in place) cur.Ty = ty return cur = cur.Next - # 创建新节点 + # Create a new node node: rawptr[NamedTypeNode] = pool.alloc(NAMED_TYPE_NODE_SIZE) if node is None: return string.memset(node, 0, NAMED_TYPE_NODE_SIZE) node.Ty = ty node.IsPacked = 0 - # opaque 类型插入链表头部,确保在引用它的完整定义之前声明。 - # 例如 TypeRegistry{HashTable*, ...} 中,HashTable 的 opaque 声明 - # 必须在 TypeRegistry 定义之前,否则 llc 报 "use of undefined type"。 - # 完整定义追加到链表尾部,保持原有顺序。 + # Insert opaque types at the head so they are declared before definitions that reference + # them (e.g. HashTable's opaque decl must precede TypeRegistry, or llc reports + # "use of undefined type"); append full definitions at the tail to keep their order. if _is_opaque_struct(ty) != 0: node.Next = self.NamedTypeHead if self.NamedTypeHead is None: @@ -288,13 +265,13 @@ class LLVMModule: self.NamedTypeCount += 1 def _is_type_defined_in_module(self, name: str) -> int: - """检查模块中是否已定义指定名称的结构体类型,返回 1=已定义 / 0=未定义""" + """Return 1 if the module already defines a struct with this name, else 0.""" if self is None or name is None: return 0 cur: rawptr[NamedTypeNode] = self.NamedTypeHead cnt_tdm: int = 0 while cur is not None: - # 步数守卫:同上,防 NamedTypeHead 链表成环无限遍历 + # Step guard: as above, prevent infinite traversal of a cyclic NamedTypeHead cnt_tdm += 1 if cnt_tdm > 100000: stdio.printf("[TDM-LOOP] NamedTypeHead 链表疑似成环, name=%s, 强制终止\n", @@ -303,7 +280,7 @@ class LLVMModule: if cur.Ty is not None: existing_name: str = get_struct_name(cur.Ty) if existing_name is not None: - # 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。 + # Fast path: strcmp equality implies equal first chars, so skip strcmp when they differ. if existing_name[0] == name[0] and string.strcmp(existing_name, name) == 0: return 1 cur = cur.Next @@ -311,71 +288,61 @@ class LLVMModule: def ensure_opaque_for_type(self, pool: memhub.MemBuddy*, ty: rawptr[LLVMType]): - """为类型中的跨模块结构体引用添加 opaque 声明 + """Add opaque declarations for cross-module struct references in a type. - 当类型引用了 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时, - 如果该名称未在模块中定义,则添加 opaque 声明。 + When the type references Ptr(Struct(name)) with a '.' in name (SHA1-qualified) and the + name is not defined in the module, add an opaque declaration. """ _scan_type_for_cross_module_refs(pool, self, ty) - # ---- 打印 ---- + # ---- Printing ---- def PrintModule(self, buf: str, size: sizet, pool: memhub.MemBuddy*, mode: int): - """将模块序列化为 MLIR(llvm 方言) 文本写入 buf + """Serialize the module to MLIR (llvm dialect) text into buf. - 输出骨架: module { <全局> <函数> } - MLIR 没有 stub/text 二分,用注释标明模式映射: - OUTPUT_FULL → 全量;OUTPUT_STUB → 仅全局/函数声明;OUTPUT_TEXT → 仅带体函数。 - target triple / datalayout 不属于 MLIR 模块,记录但不输出(见注释与 README)。 + Skeleton: module { }. MLIR has no stub/text split, so a comment + marks the mode: OUTPUT_FULL → all; OUTPUT_STUB → global/function declarations only; + OUTPUT_TEXT → body-bearing functions only. target triple / datalayout are recorded but + not emitted (not part of an MLIR module). """ if self is None or buf is None or size == 0: return - # 清空行上下文:全局段不是"行驱动"渲染,报类型错时不该带上一函数残留的旧行 - # (行上下文由 __function.vp 的 _MlirRenderLine 赋值,见 __types.vp 的 _MlirCtxLine)。 + # Clear the line context: the global section is not line-driven, so a type error here + # must not carry a stale line from a previous function (_MlirCtxLine). MlirSetContextLine(None) - # 发布本模块的命名结构体登记表:中间文本里"只有名字"的类型(`%"name"`) - # 经它回查完整定义、展开为使用处的内联具名形态(!llvm.struct<"name", (…)>)。 - # 表随本次打印重建,打印结束(本函数末尾)即清空 —— 表内类型指针只在本次 - # 打印期间有效,清空后不再解引用(避免 pool reset 后悬空)。 + # Publish this module's named-struct registry: name-only type text (%"name") resolves + # through it to the full definition, expanded inline (!llvm.struct<"name", (…)>). The + # table is rebuilt per print and cleared at the end, so its pointers are valid only then. _MlirPublishNamedTypes(self, pool) - # 使 region 渲染草稿失效:本次打印首次用到时按当前 pool 惰性重建(见 - # _MlirGvEnsureScratch)。跨打印绝不复用(上一次的池可能已释放/被复用)。 + # Invalidate the region-render scratch: it is rebuilt lazily for the current pool on + # first use (see _MlirGvEnsureScratch) and never reused across prints (stale pool). MlirGvResetScratch() _append_cstr(buf, size, "module {\n") - # 模式说明注释(MLIR 无 stub/text 二分) + # Mode comment (MLIR has no stub/text split) if mode == OUTPUT_STUB: _append_cstr(buf, size, " // MLIR-STUB: 仅全局声明与函数声明\n") elif mode == OUTPUT_TEXT: _append_cstr(buf, size, " // MLIR-TEXT: 仅带体函数与含初值全局\n") - # target triple / datalayout:MLIR 模块不携带,由 mlir-translate/llc 阶段提供 + # target triple / datalayout: not carried by an MLIR module; supplied at mlir-translate/llc stage if self.TargetTriple is not None or self.DataLayout is not None: _append_cstr(buf, size, " // target triple / datalayout 由 mlir-translate / llc 阶段提供\n") - # 行缩进辅助缓冲(模块成员统一缩进 2 空格)。 - # 单函数/单全局的 MLIR 文本因内联具名结构体完整字段体而显著变长 - # (旧 64 KiB 会截断;截断现由 FunctionPrint/LLVMModulePrint 的截断检查 - # 响亮报错)。 - # 容量上限**受模块池的 buddy 上限约束**:模块池是 ~2 MiB 子池 - # (SUB_POOL_SIZE 2097136),其 max_order=15 ⇒ 单个 buddy 块最大 - # 32<<15 = 1 MiB;分配还要留 16 字节块头 ⇒ 单次 alloc 请求必须 - # ≤ 1 MiB − 16 = 1048560。 - # 取该上限(1048560):此前取 1048576 会被判需 order 16 而**结构性失败** - # ([MEM] alloc 失败 size=1048576 needed=1048592 order=16 max_order=15); - # 而取 256 KiB 又对个别大函数偏小(触发"输出文本超出缓冲被截断"护栏)。 - # 单函数文本因内联具名结构体完整字段体而显著变长,故按池可服务的最大单块给足; - # 若仍超出,截断由 FunctionPrint/LLVMModulePrint 的截断检查响亮报错。 + # Line-indent scratch buffer (module members indent 2 spaces). Its cap is bounded by the + # module pool's buddy limit: a ~2 MiB sub-pool (max_order=15) allows max 1 MiB per block, + # minus a 16-byte header ⇒ 1048560. MLIR text is long (inlined named-struct bodies), so use + # the largest single block the pool can serve; overflow is caught and fails loud. tmp_cap: sizet = 1048560 tmp: str = pool.alloc(tmp_cap) if tmp is None: MlirFail("MLIR 渲染: 模块缩进辅助缓冲分配失败(1048560 ≈ 1 MiB − 16 字节块头)") - # 全局变量(MLIR 无独立类型定义行:命名结构体类型在使用处内联, - # 故这里不再输出 NamedTypeHead 类型定义,仅登记供类型文本回查) + # Globals (MLIR has no standalone type-definition line: named structs are inlined at the + # use site, so NamedTypeHead definitions are not emitted here, only registered for lookup). gv: rawptr[GlobalVariable] = self.GlobalHead gv_steps: sizet = 0 while gv is not None: @@ -385,7 +352,7 @@ class LLVMModule: self.GlobalCount) stdlib.exit(1) if mode == OUTPUT_TEXT and gv.Initializer is None: - # text 模式跳过无初值的全局变量(纯声明) + # text mode skips globals with no initializer (pure declarations) gv = gv.Next continue if tmp is not None: @@ -397,7 +364,7 @@ class LLVMModule: _MlirAppendIndented(buf, size, tmp, 2, pool) gv = gv.Next - # 遍历函数(按模式过滤 declare/define) + # Walk functions (filter declare/define by mode) func: rawptr[Function] = self.FuncHead func_steps: sizet = 0 while func is not None: @@ -422,27 +389,20 @@ class LLVMModule: func = func.Next _append_cstr(buf, size, "}\n") - # 打印结束:清空命名结构体登记表(表内类型指针此后不再解引用) + # Print done: clear the named-struct registry (its type pointers are no longer dereferenced) MlirNamedTypesClear() -# ============================================================ -# _MlirPublishNamedTypes - 把模块的命名结构体链表发布给 __types.vp 的登记表 -# -# 用途:类型文本里"只有名字"的形态(中间文本 `%"sha1.Cls"`)在 MLIR 侧必须 -# 展开成使用处的内联具名体(MLIR 无定义行、且不允许无体前向声明),故需要一个 -# 名字 → 完整定义的查表设施。NamedTypeHead 由 module_add_named_type 维护 -# (同名至多一项,opaque 会被后到的完整定义就地替换),这里在模块打印开始时 -# 逐项发布;opaque 项也发布,使引用它时报"仍为 opaque"(而不是"未登记")。 -# 同时把节点的 packed 标记(NamedTypeNode.IsPacked,设置点唯一: -# module_set_named_type_packed)与类型一并登记,供渲染侧按名字回查。 -# ============================================================ +# _MlirPublishNamedTypes - publish the module's named-struct list to the __types.vp registry. +# Name-only type text (%"sha1.Cls") must expand to the inline named body, so this publishes a +# name → full-definition lookup (opaque entries too, so they report "still opaque", not +# "unregistered") plus each node's packed flag (NamedTypeNode.IsPacked) for the render side. def _MlirPublishNamedTypes(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*): - """按 NamedTypeHead 重建命名结构体登记表(打印期查表用,含 packed 标记) + """Rebuild the named-struct registry from NamedTypeHead (print-time lookup, incl. packed). - 先数一遍真实链表长度(不依赖 NamedTypeCount 字段),再按该数量精确分配槽位、 - 逐项登记 —— TPV 真实模块的具名类型可达数千个(见 README §5.9),故不做固定 - 容量假设;无具名类型时不分配。 + Count the real list length first (not trusting NamedTypeCount), then allocate exactly that + many slots and register each; real modules can have thousands of named types, so no fixed + capacity is assumed; nothing is allocated when there are none. """ if mod is None or pool is None: return @@ -467,19 +427,17 @@ def _MlirPublishNamedTypes(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*): mod.NamedTypeCount) stdlib.exit(1) if nt.Ty is not None: - # packed 标记只挂在 NamedTypeNode 上(LLVMType.Struct 无该字段), - # 设置点唯一:module_set_named_type_packed。发布时与类型一并登记。 + # The packed flag lives only on NamedTypeNode (LLVMType.Struct lacks it), set only by + # module_set_named_type_packed; register it together with the type here. MlirNamedTypeRegister(nt.Ty, nt.IsPacked) nt = nt.Next -# ============================================================ -# 工厂函数 -# ============================================================ +# Factory functions def new_module(pool: memhub.MemBuddy*, name: str) -> rawptr[LLVMModule]: - """创建一个模块""" - # 硬编码 144 字节:18 字段 * 8 字节(无 vtable,@t.NoVTable) - # 含 DI 字段(DIMetaHead/Tail/Count、DINamedHead/Tail/Count),与 llvmlite 同布局。 + """Create a module.""" + # Hardcoded 144 bytes: 18 fields * 8 bytes (no vtable, @t.NoVTable), incl. the DI fields + # (DIMetaHead/Tail/Count, DINamedHead/Tail/Count), same layout as llvmlite. ptr: rawptr[LLVMModule] = pool.alloc(144) if ptr is None: return None string.memset(ptr, 0, 144) @@ -504,21 +462,21 @@ def new_module(pool: memhub.MemBuddy*, name: str) -> rawptr[LLVMModule]: return ptr -# GlobalVariable 硬编码大小: Next(8) + Name(8) + Ty(8) + Initializer(8) + IsConstant(4) + Linkage(8) + Section(8) + UnnamedAddr(4) + Align(4) = 72 字节 +# GlobalVariable hardcoded size: Next(8)+Name(8)+Ty(8)+Initializer(8)+IsConstant(4)+Linkage(8)+Section(8)+UnnamedAddr(4)+Align(4) = 72 bytes define GV_SIZE = 72 -# NamedTypeNode 硬编码大小: Next(8) + Ty(8) + IsPacked(8) = 24 字节 +# NamedTypeNode hardcoded size: Next(8) + Ty(8) + IsPacked(8) = 24 bytes define NAMED_TYPE_NODE_SIZE = 24 -# DINode 硬编码大小: Next(8) + Text(8) = 16 字节(同 llvmlite) +# DINode hardcoded size: Next(8) + Text(8) = 16 bytes (same as llvmlite) define DI_NODE_SIZE = 16 -# DINamedMeta 硬编码大小: Next(8) + Name(8) + Refs(8) = 24 字节(同 llvmlite) +# DINamedMeta hardcoded size: Next(8) + Name(8) + Refs(8) = 24 bytes (same as llvmlite) define DI_NAMED_SIZE = 24 def new_global_variable(pool: memhub.MemBuddy*, name: str, ty: rawptr[LLVMType]) -> rawptr[GlobalVariable]: - """创建一个全局变量""" + """Create a global variable.""" ptr: rawptr[GlobalVariable] = pool.alloc(GV_SIZE) if ptr is None: return None string.memset(ptr, 0, GV_SIZE) @@ -533,15 +491,13 @@ def new_global_variable(pool: memhub.MemBuddy*, return ptr -# ============================================================ -# 链表操作(嵌入式 Head/Tail/Count,O(1) 追加) -# ============================================================ +# List operations (embedded Head/Tail/Count, O(1) append) def module_add_function(mod: rawptr[LLVMModule], func: rawptr[Function]): mod.add_function(func) def module_get_func_head(mod: rawptr[LLVMModule]) -> rawptr[Function]: - """获取模块函数链表头(跨模块访问器,绕 stub 字段访问陷阱)""" + """Return the module's function list head (cross-module accessor bypassing the stub-field trap).""" if mod is None: return None return mod.FuncHead @@ -559,52 +515,39 @@ def module_set_datalayout(mod: rawptr[LLVMModule], layout: str): mod.set_datalayout(layout) -# ============================================================ -# DI debug metadata(同形保留;MLIR 打印阶段忽略,仅记录模型) -# ============================================================ +# DI debug metadata (kept in sync; ignored at MLIR print time, model only) def module_add_di_node(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*, text: str) -> int: - """Append a DI metadata node; return its !N id (-1 on failure)""" + """Append a DI metadata node; return its !N id (-1 on failure).""" return mod.add_di_node(pool, text) def module_add_named_metadata(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*, name: str, refs: str): - """Register named metadata "! = !{ }" """ + """Register named metadata "! = !{ }".""" mod.add_named_metadata(pool, name, refs) -# ============================================================ -# 命名结构体类型管理 -# ============================================================ +# Named-struct type management -# ============================================================ -# _is_opaque_struct - 检查类型是否是 opaque 结构体 -# -# opaque 结构体特征: Struct 类型,fcount == -1(哨兵标记,跨模块前向 -# 声明,仅名称无字段定义)。通过 Struct(pool, None, -1, name) 创建, -# IR 输出为 %"name" = type opaque。注意区分空结构体(fcount==0, -# fields==None,无字段类)——它是合法完整定义(= type {},可 alloca)。 -# 用于在 module_add_named_type 中区分 opaque 声明和完整定义, -# 确保 opaque 声明插入链表头部(在使用之前声明)。 -# -# Returns: 1=是 opaque / 0=否 -# ============================================================ +# _is_opaque_struct - check whether a type is an opaque struct. +# An opaque struct is a Struct with fcount == -1 (sentinel for a cross-module forward declaration, +# name only), created by Struct(pool, None, -1, name). Must be distinguished from an empty struct +# (fcount==0, fields==None), which is a valid full definition ({} type, allocatable). +# Returns: 1 = opaque / 0 = not. def _is_opaque_struct(ty: rawptr[LLVMType]) -> int: - """检查类型是否是 opaque 结构体 + """Return 1 if the type is an opaque struct, else 0. - opaque 是跨模块前向声明(仅名称无字段定义),由 - _scan_type_for_cross_module_refs 创建,fcount=-1 哨兵标记。 - 空结构体(fcount==0, fields==None,无字段类)是合法完整定义 - (LLVM {} 类型,可 alloca),必须与 opaque 区分——此前混淆导致 - 空类结构体被输出为 "= type opaque",alloca 报 Cannot allocate - unsized type。 + opaque is a cross-module forward declaration (name only) created by + _scan_type_for_cross_module_refs, marked by fcount=-1. An empty struct (fcount==0, + fields==None) is a valid full definition (LLVM {} type, allocatable) and must be kept + distinct from opaque, or alloca fails with Cannot allocate unsized type. """ if ty is None: return 0 match ty: case LLVMType.Struct(fields, fcount, name): - # opaque: fcount == -1(哨兵) + # opaque: fcount == -1 (sentinel) if fcount < 0: return 1 return 0 @@ -619,14 +562,13 @@ def module_add_named_type(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*, def module_set_named_type_packed(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*, ty: rawptr[LLVMType], is_packed: int): - """按名称设置命名结构体的 packed 标记(MLIR 输出 !llvm.struct<"name", packed (…)>)。 + """Set a named struct's packed flag by name (MLIR emits !llvm.struct<"name", packed (…)>). - 编译器解析 @c.Attribute(t.attr.packed) 后调用,使 llc 按 packed 布局 - (无字段填充)计算字段偏移/GEP。MLIR 侧由打印期登记表携带该标记 - (_MlirPublishNamedTypes 读 NamedTypeNode.IsPacked),使用处内联形态加 - `packed ` 关键字。ty 为命名 Struct 类型(按 get_struct_name 定位已注册节点); - 未找到则静默跳过(opaque/跨模块 stub 场景无布局需求)。匿名结构体 - (Name 为 None)在此直接返回 —— packed 标记无法也没有位置挂在匿名结构体上。 + Called after parsing @c.Attribute(t.attr.packed) so llc computes field offsets/GEP with the + packed layout. MLIR carries the flag via the print-time registry (read from + NamedTypeNode.IsPacked) and adds the `packed ` keyword at the use site. ty must be a named + Struct (located by get_struct_name); not found is skipped silently. Anonymous structs return + immediately — the flag has no place on them. """ if mod is None or ty is None: return @@ -647,13 +589,9 @@ def module_set_named_type_packed(mod: rawptr[LLVMModule], pool: memhub.MemBuddy* cur = cur.Next -# ============================================================ -# module_ensure_opaque_for_type - 为类型中的跨模块结构体引用添加 opaque 声明 -# -# 递归扫描 LLVMType 树,当遇到 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时, -# 如果该名称未在模块中定义,则添加 opaque 声明(%"name" = type opaque)。 -# 这解决了跨模块结构体引用导致的 llc "use of undefined type" 错误。 -# ============================================================ +# module_ensure_opaque_for_type - add opaque declarations for cross-module struct references. +# Recursively scans the LLVMType tree; for a Ptr(Struct(name)) whose '.'-containing (SHA1-qualified) +# name is undefined in the module, adds an opaque declaration, fixing llc "use of undefined type". def _is_type_defined_in_module(mod: rawptr[LLVMModule], name: str) -> int: return mod._is_type_defined_in_module(name) @@ -662,11 +600,11 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], ty: rawptr[LLVMType], depth: int = 0): - """递归扫描类型树,为跨模块结构体引用添加 opaque 声明""" + """Recursively scan the type tree and add opaque declarations for cross-module struct refs.""" if ty is None or mod is None or pool is None: return - # 深度守卫:自引用结构体(链表/树节点,如 Node{...; Ptr(Node) next}) - # 会无限递归栈溢出,与其他递归(TypePrint/DeepCopyType)一致设上限 + # Depth guard: a self-referential struct (list/tree node, e.g. Node{...; Ptr(Node) next}) + # would recurse forever; cap the depth like other recursions (TypePrint/DeepCopyType). if depth >= 100: return match ty: @@ -675,18 +613,18 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy*, match pointee: case LLVMType.Struct(sfields, sfcount, sname): if sname is not None: - # 检查是否为跨模块引用(名称包含 '.') + # Check for a cross-module reference (name contains '.') if string.strchr(sname, 46) is not None: - # 检查是否已在模块中定义 + # Check whether it is already defined in the module if _is_type_defined_in_module(mod, sname) == 0: - # 创建 opaque 类型并添加到模块 - # fcount=-1 哨兵标记 opaque(区别于空结构体 {}, - # 后者 fcount==0 是合法完整定义,可 alloca) + # Create an opaque type and add it to the module; fcount=-1 marks + # opaque (vs. empty {} struct, whose fcount==0 is a valid + # full definition and can be alloc'd). opaque_ty: rawptr[LLVMType] = Struct(pool, None, -1, sname) if opaque_ty is not None: module_add_named_type(mod, pool, opaque_ty) - # 递归扫描结构体字段中的跨模块引用 - # 例如 TypeRegistry{HashTable*, MemManager*} 中的 HashTable 引用 + # Recurse into struct fields for cross-module refs + # (e.g. HashTable in TypeRegistry{HashTable*, MemManager*}). if sfields is not None: fld: rawptr[ParamNode] = sfields fld_steps: sizet = 0 @@ -702,7 +640,7 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy*, case _: _scan_type_for_cross_module_refs(pool, mod, pointee, depth + 1) case LLVMType.Struct(sfields2, sfcount2, sname2): - # 顶层结构体(非指针):递归扫描字段 + # Top-level struct (non-pointer): recurse into fields if sfields2 is not None: fld2: rawptr[ParamNode] = sfields2 fld2_steps: sizet = 0 @@ -728,53 +666,43 @@ def module_ensure_opaque_for_type(mod: rawptr[LLVMModule], mod.ensure_opaque_for_type(pool, ty) -# ============================================================ -# 全局变量属性设置 -# ============================================================ +# Global variable attribute setters def global_set_constant(gv: rawptr[GlobalVariable], is_const: int): - """设置全局变量是否为常量""" + """Set whether the global is a constant.""" if gv is None: return gv.IsConstant = is_const def global_set_linkage(gv: rawptr[GlobalVariable], linkage: str): - """设置链接类型 (private/internal/external/... )""" + """Set the linkage (private/internal/external/...).""" if gv is None: return gv.Linkage = linkage def global_set_unnamed_addr(gv: rawptr[GlobalVariable], level: int): - """设置 unnamed_addr 属性 (0=none, 1=unnamed_addr, 2=local_unnamed_addr)""" + """Set the unnamed_addr attribute (0=none, 1=unnamed_addr, 2=local_unnamed_addr).""" if gv is None: return gv.UnnamedAddr = level def global_set_section(gv: rawptr[GlobalVariable], section: str): - """设置段名""" + """Set the section name.""" if gv is None: return gv.Section = section -# ============================================================ -# 输出模式常量 -# ============================================================ -define OUTPUT_FULL = 0# 完整 IR(stub + text) -define OUTPUT_STUB = 1# 仅声明(header + 类型定义 + declare + external global) -define OUTPUT_TEXT = 2# 仅代码(define + global 带初值) +# Output mode constants +define OUTPUT_FULL = 0# Full IR (stub + text) +define OUTPUT_STUB = 1# Declarations only (header + type defs + declare + external global) +define OUTPUT_TEXT = 2# Code only (define + global with initializer) -# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast)。 -# 任何一条链(函数/全局/命名类型/结构体字段)的 Next 被越界写破坏成环时, -# 遍历会无限循环导致 tpv.exe 挂起(builtins.vp dump 卡死根因排查)。 -# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位" -# (对齐 memhub.self_check 的 max_steps 哲学)。 +# dump sentinel: max traversal steps for IR lists (cycle detection, fail-fast). If any chain's +# Next is corrupted into a cycle, traversal would hang tpv.exe; exceeding the cap prints the list +# name and exits(1), turning a hang into a deterministic error (like memhub.self_check max_steps). define IR_DUMP_CHAIN_LIMIT = 100000 _MlirModOutTrunc: int = 0 -# ============================================================ -# LLVMModulePrint: 将模块序列化为 .ll 文本 -# -# mode: OUTPUT_FULL=完整, OUTPUT_STUB=仅声明, OUTPUT_TEXT=仅代码 -# ============================================================ +# LLVMModulePrint: serialize the module to .ll text; mode: OUTPUT_FULL / OUTPUT_STUB / OUTPUT_TEXT. def LLVMModulePrint(buf: str, size: sizet, mod: rawptr[LLVMModule], pool: memhub.MemBuddy*, mode: int): @@ -786,34 +714,23 @@ def LLVMModulePrint(buf: str, size: sizet, MlirFail("MLIR 渲染: 模块文本超出输出缓冲被截断(需增大 dump_ir 缓冲)") -# ============================================================ -# 全局变量渲染(MLIR) -# -# 形态: -# llvm.mlir.global internal constant @.str("hello") : !llvm.array<6 x i8> -# llvm.mlir.global @counter(0 : i32) : i32 -# llvm.mlir.global external @x : i32 -# linkage/constant/unnamed_addr/section 映射为对应关键字/属性。 -# 初值有两条路径(判定见 _MlirGlobalInitNeedsRegion): -# * 属性路径:整数字面量、浮点、cstring、字符串字面量、纯标量数组; -# * region 路径:根级裸 null/zeroinitializer(属性形态 `(zeroinitializer)` -# 被 mlir-opt 拒绝)、结构体/vtable、含符号引用的聚合、指针型初值 → -# 空值括号 + `: 类型 {` 内的 initializer region -# (叶子 op + undef + insertvalue 链 + return)。 -# 两条路径都覆盖不到的形态一律 MlirFail **响亮报错**,绝不静默输出错误 MLIR。 -# ============================================================ +# Global variable rendering (MLIR); linkage/constant/unnamed_addr/section map to keywords/attributes. +# Initializers take two paths (see _MlirGlobalInitNeedsRegion): attribute path (integer/float +# literals, cstring, string literal, pure scalar array) or region path (root-level bare null/ +# zeroinitializer, struct/vtable, symbol-referencing aggregates, pointer initializers → empty value +# parens + initializer region). Any form neither path covers fails loud via MlirFail. def _MlirGlobalLine(buf: str, size: sizet, pool: memhub.MemBuddy*, gv: rawptr[GlobalVariable], stub: int): - """渲染一条 llvm.mlir.global(stub=1 时输出 external 声明形态)""" + """Render one llvm.mlir.global (stub=1 emits the external declaration form).""" if gv is None: return _append_cstr(buf, size, "llvm.mlir.global ") - # 链接类型 + # Linkage if stub != 0: _append_cstr(buf, size, "external ") elif gv.Linkage is not None: - # external + 初值是非法 IR,去掉 external + # external + initializer is illegal IR, so drop external if gv.Initializer is not None and string.strcmp(gv.Linkage, "external") == 0: pass else: @@ -825,10 +742,10 @@ def _MlirGlobalLine(buf: str, size: sizet, _append_cstr(buf, size, "unnamed_addr ") elif gv.UnnamedAddr == 2: _append_cstr(buf, size, "local_unnamed_addr ") - # constant / 普通 global(MLIR 中非 constant 无需关键字) + # constant / plain global (non-constant needs no keyword in MLIR) if gv.IsConstant == 1: _append_cstr(buf, size, "constant ") - # 名字 + # Name _append_cstr(buf, size, "@") if gv.Name is not None: if _ll_name_needs_quote(gv.Name) != 0: @@ -837,8 +754,8 @@ def _MlirGlobalLine(buf: str, size: sizet, _append_cstr(buf, size, "\"") else: _append_cstr(buf, size, gv.Name) - # 初值:聚合/含符号引用/根级裸零值形态改用 initializer region - # (见 _MlirGlobalInitNeedsRegion) + # Initializer: aggregates / symbol references / root-level bare zero use the region path + # (see _MlirGlobalInitNeedsRegion). region: int = 0 if stub == 0 and gv.Initializer is not None: region = _MlirGlobalInitNeedsRegion(gv.Initializer) @@ -847,21 +764,15 @@ def _MlirGlobalLine(buf: str, size: sizet, _MlirGlobalInit(buf, size, pool, gv) _append_cstr(buf, size, ")") else: - # region 形态:值括号为空(初值由 region 内的 insertvalue 链构造) + # Region form: empty value parens (the initializer is built by insertvalue in the region) _append_cstr(buf, size, "()") else: - # 无初值(stub=1 的 external 声明 / 无初值全局):**值括号必须保留** - # ⚠ mlir-opt 22.1.8 实测:`llvm.mlir.global external @x : i32` 报 - # "expected '('";`llvm.mlir.global external @x() : i32` exit 0。 + # No initializer (stub=1 external decl / global without one): the value parens MUST be kept + # — mlir-opt 22.1.8 rejects `@x : i32` ("expected '('") but accepts `@x() : i32`. _append_cstr(buf, size, "()") - # 属性字典(section / alignment):**必须在类型之前**(值之后)输出。 - # ⚠ mlir-opt 22.1.8 实测确立(违反即解析失败): - # * 正确 = `@"s"("ab\00") {section = "data", alignment = 16 : i64} : !llvm.array<3 x i8>` - # (exit 0); - # * 错误 = 字典放在**类型之后**(`… : !llvm.array<3 x i8> {section = "data"}` - # 被当成新 op,报 "Dialect `' not found for custom op 'section'"); - # * `unnamed_addr` 是**关键字**(见上,在名字之前输出),**不能**放进字典 - # (放进字典报 "attribute 'unnamed_addr' occurs more than once")。 + # Attribute dict (section / alignment) MUST be emitted before the type (after the value); + # mlir-opt 22.1.8 fails to parse otherwise (a dict after the type is read as a new op). + # `unnamed_addr` is a keyword (emitted before the name) and must not go in the dict. if stub == 0 and (gv.Section is not None or gv.Align > 0): _append_cstr(buf, size, " {") if gv.Section is not None: @@ -876,14 +787,14 @@ def _MlirGlobalLine(buf: str, size: sizet, viperlib.snprintf(ab, 48, "alignment = %d : i64", gv.Align) _append_cstr(buf, size, ab) _append_cstr(buf, size, "}") - # 类型 + # Type _append_cstr(buf, size, " : ") ty_buf: str = pool.alloc(4096) if ty_buf is not None: ty_buf[0] = '\0' TypePrint(ty_buf, 4096, gv.Ty, pool) _append_cstr(buf, size, ty_buf) - # initializer region(顶层聚合/指针初值:叶子 op + undef + insertvalue 链 + return) + # initializer region (top-level aggregate/pointer initializer: leaf op + undef + insertvalue chain + return) if region != 0: _append_cstr(buf, size, " {\n") _MlirGlobalInitRegion(buf, size, pool, gv) @@ -893,7 +804,7 @@ def _MlirGlobalLine(buf: str, size: sizet, def _MlirGlobalInit(buf: str, size: sizet, pool: memhub.MemBuddy*, gv: rawptr[GlobalVariable]): - """渲染全局初值(MLIR attribute 文本)""" + """Render a global initializer (MLIR attribute text).""" x: str = gv.Initializer if x is None: return @@ -901,31 +812,28 @@ def _MlirGlobalInit(buf: str, size: sizet, _MlirCStringToMlir(buf, size, x, pool) return if x[0] == '"': - # 已是字符串字面量 + # Already a string literal _append_cstr(buf, size, x) return if x[0] == '[': - # 整型/浮点数组: [i8 1, i8 2] → [1 : i8, 2 : i8] - # ⚠ 实测 mlir-opt 22.1.8: - # - `dense<[...] : !llvm.array<…>>` **非法**("elements literal must be - # a shaped type":dense 只接受 builtin shaped 类型 tensor/vector); - # - `[1, 2]`(元素无类型注解)仅在元素类型等于默认 i64 时可用; - # - 正确形态 = 数组属性直接放进 value 括号,元素带 `值 : 类型` 注解。 - # 全局类型由 _MlirGlobalLine 在括号后统一输出,此处不再重复。 + # Integer/float array: [i8 1, i8 2] → [1 : i8, 2 : i8]. mlir-opt 22.1.8 rejects + # dense<[...]> (dense only takes builtin shaped types) and untyped elements default to + # i64; the correct form is the array attribute in the value parens with per-element + # `value : type` annotations. The global type is emitted after the parens by _MlirGlobalLine. _MlirArrayDenseAt(buf, size, x, pool) return if string.strcmp(x, "null") == 0 or string.strcmp(x, "zeroinitializer") == 0: - # 根级裸零值现由 region 路径处理(_MlirGlobalInitNeedsRegion 判 1), - # 属性形态 `(zeroinitializer)` 已被 mlir-opt 拒绝,此处仅为兜底保留。 + # Root-level bare zero now goes through the region path (_MlirGlobalInitNeedsRegion returns + # 1); this attribute fallback is kept only as a safety net (mlir-opt rejects `(zeroinitializer)`). _append_cstr(buf, size, "zeroinitializer") return if x[0] == '@': - # 符号引用初值(顶层 @name 现由 region 路径处理,这里是属性路径的兜底) + # Symbol-reference initializer (top-level @name uses the region path; this is the fallback) _append_cstr(buf, size, x) return c0: int = x[0] if c0 == '-' or (c0 >= '0' and c0 <= '9'): - # 整数/浮点字面量: "<文本> : " + # Integer/float literal: " : " _append_cstr(buf, size, x) _append_cstr(buf, size, " : ") ty_buf: str = pool.alloc(4096) @@ -934,22 +842,20 @@ def _MlirGlobalInit(buf: str, size: sizet, TypePrint(ty_buf, 4096, gv.Ty, pool) _append_cstr(buf, size, ty_buf) return - # 属性路径未覆盖且未走 region 的残留形态:响亮报错,绝不静默输出错误 MLIR - # (结构体/vtable、含符号引用的聚合、指针型初值已由 _MlirGlobalInitRegion - # 在 region 路径渲染,不再落到这里) + # Residual form not covered by the attribute path and not routed to region: fail loud, never + # emit wrong MLIR (structs/vtables, symbol-referencing aggregates and pointer initializers are + # already handled by _MlirGlobalInitRegion on the region path). MlirFail("MLIR 渲染: 全局初值形态 v1 不支持(属性路径与 initializer region 均未覆盖)") def _MlirCStringToMlir(buf: str, size: sizet, src: str, pool: memhub.MemBuddy*): - """c"hello\\00" → "hello\\00"(仅去掉 'c' 前缀,转义内容原样保留) + """c"hello\\00" → "hello\\00" (drop only the 'c' prefix; keep escapes verbatim). - ⚠ 实测 mlir-opt 22.1.8:`llvm.mlir.global` 的字符串属性**长度必须等于** - 声明的 i8 数组长度,否则报 - "op requires an i8 array type of the length equal to that of the string attribute"。 - TPV 的字符串常量类型含尾随 NUL([N+1 x i8]),故属性里**必须保留 \\00**。 - 绝不能改为剥掉类型里的 NUL —— 那会丢掉 C 字符串终止符、破坏 ABI。 + mlir-opt 22.1.8 requires the string attribute length to equal the declared i8 array length. + TPV string constants include a trailing NUL ([N+1 x i8]), so \\00 MUST be kept — stripping it + would drop the C terminator and break the ABI. """ - p: str = src + 1 # 跳过 'c',指向 '"' + p: str = src + 1 # Skip 'c', point at '"' if p[0] != '"': MlirFail("MLIR 渲染: 全局 c 字符串初值形态非法") _append_cstr(buf, size, p) @@ -957,7 +863,7 @@ def _MlirCStringToMlir(buf: str, size: sizet, src: str, pool: memhub.MemBuddy*): def _MlirArrayDenseAt(buf: str, size: sizet, src: str, pool: memhub.MemBuddy*) -> str: - """LLVM 数组字面量 → MLIR dense 内容(嵌套递归),返回新游标""" + """LLVM array literal → MLIR dense content (nested recursive); returns the new cursor.""" if src is None or src[0] != '[': MlirFail("MLIR 渲染: 全局数组初值形态 v1 不支持") p: str = src + 1 @@ -989,9 +895,9 @@ def _MlirArrayDenseAt(buf: str, size: sizet, src: str, MlirFail("MLIR 渲染: 数组元素值缓冲分配失败") p = _mlir_read_value(p, vb, 512) _append_cstr(buf, size, vb) - # 元素必须带类型注解:MLIR 数组属性元素默认按 i64 解释, - # i8/i32/指针等元素不注解会与全局类型不匹配(实测通过形态: - # `[1 : i8, 2 : i8] : !llvm.array<2 x i8>`)。 + # Elements must carry a type annotation: MLIR array attributes default to i64, so + # i8/i32/pointer elements would mismatch the global type (working form: + # `[1 : i8, 2 : i8] : !llvm.array<2 x i8>`). _append_cstr(buf, size, " : ") mtb: str = pool.alloc(4096) if mtb is None: @@ -1007,77 +913,52 @@ def _MlirArrayDenseAt(buf: str, size: sizet, src: str, return p -# ============================================================ -# 全局初值的 initializer region 渲染(结构体/vtable 等聚合与指针初值) -# -# 实测 mlir-opt 22.1.8 通过形态(internal 与 internal constant 均可): -# llvm.mlir.global constant @vt() : !llvm.struct<(!llvm.ptr, !llvm.ptr)> { -# %v0 = llvm.mlir.addressof @"sha1.m" : !llvm.ptr -# %undef = llvm.mlir.undef : !llvm.struct<(!llvm.ptr, !llvm.ptr)> -# %s1 = llvm.insertvalue %v0, %undef[0] : !llvm.struct<(!llvm.ptr, !llvm.ptr)> -# %v1 = llvm.mlir.zero : !llvm.ptr -# %s2 = llvm.insertvalue %v1, %s1[1] : !llvm.struct<(!llvm.ptr, !llvm.ptr)> -# llvm.return %s2 : !llvm.struct<(!llvm.ptr, !llvm.ptr)> -# } -# -# 实现要点: -# * 叶子 op(addressof / zero / constant)**先集中发射**,随后 undef 与 -# insertvalue 链;叶子 op 与插入 op 分写两个缓冲,最后按此顺序拼装; -# * 每个 insertvalue 的**类型后缀始终是顶层聚合类型**,下标是"初值树 × -# 类型树"并行遍历得到的下标路径(嵌套聚合用多下标,如 [0, 1]); -# * 元素 MLIR 类型来自**全局类型树**(Struct 字段链 / Array 元素类型)经 -# TypePrint 取文本;初值文本里的类型记号只用于推进游标,不作类型依据; -# * bitcast/getelementptr 常量表达式整体视为"符号引用"——不透明指针下 -# bitcast 恒等、GEP 取首元素地址等价于 addressof,故不还原 bitcast; -# * 符号名一律经 _MlirSymRefAppend(先剥 LLVM 口径引号,再按 MLIR 口径重加); -# * 不支持的叶子形态一律 MlirFail(带原始文本),绝不静默输出错误 MLIR。 -# ============================================================ +# Global initializer region rendering (struct/vtable aggregates and pointer initializers): leaves +# (addressof/zero/constant) first, then undef + insertvalue chain, whose type suffix is always the +# top-level aggregate and whose index path comes from a parallel initializer/type walk. Element MLIR +# types come from the global type tree via TypePrint; constexpr bitcast/GEP are treated as symbol +# references; unsupported leaves fail loud. define GV_REGION_BUF_SIZE = 32768 -# 下标路径复用栈:每个递归深度一个槽(顶层 path="" → 深度 d 的路径写在槽 d)。 -# 深度上限与 _MlirGvWalk 的 64 层守卫一致,留余量取 72;每槽 512 字节足够 -# (64 层 × " , NN" ≈ 400 字节)。 +# Index-path reuse stack: one slot per recursion depth (top-level path="" → depth d in slot d). +# Depth cap matches _MlirGvWalk's 64-level guard with margin (72); 512 bytes per slot suffices. define GV_PATH_SLOT = 512 define GV_PATH_MAX = 72 -# region 渲染期状态(单线程递归渲染,逐全局重置) -_MlirGvLeafBuf: str = None # 叶子 op 缓冲 -_MlirGvInsBuf: str = None # insertvalue 链缓冲 -_MlirGvTopType: str = None # 顶层聚合 MLIR 类型文本 -_MlirGvLast: str = None # 当前节点的 SSA 名 -_MlirGvPrev: str = None # insertvalue 链前一结果 SSA 名 +# Region-render state (single-threaded recursive render, reset per global) +_MlirGvLeafBuf: str = None # Leaf op buffer +_MlirGvInsBuf: str = None # insertvalue chain buffer +_MlirGvTopType: str = None # Top-level aggregate MLIR type text +_MlirGvLast: str = None # SSA name of the current node +_MlirGvPrev: str = None # SSA name of the previous insertvalue result _MlirGvPool: memhub.MemBuddy* = None _MlirGvLeafSeq: int = 0 _MlirGvInsSeq: int = 0 -# ---- region 渲染的**复用**草稿缓冲(惰性分配一次,跨全局/跨节点共享)---- -# -# 此前每个全局、每个初值节点都从模块子分配器 alloc 新缓冲且从不释放: -# 每全局 leaf/ins 各 32 KiB、每节点 4 KiB ⇒ 结构体/vtable 全局多的模块 -# (MLIRLiteTest #12)把 ~2 MiB 子池耗尽,报 "零初值类型缓冲分配失败"。 -# region 渲染是单线程顺序的,草稿内容用完即弃 ⇒ 分配一次复用即可。 -_MlirGvSbRead1: str = None # 512B:类型前缀记号读取草稿 -_MlirGvSbRead2: str = None # 512B:叶子类型记号读取草稿 -_MlirGvSbName: str = None # 512B:符号名 / 常量表达式符号名草稿 -_MlirGvSbVal: str = None # 512B:标量初值字面量草稿 -_MlirGvTyBuf: str = None # 4096B:元素 MLIR 类型文本(zero/const) -_MlirGvNameBuf: str = None # 32B:SSA 名生成草稿 -_MlirGvPathStack: str = None # GV_PATH_MAX × GV_PATH_SLOT:下标路径复用栈 +# ---- Region-render REUSED scratch buffers (lazily allocated once, shared across globals/nodes) ---- +# Allocating new buffers per global/node exhausted the ~2 MiB sub-pool for struct/vtable-heavy +# modules; the render is single-threaded and sequential, so one allocation reused is enough. +_MlirGvSbRead1: str = None # 512B: type-prefix token read scratch +_MlirGvSbRead2: str = None # 512B: leaf type-token read scratch +_MlirGvSbName: str = None # 512B: symbol-name / constexpr symbol-name scratch +_MlirGvSbVal: str = None # 512B: scalar initializer literal scratch +_MlirGvTyBuf: str = None # 4096B: element MLIR type text (zero/const) +_MlirGvNameBuf: str = None # 32B: SSA name generation scratch +_MlirGvPathStack: str = None # GV_PATH_MAX × GV_PATH_SLOT: index-path reuse stack def MlirGvResetScratch(): - """使 region 渲染的复用草稿失效(下次 EnsureScratch 按当前池重新分配) + """Invalidate the region-render scratch so EnsureScratch reallocates from the current pool. - 草稿只在**单次 PrintModule** 内经惰性分配复用(跨全局/跨节点共享); - 打印结束由 PrintModule 调用本函数置空 ⇒ 绝不复用上一次打印的指针 - (上一次的池可能已释放,或同一地址被新池复用 → 悬空写穿)。 + The scratch is reused only within a single PrintModule (shared across globals/nodes); clearing + it at print end prevents reusing a previous print's pointers (freed or reused pool → dangling). """ global _MlirGvLeafBuf _MlirGvLeafBuf = None def _MlirGvEnsureScratch(pool: memhub.MemBuddy*) -> int: - """惰性分配本次 PrintModule 的复用草稿缓冲(跨全局/跨节点共享),返回 1=就绪""" + """Lazily allocate this PrintModule's reused scratch buffers (shared across globals/nodes); returns 1 when ready.""" global _MlirGvLeafBuf, _MlirGvInsBuf, _MlirGvTopType, _MlirGvLast, _MlirGvPrev global _MlirGvSbRead1, _MlirGvSbRead2, _MlirGvSbName, _MlirGvSbVal global _MlirGvTyBuf, _MlirGvNameBuf, _MlirGvPathStack @@ -1118,14 +999,12 @@ def _MlirGvEnsureScratch(pool: memhub.MemBuddy*) -> int: def _MlirGlobalInitNeedsRegion(x: str) -> int: - """判定全局初值是否改走 initializer region(1=region / 0=属性路径) + """Return 1 if a global initializer must go through the initializer region, else 0. - 属性路径(已实测通过,不得回退)仅覆盖: c 字符串、字符串字面量、 - 标量字面量、**纯标量**数组。 - 其余形态一律 region: **根级裸 `null` / `zeroinitializer`**、结构体 `{…}`、 - 含符号引用(@)/结构体/MLIR 类型前缀的数组、顶层裸符号引用 `@name`、 - 指针/聚合类型前缀开头的初值(如 `i8* null`、`i8* bitcast (…)`、 - `i8* getelementptr …`)。 + The attribute path (tested; do not regress) covers only c strings, string literals, scalar + literals and pure-scalar arrays. Everything else uses region: root-level bare `null`/ + `zeroinitializer`, struct `{…}`, arrays with symbol refs/structs/MLIR type prefixes, top-level + bare `@name`, and pointer/aggregate-prefixed initializers (e.g. `i8* null`, `i8* bitcast (…)`). """ if x is None or x[0] == '\0': return 0 @@ -1138,9 +1017,8 @@ def _MlirGlobalInitNeedsRegion(x: str) -> int: if x[0] == '{': return 1 if string.strcmp(x, "null") == 0 or string.strcmp(x, "zeroinitializer") == 0: - # 根级裸零值改走 region:属性形态 `(zeroinitializer)` 被 mlir-opt 拒绝 - # (实测 22.1.8: "expected attribute value");region 输出 - # `llvm.mlir.zero : <顶层类型>` + 直接 `llvm.return`。 + # Root-level bare zero uses region: the attribute form `(zeroinitializer)` is rejected by + # mlir-opt 22.1.8; region emits `llvm.mlir.zero : ` + a direct `llvm.return`. return 1 if x[0] == '@': return 1 @@ -1151,15 +1029,13 @@ def _MlirGlobalInitNeedsRegion(x: str) -> int: def _MlirGvArrayNeedsRegion(x: str) -> int: - """数组初值是否必须走 initializer region(1=region / 0=属性路径) + """Return 1 if an array initializer must use the initializer region, else 0. - 出现以下任一记号即返回 1: - - 符号引用 '@':属性路径无法表达地址引用; - - 结构体 '{':聚合元素; - - MLIR 类型前缀 '!':如嵌套数组元素的子数组前缀 `!llvm.array<…>`。 - 属性路径(_MlirArrayDenseAt)只认"元素为 `值 : 标量类型`"的稠密数组, - 内层类型前缀与子 `zeroinitializer` 都无法表达,必须交给 region 的 - insertvalue 链。扫描跳过字符串字面量,避免把 LLVM 字符串里的 '{'/'!' 误判。 + Returns 1 on any of: symbol reference '@' (attribute path cannot express addresses), struct + '{' (aggregate element), or MLIR type prefix '!' (e.g. a nested sub-array prefix). The + attribute path (_MlirArrayDenseAt) only handles dense arrays of `value : scalar type`; + inner type prefixes and sub `zeroinitializer` need the region's insertvalue chain. String + literals are skipped so '{'/'!' inside them are not misdetected. """ p: str = x while p[0] != '\0': @@ -1167,7 +1043,7 @@ def _MlirGvArrayNeedsRegion(x: str) -> int: if ch == '"': p = p + 1 while p[0] != '\0' and p[0] != '"': - # LLVM 字符串用 \xx 十六进制转义;'\' 跳过其后一字节 + # LLVM strings use \xx hex escapes; skip the byte after a '\' if p[0] == 0x5C and p[1] != '\0': p = p + 1 p = p + 1 @@ -1182,16 +1058,16 @@ def _MlirGvArrayNeedsRegion(x: str) -> int: def _MlirGlobalInitRegion(buf: str, size: sizet, pool: memhub.MemBuddy*, gv: rawptr[GlobalVariable]): - """渲染 initializer region 的体(2 空格缩进的行序列,不含最外层大括号) + """Render the initializer region body (lines indented 2 spaces, without the outer braces). - 每行都带 '\\n';调用方在末尾补 '}',模块层再统一缩进 2 空格。 + Each line ends with '\\n'; the caller appends '}' and the module layer re-indents by 2. """ global _MlirGvPool, _MlirGvLeafSeq, _MlirGvInsSeq x: str = gv.Initializer if x is None: return - # 复用缓冲(惰性分配一次,跨全局共享)——此前逐全局 alloc 且不释放, - # 结构体/vtable 全局多时耗尽模块子分配器。 + # Reused buffers (lazily allocated once, shared across globals) — allocating per global without + # freeing exhausted the module sub-allocator for struct/vtable-heavy modules. _MlirGvEnsureScratch(pool) topbuf: str = _MlirGvTopType leafbuf: str = _MlirGvLeafBuf @@ -1218,12 +1094,12 @@ def _MlirGlobalInitRegion(buf: str, size: sizet, case _: is_agg = 0 if is_agg != 0: - # insertvalue 链的起点: 顶层 undef + # Start of the insertvalue chain: top-level undef string.strcpy(prevbuf, "%undef") _MlirGvWalk(x, gv.Ty, "", 0) - # 叶子 op 集中发射,再 undef + insertvalue 链 + # Emit leaf ops first, then the undef + insertvalue chain _append_cstr(buf, size, leafbuf) if is_agg != 0: _append_cstr(buf, size, " %undef = llvm.mlir.undef : ") @@ -1241,10 +1117,10 @@ def _MlirGlobalInitRegion(buf: str, size: sizet, def _MlirGvWalk(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str: - """并行遍历"初值树 × 类型树"的一个节点,返回新游标 + """Walk one node of the initializer-tree × type-tree in parallel; returns the new cursor. - 结果 SSA 名写入 _MlirGvLast(叶子=物化名;聚合=最后一个 insertvalue 名; - 空聚合= %undef)。下标路径 path 形如 "" / "0" / "0, 1"。 + The result SSA name goes to _MlirGvLast (leaf = materialized name; aggregate = last + insertvalue name; empty aggregate = %undef). path looks like "" / "0" / "0, 1". """ if depth > 64: MlirFail("MLIR 渲染: 全局初值嵌套过深(> 64 层)") @@ -1253,23 +1129,22 @@ def _MlirGvWalk(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str: p = _mlir_ws(p) if p[0] == '\0': MlirFail("MLIR 渲染: 全局初值文本意外结束") - # 显式数组类型前缀(如字段写成 "[3 x i8] [i8 1, i8 2]")先整体读掉 + # Explicit array type prefix (e.g. "[3 x i8] [i8 1, i8 2]") is read off first if p[0] == '[' and _MlirGvIsArrayType(p) != 0: tb: str = _MlirGvSbRead1 p = _mlir_read_type(p, tb, 512) p = _mlir_ws(p) - # 显式结构体类型前缀(如 llvm.global_ctors 元素的 - # "{ i32, void ()*, i8* } { i32 65535, … }")先整体读掉;`{…}` 既可能是 - # 类型也可能是字面量,靠"其后是否紧跟另一个 `{`"区分(见 _MlirGvIsStructType) + # Explicit struct type prefix (e.g. "{ i32, void ()*, i8* } { i32 65535, … }" for a + # llvm.global_ctors element) is read off first; `{…}` may be a type or a literal, told apart + # by whether another `{` immediately follows (see _MlirGvIsStructType). if p[0] == '{' and _MlirGvIsStructType(p) != 0: stb: str = _MlirGvSbRead1 p = _mlir_read_type(p, stb, 512) p = _mlir_ws(p) - # MLIR 形态聚合类型前缀(`!llvm.array<…>` / `!llvm.struct<…>`)后紧跟聚合 - # 字面量时先整体读掉:来自上层用 mlirlite.TypePrint 生成的内层类型(如嵌套 - # 数组初始化器的子数组前缀 `!llvm.array<16 x i8>[i8 0, …]`,见 - # TranslatorGenMixin._build_array_initializer_text),上方 LLVM 形态前缀 - # 判据(_MlirGvIsArrayType/_MlirGvIsStructType)认不出 '!' 开头 + # An MLIR-form aggregate type prefix (`!llvm.array<…>` / `!llvm.struct<…>`) immediately + # followed by an aggregate literal is read off first: it comes from upper layers via + # mlirlite.TypePrint (e.g. a nested sub-array prefix `!llvm.array<16 x i8>[i8 0, …]`); the + # LLVM-form predicates above do not recognize a '!' prefix. if p[0] == '!': mtb: str = _MlirGvSbRead1 qm: str = _mlir_read_type(p, mtb, 512) @@ -1284,7 +1159,7 @@ def _MlirGvWalk(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str: def _MlirGvIsArrayType(p: str) -> int: - """判断游标处是否为 LLVM 数组类型前缀 "[N x TY]"(用于与数组字面量区分)""" + """Return 1 if the cursor is at an LLVM array type prefix "[N x TY]" (vs. an array literal).""" if p is None or p[0] != '[': return 0 q: str = p + 1 @@ -1301,13 +1176,12 @@ def _MlirGvIsArrayType(p: str) -> int: def _MlirGvIsStructType(p: str) -> int: - """判断游标处是否为 LLVM 结构体**类型前缀** `{ … } { … }`(与结构体字面量区分) + """Return 1 if the cursor is at an LLVM struct TYPE prefix `{ … } { … }` (vs. a struct literal). - `{ … }` 既是结构体类型词法、也是结构体字面量词法,单看首个花括号无法区分。 - 可靠判据:把首个花括号组**按深度配平**读完(跳过引号内文本,LLVM 字符串里 - 的 `{`/`}` 是普通字符)后,下一个非空白记号若仍是 '{',则前一花括号组是 - 类型前缀、后一花括号组才是初值;否则该花括号组本身就是初值——同一元素的 - 花括号组之后只会是 ',' / ']' / '}' / 结束,绝不会紧跟 '{'。 + `{ … }` serves as both struct-type and struct-literal syntax, so the first brace group alone + is ambiguous. Reliable test: read the first brace group with depth balancing (skipping quoted + text, where LLVM strings may contain `{`/`}`); if the next non-space token is still '{', the + first group was a type prefix and the second is the initializer, else the group is the value. """ if p is None or p[0] != '{': return 0 @@ -1341,10 +1215,10 @@ def _MlirGvIsStructType(p: str) -> int: def _MlirGvPathChild(path: str, idx: int, depth: int) -> str: - """由父下标路径与子序号拼出子下标路径(顶层 path="" → "0") + """Build a child index path from the parent path and child index (top-level "" → "0"). - 写入按递归深度分槽的复用栈(槽 = depth):祖先路径占 0..depth-1, - 本层写槽 depth、子层写槽 depth+1,互不覆盖;避免逐节点 alloc 泄漏。 + Writes to the depth-slotted reuse stack (slot = depth): ancestors occupy 0..depth-1, this + level writes slot depth, the child writes depth+1, so nothing overlaps; avoids per-node allocs. """ if depth < 0 or depth >= GV_PATH_MAX: MlirFail("MLIR 渲染: 下标路径深度越界") @@ -1357,7 +1231,7 @@ def _MlirGvPathChild(path: str, idx: int, depth: int) -> str: def _MlirGvWalkStruct(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str: - """结构体初值 { 元素, 元素, … }: 字段类型取类型树的 Struct 字段链""" + """Struct initializer { elem, elem, … }: field types come from the type tree's Struct field chain.""" fields: rawptr[ParamNode] = None match ty: case LLVMType.Struct(sf, sfc, sn): @@ -1395,7 +1269,7 @@ def _MlirGvWalkStruct(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> st def _MlirGvWalkArray(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str: - """数组初值 [ 元素, 元素, … ]: 元素类型取类型树的 Array 元素类型""" + """Array initializer [ elem, elem, … ]: element type comes from the type tree's Array element type.""" elem_ty: rawptr[LLVMType] = None match ty: case LLVMType.Array(ae, ac): @@ -1427,13 +1301,12 @@ def _MlirGvWalkArray(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str def _MlirGvIsBareValue(p: str) -> int: - """游标处是否**直接**是值(无 LLVM/MLIR 类型前缀) + """Return 1 if the cursor is DIRECTLY at a value (no LLVM/MLIR type prefix). - TPV 的根初值可能是无类型前缀的裸形态(如字符串指针全局的 - `getelementptr inbounds ([N x i8], … @.str.x …)`、对象 storage 引用 `@name`); - 聚合元素则通常是 `<类型> <值>`。类型记号从不以 `@`/数字/`-`/`.` 开头, - 也不会有 `bitcast`/`getelementptr`/`null`/`zeroinitializer` 这些名字, - 故该判据无歧义。 + TPV root initializers may be bare (e.g. a string-pointer global's + `getelementptr inbounds ([N x i8], … @.str.x …)`, or an object storage reference `@name`); + aggregate elements are usually ` `. A type token never starts with + `@`/digit/`-`/`.` nor has the names bitcast/getelementptr/null/zeroinitializer, so this is unambiguous. """ if p is None or p[0] == '\0': return 0 @@ -1454,28 +1327,28 @@ def _MlirGvIsBareValue(p: str) -> int: def _MlirGvWalkLeaf(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str: - """叶子元素 ` <值>`: 值解析为 符号引用 / null / 标量字面量 + """Leaf element ` `: the value parses to a symbol ref / null / scalar literal. - 元素 MLIR 类型不取初值文本里的类型记号,而取类型树(ty)。 - 顶层叶子(depth == 0,整棵初值即该叶子,如指针型全局)不走 insertvalue - 链;只有位于聚合内部的叶子才需要插入。 + The element MLIR type comes from the type tree (ty), not the text's type token. A top-level + leaf (depth == 0, e.g. a pointer global) skips the insertvalue chain; only leaves inside an + aggregate need insertion. """ p = _mlir_ws(p) if _MlirGvIsBareValue(p) == 0: - # 元素自带类型记号(`i8* null` / `!llvm.ptr null` 等):只用于推进游标 + # Element carries its own type token (`i8* null` / `!llvm.ptr null`): used only to advance the cursor tb: str = _MlirGvSbRead2 p = _mlir_read_type(p, tb, 512) p = _mlir_ws(p) if p[0] == '\0': MlirFail("MLIR 渲染: 叶子初值只有类型、缺少值") if p[0] == '@': - # 裸符号引用(剥 LLVM 引号 → 经 _MlirSymRefAppend 按 MLIR 口径重加) + # Bare symbol reference (strip LLVM quotes → re-add MLIR-style via _MlirSymRefAppend) nb: str = _MlirGvSbName p = _MlirGvReadSymName(p, nb, 512) _MlirGvEmitAddressOf(nb) elif string.strncmp(p, "bitcast", 7) == 0 \ or string.strncmp(p, "getelementptr", 13) == 0: - # 常量表达式整体视为符号引用(bitcast 不透明指针下恒等;GEP 取首元素地址) + # Treat the whole constexpr as a symbol reference (bitcast is identity under opaque pointers; GEP takes the first element's address) nb2: str = _MlirGvSbName p = _MlirGvScanConstExprSym(p, nb2, 512) _MlirGvEmitAddressOf(nb2) @@ -1492,7 +1365,7 @@ def _MlirGvWalkLeaf(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str: p = _mlir_read_value(p, vb, 512) _MlirGvEmitConst(vb, ty) else: - # 不支持的叶子形态: 带原始文本响亮报错,绝不静默产错 + # Unsupported leaf form: fail loud with the raw text, never emit wrong output silently emb: str = _MlirGvPool.alloc(string.strlen(p) + 128) if emb is not None: viperlib.snprintf(emb, string.strlen(p) + 128, @@ -1505,8 +1378,8 @@ def _MlirGvWalkLeaf(p: str, ty: rawptr[LLVMType], path: str, depth: int) -> str: def _MlirGvScanConstExprSym(p: str, out: str, outsz: sizet) -> str: - """扫描 `bitcast (…)` / `getelementptr … (…)`: 在括号组内提取首个 '@' 符号名 - (剥引号写入 out),返回括号组之后的游标""" + """Scan `bitcast (…)` / `getelementptr … (…)`: extract the first '@' symbol name inside the + paren group (quotes stripped, written to out); returns the cursor after the group.""" q: str = p while q[0] != '\0' and q[0] != '(': q = q + 1 @@ -1537,7 +1410,7 @@ def _MlirGvScanConstExprSym(p: str, out: str, outsz: sizet) -> str: def _MlirGvReadSymName(at: str, out: str, outsz: sizet) -> str: - """从 '@' 处读符号名,剥掉 LLVM 口径引号("…"),返回名字之后的游标""" + """Read a symbol name at '@', strip LLVM-style quotes ("…"); returns the cursor after the name.""" if at is None or at[0] != '@': MlirFail("MLIR 渲染: 期望符号引用('@')") q: str = at + 1 @@ -1564,7 +1437,7 @@ def _MlirGvReadSymName(at: str, out: str, outsz: sizet) -> str: def _MlirGvSymNameEnd(ch: int) -> int: - """裸符号名的终止字符(空白 / ',' / ')' / ']' / '}')""" + """Terminator for a bare symbol name (whitespace / ',' / ')' / ']' / '}').""" if ch == 0 or ch == 32 or ch == 9 or ch == 10 or ch == 13: return 1 if ch == ',' or ch == ')' or ch == ']' or ch == '}': @@ -1573,7 +1446,7 @@ def _MlirGvSymNameEnd(ch: int) -> int: def _MlirGvNextLeafName() -> str: - """分配下一个叶子 SSA 名(%v0, %v1, …;复用共享名缓冲,调用后立即取用)""" + """Allocate the next leaf SSA name (%v0, %v1, …; reuses the shared name buffer — use immediately).""" global _MlirGvLeafSeq ln: str = _MlirGvNameBuf viperlib.snprintf(ln, 32, "%%v%d", _MlirGvLeafSeq) @@ -1582,7 +1455,7 @@ def _MlirGvNextLeafName() -> str: def _MlirGvEmitAddressOf(name: str): - """发射叶子 op: %vN = llvm.mlir.addressof @"name" : !llvm.ptr""" + """Emit a leaf op: %vN = llvm.mlir.addressof @"name" : !llvm.ptr.""" ln: str = _MlirGvNextLeafName() _append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " ") _append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ln) @@ -1593,7 +1466,7 @@ def _MlirGvEmitAddressOf(name: str): def _MlirGvEmitZero(ty: rawptr[LLVMType]): - """发射叶子 op: %vN = llvm.mlir.zero : <元素 MLIR 类型>""" + """Emit a leaf op: %vN = llvm.mlir.zero : .""" mt: str = _MlirGvTyBuf mt[0] = '\0' TypePrint(mt, 4096, ty, _MlirGvPool) @@ -1607,7 +1480,7 @@ def _MlirGvEmitZero(ty: rawptr[LLVMType]): def _MlirGvEmitConst(val: str, ty: rawptr[LLVMType]): - """发射叶子 op: %vN = llvm.mlir.constant( : <元素 MLIR 类型>) : <同类型>""" + """Emit a leaf op: %vN = llvm.mlir.constant( : ) : .""" mt: str = _MlirGvTyBuf mt[0] = '\0' TypePrint(mt, 4096, ty, _MlirGvPool) @@ -1618,9 +1491,9 @@ def _MlirGvEmitConst(val: str, ty: rawptr[LLVMType]): _append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, ln) _append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " = llvm.mlir.constant(") _append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, val) - # i1 的 `true`/`false` 是 MLIR 的**裸布尔关键字**,不能再带类型后缀 - # (`llvm.mlir.constant(false : i1)` 报 "expected ')'");其余标量必须写 - # `值 : 类型`(不写会被当成无类型 attr,与结果类型不符)。 + # i1's `true`/`false` are MLIR bare boolean keywords and take no type suffix + # (`llvm.mlir.constant(false : i1)` errors "expected ')'"); other scalars must use + # `value : type` (otherwise they are untyped attrs and mismatch the result type). if string.strcmp(val, "true") != 0 and string.strcmp(val, "false") != 0: _append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, " : ") _append_cstr(_MlirGvLeafBuf, GV_REGION_BUF_SIZE, mt) @@ -1631,7 +1504,7 @@ def _MlirGvEmitConst(val: str, ty: rawptr[LLVMType]): def _MlirGvInsertLeaf(path: str): - """发射 insertvalue(多下标路径;类型后缀始终是顶层聚合类型)""" + """Emit an insertvalue (multi-index path; the type suffix is always the top-level aggregate type).""" global _MlirGvInsSeq _MlirGvInsSeq += 1 sn: str = _MlirGvNameBuf @@ -1653,7 +1526,7 @@ def _MlirGvInsertLeaf(path: str): def _MlirAppendIndented(dst: str, dst_size: sizet, src: str, indent: int, pool: memhub.MemBuddy*): - """把 src 的每一行前加 indent 个空格后追加到 dst(模块成员缩进)""" + """Append each line of src to dst with indent leading spaces (module member indentation).""" if dst is None or src is None: return cb: str = pool.alloc(2) @@ -1674,30 +1547,26 @@ def _MlirAppendIndented(dst: str, dst_size: sizet, src: str, p = p + 1 -# ============================================================ -# 内部辅助 -# ============================================================ -# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, -# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 -# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 +# Internal helpers +# _append_cstr write-position cache (perf: O(1) positioning for consecutive appends to the same +# buffer, avoiding O(n²) strlen from the start — the IR-serialization hot spot). Falls back to +# strlen when the buffer switches or is reset. _g_last_dst: uint64 = 0 _g_last_pos: sizet = 0 def _append_cstr(dst: str, dst_size: sizet, src: str): - """将 C 字符串 src 追加到 dst 末尾;被输出缓冲上限截断时置截断标志""" + """Append C string src to the end of dst; set the truncation flag on buffer-limit truncation.""" global _g_last_dst, _g_last_pos, _MlirModOutTrunc if dst is None or src is None: return dlen: sizet = 0 if _g_last_dst == uint64(dst): dlen = _g_last_pos - # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 + # Buffer was reset (caller reused the same address after buf[0]=0) → back to the head if dlen > 0 and dst[0] == '\0': dlen = 0 - # 缓存一致性校验:缓存结尾必须仍是当前文本末端(末端 NUL 且前一字符非 NUL)。 - # 多个模块(__module/__function 各自一份 _append_cstr 缓存)轮流写同一 buf、 - # 或 pool 复用地址残留旧内容时,缓存 pos 会过期 → 追加落到首个 NUL 之后, - # 内容"看不见"(LLvmLiteTest Test5/9 模块函数链为空根因)。校验失败回退 - # strlen 重定位;正常热路径不受影响(仍 O(1))。 + # Cache-consistency check: the cached end must still be the text end (trailing NUL, previous + # byte non-NUL). Interleaved writers or a reused pool address make the cached pos stale, so + # appends land past the first NUL and become invisible; on failure fall back to strlen. if dlen == 0: if dst[0] != '\0': dlen = string.strlen(dst) @@ -1706,8 +1575,8 @@ def _append_cstr(dst: str, dst_size: sizet, src: str): else: dlen = string.strlen(dst) _g_last_dst = uint64(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + # Unsigned underflow guard: if dlen exceeds the buffer size (corrupted/dangling buffer) + # return immediately, else remain = dst_size - dlen underflows and writes out of bounds. if dlen >= dst_size: _MlirModOutTrunc = 1 return diff --git a/mlirlite/__types.vp b/mlirlite/__types.vp index 6b4e24e..b426815 100644 --- a/mlirlite/__types.vp +++ b/mlirlite/__types.vp @@ -8,78 +8,63 @@ import stdlib from linkedlist import GSListNode -# ============================================================ -# ParamNode: 函数参数类型链表节点 -# 继承 GSListNode[ParamNode] 获取强类型 Next: ParamNode|CPtr -# Ty 用 rawptr[t\.CVoid] 前向引用,使用时转型为 (rawptr[LLVMType]) -# -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 -# ============================================================ +# ParamNode: function-parameter type list node. Strong-typed Next via GSListNode[ParamNode]; +# Ty is a rawptr forward reference, cast to LLVMType* on use. Must carry @t.NoVTable. @t.NoVTable class ParamNode(GSListNode[ParamNode]): - Ty: rawptr # LLVMType*(前向引用,使用时转型) + Ty: rawptr # LLVMType* (forward reference; cast on use) -# ============================================================ -# LLVMType: LLVM IR 类型系统(t.REnum tagged union) -# -# 变体少、无通用字段、操作通过 match 分派 —— 契合 REnum 模型。 -# 布局: { i32 __tag, } -# ============================================================ +# LLVMType: LLVM IR type system (t.REnum tagged union), layout { i32 __tag, }. +# Few variants, no common field, dispatch via match — fits the REnum model. class LLVMType(t.REnum): - # 整数类型: i1/i8/i16/i32/i64 + # Integer type: i1/i8/i16/i32/i64 class Int: Bits: int - # 指针类型: pointee* + # Pointer type: pointee* class Ptr: Pointee: rawptr[LLVMType] - # 函数类型: ret (params) + # Function type: ret (params) class Func: Ret: rawptr[LLVMType] Params: rawptr[ParamNode] PCount: int - # 数组类型: [N x elem_ty] + # Array type: [N x elem_ty] class Array: ElemTy: rawptr[LLVMType] Count: int - # 结构体类型: {ty1, ty2, ...} 或 %"name" = type {ty1, ty2, ...} - # Fields 复用 ParamNode 链表(ParamNode.Ty 即字段类型) - # Name 非 None 时为命名结构体,TypePrint 输出 %"name" 而非 {...} + # Struct type {ty1, ty2, ...} or %"name" = type {...}; Fields reuse the ParamNode list + # (ParamNode.Ty is the field type); a non-None Name makes it named (%"name" output). class Struct: Fields: rawptr[ParamNode] FCount: int Name: str - # 浮点类型: half/float/double/fp128 + # Float type: half/float/double/fp128 class Float: Bits: int # 16=half, 32=float, 64=double, 128=fp128 - # 无值类型 + # Void type class Void: pass - # 标签类型 + # Label type class Label: pass - # 元数据类型 + # Metadata type class Metadata: pass -# ============================================================ -# 内部分配辅助 -# ============================================================ +# Internal allocation helpers def _TypeSizeGuard() -> sizet: - """LLVMType 的字节尺寸;为 0 时响亮失败。 - - __sizeof__() 在布局未就绪(结构体未注册 / opaque / 字段链未填充)时被 - 算成 0,此时 alloc(0) 只会静默返回 NULL,类型对象随之丢失且无从追查。 - """ + """Byte size of LLVMType; fail loud if 0: __sizeof__() returns 0 when the layout is + not ready (unregistered/opaque struct or unfilled field chain), so alloc(0) loses the object.""" sz: sizet = LLVMType.__sizeof__() if sz == 0: stdio.printf("FATAL: mlirlite.LLVMType.__sizeof__() == 0" @@ -89,7 +74,7 @@ def _TypeSizeGuard() -> sizet: def _new_type(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: - """从 mpool 分配一个零初始化的 LLVMType""" + """Allocate a zero-initialized LLVMType from the pool.""" sz_nt: sizet = _TypeSizeGuard() ptr: rawptr[LLVMType] = pool.alloc(sz_nt) if ptr: @@ -97,9 +82,7 @@ def _new_type(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: return ptr -# ============================================================ -# 类型工厂函数 -# ============================================================ +# Type factory functions def Int1(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: ptr: rawptr[LLVMType] = _new_type(pool) if ptr is None: return None @@ -136,15 +119,13 @@ def Int64(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: def Ptr(pool: memhub.MemBuddy*, pointee: rawptr[LLVMType]) -> rawptr[LLVMType]: - """创建指针类型 Ptr(pointee) + """Create pointer type Ptr(pointee). - LLVM IR 中 **不允许 void* 指针**("pointers to void are invalid"), - 因此: - - 若 pointee == None → 回退 Ptr(Int8) - - 若 pointee 是 LLVMType.Void → 回退 Ptr(Int8)(对应 C 的 void* 语法在 TPV 中写 Ptr(t.CVoid)) - 其他类型正常构造 Ptr(pointee)。 + LLVM IR forbids void* pointers ("pointers to void are invalid"): None or + LLVMType.Void pointee falls back to Ptr(Int8) (C void* is written Ptr(t.CVoid)); + all other types build Ptr(pointee) normally. """ - # ============== 前置校验: 拦截 Void / None pointee(void* 非法)============== + # Pre-check: reject Void / None pointee (void* is illegal). safe_pointee: rawptr[LLVMType] = pointee if safe_pointee is None: safe_pointee = Int8(pool) @@ -177,7 +158,7 @@ def Void(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: def Array(pool: memhub.MemBuddy*, elem_ty: rawptr[LLVMType], count: int) -> rawptr[LLVMType]: - """创建数组类型 [count x elem_ty]""" + """Create array type [count x elem_ty].""" ptr: rawptr[LLVMType] = _new_type(pool) if ptr is None: return None c.DerefAs(ptr, LLVMType.Array(elem_ty, count)) @@ -186,11 +167,10 @@ def Array(pool: memhub.MemBuddy*, elem_ty: rawptr[LLVMType], def Struct(pool: memhub.MemBuddy*, fields: rawptr[ParamNode], fcount: int, name: str) -> rawptr[LLVMType]: - """创建结构体类型 {ty1, ty2, ...} + """Create struct type {ty1, ty2, ...}. - fields 为 ParamNode 链表头,fcount 为字段数量。 - name 非 None 时为命名结构体(如 "sha1.ClassName"),TypePrint 输出 %"name"。 - name 为 None 时为匿名结构体,TypePrint 输出 {ty1, ty2, ...}。 + fields is the ParamNode list head and fcount the field count; a non-None name + (e.g. "sha1.ClassName") yields a named struct printed as %"name", else anonymous. """ ptr: rawptr[LLVMType] = _new_type(pool) if ptr is None: return None @@ -199,7 +179,7 @@ def Struct(pool: memhub.MemBuddy*, fields: rawptr[ParamNode], def Half(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: - """创建半精度浮点类型 half (16-bit)""" + """Create half-precision float type half (16-bit).""" ptr: rawptr[LLVMType] = _new_type(pool) if ptr is None: return None c.DerefAs(ptr, LLVMType.Float(16)) @@ -207,7 +187,7 @@ def Half(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: def Float(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: - """创建单精度浮点类型 float (32-bit)""" + """Create single-precision float type float (32-bit).""" ptr: rawptr[LLVMType] = _new_type(pool) if ptr is None: return None c.DerefAs(ptr, LLVMType.Float(32)) @@ -215,7 +195,7 @@ def Float(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: def Double(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: - """创建双精度浮点类型 double (64-bit)""" + """Create double-precision float type double (64-bit).""" ptr: rawptr[LLVMType] = _new_type(pool) if ptr is None: return None c.DerefAs(ptr, LLVMType.Float(64)) @@ -223,7 +203,7 @@ def Double(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: def FP128(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: - """创建四精度浮点类型 fp128 (128-bit)""" + """Create quad-precision float type fp128 (128-bit).""" ptr: rawptr[LLVMType] = _new_type(pool) if ptr is None: return None c.DerefAs(ptr, LLVMType.Float(128)) @@ -231,21 +211,19 @@ def FP128(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: def Label(pool: memhub.MemBuddy*) -> rawptr[LLVMType]: - """创建标签类型 label""" + """Create label type label.""" ptr: rawptr[LLVMType] = _new_type(pool) if ptr is None: return None c.DerefAs(ptr, LLVMType.Label()) return ptr -# ============================================================ -# ParamNode 辅助 -# ============================================================ -# ParamNode 硬编码大小: Next(8) + Ty(8) = 16 字节 +# ParamNode helpers +# ParamNode hardcoded size: Next(8) + Ty(8) = 16 bytes define PNODE_SIZE = 16 def new_param_node(pool: memhub.MemBuddy*, ty: rawptr[LLVMType]) -> rawptr[ParamNode]: - """创建一个参数节点,Ty 设为给定类型指针""" + """Create a parameter node with Ty set to the given type pointer.""" ptr: rawptr[ParamNode] = pool.alloc(PNODE_SIZE) if ptr is None: return None string.memset(ptr, 0, PNODE_SIZE) @@ -256,7 +234,7 @@ def new_param_node(pool: memhub.MemBuddy*, ty: rawptr[LLVMType]) -> rawptr[Param def param_list_append(head: rawptr[ParamNode], tail: rawptr[ParamNode], node: rawptr[ParamNode]) -> rawptr[ParamNode]: - """将 node 追加到参数链表尾部,返回 head 和更新后的 tail""" + """Append node to the parameter list tail, returning head and updated tail.""" if node is None: return head node.Next = None if head is None: @@ -266,53 +244,41 @@ def param_list_append(head: rawptr[ParamNode], tail: rawptr[ParamNode], return node -# ============================================================ -# ParamNode 字段访问器(供其他模块绕过 stub 类型限制使用) -# -# 跨模块编译时,使用方模块只有 ParamNode 的 stub 类型, -# 导致 TransPyC 静默跳过字段访问。这些访问器在拥有完整类型定义的 -# 本模块内执行,能正确访问所有字段。 -# ============================================================ +# ParamNode field accessors (bypass the stub-type limitation): cross-module consumers +# only have the stub type, so field access is skipped; these run where the type is full. def paramnode_get_ty(node: rawptr[ParamNode]) -> rawptr[LLVMType]: - """获取参数节点的类型""" + """Return the parameter node's type.""" if node is None: return None return (rawptr[LLVMType])(node.Ty) def paramnode_get_next(node: rawptr[ParamNode]) -> rawptr[ParamNode]: - """获取链表下一个参数节点""" + """Return the next parameter node in the list.""" if node is None: return None return node.Next def paramnode_set_ty(node: rawptr[ParamNode], ty: rawptr[LLVMType]): - """设置参数节点的类型""" + """Set the parameter node's type.""" if node is None: return node.Ty = ty def paramnode_set_next(node: rawptr[ParamNode], nxt: rawptr[ParamNode]): - """设置链表下一个参数节点""" + """Set the next parameter node in the list.""" if node is None: return node.Next = nxt -# ============================================================ -# LLVMTypeCore: 类型创建核心,封装 pool 避免重复传参 -# -# 用法: -# core: rawptr[LLVMTypeCore] = LLVMTypeCore(pool) -# ty_i32: rawptr[LLVMType] = core.Int32() -# ty_ptr: rawptr[LLVMType] = core.Ptr(ty_i32) -# ty_void: rawptr[LLVMType] = core.Void() -# ============================================================ +# LLVMTypeCore: type-creation core wrapping the pool to avoid passing it every time. +# Usage: core = LLVMTypeCore(pool); core.Int32(); core.Ptr(ty_i32); core.Void(). @t.NoVTable class LLVMTypeCore: - """类型创建核心,封装 pool 避免每次创建类型都传 pool""" + """Type-creation core wrapping the pool so callers need not pass it each time.""" Pool: memhub.MemBuddy* def __init__(self, pool: memhub.MemBuddy*): @@ -368,25 +334,21 @@ class LLVMTypeCore: def NewParamNode(self, ty: rawptr[LLVMType]) -> rawptr[ParamNode]: return new_param_node(self.Pool, ty) - # ---- 链表辅助 ---- + # ---- List helpers ---- def ParamListAppend(self, head: rawptr[ParamNode], tail: rawptr[ParamNode], node: rawptr[ParamNode]) -> rawptr[ParamNode]: - """将 node 追加到参数/字段链表,返回新的 tail""" + """Append node to the parameter/field list, returning the new tail.""" return param_list_append(head, tail, node) - # ---- 打印 ---- + # ---- Printing ---- def PrintType(self, buf: str, size: sizet, ty: rawptr[LLVMType]): TypePrint(buf, size, ty, self.Pool) -# ============================================================ -# MlirFail - MLIR 渲染期"响亮报错" -# -# 本库的设计红线:v1 不支持的形态绝不静默输出错误 MLIR, -# 一律打印定位信息后立即终止(对齐 llvmlite 的 [IR-DUMP]/[IR-FAIL] fail-fast 风格)。 -# ============================================================ +# MlirFail - loud failure during MLIR rendering. Design rule: never silently emit +# wrong MLIR; always print locating info and terminate (llvmlite [IR-FAIL] fail-fast style). def MlirFail(msg: str): - """打印 MLIR 渲染错误并立即终止""" + """Print an MLIR render error and terminate immediately.""" if msg is None: stdio.printf("[MLIR-FAIL] (no message)\n") else: @@ -394,93 +356,48 @@ def MlirFail(msg: str): stdlib.exit(1) -# ============================================================ -# 渲染上下文行:类型文本转换失败时随报文一并打印 -# -# MlirFail 是即时终止,拿不到调用栈。故由渲染侧(__function.vp 的 -# _MlirRenderLine 入口)把"当前正在渲染的中间指令文本行"寄存到本模块, -# 类型转换失败报文再把它追加出来,一次定位到出错的那一行中间文本。 -# 依赖方向保持单向:本模块只存/读,不反向 import __function.vp。 -# ============================================================ +# Render context line: appended to a type-text conversion failure message. MlirFail gives no +# stack trace, so the render side (_MlirRenderLine in __function.vp) stores the in-flight IR line +# here for pinpointing it. One-way dependency: this module never imports __function.vp. _MlirCtxLine: str = None -# 类型文本渲染溢出标志:_strappend/_MlirAppendN 因缓冲上限截断时置 1, -# 由 TypePrint / MlirTypeText 在渲染后检查并响亮报错。 -# 为什么必须响亮:MLIR 把具名结构体内联为**完整字段体** -# (`!llvm.struct<"name", (13 个字段…)>`),类型文本远长于 LLVM 形态的 -# `%"name"` 引用,固定小缓冲(128/256)会被截断并产出畸形 MLIR -# (实测:alloca 行类型截断 → mlir-opt 报 "unbalanced '(' character")。 +# Type-text render overflow flag: set when _strappend/_MlirAppendN truncate at the buffer limit, +# checked by TypePrint / MlirTypeText to fail loud. MLIR inlines named structs as full field +# bodies (far longer than LLVM's %"name"), so small fixed buffers truncate and yield malformed MLIR. _MlirTypeTrunc: int = 0 def MlirSetContextLine(line: str = None): - """寄存当前正在渲染的中间文本行(无行时传 None)""" + """Store the in-flight IR text line (pass None when there is none).""" global _MlirCtxLine _MlirCtxLine = line -# ============================================================ -# 命名结构体(%"name" = type {…})在 MLIR 的表示 -# -# MLIR(llvm 方言) **没有独立类型定义语法**(故 llvmlite 的 -# `%"name" = type {…}` 定义行在 MLIR 侧**整体丢弃**,见 __module.vp 的 -# PrintModule:NamedTypeHead 只作模型保留、不产生输出),也**不允许无体 -# 前向声明**(实测 mlir-opt 22.1.8:`!llvm.struct<"x">` 报 -# "struct without a body only allowed in a recursive struct")。 -# 故唯一可表达形态是**使用处的内联具名体**: -# -# !llvm.struct<"名字", (字段类型…)> -# -# 四种形态均由 mlir-opt / mlir-translate 22.1.8 实测 exit 0(逐字照此): -# 按值 : !llvm.struct<"a.b", (i32, !llvm.ptr)> -# 特殊字符 : !llvm.struct<"01c70390f01048df.Lock[0]", (i32)> -# (名字含数字/`.`/`[`/`]` 直接作字符串字面量,**不额外转义/加引号**) -# 嵌套具名 : !llvm.struct<"A.Lock", (!llvm.struct<"A.CRIT", (i32)>)> -# 空体 : !llvm.struct<"x.Empty", ()> -# 降级证据:mlir-translate --mlir-to-llvmir 会由使用处的内联形态**自动**生成 -# 定义行(实测得 `%aa11bb22cc33dd44.Cls = type { i32, ptr }` 等,见 README §2.10)。 -# 指向命名结构体的指针仍是 !llvm.ptr(不透明指针,既有路径不变)。 -# -# 边界(一律响亮报错,绝不静默输出错误类型): -# * fcount < 0(opaque 哨兵,跨模块前向声明)⇒ MLIR 无法表达无体结构体; -# * 名字含 `"` / `\` / 控制字符 ⇒ MLIR 字符串字面量非法字符; -# * 名字为空、或具名而字段链缺失(FCount 非 0 但 Fields == None)⇒ 模型不自洽。 -# ============================================================ +# Named structs in MLIR: no standalone type definition and no bodyless forward declaration, +# so the sole form is the inline use-site body !llvm.struct<"name", (fields…)>; pointers stay +# !llvm.ptr. Opaque (fcount<0), illegal name chars, and empty/missing fields all fail loud. -# ============================================================ -# 命名结构体登记表(模块打印期由 __module.vp 发布;供"只有名字"的类型文本回查) -# -# 中间文本里的 `%"name"`(只有名字的结构体引用:手工构造 / TPV 注入)本身不含 -# 字段,而 MLIR 又不允许无体前向声明 ⇒ 只有回查**模块里已登记的完整定义**才能 -# 展开为使用处的内联具名形态。__module.vp 的 PrintModule 在打印开始时按 -# NamedTypeHead 重建本表(module_add_named_type 已保证同名节点至多一项、且 -# opaque 会被后到的完整定义就地替换),打印结束时清空 —— 表内类型指针只在 -# 当次打印期间有效,清空后不再解引用(避免 pool reset 后悬空)。 -# 表按当次模块的具名类型**实际数量**精确分配(无具名类型则零开销)。 -# ============================================================ -_MlirNamedTys: sizet* = None # 槽位数组:每槽 16 字节 = { LLVMType*, IsPacked } -_MlirNamedCount: int = 0 # 已登记项数 -_MlirNamedCap: sizet = 0 # 槽位容量(= 当次模块的具名类型数) +# Named-struct registry (published by __module.vp during printing) resolves name-only type text: +# such references carry no fields and MLIR forbids bodyless declarations, so they expand from the +# registered full definition. Rebuilt at print start, cleared at end (valid only during that print). +_MlirNamedTys: sizet* = None # Slot array: 16 bytes each = { LLVMType*, IsPacked } +_MlirNamedCount: int = 0 # Number of registered entries +_MlirNamedCap: sizet = 0 # Slot capacity (= named-type count of this module) -# 槽位布局: [0] = LLVMType*(命名结构体类型);[1] = IsPacked(1=packed)。 -# packed 标记只存在于 NamedTypeNode 上(LLVMType.Struct 变体没有该字段, -# 匿名结构体结构上无法携带),设置点唯一:module_set_named_type_packed。 -# 故登记时由 __module.vp 的 _MlirPublishNamedTypes 从节点读出后一并存入本表, -# 渲染侧(TypePrint 具名分支 / 文本→文本具名路径)只从本表取标记。 +# Slot layout: [0] = LLVMType* (named struct type); [1] = IsPacked (1=packed). The packed flag +# lives only on NamedTypeNode (LLVMType.Struct lacks it), set only by module_set_named_type_packed; +# it is read from the node and stored here so the render side can look it up by name. def _MlirNamedSlot(idx: int) -> sizet*: - """登记表槽位地址(数组基址 + idx*16)""" + """Registry slot address (array base + idx*16).""" return (sizet*)(t.CPtr(uint64(_MlirNamedTys) + uint64(idx * 16))) def MlirNamedTypesBegin(pool: memhub.MemBuddy*, capacity: sizet): - """按模块的具名结构体**实际数量**分配登记表并清空计数(模块打印开始时调用) - - 按需精确分配(TPV 真实模块里具名类型可达数千个,见 README §5.9), - capacity == 0 时不分配(无具名类型的模块零开销)。 - """ + """Allocate the registry sized to the module's actual named-struct count and clear the + counter (called at print start). Exact on-demand allocation; capacity 0 allocates nothing.""" global _MlirNamedTys, _MlirNamedCount, _MlirNamedCap _MlirNamedTys = None _MlirNamedCount = 0 @@ -494,7 +411,7 @@ def MlirNamedTypesBegin(pool: memhub.MemBuddy*, capacity: sizet): def MlirNamedTypesClear(): - """清空登记表(模块打印结束时调用;表内类型指针此后不再解引用)""" + """Clear the registry (called at print end; its type pointers are no longer dereferenced).""" global _MlirNamedTys, _MlirNamedCount, _MlirNamedCap _MlirNamedTys = None _MlirNamedCount = 0 @@ -502,13 +419,11 @@ def MlirNamedTypesClear(): def MlirNamedTypeRegister(ty: rawptr[LLVMType], is_packed: int): - """登记一个命名结构体类型及其 packed 标记(同名:完整定义覆盖 opaque 声明) + """Register a named struct type and its packed flag (same name: full def replaces opaque). - 与 module_add_named_type 的去重口径一致:opaque 声明不覆盖已有的完整定义。 - 非命名结构体(Name 为 None)忽略;超出容量(理论不可达,容量取自链表长度) - 则停止登记 —— 查不到 ⇒ 响亮报错,不静默产出错误类型。 - is_packed 取自 NamedTypeNode.IsPacked(packed 标记只在具名节点上存在, - 匿名结构体结构上无法携带,见 README §2.10)。 + Dedup matches module_add_named_type; non-named structs are ignored; overflow stops + registration (a lookup miss then fails loud, never a silently wrong type). is_packed comes + from NamedTypeNode.IsPacked (the flag exists only on named nodes). """ global _MlirNamedCount if ty is None or _MlirNamedTys is None: @@ -536,7 +451,7 @@ def MlirNamedTypeRegister(ty: rawptr[LLVMType], is_packed: int): def _MlirNamedFindSlot(name: str) -> int: - """按名字回查已登记槽位下标(未登记 / 表未发布 ⇒ -1)""" + """Look up a registered slot index by name (-1 if unregistered / not published).""" if name is None or _MlirNamedTys is None: return -1 for i in range(_MlirNamedCount): @@ -549,11 +464,11 @@ def _MlirNamedFindSlot(name: str) -> int: def _MlirNamedPackedOf(name: str) -> int: - """按名字回查登记表里的 packed 标记(未登记 / 表未发布 ⇒ 0) + """Look up the packed flag by name in the registry (0 if unregistered / not published). - TypePrint 的具名分支只有内部模型的 Name(LLVMType.Struct 无 packed 字段), - 标记经此从登记表取,与文本→文本路径同源;故 packed 只在模块打印期 - (登记表已发布)可见,独立 TypePrint 调用无表即视为非 packed。 + The TypePrint named branch only has the model's Name (LLVMType.Struct has no packed + field), so the flag comes from the registry, same source as the text-to-text path; an + independent TypePrint call without a registry treats it as non-packed. """ si: int = _MlirNamedFindSlot(name) if si < 0: @@ -563,10 +478,10 @@ def _MlirNamedPackedOf(name: str) -> int: def _MlirFailNamed(pool: memhub.MemBuddy*, reason: str, name: str): - """带类型名 + 当前中间文本行的响亮报错 + """Loud failure with the type name plus the current IR text line. - MlirFail 是即时终止、拿不到调用栈(见 README §5.8),故把定位信息备齐: - 类型名 + 当前正在渲染的中间文本行(_MlirCtxLine,由渲染侧寄存)。 + MlirFail gives no stack trace, so gather the type name and the in-flight IR line + (_MlirCtxLine, stored by the render side) for pinpointing. """ if name is None or name[0] == '\0': MlirFail(reason) @@ -588,7 +503,7 @@ def _MlirFailNamed(pool: memhub.MemBuddy*, reason: str, name: str): def _MlirNamedCheckName(pool: memhub.MemBuddy*, name: str): - """校验名字可作 MLIR 字符串字面量内容(`"` / `\\` / 控制字符 ⇒ 响亮报错)""" + """Validate the name as MLIR string-literal content (quote / backslash / control chars fail loud).""" if name is None or name[0] == '\0': MlirFail("MLIR 渲染: 命名结构体名字为空") i: sizet = 0 @@ -604,17 +519,11 @@ def _MlirNamedCheckName(pool: memhub.MemBuddy*, name: str): def _MlirNamedStructAppend(dst: str, dstsz: sizet, name: str, fields: rawptr[ParamNode], fcount: int, packed: int, pool: memhub.MemBuddy*, depth: int): - """追加 MLIR 具名结构体类型文本(追加写 dst) + """Append MLIR named-struct type text to dst. - 非 packed: `!llvm.struct<"name", (字段…)>`; - packed: `!llvm.struct<"name", packed (字段…)>` —— 实测 mlir-opt / - mlir-translate 22.1.8 exit 0,降级为 `%"name" = type <{ … }>`(见 README §2.10)。 - MLIR 只在**具名**形态下支持该关键字(匿名 `{…}` 对应 `!llvm.struct<(...)>`, - 没有关键字位);而内部模型的 packed 标记只挂在 NamedTypeNode 上 - (LLVMType.Struct 变体无该字段),匿名结构体结构上无法携带 packed。 - - opaque(fcount < 0)与"具名但字段链缺失"一律响亮报错:MLIR 无法表达无体 - 结构体,静默输出空体(`!llvm.struct<"x", ()>`)会得到**错误布局**。 + Non-packed !llvm.struct<"name", (fields…)> or packed !llvm.struct<"name", packed (fields…)> + (the keyword is only valid in the named form). opaque (fcount < 0) or named-but-fieldless + always fail loud: MLIR cannot express a bodyless struct and an empty body gives the wrong layout. """ if dst is None or dstsz == 0: return @@ -638,8 +547,8 @@ def _MlirNamedStructAppend(dst: str, dstsz: sizet, name: str, fidx: int = 0 tmp: str = pool.alloc(4096) while cur is not None: - # 悬空保护:字段链成环/内容复用时限制节点数并**响亮报错**——截断(llvmlite - # 匿名路径的 "...")会得到静默错误的布局,故具名路径不做截断。 + # Dangling guard: cap the node count and fail loud on a cyclic/reused field chain; + # truncating (like the anonymous "..." path) would give a silently wrong layout. if fidx >= 4096: _MlirFailNamed(pool, "MLIR 渲染: 命名结构体字段过多或字段链成环(> 4096)", @@ -657,14 +566,13 @@ def _MlirNamedStructAppend(dst: str, dstsz: sizet, name: str, def _MlirNamedBodyFromTable(dst: str, dstsz: sizet, name: str, pool: memhub.MemBuddy*, depth: int) -> int: - """按名字回查登记表,用**登记定义**输出具名结构体体;返回 1=已输出 + """Look up the registry by name and emit the named struct body from the registered def + (returns 1 if emitted). - MLIR 的 identified type **同名必须同体**,故具名结构体一律以模块登记定义 - (NamedTypeHead,见 _MlirPublishNamedTypes,模块内唯一真源)为准: - 类型对象/中间文本里可能只带**部分或空缺字段**(跨模块 stub 构造的占位), - 按自身字段内联会与别处的完整体冲突,mlir-opt 报 - `identified type already used with a different body`。 - 未登记 / 表未发布 / 登记项非结构体 ⇒ 返回 0(调用方按自身字段回退)。 + MLIR identified types require same name, same body, so emit the module's registered + definition (NamedTypeHead, the single source of truth): an object or text may carry only + partial/empty fields (cross-module stub placeholder), and inlining those would clash with + the full body elsewhere. Unregistered / unpublished / non-struct returns 0 (caller falls back). """ si: int = _MlirNamedFindSlot(name) if si < 0: @@ -680,29 +588,16 @@ def _MlirNamedBodyFromTable(dst: str, dstsz: sizet, name: str, return 0 -# ============================================================ -# TypePrint: 将 LLVMType 序列化为 MLIR(llvm 方言) 类型文本 -# -# 渲染规则: -# Int1/8/16/32/64 -> i1/i8/i16/i32/i64 -# Half/Float/Double/FP128 -> f16/f32/f64/f128 -# Ptr(_) -> !llvm.ptr(不透明指针;pointee 只保留在内部模型里) -# Array(elem,N) -> !llvm.array -# Struct(fields,N,name) -> !llvm.struct<(f0, f1, …)>;空 -> !llvm.struct<()> -# Func(ret,params) -> !llvm.func -# Void -> none(仅必须打印时;函数返回值位置省略) -# Label / Metadata -> v1 不支持,响亮报错(MLIR 无 label/metadata 类型) -# -# 注意:由于 builders 未改、内嵌类型一律经 _type_str → TypePrint, -# **中间指令文本里的类型已经是 MLIR 文本**(如 `load i32, !llvm.ptr %p`)。 -# pool 用于递归打印子类型时分配临时缓冲区。 -# ============================================================ +# TypePrint: serialize LLVMType to MLIR (llvm dialect) type text — Int→iN, Float→f16/f32/f64/f128, +# Ptr→!llvm.ptr (opaque; pointee stays in the model), Array→!llvm.array, Struct→!llvm.struct<(…)>, +# Func→!llvm.func, Void→none (omitted at return position); Label/Metadata fail loud. +# Embedded types already arrive as MLIR text; pool allocates temp buffers for subtypes. def TypePrint(buf: str, size: sizet, ty: rawptr[LLVMType], pool: memhub.MemBuddy*): - """将类型 ty 序列化为 MLIR 类型文本写入 buf(容量 size) + """Serialize type ty as MLIR type text into buf (capacity size). - pool 用于递归打印子类型时分配临时缓冲区。 - 溢出(类型文本超出 size)**响亮报错**:截断的类型文本会产出畸形 MLIR。 + pool allocates temp buffers for subtypes; overflow (text exceeds size) fails loud, since + truncated type text yields malformed MLIR. """ global _MlirTypeTrunc if buf is None or size == 0: @@ -715,20 +610,13 @@ def TypePrint(buf: str, size: sizet, MlirFail("MLIR 渲染: 类型文本超出输出缓冲被截断(该处缓冲过小,需按类型长度增大)") -# ============================================================ -# 类型文本 → MLIR 类型文本转换 -# -# 中间指令文本里的类型有两种来源,本函数**都接受**: -# ① MLIR 形态(常态):builders 内嵌类型经 _type_str → TypePrint 渲染, -# 已是 `i32` / `f64` / `!llvm.ptr` / `!llvm.array` / `!llvm.struct<(…)>` -# —— 以 `!` 开头者与浮点标量 `f16/f32/f64/f128` 原样直通,`iN` 走整数分支; -# ② LLVM 形态(手工构造中间文本时可能出现):`i8*` / `[4 x i32]` / `{i32, i8*}` -# / `double` / `%"name"`,由下方各分支转写为对应 MLIR 语法。 -# ============================================================ +# Type-text → MLIR type-text conversion. Accepts both sources: MLIR form (the norm, embedded by +# builders via TypePrint) passes through for '!'/f16/f32/f64/f128 and routes iN to the integer +# branch; LLVM form (i8*, [4 x i32], {i32, i8*}, double, %"name") is rewritten by the branches below. def MlirTypeText(dst: str, dstsz: sizet, src: str, pool: memhub.MemBuddy*): - """把中间文本类型(MLIR 或 LLVM 形态)规范化为 MLIR 类型文本(追加到 dst) + """Normalize an IR-text type (MLIR or LLVM form) to MLIR type text, appended to dst. - 溢出(转换结果超出 dstsz)同样响亮报错。 + Overflow (result exceeds dstsz) also fails loud. """ global _MlirTypeTrunc if dst is None or dstsz == 0 or src is None: @@ -741,10 +629,9 @@ def MlirTypeText(dst: str, dstsz: sizet, src: str, pool: memhub.MemBuddy*): def MlirTypeTextIsPointer(src: str) -> int: - """判断类型文本是否为指针,返回 1/0 + """Return 1 if the type text is a pointer, else 0. - MLIR 形态:`!llvm.ptr`(builders 内嵌类型的实际形态); - 兼容 LLVM 形态:末字符为 `*`(手工构造中间文本时可能出现)。 + MLIR form: !llvm.ptr; LLVM form compatibility: a trailing '*'. """ if src is None: return 0 @@ -760,7 +647,7 @@ def MlirTypeTextIsPointer(src: str) -> int: def _MlirSkipWs(p: str) -> str: - """跳过空白(空格/制表/换行)""" + """Skip whitespace (space/tab/newline).""" if p is None: return None while p[0] == ' ' or p[0] == '\t' or p[0] == '\n' or p[0] == '\r': @@ -769,7 +656,7 @@ def _MlirSkipWs(p: str) -> str: def _MlirTextLen(p: str) -> sizet: - """去除尾部空白后的文本长度""" + """Text length after trimming trailing whitespace.""" if p is None: return 0 n: sizet = string.strlen(p) @@ -783,7 +670,7 @@ def _MlirTextLen(p: str) -> sizet: def _MlirAppendN(dst: str, dstsz: sizet, src: str, n: sizet): - """把 src 的前 n 字节追加到 dst;被截断时置溢出标志""" + """Append the first n bytes of src to dst; set the overflow flag on truncation.""" global _MlirTypeTrunc if dst is None or src is None or n == 0: return @@ -802,7 +689,7 @@ def _MlirAppendN(dst: str, dstsz: sizet, src: str, n: sizet): def _MlirAllDigits(p: str, lt: sizet, start: sizet) -> int: - """判断 p[start..lt) 是否全为十进制数字(且非空),返回 1/0""" + """Return 1 if p[start..lt) is non-empty and all decimal digits, else 0.""" if p is None or start >= lt: return 0 i: sizet = start @@ -816,7 +703,7 @@ def _MlirAllDigits(p: str, lt: sizet, start: sizet) -> int: def _MlirTypeAppend(dst: str, dstsz: sizet, src: str, pool: memhub.MemBuddy*, depth: int): - """LLVM 类型文本 → MLIR 类型文本(递归实现,追加写 dst)""" + """LLVM type text → MLIR type text (recursive, appended to dst).""" if dst is None or dstsz == 0 or src is None: return if depth > 64: @@ -825,15 +712,13 @@ def _MlirTypeAppend(dst: str, dstsz: sizet, src: str, pool: memhub.MemBuddy*, lt: sizet = _MlirTextLen(p) if lt == 0: MlirFail("空类型文本无法转换为 MLIR 类型") - # ---- MLIR 文本直通(builders 内嵌类型已由 TypePrint 渲染为 MLIR 形态)---- + # ---- MLIR text passthrough (builders' embedded types are already rendered by TypePrint) ---- if p[0] == '!': - # 具名结构体文本(`!llvm.struct<"name", …>`):体必须与模块登记定义 - # **一致**(identified type 同名同体)。builders 期 TypePrint 可能在 - # "跨模块 stub 的 0 字段占位类型对象"上生成 `()` 体,与别处内联的完整 - # 字段体冲突 ⇒ mlir-opt 报 - # `identified type already used with a different body`。 - # 故按名字回查登记表,命中则用**登记定义**重渲;未登记(纯外在类型) - # 才原样直通。 + # Named-struct text (!llvm.struct<"name", …>): the body must match the module's + # registered definition (identified type = same name, same body). TypePrint may have + # emitted a `()` body for a cross-module stub placeholder, clashing with the full body + # elsewhere, so look up the registry by name and re-render from the registered def; + # only genuinely external types pass through unchanged. if lt >= 14 and string.strncmp(p, "!llvm.struct<\"", 14) == 0: nmb: str = pool.alloc(lt + 1) if nmb is not None: @@ -848,17 +733,16 @@ def _MlirTypeAppend(dst: str, dstsz: sizet, src: str, pool: memhub.MemBuddy*, return _MlirAppendN(dst, dstsz, p, lt) return - # 浮点标量的 MLIR 形态 f16/f32/f64/f128:TypePrint 直接输出该形态(见本文件 - # 顶部 TypePrint 渲染规则),必须原样直通;下方 half/float/double/fp128 分支 - # 只兼容手工构造的 LLVM 形态中间文本。'f' + 全数字与上述 LLVM 名不冲突 - # ("float" 次字符 'l'、"fp128" 次字符 'p' 均非数字)。 + # Float-scalar MLIR form f16/f32/f64/f128: TypePrint emits it, so pass through as-is; + # the half/float/double/fp128 branches below only cover hand-built LLVM-form text. + # ('f' + all digits cannot collide: "float"/"fp128" have a non-digit second char.) if p[0] == 'f' and _MlirAllDigits(p, lt, 1) != 0: _MlirAppendN(dst, dstsz, p, lt) return if lt == 4 and string.strncmp(p, "none", 4) == 0: _strappend(dst, dstsz, "none") return - # ---- 标量 ---- + # ---- Scalars ---- if lt == 4 and string.strncmp(p, "void", 4) == 0: _strappend(dst, dstsz, "none") return @@ -874,34 +758,34 @@ def _MlirTypeAppend(dst: str, dstsz: sizet, src: str, pool: memhub.MemBuddy*, if lt == 5 and string.strncmp(p, "fp128", 5) == 0: _strappend(dst, dstsz, "f128") return - # ---- 不透明指针:任何 '*'(含 * *、结构体指针、命名结构体指针)统一 !llvm.ptr ---- + # ---- Opaque pointer: any trailing '*' (incl. **, struct, named-struct) becomes !llvm.ptr ---- if p[lt - 1] == '*': _strappend(dst, dstsz, "!llvm.ptr") return - # ---- 整数 ---- + # ---- Integers ---- if p[0] == 'i' and _MlirAllDigits(p, lt, 1) != 0: _MlirAppendN(dst, dstsz, p, lt) return - # ---- 数组 ---- + # ---- Arrays ---- if p[0] == '[' and p[lt - 1] == ']': _MlirArrayText(dst, dstsz, p, lt, pool, depth) return - # ---- 结构体 ---- + # ---- Structs ---- if p[0] == '{' and p[lt - 1] == '}': _MlirStructText(dst, dstsz, p, lt, pool, depth) return - # ---- 命名结构体 ---- + # ---- Named structs ---- if p[0] == '%': _MlirNamedStructText(dst, dstsz, p, lt, pool, depth) return - # ---- 函数类型: ret (p0, p1) ---- + # ---- Function type: ret (p0, p1) ---- fp: sizet = _MlirFindFuncParen(p, lt) if fp != 0: _MlirFuncText(dst, dstsz, p, lt, fp, pool, depth) return - # 报文带上原始类型文本(截断到 lt)+ 当前正在渲染的中间文本行,便于一次定位: - # 若谓词/记号被误当类型,会直接看到 'sgt' 这类内容;行上下文由 - # MlirSetContextLine 寄存(见本文件顶部的 _MlirCtxLine)。 + # Include the original type text (trimmed to lt) plus the in-flight IR line for + # pinpointing: a token mistaken for a type shows up as e.g. 'sgt'; the line context is + # stored via MlirSetContextLine (_MlirCtxLine). emb: str = pool.alloc(lt + 1) if emb is not None: string.strncpy(emb, p, lt) @@ -924,7 +808,7 @@ def _MlirTypeAppend(dst: str, dstsz: sizet, src: str, pool: memhub.MemBuddy*, def _MlirArrayText(dst: str, dstsz: sizet, p: str, lt: sizet, pool: memhub.MemBuddy*, depth: int): - """[N x elem] → !llvm.array""" + """[N x elem] → !llvm.array.""" inner: str = p + 1 innerlen: sizet = lt - 2 xpos: sizet = 0 @@ -959,7 +843,7 @@ def _MlirArrayText(dst: str, dstsz: sizet, p: str, lt: sizet, def _MlirStructText(dst: str, dstsz: sizet, p: str, lt: sizet, pool: memhub.MemBuddy*, depth: int): - """{f0, f1, …} → !llvm.struct<(f0, f1, …)>;空 → !llvm.struct<()>""" + """{f0, f1, …} → !llvm.struct<(f0, f1, …)>; empty → !llvm.struct<()>.""" inner: str = p + 1 innerlen: sizet = lt - 2 _strappend(dst, dstsz, "!llvm.struct<(") @@ -974,7 +858,7 @@ def _MlirStructText(dst: str, dstsz: sizet, p: str, lt: sizet, ch = inner[i] if i == innerlen or (ch == ',' and d == 0): flen: sizet = i - start - # 去掉字段尾随空白 + # Trim the field's trailing whitespace while flen > 1 and inner[start + flen - 1] == ' ': flen -= 1 fb: str = pool.alloc(flen + 1) @@ -1001,17 +885,14 @@ def _MlirStructText(dst: str, dstsz: sizet, p: str, lt: sizet, def _MlirNamedStructText(dst: str, dstsz: sizet, p: str, lt: sizet, pool: memhub.MemBuddy*, depth: int): - """命名结构体类型文本(形如 `%"name"` / `%name`)→ 使用处的内联具名形态 + """Named-struct type text (%"name" / %name) → inline named form at the use site. - MLIR 无独立类型定义语法、不允许无体前向声明(实测 mlir-opt 22.1.8: - `!llvm.struct<"x">` 报 "struct without a body only allowed in a recursive - struct"),故名字必须回查**模块里已登记的完整定义**(登记表由 __module.vp - 的 PrintModule 按 NamedTypeHead 发布)才能展开为 - `!llvm.struct<"name", (字段…)>`;packed 标记与类型同槽登记,一并取出, - 故 packed 具名结构体在此路径输出 `!llvm.struct<"name", packed (…)>`。 - 文本里的名字可能带 LLVM 口径引号(`%"a.b"`)——剥掉 `%` 与引号后,名字 - 原样作 MLIR 字符串字面量内容(实测 `[`/`]`/数字开头无需额外转义)。 - 查不到 / 仍是 opaque / 名字非法 ⇒ 响亮报错(绝不静默输出空体错误布局)。 + MLIR has no standalone type definition and forbids bodyless forward declarations, so the + name must be looked up in the module's registered full definition (published by + __module.vp's PrintModule from NamedTypeHead) to expand to !llvm.struct<"name", (fields…)>; + the packed flag shares the slot and yields !llvm.struct<"name", packed (…)> here. Names may + carry LLVM-style quotes (%"a.b"): after stripping % and quotes the name is used verbatim as + the MLIR string-literal content. Not found / still opaque / invalid name fails loud. """ if dst is None or dstsz == 0 or p is None: return @@ -1050,7 +931,7 @@ def _MlirNamedStructText(dst: str, dstsz: sizet, p: str, lt: sizet, def _MlirNamedNameEnd(ch: int) -> int: - """裸命名结构体名(`%name`)的终止字符(空白 / 顶层分隔符)""" + """Terminator for a bare named-struct name (%name): whitespace / top-level separator.""" if ch == 0 or ch == 32 or ch == 9 or ch == 10 or ch == 13: return 1 if ch == ',' or ch == ')' or ch == ']' or ch == '}' or ch == '(' or ch == '{' or ch == '[': @@ -1059,7 +940,7 @@ def _MlirNamedNameEnd(ch: int) -> int: def _MlirFindFuncParen(p: str, lt: sizet) -> sizet: - """定位函数类型文本中顶层 ' (' 的下标,找不到返回 0""" + """Find the index of the top-level ' (' in function type text, or 0 if absent.""" d: int = 0 i: sizet = 0 while i + 1 < lt: @@ -1076,7 +957,7 @@ def _MlirFindFuncParen(p: str, lt: sizet) -> sizet: def _MlirFuncText(dst: str, dstsz: sizet, p: str, lt: sizet, fp: sizet, pool: memhub.MemBuddy*, depth: int): - """ret (p0, p1) → !llvm.func""" + """ret (p0, p1) → !llvm.func.""" ret_len: sizet = fp rbuf: str = pool.alloc(ret_len + 1) if rbuf is None: @@ -1086,7 +967,7 @@ def _MlirFuncText(dst: str, dstsz: sizet, p: str, lt: sizet, fp: sizet, _strappend(dst, dstsz, "!llvm.func<") _MlirTypeAppend(dst, dstsz, rbuf, pool, depth + 1) _strappend(dst, dstsz, " (") - # 参数区:fp+2 .. lt-1(末字符 ')') + # Parameter section: fp+2 .. lt-1 (last char is ')') if lt >= fp + 3: inner: str = p + fp + 2 innerlen: sizet = lt - fp - 3 @@ -1122,15 +1003,14 @@ def _MlirFuncText(dst: str, dstsz: sizet, p: str, lt: sizet, fp: sizet, _strappend(dst, dstsz, ")>") -# 递归深度上限:悬空/自引用的 LLVMType 树(reset 后内存被复用)会导致 -# Ptr(pointee)/Struct(fields)/Func(params) 无限递归,每次递归分配临时缓冲 -# 造成 OOM。超过 100 层即停止打印。 +# Recursion depth cap: a dangling/self-referential LLVMType tree (memory reused after reset) +# makes Ptr/Struct/Func recurse forever and OOM. Stop printing past 100 levels. define TYPEPRINT_MAX_DEPTH = 100 def _MlirTypePrintDepth(buf: str, size: sizet, ty: rawptr[LLVMType], pool: memhub.MemBuddy*, depth: int): - """带递归深度上限的 MLIR 类型打印内部实现(覆盖式写入 buf)""" + """Internal MLIR type printer with a recursion depth cap (overwrite-writes buf).""" if ty is None or buf is None or size == 0: return buf[0] = '\0' @@ -1152,8 +1032,8 @@ def _MlirTypePrintDepth(buf: str, size: sizet, else: MlirFail("TypePrint: 不支持的浮点位宽") case LLVMType.Ptr(pointee): - # MLIR 使用不透明指针:任何 pointee 一律渲染 !llvm.ptr。 - # pointee 仅保留在内部模型中,供 op 渲染(如 load 的结果类型)使用。 + # MLIR uses opaque pointers: any pointee renders as !llvm.ptr. The pointee is kept + # only in the internal model, for op rendering (e.g. a load's result type). viperlib.snprintf(buf, size, "%s", "!llvm.ptr") case LLVMType.Array(elem_ty, count): viperlib.snprintf(buf, size, "!llvm.array<%d x ", count) @@ -1163,32 +1043,27 @@ def _MlirTypePrintDepth(buf: str, size: sizet, _strappend(buf, size, tmp) _strappend(buf, size, ">") case LLVMType.Struct(fields, fcount, name): - # 命名结构体(Name 非 None):内联具名形态 !llvm.struct<"name", (…)>, - # 名字参与渲染(MLIR 无定义行,名字只在使用处出现)。 - # packed 标记不在 LLVMType 上(Struct 变体无该字段),而在 NamedTypeNode - # 上 ⇒ 由登记表按名字回查(模块打印期已发布;独立 TypePrint 无表 ⇒ 非 packed)。 - # 匿名结构体(Name 为 None):沿用 !llvm.struct<(…)>(无关键字位)。 - # 指针场景已由 Ptr → !llvm.ptr 收敛(不透明指针),不在此分支。 + # Named struct (non-None Name): inline named form !llvm.struct<"name", (…)>; the + # name appears only at the use site (MLIR has no definition line). The packed flag + # is not on LLVMType but on NamedTypeNode, so it is looked up from the registry + # (published during printing; an independent TypePrint sees non-packed). Anonymous + # struct: plain !llvm.struct<(…)>. Pointer cases converge to !llvm.ptr in the Ptr branch. if name is not None: buf[0] = '\0' - # 同名同体:登记表(模块定义的唯一真源)命中时一律以**登记定义** - # 为准 —— 类型对象自身可能只带空缺/部分字段(跨模块 stub 构造的 - # 占位)⇒ 与别处内联体冲突,mlir-opt 报 "identified type already - # used with a different body"。未登记(独立 TypePrint / 外在类型) - # 才按自身字段内联。 + # Same name, same body: on a registry hit always use the registered definition + # (the single source of truth); the object may carry empty/partial fields + # (cross-module stub placeholder) that would clash with the body elsewhere. Only + # unregistered types (independent TypePrint / external) inline their own fields. if _MlirNamedBodyFromTable(buf, size, name, pool, depth) == 0: _MlirNamedStructAppend(buf, size, name, fields, fcount, _MlirNamedPackedOf(name), pool, depth) else: _MlirStructFields(buf, size, fields, pool, depth) case LLVMType.Func(ret, params, count): - # MLIR 函数类型:!llvm.func - # 与文本→文本的 _MlirFuncText 保持同形外壳。 - # 两个实现要点: - # 1) _MlirTypePrintDepth 是**覆盖式**写入,ret 必须先落临时缓冲再 - # _strappend 进 buf,否则先写的外壳会被 ret 的打印清掉; - # 2) 不用 viperlib.snprintf 拼"字面量 + 类型文本",统一走 _strappend - # (与本文件 _MlirStructFields / _MlirFuncText 同款写法)。 + # MLIR function type !llvm.func, same shell as _MlirFuncText. + # Two key points: (1) _MlirTypePrintDepth overwrite-writes, so ret must go through a + # temp buffer before _strappend, else the printed ret wipes the shell; (2) build with + # _strappend (not snprintf) for the literal + type text. _strappend(buf, size, "!llvm.func<") rfb: str = pool.alloc(4096) if rfb is not None: @@ -1200,7 +1075,7 @@ def _MlirTypePrintDepth(buf: str, size: sizet, _pc2: int = 0 tmp3: str = pool.alloc(4096) while cur2 is not None: - # 悬空保护:参数链若被 reset 回收后内容复用,可能成环无限循环 + # Dangling guard: a parameter chain reused after reset may form an infinite cycle. _pc2 += 1 if _pc2 > 200: _strappend(buf, size, "...") @@ -1216,7 +1091,7 @@ def _MlirTypePrintDepth(buf: str, size: sizet, case LLVMType.Void(): viperlib.snprintf(buf, size, "%s", "none") case LLVMType.Label(): - # MLIR 无 label 类型(块用 ^name 引用),v1 不支持 → 响亮报错 + # MLIR has no label type (blocks use ^name references); unsupported in v1 → fail loud MlirFail("TypePrint(Label): MLIR 无 label 类型,v1 不支持") case LLVMType.Metadata(): MlirFail("TypePrint(Metadata): MLIR llvm 方言 v1 不支持 metadata 类型") @@ -1227,7 +1102,7 @@ def _MlirTypePrintDepth(buf: str, size: sizet, def _MlirStructFields(buf: str, size: sizet, fields: rawptr[ParamNode], pool: memhub.MemBuddy*, depth: int): - """渲染 !llvm.struct<(f0, f1, …)>(覆盖式写入 buf)""" + """Render !llvm.struct<(f0, f1, …)> (overwrite-writes buf).""" buf[0] = '\0' _strappend(buf, size, "!llvm.struct<(") cur: rawptr[ParamNode] = fields @@ -1235,7 +1110,7 @@ def _MlirStructFields(buf: str, size: sizet, fidx: int = 0 tmp: str = pool.alloc(4096) while cur is not None: - # 悬空保护:ParamNode 链成环时限制打印字段数,防止 OOM + # Dangling guard: cap printed fields to avoid OOM when the ParamNode chain cycles. if fidx >= 200: _strappend(buf, size, "...") break @@ -1250,16 +1125,14 @@ def _MlirStructFields(buf: str, size: sizet, _strappend(buf, size, ")>") -# ============================================================ -# 内部辅助 -# ============================================================ +# Internal helpers def _strappend(dst: str, dst_size: sizet, src: str): - """将 src 追加到 dst 末尾(不超过 dst_size);被截断时置溢出标志""" + """Append src to the end of dst (within dst_size); set the overflow flag on truncation.""" global _MlirTypeTrunc if dst is None or src is None: return dlen: sizet = string.strlen(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到 GB 外 + # Unsigned underflow guard: if dlen exceeds the buffer size (corrupted/dangling buffer) + # return immediately, else remain = dst_size - dlen underflows and writes far out of bounds. if dlen >= dst_size: _MlirTypeTrunc = 1 return @@ -1277,32 +1150,22 @@ def _strappend(dst: str, dst_size: sizet, src: str): dst[dlen + i] = '\0' -# ============================================================ -# PrintStructDefinition - 命名结构体类型定义行(MLIR 侧为空操作) -# -# LLVM IR 需要 "%"name" = type { ty1, ty2, ... } 这样的独立类型定义行; -# MLIR(llvm 方言) 没有独立类型定义语法,结构体类型在使用处内联书写 -# (!llvm.struct<(…)>),指针一律 !llvm.ptr(不透明)。 -# 因此本函数在 MLIR 后端输出空串 —— 保留同名同签名的 API 只为与 llvmlite 同形, -# 调用方(LLVMModulePrint)无需为此分叉。 -# ============================================================ +# PrintStructDefinition - named struct type definition line (a no-op on the MLIR side). +# MLIR has no standalone type definition; struct types are inlined at the use site, so this +# writes an empty string. The same-name/same-signature API is kept to match llvmlite. def PrintStructDefinition(buf: str, size: sizet, ty: rawptr[LLVMType], pool: memhub.MemBuddy*, packed: int = 0): - """MLIR 侧无类型定义行:写入空串(保留 API 同形)""" + """No type definition line on the MLIR side: write an empty string (API kept in sync).""" if buf is None or size == 0: return buf[0] = '\0' -# ============================================================ -# get_struct_name - 获取命名结构体的名称 -# -# 返回 Name 字段(如 "sha1.ClassName"),非 Struct 类型或匿名 Struct 返回 None -# 供 module_add_named_type 检查是否为命名结构体及去重 -# ============================================================ +# get_struct_name - returns the named struct's Name (e.g. "sha1.ClassName"), or None for a +# non-Struct / anonymous Struct; used by module_add_named_type to detect named structs and dedup. def get_struct_name(ty: rawptr[LLVMType]) -> str: - """获取命名结构体的名称,非命名结构体返回 None""" + """Return the named struct's name, or None if not a named struct.""" if ty is None: return None match ty: @@ -1313,7 +1176,7 @@ def get_struct_name(ty: rawptr[LLVMType]) -> str: def get_struct_fields(ty: rawptr[LLVMType]) -> rawptr[ParamNode]: - """获取结构体的字段链表(供跨模块重建 Struct 类型用)""" + """Return the struct's field list (used to rebuild Struct types across modules).""" if ty is None: return None match ty: @@ -1324,7 +1187,7 @@ def get_struct_fields(ty: rawptr[LLVMType]) -> rawptr[ParamNode]: def get_struct_fcount(ty: rawptr[LLVMType]) -> int: - """获取结构体的字段数量(供跨模块重建 Struct 类型用)""" + """Return the struct's field count (used to rebuild Struct types across modules).""" if ty is None: return 0 match ty: @@ -1334,36 +1197,24 @@ def get_struct_fcount(ty: rawptr[LLVMType]) -> int: return 0 -# ============================================================ -# DeepCopyType - 深拷贝 LLVMType 到 stdlib 区(跨文件 reset 存活) -# -# 背景: 1b 阶段(mark1 之后)新注册的结构体(泛型特化类等),其 -# struct_ty 是 pool 分配的 LLVMType,所在文件翻译完 reset_to(mark1) -# 后指针悬空。_struct_table 中的 entry.Ty 指向它,后续文件遍历 -# _struct_table(find_struct_by_type/lookup_field 等)时解引用悬空 -# 指针导致崩溃(0xC0000005)。 -# -# 本函数递归拷贝整棵 LLVMType 树到 stdlib(含 ParamNode 字段链和 -# 字符串名),保证 entry.Ty 跨文件永久存活。 -# -# 注意: 自引用类型(如 Value 含 Ptr(Value) 字段)会导致无限递归, -# 用深度上限防护(超过即返回浅拷贝副本,后续不再递归)。 -# ============================================================ +# DeepCopyType - deep-copy an LLVMType into the permanent pool so it survives cross-file reset. +# Pool-allocated struct types registered after mark1 dangle after reset_to(mark1) and crash later +# struct-table walks (0xC0000005); this copies the whole tree (ParamNode chain + name string) to +# keep entry.Ty alive. A depth cap guards self-referential types (shallow copy past the cap). define DEEPCOPY_MAX_DEPTH = 64 def DeepCopyType(pool: memhub.MemBuddy*, ty: rawptr[LLVMType]) -> rawptr[LLVMType]: - """深拷贝 LLVMType 到 stdlib 区(深度限制防自引用递归),失败返回 None""" + """Deep-copy LLVMType to the permanent pool (depth-limited against self-recursion); None on failure.""" return _DeepCopyDepth(pool, ty, 0) def _DeepCopyDepth(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], depth: int) -> rawptr[LLVMType]: - """带深度上限的深拷贝实现 + """Deep-copy implementation with a depth cap. - 分配源用传入的 pool 参数(调用方决定):传主 pool 为瞬时拷贝 - (随 mark/reset 回收),传永久 pool(CompilerFactory.get_perm_pool()) - 为跨 mark 持久拷贝(代替 stdlib.malloc,buddy 分配更快)。 + The passed pool decides the lifetime: the main pool gives a transient copy (reclaimed by + mark/reset); the permanent pool (CompilerFactory.get_perm_pool()) gives a cross-mark copy. """ if ty is None: return None @@ -1373,7 +1224,7 @@ def _DeepCopyDepth(pool: memhub.MemBuddy*, return None string.memset(new_ty, 0, sz_dc) if depth >= DEEPCOPY_MAX_DEPTH: - # 深度超限:浅拷贝 tag 后停止递归(复制当前变体但不深拷子类型) + # Depth exceeded: shallow-copy the tag and stop (copy this variant, not its subtypes). match ty: case LLVMType.Int(bits): c.DerefAs(new_ty, LLVMType.Int(bits)) @@ -1412,9 +1263,8 @@ def _DeepCopyDepth(pool: memhub.MemBuddy*, _DeepCopyParamChain(pool, fields, depth + 1), fcount, _DeepCopyStr(pool, name))) case _: - # new_ty 由 pool(MemBuddy)分配,必须用 pool.free(buddy 回收) - # 而非 stdlib.free(仅回收 malloc 块)——后者对 arena 内指针调用 - # 会损坏堆(0xC0000374 / ASan bad-free)。 + # new_ty came from pool (MemBuddy): free it with pool.free, not stdlib.free + # (which only reclaims malloc blocks and would corrupt the heap / ASan bad-free). pool.free(new_ty) return None return new_ty @@ -1422,7 +1272,7 @@ def _DeepCopyDepth(pool: memhub.MemBuddy*, def _DeepCopyParamChain(pool: memhub.MemBuddy*, head: rawptr[ParamNode], depth: int) -> rawptr[ParamNode]: - """深拷贝 ParamNode 链表到 stdlib""" + """Deep-copy the ParamNode chain to the permanent pool.""" if head is None: return None first: rawptr[ParamNode] = None @@ -1449,7 +1299,7 @@ def _DeepCopyParamChain(pool: memhub.MemBuddy*, def _DeepCopyStr(pool: memhub.MemBuddy*, s: str) -> str: - """深拷贝字符串到 stdlib""" + """Deep-copy a string to the permanent pool.""" if s is None: return None slen: sizet = string.strlen(s) diff --git a/mlirlite/__values.vp b/mlirlite/__values.vp index 90d4800..e005d3f 100644 --- a/mlirlite/__values.vp +++ b/mlirlite/__values.vp @@ -19,21 +19,21 @@ from .__types import LLVMType, TypePrint # ============================================================ @t.NoVTable class Value(GSListNode[Value]): - Ty: rawptr[LLVMType] # 值的类型 - Name: str # SSA 名("%0"/"%result")或常量文本("42"/"1.5e+00") - IsConst: int # 1=常量字面量, 0=SSA 临时值 - IntVal: int64 # 整数常量值(当 IsConst=1 时使用) - FloatVal: double # 浮点常量值(当 IsConst=1 且 Ty 为 Float 时使用) + Ty: rawptr[LLVMType] # value type + Name: str # SSA name ("%0"/"%result") or constant text ("42"/"1.5e+00") + IsConst: int # 1 = constant literal, 0 = SSA temporary + IntVal: int64 # integer constant value (used when IsConst=1) + FloatVal: double # float constant value (used when IsConst=1 and Ty is Float) # ============================================================ -# 工厂函数 +# Factory functions # ============================================================ -# Value 硬编码大小: Next(8) + Ty(8) + Name(8) + IsConst(4) + IntVal(8) + FloatVal(8) = 48 字节 +# Value hardcoded size: Next(8) + Ty(8) + Name(8) + IsConst(4) + IntVal(8) + FloatVal(8) = 48 bytes define VALUE_SIZE = 48 def new_value(pool: memhub.MemBuddy*) -> rawptr[Value]: - """从 mpool 分配一个零初始化的 Value""" + """Allocate a zero-initialized Value from mpool.""" ptr: rawptr[Value] = pool.alloc(VALUE_SIZE) if ptr: string.memset(ptr, 0, VALUE_SIZE) @@ -42,12 +42,8 @@ def new_value(pool: memhub.MemBuddy*) -> rawptr[Value]: def ConstInt(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], val: int64, name: str) -> rawptr[Value]: - """创建整数常量值 - - 参数: - ty: 整数类型(Int1/Int8/Int32/Int64) - val: 整数值 - name: 常量文本(如 "42"),由调用方提供 + """Create an integer constant value. + ty: integer type (Int1/Int8/Int32/Int64); val: integer value; name: caller-provided constant text (e.g. "42"). """ v: rawptr[Value] = new_value(pool) if v is None: return None @@ -60,7 +56,7 @@ def ConstInt(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], def ConstNull(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], name: str) -> rawptr[Value]: - """创建 null 指针常量""" + """Create a null pointer constant.""" v: rawptr[Value] = new_value(pool) if v is None: return None v.Ty = ty @@ -71,7 +67,7 @@ def ConstNull(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], def ConstZero(pool: memhub.MemBuddy*, ty: rawptr[LLVMType]) -> rawptr[Value]: - """创建 zeroinitializer 常量(零初始化聚合类型:结构体/数组)""" + """Create a zeroinitializer constant (zero-initialized aggregate: struct/array).""" v: rawptr[Value] = new_value(pool) if v is None: return None v.Ty = ty @@ -83,19 +79,10 @@ def ConstZero(pool: memhub.MemBuddy*, ty: rawptr[LLVMType]) -> rawptr[Value]: def ConstFloat(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], val: double) -> rawptr[Value]: - """创建浮点常量值 + """Create a float constant value. - 参数: - ty: 浮点类型(Half/Float/Double/FP128) - val: 浮点数值 - 名字格式化为浮点**位模式**文本(f16 → `0x%04x`,f32 → `0x%08x`, - f64 → `0x%016llx`,f128 → `0x%032x` 高低两字拼接): - MLIR 的 FloatAttr 按底层二进制位解释十六进制字面量,位模式因此无精度损失。 - 原因: %.17e 科学计数法文本经 llc 解析对部分值产生 ±1 ulp 误差 - (如 "1.10000000000000000e+01" 解析为 11.000000000000002 而非 11.0, - 导致 add_scalar[0]==11 等浮点常量比较失败)。位模式无精度损失。 - 原 %e 默认 6 位小数还会截断精度(3.1415926535897931 -> 3.141593e+00)。 - 用 CRT stdio.snprintf: viperlib 自实现 %g 有小数位反转 bug。 + ty: float type; val: float value. Name is the raw bit pattern (f16 0x%04x, f32 0x%08x, + f64 0x%016llx, f128 0x%032x hi+lo) so MLIR FloatAttr is lossless; use CRT stdio.snprintf (%g in viperlib is buggy). """ v: rawptr[Value] = new_value(pool) if v is None: return None @@ -110,24 +97,21 @@ def ConstFloat(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], name_buf: str = pool.alloc(64) if name_buf is not None: if f_bits == 16: - # half:先按 half 舍入(double → half 为 fptrunc),再取其 2 字节位模式 + # half: round to half first (double -> half is fptrunc), then take its 2-byte bit pattern h16: t.CFloat16T = t.CFloat16T(val) b16: uint16 = 0 *&b16 = *&h16 stdio.snprintf(name_buf, 64, "0x%04x", b16) elif f_bits == 32: - # f32 位模式必须取 **float 的 32 位编码**,不是 double 位模式的低 32 位 - # (3.14 的 double 是 0x40091EB851EB851F,低 32 位 0x51EB851F 并非 f32 - # 编码 0x4048F5C3)。先把值按 f32 舍入再取其 4 字节编码。 + # f32 bit pattern must be the float 32-bit encoding, not the low 32 bits of the double + # (3.14 double=0x40091EB851EB851F, low 0x51EB851F != f32 0x4048F5C3): round to f32 first. f32v: float = float(val) b32: uint32 = 0 *&b32 = *&f32v stdio.snprintf(name_buf, 64, "0x%08x", b32) elif f_bits == 128: - # quad:double → fp128 为无损扩展(fpext)。小端内存里低 64 位在前、 - # 高 64 位在后;文本按大端(高位在前)拼成 32 位十六进制。 - # 注意 c.Load 的宽度取**源指针**的 pointee,故源必须显式转成 u64* - # (否则会按 fp128 的 16 字节写入 8 字节目标 → 越界)。 + # quad: double -> fp128 is lossless (fpext); little-endian low 64 first, print big-endian + # (hi first). c.Load width follows the source pointee, so cast the source to u64* to avoid overflow. q128: t.CFloat128T = t.CFloat128T(val) q8: rawptr[uint8] = (rawptr[uint8])(&q128) q_lo: uint64 = 0 @@ -145,11 +129,8 @@ def ConstFloat(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], def SSAValue(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], name: str) -> rawptr[Value]: - """创建 SSA 临时值 - - 参数: - ty: 值类型 - name: SSA 名(如 "%0"/"%result"),由调用方提供 + """Create an SSA temporary value. + ty: value type; name: caller-provided SSA name (e.g. "%0"/"%result"). """ v: rawptr[Value] = new_value(pool) if v is None: return None @@ -161,20 +142,16 @@ def SSAValue(pool: memhub.MemBuddy*, ty: rawptr[LLVMType], # ============================================================ -# ValuePrint: 将值序列化为 MLIR 文本(仅供诊断) +# ValuePrint: serialize a value to MLIR text (diagnostics only) # -# 渲染规则: -# SSA 值 -> "%0"(MLIR 中操作数就是 SSA 名,不附带类型) -# 常量 -> "42 : i32"(MLIR attribute 文本形态) -# -# 注意: 指令渲染**不得**依赖本函数。函数体的 op 渲染走 __function.vp 的 -# 操作数解析路径(常量必须先物化为 llvm.mlir.constant/zero/addressof op)。 +# SSA value -> "%0" (operands are bare SSA names); constant -> "42 : i32" (MLIR attribute form). +# Instruction rendering MUST NOT depend on this; op rendering goes through __function.vp (constants +# must first be materialized as llvm.mlir.constant/zero/addressof ops). # ============================================================ def ValuePrint(buf: str, size: sizet, val: rawptr[Value], pool: memhub.MemBuddy*): - """将值 val 序列化为 MLIR 文本写入 buf - - pool 用于复杂类型(Array/Struct/Func)递归打印时分配临时缓冲区。 + """Serialize val into buf as MLIR text; pool is used to allocate scratch buffers for + recursive complex types (Array/Struct/Func). """ if val is None or buf is None or size == 0: return @@ -182,10 +159,10 @@ def ValuePrint(buf: str, size: sizet, if val.Name is None: return if val.IsConst == 0: - # SSA 值: 只输出 SSA 名 + # SSA value: emit only the SSA name _append_str(buf, size, val.Name) return - # 常量: "<常量文本> : " + # constant: " : " _append_str(buf, size, val.Name) _append_str(buf, size, " : ") tp: str = pool.alloc(4096) @@ -195,63 +172,62 @@ def ValuePrint(buf: str, size: sizet, # ============================================================ -# Value 字段访问器(供其他模块绕过 stub 类型限制使用) +# Value field accessors (let other modules bypass stub-type limits) # -# 跨模块编译时,使用方模块只有 Value 的 stub 类型(字段不足或偏移错误), -# 导致 TransPyC 静默跳过字段访问(不生成 GEP,不报错)。 -# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问所有字段。 +# Cross-module callers only see a stub Value (missing/wrong fields), so TransPyC silently skips +# field access; these accessors run inside this module with the full type definition. # ============================================================ def value_get_ty(val: rawptr[Value]) -> rawptr[LLVMType]: - """获取值的类型""" + """Get the value's type.""" if val is None: return None return val.Ty def value_get_next(val: rawptr[Value]) -> rawptr[Value]: - """获取链表下一个值""" + """Get the next value in the list.""" if val is None: return None return val.Next def value_set_ty(val: rawptr[Value], ty: rawptr[LLVMType]): - """设置值的类型""" + """Set the value's type.""" if val is None: return val.Ty = ty def value_set_next(val: rawptr[Value], nxt: rawptr[Value]): - """设置链表下一个值""" + """Set the next value in the list.""" if val is None: return val.Next = nxt def value_set_name(val: rawptr[Value], name: str): - """设置值的名字""" + """Set the value's name.""" if val is None: return val.Name = name def value_set_isconst(val: rawptr[Value], is_const: int): - """设置是否为常量""" + """Set whether the value is a constant.""" if val is None: return val.IsConst = is_const # ============================================================ -# 内部辅助 +# Internal helpers # ============================================================ def _append_str(dst: str, dst_size: sizet, src: str): - """将 src 追加到 dst 末尾""" + """Append src to the end of dst.""" if dst is None or src is None: return dlen: sizet = string.strlen(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + # Unsigned-underflow guard: if dlen >= buffer size (buffer corrupt/dangling) return early, + # else remain = dst_size - dlen underflows and dst[dlen+i] would write past the buffer. if dlen >= dst_size: return slen: sizet = string.strlen(src) diff --git a/mlirlite/__verify.vp b/mlirlite/__verify.vp index 226b22f..5e6858a 100644 --- a/mlirlite/__verify.vp +++ b/mlirlite/__verify.vp @@ -22,7 +22,7 @@ from .__module import LLVMModule # 4. Phi 位置: phi 指令只能出现在块首(非 phi 指令之前) # ============================================================ -# 错误码 +# Error codes define VERIFY_OK = 0 define VERIFY_ERR_NO_TERMINATOR = 1 define VERIFY_ERR_DUPLICATE_DEF = 2 @@ -31,32 +31,32 @@ define VERIFY_ERR_PHI_NOT_FIRST = 4 # ============================================================ -# NameEntry: 已定义 SSA 名条目(链表节点) +# NameEntry: defined SSA name entry (linked-list node) # -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 +# NOTE: must carry @t.NoVTable (see the Line comment in __function.vp). # ============================================================ @t.NoVTable class NameEntry(GSListNode[NameEntry]): - Name: str # SSA 名(如 "%0"/"%result") + Name: str # SSA name (e.g. "%0"/"%result") # ============================================================ -# VerifyResult: 验证结果 +# VerifyResult: verification result # ============================================================ class VerifyResult: - ErrorCount: int # 错误总数 - WarningCount: int # 警告总数 - ErrorCode: int # 第一个错误码 - ErrorBuf: str # 错误消息缓冲区 - ErrorBufSize: sizet # 缓冲区大小 + ErrorCount: int # total error count + WarningCount: int # total warning count + ErrorCode: int # first error code + ErrorBuf: str # error message buffer + ErrorBufSize: sizet # buffer size # ============================================================ -# 辅助函数 +# Helper functions # ============================================================ def _new_verify_result(pool: memhub.MemBuddy*, buf_size: sizet) -> rawptr[VerifyResult]: - """创建验证结果对象""" + """Create a VerifyResult object""" ptr: rawptr[VerifyResult] = pool.alloc(VerifyResult.__sizeof__()) if ptr is None: return None string.memset(ptr, 0, VerifyResult.__sizeof__()) @@ -72,7 +72,7 @@ def _new_verify_result(pool: memhub.MemBuddy*, def _report_error(result: rawptr[VerifyResult], code: int, msg: str): - """记录一个错误""" + """Record one error""" if result is None: return result.ErrorCount += 1 if result.ErrorCode == VERIFY_OK: @@ -82,24 +82,23 @@ def _report_error(result: rawptr[VerifyResult], code: int, msg: str): _append_cstr(result.ErrorBuf, result.ErrorBufSize, "\n") -# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, -# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 -# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 +# _append_cstr write-position cache: O(1) append into the same buffer, avoiding +# O(n^2) strlen(dst) rescans; falls back to strlen on buffer switch or reset. _g_last_dst: uint64 = 0 _g_last_pos: sizet = 0 def _append_cstr(dst: str, dst_size: sizet, src: str): - """将 src 追加到 dst 末尾""" + """Append src to the end of dst""" global _g_last_dst, _g_last_pos if dst is None or src is None: return dlen: sizet = 0 if _g_last_dst == uint64(dst): dlen = _g_last_pos - # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 + # Buffer reset (caller reuses same address after buf[0]=0) -> restart from head if dlen > 0 and dst[0] == '\0': dlen = 0 - # 缓存一致性校验(同 __module/__function 版):缓存 pos 过期会追加到首个 NUL - # 之后使内容不可见,校验失败回退 strlen;正常热路径仍 O(1)。 + # Cache-consistency check (same as __module/__function): a stale cached pos would append + # past the first NUL and hide content; fall back to strlen on failure, hot path stays O(1). if dlen == 0: if dst[0] != '\0': dlen = string.strlen(dst) @@ -108,8 +107,8 @@ def _append_cstr(dst: str, dst_size: sizet, src: str): else: dlen = string.strlen(dst) _g_last_dst = uint64(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + # Unsigned-underflow guard: if dlen exceeds the buffer size (corrupted/dangling buffer), + # return; otherwise remain = dst_size - dlen underflows and writes out of bounds. if dlen >= dst_size: return slen: sizet = string.strlen(src) @@ -124,31 +123,31 @@ def _append_cstr(dst: str, dst_size: sizet, src: str): # ============================================================ -# SSA 名表操作 +# SSA name-table operations # ============================================================ def _new_name_table(pool: memhub.MemBuddy*) -> rawptr[GSList[NameEntry]]: - """创建空名表""" + """Create an empty name table""" gsl: rawptr[GSList[NameEntry]] = pool.alloc(24) # GSList: Head+Tail+Count if gsl is None: return None string.memset(gsl, 0, 24) return gsl -# NameEntry 硬编码大小: Next(8) + Name(8) = 16 字节 +# NameEntry hardcoded size: Next(8) + Name(8) = 16 bytes define NENTRY_SIZE = 16 def _name_table_add(table: rawptr[GSList[NameEntry]], pool: memhub.MemBuddy*, name: str) -> int: - """添加名到表。返回 1=重复, 0=成功添加""" + """Add a name to the table. Returns 1=duplicate, 0=added""" if table is None or name is None: return 0 - # 检查重复 + # Check for duplicates cur: rawptr[NameEntry] = table.Head while cur is not None: if string.strcmp(cur.Name, name) == 0: return 1 cur = cur.Next - # 添加新条目 + # Add new entry entry: rawptr[NameEntry] = pool.alloc(NENTRY_SIZE) if entry is None: return 0 string.memset(entry, 0, NENTRY_SIZE) @@ -159,7 +158,7 @@ def _name_table_add(table: rawptr[GSList[NameEntry]], def _name_table_contains(table: rawptr[GSList[NameEntry]], name: str) -> int: - """检查名是否在表中。返回 1=存在, 0=不存在""" + """Return 1 if name is in the table, else 0""" if table is None or name is None: return 0 cur: rawptr[NameEntry] = table.Head while cur is not None: @@ -170,10 +169,10 @@ def _name_table_contains(table: rawptr[GSList[NameEntry]], # ============================================================ -# 文本分析辅助 +# Text analysis helpers # ============================================================ def _is_char_name(ch: char) -> int: - """判断字符是否为 SSA 名字符 [a-zA-Z0-9._]""" + """Return 1 if ch is a SSA name char [a-zA-Z0-9._]""" if ch >= 'a' and ch <= 'z': return 1 if ch >= 'A' and ch <= 'Z': return 1 if ch >= '0' and ch <= '9': return 1 @@ -183,18 +182,16 @@ def _is_char_name(ch: char) -> int: def _extract_def_name(pool: memhub.MemBuddy*, text: str) -> str: - """从定义行提取被定义的 SSA 名。 - - 定义行格式: "% = " - 返回分配的 NUL 结尾名串(如 "%0"),或 None 若非定义行。 + """Extract the defined SSA name from "% = ". + Returns an allocated NUL-terminated name (e.g. "%0"), or None if not a definition line. """ if text is None: return None if text[0] != '%': return None - # 扫描 " = " 模式 + # Scan for the " = " pattern i: int = 1 while text[i] != '\0': if text[i] == ' ' and text[i + 1] == '=' and text[i + 2] == ' ': - # 找到 " = " 在位置 i + # Found " = " at position i name_len: int = i name: str = pool.alloc(name_len + 1) if name is None: return None @@ -207,7 +204,7 @@ def _extract_def_name(pool: memhub.MemBuddy*, def _is_terminator(text: str) -> int: - """判断文本是否为终止指令""" + """Return 1 if text is a terminator instruction""" if text is None: return 0 if string.strncmp(text, "ret", 3) == 0: return 1 if string.strncmp(text, "br ", 3) == 0: return 1 @@ -219,7 +216,7 @@ def _is_terminator(text: str) -> int: def _is_phi(text: str) -> int: - """判断文本是否为 phi 指令""" + """Return 1 if text is a phi instruction""" if text is None: return 0 if string.strstr(text, " = phi ") is not None: return 1 return 0 @@ -228,28 +225,24 @@ def _is_phi(text: str) -> int: def _check_uses_in_line(table: rawptr[GSList[NameEntry]], text: str, def_name: str) -> int: - """检查行中所有 SSA 名使用是否已定义。 - - 跳过 def_name(定义位置的名字)。 - 返回未定义使用数量。 + """Check that all SSA name uses in the line are defined. + Skips def_name; returns the number of undefined uses. """ if text is None: return 0 i: int = 0 undef_count: int = 0 while text[i] != '\0': if text[i] == '%': - # 提取名 + # Extract name name_start: int = i i += 1 while text[i] != '\0' and _is_char_name(text[i]) == 1: i += 1 name_len: int = i - name_start if name_len > 1: - # 构建临时名串进行比较 - # 由于无法在栈上分配,用 strstr 进行简化检查 - # 这里采用逐字符比较方式 + # Build no temp string (no stack alloc); compare char by char. if _name_matches_at(text, name_start, name_len, table) == 0: - # 检查是否是 def_name(定义本身的位置) + # Check whether this is def_name (the definition site itself) if def_name is not None: if _str_matches_at(text, name_start, name_len, def_name) == 0: undef_count += 1 @@ -262,9 +255,7 @@ def _check_uses_in_line(table: rawptr[GSList[NameEntry]], def _name_matches_at(text: str, start: int, length: int, table: rawptr[GSList[NameEntry]]) -> int: - """检查 text[start:start+length] 是否匹配名表中的某个条目。 - 返回 1=匹配, 0=不匹配 - """ + """Return 1 if text[start:start+length] matches an entry in the name table, else 0.""" if table is None: return 0 cur: rawptr[NameEntry] = table.Head while cur is not None: @@ -277,11 +268,9 @@ def _name_matches_at(text: str, start: int, length: int, def _str_matches_at(text: str, start: int, length: int, target: str) -> int: - """检查 text[start:start+length] 是否等于 target 字符串。 - 返回 1=匹配, 0=不匹配 - """ + """Return 1 if text[start:start+length] equals target, else 0.""" if text is None or target is None: return 0 - # 检查 target 长度 + # Check target length target_len: sizet = string.strlen(target) if target_len != length: return 0 for i in range(length): @@ -290,27 +279,21 @@ def _str_matches_at(text: str, start: int, length: int, # ============================================================ -# 主验证函数 +# Main verification functions # ============================================================ def verify_function(pool: memhub.MemBuddy*, func: rawptr[Function], result: rawptr[VerifyResult]) -> int: - """验证函数的 SSA 正确性。 - - 参数: - pool: 内存池 - func: 要验证的函数 - result: 验证结果(调用前需用 _new_verify_result 创建) - - 返回: 错误数量(0 = 验证通过) + """Verify a function's SSA correctness; result must be created via + _new_verify_result. Returns the error count (0 = pass). """ if func is None or result is None: return 1 - # 创建名表 + # Create name table name_table: rawptr[GSList[NameEntry]] = _new_name_table(pool) if name_table is None: return 1 - # 1. 将函数参数加入名表(参数是已定义的) + # 1. Add function params to the name table (params are defined) params: rawptr[GSList[Param]] = func.Params param: rawptr[Param] = None if params is not None: @@ -320,7 +303,7 @@ def verify_function(pool: memhub.MemBuddy*, _name_table_add(name_table, pool, param.Name) param = param.Next - # 1b. 将基本块名加入名表(块标签如 %then 是 br/phi 的合法引用目标) + # 1b. Add block names to the table (block labels like %then are valid br/phi targets) blocks: rawptr[GSList[BasicBlock]] = func.Blocks blk_iter: rawptr[BasicBlock] = None if blocks is not None: @@ -330,7 +313,7 @@ def verify_function(pool: memhub.MemBuddy*, blk_name_len: sizet = string.strlen(blk_iter.Name) blk_ref: str = pool.alloc(blk_name_len + 2) if blk_ref is not None: - # 构造 "%" 字符串 + # Build the "%" string blk_ref[0] = '%' for k in range(blk_name_len): blk_ref[k + 1] = blk_iter.Name[k] @@ -338,14 +321,14 @@ def verify_function(pool: memhub.MemBuddy*, _name_table_add(name_table, pool, blk_ref) blk_iter = blk_iter.Next - # 2. 遍历所有块 + # 2. Walk all blocks blk: rawptr[BasicBlock] = None if blocks is not None: blk = blocks.Head while blk is not None: - # 检查终止指令 + # Check terminator if blk.IsTerminated == 0: - # 进一步检查最后一行是否为终止指令 + # Also check whether the last line is a terminator has_term: int = 0 line_check: rawptr[Line] = None if blk.Lines is not None: @@ -365,7 +348,7 @@ def verify_function(pool: memhub.MemBuddy*, viperlib.snprintf(err_buf, 128, "ERROR: block '%s' missing terminator", blk_name) _report_error(result, VERIFY_ERR_NO_TERMINATOR, err_buf) - # 遍历块内指令行 + # Walk instruction lines in the block seen_non_phi: int = 0 line: rawptr[Line] = None if blk.Lines is not None: @@ -375,7 +358,7 @@ def verify_function(pool: memhub.MemBuddy*, if line.Buf is not None: text: str = line.Buf - # 检查 phi 位置 + # Check phi position if _is_phi(text) == 1: if seen_non_phi == 1: phi_err: str = pool.alloc(128) @@ -383,14 +366,14 @@ def verify_function(pool: memhub.MemBuddy*, viperlib.snprintf(phi_err, 128, "ERROR: phi not at block start") _report_error(result, VERIFY_ERR_PHI_NOT_FIRST, phi_err) else: - # 非空行且非 phi 标记为已见非 phi + # Mark a non-empty non-phi line as seen-non-phi if string.strlen(text) > 0: seen_non_phi = 1 - # 提取定义名 + # Extract defined name def_name: str = _extract_def_name(pool, text) if def_name is not None: - # 检查重复定义 + # Check for duplicate definition dup: int = _name_table_add(name_table, pool, def_name) if dup == 1: dup_err: str = pool.alloc(128) @@ -398,7 +381,7 @@ def verify_function(pool: memhub.MemBuddy*, viperlib.snprintf(dup_err, 128, "ERROR: duplicate definition '%s'", def_name) _report_error(result, VERIFY_ERR_DUPLICATE_DEF, dup_err) - # 检查未定义使用 + # Check undefined uses undef: int = _check_uses_in_line(name_table, text, def_name) if undef > 0: use_err: str = pool.alloc(128) @@ -414,15 +397,14 @@ def verify_function(pool: memhub.MemBuddy*, def verify_module(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], result: rawptr[VerifyResult]) -> int: - """验证模块中所有函数的 SSA 正确性。 - - 返回: 错误总数 + """Verify SSA correctness of all functions in the module. + Returns the total error count. """ if mod is None or result is None: return 1 func: rawptr[Function] = mod.FuncHead while func is not None: if func.IsDeclared == 0: - # 只验证有函数体的定义,跳过声明 + # Verify only definitions with a body; skip declarations verify_function(pool, func, result) func = func.Next return result.ErrorCount diff --git a/openssl/bio.vp b/openssl/bio.vp index 2971354..5fff092 100644 --- a/openssl/bio.vp +++ b/openssl/bio.vp @@ -8,7 +8,7 @@ from openssl.types import * # 含 BIO_ADDR/BIO_ADDRINFO:主机名解析(getaddrinfo 封装)+ TCP 连接/读写。 # ============================================================ -# ---- BIO_METHOD 获取 ---- +# ---- BIO_METHOD accessors ---- extern def BIO_s_mem() -> BioMethodPtr extern def BIO_s_socket() -> BioMethodPtr extern def BIO_s_connect() -> BioMethodPtr @@ -18,7 +18,7 @@ extern def BIO_s_file() -> BioMethodPtr extern def BIO_s_null() -> BioMethodPtr extern def BIO_f_buffer() -> BioMethodPtr -# ---- 创建 / 释放 ---- +# ---- Create / free ---- # BIO *BIO_new(const BIO_METHOD *type) extern def BIO_new(type: BioMethodPtr) -> BioPtr # BIO *BIO_new_socket(int sock, int close_flag) @@ -38,7 +38,7 @@ extern def BIO_free(a: BioPtr) -> int extern def BIO_free_all(a: BioPtr) extern def BIO_vfree(a: BioPtr) -# ---- 读写 ---- +# ---- Read / write ---- # int BIO_read(BIO *b, void *data, int dlen) extern def BIO_read(b: BioPtr, data: bytes, dlen: int) -> int # int BIO_write(BIO *b, const void *data, int dlen) @@ -52,10 +52,10 @@ extern def BIO_ctrl_pending(b: BioPtr) -> sizet # size_t BIO_ctrl_wpending(BIO *b) extern def BIO_ctrl_wpending(b: BioPtr) -> sizet -# ---- BIO 控制(宏的底层 ctrl;连接/非阻塞等由它实现)---- +# ---- BIO control (backing ctrl for macros; connect/nonblocking implemented here) ---- # long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg) extern def BIO_ctrl(bp: BioPtr, cmd: int, larg: int32, parg: bytes) -> int32 -# int BIO_do_connect(BIO *b) —— 宏:BIO_ctrl(b, BIO_C_DO_STATE_MACHINE, 0, NULL) +# int BIO_do_connect(BIO *b) -- macro: BIO_ctrl(b, BIO_C_DO_STATE_MACHINE, 0, NULL) define BioCDoStateMachine = 101 define BioCSetConnect = 100 define BioCSetNbio = 102 @@ -63,12 +63,12 @@ define BioCSetFd = 104 define BioCGetFd = 105 define BioNoclose = 0x00 define BioClose = 0x01 -# BIO 链 +# BIO chain # BIO *BIO_push(BIO *b, BIO *append) / BIO *BIO_pop(BIO *b) extern def BIO_push(b: BioPtr, append: BioPtr) -> BioPtr extern def BIO_pop(b: BioPtr) -> BioPtr -# ---- 便捷控制封装(对应 bio.h 宏,避免 Viper 端裸用 BIO_ctrl)---- +# ---- Convenience control wrappers (match bio.h macros; avoid raw BIO_ctrl in Viper) ---- def bio_set_conn_hostname(b: BioPtr, name: str): BIO_ctrl(b, BioCSetConnect, 0, name) @@ -86,7 +86,7 @@ def bio_do_connect(b: BioPtr) -> int: # ============================================================ -# Socket 原语(BIO 层直接管 fd) +# Socket primitives (BIO manages fd directly) # ============================================================ define BioSockReuseaddr = 0x01 define BioSockV6Only = 0x02 @@ -116,7 +116,7 @@ extern def BIO_set_tcp_ndelay(sock: int, turn_on: int) -> int extern def BIO_sock_should_retry(i: int) -> int # ============================================================ -# BIO_ADDR / BIO_ADDRINFO(网络地址,TCP 解析用) +# BIO_ADDR / BIO_ADDRINFO (network addresses, for TCP resolution) # ============================================================ # BIO_ADDR *BIO_ADDR_new(void) / dup extern def BIO_ADDR_new() -> BioAddrPtr @@ -128,14 +128,14 @@ extern def BIO_ADDR_family(ap: BioAddrPtr) -> int extern def BIO_ADDR_rawport(ap: BioAddrPtr) -> uint16 # int BIO_ADDR_rawaddress(const BIO_ADDR *ap, void *p, size_t *l) extern def BIO_ADDR_rawaddress(ap: BioAddrPtr, p: bytes, l: bytes) -> int -# char *BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric) → 返回需 CRYPTO_free +# char *BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric) -> result must be CRYPTO_free'd extern def BIO_ADDR_hostname_string(ap: BioAddrPtr, numeric: int) -> str # char *BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric) extern def BIO_ADDR_service_string(ap: BioAddrPtr, numeric: int) -> str # char *BIO_ADDR_path_string(const BIO_ADDR *ap) extern def BIO_ADDR_path_string(ap: BioAddrPtr) -> str -# BIO_ADDRINFO 遍历 +# BIO_ADDRINFO iteration extern def BIO_ADDRINFO_free(bai: BioAddrInfoPtr) # const BIO_ADDRINFO *BIO_ADDRINFO_next(const BIO_ADDRINFO *bai) extern def BIO_ADDRINFO_next(bai: BioAddrInfoPtr) -> BioAddrInfoPtr @@ -148,7 +148,7 @@ extern def BIO_ADDRINFO_protocol(bai: BioAddrInfoPtr) -> int # const BIO_ADDR *BIO_ADDRINFO_address(const BIO_ADDRINFO *bai) extern def BIO_ADDRINFO_address(bai: BioAddrInfoPtr) -> BioAddrPtr -# 主机名解析(getaddrinfo 封装) +# Hostname resolution (getaddrinfo wrapper) define BioLookupClient = 0 define BioLookupServer = 1 # int BIO_lookup(const char *host, const char *service, int lookup_type, diff --git a/openssl/crypto.vp b/openssl/crypto.vp index 5c3eb25..6ed4bc5 100644 --- a/openssl/crypto.vp +++ b/openssl/crypto.vp @@ -6,17 +6,17 @@ from openssl.types import * # 对应头文件: crypto.h / opensslv.h # ============================================================ -# ---- 版本查询类型(OpenSSL_version(type) 的 type 取值)---- -define OpenSslVersion = 0# OPENSSL_VERSION 完整版本文本 -define OpenSslBuiltOn = 2# OPENSSL_BUILT_ON 编译时间 -define OpenSslPlatform = 3# OPENSSL_PLATFORM 平台 +# ---- Version query types (type arg of OpenSSL_version(type)) ---- +define OpenSslVersion = 0# OPENSSL_VERSION full version text +define OpenSslBuiltOn = 2# OPENSSL_BUILT_ON build time +define OpenSslPlatform = 3# OPENSSL_PLATFORM platform define OpenSslDir = 4# OPENSSL_DIR define OpenSslEnginesDir = 5# OPENSSL_ENGINES_DIR define OpenSslVersionString = 6# OPENSSL_VERSION_STRING "3.x.y" define OpenSslModulesDir = 8# OPENSSL_MODULES_DIR define OpenSslCpuInfo = 9# OPENSSL_CPU_INFO -# ---- OPENSSL_init_crypto / OPENSSL_init_ssl 选项位 ---- +# ---- OPENSSL_init_crypto / OPENSSL_init_ssl option bits ---- define OpenSslInitNoLoadCryptoStrings = 0x00000001 define OpenSslInitNoLoadConfig = 0x00000004 define OpenSslInitNoAddAllCiphers = 0x00000008 @@ -28,19 +28,19 @@ define OpenSslInitAtFork = 0x00020000 define OpenSslInitNoLoadSslStrings = 0x00100000 define OpenSslInitNoAtomics = 0x00200000 -# ---- 版本号(3.6.2,随链接库而定)---- +# ---- Version numbers (3.6.2, depends on the linked library) ---- define OpenSslVersionMajor = 3 define OpenSslVersionMinor = 6 define OpenSslVersionPatch = 2 -# ---- 全局初始化 / 清理 ---- +# ---- Global init / cleanup ---- # int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) extern def OPENSSL_init_crypto(opts: uint64, settings: bytes) -> int # int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) extern def OPENSSL_init_ssl(opts: uint64, settings: bytes) -> int extern def OPENSSL_cleanup() -# ---- 版本查询 ---- +# ---- Version query ---- # const char *OpenSSL_version(int type) extern def OpenSSL_version(type: int) -> str # unsigned long OpenSSL_version_num(void) @@ -48,7 +48,7 @@ extern def OpenSSL_version_num() -> uint32 extern def OPENSSL_version_pre_release() -> str extern def OPENSSL_version_build_metadata() -> str -# ---- 内存 / 安全擦除 ---- +# ---- Memory / secure erase ---- # void OPENSSL_cleanse(void *ptr, size_t len) extern def OPENSSL_cleanse(ptr: bytes, len: sizet) # void CRYPTO_free(void *ptr, const char *file, int line) @@ -57,7 +57,7 @@ extern def CRYPTO_free(ptr: bytes, file: str, line: int) extern def CRYPTO_malloc(num: sizet, file: str, line: int) -> bytes # int CRYPTO_memcmp(const void *in_a, const void *in_b, size_t len) extern def CRYPTO_memcmp(in_a: bytes, in_b: bytes, len: sizet) -> int -# char *OPENSSL_buf2hexstr(const unsigned char *buf, long buflen) → 由 ossl_free 释放 +# char *OPENSSL_buf2hexstr(const unsigned char *buf, long buflen) -> free with ossl_free extern def OPENSSL_buf2hexstr(buf: bytes, buflen: int32) -> str # int OPENSSL_hexchar2int(unsigned char c) extern def OPENSSL_hexchar2int(c: uint8) -> int @@ -66,7 +66,7 @@ extern def OPENSSL_strcasecmp(s1: str, s2: str) -> int # int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t n) extern def OPENSSL_strncasecmp(s1: str, s2: str, n: sizet) -> int -# ---- 便捷封装(OpenSSL 的 CRYPTO_free/CRYPTO_malloc 宏带 file/line 两参,此处封装为单参)---- +# ---- Convenience wrappers (CRYPTO_free/CRYPTO_malloc macros take file/line; wrapped as single-arg) ---- def ossl_free(ptr: bytes): CRYPTO_free(ptr, "", 0) diff --git a/openssl/err.vp b/openssl/err.vp index 5a3caf6..66fa0de 100644 --- a/openssl/err.vp +++ b/openssl/err.vp @@ -6,7 +6,7 @@ from openssl.types import * # 对应头文件: err.h # ============================================================ -# unsigned long ERR_get_error(void) — 弹出队列头并返回错误码 +# unsigned long ERR_get_error(void) -- pop queue head and return error code extern def ERR_get_error() -> uint32 # unsigned long ERR_peek_error(void) extern def ERR_peek_error() -> uint32 @@ -15,7 +15,7 @@ extern def ERR_peek_last_error() -> uint32 # void ERR_clear_error(void) extern def ERR_clear_error() -# char *ERR_error_string(unsigned long e, char *buf) — buf 可为 NULL(用内部静态缓冲) +# char *ERR_error_string(unsigned long e, char *buf) -- buf may be NULL (uses internal static buffer) extern def ERR_error_string(e: uint32, buf: bytes) -> str # void ERR_error_string_n(unsigned long e, char *buf, size_t len) extern def ERR_error_string_n(e: uint32, buf: bytes, len: sizet) @@ -26,17 +26,17 @@ extern def ERR_func_error_string(e: uint32) -> str # const char *ERR_reason_error_string(unsigned long e) extern def ERR_reason_error_string(e: uint32) -> str -# 栈标记(用于部分失败回滚) +# Stack marks (for partial-failure rollback) extern def ERR_set_mark() -> int extern def ERR_pop_to_mark() -> int extern def ERR_clear_last_mark() -> int -# void ERR_print_errors(BIO *bp) — 把整个队列打到 BIO(见 bio.vp) +# void ERR_print_errors(BIO *bp) -- dump the whole queue to BIO (see bio.vp) extern def ERR_print_errors(bp: BioPtr) -# void ERR_print_errors_fp(FILE *fp) — FILE* 暂不暴露 +# void ERR_print_errors_fp(FILE *fp) -- FILE* not exposed yet -# ---- 错误码拆包(内联宏等价物)---- -# OpenSSL 错误码布局:系统位 0x80000000,库 24-31,原因 0-15 +# ---- Error code unpacking (inline-macro equivalents) ---- +# OpenSSL error code layout: system bit 0x80000000, library 24-31, reason 0-15 define ErrLibMask = 0xff000000 define ErrReasonMask = 0x0000ffff define ErrSystemFlag = 0x80000000 diff --git a/openssl/evp.vp b/openssl/evp.vp index a98ef21..6133714 100644 --- a/openssl/evp.vp +++ b/openssl/evp.vp @@ -15,7 +15,7 @@ define EvpMaxIvLength = 16 define EvpMaxBlockLength = 32 # ============================================================ -# 1) 摘要算法对象(const EVP_MD*) +# 1) Digest algorithm objects (const EVP_MD*) # ============================================================ extern def EVP_md5() -> EvpMdPtr extern def EVP_sha1() -> EvpMdPtr @@ -38,7 +38,7 @@ extern def EVP_MD_get_block_size(md: EvpMdPtr) -> int extern def EVP_MD_get_type(md: EvpMdPtr) -> int # ============================================================ -# 2) 摘要上下文(增量计算) +# 2) Digest contexts (incremental) # ============================================================ # EVP_MD_CTX *EVP_MD_CTX_new(void) extern def EVP_MD_CTX_new() -> EvpMdCtxPtr @@ -58,13 +58,13 @@ extern def EVP_DigestFinal_ex(ctx: EvpMdCtxPtr, md: bytes, s: bytes) -> int extern def EVP_DigestFinal(ctx: EvpMdCtxPtr, md: bytes, s: bytes) -> int # int EVP_MD_CTX_get_size(const EVP_MD_CTX *ctx) extern def EVP_MD_CTX_get_size(ctx: EvpMdCtxPtr) -> int -# 一次性摘要: int EVP_Digest(const void *data, size_t count, unsigned char *md, +# One-shot digest: int EVP_Digest(const void *data, size_t count, unsigned char *md, # unsigned int *size, const EVP_MD *type, ENGINE *impl) extern def EVP_Digest(data: bytes, count: sizet, md: bytes, size: bytes, type: EvpMdPtr, impl: bytes) -> int # ============================================================ -# 3) 对称加密算法对象(const EVP_CIPHER*) +# 3) Symmetric cipher objects (const EVP_CIPHER*) # ============================================================ extern def EVP_aes_128_ecb() -> EvpCipherPtr extern def EVP_aes_128_cbc() -> EvpCipherPtr @@ -87,7 +87,7 @@ extern def EVP_chacha20() -> EvpCipherPtr extern def EVP_rc4() -> EvpCipherPtr # const EVP_CIPHER *EVP_get_cipherbyname(const char *name) extern def EVP_get_cipherbyname(name: str) -> EvpCipherPtr -# 参数查询 +# Parameter queries extern def EVP_CIPHER_get_block_size(cipher: EvpCipherPtr) -> int extern def EVP_CIPHER_get_key_length(cipher: EvpCipherPtr) -> int extern def EVP_CIPHER_get_iv_length(cipher: EvpCipherPtr) -> int @@ -95,7 +95,7 @@ extern def EVP_CIPHER_get_nid(cipher: EvpCipherPtr) -> int extern def EVP_CIPHER_get_type(cipher: EvpCipherPtr) -> int # ============================================================ -# 4) 对称加解密上下文 +# 4) Symmetric cipher contexts # ============================================================ # EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void) extern def EVP_CIPHER_CTX_new() -> EvpCipherCtxPtr @@ -106,7 +106,7 @@ extern def EVP_CIPHER_CTX_reset(c: EvpCipherCtxPtr) -> int # int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in) extern def EVP_CIPHER_CTX_copy(out: EvpCipherCtxPtr, inn: EvpCipherCtxPtr) -> int -# 统一接口(enc: 1 加密 / 0 解密) +# Unified interface (enc: 1 encrypt / 0 decrypt) # int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, # ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc) extern def EVP_CipherInit_ex(ctx: EvpCipherCtxPtr, cipher: EvpCipherPtr, @@ -122,7 +122,7 @@ extern def EVP_CipherFinal_ex(ctx: EvpCipherCtxPtr, outm: bytes, outl: bytes) -> extern def EVP_CipherInit(ctx: EvpCipherCtxPtr, cipher: EvpCipherPtr, key: bytes, iv: bytes, enc: int) -> int -# 分体加密/解密(对上面统一接口的细分,省略相同底层) +# Split encrypt/decrypt (specializations of the unified interface above) # int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, # ENGINE *impl, const unsigned char *key, const unsigned char *iv) extern def EVP_EncryptInit_ex(ctx: EvpCipherCtxPtr, cipher: EvpCipherPtr, @@ -145,7 +145,7 @@ extern def EVP_DecryptUpdate(ctx: EvpCipherCtxPtr, out: bytes, outl: bytes, # int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl) extern def EVP_DecryptFinal_ex(ctx: EvpCipherCtxPtr, outm: bytes, outl: bytes) -> int -# 上下文查询/控制 +# Context query / control extern def EVP_CIPHER_CTX_set_padding(c: EvpCipherCtxPtr, pad: int) -> int extern def EVP_CIPHER_CTX_set_key_length(c: EvpCipherCtxPtr, keylen: int) -> int extern def EVP_CIPHER_CTX_ctrl(ctx: EvpCipherCtxPtr, type: int, arg: int, ptr: bytes) -> int @@ -159,13 +159,13 @@ extern def EVP_CIPHER_CTX_get_iv(ctx: EvpCipherCtxPtr, iv: bytes, len: sizet) -> # int EVP_CIPHER_CTX_set_iv(EVP_CIPHER_CTX *ctx, const void *iv, size_t len) extern def EVP_CIPHER_CTX_set_iv(ctx: EvpCipherCtxPtr, iv: bytes, len: sizet) -> int -# ---- GCM 相关 ctrl 常量(EVP_CIPHER_CTX_ctrl 使用)---- +# ---- GCM ctrl constants (used with EVP_CIPHER_CTX_ctrl) ---- define EvpCtrlGcmSetIvl = 0x9# EVP_CTRL_AEAD_SET_IVLEN define EvpCtrlGcmGetTag = 0x10# EVP_CTRL_AEAD_GET_TAG define EvpCtrlGcmSetTag = 0x11# EVP_CTRL_AEAD_SET_TAG # ============================================================ -# 5) Base64(EVP 编解码块函数) +# 5) Base64 (EVP encode/decode block functions) # ============================================================ # int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int n) extern def EVP_EncodeBlock(t: bytes, f: bytes, n: int) -> int @@ -173,12 +173,12 @@ extern def EVP_EncodeBlock(t: bytes, f: bytes, n: int) -> int extern def EVP_DecodeBlock(t: bytes, f: bytes, n: int) -> int # ============================================================ -# 6) 非对称(EVP_PKEY / EVP_PKEY_CTX) +# 6) Asymmetric (EVP_PKEY / EVP_PKEY_CTX) # ============================================================ # EVP_PKEY *EVP_PKEY_new(void) / void EVP_PKEY_free(EVP_PKEY *pkey) extern def EVP_PKEY_new() -> EvpPkeyPtr extern def EVP_PKEY_free(pkey: EvpPkeyPtr) -# int EVP_PKEY_get_size(const EVP_PKEY *pkey) (签名长度/密文长度) +# int EVP_PKEY_get_size(const EVP_PKEY *pkey) (signature/ciphertext length) extern def EVP_PKEY_get_size(pkey: EvpPkeyPtr) -> int extern def EVP_PKEY_get_id(pkey: EvpPkeyPtr) -> int extern def EVP_PKEY_get_base_id(pkey: EvpPkeyPtr) -> int @@ -190,7 +190,7 @@ extern def EVP_PKEY_CTX_new(pkey: EvpPkeyPtr, e: bytes) -> EvpPkeyCtxPtr # void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx) extern def EVP_PKEY_CTX_free(ctx: EvpPkeyCtxPtr) -# 签名 / 验签(裸 RSA/EC) +# Sign / verify (raw RSA/EC) extern def EVP_PKEY_sign_init(ctx: EvpPkeyCtxPtr) -> int extern def EVP_PKEY_verify_init(ctx: EvpPkeyCtxPtr) -> int extern def EVP_PKEY_encrypt_init(ctx: EvpPkeyCtxPtr) -> int @@ -206,7 +206,7 @@ extern def EVP_PKEY_encrypt(ctx: EvpPkeyCtxPtr, out: bytes, outlen: bytes, extern def EVP_PKEY_decrypt(ctx: EvpPkeyCtxPtr, out: bytes, outlen: bytes, inn: bytes, inlen: sizet) -> int -# 摘要签名/验签(DigestSign*:RSA-PSS/ECDSA 最常用) +# Digest sign/verify (DigestSign*: mostly RSA-PSS/ECDSA) # int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, # ENGINE *e, EVP_PKEY *pkey) extern def EVP_DigestSignInit(ctx: EvpMdCtxPtr, pctx: bytes, type: EvpMdPtr, @@ -222,7 +222,7 @@ extern def EVP_DigestVerifyUpdate(ctx: EvpMdCtxPtr, data: bytes, dsize: sizet) - extern def EVP_DigestVerifyFinal(ctx: EvpMdCtxPtr, sig: bytes, siglen: sizet) -> int # ============================================================ -# 7) 口令派生 / 辅助 +# 7) Key derivation / helpers # ============================================================ # int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md, # const unsigned char *salt, const unsigned char *data, int datal, int count, diff --git a/openssl/hmac.vp b/openssl/hmac.vp index 24d9c22..be9b729 100644 --- a/openssl/hmac.vp +++ b/openssl/hmac.vp @@ -6,13 +6,13 @@ from openssl.types import * # 对应头文件: hmac.h # ============================================================ -# ---- 一次性 HMAC ---- +# ---- One-shot HMAC ---- # unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len, # const unsigned char *d, size_t n, unsigned char *md, unsigned int *md_len) extern def HMAC(evp_md: EvpMdPtr, key: bytes, key_len: int, d: bytes, n: sizet, md: bytes, md_len: bytes) -> bytes -# ---- 上下文增量 HMAC(OpenSSL 3.x 提供)---- +# ---- Incremental HMAC context (provided by OpenSSL 3.x) ---- # HMAC_CTX *HMAC_CTX_new(void) extern def HMAC_CTX_new() -> HmacCtxPtr # void HMAC_CTX_free(HMAC_CTX *ctx) diff --git a/openssl/objects.vp b/openssl/objects.vp index 0889404..fe46146 100644 --- a/openssl/objects.vp +++ b/openssl/objects.vp @@ -6,7 +6,7 @@ from openssl.types import * # 对应头文件: objects.h / obj_mac.h # ============================================================ -# ---- 常用 NID(obj_mac.h 常量)---- +# ---- Common NIDs (obj_mac.h constants) ---- define NidCommonName = 13 define NidCountryName = 14 define NidLocalityName = 15 @@ -30,7 +30,7 @@ define NidMd5 = 4 define NidHmac = 855 define NidX9_62IdEcPublicKey = 408 -# ---- OBJ / NID 转换 ---- +# ---- OBJ / NID conversion ---- # int OBJ_txt2nid(const char *s) extern def OBJ_txt2nid(s: str) -> int # int OBJ_sn2nid(const char *s) / int OBJ_ln2nid(const char *s) diff --git a/openssl/pem.vp b/openssl/pem.vp index 1c08114..f65c948 100644 --- a/openssl/pem.vp +++ b/openssl/pem.vp @@ -9,7 +9,7 @@ from openssl.types import * # (函数返回新对象)。回调 cb 用 bytes(Viper 传 t.CPtr(0))。 # ============================================================ -# ---- X509 证书 ---- +# ---- X509 certificate ---- # X509 *PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb *cb, void *u) extern def PEM_read_bio_X509(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> X509Ptr # int PEM_write_bio_X509(BIO *bp, X509 *x) @@ -19,7 +19,7 @@ extern def PEM_read_bio_X509_AUX(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> X # int PEM_write_bio_X509_AUX(BIO *bp, X509 *x) extern def PEM_write_bio_X509_AUX(bp: BioPtr, x: X509Ptr) -> int -# ---- 私钥 / 公钥(EVP_PKEY 通用容器)---- +# ---- Private / public key (generic EVP_PKEY container) ---- # EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u) extern def PEM_read_bio_PrivateKey(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> EvpPkeyPtr # int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc, @@ -31,7 +31,7 @@ extern def PEM_read_bio_PUBKEY(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> Evp # int PEM_write_bio_PUBKEY(BIO *bp, EVP_PKEY *x) extern def PEM_write_bio_PUBKEY(bp: BioPtr, x: EvpPkeyPtr) -> int -# ---- 传统 RSA 私钥(OpenSSL 3.x 仍导出,兼容旧文件)---- +# ---- Legacy RSA private key (still exported in OpenSSL 3.x, for old files) ---- # RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x, pem_password_cb *cb, void *u) extern def PEM_read_bio_RSAPrivateKey(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> bytes # int PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc, @@ -39,13 +39,13 @@ extern def PEM_read_bio_RSAPrivateKey(bp: BioPtr, x: bytes, cb: bytes, u: bytes) extern def PEM_write_bio_RSAPrivateKey(bp: BioPtr, x: bytes, enc: EvpCipherPtr, kstr: bytes, klen: int, cb: bytes, u: bytes) -> int -# ---- 证书请求 / CRL ---- +# ---- Certificate request / CRL ---- # X509_REQ *PEM_read_bio_X509_REQ(BIO *bp, X509_REQ **x, pem_password_cb *cb, void *u) extern def PEM_read_bio_X509_REQ(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> X509ReqPtr # X509_CRL *PEM_read_bio_X509_CRL(BIO *bp, X509_CRL **x, pem_password_cb *cb, void *u) extern def PEM_read_bio_X509_CRL(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> X509CrlPtr -# ---- 便捷读取封装(调用方无需传递 x 出参/cb)---- +# ---- Convenience readers (caller passes no x out-param / cb) ---- def pem_read_bio_x509_simple(bp: BioPtr) -> X509Ptr: return PEM_read_bio_X509(bp, None, None, None) diff --git a/openssl/rand.vp b/openssl/rand.vp index 3f90426..74049a3 100644 --- a/openssl/rand.vp +++ b/openssl/rand.vp @@ -6,9 +6,9 @@ from openssl.types import * # 对应头文件: rand.h # ============================================================ -# int RAND_bytes(unsigned char *buf, int num) — 加密安全随机 +# int RAND_bytes(unsigned char *buf, int num) -- cryptographically secure random extern def RAND_bytes(buf: bytes, num: int) -> int -# int RAND_priv_bytes(unsigned char *buf, int num) — 私钥级随机(私钥/密钥建议用) +# int RAND_priv_bytes(unsigned char *buf, int num) -- private-key-grade random (preferred for keys) extern def RAND_priv_bytes(buf: bytes, num: int) -> int # int RAND_status(void) extern def RAND_status() -> int diff --git a/openssl/sha.vp b/openssl/sha.vp index 2ed85ab..148f4c9 100644 --- a/openssl/sha.vp +++ b/openssl/sha.vp @@ -25,4 +25,4 @@ extern def SHA256(d: bytes, n: sizet, md: bytes) -> bytes extern def SHA384(d: bytes, n: sizet, md: bytes) -> bytes # unsigned char *SHA512(const unsigned char *d, size_t n, unsigned char *md) extern def SHA512(d: bytes, n: sizet, md: bytes) -> bytes -# SHA-3 只经 EVP 提供(EVP_sha3_256() 等,见 evp.vp) +# SHA-3 is provided only via EVP (EVP_sha3_256(), etc.; see evp.vp) diff --git a/openssl/ssl.vp b/openssl/ssl.vp index 700620e..0c43419 100644 --- a/openssl/ssl.vp +++ b/openssl/ssl.vp @@ -16,26 +16,26 @@ from openssl.types import * # SSL_write/SSL_read # ============================================================ -# ---- 证书文件格式(SSL_CTX_use_*_file / use_certificate_file 的 type)---- +# ---- Certificate file format (type arg of SSL_CTX_use_*_file / use_certificate_file) ---- define X509FiletypePem = 1 define X509FiletypeAsn1 = 2 -# SSL_FILETYPE_* 与 X509_FILETYPE_* 同值 +# SSL_FILETYPE_* shares the same value as X509_FILETYPE_* define SslFiletypePem = 1 define SslFiletypeAsn1 = 2 -# ---- 校验模式(SSL_CTX_set_verify mode)---- +# ---- Verify modes (SSL_CTX_set_verify mode) ---- define SslVerifyNone = 0x00 define SslVerifyPeer = 0x01 define SslVerifyFailIfNoPeerCert = 0x02 define SslVerifyClientOnce = 0x04 -# ---- 协议版本(SSL_CTX_ctrl SSL_CTRL_SET_MIN/MAX_PROTO_VERSION 的 arg)---- +# ---- Protocol versions (arg of SSL_CTRL_SET_MIN/MAX_PROTO_VERSION) ---- define Tls1Version = 0x0301 define Tls1_1Version = 0x0302 define Tls1_2Version = 0x0303 define Tls1_3Version = 0x0304 -# ---- ctrl 命令 ---- +# ---- ctrl commands ---- define SslCtrlSetTlsextHostname = 55# SSL_ctrl(ssl, ..., TLSEXT_NAMETYPE_host_name, (void*)name) define SslCtrlSetMinProtoVersion = 123 define SslCtrlSetMaxProtoVersion = 124 @@ -43,11 +43,11 @@ define SslCtrlMode = 33 define SslCtrlClearMode = 34 define TlsextNametypeHostName = 0 -# ---- shutdown 状态 ---- +# ---- shutdown states ---- define SslSentShutdown = 1 define SslReceivedShutdown = 2 -# ---- SSL_get_error 返回码 ---- +# ---- SSL_get_error return codes ---- define SslErrorNone = 0 define SslErrorSsl = 1 define SslErrorWantRead = 2 @@ -62,7 +62,7 @@ define SslErrorWantAsyncJob = 10 define SslErrorWantClientHelloCb = 11 define SslErrorWantRetryVerify = 12 -# ---- SSL_OP_* 选项 ---- +# ---- SSL_OP_* options ---- # SSL_CTX_set_options(ctx, uint64_t) define SslOpNoSslv2 = 0x01000000 define SslOpNoSslv3 = 0x02000000 @@ -73,9 +73,9 @@ define SslOpNoTlsv1_3 = 0x20000000 define SslOpAll = 0x00000fff # ============================================================ -# 初始化 / 方法 +# Init / methods # ============================================================ -# (OPENSSL_init_ssl 见 crypto.vp) +# (see crypto.vp for OPENSSL_init_ssl) # const SSL_METHOD *TLS_method(void) / *_client_method / *_server_method extern def TLS_method() -> SslMethodPtr extern def TLS_client_method() -> SslMethodPtr @@ -90,7 +90,7 @@ extern def SSL_CTX_new(meth: SslMethodPtr) -> SslCtxPtr extern def SSL_CTX_free(ctx: SslCtxPtr) # int SSL_CTX_up_ref(SSL_CTX *ctx) extern def SSL_CTX_up_ref(ctx: SslCtxPtr) -> int -# 证书 / 私钥 +# Certificate / private key # int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) extern def SSL_CTX_use_certificate_file(ctx: SslCtxPtr, file: str, type: int) -> int # int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) @@ -99,7 +99,7 @@ extern def SSL_CTX_use_PrivateKey_file(ctx: SslCtxPtr, file: str, type: int) -> extern def SSL_CTX_use_certificate_chain_file(ctx: SslCtxPtr, file: str) -> int # int SSL_CTX_check_private_key(const SSL_CTX *ctx) extern def SSL_CTX_check_private_key(ctx: SslCtxPtr) -> int -# 校验配置 +# Verify config # void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb callback) extern def SSL_CTX_set_verify(ctx: SslCtxPtr, mode: int, callback: bytes) # void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) @@ -118,11 +118,11 @@ extern def SSL_CTX_set_alpn_protos(ctx: SslCtxPtr, protos: bytes, protos_len: ui extern def SSL_CTX_set_options(ctx: SslCtxPtr, op: uint64) -> uint64 # long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) extern def SSL_CTX_ctrl(ctx: SslCtxPtr, cmd: int, larg: int32, parg: bytes) -> int32 -# long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) -- 用 ctrl? 不是,是函数 +# long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) # int SSL_CTX_set_session_cache_mode(SSL_CTX *ctx, int mode) # ============================================================ -# SSL 对象 +# SSL object # ============================================================ # SSL *SSL_new(SSL_CTX *ctx) extern def SSL_new(ctx: SslCtxPtr) -> SslPtr @@ -136,7 +136,7 @@ extern def SSL_get_fd(ssl: SslPtr) -> int # void SSL_set_connect_state(SSL *ssl) / accept extern def SSL_set_connect_state(ssl: SslPtr) extern def SSL_set_accept_state(ssl: SslPtr) -# 握手 +# Handshake extern def SSL_connect(ssl: SslPtr) -> int extern def SSL_accept(ssl: SslPtr) -> int # int SSL_do_handshake(SSL *ssl) @@ -163,31 +163,31 @@ extern def SSL_get_verify_mode(ssl: SslPtr) -> int # X509 *SSL_get_peer_certificate(const SSL *s) / SSL_get1_peer_certificate extern def SSL_get_peer_certificate(s: SslPtr) -> X509Ptr extern def SSL_get1_peer_certificate(s: SslPtr) -> X509Ptr -# 当前加密套件 -# SSL_get_cipher_name 在 ssl.h 是宏(SSL_CIPHER_get_name(SSL_get_current_cipher(s))), -# FFI 拆成两个真实导出函数调用: +# Current cipher +# SSL_get_cipher_name is a macro in ssl.h (SSL_CIPHER_get_name(SSL_get_current_cipher(s))); +# FFI calls the two real exported functions instead. # const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) extern def SSL_get_current_cipher(s: SslPtr) -> SslCipherPtr # const char *SSL_CIPHER_get_name(const SSL_CIPHER *c) extern def SSL_CIPHER_get_name(cipher: SslCipherPtr) -> str -# SNI: int SSL_set_tlsext_host_name(SSL *s, const char *name)(宏 → SSL_ctrl) +# SNI: int SSL_set_tlsext_host_name(SSL *s, const char *name) (macro -> SSL_ctrl) # const char *SSL_get_servername(const SSL *s, int type) extern def SSL_get_servername(s: SslPtr, type: int) -> str # ALPN # void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, unsigned int *len) extern def SSL_get0_alpn_selected(ssl: SslPtr, data: bytes, len: bytes) -# 控制 / 选项 +# Control / options # long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg) extern def SSL_ctrl(ssl: SslPtr, cmd: int, larg: int32, parg: bytes) -> int32 # uint64_t SSL_set_options(SSL *ssl, uint64_t op) extern def SSL_set_options(ssl: SslPtr, op: uint64) -> uint64 -# int SSL_set1_host(SSL *s, const char *hostname) (主机名校验) +# int SSL_set1_host(SSL *s, const char *hostname) (hostname check) extern def SSL_set1_host(s: SslPtr, hostname: str) -> int # void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio) extern def SSL_set_bio(s: SslPtr, rbio: BioPtr, wbio: BioPtr) # ============================================================ -# 便捷封装 +# Convenience wrappers # ============================================================ def ssl_set_tlsext_host_name(ssl: SslPtr, name: str) -> int: return SSL_ctrl(ssl, SslCtrlSetTlsextHostname, TlsextNametypeHostName, name) diff --git a/openssl/types.vp b/openssl/types.vp index 3084a0e..8139c95 100644 --- a/openssl/types.vp +++ b/openssl/types.vp @@ -14,18 +14,18 @@ import t, c # opt -passes=asan 会报 invalid type for function argument。 # ============================================================ -# ---- 基础上下文 ---- +# ---- Base contexts ---- OsslLibCtxPtr: t.CTypedef = t.CPtr # OSSL_LIB_CTX* EnginePtr: t.CTypedef = t.CPtr # ENGINE* OpensslInitSettingsPtr: t.CTypedef = t.CPtr # OPENSSL_INIT_SETTINGS* -# ---- BIO(内存/文件/Socket/Connect)---- +# ---- BIO (memory / file / socket / connect) ---- BioPtr: t.CTypedef = t.CPtr # BIO* BioMethodPtr: t.CTypedef = t.CPtr # BIO_METHOD* BioAddrPtr: t.CTypedef = t.CPtr # BIO_ADDR* BioAddrInfoPtr: t.CTypedef = t.CPtr # BIO_ADDRINFO* -# ---- EVP(摘要/对称/非对称)---- +# ---- EVP (digest / symmetric / asymmetric) ---- EvpMdPtr: t.CTypedef = t.CPtr # EVP_MD* EvpMdCtxPtr: t.CTypedef = t.CPtr # EVP_MD_CTX* EvpCipherPtr: t.CTypedef = t.CPtr # EVP_CIPHER* @@ -35,7 +35,7 @@ EvpPkeyCtxPtr: t.CTypedef = t.CPtr # EVP_PKEY_CTX* EvpEncodeCtxPtr: t.CTypedef = t.CPtr # EVP_ENCODE_CTX* HmacCtxPtr: t.CTypedef = t.CPtr # HMAC_CTX* -# ---- X509 / 证书 ---- +# ---- X509 / certificate ---- X509Ptr: t.CTypedef = t.CPtr # X509* X509NamePtr: t.CTypedef = t.CPtr # X509_NAME* X509NameEntryPtr: t.CTypedef = t.CPtr # X509_NAME_ENTRY* diff --git a/openssl/x509.vp b/openssl/x509.vp index 4052c0b..a58e868 100644 --- a/openssl/x509.vp +++ b/openssl/x509.vp @@ -14,18 +14,18 @@ define X509Version2 = 1 define X509Version3 = 2 # ============================================================ -# 1) 证书对象生命周期 +# 1) Certificate object lifecycle # ============================================================ # X509 *X509_new(void) / void X509_free(X509 *a) extern def X509_new() -> X509Ptr extern def X509_free(a: X509Ptr) # int i2d_X509(X509 *a, unsigned char **pp) / X509 *d2i_X509(X509 **a, const unsigned char **pp, long length) -# (pp 语义复杂,Viper 端建议用 d2i_X509_bio/i2d_X509_bio,故不暴露裸 d2i/i2d) +# (pp semantics are complex; use d2i_X509_bio/i2d_X509_bio, so raw d2i/i2d are not exposed) extern def d2i_X509_bio(bp: BioPtr, x509: bytes) -> X509Ptr extern def i2d_X509_bio(bp: BioPtr, x509: X509Ptr) -> int # ============================================================ -# 2) 证书字段读取 +# 2) Certificate field access # ============================================================ # long X509_get_version(const X509 *x) extern def X509_get_version(x: X509Ptr) -> int32 @@ -51,7 +51,7 @@ extern def X509_digest(data: X509Ptr, type: EvpMdPtr, md: bytes, len: bytes) -> extern def X509_verify(a: X509Ptr, r: EvpPkeyPtr) -> int # ============================================================ -# 3) X509_NAME 读取(subject/issuer 各 RDN 段) +# 3) X509_NAME access (subject/issuer RDN entries) # ============================================================ # int X509_NAME_entry_count(const X509_NAME *name) extern def X509_NAME_entry_count(name: X509NamePtr) -> int @@ -65,11 +65,11 @@ extern def X509_NAME_ENTRY_get_object(ne: X509NameEntryPtr) -> Asn1ObjectPtr extern def X509_NAME_ENTRY_get_data(ne: X509NameEntryPtr) -> Asn1StringPtr # int X509_NAME_get_text_by_NID(const X509_NAME *name, int nid, char *buf, int len) extern def X509_NAME_get_text_by_NID(name: X509NamePtr, nid: int, buf: bytes, len: int) -> int -# char *X509_NAME_oneline(const X509_NAME *a, char *buf, int size) → 返回指向 buf 的指针 +# char *X509_NAME_oneline(const X509_NAME *a, char *buf, int size) -> returns pointer to buf extern def X509_NAME_oneline(a: X509NamePtr, buf: bytes, size: int) -> str # ============================================================ -# 4) 扩展(SAN 等) +# 4) Extensions (SAN, ...) # ============================================================ # int X509_get_ext_count(const X509 *x) extern def X509_get_ext_count(x: X509Ptr) -> int @@ -87,30 +87,30 @@ extern def X509_EXTENSION_get_data(ne: X509ExtensionPtr) -> Asn1OctetStringPtr extern def X509V3_EXT_print(out: BioPtr, ext: X509ExtensionPtr, flag: uint32, indent: int) -> int -# GENERAL_NAME 遍历(SAN:DNS/URI/IP/email) +# GENERAL_NAME iteration (SAN: DNS/URI/IP/email) extern def GENERAL_NAME_free(a: GeneralNamePtr) extern def GENERAL_NAMES_free(a: GeneralNamesPtr) # int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen) extern def GENERAL_NAME_print(out: BioPtr, gen: GeneralNamePtr) -> int -# 通用 OPENSSL 栈访问(STACK_OF 的内核,宏 sk_xxx_num/value 最终落到这) +# Generic OPENSSL stack access (backing STACK_OF; sk_xxx_num/value macros land here) # int OPENSSL_sk_num(const OPENSSL_STACK *st) extern def OPENSSL_sk_num(st: bytes) -> int # void *OPENSSL_sk_value(const OPENSSL_STACK *st, int i) extern def OPENSSL_sk_value(st: bytes, i: int) -> GeneralNamePtr # void OPENSSL_sk_free(OPENSSL_STACK *st) extern def OPENSSL_sk_free(st: bytes) -# const char *OBJ_nid2sn ... 已放 objects.vp;此处放 GNR 常量 +# const char *OBJ_nid2sn ... lives in objects.vp; GNR constants go here define GenEmail = 1 define GenDns = 2 define GenUri = 6 define GenIpadd = 7 -# ---- SAN 便捷读取(把某类 GENERAL_NAME 数量统计)---- +# ---- SAN convenience access (count GENERAL_NAME entries) ---- def x509_san_count(gns: GeneralNamesPtr) -> int: return OPENSSL_sk_num(gns) # ============================================================ -# 5) 证书链校验(X509_STORE_CTX) +# 5) Certificate chain verification (X509_STORE_CTX) # ============================================================ # X509_STORE *X509_STORE_new(void) / void X509_STORE_free(X509_STORE *v) extern def X509_STORE_new() -> X509StorePtr @@ -149,7 +149,7 @@ extern def X509_STORE_CTX_get_current_cert(ctx: X509StoreCtxPtr) -> X509Ptr extern def X509_verify_cert_error_string(n: int32) -> str # ============================================================ -# 6) ASN.1 字符串 / 时间助手(asn1.h) +# 6) ASN.1 string / time helpers (asn1.h) # ============================================================ # int ASN1_STRING_length(const ASN1_STRING *x) extern def ASN1_STRING_length(x: Asn1StringPtr) -> int @@ -157,7 +157,7 @@ extern def ASN1_STRING_length(x: Asn1StringPtr) -> int extern def ASN1_STRING_get0_data(x: Asn1StringPtr) -> bytes # int ASN1_STRING_type(const ASN1_STRING *x) extern def ASN1_STRING_type(x: Asn1StringPtr) -> int -# int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in) → 返回长度,需 CRYPTO_free(*out) +# int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in) -> returns length; caller must CRYPTO_free(*out) extern def ASN1_STRING_to_UTF8(out: bytes, inn: Asn1StringPtr) -> int # int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v) extern def ASN1_STRING_print(bp: BioPtr, v: Asn1StringPtr) -> int @@ -171,7 +171,7 @@ extern def ASN1_TIME_compare(a: Asn1TimePtr, b: Asn1TimePtr) -> int extern def ASN1_INTEGER_get(a: Asn1IntegerPtr) -> int32 # int ASN1_INTEGER_set(ASN1_INTEGER *a, long v) extern def ASN1_INTEGER_set(a: Asn1IntegerPtr, v: int32) -> int -# int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a) → 十进制打印到 BIO +# int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a) -> prints decimal to BIO extern def i2a_ASN1_INTEGER(bp: BioPtr, a: Asn1IntegerPtr) -> int # const char *ASN1_tag2str(int tag) extern def ASN1_tag2str(tag: int) -> str diff --git a/os/__init__.vp b/os/__init__.vp index 373b107..e91f1d9 100644 --- a/os/__init__.vp +++ b/os/__init__.vp @@ -111,24 +111,20 @@ def chmod(path: str, mode: int) -> int: # ============================================================================= def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: - """列举目录下的文件/子目录名。 - out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。 - 文件名通过 _mbuddy 分配内存。 - max_count 为 out_names 数组最大容量。 - 返回实际列举的条目数。失败返回 0。""" + """List file/subdirectory names under a directory. + out_names is a caller-provided char** array (each entry points to a filename allocated via _mbuddy); max_count is its capacity. Return the count, or 0 on failure.""" if platmacro.IS_WINDOWS: return os._win32.listdir(path, out_names, max_count) return os._posix.listdir(path, out_names, max_count) # ============================================================================= -# 命令执行 +# Command Execution # ============================================================================= def system(cmd: str) -> int: - """执行命令行命令(通过 C 运行时 system())。 - 命令输出直接到终端(继承父进程 stdout/stderr)。 - 返回命令的退出码(0 表示成功)。""" + """Execute a command line command (via the C runtime system()). + Output goes to the terminal (inherits parent stdout/stderr). Return the exit code (0 = success).""" if platmacro.IS_WINDOWS: return os._win32.system(cmd) return os._posix.system_impl(cmd) diff --git a/os/_posix.vp b/os/_posix.vp index dda9f84..21c4bd5 100644 --- a/os/_posix.vp +++ b/os/_posix.vp @@ -5,20 +5,20 @@ import memhub import stdlib -# POSIX 路径最大长度 +# Maximum POSIX path length define MAX_PATH_LEN = 4096 -# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化) +# Module-level global MBuddy pointer (initialized by the user in main) _mbuddy: memhub.MemBuddy* # ============================================================================= -# 文件/目录属性查询 +# File/Directory Attribute Query # ============================================================================= def _stat(path: str) -> rawptr[posix.Stat]: - """调用 stat 并返回 Stat 结构体指针。失败返回 None。 - 注意:Stat 结构体通过 _mbuddy 分配,调用者负责释放。""" + """Call stat and return a Stat struct pointer. Return None on failure. + Note: the Stat struct is allocated via _mbuddy; the caller must free it.""" st: posix.Stat = posix.Stat() result: INT = posix.stat(path, t.CPtr(&st)) if result != 0: @@ -27,7 +27,7 @@ def _stat(path: str) -> rawptr[posix.Stat]: def exists(path: str) -> bool: - """检查文件/目录是否存在""" + """Check whether the file/directory exists""" result: INT = posix.access(path, posix.F_OK) if result == 0: return True @@ -35,7 +35,7 @@ def exists(path: str) -> bool: def isdir(path: str) -> bool: - """检查路径是否为目录""" + """Check whether the path is a directory""" st: posix.Stat = posix.Stat() result: INT = posix.stat(path, t.CPtr(&st)) if result != 0: @@ -46,7 +46,7 @@ def isdir(path: str) -> bool: def isfile(path: str) -> bool: - """检查路径是否为普通文件""" + """Check whether the path is a regular file""" st: posix.Stat = posix.Stat() result: INT = posix.stat(path, t.CPtr(&st)) if result != 0: @@ -57,7 +57,7 @@ def isfile(path: str) -> bool: def getsize(path: str) -> int64: - """返回文件大小(字节)。失败返回 -1。""" + """Return the file size in bytes. Return -1 on failure.""" st: posix.Stat = posix.Stat() result: INT = posix.stat(path, t.CPtr(&st)) if result != 0: @@ -66,11 +66,11 @@ def getsize(path: str) -> int64: # ============================================================================= -# 工作目录 +# Working Directory # ============================================================================= def getcwd_impl() -> str: - """返回当前工作目录(通过 _mbuddy 分配内存)""" + """Return the current working directory (memory allocated via _mbuddy)""" buf: bytes = _mbuddy.alloc(MAX_PATH_LEN) if buf == None: return None result: CHARPTR = posix.getcwd(buf, MAX_PATH_LEN) @@ -80,7 +80,7 @@ def getcwd_impl() -> str: def chdir_impl(path: str) -> int: - """切换当前工作目录。成功返回 0,失败返回 -1。""" + """Switch the current working directory. Return 0 on success, -1 on failure.""" result: INT = posix.chdir(path) if result != 0: return -1 @@ -88,12 +88,12 @@ def chdir_impl(path: str) -> int: # ============================================================================= -# 文件/目录操作 +# File/Directory Operations # ============================================================================= def mkdir_impl(path: str) -> int: - """创建目录。成功返回 0,失败返回 -1。 - 默认权限 0o755(rwxr-xr-x)。""" + """Create a directory. Return 0 on success, -1 on failure. + Default permissions are 0o755 (rwxr-xr-x).""" result: INT = posix.mkdir(path, 0o755) if result != 0: return -1 @@ -101,7 +101,7 @@ def mkdir_impl(path: str) -> int: def remove(path: str) -> int: - """删除文件。成功返回 0,失败返回 -1。""" + """Delete a file. Return 0 on success, -1 on failure.""" result: INT = posix.unlink(path) if result != 0: return -1 @@ -109,7 +109,7 @@ def remove(path: str) -> int: def rmdir_impl(path: str) -> int: - """删除空目录。成功返回 0,失败返回 -1。""" + """Delete an empty directory. Return 0 on success, -1 on failure.""" result: INT = posix.rmdir(path) if result != 0: return -1 @@ -117,7 +117,7 @@ def rmdir_impl(path: str) -> int: def rename_impl(old_path: str, new_path: str) -> int: - """重命名/移动文件或目录。成功返回 0,失败返回 -1。""" + """Rename/move a file or directory. Return 0 on success, -1 on failure.""" result: INT = posix.rename(old_path, new_path) if result != 0: return -1 @@ -125,8 +125,8 @@ def rename_impl(old_path: str, new_path: str) -> int: def chmod_impl(path: str, mode: int) -> int: - """修改文件权限。成功返回 0,失败返回 -1。 - mode 为 POSIX 权限位(如 0o755)。""" + """Modify file permissions. Return 0 on success, -1 on failure. + mode is POSIX permission bits (e.g. 0o755).""" result: INT = posix.chmod(path, posix.mode_t(mode)) if result != 0: return -1 @@ -134,15 +134,12 @@ def chmod_impl(path: str, mode: int) -> int: # ============================================================================= -# 目录列举(opendir / readdir / closedir) +# Directory Listing (opendir / readdir / closedir) # ============================================================================= def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: - """列举目录下的文件/子目录名。 - out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。 - 文件名通过 _mbuddy 分配内存。 - max_count 为 out_names 数组最大容量。 - 返回实际列举的条目数。失败返回 0。""" + """List file/subdirectory names under a directory. + out_names is a caller-provided char** array (each entry points to a filename allocated via _mbuddy); max_count is its capacity. Return the count, or 0 on failure.""" dirp: posix.DIR = posix.opendir(path) if dirp == None: return 0 @@ -153,7 +150,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: if entry == None: break - # 读取 d_name 字段 + # Read the d_name field name: str = entry.d_name if name[0] == '.': if name[1] == 0: @@ -163,7 +160,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: # ".." continue - # 复制文件名到新分配的内存 + # Copy the filename into newly allocated memory name_len: sizet = len(name) name_buf: bytes = _mbuddy.alloc(name_len + 1) if name_buf == None: break @@ -180,11 +177,10 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: # ============================================================================= -# 命令执行 +# Command Execution # ============================================================================= def system_impl(cmd: str) -> int: - """执行命令行命令(通过 C 运行时 system())。 - 命令输出直接到子进程的终端(继承父进程 stdout/stderr)。 - 返回命令的退出码(0 表示成功)。""" + """Execute a command line command (via the C runtime system()). + Output goes to the child terminal (inherits parent stdout/stderr). Return the exit code (0 = success).""" return stdlib.system(cmd) diff --git a/os/_win32.vp b/os/_win32.vp index f2376e0..f778bf6 100644 --- a/os/_win32.vp +++ b/os/_win32.vp @@ -6,27 +6,27 @@ import memhub import stdlib -# Windows 路径最大长度 +# Maximum Windows path length define MAX_PATH_LEN = 260 # INVALID_FILE_ATTRIBUTES define INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF -# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化) +# Module-level global MBuddy pointer (initialized by the user in main) _mbuddy: memhub.MemBuddy* # ============================================================================= -# 文件/目录属性查询 +# File/Directory Attribute Query # ============================================================================= def _get_attrs(path: str) -> ULONG: - """获取文件属性。失败返回 INVALID_FILE_ATTRIBUTES。""" + """Get file attributes. Return INVALID_FILE_ATTRIBUTES on failure.""" return w32.win32file.GetFileAttributesA(path) def exists(path: str) -> bool: - """检查文件/目录是否存在""" + """Check whether the file/directory exists""" attrs: ULONG = _get_attrs(path) if attrs == INVALID_FILE_ATTRIBUTES: return False @@ -34,7 +34,7 @@ def exists(path: str) -> bool: def isdir(path: str) -> bool: - """检查路径是否为目录""" + """Check whether the path is a directory""" attrs: ULONG = _get_attrs(path) if attrs == INVALID_FILE_ATTRIBUTES: return False @@ -44,7 +44,7 @@ def isdir(path: str) -> bool: def isfile(path: str) -> bool: - """检查路径是否为普通文件""" + """Check whether the path is a regular file""" attrs: ULONG = _get_attrs(path) if attrs == INVALID_FILE_ATTRIBUTES: return False @@ -54,7 +54,7 @@ def isfile(path: str) -> bool: def getsize(path: str) -> int64: - """返回文件大小(字节)。失败返回 -1。""" + """Return the file size in bytes. Return -1 on failure.""" handle: w32.win32base.HANDLE = w32.win32file.CreateFileA( path, w32.win32file.GENERIC_READ, w32.win32file.FILE_SHARE_READ, t.CPtr(0), w32.win32file.OPEN_EXISTING, w32.win32file.FILE_ATTRIBUTE_NORMAL, t.CPtr(0) @@ -70,18 +70,18 @@ def getsize(path: str) -> int64: # ============================================================================= -# 工作目录 +# Working Directory # ============================================================================= def getcwd() -> str: - """返回当前工作目录(通过 _mbuddy 分配内存)""" + """Return the current working directory (memory allocated via _mbuddy)""" buf: bytes = _mbuddy.alloc(MAX_PATH_LEN + 1) if buf == None: return None length: ULONG = w32.win32file.GetCurrentDirectoryA(MAX_PATH_LEN + 1, buf) if length == 0: return None if length > MAX_PATH_LEN: - # 缓冲区不够,重新分配 + # Buffer too small; reallocate buf2: bytes = _mbuddy.alloc(length + 1) if buf2 == None: return None w32.win32file.GetCurrentDirectoryA(length + 1, buf2) @@ -92,7 +92,7 @@ def getcwd() -> str: def chdir(path: str) -> int: - """切换当前工作目录。成功返回 0,失败返回 -1。""" + """Switch the current working directory. Return 0 on success, -1 on failure.""" result: BOOL = w32.win32file.SetCurrentDirectoryA(path) if result == 0: return -1 @@ -100,12 +100,12 @@ def chdir(path: str) -> int: # ============================================================================= -# 文件/目录操作 +# File/Directory Operations # ============================================================================= def mkdir(path: str) -> int: - """创建目录。成功返回 0,失败返回 -1。 - 路径按 UTF-8 解释时转 UTF-16 走 CreateDirectoryW, 否则退回 A 系列""" + """Create a directory. Return 0 on success, -1 on failure. + If path is valid UTF-8, convert to UTF-16 and use CreateDirectoryW; otherwise fall back to the A-series.""" if w32.win32base.utf8_is_valid(path) == 0: result: BOOL = w32.win32file.CreateDirectoryA(path, t.CPtr(0)) if result == 0: @@ -125,7 +125,7 @@ def mkdir(path: str) -> int: def remove(path: str) -> int: - """删除文件。成功返回 0,失败返回 -1。""" + """Delete a file. Return 0 on success, -1 on failure.""" result: BOOL = w32.win32file.DeleteFileA(path) if result == 0: return -1 @@ -133,7 +133,7 @@ def remove(path: str) -> int: def rmdir(path: str) -> int: - """删除空目录。成功返回 0,失败返回 -1。""" + """Delete an empty directory. Return 0 on success, -1 on failure.""" result: BOOL = w32.win32file.RemoveDirectoryA(path) if result == 0: return -1 @@ -141,7 +141,7 @@ def rmdir(path: str) -> int: def rename(old_path: str, new_path: str) -> int: - """重命名/移动文件或目录。成功返回 0,失败返回 -1。""" + """Rename/move a file or directory. Return 0 on success, -1 on failure.""" result: BOOL = w32.win32file.MoveFileExA( old_path, new_path, w32.win32file.MOVEFILE_REPLACE_EXISTING | w32.win32file.MOVEFILE_COPY_ALLOWED @@ -152,16 +152,15 @@ def rename(old_path: str, new_path: str) -> int: def chmod(path: str, mode: int) -> int: - """修改文件属性(Windows 简化版:只处理只读位)。 - mode 的 S_IWUSR 位(0o200)为 0 时设为只读,为 1 时设为可写。 - 成功返回 0,失败返回 -1。""" + """Modify file attributes (simplified Windows: read-only bit only). + S_IWUSR (0o200) in mode: 0 sets read-only, 1 sets writable. Return 0 on success, -1 on failure.""" if (mode & 0o200) != 0: - # 可写:清除只读属性 + # Writable: clear the read-only attribute result: BOOL = w32.win32file.SetFileAttributesA( path, w32.win32file.FILE_ATTRIBUTE_NORMAL ) else: - # 只读:设置只读属性 + # Read-only: set the read-only attribute result: BOOL = w32.win32file.SetFileAttributesA( path, w32.win32file.FILE_ATTRIBUTE_READONLY ) @@ -171,16 +170,13 @@ def chmod(path: str, mode: int) -> int: # ============================================================================= -# 目录列举(FindFirstFile / FindNextFile) +# Directory Listing (FindFirstFile / FindNextFile) # ============================================================================= def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: - """列举目录下的文件/子目录名。 - out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。 - 文件名通过 _mbuddy 分配内存。 - max_count 为 out_names 数组最大容量。 - 返回实际列举的条目数。失败返回 0。""" - # 构造搜索模式:path\* + """List file/subdirectory names under a directory. + out_names is a caller-provided char** array (each entry points to a filename allocated via _mbuddy); max_count is its capacity. Return the count, or 0 on failure.""" + # Build search pattern: path\* path_len: sizet = len(path) pattern: bytes = _mbuddy.alloc(path_len + 3) if pattern == None: return 0 @@ -188,7 +184,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: for ch in path: pattern[i] = ch i += 1 - # 添加 \* 后缀 + # Append \* suffix if path[path_len - 1] != '\\' and path[path_len - 1] != '/': pattern[i] = '\\' i += 1 @@ -203,7 +199,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: count: ULONG = 0 while count < max_count: - # 跳过 . 和 .. + # Skip . and .. name: str = find_data.cFileName if name[0] == '.': if name[1] == 0: @@ -217,7 +213,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: break continue - # 复制文件名到新分配的内存 + # Copy the filename into newly allocated memory name_len: sizet = len(name) name_buf: bytes = _mbuddy.alloc(name_len + 1) if name_buf == None: break @@ -237,11 +233,10 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG: # ============================================================================= -# 命令执行 +# Command Execution # ============================================================================= def system(cmd: str) -> int: - """执行命令行命令(通过 C 运行时 system())。 - 命令输出直接到子进程的终端(继承父进程 stdout/stderr)。 - 返回命令的退出码(0 表示成功)。""" + """Execute a command line command (via the C runtime system()). + Output goes to the child terminal (inherits parent stdout/stderr). Return the exit code (0 = success).""" return stdlib.system(cmd) diff --git a/os/path.vp b/os/path.vp index 2dcdc91..f6d4ab7 100644 --- a/os/path.vp +++ b/os/path.vp @@ -5,32 +5,32 @@ import memhub import platmacro -# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化) +# Module-level global MBuddy pointer (initialized by the user in main) _mbuddy: memhub.MemBuddy* # ============================================================================= -# 路径分隔符 +# Path Separators # ============================================================================= def sep() -> char: - """返回当前平台的路径分隔符(Windows: \\,POSIX: /)""" + """Return the platform path separator (Windows: \\, POSIX: /)""" if platmacro.IS_WINDOWS: return '\\' return '/' def _is_sep(ch: char) -> bool: - """判断字符是否为路径分隔符(/ 或 \\,跨平台识别)""" + """Return True if ch is a path separator (/ or \\, cross-platform)""" return ch == '/' or ch == '\\' # ============================================================================= -# 内部辅助函数 +# Internal Helpers # ============================================================================= def _dup_str(s: str) -> str: - """复制字符串(通过全局 MBuddy 分配内存)""" + """Duplicate a string; memory allocated via the global MBuddy""" if s == None: return None length: sizet = len(s) buf: bytes = _mbuddy.alloc(length + 1) @@ -44,7 +44,7 @@ def _dup_str(s: str) -> str: def _substr(s: str, start: sizet, end: sizet) -> str: - """提取子串 s[start:end](通过全局 MBuddy 分配内存)""" + """Extract substring s[start:end]; memory allocated via the global MBuddy""" if s == None: return None if start >= end: return _dup_str("") result_len: sizet = end - start @@ -61,18 +61,17 @@ def _substr(s: str, start: sizet, end: sizet) -> str: # ============================================================================= -# 路径判断 +# Path Predicates # ============================================================================= def isabs(path: str) -> bool: - """判断路径是否为绝对路径。 - POSIX: 以 / 开头。 - Windows: 以 \\ 开头(UNC)或 X:\\ / X:/ 开头(盘符)。""" + """Return True if path is absolute. + POSIX: starts with /. Windows: starts with \\ (UNC) or X:\\ / X:/ (drive).""" if path == None: return False if path[0] == 0: return False if path[0] == '/' or path[0] == '\\': return True if platmacro.IS_WINDOWS: - # 检查 X:\ 或 X:/ 模式 + # Check for X:\ or X:/ pattern c0: char = path[0] if (c0 >= 'A' and c0 <= 'Z') or (c0 >= 'a' and c0 <= 'z'): if path[1] == ':': @@ -82,11 +81,11 @@ def isabs(path: str) -> bool: # ============================================================================= -# 路径拼接与分割 +# Path Join and Split # ============================================================================= def join(path1: str, path2: str) -> str: - """拼接两个路径。如果 path2 是绝对路径,返回 path2 的副本。""" + """Join two paths. If path2 is absolute, return a copy of path2.""" if path1 == None or path1[0] == 0: return _dup_str(path2) if path2 == None or path2[0] == 0: @@ -97,7 +96,7 @@ def join(path1: str, path2: str) -> str: len1: sizet = len(path1) len2: sizet = len(path2) - # 检查 path1 是否以分隔符结尾 + # Check whether path1 ends with a separator need_sep: bool = True if _is_sep(path1[len1 - 1]): need_sep = False @@ -109,18 +108,18 @@ def join(path1: str, path2: str) -> str: buf: bytes = _mbuddy.alloc(total) if buf == None: return None - # 复制 path1 + # Copy path1 i: sizet = 0 for ch in path1: buf[i] = ch i += 1 - # 添加分隔符 + # Add separator if need_sep: buf[i] = sep() i += 1 - # 复制 path2 + # Copy path2 for ch in path2: buf[i] = ch i += 1 @@ -130,54 +129,54 @@ def join(path1: str, path2: str) -> str: def basename(path: str) -> str: - """返回路径的文件名部分(最后一个分隔符之后的内容)""" + """Return the filename part of path (content after the last separator)""" if path == None: return None length: sizet = len(path) if length == 0: return _dup_str("") - # 从后向前找分隔符 + # Scan backward for a separator i: sizet = length while i > 0: i -= 1 if _is_sep(path[i]): return _substr(path, i + 1, length) - # 没有分隔符,返回整个路径的副本 + # No separator; return a copy of the whole path return _dup_str(path) def dirname(path: str) -> str: - """返回路径的目录部分(最后一个分隔符之前的内容)""" + """Return the directory part of path (content before the last separator)""" if path == None: return None length: sizet = len(path) if length == 0: return _dup_str("") - # 从后向前找分隔符 + # Scan backward for a separator i: sizet = length while i > 0: i -= 1 if _is_sep(path[i]): if i == 0: - # 路径以分隔符开头,返回根目录 + # Path starts with a separator; return the root directory return _dup_str("/") return _substr(path, 0, i) - # 没有分隔符,返回空字符串 + # No separator; return an empty string return _dup_str("") # ============================================================================= -# 扩展名处理 +# Extension Handling # ============================================================================= def splitext_name(path: str) -> str: - """返回路径去除扩展名后的主名部分。 - 扩展名从最后一个 '.' 开始(该 '.' 不能是路径最后一个组件的第一个字符)。""" + """Return the stem of path with the extension removed. + The extension starts at the last '.' (not the first char of the last component).""" if path == None: return None length: sizet = len(path) if length == 0: return _dup_str("") - # 从后向前找 '.',遇到分隔符停止 + # Scan backward for '.'; stop at a separator i: sizet = length while i > 0: i -= 1 @@ -186,17 +185,17 @@ def splitext_name(path: str) -> str: if _is_sep(path[i]): break - # 没有扩展名,返回整个路径的副本 + # No extension; return a copy of the whole path return _dup_str(path) def splitext_ext(path: str) -> str: - """返回路径的扩展名部分(包含 '.')。无扩展名时返回空字符串。""" + """Return the extension part of path (including '.'). Return an empty string if none.""" if path == None: return None length: sizet = len(path) if length == 0: return _dup_str("") - # 从后向前找 '.',遇到分隔符停止 + # Scan backward for '.'; stop at a separator i: sizet = length while i > 0: i -= 1 @@ -205,22 +204,22 @@ def splitext_ext(path: str) -> str: if _is_sep(path[i]): break - # 没有扩展名,返回空字符串 + # No extension; return an empty string return _dup_str("") # ============================================================================= -# 路径规范化 +# Path Normalization # ============================================================================= def normpath(path: str) -> str: - """规范化路径:统一分隔符、消除多余的 . 和 .. 组件、合并连续分隔符。 - 注意:不解析符号链接,不调用系统 API。""" + """Normalize path: unify separators, remove redundant . and .. components, merge consecutive separators. + Note: does not resolve symlinks or call system APIs.""" if path == None: return None length: sizet = len(path) if length == 0: return _dup_str(".") - # 分配输出缓冲区(最坏情况:长度不变 + 前缀 /) + # Allocate output buffer (worst case: same length + leading /) buf: bytes = _mbuddy.alloc(length + 2) if buf == None: return None @@ -228,7 +227,7 @@ def normpath(path: str) -> str: i: sizet = 0 s: char = sep() - # 处理绝对路径前缀 + # Handle the absolute path prefix is_absolute: bool = False if _is_sep(path[0]): is_absolute = True @@ -236,7 +235,7 @@ def normpath(path: str) -> str: out_pos += 1 i = 1 - # Windows 盘符前缀(X:\ 或 X:/) + # Windows drive prefix (X:\ or X:/) drive_prefix_len: sizet = 0 if platmacro.IS_WINDOWS: if length >= 3: @@ -254,48 +253,48 @@ def normpath(path: str) -> str: i = 3 is_absolute = True - # 逐组件处理 + # Process component by component while i < length: - # 跳过连续分隔符 + # Skip consecutive separators while i < length and _is_sep(path[i]): i += 1 if i >= length: break - # 提取组件 + # Extract component comp_start: sizet = i while i < length and not _is_sep(path[i]): i += 1 comp_len: sizet = i - comp_start - # 处理 "." 组件:跳过 + # Handle "." component: skip if comp_len == 1 and path[comp_start] == '.': continue - # 处理 ".." 组件:回退一级 + # Handle ".." component: go up one level if comp_len == 2 and path[comp_start] == '.' and path[comp_start + 1] == '.': if out_pos > drive_prefix_len: - # 回退到上一个分隔符前 + # Rewind to just before the previous separator out_pos -= 1 while out_pos > drive_prefix_len and not _is_sep(buf[out_pos - 1]): out_pos -= 1 - # 如果回退到了根目录前缀,不删除前缀 + # If rewound to the root prefix, keep the prefix if out_pos == drive_prefix_len and is_absolute: out_pos = drive_prefix_len if drive_prefix_len == 0: - # POSIX 根:保留 / + # POSIX root: keep / buf[out_pos] = s out_pos += 1 continue - # 普通组件:复制到输出 + # Normal component: copy to output if out_pos > drive_prefix_len and not (drive_prefix_len == 0 and out_pos == 1 and is_absolute): buf[out_pos] = s out_pos += 1 elif out_pos == 0 and not is_absolute: - pass # 第一个组件,不需要前导分隔符 + pass # First component, no leading separator needed elif is_absolute and out_pos == drive_prefix_len: - # 已经有前导分隔符,不重复添加 + # Leading separator already present, do not add again pass else: buf[out_pos] = s @@ -307,7 +306,7 @@ def normpath(path: str) -> str: out_pos += 1 j += 1 - # 处理空结果 + # Handle empty result if out_pos == 0: buf[out_pos] = '.' out_pos += 1 diff --git a/requests/__init__.vp b/requests/__init__.vp index d5ac28d..7d605c8 100644 --- a/requests/__init__.vp +++ b/requests/__init__.vp @@ -34,12 +34,12 @@ define HTTP_PORT = 80 define HTTP_BUFSIZ = 4096 define GZIP_BUFSIZ = 65536 -# 模块内重定向 / 失败诊断(静态缓冲) +# Module-internal redirect / failure diagnostics (static buffer) _http_err_store: t.CArray[char, 512] def last_error() -> str: - """返回 HTTP 层最近一次失败的原因(无则空串)。""" + """Last HTTP-layer failure reason (empty string if none).""" return _http_err_store @@ -61,8 +61,8 @@ class Response: status_code: INT # HTTP status code (e.g., 200, 404) text: str # Response body text body_len: INT # Response body length - _raw_buf: str # 原始响应缓冲(含头与正文,release 释放) - _body_owned: str # 独立解码正文缓冲(chunked 等解码结果,release 一并释放) + _raw_buf: str # raw response buffer (headers+body, freed by release) + _body_owned: str # separately decoded body buffer (chunked etc., freed by release) # ============================================================ @@ -185,7 +185,7 @@ def _FindBodyStart(resp_text: str, resp_len: INT) -> INT: # ============================================================ -# 公共辅助(供 requests.https 等子模块复用) +# Public helpers (reused by requests.https and other submodules) # ============================================================ def create_response() -> Response*: @@ -207,35 +207,29 @@ def find_body_start(resp_text: str, resp_len: INT) -> INT: # ============================================================ -# 响应解析(header 查询 / chunked 解码 / 统一 parse_response) +# Response parsing (header lookup / chunked decode / unified parse_response) # ============================================================ def _CIEq(a: char, b: char) -> bool: - """大小写不敏感字符相等(仅 ASCII)。""" + """Case-insensitive char equality (ASCII only).""" if a >= 'A' and a <= 'Z': a = char(INT(a) - INT('A') + INT('a')) if b >= 'A' and b <= 'Z': b = char(INT(b) - INT('A') + INT('a')) return a == b def header_value(resp: Response*, name: str, out: str, out_size: INT) -> INT: - """在原始响应中按名查询响应头(大小写不敏感,返回首值)。 - - Args: - resp: Response(其 _raw_buf 保留原始响应) - name: 头名称,如 "Content-Type" - out / out_size: 值写入缓冲 - Returns: - 0 找到并拷贝;-1 未找到或参数非法。 + """Look up a response header by name in the raw response (case-insensitive, first value). + Returns 0 on found+copied; -1 if missing or bad args. """ if resp is None or resp._raw_buf is None or name is None or out is None: return -1 p: str = resp._raw_buf - # 跳过状态行 + # Skip status line while p[0] != '\0' and p[0] != '\n': p += 1 if p[0] == '\n': p += 1 - # 逐头行匹配 + # Match each header line while p[0] != '\0' and p[0] != '\r' and p[0] != '\n': h: str = p q: str = name @@ -246,7 +240,7 @@ def header_value(resp: Response*, name: str, out: str, out_size: INT) -> INT: break h += 1 q += 1 - # name 匹配后允许 [空格]* ':'(RFC 无空格,宽松兼容) + # Allow [spaces]* ':' after name (RFC has none; lenient) while h[0] == ' ': h += 1 if matched and h[0] == ':': @@ -260,7 +254,7 @@ def header_value(resp: Response*, name: str, out: str, out_size: INT) -> INT: h += 1 out[i] = 0 return 0 - # 跳到下一行 + # Skip to next line while p[0] != '\0' and p[0] != '\n': p += 1 if p[0] == '\n': p += 1 @@ -276,20 +270,15 @@ def _HexVal(ch: char) -> INT: def decode_chunked(src: str, src_len: INT, dst: str, dst_cap: INT) -> INT: - """按 RFC 7230 解码 chunked 正文(不处理 chunk-ext,trailer 忽略)。 - - Args: - src / src_len: chunked 编码数据 - dst / dst_cap: 解码输出缓冲 - Returns: - 解码字节数;失败返回 -1。 + """Decode chunked body per RFC 7230 (chunk-ext ignored, trailer ignored). + Returns decoded byte count; -1 on failure. """ if src is None or dst is None: return -1 r: INT = 0 w: INT = 0 while r < src_len: - # 1) chunk size(hex,可带 ;ext 扩展) + # 1) chunk size (hex, optional ;ext) size: INT = 0 digit_seen: bool = False while r < src_len: @@ -302,13 +291,13 @@ def decode_chunked(src: str, src_len: INT, dst: str, dst_cap: INT) -> INT: size = size * 16 + hv r += 1 if not digit_seen: return -1 - # 2) 跳过扩展与行尾 + # 2) Skip extension and line end while r < src_len and src[r] != '\n': r += 1 if r < src_len and src[r] == '\n': r += 1 - # 3) 结束块 + # 3) Terminator chunk if size == 0: return w - # 4) 拷贝数据 + 跳过 CRLF + # 4) Copy data + skip CRLF if w + size > dst_cap: return -1 for k in range(size): dst[w + k] = src[r + k] @@ -321,13 +310,8 @@ def decode_chunked(src: str, src_len: INT, dst: str, dst_cap: INT) -> INT: def parse_response(resp: Response*, total: INT) -> INT: - """统一解析已接收的原始响应并填充 Response 字段。 - - - 解析状态码 / 定位正文 - - Transfer-Encoding: chunked 时解码到独立缓冲(_body_owned), - 原始缓冲 _raw_buf 保留(供 header_value 查询) - Returns: - 0 成功;-1 失败(此时 text=None / body_len=0)。 + """Parse the received raw response into Response fields; chunked Transfer-Encoding decodes + into _body_owned while _raw_buf is kept. Returns 0 ok; -1 fail (text=None, body_len=0). """ if resp is None or resp._raw_buf is None: return -1 buf: str = resp._raw_buf @@ -340,7 +324,7 @@ def parse_response(resp: Response*, total: INT) -> INT: resp.body_len = 0 return 0 - # 检测 Transfer-Encoding: chunked + # Detect Transfer-Encoding: chunked te_buf: t.CArray[char, 64] chunked: bool = False if header_value(resp, "Transfer-Encoding", te_buf, 64) == 0: @@ -367,7 +351,7 @@ def parse_response(resp: Response*, total: INT) -> INT: resp.text = buf + body_offset resp.body_len = total - body_offset - # ---- Content-Encoding: gzip 解压(zlib.pyzlib,纯 Viper 实现)---- + # ---- Content-Encoding: gzip decompress (zlib.pyzlib, pure Viper) ---- ce_buf: t.CArray[char, 64] if header_value(resp, "Content-Encoding", ce_buf, 64) == 0 \ and string.strstr(ce_buf, "gzip") is not None \ @@ -385,18 +369,18 @@ def parse_response(resp: Response*, total: INT) -> INT: resp.text = gz_str resp.body_len = INT(olen2) else: - # 解压失败:保留原始 body,记录诊断信息 + # Decompress failed: keep raw body, record diagnostics stdio.snprintf(_http_err_store, 512, "gzip decode failed: %s", pyzlib.get_error()) return 0 # ============================================================ -# 公共 API +# Public API # ============================================================ def _HttpGetOnce(url: str) -> Response*: - """HTTP GET 单次请求(不跟随重定向)。请求头带 Accept-Encoding: gzip。""" + """HTTP GET single request (no redirect). Sends Accept-Encoding: gzip.""" if _mbuddy is None: return None resp: Response* = _AllocResponse() @@ -469,7 +453,7 @@ def _HttpGetOnce(url: str) -> Response*: def _HttpPostOnce(url: str, data: str = None, content_type: str = "application/x-www-form-urlencoded") -> Response*: - """HTTP POST 单次请求(不跟随重定向)。请求头带 Accept-Encoding: gzip。""" + """HTTP POST single request (no redirect). Sends Accept-Encoding: gzip.""" if _mbuddy is None: return None resp: Response* = _AllocResponse() @@ -545,13 +529,12 @@ def _HttpPostOnce(url: str, data: str = None, content_type: str = "application/x # ============================================================ -# 重定向跟随 +# Redirect following # ============================================================ def _ResolveRedirectUrl(base: str, loc: str, out: str, cap: INT) -> INT: - """把 Location 合并为完整 URL(支持绝对 / 协议相对 / 根相对),结果写 out。 - - Returns: 0 成功;-1 失败。 + """Merge Location into a full URL (absolute / protocol-relative / root-relative), written to out. + Returns 0 on success; -1 on failure. """ if base is None or loc is None or out is None: return -1 lb: INT = INT(string.strlen(loc)) @@ -559,7 +542,7 @@ def _ResolveRedirectUrl(base: str, loc: str, out: str, cap: INT) -> INT: if lb >= cap: return -1 string.strcpy(out, loc) return 0 - # 取 base 的 scheme://authority 前缀 + # Take scheme://authority prefix of base q: str = base while q[0] != '\0' and q[0] != ':': q += 1 if q[0] != ':': return -1 @@ -591,7 +574,7 @@ def _ResolveRedirectUrl(base: str, loc: str, out: str, cap: INT) -> INT: def _HttpRedirectLoop(method: str, url: str, data: str, content_type: str) -> Response*: - """GET/POST + 重定向跟随(http:// 链,最多 5 跳)。""" + """GET/POST with redirect following (http:// chain, max 5 hops).""" if _mbuddy is None: return None cur: str = _mbuddy.alloc(2048) @@ -660,18 +643,14 @@ def _IsHttpRedirect(code: INT) -> bool: def get(url: str) -> Response*: - """HTTP GET(自动跟随 3xx 重定向,最多 5 跳)。 - - Args: - url: Full URL (e.g., "http://example.com/path") - Returns: - Pointer to a Response object, None on failure (see last_error()). + """HTTP GET (follows 3xx redirects, max 5 hops). Returns Response* or None + on failure (see last_error()). """ return _HttpRedirectLoop("GET", url, None, None) def post(url: str, data: str = None, content_type: str = "application/x-www-form-urlencoded") -> Response*: - """HTTP POST(自动跟随 3xx 重定向,最多 5 跳)。""" + """HTTP POST (follows 3xx redirects, max 5 hops).""" return _HttpRedirectLoop("POST", url, data, content_type) @@ -691,20 +670,12 @@ def release(resp: Response*): # ============================================================ -# 大文件 Range 下载原语(requests.download 使用) -# -# _DlOnce 单次发起 "Range: bytes=start-end" 的 GET(不带 Accept-Encoding, -# 保证响应是原始字节),把响应体写入调用方提供的 dst 缓冲。 -# 线程安全:本函数不使用 _mbuddy(响应/请求缓冲一律 stdlib), -# socket 连接独立,供多线程并行切片下载并发调用。 -# -# 返回: >=0 响应体字节数;-1 参数错;-2 3xx 重定向(Location 写 out_loc, -# 由 download 模块跟随);-3 其它失败(last_error() 查看原因)。 -# out_status/out_total 分别输出 HTTP 状态码与 Content-Range 总大小。 +# Large-file Range download primitive for requests.download: _DlOnce issues a "Range: bytes= +# start-end" GET (no Accept-Encoding) into dst. Thread-safe (no _mbuddy, own socket). >=0 bytes; -1/-2/-3. # ============================================================ def _RawHeaderValue(buf: str, total: INT, name: str, out: str, out_size: INT) -> INT: - """对原始响应头区做大小写不敏感查询(0=命中并拷贝值;-1=未命中)。""" + """Case-insensitive lookup on raw response header area (0=hit+copied; -1=miss).""" if buf is None or name is None or out is None or out_size <= 0: return -1 body: INT = _FindBodyStart(buf, total) @@ -741,7 +712,7 @@ def _RawHeaderValue(buf: str, total: INT, name: str, out: str, out_size: INT) -> def _StatusRaw(buf: str) -> INT: - """从 "HTTP/1.1 206 ..." 首行解析状态码。""" + """Parse status code from the first line ("HTTP/1.1 206 ...").""" if buf is None: return 0 i: INT = 0 @@ -759,7 +730,7 @@ def _StatusRaw(buf: str) -> INT: def _CLenOf(buf: str, total: INT) -> INT: - """读取 Content-Length(无则 -1)。""" + """Read Content-Length (-1 if absent).""" hbuf: t.CArray[char, 32] if _RawHeaderValue(buf, total, "Content-Length", hbuf, 32) != 0: return -1 @@ -772,7 +743,7 @@ def _CLenOf(buf: str, total: INT) -> INT: def _CRTotalOf(buf: str, total: INT) -> sizet: - """从 Content-Range: bytes s-e/TOTAL 解析 TOTAL(非 206/缺头则 0)。""" + """Parse TOTAL from Content-Range: bytes s-e/TOTAL (0 if not 206 / header absent).""" hbuf: t.CArray[char, 128] if _RawHeaderValue(buf, total, "Content-Range", hbuf, 128) != 0: return 0 @@ -792,7 +763,7 @@ def _CRTotalOf(buf: str, total: INT) -> sizet: def _DlOnce(url: str, start: sizet, count: INT, dst: str, dst_cap: INT, out_status: INT*, out_loc: str, out_loccap: INT, out_total: sizet*) -> INT: - """HTTP 单次 Range 拉取(线程安全,见模块注释)。""" + """HTTP single Range fetch (thread-safe; see module comment).""" if url is None or dst is None or dst_cap <= 0 \ or out_status is None or out_total is None: return -1 diff --git a/requests/download.vp b/requests/download.vp index 1658c01..5991941 100644 --- a/requests/download.vp +++ b/requests/download.vp @@ -40,7 +40,7 @@ _dl_err_store: t.CArray[char, 512] def last_error() -> str: - """返回最近一次下载失败原因文本(无则空串)。""" + """Last download failure reason (empty string if none).""" return _dl_err_store @@ -52,7 +52,7 @@ def _dl_set_err(text: str): def _dl_strdup(s: str) -> str: - """stdlib 堆上复制字符串(调用方 free),None 失败。""" + """Duplicate string on stdlib heap (caller frees); None on failure.""" if s is None: return None n: sizet = string.strlen(s) @@ -66,13 +66,14 @@ def _dl_strdup(s: str) -> str: # ============================================================ -# 单切片拉取(含重定向跟随,≤5 跳;http/https 按 scheme 分发) +# Single-slice fetch (redirect follow, <=5 hops; dispatch by http/https scheme) # ============================================================ def _dl_slice(url: str, pos: sizet, want: INT, verify: bool, ca_file: str, dst: str, dst_cap: INT, out_total: sizet*) -> INT: - """拉取 [pos, pos+want) 一片,返回体字节数写入 dst(dst 内正文起始需 - 调用方用 _dl_body_off 定位);-2 重定向超限;-3 网络/服务器失败。""" + """Fetch slice [pos, pos+want); body bytes written to dst (locate body via _dl_body_off). + Returns -2 on too many redirects; -3 on network/server failure. + """ if url is None or dst is None or out_total is None: return -1 cur: str = _dl_strdup(url) @@ -118,16 +119,16 @@ def _dl_slice(url: str, pos: sizet, want: INT, verify: bool, ca_file: str, def _dl_body_off(dst: str, dst_cap: INT) -> INT: - """定位响应体(头结束处)在 dst 中的偏移,找不到返回 -1。""" + """Offset of response body (after headers) in dst; -1 if not found.""" return requests._FindBodyStart(dst, dst_cap) # ============================================================ -# 文件写入(按偏移落盘,支持多线程并发写不同区段) +# File write (write at offset; supports concurrent threads writing distinct ranges) # ============================================================ def _dl_write_at(path: str, src: str, length: INT, offset: sizet) -> INT: - """打开文件(不存在则创建),Seek 到 offset 写 length 字节。0 成功;-1 失败。""" + """Open file (create if absent), seek to offset, write length bytes. 0 ok; -1 fail.""" if path is None or src is None or length <= 0: return -1 h: wb.HANDLE = wf.CreateFileA( @@ -136,21 +137,14 @@ def _dl_write_at(path: str, src: str, length: INT, offset: sizet) -> INT: None, wf.OPEN_ALWAYS, wf.FILE_ATTRIBUTE_NORMAL, None) if h == wb.INVALID_HANDLE_VALUE: return -1 - # 用 32 位版 SetFilePointer:lDistanceToMove 按值传 LONG,无结构体 ABI 歧义。 - # 不用 SetFilePointerEx:win32file.vp:93 声明为 - # liDistanceToMove: LARGE_INTEGER | t.CPtr - # 传 c.Addr(li) 时该联合实参按"指针"传,而真实 Win32 的 liDistanceToMove 是 - # 按值传 8 字节 ⇒ 移动距离变成 li 的地址数值(与 VET 已修掉的 VFs.SeekTo 同根因, - # 见 VET/App/lib/VPe.vp:286 的绕行说明);可用先例:includes/w32/fileio.vp:169 - # 的 File.seek 就走 32 位版。 - # lpDistanceToMoveHigh=NULL ⇒ 距离限定 32 位有符号(|offset| < 2 GiB), - # 故先显式上界守卫:超范围直接判失败,绝不静默 seek 到错误位置。 + # Use 32-bit SetFilePointer (lDistanceToMove passed by value as LONG; no struct ABI ambiguity). + # lpDistanceToMoveHigh=NULL limits distance to 32-bit signed (<2GiB), so guard the upper bound first. if offset > sizet(0x7FFFFFFF): wb.CloseHandle(h) return -1 ok_seek: LONG = wf.SetFilePointer(h, LONG(offset), None, wf.FILE_BEGIN) - # INVALID_SET_FILE_POINTER(0xFFFFFFFF) 截断成 LONG 即 -1。因上面已把 offset - # 限制在 0..0x7FFFFFFF,0xFFFFFFFF 不可能是"合法偏移",无需再叠 GetLastError 消歧。 + # INVALID_SET_FILE_POINTER(0xFFFFFFFF) truncates to LONG -1; offset is already clamped to + # 0..0x7FFFFFFF, so 0xFFFFFFFF cannot be a valid offset and no GetLastError check is needed. if ok_seek == -1: wb.CloseHandle(h) return -1 @@ -165,7 +159,7 @@ def _dl_write_at(path: str, src: str, length: INT, offset: sizet) -> INT: def _dl_truncate(path: str) -> INT: - """CREATE_ALWAYS 建(截断)空文件。0 成功;-1 失败。""" + """Create (truncate) an empty file via CREATE_ALWAYS. 0 ok; -1 fail.""" h: wb.HANDLE = wf.CreateFileA( path, wf.GENERIC_WRITE, wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE | wf.FILE_SHARE_DELETE, @@ -177,14 +171,14 @@ def _dl_truncate(path: str) -> INT: # ============================================================ -# 探测:总大小 / Range 支持(首片 bytes=0-0) +# Probe: total size / Range support (first slice bytes=0-0) # ============================================================ def _dl_probe(url: str, verify: bool, ca_file: str, out_total: sizet*) -> INT: - """探测服务器总大小与 Range 支持。 - 返回 0=支持 Range 且 out_total 有效;1=服务器返回 200(不支持 Range, - 退化为整段下载,out_total=Content-Length);-3 失败。""" + """Probe server total size and Range support. Returns 0=Range supported (out_total valid); + 1=server returned 200 (no Range, full download, out_total=Content-Length); -3 fail. + """ *out_total = 0 buf: str = stdlib.malloc(8192) if buf is None: @@ -204,14 +198,14 @@ def _dl_probe(url: str, verify: bool, ca_file: str, # ============================================================ -# 下载一个区间带([b_start, b_end)),逐片拉取并落盘 +# Download one band ([b_start, b_end)), fetching and writing slice by slice # ============================================================ def _dl_download_band(path: str, url: str, is_https: INT, verify: bool, ca_file: str, b_start: sizet, b_end: sizet, chunk: INT, buf: str, buf_cap: INT, p_done: sizet*) -> INT: - """0 成功;-1 网络/服务器;-2 写入失败。p_done 累计已写字节。""" + """0 ok; -1 network/server; -2 write failure. p_done accumulates bytes written.""" pos: sizet = b_start while pos < b_end: want: INT = chunk @@ -238,7 +232,7 @@ def _dl_download_band(path: str, url: str, is_https: INT, verify: bool, ca_file: *p_done = *p_done + sizet(got) pos += sizet(got) if got < want: - # 服务端提前结束(正常收尾时仅末片 < want) + # Server ended early (only the last slice is < want on normal completion) if pos >= b_end: break _dl_set_err("short slice") @@ -247,7 +241,7 @@ def _dl_download_band(path: str, url: str, is_https: INT, verify: bool, ca_file: # ============================================================ -# 多线程 worker +# Multi-thread worker # ============================================================ class DlJob: path: str @@ -280,23 +274,14 @@ def _dl_worker(arg: VOIDPTR) -> t.CPtr: # ============================================================ -# 公共 API +# Public API # ============================================================ def download(url: str, path: str, threads: INT = 1, chunk_size: INT = DL_DEFAULT_CHUNK, verify: bool = True, ca_file: str = None) -> INT: - """大文件切片下载。 - - Args: - url: 支持 http:// 与 https://(Range 分段拉取) - path: 保存路径(不存在自动创建;已存在则截断重建) - threads: 1=串行切片;2~8=多线程并行分带下载 - chunk_size: 单片大小(字节,默认 256KB) - verify / ca_file: https 证书校验参数(对齐 requests.https) - Returns: - 0 成功;-1 参数错误;-2 文件创建失败;-3 服务器不支持/下载失败; - -4 写入失败。last_error() 可查原因文本。 + """Chunked large-file download over http:// or https:// (Range). threads 1=serial, + 2~8=parallel bands; chunk_size bytes (default 256KB). Returns 0/-1/-2/-3/-4 (see last_error()). """ _dl_err_store[0] = 0 if url is None or path is None or url[0] == 0 or path[0] == 0: @@ -313,7 +298,7 @@ def download(url: str, path: str, threads: INT = 1, _dl_set_err("unsupported scheme") return -1 - # 探测总大小与 Range 支持 + # Probe total size and Range support total: sizet = 0 prc: INT = _dl_probe(url, verify, ca_file, &total) if prc < 0: @@ -322,7 +307,7 @@ def download(url: str, path: str, threads: INT = 1, _dl_set_err("cannot determine size") return -3 - # 建立(截断)目标文件 + # Create (truncate) target file if _dl_truncate(path) != 0: _dl_set_err("create file failed") return -2 @@ -333,7 +318,7 @@ def download(url: str, path: str, threads: INT = 1, slab: INT = chunk_size + 8192 if threads == 1: - # ---- 串行切片 ---- + # ---- Serial slices ---- buf: str = stdlib.malloc(sizet(slab)) if buf is None: _dl_set_err("out of memory") @@ -347,7 +332,7 @@ def download(url: str, path: str, threads: INT = 1, stdio.printf("[download] done: %zu/%zu bytes\n", done, total) return 0 - # ---- 多线程并行分带 ---- + # ---- Multi-thread parallel bands ---- band: sizet = (total + sizet(threads) - 1) / sizet(threads) ctxs: t.CArray[VOIDPTR, DL_MAX_THREADS] handles: t.CArray[VOIDPTR, DL_MAX_THREADS] @@ -406,12 +391,12 @@ def download(url: str, path: str, threads: INT = 1, _dl_set_err("thread create failed") return -3 - # 等待全部线程结束 + # Wait for all threads to finish for j in range(threads_ok): wsync.WaitForSingleObject(handles[j], 0xFFFFFFFF) wb.CloseHandle(handles[j]) - # 汇总结果 + # Aggregate results total_done: sizet = 0 failed_job: INT = 0 for k in range(threads_ok): @@ -420,7 +405,7 @@ def download(url: str, path: str, threads: INT = 1, if jobk.status != 0: failed_job = 1 - # 释放线程上下文 + # Free thread contexts for m in range(threads_ok): jobm: DlJob* = ctxs[m] stdlib.free(jobm.path) diff --git a/requests/https.vp b/requests/https.vp index 8cbfd50..bd6a670 100644 --- a/requests/https.vp +++ b/requests/https.vp @@ -31,21 +31,18 @@ import memhub define HTTPS_PORT = 443 -# 模块内部状态 +# Module-internal state _ossl_ready: bool = False _socket_ready: bool = False -_err_store: t.CArray[char, 512] # 最近一次失败原因(静态缓冲) -_info_store: t.CArray[char, 512] # 最近一次成功握手的 TLS 元信息 -_scratch_store: t.CArray[char, 512] # 临时拼接缓冲 -_last_verify_val: INT = -1 # 最近一次握手后 SSL_get_verify_result(0=通过;-1=未启用) -_last_verify_flag: INT = -1 # 最近一次请求 verify 标志(1=开启;0=关闭;-1=未知) +_err_store: t.CArray[char, 512] # last failure reason (static buffer) +_info_store: t.CArray[char, 512] # TLS metadata of last successful handshake +_scratch_store: t.CArray[char, 512] # scratch concat buffer +_last_verify_val: INT = -1 # last SSL_get_verify_result (0=ok; -1=disabled) +_last_verify_flag: INT = -1 # last request verify flag (1=on; 0=off; -1=unknown) # ============================================================ -# SSE 流式内容接收区(post_stream 专用;单路请求,UI 线程轮询拉取) -# _st_len: 工作线程已写入的内容字节数 -# _st_consumed: UI 已消费游标(仅拉取方推进) -# _st_state: 0=运行中 1=完成(2xx) 2=失败 -# _st_status: HTTP 状态码(失败网络路径为负错误码) +# SSE streaming receive area (post_stream only; single request, polled by UI thread). +# _st_len written bytes; _st_consumed consumed cursor; _st_state 0=running 1=done(2xx) 2=fail; _st_status HTTP code. # ============================================================ define STREAM_CAP = 262144 _st_buf: t.CArray[char, 262144] @@ -57,7 +54,7 @@ _st_err: t.CArray[char, 512] def stream_begin(): - """开始新一轮流式接收:重置缓冲/游标/状态。""" + """Start a new streaming receive: reset buffer/cursor/state.""" global _st_len, _st_consumed, _st_state, _st_status _st_len = 0 _st_consumed = 0 @@ -67,7 +64,7 @@ def stream_begin(): def stream_pull(out: str, cap: INT) -> INT: - """拉取未消费的增量内容到 out(自动补 0),返回拷贝字节数。""" + """Copy unconsumed delta into out (NUL-terminated); return bytes copied.""" global _st_consumed if out is None or cap <= 1: return 0 @@ -84,60 +81,60 @@ def stream_pull(out: str, cap: INT) -> INT: def stream_state() -> INT: - """0=运行中 1=完成(2xx) 2=失败。""" + """0=running 1=done(2xx) 2=failed.""" return _st_state def stream_status() -> INT: - """HTTP 状态码;负数为网络/解析错误。""" + """HTTP status code; negative means network/parse error.""" return _st_status def stream_error_text() -> str: - """失败原因文本(_st_state==2 时)。""" + """Failure reason text (when _st_state==2).""" return _st_err def last_verify_result() -> int: - """返回最近一次握手的证书校验结果:0 通过;>0 失败码;-1 未开启校验。""" + """Last cert verify result: 0=ok; >0=failure code; -1=verify disabled.""" return _last_verify_val def last_verify_enabled() -> int: - """返回最近一次请求是否开启校验(1=是;0=否;-1=未知)。""" + """Whether last request enabled verify (1=yes; 0=no; -1=unknown).""" return _last_verify_flag # ============================================================ -# 内部辅助 +# Internal helpers # ============================================================ def last_error() -> str: - """返回最近一次失败的原因文本(无则空串)。""" + """Last failure reason text (empty string if none).""" return _err_store def last_tls_info() -> str: - """返回最近一次成功握手的 TLS 信息(版本/套件/对端主题),无则空串。""" + """Last successful handshake TLS info (version/cipher/peer subject), empty if none.""" return _info_store def _RecordErrorText(text: str): - """把失败原因写入静态缓冲 _err_store。""" + """Write failure reason into static buffer _err_store.""" if text is None: return _err_store[0] = 0 stdio.snprintf(_err_store, 512, "%s", text) def _RecordInfoText(text: str): - """把 TLS 元信息写入静态缓冲 _info_store。""" + """Write TLS metadata into static buffer _info_store.""" if text is None: return _info_store[0] = 0 stdio.snprintf(_info_store, 512, "%s", text) def _EnsureReady() -> INT: - """初始化 socket 与 openssl(均幂等)。""" + """Initialize socket and openssl (both idempotent).""" global _socket_ready, _ossl_ready if not _socket_ready: if socket.SocketInit() != 0: @@ -153,13 +150,13 @@ def _EnsureReady() -> INT: def _ParseHttpsUrl(url: str, host_buf: str, host_size: INT, path_buf: str, path_size: INT, port: INT*) -> INT: - """解析 https://host[:port][/path],端口缺省 443。""" + """Parse https://host[:port][/path]; default port 443.""" if url is None: return -1 if host_buf is None or path_buf is None or port is None: return -1 p: str = url - # Skip "https://"(8 字节) + # Skip "https://" (8 bytes) if string.strncmp(p, "https://", 8) == 0: p += 8 @@ -204,12 +201,12 @@ def _ParseHttpsUrl(url: str, host_buf: str, host_size: INT, path_buf: str, path_ def _ScratchBuf() -> str: - """返回静态拼接缓冲(512B,复用)。""" + """Return static concat buffer (512B, reused).""" return _scratch_store def _TlsErrText(): - """把最近一次 openssl 错误队列文本写入静态 scratch 缓冲。""" + """Write last openssl error-queue text into static scratch buffer.""" e: uint32 = openssl.ERR_get_error() if e == 0: _scratch_store[0] = 0 @@ -219,16 +216,12 @@ def _TlsErrText(): # ============================================================ -# 核心请求 +# Core request # ============================================================ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, content_type: str, auth: str = None) -> rawptr[requests.Response]: - """HTTPS GET/POST 核心实现。 - - verify=False 时跳过证书链/主机名校验(不推荐);verify=True 时 - ca_file 提供 PEM CA bundle,否则使用系统默认 CA 路径。 - auth(可选):形如 "Bearer " 的 Authorization 头值, - 非空时仅在 POST 请求头中插入 Authorization 头(默认 None = 行为不变)。 + """HTTPS GET/POST core. verify=False skips cert chain/hostname checks (not recommended); + verify=True uses ca_file PEM bundle (else system default); auth adds Authorization header on POST only. """ global _last_verify_val, _last_verify_flag if requests._mbuddy is None: return None @@ -242,13 +235,13 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, requests.release(resp) return None - # 临时缓冲(错误文本格式化;openssl 详情走模块级 scratch) + # Temp buffer for error text; openssl details go to module scratch. errmsg: str = requests._mbuddy.alloc(512) if errmsg is None: requests.release(resp) return None - # ---- 解析 URL ---- + # ---- Parse URL ---- host_buf: t.CArray[char, 256] path_buf: t.CArray[char, 1024] port: INT = 0 @@ -260,7 +253,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, requests.release(resp) return None - # ---- 建立 TCP 连接 ---- + # ---- Establish TCP connection ---- sock = socket.Socket(socket.AF_INET, socket.SOCK_STREAM) if sock.fd == socket.INVALID_SOCKET: _RecordErrorText("socket() failed") @@ -298,7 +291,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, requests.release(resp) return None else: - # 系统默认路径(Windows 上通常为空,证书链校验将失败) + # System default paths (usually empty on Windows; cert chain check will fail). openssl.SSL_CTX_set_default_verify_paths(ctx) else: openssl.SSL_CTX_set_verify(ctx, openssl.SslVerifyNone, None) @@ -313,13 +306,13 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, return None openssl.SSL_set_fd(ssl, sock.fd) - # SNI(SSL_set_tlsext_host_name 是宏 → 直接 SSL_ctrl) + # SNI (SSL_set_tlsext_host_name is a macro -> call SSL_ctrl directly) openssl.SSL_ctrl(ssl, openssl.SslCtrlSetTlsextHostname, openssl.TlsextNametypeHostName, host_buf) - # 主机名校验(TLS 内核对证书 SAN/CN 与 host 比对) + # Hostname verification (TLS core compares cert SAN/CN against host) if verify: openssl.SSL_set1_host(ssl, host_buf) - # ---- 握手 ---- + # ---- Handshake ---- hret: INT = openssl.SSL_connect(ssl) if hret <= 0: want: INT = openssl.SSL_get_error(ssl, hret) @@ -333,13 +326,13 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, requests.release(resp) return None - # ---- 记录最近一次证书校验结果(供 last_verify_result 查询)---- + # ---- Record last cert verify result (for last_verify_result) ---- if verify: _last_verify_val = INT(openssl.SSL_get_verify_result(ssl)) else: _last_verify_val = -1 - # ---- 证书链校验结果复核 ---- + # ---- Re-check cert chain verify result ---- if verify: vres: int32 = openssl.SSL_get_verify_result(ssl) if vres != 0: @@ -356,7 +349,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, requests.release(resp) return None - # ---- 记录 TLS 元信息(版本 / 套件 / 对端证书主题)---- + # ---- Record TLS metadata (version / cipher / peer subject) ---- name_buf: str = requests._mbuddy.alloc(512) if name_buf is not None: peer: openssl.X509Ptr = openssl.SSL_get1_peer_certificate(ssl) @@ -378,7 +371,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, _RecordInfoText(errmsg) requests._mbuddy.free(name_buf) - # ---- 组请求头并发送 ---- + # ---- Build request headers and send ---- req_buf: str = requests._mbuddy.alloc(requests.HTTP_BUFSIZ) if req_buf is None: _RecordErrorText("alloc request buffer failed") @@ -394,7 +387,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, if is_post: body_len = INT(string.strlen(data)) if data is not None else 0 ct: str = content_type if content_type is not None else "application/x-www-form-urlencoded" - # 分段组头,便于按需插入 Authorization(auth 非空才加,默认路径输出与原来完全一致) + # Build header in stages so Authorization can be inserted on demand (only when auth non-empty). hdr_off: INT = 0 hdr_cap: INT = requests.HTTP_BUFSIZ stdio.snprintf(req_buf, hdr_cap, "POST %s HTTP/1.1\r\nHost: %s\r\n", path_buf, host_buf) @@ -429,7 +422,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, requests._mbuddy.free(req_buf) - # ---- 接收响应(缓冲自动翻倍增长, 支持 MB 级正文, 如 MNIST .gz 下载) ---- + # ---- Receive response (buffer auto-doubles, supports MB bodies) ---- resp_cap: INT = requests.HTTP_BUFSIZ resp_buf: str = requests._mbuddy.alloc(sizet(resp_cap)) if resp_buf is None: @@ -445,7 +438,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, while True: room: INT = resp_cap - 1 - total if room <= 0: - # 缓冲已满仍可能有数据: 翻倍扩容(硬上限 256MB, 防失控流) + # Buffer full but more data may follow: double capacity (hard cap 256MB, runaway-guard). if resp_cap >= 268435456: break resp_cap = resp_cap * 2 @@ -468,7 +461,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, resp_buf[total] = 0 - # 关闭 TLS 与 TCP + # Close TLS and TCP openssl.SSL_shutdown(ssl) openssl.SSL_free(ssl) openssl.SSL_CTX_free(ctx) @@ -482,14 +475,12 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, # ============================================================ -# 公共 API +# Public API # ============================================================ def _HttpsRedirectLoop(method: str, url: str, verify: bool, ca_file: str, data: str, content_type: str, auth: str = None) -> rawptr[requests.Response]: - """HTTPS GET/POST + 重定向跟随(跨 http/https,最多 5 跳)。 - - auth(可选)原样透传给 _HttpsRequest;None 时行为与原来完全一致。 - 注意:跨主机重定向时 Authorization 同样会透传(仅提示,逻辑未改动)。 + """HTTPS GET/POST with redirect following (cross http/https, max 5 hops). + auth is passed through to _HttpsRequest; note Authorization also follows cross-host redirects. """ if requests._mbuddy is None: return None @@ -535,7 +526,7 @@ def _HttpsRedirectLoop(method: str, url: str, verify: bool, ca_file: str, data: return None requests.release(resp) - # 跨协议分流:https → 继续本循环;http → 交给 requests(http)链 + # Cross-scheme dispatch: https -> continue loop; http -> hand to requests (http) chain if string.strncmp(nxt, "https://", 8) != 0: requests._mbuddy.free(loc) requests._mbuddy.free(cur) @@ -553,50 +544,27 @@ def _HttpsRedirectLoop(method: str, url: str, verify: bool, ca_file: str, data: def get(url: str, verify: bool = True, ca_file: str = None) -> rawptr[requests.Response]: - """HTTPS GET request(自动跟随 3xx 重定向,最多 5 跳)。 - - Args: - url: Full URL (e.g., "https://example.com/path") - verify: True 时校验证书链 + 主机名(默认) - ca_file: PEM CA bundle 路径;None 时使用系统默认 CA 路径 - Returns: - Pointer to a Response object, None on failure (see last_error()). + """HTTPS GET (follows 3xx redirects, max 5 hops). verify checks cert chain + hostname; + ca_file is a PEM CA bundle path, None uses system default. Returns Response* or None. """ return _HttpsRedirectLoop("GET", url, verify, ca_file, None, None) def post(url: str, data: str = None, content_type: str = "application/x-www-form-urlencoded", verify: bool = True, ca_file: str = None, auth: str = None) -> rawptr[requests.Response]: - """HTTPS POST request(自动跟随 3xx 重定向,最多 5 跳)。 - - Args: - url: Full URL - data: Request body data - content_type: Content-Type header - verify: True 时校验证书链 + 主机名(默认) - ca_file: PEM CA bundle 路径;None 时使用系统默认 CA 路径 - auth: 可选 Authorization 头值(形如 "Bearer "), - 非空时随请求头发送;None/空串 = 与旧版完全一致。 - Returns: - Pointer to a Response object, None on failure (see last_error()). + """HTTPS POST (follows 3xx redirects, max 5 hops). verify checks cert chain + hostname; + ca_file PEM CA bundle (None = system default); auth optional "Bearer ", sent when non-empty. """ return _HttpsRedirectLoop("POST", url, verify, ca_file, data, content_type, auth) # ============================================================ -# SSE 流式 Chat Completions(post_stream) -# -# 与 post() 不同:请求头带 Accept: text/event-stream;响应体按行解析 -# SSE(data: {json}),把每段 choices[0].delta.content 抽进 _st_buf 供 -# UI 线程 stream_pull() 增量拉取显示。全程阻塞读取,应在后台线程调用。 -# -# 返回: >=0 HTTP 状态码(2xx 正常完成;3xx/4xx/5xx 状态仍返回给调用方, -# 正文中的非 SSE 内容不会进 _st_buf);-1/-2 网络/解析失败 -# (_st_state=2,失败原因见 stream_error_text()/last_error())。 +# SSE streaming Chat Completions (post_stream): sends Accept: text/event-stream and extracts +# choices[0].delta.content from data:{json} lines into _st_buf; blocking, returns HTTP status or -1/-2. # ============================================================ def _SseDeltaContent(root: rawptr[json.JsonValue]) -> str: - """从流式 data 事件取 choices[0].delta.content;缺失返回 None。""" + """Extract choices[0].delta.content from a stream data event; None if absent.""" if root is None or not root.is_object(): return None cv: rawptr[json.JsonValue] = root["choices"] @@ -615,7 +583,7 @@ def _SseDeltaContent(root: rawptr[json.JsonValue]) -> str: def _StreamAppendContent(txt: str): - """把一段增量内容追加到 _st_buf(超容量则截断丢弃并记录)。""" + """Append delta content to _st_buf (truncate on overflow).""" global _st_len if txt is None: return @@ -631,7 +599,7 @@ def _StreamAppendContent(txt: str): def _SetStreamFail(rc: INT): - """流式失败收尾:从 _err_store 拷原因文本并置状态。""" + """Finish stream failure: copy reason from _err_store and set state.""" global _st_state, _st_status _st_state = 2 _st_status = rc @@ -640,10 +608,8 @@ def _SetStreamFail(rc: INT): def post_stream(url: str, data: str, content_type: str = "application/json", verify: bool = True, ca_file: str = None, auth: str = None) -> INT: - """SSE 流式 POST(阻塞,应在后台线程调用)。 - - data 应为已含 "stream":true 的 JSON 请求体。 - 返回 HTTP 状态码;负数为网络/解析失败(_st_state=2)。 + """SSE streaming POST (blocking; call from a background thread). data must be a JSON body + containing "stream":true. Returns HTTP status; negative means network/parse failure. """ global _st_state, _st_status if requests._mbuddy is None: @@ -652,12 +618,12 @@ def post_stream(url: str, data: str, content_type: str = "application/json", _st_state = 0 _st_status = 0 - # Winsock/openssl 初始化(幂等;流式首次可能在后台线程执行) + # Winsock/openssl init (idempotent; first call may run on a background thread) if _EnsureReady() != 0: _SetStreamFail(-1) return -1 - # ---- 连接/TLS(与 _HttpsRequest 一致)---- + # ---- Connect/TLS (same as _HttpsRequest) ---- errmsg: str = requests._mbuddy.alloc(512) if errmsg is None: return -1 @@ -754,7 +720,7 @@ def post_stream(url: str, data: str, content_type: str = "application/json", requests._mbuddy.free(errmsg) return -1 - # ---- 组请求头(POST + Accept: text/event-stream)并发送 ---- + # ---- Build request headers (POST + Accept: text/event-stream) and send ---- req_buf: str = requests._mbuddy.alloc(requests.HTTP_BUFSIZ) if req_buf is None: _RecordErrorText("alloc request buffer failed") @@ -794,7 +760,7 @@ def post_stream(url: str, data: str, content_type: str = "application/json", openssl.SSL_write(ssl, data, body_len) requests._mbuddy.free(req_buf) - # ---- 读取响应:跳过响应头 → SSE 行解析 → 抽 delta.content ---- + # ---- Read response: skip headers -> parse SSE lines -> extract delta.content ---- rd: str = requests._mbuddy.alloc(16384) if rd is None: _RecordErrorText("alloc stream buffer failed") @@ -805,7 +771,7 @@ def post_stream(url: str, data: str, content_type: str = "application/json", requests._mbuddy.free(errmsg) return -1 - # JSON 解析池(事件粒度 reset,足够容纳单个 data 事件) + # JSON pool (reset per event, sized for one data event) pmem: str = stdlib.malloc(524288) if pmem is None: _RecordErrorText("alloc json pool failed") @@ -829,15 +795,15 @@ def post_stream(url: str, data: str, content_type: str = "application/json", if n <= 0: break had_read = 1 - # 1) 追加到 leftover + # 1) Append to leftover ai: INT = 0 while ai < n and llen < 16383: leftover[llen] = rd[ai] llen += 1 ai += 1 - # 2) 逐行处理 + # 2) Process line by line while True: - # 找行尾 \n + # Find line end \n nl: INT = -1 for k in range(llen): if leftover[k] == 10: @@ -852,7 +818,7 @@ def post_stream(url: str, data: str, content_type: str = "application/json", line: str = leftover if header_done == 0: if string.strncmp(line, "HTTP/", 5) == 0: - # 状态码:HTTP/1.1 200 OK + # Status code: HTTP/1.1 200 OK sp: INT = 0 while line[sp] != 0 and line[sp] != ' ': sp += 1 @@ -878,13 +844,13 @@ def post_stream(url: str, data: str, content_type: str = "application/json", dtx: str = _SseDeltaContent(ev) if dtx is not None: _StreamAppendContent(dtx) - # 3) 移除已处理行 + # 3) Remove processed line rem: INT = llen - (nl + 1) for j2 in range(rem): leftover[j2] = leftover[nl + 1 + j2] llen = rem - # ---- 收尾 ---- + # ---- Wrap up ---- openssl.SSL_shutdown(ssl) openssl.SSL_free(ssl) openssl.SSL_CTX_free(ctx) @@ -911,21 +877,15 @@ def post_stream(url: str, data: str, content_type: str = "application/json", # ============================================================ -# 大文件 Range 下载原语(requests.download 使用,与 requests._DlOnce 镜像) -# -# 线程安全:本函数不使用 _mbuddy(请求/响应缓冲一律 stdlib),每个连接独立 -# SSL_CTX/SSL/socket,供多线程并行切片并发调用(OPENSSL_init_ssl 由主线程 -# 先 _EnsureReady 预热一次)。 -# -# 返回: >=0 响应体字节数;-1 参数错;-2 3xx 重定向(Location 写 out_loc); -# -3 其它失败(last_error() 查看原因)。 +# Large-file Range download primitive for requests.download. Thread-safe: no _mbuddy, +# independent SSL_CTX/SSL/socket per connection. Returns body bytes>=0; -1 arg; -2 redirect; -3 failure. # ============================================================ def _DlOnce(url: str, start: sizet, count: INT, verify: bool, ca_file: str, dst: str, dst_cap: INT, out_status: INT*, out_loc: str, out_loccap: INT, out_total: sizet*) -> INT: - """HTTPS 单次 Range 拉取(线程安全,见模块注释)。""" + """HTTPS single Range fetch (thread-safe; see module comment).""" global _last_verify_val, _last_verify_flag if url is None or dst is None or dst_cap <= 0 \ or out_status is None or out_total is None: @@ -1005,7 +965,7 @@ def _DlOnce(url: str, start: sizet, count: INT, verify: bool, ca_file: str, else: _last_verify_val = -1 - # 组 Range 请求头(不带 Accept-Encoding,保证响应为原始字节) + # Build Range request header (no Accept-Encoding, so response is raw bytes) req: str = stdlib.malloc(1024) if req is None: openssl.SSL_free(ssl) diff --git a/stdint.vp b/stdint.vp index a389cfa..1627c49 100644 --- a/stdint.vp +++ b/stdint.vp @@ -2,9 +2,8 @@ import t # ============================================================================= -# 大写别名(Win32/TransPyC 风格) -# 注意:INTPTR/UINTPTR 在此模块中是 int*/unsigned int* 指针类型(历史命名)。 -# 它们与 C 标准的 intptr_t/uintptr_t 整数类型不同,后者见下方小写 _t 区域。 +# Uppercase aliases (Win32/TransPyC style) +# Note: INTPTR/UINTPTR are int*/unsigned int* pointer types here (historical), unlike the C intptr_t/uintptr_t integer types in the lowercase _t section below. # ============================================================================= INT: t.CTypedef = int INTPTR: t.CTypedef = int | t.CPtr @@ -48,7 +47,7 @@ FLOAT128: t.CTypedef = t.CFloat128T # ============================================================================= -# 定长整数大写别名 +# Fixed-width uppercase integer aliases # ============================================================================= INT8: t.CTypedef = int8 INT16: t.CTypedef = int16 @@ -93,9 +92,8 @@ PTRDIFF_T: t.CTypedef = t.CPtrDiffT # ============================================================================= -# C 标准库 类型名(小写 _t 后缀) -# 这些是 C 语言标准类型名,从 _CNAME_ALIAS 挪到此处作为 typedef 提供。 -# 用户 `from stdint import *` 后即可直接使用 int8_t / size_t 等。 +# C standard library type names (lowercase _t suffix) +# C standard type names provided here as typedefs; after `from stdint import *` you can use int8_t / size_t etc. directly. # ============================================================================= int8_t: t.CTypedef = int8 int16_t: t.CTypedef = int16 @@ -111,18 +109,18 @@ size_t: t.CTypedef = sizet ssize_t: t.CTypedef = t.CPtrDiffT ptrdiff_t: t.CTypedef = t.CPtrDiffT -# intptr_t/uintptr_t 是足以容纳指针的整数类型(不是指针!) -# 与上方的 INTPTR/UINTPTR(int 指针)语义不同。 +# intptr_t/uintptr_t are integer types large enough to hold a pointer (NOT pointers!) +# Different from the INTPTR/UINTPTR above (int pointers). intptr_t: t.CTypedef = t.CIntPtrT uintptr_t: t.CTypedef = t.CUIntPtrT -# 宽字符 / 字符类型 +# Wide-character / character types wchar_t: t.CTypedef = t.CWCharT char8_t: t.CTypedef = t.CChar8T char16_t: t.CTypedef = t.CChar16T char32_t: t.CTypedef = t.CChar32T -# 浮点特化类型 +# Floating-point specializations float8_t: t.CTypedef = t.CFloat8T float16_t: t.CTypedef = t.CFloat16T float32_t: t.CTypedef = t.CFloat32T diff --git a/string.vp b/string.vp index 8caac3e..800931c 100644 --- a/string.vp +++ b/string.vp @@ -73,14 +73,11 @@ export def strlen(src: str) -> sizet: return length # String comparison function -# 注意:str1[0] - str2[0] 是 i8 减法会回绕(0xFF 表示 -1),返回前必须 -# int() 提升到 i32 再相减(与下方 memcmp 的 int(p1[i]) - int(p2[i]) 一致), -# 否则 strcmp("abc","abd") 返回 255 而非 -1(StringTest Test 4 根因)。 +# Note: str1[0] - str2[0] is an i8 subtraction that wraps (0xFF means -1), so both operands +# must be promoted to i32 before subtracting (as in memcmp below); otherwise strcmp("abc","abd") returns 255 instead of -1. def strcmp(str1: str, str2: str) -> int: - # 指针相等短路(降单次成本,不降调用次数):查表/自比较场景常拿同一驻留 - # 字符串与自身比较(如 HashTable 以同一指针 set/lookup),指针相同则内容 - # 必相同,直接返回 0,省去整趟逐字节循环;指针不同则走下方原逻辑。 - # 语义严格不变(相同指针 ⇒ 内容相同 ⇒ strcmp 结果本应为 0)。 + # Pointer-equality short-circuit: identical pointers guarantee identical contents, so return 0 directly and skip the full byte loop. + # Semantics strictly unchanged (same pointer => same content => strcmp result is 0); different pointers fall through to the loop below. if str1 == str2: return 0 while str1[0] != '\0' and str2[0] != '\0': @@ -96,8 +93,8 @@ def samestr(str1: str, str2: str) -> bool: # String compare function with limited length def strncmp(str1: str, str2: str, n: sizet) -> int: - # 指针相等短路(同 strcmp):相同指针在任意 n 下比较结果恒为 0(相同字符串 - # 的前 n 个字符必然相等),直接返回 0,语义严格不变。 + # Pointer-equality short-circuit (same as strcmp): identical pointers compare equal for any n, so return 0. + # Semantics strictly unchanged. if str1 == str2: return 0 i: sizet = 0 @@ -144,11 +141,8 @@ def strrchr(s: str, cr: int) -> str: # Substring search function (find first occurrence of needle in s, return pointer to that position, or None if not found) -# 热点优化(实测):原实现对**每个字节位置**都调用一次 strncmp,并先对 s 做一次全文 strlen。 -# 实测 strstr 被调用 918 万次、strncmp 因此被调用 7.44 亿次(占全程序调用 29%,是第一名)。 -# 改为先用 strchr 跳到"首字符匹配"的候选位置(跳过绝大多数不可能匹配的字节), -# 只在候选点做整段比较;同时不再预先 strlen(s)(strncmp 本身遇 '\0' 即停,安全)。 -# 语义与原实现完全一致:needle 为空返回 s;未找到返回 None。 +# Hot-path optimization: use strchr to jump to first-char candidates instead of calling strncmp at every byte, and drop the upfront strlen(s) (strncmp stops at '\0'). +# Semantics unchanged: an empty needle returns s; not found returns None. def strstr(s: str, needle: str) -> str: if needle[0] == 0: return s @@ -232,9 +226,9 @@ export def memcpy(dest: t.CPtr, src: t.CPtr, num: sizet) -> t.CPtr: return dest -# 内存移动函数 +# Memory move function def memmove(dest: t.CPtr, src: t.CPtr, num: sizet) -> t.CPtr: - # 原始实现: + # Original implementation: # d: BYTEPTR = BYTEPTR(dest) # s: BYTEPTR = BYTEPTR(src) # i: sizet @@ -271,10 +265,10 @@ def atoi(src: str) -> int: sign: int = 1 s: str = src if s == None: return 0 - # 跳过空白 + # Skip whitespace while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r': s += 1 - # 符号 + # Sign if s[0] == '-': sign = -1 s += 1 @@ -299,7 +293,7 @@ def atoll(src: str) -> int64: s += 1 elif s[0] == '+': s += 1 - # 十六进制前缀 0x / 0X + # Hex prefix 0x / 0X is_hex: int = 0 if s[0] == '0' and (s[1] == 'x' or s[1] == 'X'): is_hex = 1 @@ -327,26 +321,14 @@ def atoll(src: str) -> int64: # ============================================================ -# 十进制字面量 → double 的正确舍入转换(N4) -# -# 旧实现用"逐位累加 + 反复乘/除 10":小数位超过 16 位后每步都舍入 -# (e 偏 +1 ULP、ln2 偏 −1 ULP),指数按 while exp>0: result*=10.0 累乘 -# 308 次(DBL_MAX 偏 −6 ULP)。 -# 新实现:尾数装进 u64(≤19 位;第 20 位做一次舍入),用 10 的**精确幂** -# 在**双 double(hi+lo,约 106 位)**下做一次缩放,最后一次性舍入。 -# 算法先在 Python 里 1:1 对拍 float()(正确舍入)通过:20 万随机样例 + -# 边界样例在**正规数范围**内 bit-exact;次正规/溢出仍走旧逐步缩放兜底 -# (语义与旧实现一致,无回归)。 -# 约定:不用全局表——10^0..10^22 由循环乘 10 得到(该区间每一步都可精确 -# 表示);p<0 时在正规区算 10^p×2^600 再精确缩回,避免双 double 低位在 -# ~1e-292 以下掉进次正规而丢有效位。 +# Correctly-rounded decimal literal -> double conversion (N4): mantissa in u64 (<=19 digits, 20th digit rounds once), +# scaled once by an exact power of ten in double-double (hi+lo, ~106 bits) then rounded a single final time. +# Convention: no global table (10^0..10^22 built by repeated multiplication of 10, each step exact). +# For p<0 compute 10^p*2^600 in the normal range and scale back exactly, avoiding subnormal loss in the double-double low part below ~1e-292. # ============================================================ -# _dd_split_hi - Dekker 分裂的上半(hi = 取高 26 位) -# -# 含量级缩尺:|a| > 6.7e292 时先乘 2^-28 再算(否则 a*(2^27+1) 溢出为 inf); -# |a| 极小(< 1e-292)时先乘 2^28(否则 lo = a-hi 掉进次正规丢位)。 -# 缩尺因子均为 2 的幂 ⇒ 对正规数**无损**;调用方用 lo = a - hi 取下半部分。 +# _dd_split_hi - upper half of the Dekker split (hi = top 26 bits). +# Magnitude rescaling: multiply by 2^-28 when |a| > 6.7e292 and by 2^28 when |a| < 1e-292 (powers of two, lossless for normals); caller gets the lower half via lo = a - hi. def _dd_split_hi(a: double) -> double: aa: double = a big: int = 0 @@ -366,7 +348,7 @@ def _dd_split_hi(a: double) -> double: return hi -# _dd_mul - 双 double 乘法:out = a × b(out[0]=hi, out[1]=lo) +# _dd_mul - double-double multiply: out = a × b (out[0]=hi, out[1]=lo) def _dd_mul(ah: double, al: double, bh: double, bl: double, out: t.CArray[double, 2]): p: double = ah * bh @@ -381,7 +363,7 @@ def _dd_mul(ah: double, al: double, bh: double, bl: double, out[1] = e - (s - p) -# _dd_add - 双 double 加法:out = a + b +# _dd_add - double-double add: out = a + b def _dd_add(ah: double, al: double, bh: double, bl: double, out: t.CArray[double, 2]): s: double = ah + bh @@ -393,7 +375,7 @@ def _dd_add(ah: double, al: double, bh: double, bl: double, out[1] = e - (r0 - s) -# _dd_div - 双 double 除法:out = a / b(q0 + 一步残差修正) +# _dd_div - double-double divide: out = a / b (q0 plus one residual correction step) def _dd_div(ah: double, al: double, bh: double, bl: double, out: t.CArray[double, 2], tmp: t.CArray[double, 2]): q0: double = ah / bh @@ -403,7 +385,7 @@ def _dd_div(ah: double, al: double, bh: double, bl: double, _dd_add(q0, 0.0, q1, 0.0, out) -# _dd_from_u64 - u64 → 双 double(精确:hi 为四舍五入值,lo 为精确残差) +# _dd_from_u64 - u64 -> double-double (exact: hi is the rounded value, lo the exact residual) def _dd_from_u64(m: uint64, out: t.CArray[double, 2]): hi: double = double(m) lo: double = 0.0 @@ -415,7 +397,7 @@ def _dd_from_u64(m: uint64, out: t.CArray[double, 2]): out[1] = (hi - (s - bb)) + (lo - bb) -# _dd_pow10 - 10^e 的双 double(e >= 0;按 ≤22 分块,每块可精确表示) +# _dd_pow10 - double-double 10^e (e >= 0; chunked by <=22 so each chunk is exact) def _dd_pow10(e: int, out: t.CArray[double, 2], tmp: t.CArray[double, 2]): out[0] = 1.0 out[1] = 0.0 @@ -433,7 +415,7 @@ def _dd_pow10(e: int, out: t.CArray[double, 2], tmp: t.CArray[double, 2]): k -= step -# _pow2 - 2^n(|n| <= 1000):循环乘 2/0.5,每步精确 +# _pow2 - 2^n (|n| <= 1000): repeated multiply by 2/0.5, exact at every step def _pow2(n: int) -> double: r: double = 1.0 i: int = n @@ -448,7 +430,7 @@ def _pow2(n: int) -> double: return r -# _atof_scale_stepwise - 旧的逐步 ×/÷10 缩放(次正规/溢出场景的兜底) +# _atof_scale_stepwise - stepwise ×/÷10 scaling (fallback for subnormal/overflow cases) def _atof_scale_stepwise(x: double, p: int) -> double: r: double = x e: int = p @@ -464,17 +446,17 @@ def _atof_scale_stepwise(x: double, p: int) -> double: def atof(src: str) -> double: s: str = src if s == None: return 0.0 - # 跳过空白 + # Skip whitespace while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r': s += 1 - # 符号 + # Sign sign: double = 1.0 if s[0] == '-': sign = -1.0 s += 1 elif s[0] == '+': s += 1 - # 尾数:有效数字装进 u64(≤19 位),同时维护十进制指数 + # Mantissa: significant digits packed into u64 (<=19 digits), tracking the decimal exponent mant: uint64 = 0 nd: int = 0 dexp: int = 0 @@ -486,7 +468,7 @@ def atof(src: str) -> double: if ch >= '0' and ch <= '9': d: int = ch - '0' if started == 0: - # 前导零不进尾数;小数部分的前导零要占十进制位 + # Leading zeros are not stored in the mantissa; leading zeros in the fraction still advance the decimal exponent if d != 0: started = 1 mant = uint64(d) @@ -501,8 +483,8 @@ def atof(src: str) -> double: if seen_dot != 0: dexp -= 1 else: - # 第 20 位起:整数部分丢弃的位让指数 +1;小数部分丢弃的位不影响 - # 指数。只用第 20 位做一次舍入(丢弃量 <= 10^-19 相对误差)。 + # From the 20th digit on: dropped integer digits bump the exponent; dropped fraction digits do not. + # Only the 20th digit is used for one rounding (dropped amount <= 1e-19 relative error). if seen_dot == 0: dexp += 1 elif rounded == 0: @@ -516,11 +498,11 @@ def atof(src: str) -> double: s += 1 if started == 0: return sign * 0.0 - # 进位:舍入可能使 19 位变 20 位 + # Carry: rounding may turn 19 digits into 20 if mant >= uint64(10000000000000000000): mant = mant // uint64(10) dexp += 1 - # 指数部分 + # Exponent part exp: int = 0 if s[0] == 'e' or s[0] == 'E': s += 1 @@ -537,7 +519,7 @@ def atof(src: str) -> double: p: int = dexp + exp if p == 0: return sign * double(mant) - # 双 double 缩放:mant × 10^p,最后一次舍入 + # Double-double scaling: mant × 10^p, single final rounding ta: t.CArray[double, 2] tb: t.CArray[double, 2] tc: t.CArray[double, 2] @@ -549,9 +531,8 @@ def atof(src: str) -> double: _dd_mul(ta[0], ta[1], tb[0], tb[1], tr) v = tr[0] + tr[1] else: - # p < 0:在正规区算「2^600 / 10^(-p)」= 10^p × 2^600,最后精确缩回。 - # 不能直接算 10^p(|p| 大时 ~1e-308,双 double 低位掉次正规丢有效位); - # 分母按 ≤308 分块累除(10^313 会先溢出成 inf 使结果变 NaN)。 + # p < 0: compute 2^600 / 10^(-p) (= 10^p * 2^600) in the normal range, then scale back exactly. + # Direct 10^p loses effective bits to subnormals for large |p|; divide the denominator in chunks of <=308 (10^313 overflows to inf and yields NaN). tb[0] = _pow2(600) tb[1] = 0.0 q: int = -p @@ -566,20 +547,17 @@ def atof(src: str) -> double: q -= step2 _dd_mul(ta[0], ta[1], tb[0], tb[1], tr) v = (tr[0] + tr[1]) * _pow2(-600) - # 正规数范围(非 NaN / 非 inf / 非次正规)→ 已正确舍入的结果 + # Normal range (not NaN / not inf / not subnormal) -> the correctly rounded result if v == v and v <= 1.7976931348623157e308 and v >= -1.7976931348623157e308 \ and (v == 0.0 or v >= 2.2250738585072014e-308 or v <= -2.2250738585072014e-308): return sign * v - # 溢出 / 次正规 / NaN → 回退旧逐步缩放(与旧实现语义一致) + # Overflow / subnormal / NaN -> fall back to stepwise scaling return sign * _atof_scale_stepwise(double(mant), p) def atof_out(src: str, out: double*) -> int: - """atof 结果写入 out[0](输出参数方式,避免跨模块 double 返回值传递 bug) - - 返回值仅表示成功与否(1=成功,0=失败),结果值通过 out 指针传递, - 不依赖 double 返回值的跨模块传递。 - """ + """Write the atof result to out[0] (out-parameter form, avoiding the cross-module double return bug). + The return value only signals success (1) or failure (0); the value is passed via the out pointer.""" if out is None: return 0 v: double = atof(src) @@ -587,39 +565,34 @@ def atof_out(src: str, out: double*) -> int: return 1 def split(s: str, delim: str, result: t.CArray[str], cap: int) -> int: - """按单字符分隔符切分 s,token 指针写入 result[0..],尾部写 None 哨兵。 - - cap: result 数组的**声明容量**(元素个数)。此前无容量参数 → 写 - result[count] 与 result[count]=None 均无上界;token 数超过调用方数组 - 容量时越界写(devfs 用 t.CArray[str, 256],256 个 token 即写穿 2048 字节)。 - 现限制 token 数 ≤ cap-1(留一格给结尾哨兵)。 - """ + """Split s by a single-character delimiter, writing token pointers to result[0..] and a None sentinel at the end. + cap is the declared capacity (element count); token count is limited to <= cap-1, leaving one slot for the sentinel.""" count: int = 0 start: str = s if not s or not delim or not result: return 0 if cap <= 0: return 0 - d: char = delim[0] # 单分隔符 + d: char = delim[0] # single delimiter while start[0]: - # 跳过分隔符 + # Skip delimiters while start[0] == d: start += 1 if not start[0]: break - # 容量保护:留最后一格放结尾哨兵 + # Capacity guard: keep the last slot for the end sentinel if count >= cap - 1: break - # 记录token起始 + # Record token start result[count] = start count += 1 - # 找到下一个分隔符,截断 + # Find the next delimiter and truncate while start[0] and start[0] != d: start += 1 if start[0]: start[0] = '\0' start += 1 - result[count] = None # 结尾标记 + result[count] = None # end marker return count diff --git a/sys.vp b/sys.vp index a104498..68819c6 100644 --- a/sys.vp +++ b/sys.vp @@ -40,7 +40,7 @@ def platform() -> str: # Process Information # ============================================================================= def get_pid() -> int: - """返回当前进程 ID""" + """Return the current process ID""" if platmacro.IS_WINDOWS: return w32.win32process.GetCurrentProcessId() return posix.getpid() @@ -57,7 +57,7 @@ def _init_argv(): """Initialize global argv once on first access""" global _argc, _argv if _argc != 0: return - # 必须用 if/else 派发:编译器只翻译"调用表达式语句",三元表达式语句会被丢弃(argv 恒空) + # Must dispatch with if/else: the compiler only translates call-expression statements; a ternary statement is dropped (argv stays empty) if platmacro.IS_WINDOWS: _init_argv_win32() else: diff --git a/torch/__init__.vp b/torch/__init__.vp index bbc3fea..374e784 100644 --- a/torch/__init__.vp +++ b/torch/__init__.vp @@ -30,17 +30,17 @@ import memhub # Tensor # ------------------------------------------------------------ novirtual class Tensor: - Data: t.CPtr # rows*cols*8 字节连续 double - Grad: t.CPtr # 同尺寸梯度(可为 None) + Data: t.CPtr # rows*cols*8 contiguous bytes of double + Grad: t.CPtr # same-shape gradient (may be None) Rows: sizet Cols: sizet -# 字段: Data/Grad/Rows/Cols = 32 字节(无 vtable) +# Fields Data/Grad/Rows/Cols = 32 bytes (no vtable) define TENSOR_SIZE = 32 def _zeros_block(pool: memhub.MemBuddy*, bytes_n: sizet) -> t.CPtr: - """分配并清零一块内存""" + """Allocate and zero a block of memory""" p: t.CPtr = pool.alloc(bytes_n) if p is not None: string.memset(p, 0, bytes_n) @@ -49,7 +49,7 @@ def _zeros_block(pool: memhub.MemBuddy*, bytes_n: sizet) -> t.CPtr: def zeros(pool: memhub.MemBuddy*, rows: sizet, cols: sizet, with_grad: int = 1) -> Tensor*: - """torch.zeros: 创建 rows x cols 的全零 Tensor, 可选分配 Grad 缓冲""" + """torch.zeros: create a rows x cols all-zero Tensor, optionally allocating a Grad buffer""" if pool is None: return None t0: rawptr[Tensor] = pool.alloc(TENSOR_SIZE) @@ -70,7 +70,7 @@ def zeros(pool: memhub.MemBuddy*, rows: sizet, def from_raw(pool: memhub.MemBuddy*, data: t.CPtr, rows: sizet, cols: sizet, with_grad: int = 0) -> Tensor*: - """torch.from_raw: 包装一块已有内存为 Tensor(不复制, 不拥有)""" + """torch.from_raw: wrap existing memory as a Tensor (no copy, not owned)""" if pool is None: return None t0: rawptr[Tensor] = pool.alloc(TENSOR_SIZE) @@ -85,7 +85,7 @@ def from_raw(pool: memhub.MemBuddy*, data: t.CPtr, def tensor_free(t0: Tensor*): - """释放 Tensor 外壳(数据由 MemBuddy 池统一回收, 无需手动 free)""" + """Free the Tensor shell (data is reclaimed by the MemBuddy pool; no manual free needed)""" pass @@ -114,7 +114,7 @@ def tensor_cols(t0: Tensor*) -> sizet: def fill(t0: Tensor*, val: double): - """torch.fill: 全部元素置为 val""" + """torch.fill: set all elements to val""" if t0 is None or t0.Data is None: return d: double* = t0.Data @@ -124,14 +124,14 @@ def fill(t0: Tensor*, val: double): def zero_grad(t0: Tensor*): - """torch.zero_grad: Grad 缓冲清零""" + """torch.zero_grad: zero the Grad buffer""" if t0 is None or t0.Grad is None: return string.memset(t0.Grad, 0, t0.Rows * t0.Cols * sizet(8)) # ------------------------------------------------------------ -# 确定性伪随机(LCG, minstd)与 Kaiming 均匀初始化 +# Deterministic pseudo-random (LCG, minstd) and Kaiming uniform init # ------------------------------------------------------------ _rng_state: double = 12345.0 LCG_M: double = 2147483647.0 @@ -146,14 +146,14 @@ def manual_seed(seed: int64): def _next_rand() -> double: - """(0,1) 均匀随机""" + """(0,1) uniform random""" global _rng_state _rng_state = math.fmod(_rng_state * LCG_A, LCG_M) return _rng_state / LCG_M def uniform_fill(t0: Tensor*, lo: double, hi: double): - """U(lo, hi) 填充""" + """Fill with U(lo, hi)""" if t0 is None or t0.Data is None: return d: double* = t0.Data @@ -165,18 +165,18 @@ def uniform_fill(t0: Tensor*, lo: double, hi: double): def kaiming_uniform_fill(w: Tensor*, fan_in: sizet, fan_out: sizet): - """PyTorch Linear 默认初始化: U(-sqrt(6/(fan_in+fan_out)), +...)""" + """PyTorch Linear default init: U(-sqrt(6/(fan_in+fan_out)), +...)""" bound: double = math.sqrt( double(6.0) / double(fan_in + fan_out)) uniform_fill(w, -bound, bound) # ------------------------------------------------------------ -# 基础元素级运算(结合 vipersimd 4 宽指令) +# Basic element-wise ops (using vipersimd 4-wide instructions) # ------------------------------------------------------------ def vector_mul_scalar4(src: t.CPtr, scalar_ptr: t.CPtr, dst: t.CPtr, n: sizet): - """dst = src * scalar (逐 4 元素 simd_mul_scalar4d + 标量尾数)""" + """dst = src * scalar (4-wide simd_mul_scalar4d + scalar tail)""" if src is None or scalar_ptr is None or dst is None: return i: sizet = 0 @@ -188,7 +188,7 @@ def vector_mul_scalar4(src: t.CPtr, scalar_ptr: t.CPtr, dst: t.CPtr, i += 4 sd: double* = src dd: double* = dst - # 尾部标量 + # Scalar tail sp: double* = scalar_ptr sval: double = sp[0] while i < n: @@ -197,7 +197,7 @@ def vector_mul_scalar4(src: t.CPtr, scalar_ptr: t.CPtr, dst: t.CPtr, def vector_add_inplace(a: t.CPtr, b: t.CPtr, n: sizet): - """a += b (逐 4 元素 simd_add_array 尾部标量)""" + """a += b (4-wide simd_add_array + scalar tail)""" if a is None or b is None: return tmp: t.CPtr = stdlib.malloc(8) @@ -219,8 +219,8 @@ def vector_add_inplace(a: t.CPtr, b: t.CPtr, n: sizet): # ------------------------------------------------------------ -# 线性层: forward = x @ W^T + b (W: out x in, x: m x in, out: m x out) -# 内积按 k%4 用 vipersimd.simd_dot4d —— MNIST 主计算热点 +# Linear layer: forward = x @ W^T + b (W: out x in, x: m x in, out: m x out) +# Inner product in k%4 chunks via vipersimd.simd_dot4d -- the MNIST compute hotspot # ------------------------------------------------------------ def linear_fwd(w: Tensor*, b: Tensor*, x: Tensor*, out: Tensor*): @@ -258,10 +258,10 @@ def linear_fwd(w: Tensor*, b: Tensor*, # ------------------------------------------------------------ -# 线性层反向: 输入 w(out,in) x(m,in) g(m,out) +# Linear backward: inputs w(out,in) x(m,in) g(m,out) # dw[j,i] += sum_b g[b,j]*x[b,i] ; db[j] += sum_b g[b,j] # dx[b,i] = sum_j g[b,j]*w[j,i] -# 注意 dw/db 按"累加"语义(调用前清零 Grad) +# Note: dw/db use accumulate semantics (zero Grad before the call) # ------------------------------------------------------------ def linear_bwd(w: Tensor*, x: Tensor*, g: Tensor*, dw: Tensor*, @@ -281,8 +281,8 @@ def linear_bwd(w: Tensor*, x: Tensor*, dbd: double* = db.Data dxd: double* = dx.Data - # dw/db/dx 清零(dx 恒清零: 它是本样本的输入梯度, 不跨样本累积) - # accum=1 时保留 dw/db 现值 —— 逐样本反向累积整批参数梯度的调用方用 + # Zero dw/db/dx (dx is always zeroed: it is this sample's input gradient, not accumulated across samples) + # accum=1 keeps dw/db -- for callers accumulating whole-batch parameter gradients sample by sample if accum == 0: string.memset(dw.Data, 0, n * k * sizet(8)) string.memset(db.Data, 0, n * sizet(8)) @@ -303,7 +303,7 @@ def linear_bwd(w: Tensor*, x: Tensor*, # ------------------------------------------------------------ -# ReLU (simd_max4d 版本) 与反向 +# ReLU (simd_max4d version) and backward # ------------------------------------------------------------ def relu_fwd(x: Tensor*, out: Tensor*): if x is None or out is None: @@ -348,9 +348,9 @@ def relu_backward(x: Tensor*, g: Tensor*, # ------------------------------------------------------------ -# softmax + CrossEntropy(融合, 数值稳定) —— MNIST 输出层 -# logits: (m, C), labels: m 个 int (0..C-1) -# loss 写入 *loss, 梯度(已按 1/m 归一)写入 grad (m, C) +# softmax + CrossEntropy (fused, numerically stable) -- MNIST output layer +# logits: (m, C), labels: m ints (0..C-1) +# loss written to *loss, gradient (normalized by 1/m) written to grad (m, C) # ------------------------------------------------------------ def softmax_crossentropy(logits: Tensor*, labels: int*, @@ -369,7 +369,7 @@ def softmax_crossentropy(logits: Tensor*, total: double = double(0.0) inv_m: double = double(1.0) / double(m) for b in range(m): - # 数值稳定 softmax + # Numerically stable softmax mx: double = ld[b * c] j: sizet = 1 while j < c: @@ -384,7 +384,7 @@ def softmax_crossentropy(logits: Tensor*, y: int = lb[b] # loss += -(logits[lab] - max - log(s)) total = total - (ld[b * c + sizet(y)] - mx - math.log(s)) - # 梯度: (softmax - onehot) / m + # Gradient: (softmax - onehot) / m j = 0 while j < c: p: double = math.exp(ld[b * c + j] - mx) / s @@ -397,7 +397,7 @@ def softmax_crossentropy(logits: Tensor*, # ------------------------------------------------------------ -# 每行 argmax (分类预测) +# Per-row argmax (classification prediction) # ------------------------------------------------------------ def row_argmax(x: Tensor*, pred: int*): if x is None or pred is None: @@ -418,7 +418,7 @@ def row_argmax(x: Tensor*, pred: int*): # ------------------------------------------------------------ -# SGD: data -= lr * grad(全量梯度) / 重球法: v = m*v+g, data -= lr*v +# SGD: data -= lr * grad (full gradient) / momentum: v = m*v+g, data -= lr*v # ------------------------------------------------------------ def sgd_step(param: Tensor*, lr: double): if param is None or param.Data is None or param.Grad is None: @@ -432,11 +432,8 @@ def sgd_step(param: Tensor*, lr: double): def sgd_step_momentum(param: Tensor*, vel: t.CPtr, lr: double, momentum: double): - """带动量(重球法)的 SGD: v = m*v + g ; w -= lr*v - - vel 是与 param 同元素个数的 double 缓冲, 由调用方分配并清零(跨 step 持有); - vel 传 None 时退化为普通 sgd_step。动量是 CNN/MLP 训练收敛速度的关键 —— - 纯 SGD 在固定 lr 下前几个 epoch 很慢且容易卡在低准确率。""" + """SGD with momentum: v = m*v + g ; w -= lr*v + vel is a caller-allocated, zeroed double buffer with the same element count as param (held across steps); pass None to fall back to plain sgd_step.""" if param is None or param.Data is None or param.Grad is None: return if vel is None: @@ -453,7 +450,7 @@ def sgd_step_momentum(param: Tensor*, vel: t.CPtr, # ------------------------------------------------------------ -# 逐元素乘加 (调试/扩展用, vipersimd fma_array) +# Element-wise multiply-add (for debug/extension, vipersimd fma_array) # a = a*b + c # ------------------------------------------------------------ def fma_array_inplace(a: Tensor*, b: t.CPtr, c: t.CPtr): @@ -463,31 +460,13 @@ def fma_array_inplace(a: Tensor*, b: t.CPtr, c: t.CPtr): # ------------------------------------------------------------ -# Conv2d / MaxPool2d —— 轻量 CNN 用的原始缓冲算子 -# -# 这些算子直接吃裸缓冲(不用 Tensor: torch.Tensor 只有 2D 形状), -# 布局一律 NCHW 连续 double; 卷积权重 [cout][cin][k][k]。 -# 约定: -# *_fwd 覆盖写输出 -# conv2d_bwd_dx 先清零再累加(逐样本输入梯度, 不跨样本累积) -# maxpool2d_bwd 同上 -# conv2d_bwd_dw += 累加参数梯度(跨样本累积, 批次开始由调用方清零) -# -# SIMD: 三个卷积算子的内层都按"输出列"做 4 路 AVX2 double。 -# 依据是 stride==1 时相邻输出列恰好对应相邻输入列, 故一列 4 个 double -# 在内存里连续, 可直接 vmovupd + vfmadd213pd, 权重标量 vbroadcastsd。 -# 代价是需要一个"安全列区间": 4 条 lane 的输出列都在 [0,wout) 内, 且它们 -# 触及的输入列 [ox-pad, ox+3+k-1-pad] 全在 [0,win) 内 —— 即 -# ox ∈ [pad, win+pad-k-3]。区间外的列与 stride!=1 一律走原标量分支。 -# (maxpool2d 不做 SIMD: k==stride==2 时 4 个窗口起点间隔 2 个 double, -# 不是连续地址, 无 gather 原语; 且它占总计算量不到 1%。) +# Conv2d / MaxPool2d raw-buffer kernels for a lightweight CNN (NCHW contiguous double, weights [cout][cin][k][k]). +# Conventions: *_fwd overwrites output; conv2d_bwd_dx/maxpool2d_bwd zero then accumulate (per-sample dx); conv2d_bwd_dw accumulates params (+=, caller zeroes per batch). +# SIMD inner loop over output columns, safe range ox in [pad, win+pad-k-3] when stride==1 (maxpool2d is scalar only). # ------------------------------------------------------------ def _dptr(base: t.CPtr, elem: sizet) -> t.CPtr: - """裸 double 缓冲的元素下标 → 字节地址 - - 裸 t.CPtr 是 i8*, `p + n` 走的是字节偏移; 而 vipersimd 的 4 路原语按 - "4 个 double = 32 字节"读写, 所以下标必须显式 ×8。 - (与 linear_fwd 里的 t.CPtr(uint64(p) + i * 8) 同一写法。)""" + """Convert an element index into a byte address for a raw double buffer. + Raw t.CPtr is i8* and `p + n` is a byte offset, so the index must be multiplied by 8 for vipersimd 4-wide (32-byte) access.""" return t.CPtr(uint64(base) + uint64(elem) * 8) @@ -496,7 +475,7 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr, cout: sizet, k: sizet, stride: sizet, pad: sizet): """out[n][cout][hout][wout] = conv2d(x, w) + b - hout = (hin + 2*pad - k)/stride + 1(wout 同理, 整除向下取整)""" + hout = (hin + 2*pad - k)/stride + 1 (same for wout; integer division floors).""" if x is None or w is None or out is None: return hout: sizet = (hin + pad * 2 - k) / stride + 1 @@ -505,14 +484,14 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr, wd: double* = w od: double* = out kk: sizet = cin * k * k - # SIMD 安全区间(见文件头说明) + # SIMD safe range (see file-header notes) simd_on: int = 0 smax: sizet = 0 if stride == 1 and win + pad >= k + 3: simd_on = 1 smax = win + pad - (k + 3) - wb: t.CPtr = None # 权重标量 × 4 路广播副本(kk 个标量) - t4: t.CPtr = None # 4 路累加器 + wb: t.CPtr = None # 4-lane broadcast copy of weight scalars (kk scalars) + t4: t.CPtr = None # 4-lane accumulator if simd_on != 0: wb = stdlib.malloc(kk * 32) t4 = stdlib.malloc(32) @@ -534,9 +513,8 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr, if b is not None: bd: double* = b bias = bd[co] - # 权重标量预广播: 每个 (ci,ky,kx) 一个 4 路副本, 本通道所有输出 - # 行/列共用。否则 vbroadcastsd(4 次 store) 会跟着 4 路循环重复, - # 把 SIMD 的收益吃掉一半。 + # Pre-broadcast weight scalars: one 4-lane copy per (ci,ky,kx), shared by all output rows/cols of this channel. + # Otherwise vbroadcastsd (4 stores) repeats in the 4-lane loop and halves the SIMD gain. if simd_on != 0: wb4: sizet = 0 while wb4 < kk: @@ -552,7 +530,7 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr, ox: sizet = 0 while ox < wout: if simd_on != 0 and ox >= pad and ox <= smax and ox + 4 <= wout: - # ---- 4 路: 一次算 4 个相邻输出列 ---- + # ---- 4-lane: compute 4 adjacent output columns at once ---- t4d[0] = bias t4d[1] = bias t4d[2] = bias @@ -582,7 +560,7 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr, od[ob + 3] = t4d[3] ox += 4 else: - # ---- 标量: 边界列 / stride != 1 ---- + # ---- scalar: boundary columns / stride != 1 ---- ox0: sizet = ox * stride acc: double = bias ci2: sizet = 0 @@ -619,11 +597,8 @@ def conv2d_bwd_dw(x: t.CPtr, dout: t.CPtr, dw: t.CPtr, db: t.CPtr, n: sizet, cin: sizet, hin: sizet, win: sizet, cout: sizet, k: sizet, stride: sizet, pad: sizet): - """累加: dw[co][ci][ky][kx] += sum dout*x ; db[co] += sum dout - (+= 语义, 调用方在批次开始前清零) - - 循环顺序为 (ci,ky,kx) 在外、(oy,ox) 在内: 每个权重元素只做一次 - 读-改-写; 内层按输出列 4 路 SIMD 累加, 最后 hsum 成标量。""" + """Accumulate: dw[co][ci][ky][kx] += sum dout*x ; db[co] += sum dout (+= semantics, caller zeroes before the batch). + Loop order (ci,ky,kx) outer, (oy,ox) inner: one read-modify-write per weight; inner loop 4-lane SIMD, then hsum to scalar.""" if x is None or dout is None or dw is None: return hout: sizet = (hin + pad * 2 - k) / stride + 1 @@ -651,7 +626,7 @@ def conv2d_bwd_dw(x: t.CPtr, dout: t.CPtr, dw: t.CPtr, db: t.CPtr, co: sizet = 0 while co < cout: gp: sizet = (bi * cout + co) * hout * wout - # ---- db[co] += sum(g): g 的整个通道平面是连续的 ---- + # ---- db[co] += sum(g): the whole channel plane of g is contiguous ---- if dbd is not None: tot0: double = double(0.0) if simd_on != 0: @@ -732,10 +707,8 @@ def conv2d_bwd_dx(w: t.CPtr, dout: t.CPtr, dx: t.CPtr, n: sizet, cin: sizet, hin: sizet, win: sizet, cout: sizet, k: sizet, stride: sizet, pad: sizet): - """dx 先清零再累加(逐样本输入梯度): dx[ci][iy][ix] += sum dout*w - - stride==1 时内层按输出列 4 路 SIMD: 4 条 lane 对应的 dx 单元也是连续的, - 故可直接对 dx 做 in-place fma(g4 * w_broadcast + dx4)。""" + """dx is zeroed then accumulated (per-sample input gradient): dx[ci][iy][ix] += sum dout*w. + With stride==1 the inner loop is 4-lane SIMD; the lanes map to contiguous dx cells, so dx can be updated in place with fma(g4 * w4 + dx4).""" if w is None or dout is None or dx is None: return hout: sizet = (hin + pad * 2 - k) / stride + 1 @@ -750,15 +723,14 @@ def conv2d_bwd_dx(w: t.CPtr, dout: t.CPtr, dx: t.CPtr, if stride == 1 and win + pad >= k + 3: simd_on = 1 smax = win + pad - (k + 3) - wb: t.CPtr = None # 整个权重张量的 4 路广播副本(cout*kk 个标量) + wb: t.CPtr = None # 4-lane broadcast copy of the whole weight tensor (cout*kk scalars) if simd_on != 0: wb = stdlib.malloc(cout * kk * 32) if wb is None: simd_on = 0 wbd: double* = wb if simd_on != 0: - # 权重在一次调用内不变 → 整体预广播一次(注意别在 4 路循环里重复 - # vbroadcastsd: 那是 4 次 store, 会把 SIMD 的收益吃掉一半) + # Weights are constant within one call, so pre-broadcast once (do not repeat vbroadcastsd in the 4-lane loop: 4 stores halves the SIMD gain) wjn: sizet = cout * kk for wj4 in range(wjn): wq2: double = wd[wj4] @@ -819,12 +791,12 @@ def conv2d_bwd_dx(w: t.CPtr, dout: t.CPtr, dx: t.CPtr, # ------------------------------------------------------------ -# MaxPool2d (k x k, stride 通常 = k) +# MaxPool2d (k x k, stride usually = k) # ------------------------------------------------------------ def maxpool2d_fwd(x: t.CPtr, out: t.CPtr, argmax: t.CPtr, n: sizet, c: sizet, hin: sizet, win: sizet, k: sizet, stride: sizet): - """argmax: int 数组 [n*c*hout*wout], 记窗口内最大值在本通道内的下标(yy*win+xx)""" + """argmax: int array [n*c*hout*wout] recording the max index within the channel (yy*win+xx)""" if x is None or out is None or argmax is None: return hout: sizet = (hin - k) / stride + 1 @@ -869,7 +841,7 @@ def maxpool2d_fwd(x: t.CPtr, out: t.CPtr, argmax: t.CPtr, def maxpool2d_bwd(dout: t.CPtr, argmax: t.CPtr, dx: t.CPtr, n: sizet, c: sizet, hin: sizet, win: sizet, k: sizet, stride: sizet): - """dx 先清零再散射累加: dx[argmax] += dout""" + """dx is zeroed then scatter-accumulated: dx[argmax] += dout""" if dout is None or argmax is None or dx is None: return hout: sizet = (hin - k) / stride + 1 diff --git a/traceback.vp b/traceback.vp index 263688e..3287707 100644 --- a/traceback.vp +++ b/traceback.vp @@ -433,17 +433,13 @@ import w32.win32dbghelp as dbg extern def GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: CHARPTR, nSize: DWORD) -> DWORD -# ---- 编译期开关 ------------------------------------------------------------- +# ---- Compile-time switch ------------------------------------------------------------- -# 1 = 允许在**可选加强**路径上使用 dbghelp!StackWalk64(**产物必须链接 -ldbghelp**); -# 注意:主回溯路径是 WalkRbpChain(零 dbghelp 依赖),dbghelp 只在 RBP 链拿不到更多帧 -# 且 Install 探针判定其可用时才补一次——ASan 只是与它**共享** DbgHelp 状态,不再一票 -# 否决(见模块头第十一节 B)。 -# 0 = 完全不用 dbghelp。切换机制:常量比较在 HandleIf 处被常量折叠,凡出现在 -# `if TRACEBACK_USE_DBGHELP != 0:` 分支里的语句(Install 的 SymInitialize/GetProcAddress -# 段、WalkViaDbgHelp 的整个函数体、DumpMainThread 的可选加强调用)在置 0 时整体不生成代码 -# ——范式与依据同 includes/memhub.vp 的 MEMBUDDY_THREAD_SAFE 注释("未取分支整体不生成 -# 代码 ⇒ 函数体为空")。故置 0 后本模块不留任何 dbghelp 链接期符号。 +# 1 = allow dbghelp!StackWalk64 on the optional-augmentation path (artifacts MUST link -ldbghelp); +# the main path is WalkRbpChain (no dbghelp) and dbghelp is only added when the RBP chain yields no +# more frames and the Install probe passes; 0 = no dbghelp at all, and constant folding removes the +# whole `if TRACEBACK_USE_DBGHELP != 0:` branch (same mechanism as MEMBUDDY_THREAD_SAFE in +# includes/memhub.vp), leaving no dbghelp link-time symbols. define TRACEBACK_USE_DBGHELP = 1 # Trigger event values: see includes/w32/win32console.vp (CTRL_C_EVENT=0 / CTRL_BREAK_EVENT=1). diff --git a/viperio.vp b/viperio.vp index 867f55f..34183a9 100644 --- a/viperio.vp +++ b/viperio.vp @@ -28,7 +28,7 @@ class Buf: for i in range(n): self.data[self.length + i] = src[i] self.length += n - # 恰好写满(n==avail)时 data[capacity] 越界 1 字节,加保护 + # When exactly full (n==avail), data[capacity] would overflow by 1 byte, so guard it if self.length < self.capacity: self.data[self.length] = 0 return n diff --git a/viperlib.vp b/viperlib.vp index 27ba65e..2710684 100644 --- a/viperlib.vp +++ b/viperlib.vp @@ -13,8 +13,8 @@ def get_digit_count(num: uint, base: int) -> int: num //= base return count -# 返回类型必须与 CRT `sprintf` 以及 includes/stdio.vp 的 -# 语义上直接返回 vsnprintf 的写入长度(与 CRT 一致)。 +# Return type must be int, matching the CRT `sprintf` and includes/stdio.vp; +# return vsnprintf's write length directly (consistent with the CRT). export def sprintf(buf: str, rule: str, *args) -> int: if buf is None or rule is None: return 0 return vsnprintf(buf, 0x7FFFFFFF, rule, args) @@ -29,14 +29,8 @@ export def snprintf(buf: str, size: sizet, fmt: str, *args) -> int: ret: int = vsnprintf(buf, size, fmt, args) return ret -# 检查浮点特殊值(IEEE 754) -# 返回:0=正常, 1=+inf, 2=-inf, 3=nan -# 注意:不可用位双关 `*&fbits = *&fv` 读取 IEEE 位模式——该赋值会被编译器静默 -# 丢弃(IR 中 fbits 恒为 0,见 TransPyV/output/db514c1af999c5f5.text.ll),会使 -# 本函数恒返回 0、特殊值检查彻底失效,进而让 inf 进入浮点格式化循环而死循环。 -# 这里改用纯浮点语义判定,不依赖任何位操作: -# NaN :fv != fv 为真 -# ±inf:fv - fv 得到 NaN(有限值得到 0.0) +# Check float special values (IEEE 754): 0=normal, 1=+inf, 2=-inf, 3=nan. +# Do NOT read the bit pattern via `*&fbits = *&fv`: the assignment is silently dropped (fbits stays 0), so this function would always return 0 and inf would spin forever in the float formatting loops. Use pure float semantics instead (NaN: fv != fv; ±inf: fv - fv is NaN, finite gives 0.0). def _check_special_float(fv: double) -> int: if fv != fv: return 3 diff: double = fv - fv @@ -586,7 +580,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: else: fc = 110 else: - # inf (f_sp==1 +inf 或 f_sp==2 -inf) + # inf (f_sp==1 +inf or f_sp==2 -inf) if fi_c == 0: fc = 105 elif fi_c == 1: @@ -631,7 +625,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: di = 1 else: scale: double = 1.0 - # 上界防护:inf 会让 `work >= scale*10.0` 永真,正常有限值最多 308 次 + # Upper-bound guard: inf makes `work >= scale*10.0` always true; finite values need at most 308 iterations scale_guard: int = 0 while work >= scale * 10.0 and scale_guard < 400: scale *= 10.0 @@ -745,7 +739,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: else: ec = 110 else: - # inf (e_sp==1 +inf 或 e_sp==2 -inf) + # inf (e_sp==1 +inf or e_sp==2 -inf) if ei_c == 0: ec = 105 elif ei_c == 1: @@ -785,7 +779,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: ptr += _step exp10: int = 0 if fv != 0.0: - # 上界防护:inf 会让下面两个循环永真(inf/10==inf、inf*10==inf) + # Upper-bound guard: inf makes the two loops below always true (inf/10==inf, inf*10==inf) exp_guard: int = 0 while fv >= 10.0 and exp_guard < 400: fv /= 10.0 @@ -939,7 +933,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: else: gc = 110 else: - # inf (g_sp==1 +inf 或 g_sp==2 -inf) + # inf (g_sp==1 +inf or g_sp==2 -inf) if gi_c == 0: gc = 105 elif gi_c == 1: @@ -980,7 +974,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: fv_orig: double = fv exp10: int = 0 if fv != 0.0: - # 上界防护:卡死现场即此循环(inf 会让 fv>=10.0 永真,inf/10==inf) + # Upper-bound guard: this loop is where it hangs (inf makes fv>=10.0 always true, inf/10==inf) exp_guard: int = 0 while fv >= 10.0 and exp_guard < 400: fv /= 10.0 @@ -1020,9 +1014,8 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: if prec_e > 0: temp[1] = 46 g_len = 2 - # 小数位逆序存入(个位放 g_len+prec_e-1),temp 直接为正序。 - # 原交换反转循环在 TPV 编译产物中失效(小数位未反转,如 456->654), - # 改逆序存储,与 %f 的倒序输出一致,绕开三变量交换。 + # Store fraction digits in reverse order (ones place at g_len+prec_e-1) so temp is already forward-ordered. + # The original swap-reverse loop fails in TPV output (digits not reversed, e.g. 456->654); reverse storage matches %f's output and avoids the three-variable swap. fi: int = 0 fp_inv: uint64 = frac_p fp_idx: int = g_len + prec_e - 1 @@ -1041,7 +1034,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: g_len = 1 else: scale: double = 1.0 - # 上界防护:inf 会让 `work >= scale*10.0` 永真,正常有限值最多 308 次 + # Upper-bound guard: inf makes `work >= scale*10.0` always true; finite values need at most 308 iterations scale_guard: int = 0 while work >= scale * 10.0 and scale_guard < 400: scale *= 10.0 @@ -1184,7 +1177,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int: else: ac = 110 else: - # inf (a_sp==1 +inf 或 a_sp==2 -inf) + # inf (a_sp==1 +inf or a_sp==2 -inf) if ai_c == 0: ac = 105 elif ai_c == 1: diff --git a/vipersimd.vp b/vipersimd.vp index cac2eed..fde7ca6 100644 --- a/vipersimd.vp +++ b/vipersimd.vp @@ -182,10 +182,8 @@ movups [{asminp(out, ASM_DESCR.REG_ANY)}], xmm0""", def simd_add_array(a: double*, b: double*, out: double*, n: sizet): """AVX2 batch: out[i:i+4] = a[i:i+4] + b[i:i+4], i=0,4,8,... - ×8 双缓冲(32 doubles/大迭代):a(ymm0-7)+b(ymm8-15) 16 路 load 全部在飞后 - 做 8 条独立 add→store 链,最大化内存级并行、压榨访存口。 - 尾巴(不足 8 个 ymm 的块)回退单向量循环,语义与原版一致(floor(n/4) 向量)。 - 注:ymm0-15 clobber 需 TPV __asm__ 支持(CallGenCBuiltin ASM_MAX_CLOBBERS=32)。""" + ×8 double-buffering (32 doubles/iteration): 16 loads in flight (a: ymm0-7, b: ymm8-15), then 8 independent add->store chains; tail falls back to the single-vector loop (floor(n/4) vectors). + Note: ymm0-15 clobber requires TPV __asm__ support (CallGenCBuiltin ASM_MAX_CLOBBERS=32).""" __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} shr rcx, 2 mov r8, {asminp(a, ASM_DESCR.REG_ANY)} @@ -279,7 +277,7 @@ vzeroupper""", def simd_mul_array(a: double*, b: double*, out: double*, n: sizet): """AVX2 batch: out[i:i+4] = a[i:i+4] * b[i:i+4] - ×8 双缓冲(同 simd_add_array)。""" + ×8 double-buffering (same as simd_add_array).""" __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} shr rcx, 2 mov r8, {asminp(a, ASM_DESCR.REG_ANY)} @@ -348,13 +346,8 @@ vzeroupper""", def simd_fma_array(a: double*, b: double*, c_val: double*, out: double*, n: sizet): - """AVX2 batch (单趟融合乘加): out[i:i+4] = a[i:i+4]*b[i:i+4] + c_val[i:i+4] - ×8 双缓冲(同 simd_mul_array):a(ymm0-7)+b(ymm8-15) 16 路 load 在飞后, - 用 8 条 vfmadd213pd 以 c_val 内存操作数为加数一次完成 FMA 并落 out, - 避免 mul→tmp→add 的两遍写法(读流量 = 每元素 24B,写 8B,无中间数组)。 - 语义:vfmadd213pd ymmX, ymmY, mem => ymmX = ymmY*ymmX + mem - ymm8..15 = a(big lane)*b(big lane) + c(big lane),单次舍入。 - 尾巴(不足 8 个 ymm 的块)回退单向量循环。n 须为 4 的倍数(floor(n/4) 向量)。""" + """AVX2 batch (single-pass FMA): out[i:i+4] = a[i:i+4]*b[i:i+4] + c_val[i:i+4] + ×8 double-buffering: 8 vfmadd213pd using the c_val memory operand, single rounding, no mul->tmp->add pass; tail falls back to the single-vector loop (n must be a multiple of 4).""" __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} shr rcx, 2 mov r8, {asminp(a, ASM_DESCR.REG_ANY)} @@ -426,15 +419,8 @@ vzeroupper""", def scalar_fma_array(a: double*, b: double*, c_val: double*, out: double*, n: sizet): - """标量(逐元素)融合乘加: out[i] = a[i]*b[i] + c_val[i], 一次只算 1 个 double。 - - 与 simd_fma_array 是同一数学内核的"未向量化"对照实现: - * 指令用标量 FMA `vfmadd213sd`(xmm 低位), 与 SIMD 版的 `vfmadd213pd` - 同为硬件融合乘加、同为单次舍入 -> 逐元素结果与 SIMD 版**逐位一致**, - CHECK 可直接对照; - * 每次循环 3 读 1 写、8 字节粒度, 相比 ×4 的 ymm 少 4 倍数据并行度, - 用来量化"向量化本身"带来的收益(SIMD 加速比)。 - n 须 >= 1; n == 0 直接跳过。""" + """Scalar (element-wise) FMA: out[i] = a[i]*b[i] + c_val[i], one double at a time. + Unvectorized counterpart of simd_fma_array: scalar `vfmadd213sd` vs SIMD `vfmadd213pd`, both hardware FMA with a single rounding, so results are bit-identical. n must be >= 1; n == 0 is skipped.""" __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} mov r8, {asminp(a, ASM_DESCR.REG_ANY)} mov r9, {asminp(b, ASM_DESCR.REG_ANY)} @@ -459,10 +445,8 @@ jnz .Lsca_loop def scalar_add_array(a: double*, b: double*, out: double*, n: sizet): - """标量(逐元素)加法: out[i] = a[i] + b[i], 一次 1 个 double。 - - `simd_add_array` 的未向量化对照: 标量 `vaddsd` vs SIMD `vaddpd`, - 同为 IEEE 加法 -> 结果逐位一致。""" + """Scalar (element-wise) add: out[i] = a[i] + b[i], one double at a time. + Unvectorized counterpart of `simd_add_array`: scalar `vaddsd` vs SIMD `vaddpd`, both IEEE add -> bit-identical results.""" __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} mov r8, {asminp(a, ASM_DESCR.REG_ANY)} mov r9, {asminp(b, ASM_DESCR.REG_ANY)} @@ -483,10 +467,8 @@ jnz .Lsca_add_loop def scalar_mul_array(a: double*, b: double*, out: double*, n: sizet): - """标量(逐元素)乘法: out[i] = a[i] * b[i], 一次 1 个 double。 - - `simd_mul_array` 的未向量化对照: 标量 `vmulsd` vs SIMD `vmulpd`, - 同为 IEEE 乘法 -> 结果逐位一致。""" + """Scalar (element-wise) multiply: out[i] = a[i] * b[i], one double at a time. + Unvectorized counterpart of `simd_mul_array`: scalar `vmulsd` vs SIMD `vmulpd`, both IEEE multiply -> bit-identical results.""" __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} mov r8, {asminp(a, ASM_DESCR.REG_ANY)} mov r9, {asminp(b, ASM_DESCR.REG_ANY)} @@ -507,10 +489,8 @@ jnz .Lsca_mul_loop def scalar_sqrt_array(a: double*, out: double*, n: sizet): - """标量(逐元素)平方根: out[i] = sqrt(a[i]), 一次 1 个 double。 - - `simd_sqrt_array` 的未向量化对照: 标量 `vsqrtsd` vs SIMD `vsqrtpd`, - 两者都是 IEEE 正确舍入的平方根 -> 结果逐位一致。""" + """Scalar (element-wise) square root: out[i] = sqrt(a[i]), one double at a time. + Unvectorized counterpart of `simd_sqrt_array`: scalar `vsqrtsd` vs SIMD `vsqrtpd`, both IEEE correctly-rounded sqrt -> bit-identical results.""" __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} mov r8, {asminp(a, ASM_DESCR.REG_ANY)} mov r10, {asminp(out, ASM_DESCR.REG_ANY)} @@ -554,7 +534,7 @@ vzeroupper""", def simd_sqrt_array(a: double*, out: double*, n: sizet): """AVX2 batch: out[i:i+4] = sqrt(a[i:i+4]) - ×4 展开:4 路独立 vsqrtpd 链隐藏平方根长延迟(吞吐受限场景显著提速)。""" + ×4 unrolled: 4 independent vsqrtpd chains hide the long sqrt latency (big speedup when throughput-bound).""" __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} shr rcx, 2 mov r8, {asminp(a, ASM_DESCR.REG_ANY)} diff --git a/vqt6/__init__.vp b/vqt6/__init__.vp index 830b8db..56582b6 100644 --- a/vqt6/__init__.vp +++ b/vqt6/__init__.vp @@ -22,20 +22,14 @@ from vqt6._qtnetwork import * # ============================================================ -# 高级 API 类(Pythonic 包装,不透明指针) +# High-level API classes (Pythonic wrappers over opaque pointers) # -# 为什么每个类都写 __new__: -# Viper 的类实例默认是"创建函数当前帧的 alloca"(见 -# CallGenCtor._translate_struct_ctor: tmp = build_alloca(struct_ty))。 -# 一旦包装对象被存进字段、或被 return 出函数,帧销毁后该指针即悬垂—— -# 表现为读 _ptr 得到垃圾、再把垃圾传给 Qt(如 QLayout::addWidget 崩溃)。 -# 编译器在类定义了 __new__ 时改用其返回值作存储指针,故这里统一 -# stdlib.malloc 堆分配包装壳(单字段 { _ptr } = 8 字节,进程期存活)。 -# 同模块内即用即弃的场景不受影响;跨帧/跨模块存取则必须靠它。 +# Every wrapper defines __new__ to heap-allocate its shell via stdlib.malloc +# ({ _ptr }, 8 bytes) so _ptr stays valid across frames and modules. # ============================================================ # ============================================================ -# 模块级 helper:QWidget 基础操作(各控件类复用) +# Module-level helpers: base QWidget operations (shared by widget classes) # ============================================================ def _w_show(ptr: t.CPtr): vqt6_qwidget_show(ptr) @@ -86,7 +80,7 @@ def _w_set_tool_tip(ptr: t.CPtr, tip: str): def _w_text_of(qstr: QtStringPtr) -> str: - """QtStringPtr → Viper str(复制到 malloc 缓冲)""" + """QtStringPtr -> Viper str (copied into a malloc buffer).""" buf: bytes = stdlib.malloc(1024) vqt6_qstring_to_utf8(qstr, buf, 1024) s: str = buf @@ -95,11 +89,11 @@ def _w_text_of(qstr: QtStringPtr) -> str: # ============================================================ -# 树状控件统计助手(返回 int,规避跨模块 FFI 返回指针) -# 叶子勾选态: 0=未选 1=半选 2=选中 +# Tree widget statistics helpers (return int to avoid cross-module FFI ptr returns) +# Leaf check state: 0=unchecked 1=partial 2=checked # ============================================================ def qtree_checked_count(tw: QtTreeWidgetPtr) -> int: - """统计被勾选的叶子节点数""" + """Count checked leaf nodes.""" n: int = 0 gi: int = 0 top_n: int = vqt6_qtreewidget_top_level_item_count(tw) @@ -117,7 +111,7 @@ def qtree_checked_count(tw: QtTreeWidgetPtr) -> int: def qtree_checked_size(tw: QtTreeWidgetPtr) -> int: - """统计被勾选叶子节点的磁盘占用合计(MB)""" + """Sum disk usage (MB) of checked leaf nodes.""" mb: int = 0 gi: int = 0 top_n: int = vqt6_qtreewidget_top_level_item_count(tw) @@ -135,7 +129,7 @@ def qtree_checked_size(tw: QtTreeWidgetPtr) -> int: def qtree_click_toggle(tw: QtTreeWidgetPtr): - """点击带子节点的当前行 → 展开/折叠""" + """Toggle expansion of the current row when it has children.""" cur: QtTreeWidgetItemPtr = vqt6_qtreewidget_current_item(tw) if cur is None: return @@ -145,17 +139,16 @@ def qtree_click_toggle(tw: QtTreeWidgetPtr): novirtual class QApplication: - """QApplication 包装(不透明指针)""" + """QApplication wrapper (opaque pointer).""" _ptr: QtAppPtr def __new__(self, argv: list[str]*) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, argv: list[str]*): - # 把 argv 转换为 argc/argv 数组 + # Convert argv into an argc/argv array. argc: int = 1 - # Viper 端 argv 是 c_char_p 指针数组 - # 这里仅传空 argv,避免 C ABI 复杂性 + # Viper argv is a c_char_p pointer array; an empty argv avoids C ABI complexity. argv_ptr: t.CPtr = t.CPtr(0) self._ptr = vqt6_qapplication_create(argc, argv_ptr) if self._ptr is None: @@ -180,24 +173,20 @@ novirtual class QApplication: return vqt6_qapplication_desktop_height(self._ptr) def set_font_family(self, family: str): - """设置应用级字体族(全局生效) + """Set the application-wide font family (global). - 必须在创建任何控件之前调用:Qt 是在控件构造时从应用字体继承的, - 之后再设只会影响"此后新建"的控件。 - - 这里必须走应用字体而不是给主窗口挂样式表(font-family):Qt Charts 的 - 标题/图例/坐标轴标签画在 QChart(QGraphicsWidget)上,样式表够不到它们, - 只有应用字体能一并覆盖。 + Must be called before creating any widget; it also reaches Qt Charts text + (titles/legend/axis labels) that stylesheets cannot. """ vqt6_qapplication_set_font_family_utf8(self._ptr, family) novirtual class QWidget: - """QWidget 包装""" + """QWidget wrapper.""" _ptr: QtWidgetPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -257,11 +246,11 @@ novirtual class QWidget: novirtual class QLabel: - """QLabel 包装""" + """QLabel wrapper.""" _ptr: QtLabelPtr def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, text: str = "", parent: "QWidget | None" = None): text_qs: QtStringPtr = vqt6_qstring_create(text) @@ -296,11 +285,11 @@ novirtual class QLabel: novirtual class QPushButton: - """QPushButton 包装""" + """QPushButton wrapper.""" _ptr: QtPushButtonPtr def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, text: str = "", parent: "QWidget | None" = None): text_qs: QtStringPtr = vqt6_qstring_create(text) @@ -348,7 +337,7 @@ novirtual class QPushButton: def setFlat(self, flat: int): vqt6_qpushbutton_set_flat(self._ptr, flat) - # 回调(cb 传 Viper 模块级函数,签名 def cb(slot_id: int, data: t.CPtr)) + # Callbacks: cb is a Viper module-level function cb(slot_id: int, data: t.CPtr). def clicked(self, cb: bytes, data: bytes = 0): vqt6_qpushbutton_connect_clicked(self._ptr, cb, data) @@ -357,19 +346,13 @@ novirtual class QPushButton: # ============================================================ -# 菜单:QAction / QMenu / QMenuBar / QStatusBar +# Menus: QAction / QMenu / QMenuBar / QStatusBar # -# 按依赖顺序定义(QAction → QMenu → QMenuBar → QStatusBar), 并整体排在 -# QMainWindow 之前, 使 addMenu()/addActionWithText() 的返回注解不再前向引用。 -# -# 注意: 返回"包装类对象"(按值 struct)的方法无法跨模块解析返回类型—— -# 依赖模块 text.ll 里是 %"sha1.QStatusBar" 这类 struct 值(不含 *, -# _ParseLLVMRetTyStr 解析失败), 而 vpi 缓存的是带引号的前向注解 -# ('QStatusBar'), 同样解析失败 → 调用方 fatal(mro_ret)。 -# 故 QMainWindow.menuBar/statusBar 一律返回裸指针。 +# Defined before QMainWindow so return annotations need no forward reference; +# menuBar/statusBar return raw pointers since wrapper-value returns don't cross modules. # ============================================================ novirtual class QAction: - """QAction 包装""" + """QAction wrapper.""" _ptr: QtActionPtr def __init__(self, text: str = ""): @@ -421,7 +404,7 @@ novirtual class QAction: novirtual class QMenu: - """QMenu 包装""" + """QMenu wrapper.""" _ptr: QtMenuPtr def __init__(self, title: str = ""): @@ -466,7 +449,7 @@ novirtual class QMenu: novirtual class QMenuBar: - """QMenuBar 包装""" + """QMenuBar wrapper.""" _ptr: QtMenuBarPtr def __init__(self, parent: "QWidget | None" = None): @@ -493,7 +476,7 @@ novirtual class QMenuBar: novirtual class QStatusBar: - """QStatusBar 包装""" + """QStatusBar wrapper.""" _ptr: QtStatusBarPtr def __init__(self, parent: "QWidget | None" = None): @@ -520,14 +503,14 @@ novirtual class QStatusBar: # ============================================================ -# 窗口:QMainWindow(依赖上面的 QMenuBar / QStatusBar, 故排在其后) +# Window: QMainWindow (defined after the QMenuBar / QStatusBar it depends on). # ============================================================ novirtual class QMainWindow: - """QMainWindow 包装""" + """QMainWindow wrapper.""" _ptr: QtMainWindowPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -541,14 +524,14 @@ novirtual class QMainWindow: vqt6_qmainwindow_set_central_widget(self._ptr, w) def menuBar(self) -> QtMenuBarPtr: - """返回窗口自带的菜单栏裸指针(不新建; 需要包装类请自行 new QMenuBar 后挂 _ptr)""" + """Return the window's own menu bar as a raw pointer (no wrapper created).""" return vqt6_qmainwindow_menu_bar(self._ptr) def setMenuBar(self, mb: t.CPtr): vqt6_qmainwindow_set_menu_bar(self._ptr, mb) def statusBar(self) -> QtStatusBarPtr: - """返回窗口自带的状态栏裸指针(不新建; 需要包装类请自行 new QStatusBar 后挂 _ptr)""" + """Return the window's own status bar as a raw pointer (no wrapper created).""" return vqt6_qmainwindow_status_bar(self._ptr) def addToolBar(self, tb: t.CPtr): @@ -586,7 +569,7 @@ novirtual class QMainWindow: novirtual class QDialog: - """QDialog 包装""" + """QDialog wrapper.""" _ptr: QtDialogPtr def __init__(self, parent: "QWidget | None" = None): @@ -638,14 +621,14 @@ novirtual class QDialog: # ============================================================ -# 输入控件:QLineEdit / QCheckBox / QRadioButton +# Input widgets: QLineEdit / QCheckBox / QRadioButton # ============================================================ novirtual class QLineEdit: - """QLineEdit 包装""" + """QLineEdit wrapper.""" _ptr: QtLineEditPtr def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, text: str = "", parent: "QWidget | None" = None): qs: QtStringPtr = vqt6_qstring_create(text) @@ -708,11 +691,11 @@ novirtual class QLineEdit: novirtual class QCheckBox: - """QCheckBox 包装""" + """QCheckBox wrapper.""" _ptr: QtCheckBoxPtr def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, text: str = "", parent: "QWidget | None" = None): qs: QtStringPtr = vqt6_qstring_create(text) @@ -770,11 +753,11 @@ novirtual class QCheckBox: novirtual class QRadioButton: - """QRadioButton 包装""" + """QRadioButton wrapper.""" _ptr: QtRadioButtonPtr def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, text: str = "", parent: "QWidget | None" = None): qs: QtStringPtr = vqt6_qstring_create(text) @@ -823,10 +806,10 @@ novirtual class QRadioButton: # ============================================================ -# 选择控件:QComboBox / QSlider / QSpinBox / QProgressBar +# Selection widgets: QComboBox / QSlider / QSpinBox / QProgressBar # ============================================================ novirtual class QComboBox: - """QComboBox 包装""" + """QComboBox wrapper.""" _ptr: QtComboBoxPtr def __init__(self, parent: "QWidget | None" = None): @@ -897,7 +880,7 @@ novirtual class QComboBox: novirtual class QSlider: - """QSlider 包装""" + """QSlider wrapper.""" _ptr: QtSliderPtr def __init__(self, orientation: int = QT_HORIZONTAL, parent: "QWidget | None" = None): @@ -952,11 +935,11 @@ novirtual class QSlider: novirtual class QSpinBox: - """QSpinBox 包装""" + """QSpinBox wrapper.""" _ptr: QtSpinBoxPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -1020,11 +1003,11 @@ novirtual class QSpinBox: novirtual class QProgressBar: - """QProgressBar 包装""" + """QProgressBar wrapper.""" _ptr: QtProgressBarPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -1083,10 +1066,10 @@ novirtual class QProgressBar: # ============================================================ -# 文本:QTextEdit / QPlainTextEdit +# Text: QTextEdit / QPlainTextEdit # ============================================================ novirtual class QTextEdit: - """QTextEdit 包装""" + """QTextEdit wrapper.""" _ptr: QtTextEditPtr def __init__(self, parent: "QWidget | None" = None): @@ -1144,11 +1127,11 @@ novirtual class QTextEdit: novirtual class QPlainTextEdit: - """QPlainTextEdit 包装""" + """QPlainTextEdit wrapper.""" _ptr: QtPlainTextEditPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -1200,10 +1183,10 @@ novirtual class QPlainTextEdit: # ============================================================ -# 列表:QListWidget / QTabWidget / QStackedWidget +# Lists: QListWidget / QTabWidget / QStackedWidget # ============================================================ novirtual class QListWidget: - """QListWidget 包装""" + """QListWidget wrapper.""" _ptr: QtListWidgetPtr def __init__(self, parent: "QWidget | None" = None): @@ -1249,13 +1232,10 @@ novirtual class QListWidget: def addBubble(self, text: str, qss: str, right_align: int = 0, max_bubble_w: int = 420, hpad: int = 28, vpad: int = 16): - """追加一条聊天气泡(微信/QQ 风格,右=自己/左=对方) + """Append a chat bubble (WeChat/QQ style, right=self / left=peer). - qss: 气泡 QLabel 样式(如 background-color/border-radius/padding) - right_align: 1=右对齐(自己),0=左对齐(对方) - max_bubble_w: 气泡最大宽度(超出自动换行) - hpad: QSS padding 左右合计(用于气泡宽度估算) - vpad: QSS padding 上下合计(用于气泡高度计算,防底部裁切) + qss: bubble QLabel style; right_align: 1=right(self)/0=left; max_bubble_w: + max width; hpad/vpad: horizontal/vertical QSS padding totals for sizing. """ tqs: QtStringPtr = vqt6_qstring_create(text) qqs: QtStringPtr = vqt6_qstring_create(qss) @@ -1265,7 +1245,7 @@ novirtual class QListWidget: def replaceLastBubble(self, text: str, qss: str, right_align: int = 0, max_bubble_w: int = 420, hpad: int = 28, vpad: int = 16): - """删除最后一条气泡并按新文本重建(流式回复逐字刷新用,参数同 addBubble)""" + """Remove the last bubble and rebuild it from new text (for streaming; params as addBubble).""" tqs: QtStringPtr = vqt6_qstring_create(text) qqs: QtStringPtr = vqt6_qstring_create(qss) vqt6_qlistwidget_replace_last_bubble(self._ptr, tqs, qqs, right_align, @@ -1299,7 +1279,7 @@ novirtual class QListWidget: novirtual class QTreeWidgetItem: - """QTreeWidgetItem 包装(树节点,勾选态 0=未选 2=选中)""" + """QTreeWidgetItem wrapper (tree node; check state 0=unchecked 2=checked).""" _ptr: QtTreeWidgetItemPtr def __init__(self, text: str = ""): @@ -1341,7 +1321,7 @@ novirtual class QTreeWidgetItem: return vqt6_qtreewidgetitem_check_state(self._ptr) def setSize(self, mb: int): - """把磁盘占用(MB)存到节点 UserRole(统计用)""" + """Store disk usage (MB) in the node's UserRole (for statistics).""" vqt6_qtreewidgetitem_set_data_int(self._ptr, mb) def setExpanded(self, expanded: int): @@ -1352,7 +1332,7 @@ novirtual class QTreeWidgetItem: novirtual class QTreeWidget: - """QTreeWidget 包装""" + """QTreeWidget wrapper.""" _ptr: QtTreeWidgetPtr def __init__(self, parent: "QWidget | None" = None): @@ -1423,11 +1403,11 @@ novirtual class QTreeWidget: novirtual class QTabWidget: - """QTabWidget 包装""" + """QTabWidget wrapper.""" _ptr: QtTabWidgetPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -1486,7 +1466,7 @@ novirtual class QTabWidget: novirtual class QStackedWidget: - """QStackedWidget 包装""" + """QStackedWidget wrapper.""" _ptr: QtStackedWidgetPtr def __init__(self, parent: "QWidget | None" = None): @@ -1535,10 +1515,10 @@ novirtual class QStackedWidget: # ============================================================ -# 布局:QHBoxLayout / QVBoxLayout / QGridLayout +# Layouts: QHBoxLayout / QVBoxLayout / QGridLayout # ============================================================ novirtual class QHBoxLayout: - """QHBoxLayout 包装(addWidget 传控件 ._ptr)""" + """QHBoxLayout wrapper (pass the widget's ._ptr to addWidget).""" _ptr: QtBoxLayoutPtr def __init__(self, parent: "QWidget | None" = None): @@ -1572,7 +1552,7 @@ novirtual class QHBoxLayout: novirtual class QVBoxLayout: - """QVBoxLayout 包装(addWidget 传控件 ._ptr)""" + """QVBoxLayout wrapper (pass the widget's ._ptr to addWidget).""" _ptr: QtBoxLayoutPtr def __init__(self, parent: "QWidget | None" = None): @@ -1606,7 +1586,7 @@ novirtual class QVBoxLayout: novirtual class QGridLayout: - """QGridLayout 包装(addWidget 传控件 ._ptr)""" + """QGridLayout wrapper (pass the widget's ._ptr to addWidget).""" _ptr: QtGridLayoutPtr def __init__(self, parent: "QWidget | None" = None): @@ -1631,10 +1611,10 @@ novirtual class QGridLayout: # ============================================================ -# 对话框:QMessageBox / QFileDialog(静态方法) +# Dialogs: QMessageBox / QFileDialog (static methods) # ============================================================ novirtual class QMessageBox: - """QMessageBox 静态对话框""" + """QMessageBox static dialog wrapper.""" _ptr: QtMessageBoxPtr @staticmethod @@ -1698,7 +1678,7 @@ novirtual class QMessageBox: novirtual class QFileDialog: - """QFileDialog 静态对话框""" + """QFileDialog static dialog wrapper.""" _ptr: QtFileDialogPtr @staticmethod @@ -1743,14 +1723,14 @@ novirtual class QFileDialog: # ============================================================ -# 定时器:QTimer +# Timer: QTimer # ============================================================ novirtual class QTimer: - """QTimer 包装""" + """QTimer wrapper.""" _ptr: QtTimerPtr def __new__(self) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self): self._ptr = vqt6_qtimer_create() @@ -1780,10 +1760,10 @@ novirtual class QTimer: # ============================================================ -# 绘图基础:QColor / QFont +# Drawing basics: QColor / QFont # ============================================================ novirtual class QColor: - """QColor 包装""" + """QColor wrapper.""" _ptr: QtColorPtr def __init__(self, r: int, g: int, b: int, a: int = 255): @@ -1805,7 +1785,7 @@ novirtual class QColor: novirtual class QFont: - """QFont 包装""" + """QFont wrapper.""" _ptr: QtFontPtr def __init__(self, family: str = "", point_size: int = 0): @@ -1838,15 +1818,15 @@ novirtual class QFont: # ============================================================ -# 剪贴板(复制链接等) +# Clipboard (copying links, etc.) # ============================================================ def clipboard_set_text(text: str): - """把文本写入系统剪贴板""" + """Write text to the system clipboard.""" vqt6_clipboard_set_text_utf8(text) def clipboard_text() -> str: - """读取系统剪贴板文本""" + """Read text from the system clipboard.""" buf: bytes = stdlib.malloc(4096) vqt6_clipboard_text_utf8(buf, 4096) s: str = buf @@ -1855,18 +1835,18 @@ def clipboard_text() -> str: # ============================================================ -# 自绘控件:Canvas / Graph3D / ChartView +# Custom-painted widgets: Canvas / Graph3D / ChartView # ============================================================ novirtual class Canvas: - """手绘板 / 28x28 灰度显示控件 + """Freehand drawing board / 28x28 grayscale display widget. - 手绘模式下鼠标左键描绘; toMnist28 把笔迹按 MNIST 预处理 - (包围盒缩放到 20x20 后质心居中)输出为 28x28 灰度字节数组。 + Left-drag draws; toMnist28 preprocesses the ink (bounding box scaled to + 20x20, centroid-centered) into a 28x28 grayscale byte array. """ _ptr: QtCanvasPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -1883,7 +1863,7 @@ novirtual class Canvas: return vqt6_canvas_has_ink(self._ptr) def setMode(self, mode: int): - """0 = 手绘, 1 = 图像显示""" + """0 = freehand drawing, 1 = image display.""" vqt6_canvas_set_mode(self._ptr, mode) def mode(self) -> int: @@ -1896,22 +1876,22 @@ novirtual class Canvas: vqt6_canvas_set_grid(self._ptr, on) def setScanLine(self, y_norm: int): - """0..1000 扫描线位置, <0 关闭""" + """Scan line position 0..1000; <0 disables it.""" vqt6_canvas_set_scan_line(self._ptr, y_norm) def setBackground(self, r: int, g: int, b: int): vqt6_canvas_set_bg(self._ptr, r, g, b) def toMnist28(self, out: bytes) -> int: - """笔迹 → 28x28 灰度(写入 out); 无墨迹返回 0""" + """Ink -> 28x28 grayscale (written to out); returns 0 if there is no ink.""" return vqt6_canvas_to_mnist28(self._ptr, out) def setImage28(self, inp: bytes): - """28x28 灰度 → 显示(自动切到图像显示模式)""" + """Show a 28x28 grayscale image (switches to image display mode).""" vqt6_canvas_set_image28(self._ptr, inp) def changed(self, cb: bytes, data: bytes = 0): - """笔迹完成(鼠标抬起)回调: def cb(slot_id: int, data: t.CPtr)""" + """Ink-finished (mouse release) callback: cb(slot_id: int, data: t.CPtr).""" vqt6_canvas_connect_changed(self._ptr, cb, data) def show(self): @@ -1937,16 +1917,15 @@ novirtual class Canvas: novirtual class Graph3D: - """软件 3D 神经网络结构图控件 + """Software 3D neural-network structure view. - 层次沿 Z 轴排布(输入层在最远端), 各层按近正方形网格铺开; - 相邻层前向全连接。左键拖拽旋转, 滚轮缩放。 - 渲染 API 与后端无关, 将来换 OpenGL 实现时调用代码无需改动。 + Layers run along Z (input farthest); each layer is a near-square grid and + adjacent layers are fully connected. Drag to rotate, wheel to zoom. """ _ptr: QtGraph3DPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -1957,7 +1936,7 @@ novirtual class Graph3D: raise RuntimeError("Failed to create Graph3D") def setup(self, layer_sizes: int*, n_layers: int): - """layer_sizes: int 数组指针(输入层→输出层各层节点数)""" + """layer_sizes: int array pointer (node count per layer, input -> output).""" vqt6_graph3d_setup(self._ptr, layer_sizes, n_layers) def clear(self): @@ -1970,18 +1949,18 @@ novirtual class Graph3D: return vqt6_graph3d_edge_count(self._ptr) def setLayerAlpha(self, layer: int, alpha: double): - """逐层点亮: 0..1""" + """Per-layer brightness: 0..1.""" vqt6_graph3d_set_layer_alpha(self._ptr, layer, alpha) def setAllLayerAlpha(self, alpha: double): vqt6_graph3d_set_all_layer_alpha(self._ptr, alpha) def setValues(self, values: double*, n: int): - """每节点激活值(0..1), 影响亮度与半径""" + """Per-node activation (0..1), affecting brightness and radius.""" vqt6_graph3d_set_values(self._ptr, values, n) def setEdgeWeights(self, w: double*, n: int): - """边权重(按层间顺序), 影响颜色/透明度并按 |w| 裁剪""" + """Edge weights (in inter-layer order); affect color/alpha and are culled by |w|.""" vqt6_graph3d_set_edge_weights(self._ptr, w, n) def setEdgeThreshold(self, tval: double): @@ -2029,9 +2008,9 @@ novirtual class Graph3D: novirtual class ChartView: - """Qt Charts 折线图控件(右侧 Y 轴可选) + """Qt Charts line-chart widget (optional right Y axis). - 典型用法: + Typical usage: cv = ChartView() loss = cv.addLine("loss", 255, 107, 107, 0) acc = cv.addLine("acc", 77, 171, 247, 1) @@ -2041,7 +2020,7 @@ novirtual class ChartView: _ptr: QtChartViewPtr def __new__(self, parent: "QWidget | None" = None) -> t.CPtr: - return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明 + return stdlib.malloc(8) # heap-allocated shell, see note at top of file def __init__(self, parent: "QWidget | None" = None): if parent is not None and parent._ptr is not None: @@ -2076,15 +2055,15 @@ novirtual class ChartView: vqt6_chartview_set_background(self._ptr, r, g, b) def setMaxPoints(self, max_points: int): - """滑动窗口: 每条折线超过 max_points 就丢弃最旧的点(<=0 表示不限制)""" + """Sliding window: drop a series' oldest points past max_points (<=0 = no limit).""" vqt6_chartview_set_max_points(self._ptr, max_points) def openZoom(self, title: str): - """打开(或复用)大窗查看当前曲线; 关闭只是隐藏, 不销毁""" + """Open (or reuse) a large window for the current curves; closing only hides it.""" vqt6_chartview_open_zoom(self._ptr, title) def syncZoom(self): - """把当前曲线与轴范围同步到大窗(大窗未打开时什么也不做)""" + """Sync current curves and axis ranges to the large window (no-op if not open).""" vqt6_chartview_sync_zoom(self._ptr) def show(self): @@ -2107,10 +2086,10 @@ novirtual class ChartView: # ============================================================ -# 模块导出 +# Module exports # ============================================================ __all__ = [ - # 高层类 + # High-level classes "QApplication", "QWidget", "QLabel", "QPushButton", "QMainWindow", "QDialog", "QLineEdit", "QCheckBox", "QRadioButton", @@ -2122,10 +2101,10 @@ __all__ = [ "QMessageBox", "QFileDialog", "QTimer", "QMenuBar", "QMenu", "QAction", "QStatusBar", "QColor", "QFont", - # 自绘控件 + # Custom-painted widgets "Canvas", "Graph3D", "ChartView", "clipboard_set_text", "clipboard_text", - # 类型 + # Types "QtObjectPtr", "QtStringPtr", "QtByteArrayPtr", "QtWidgetPtr", "QtAppPtr", "QtLabelPtr", "QtPushButtonPtr", "QtTreeWidgetPtr", "QtTreeWidgetItemPtr", @@ -2133,7 +2112,7 @@ __all__ = [ "QtMenuBarPtr", "QtMenuPtr", "QtActionPtr", "QtStatusBarPtr", "QtCanvasPtr", "QtGraph3DPtr", "QtChartViewPtr", "QtChartSeriesPtr", "QtTcpSocketPtr", "QtHostAddressPtr", "QtNetworkRequestPtr", "QtNetworkReplyPtr", - # 常量 + # Constants "QT_KEY_ESCAPE", "QT_KEY_RETURN", "QT_KEY_A", "QT_KEY_Z", "QT_LEFT_BUTTON", "QT_RIGHT_BUTTON", "QT_MID_BUTTON", "QT_HORIZONTAL", "QT_VERTICAL", diff --git a/vqt6/_qtcore.vp b/vqt6/_qtcore.vp index 465a589..cca93ec 100644 --- a/vqt6/_qtcore.vp +++ b/vqt6/_qtcore.vp @@ -10,34 +10,34 @@ from vqt6._types import * # ============================================================ # ============================================================ -# QString — UTF-16 字符串 +# QString — UTF-16 string # ============================================================ -# 创建 QString(从 UTF-8 C 字符串) +# Create a QString from a UTF-8 C string. def vqt6_qstring_create(utf8: char*) -> QtStringPtr | t.State: pass -# 获取 QString 的 UTF-8 表示(写入 buf,返回实际长度,不含 \0) -# buf 需足够大(建议 4 * qstring.length() + 1) +# Get the UTF-8 form of a QString (writes to buf, returns the actual length, excluding \0). +# buf must be large enough (suggested 4 * qstring.length() + 1). def vqt6_qstring_to_utf8(qstr: QtStringPtr, buf: char*, buf_size: sizet) -> sizet | t.State: pass -# 释放 QString +# Free a QString. def vqt6_qstring_free(qstr: QtStringPtr) -> t.State: pass -# 获取 QString 字符数 +# Get the QString character count. def vqt6_qstring_length(qstr: QtStringPtr) -> int | t.State: pass -# QString 是否为空 +# Whether the QString is empty. def vqt6_qstring_is_empty(qstr: QtStringPtr) -> int | t.State: pass -# QString 比较 +# Compare two QStrings. def vqt6_qstring_equals(qstr: QtStringPtr, other: QtStringPtr) -> int | t.State: pass -# QString 拼接(返回新 QString) +# Concatenate QStrings (returns a new QString). def vqt6_qstring_append(qstr: QtStringPtr, other: QtStringPtr) -> QtStringPtr | t.State: pass def vqt6_asset_url_utf8(buf: char*, buf_size: sizet, name: char*) -> t.State: pass # ============================================================ -# QByteArray — 字节数组 +# QByteArray — byte array # ============================================================ def vqt6_qbytearray_create(data: t.CPtr, size: sizet) -> QtByteArrayPtr | t.State: pass def vqt6_qbytearray_data(qba: QtByteArrayPtr, out_size: sizet*) -> t.CPtr | t.State: pass @@ -46,9 +46,9 @@ def vqt6_qbytearray_free(qba: QtByteArrayPtr) -> t.State: pass # ============================================================ -# QObject — 所有 Qt 对象的基类 +# QObject — base class of all Qt objects # ============================================================ -# 注意: Qt 对象的内存由 C++ 管理,Viper 端不应直接 free +# Note: Qt object memory is managed by C++; do not free it directly from Viper. def vqt6_qobject_delete(obj: QtObjectPtr) -> t.State: pass def vqt6_qobject_delete_later(obj: QtObjectPtr) -> t.State: pass def vqt6_qobject_set_object_name(obj: QtObjectPtr, name: QtStringPtr) -> t.State: pass @@ -56,23 +56,22 @@ def vqt6_qobject_object_name(obj: QtObjectPtr) -> QtStringPtr | t.State: pass def vqt6_qobject_set_parent(child: QtObjectPtr, parent: QtObjectPtr) -> t.State: pass def vqt6_qobject_parent(obj: QtObjectPtr) -> QtObjectPtr | t.State: pass -# 信号/槽连接 (简化版: 函数指针形式) +# Signal/slot connection (simplified). # typedef void (*vqt6_callback_t)(void* user_data); -# vqt6_qobject_connect_typed(sender, signal_id, receiver, slot_id, type) 由 C++ 桥接层处理 -# 由于函数指针在 C ABI 中复杂,我们采用字符串 signal/slot 名 + 全局回调注册表 +# Uses string signal/slot names + a global callback registry (function pointers are complex in the C ABI). def vqt6_qobject_connect(sender: QtObjectPtr, signal_name: char*, receiver: QtObjectPtr, slot_name: char*) -> int | t.State: pass def vqt6_qobject_disconnect(sender: QtObjectPtr, signal_name: char*, receiver: QtObjectPtr, slot_name: char*) -> int | t.State: pass -# 事件过滤器 +# Event filter def vqt6_qobject_install_event_filter(watched: QtObjectPtr, filter: QtObjectPtr) -> t.State: pass def vqt6_qobject_remove_event_filter(watched: QtObjectPtr, filter: QtObjectPtr) -> t.State: pass # ============================================================ -# QCoreApplication — 事件循环基类 +# QCoreApplication — base event loop class # ============================================================ def vqt6_qcoreapp_create(app_name: char*) -> QtCoreAppPtr | t.State: pass def vqt6_qcoreapp_instance() -> QtCoreAppPtr | t.State: pass @@ -89,7 +88,7 @@ def vqt6_qcoreapp_set_application_name(app: QtCoreAppPtr, name: QtStringPtr) -> # ============================================================ -# QTimer — 定时器 +# QTimer — timer # ============================================================ def vqt6_qtimer_create() -> QtTimerPtr | t.State: pass def vqt6_qtimer_create_with_parent(parent: QtObjectPtr) -> QtTimerPtr | t.State: pass @@ -100,12 +99,12 @@ def vqt6_qtimer_interval(timer: QtTimerPtr) -> int | t.State: pass def vqt6_qtimer_is_active(timer: QtTimerPtr) -> int | t.State: pass def vqt6_qtimer_set_single_shot(timer: QtTimerPtr, single_shot: int) -> t.State: pass def vqt6_qtimer_is_single_shot(timer: QtTimerPtr) -> int | t.State: pass -# timeout 信号 → 回调(cb 是 Viper 模块级函数,签名 (slot_id: int, user_data: t.CPtr)) +# timeout signal -> callback (cb is a Viper module-level function (slot_id: int, user_data: t.CPtr)). def vqt6_qtimer_connect_timeout(timer: QtTimerPtr, cb: bytes, user_data: bytes) -> t.State: pass # ============================================================ -# QVariant — 通用数据容器 +# QVariant — generic data container # ============================================================ def vqt6_qvariant_create_int(value: int) -> QtVariantPtr | t.State: pass def vqt6_qvariant_create_double(value: double) -> QtVariantPtr | t.State: pass @@ -121,7 +120,7 @@ def vqt6_qvariant_free(v: QtVariantPtr) -> t.State: pass # ============================================================ -# QEvent — 事件基类 +# QEvent — base event class # ============================================================ def vqt6_qevent_type(event: QtEventPtr) -> int | t.State: pass def vqt6_qevent_accept(event: QtEventPtr) -> t.State: pass @@ -130,7 +129,7 @@ def vqt6_qevent_is_accepted(event: QtEventPtr) -> int | t.State: pass # ============================================================ -# 字符串列表 (QStringList / QList) +# String list (QStringList / QList) # ============================================================ def vqt6_qstringlist_create() -> QtStringListPtr | t.State: pass def vqt6_qstringlist_append(list: QtStringListPtr, str: QtStringPtr) -> t.State: pass @@ -140,7 +139,7 @@ def vqt6_qstringlist_free(list: QtStringListPtr) -> t.State: pass # ============================================================ -# 字节数组列表 (QList) +# Byte-array list (QList) # ============================================================ def vqt6_qbytearraylist_create() -> QtByteArrayListPtr | t.State: pass def vqt6_qbytearraylist_append(list: QtByteArrayListPtr, qba: QtByteArrayPtr) -> t.State: pass @@ -150,18 +149,18 @@ def vqt6_qbytearraylist_free(list: QtByteArrayListPtr) -> t.State: pass # ============================================================ -# 全局回调注册表 +# Global callback registry # ============================================================ -# 用于从 C++ 端回调 Viper 代码 -# Viper 注册一个 slot 函数(用整数 ID 标识),C++ 在触发信号时通过 ID 查找 +# Lets C++ call back into Viper: Viper registers a slot function (integer ID) that +# C++ looks up by ID when a signal fires. # typedef void (*vqt6_slot_callback_t)(int slot_id, void* user_data); def vqt6_register_slot(slot_id: int, user_data: t.CPtr) -> t.State: pass def vqt6_unregister_slot(slot_id: int) -> t.State: pass -def vqt6_trigger_slot(slot_id: int) -> t.State: pass # 测试用 +def vqt6_trigger_slot(slot_id: int) -> t.State: pass # for testing # ============================================================ -# 剪贴板 — 复制链接等 +# Clipboard — copying links, etc. # ============================================================ def vqt6_clipboard_set_text_utf8(text: char*) -> t.State: pass def vqt6_clipboard_text_utf8(buf: char*, buf_size: int) -> int | t.State: pass diff --git a/vqt6/_qtgui.vp b/vqt6/_qtgui.vp index cbcde31..798df01 100644 --- a/vqt6/_qtgui.vp +++ b/vqt6/_qtgui.vp @@ -7,7 +7,7 @@ from vqt6._types import * # ============================================================ # ============================================================ -# QColor — 颜色 +# QColor — color # ============================================================ extern def vqt6_qcolor_create_rgb(r: int, g: int, b: int) -> QtColorPtr extern def vqt6_qcolor_create_rgba(r: int, g: int, b: int, a: int) -> QtColorPtr @@ -29,7 +29,7 @@ extern def vqt6_qcolor_free(c: QtColorPtr) # ============================================================ -# QFont — 字体 +# QFont — font # ============================================================ extern def vqt6_qfont_create() -> QtFontPtr extern def vqt6_qfont_create_with_family(family: QtStringPtr) -> QtFontPtr @@ -54,7 +54,7 @@ extern def vqt6_qfont_free(font: QtFontPtr) # ============================================================ -# QFontMetrics — 字体度量 +# QFontMetrics — font metrics # ============================================================ extern def vqt6_qfontmetrics_create(font: QtFontPtr) -> QtFontMetricsPtr extern def vqt6_qfontmetrics_width(fm: QtFontMetricsPtr, text: QtStringPtr) -> int @@ -65,7 +65,7 @@ extern def vqt6_qfontmetrics_free(fm: QtFontMetricsPtr) # ============================================================ -# QPixmap — 像素图 +# QPixmap — pixmap # ============================================================ extern def vqt6_qpixmap_create() -> QtPixmapPtr extern def vqt6_qpixmap_create_with_size(w: int, h: int) -> QtPixmapPtr @@ -81,7 +81,7 @@ extern def vqt6_qpixmap_free(pm: QtPixmapPtr) # ============================================================ -# QImage — 图像 +# QImage — image # ============================================================ extern def vqt6_qimage_create() -> QtImagePtr extern def vqt6_qimage_create_with_size(w: int, h: int, format: int) -> QtImagePtr @@ -97,7 +97,7 @@ extern def vqt6_qimage_free(img: QtImagePtr) # ============================================================ -# QPainter — 绘画器 +# QPainter — painter # ============================================================ extern def vqt6_qpainter_create() -> QtPainterPtr extern def vqt6_qpainter_begin(p: QtPainterPtr, device: QtPixmapPtr) -> int @@ -112,7 +112,7 @@ extern def vqt6_qpainter_font(p: QtPainterPtr) -> QtFontPtr extern def vqt6_qpainter_set_opacity(p: QtPainterPtr, opacity: double) extern def vqt6_qpainter_opacity(p: QtPainterPtr) -> double -# 绘图原语 +# Drawing primitives extern def vqt6_qpainter_draw_line(p: QtPainterPtr, x1: int, y1: int, x2: int, y2: int) extern def vqt6_qpainter_draw_rect(p: QtPainterPtr, x: int, y: int, w: int, h: int) extern def vqt6_qpainter_fill_rect(p: QtPainterPtr, x: int, y: int, w: int, h: int, color: QtColorPtr) @@ -130,7 +130,7 @@ extern def vqt6_qpainter_draw_pixmap(p: QtPainterPtr, x: int, y: int, pixmap: Qt extern def vqt6_qpainter_draw_image(p: QtPainterPtr, x: int, y: int, image: QtImagePtr) extern def vqt6_qpainter_draw_tiled_pixmap(p: QtPainterPtr, x: int, y: int, w: int, h: int, pixmap: QtPixmapPtr) -# 变换 +# Transforms extern def vqt6_qpainter_translate(p: QtPainterPtr, dx: int, dy: int) extern def vqt6_qpainter_rotate(p: QtPainterPtr, angle: double) extern def vqt6_qpainter_scale(p: QtPainterPtr, sx: double, sy: double) @@ -142,7 +142,7 @@ extern def vqt6_qpainter_free(p: QtPainterPtr) # ============================================================ -# QPen — 画笔 +# QPen — pen # ============================================================ extern def vqt6_qpen_create() -> QtPenPtr extern def vqt6_qpen_create_with_color(color: QtColorPtr) -> QtPenPtr @@ -157,7 +157,7 @@ extern def vqt6_qpen_free(pen: QtPenPtr) # ============================================================ -# QBrush — 画刷 +# QBrush — brush # ============================================================ extern def vqt6_qbrush_create() -> QtBrushPtr extern def vqt6_qbrush_create_with_color(color: QtColorPtr) -> QtBrushPtr @@ -170,7 +170,7 @@ extern def vqt6_qbrush_free(brush: QtBrushPtr) # ============================================================ -# QIcon — 图标 +# QIcon — icon # ============================================================ extern def vqt6_qicon_create() -> QtIconPtr extern def vqt6_qicon_create_with_file(file_name: QtStringPtr) -> QtIconPtr @@ -182,7 +182,7 @@ extern def vqt6_qicon_free(icon: QtIconPtr) # ============================================================ -# QCursor — 鼠标光标 +# QCursor — mouse cursor # ============================================================ extern def vqt6_qcursor_create() -> QtCursorPtr extern def vqt6_qcursor_create_with_shape(shape: int) -> QtCursorPtr @@ -195,7 +195,7 @@ extern def vqt6_qcursor_free(cursor: QtCursorPtr) # ============================================================ -# QKeyEvent — 键盘事件 +# QKeyEvent — key event # ============================================================ extern def vqt6_qkeyevent_create(type: int, key: int, modifiers: int, text: QtStringPtr) -> QtKeyEventPtr extern def vqt6_qkeyevent_key(event: QtKeyEventPtr) -> int @@ -206,7 +206,7 @@ extern def vqt6_qkeyevent_free(event: QtKeyEventPtr) # ============================================================ -# QMouseEvent — 鼠标事件 +# QMouseEvent — mouse event # ============================================================ extern def vqt6_qmouseevent_create(type: int, local_pos: QtObjectPtr, button: int, buttons: int, modifiers: int) -> QtMouseEventPtr extern def vqt6_qmouseevent_button(event: QtMouseEventPtr) -> int diff --git a/vqt6/_qtnetwork.vp b/vqt6/_qtnetwork.vp index a4807eb..1ba51a4 100644 --- a/vqt6/_qtnetwork.vp +++ b/vqt6/_qtnetwork.vp @@ -7,7 +7,7 @@ from vqt6._types import * # ============================================================ # ============================================================ -# QHostAddress — IP 地址 +# QHostAddress — IP address # ============================================================ def vqt6_qhostaddress_create() -> QtHostAddressPtr | t.State: pass def vqt6_qhostaddress_create_with_ipv4(ipv4: uint32) -> QtHostAddressPtr | t.State: pass @@ -26,9 +26,9 @@ def vqt6_qhostaddress_free(addr: QtHostAddressPtr) -> t.State: pass # ============================================================ -# QAbstractSocket — TCP/UDP 套接字基类 +# QAbstractSocket — TCP/UDP socket base class # ============================================================ -# 套接字状态 +# Socket state define QT_SOCKET_UNCONNECTED_STATE = 0 define QT_SOCKET_HOST_LOOKUP_STATE = 1 define QT_SOCKET_CONNECTING_STATE = 2 @@ -37,14 +37,14 @@ define QT_SOCKET_BOUND_STATE = 4 define QT_SOCKET_LISTENING_STATE = 5 define QT_SOCKET_CLOSING_STATE = 6 -# 套接字类型 +# Socket type define QT_SOCKET_TCP = 0 define QT_SOCKET_UDP = 1 -# 套接字错误 +# Socket error define QT_SOCKET_NO_ERROR = 0 -# 通用方法 +# Common methods def vqt6_qabstractsocket_state(socket: QtAbstractSocketPtr) -> int | t.State: pass def vqt6_qabstractsocket_error(socket: QtAbstractSocketPtr) -> int | t.State: pass def vqt6_qabstractsocket_error_string(socket: QtAbstractSocketPtr) -> QtStringPtr | t.State: pass @@ -72,7 +72,7 @@ def vqt6_qabstractsocket_local_port(socket: QtAbstractSocketPtr) -> uint16 | t.S # ============================================================ -# QTcpSocket — TCP 套接字 +# QTcpSocket — TCP socket # ============================================================ def vqt6_qtcpsocket_create() -> QtTcpSocketPtr | t.State: pass def vqt6_qtcpsocket_create_with_parent(parent: QtObjectPtr) -> QtTcpSocketPtr | t.State: pass @@ -85,7 +85,7 @@ def vqt6_qtcpsocket_abort(socket: QtTcpSocketPtr) -> t.State: pass # ============================================================ -# QUdpSocket — UDP 套接字 +# QUdpSocket — UDP socket # ============================================================ def vqt6_qudpsocket_create() -> QtUdpSocketPtr | t.State: pass def vqt6_qudpsocket_create_with_parent(parent: QtObjectPtr) -> QtUdpSocketPtr | t.State: pass @@ -101,7 +101,7 @@ def vqt6_qudpsocket_leave_multicast_group(socket: QtUdpSocketPtr, group_address: # ============================================================ -# QNetworkRequest — 网络请求 +# QNetworkRequest — network request # ============================================================ def vqt6_qnetworkrequest_create() -> QtNetworkRequestPtr | t.State: pass def vqt6_qnetworkrequest_create_with_url(url: QtStringPtr) -> QtNetworkRequestPtr | t.State: pass @@ -117,7 +117,7 @@ def vqt6_qnetworkrequest_free(req: QtNetworkRequestPtr) -> t.State: pass # ============================================================ -# QNetworkAccessManager — HTTP 客户端管理器 +# QNetworkAccessManager — HTTP client manager # ============================================================ def vqt6_qnetworkaccessmanager_create() -> QtNetworkAccessManagerPtr | t.State: pass def vqt6_qnetworkaccessmanager_create_with_parent(parent: QtObjectPtr) -> QtNetworkAccessManagerPtr | t.State: pass @@ -130,7 +130,7 @@ def vqt6_qnetworkaccessmanager_set_cookie_store(nam: QtNetworkAccessManagerPtr, # ============================================================ -# QNetworkReply — 网络响应 +# QNetworkReply — network response # ============================================================ def vqt6_qnetworkreply_url(reply: QtNetworkReplyPtr) -> QtStringPtr | t.State: pass def vqt6_qnetworkreply_request(reply: QtNetworkReplyPtr) -> QtNetworkRequestPtr | t.State: pass @@ -150,18 +150,18 @@ def vqt6_qnetworkreply_set_raw_header(reply: QtNetworkReplyPtr, header_name: cha # ============================================================ -# 套接字类型枚举 +# Socket type enums # ============================================================ -# 套接字类型 +# Socket type define QT_ABSTRACT_SOCKET_TCP = 0 define QT_ABSTRACT_SOCKET_UDP = 1 -# 网络层协议 +# Network-layer protocol define QT_NETWORK_IPV4_PROTOCOL = 0 define QT_NETWORK_IPV6_PROTOCOL = 1 define QT_NETWORK_ANY_IP_PROTOCOL = 2 -# 主机地址特殊情况 +# Host address special cases define QT_HOST_ADDRESS_LOCAL_HOST = 0x00000000 define QT_HOST_ADDRESS_LOCAL_HOST_IPV6 = 0x00000001 define QT_HOST_ADDRESS_BROADCAST = 0x00000002 @@ -170,7 +170,7 @@ define QT_HOST_ADDRESS_ANY_IPV6 = 0x00000005 # ============================================================ -# 已知端口常量 +# Well-known port constants # ============================================================ define QT_PORT_FTP = 21 define QT_PORT_SSH = 22 diff --git a/vqt6/_qtwidgets.vp b/vqt6/_qtwidgets.vp index eec690a..5b02f94 100644 --- a/vqt6/_qtwidgets.vp +++ b/vqt6/_qtwidgets.vp @@ -7,9 +7,9 @@ from vqt6._types import * # ============================================================ # ============================================================ -# QApplication — GUI 应用入口 +# QApplication — GUI application entry point # ============================================================ -# argc/argv 通过单独的 buffer 数组传入(Viper 中 C 数组使用指针+长度) +# argc/argv are passed via a separate buffer array (C arrays use pointer+length in Viper) def vqt6_qapplication_create(argc: int, argv: t.CPtr*) -> QtAppPtr | t.State: pass def vqt6_qapplication_instance() -> QtAppPtr | t.State: pass def vqt6_qapplication_exec(app: QtAppPtr) -> int | t.State: pass @@ -26,7 +26,7 @@ def vqt6_enable_custom_item_checks(app: QtAppPtr) -> t.State: pass # ============================================================ -# QWidget — 所有窗口的基类 +# QWidget — base class of all windows # ============================================================ def vqt6_qwidget_create() -> QtWidgetPtr | t.State: pass def vqt6_qwidget_create_with_parent(parent: QtWidgetPtr) -> QtWidgetPtr | t.State: pass @@ -36,7 +36,7 @@ def vqt6_qwidget_close(widget: QtWidgetPtr) -> int | t.State: pass def vqt6_qwidget_is_visible(widget: QtWidgetPtr) -> int | t.State: pass def vqt6_qwidget_set_visible(widget: QtWidgetPtr, visible: int) -> t.State: pass -# 几何 +# Geometry def vqt6_qwidget_set_geometry(widget: QtWidgetPtr, x: int, y: int, w: int, h: int) -> t.State: pass def vqt6_qwidget_geometry(widget: QtWidgetPtr, x: int*, y: int*, w: int*, h: int*) -> t.State: pass def vqt6_qwidget_resize(widget: QtWidgetPtr, w: int, h: int) -> t.State: pass @@ -48,51 +48,51 @@ def vqt6_qwidget_height(widget: QtWidgetPtr) -> int | t.State: pass def vqt6_qwidget_x(widget: QtWidgetPtr) -> int | t.State: pass def vqt6_qwidget_y(widget: QtWidgetPtr) -> int | t.State: pass -# 文本与标题 +# Text and title def vqt6_qwidget_set_window_title(widget: QtWidgetPtr, title: QtStringPtr) -> t.State: pass def vqt6_qwidget_window_title(widget: QtWidgetPtr) -> QtStringPtr | t.State: pass def vqt6_qwidget_set_text(widget: QtWidgetPtr, text: QtStringPtr) -> t.State: pass def vqt6_qwidget_text(widget: QtWidgetPtr) -> QtStringPtr | t.State: pass -# 启用 +# Enabled state def vqt6_qwidget_set_enabled(widget: QtWidgetPtr, enabled: int) -> t.State: pass def vqt6_qwidget_is_enabled(widget: QtWidgetPtr) -> int | t.State: pass -# 父对象 +# Parent def vqt6_qwidget_set_parent(widget: QtWidgetPtr, parent: QtWidgetPtr) -> t.State: pass def vqt6_qwidget_parent_widget(widget: QtWidgetPtr) -> QtWidgetPtr | t.State: pass -# 焦点 +# Focus def vqt6_qwidget_set_focus(widget: QtWidgetPtr) -> t.State: pass def vqt6_qwidget_clear_focus(widget: QtWidgetPtr) -> t.State: pass def vqt6_qwidget_has_focus(widget: QtWidgetPtr) -> int | t.State: pass def vqt6_qwidget_set_focus_policy(widget: QtWidgetPtr, policy: int) -> t.State: pass -# 更新 +# Update def vqt6_qwidget_update(widget: QtWidgetPtr) -> t.State: pass def vqt6_qwidget_repaint(widget: QtWidgetPtr) -> t.State: pass def vqt6_qwidget_set_update_enabled(widget: QtWidgetPtr, enabled: int) -> t.State: pass -# 窗口标志 +# Window flags def vqt6_qwidget_set_window_flags(widget: QtWidgetPtr, flags: int) -> t.State: pass def vqt6_qwidget_window_flags(widget: QtWidgetPtr) -> int | t.State: pass -# 鼠标光标 +# Mouse cursor def vqt6_qwidget_set_cursor(widget: QtWidgetPtr, cursor: QtCursorPtr) -> t.State: pass def vqt6_qwidget_cursor(widget: QtWidgetPtr) -> QtCursorPtr | t.State: pass def vqt6_qwidget_unset_cursor(widget: QtWidgetPtr) -> t.State: pass -# 工具提示 +# Tooltip def vqt6_qwidget_set_tool_tip(widget: QtWidgetPtr, tip: QtStringPtr) -> t.State: pass -# Qt 样式表(QSS):直接传 Viper str +# Qt stylesheet (QSS): pass the Viper str directly def vqt6_qwidget_set_stylesheet_utf8(widget: QtWidgetPtr, qss: char*) -> t.State: pass def vqt6_qwidget_tool_tip(widget: QtWidgetPtr) -> QtStringPtr | t.State: pass -# 状态栏提示 +# Status bar tip def vqt6_qwidget_set_status_tip(widget: QtWidgetPtr, tip: QtStringPtr) -> t.State: pass def vqt6_qwidget_status_tip(widget: QtWidgetPtr) -> QtStringPtr | t.State: pass -# 最小/最大尺寸 +# Minimum/maximum size def vqt6_qwidget_set_minimum_size(widget: QtWidgetPtr, w: int, h: int) -> t.State: pass def vqt6_qwidget_set_maximum_size(widget: QtWidgetPtr, w: int, h: int) -> t.State: pass def vqt6_qwidget_set_fixed_size(widget: QtWidgetPtr, w: int, h: int) -> t.State: pass @@ -101,7 +101,7 @@ def vqt6_qwidget_maximum_size(widget: QtWidgetPtr, w: int*, h: int*) -> t.State: # ============================================================ -# QDialog — 对话框 +# QDialog — dialog # ============================================================ def vqt6_qdialog_create() -> QtDialogPtr | t.State: pass def vqt6_qdialog_create_with_parent(parent: QtWidgetPtr) -> QtDialogPtr | t.State: pass @@ -116,7 +116,7 @@ def vqt6_qdialog_is_modal(dialog: QtDialogPtr) -> int | t.State: pass # ============================================================ -# QMainWindow — 主窗口 +# QMainWindow — main window # ============================================================ def vqt6_qmainwindow_create() -> QtMainWindowPtr | t.State: pass def vqt6_qmainwindow_create_with_parent(parent: QtWidgetPtr) -> QtMainWindowPtr | t.State: pass @@ -131,13 +131,13 @@ def vqt6_qmainwindow_add_tool_bar_break(mw: QtMainWindowPtr) -> t.State: pass # ============================================================ -# QLabel — 标签 +# QLabel — label # ============================================================ def vqt6_qlabel_create() -> QtLabelPtr | t.State: pass def vqt6_qlabel_create_with_text(text: QtStringPtr) -> QtLabelPtr | t.State: pass def vqt6_qlabel_create_with_text_parent(text: QtStringPtr, parent: QtWidgetPtr) -> QtLabelPtr | t.State: pass def vqt6_qlabel_set_text(label: QtLabelPtr, text: QtStringPtr) -> t.State: pass -# UTF-8 便捷版:直接传 Viper str,避免手动创建/释放 QString +# UTF-8 convenience: pass the Viper str directly, no manual QString create/free def vqt6_qlabel_set_text_utf8(label: QtLabelPtr, text: char*) -> t.State: pass def vqt6_qlabel_text(label: QtLabelPtr) -> QtStringPtr | t.State: pass def vqt6_qlabel_set_alignment(label: QtLabelPtr, alignment: int) -> t.State: pass @@ -151,7 +151,7 @@ def vqt6_qlabel_word_wrap(label: QtLabelPtr) -> int | t.State: pass # ============================================================ -# QPushButton — 按钮 +# QPushButton — button # ============================================================ def vqt6_qpushbutton_create() -> QtPushButtonPtr | t.State: pass def vqt6_qpushbutton_create_with_text(text: QtStringPtr) -> QtPushButtonPtr | t.State: pass @@ -172,7 +172,7 @@ def vqt6_qpushbutton_click(btn: QtPushButtonPtr) -> t.State: pass # ============================================================ -# QLineEdit — 单行输入框 +# QLineEdit — single-line input # ============================================================ def vqt6_qlineedit_create() -> QtLineEditPtr | t.State: pass def vqt6_qlineedit_create_with_text(text: QtStringPtr) -> QtLineEditPtr | t.State: pass @@ -192,7 +192,7 @@ def vqt6_qlineedit_clear(le: QtLineEditPtr) -> t.State: pass # ============================================================ -# QTextEdit / QPlainTextEdit — 多行文本编辑器 +# QTextEdit / QPlainTextEdit — multi-line text editor # ============================================================ def vqt6_qtextedit_create() -> QtTextEditPtr | t.State: pass def vqt6_qtextedit_create_with_parent(parent: QtWidgetPtr) -> QtTextEditPtr | t.State: pass @@ -210,7 +210,7 @@ def vqt6_qplaintextedit_create_with_parent(parent: QtWidgetPtr) -> QtPlainTextEd def vqt6_qplaintextedit_to_plain_text(pte: QtPlainTextEditPtr) -> QtStringPtr | t.State: pass def vqt6_qplaintextedit_set_plain_text(pte: QtPlainTextEditPtr, text: QtStringPtr) -> t.State: pass def vqt6_qplaintextedit_append(pte: QtPlainTextEditPtr, text: QtStringPtr) -> t.State: pass -# UTF-8 便捷版:直接传 Viper str,避免手动创建/释放 QString +# UTF-8 convenience: pass the Viper str directly, no manual QString create/free def vqt6_qplaintextedit_append_utf8(pte: QtPlainTextEditPtr, text: char*) -> t.State: pass def vqt6_qplaintextedit_set_plain_text_utf8(pte: QtPlainTextEditPtr, text: char*) -> t.State: pass def vqt6_qplaintextedit_clear(pte: QtPlainTextEditPtr) -> t.State: pass @@ -219,7 +219,7 @@ def vqt6_qplaintextedit_line_count(pte: QtPlainTextEditPtr) -> int | t.State: pa # ============================================================ -# QCheckBox / QRadioButton — 复选/单选 +# QCheckBox / QRadioButton — check/radio # ============================================================ def vqt6_qcheckbox_create() -> QtCheckBoxPtr | t.State: pass def vqt6_qcheckbox_create_with_text(text: QtStringPtr) -> QtCheckBoxPtr | t.State: pass @@ -241,7 +241,7 @@ def vqt6_qradiobutton_text(rb: QtRadioButtonPtr) -> QtStringPtr | t.State: pass # ============================================================ -# QComboBox — 下拉框 +# QComboBox — combo box # ============================================================ def vqt6_qcombobox_create() -> QtComboBoxPtr | t.State: pass def vqt6_qcombobox_create_with_parent(parent: QtWidgetPtr) -> QtComboBoxPtr | t.State: pass @@ -260,7 +260,7 @@ def vqt6_qcombobox_is_editable(cb: QtComboBoxPtr) -> int | t.State: pass # ============================================================ -# QListWidget — 列表控件 +# QListWidget — list widget # ============================================================ def vqt6_qlistwidget_create() -> QtListWidgetPtr | t.State: pass def vqt6_qlistwidget_create_with_parent(parent: QtWidgetPtr) -> QtListWidgetPtr | t.State: pass @@ -276,11 +276,11 @@ def vqt6_qlistwidget_set_current_row(lw: QtListWidgetPtr, row: int) -> t.State: def vqt6_qlistwidget_current_item_text(lw: QtListWidgetPtr) -> QtStringPtr | t.State: pass def vqt6_qlistwidget_item_text(lw: QtListWidgetPtr, row: int) -> QtStringPtr | t.State: pass def vqt6_qlistwidget_set_item_text(lw: QtListWidgetPtr, row: int, text: QtStringPtr) -> t.State: pass -# 聊天气泡行:右/左对齐气泡(setItemWidget 实现);hpad/vpad = QSS padding 左右/上下合计像素 +# Chat bubble row: right/left aligned bubble (via setItemWidget); hpad/vpad = QSS padding h/v total px def vqt6_qlistwidget_add_bubble(lw: QtListWidgetPtr, text: QtStringPtr, qss: QtStringPtr, right_align: int, max_bubble_w: int, hpad: int, vpad: int) -> t.State: pass -# 流式刷新:删除最后一条气泡后按新文本重建 +# Streaming refresh: drop the last bubble and rebuild it from new text def vqt6_qlistwidget_replace_last_bubble(lw: QtListWidgetPtr, text: QtStringPtr, qss: QtStringPtr, right_align: int, max_bubble_w: int, hpad: int, vpad: int) -> t.State: pass @@ -288,7 +288,7 @@ def vqt6_qlistwidget_scroll_to_bottom(lw: QtListWidgetPtr) -> t.State: pass # ============================================================ -# QTreeWidget / QTreeWidgetItem — 树状控件 +# QTreeWidget / QTreeWidgetItem — tree widget # ============================================================ def vqt6_qtreewidget_create() -> QtTreeWidgetPtr | t.State: pass def vqt6_qtreewidget_create_with_parent(parent: QtWidgetPtr) -> QtTreeWidgetPtr | t.State: pass @@ -319,7 +319,7 @@ def vqt6_qtreewidgetitem_data_int(item: QtTreeWidgetItemPtr) -> int | t.State: p # ============================================================ -# QProgressBar — 进度条 +# QProgressBar — progress bar # ============================================================ def vqt6_qprogressbar_create() -> QtProgressBarPtr | t.State: pass def vqt6_qprogressbar_create_with_parent(parent: QtWidgetPtr) -> QtProgressBarPtr | t.State: pass @@ -336,7 +336,7 @@ def vqt6_qprogressbar_reset(pb: QtProgressBarPtr) -> t.State: pass # ============================================================ -# QSlider — 滑块 +# QSlider — slider # ============================================================ def vqt6_qslider_create() -> QtSliderPtr | t.State: pass def vqt6_qslider_create_with_orientation(orientation: int) -> QtSliderPtr | t.State: pass @@ -353,7 +353,7 @@ def vqt6_qslider_set_page_step(s: QtSliderPtr, step: int) -> t.State: pass # ============================================================ -# QSpinBox — 数字输入框 +# QSpinBox — number input # ============================================================ def vqt6_qspinbox_create() -> QtSpinBoxPtr | t.State: pass def vqt6_qspinbox_create_with_parent(parent: QtWidgetPtr) -> QtSpinBoxPtr | t.State: pass @@ -372,7 +372,7 @@ def vqt6_qspinbox_suffix(sb: QtSpinBoxPtr) -> QtStringPtr | t.State: pass # ============================================================ -# 布局 - QLayout / QBoxLayout / QGridLayout / QFormLayout +# Layouts - QLayout / QBoxLayout / QGridLayout / QFormLayout # ============================================================ def vqt6_qlayout_add_widget(layout: QtLayoutPtr, widget: QtWidgetPtr) -> t.State: pass def vqt6_qlayout_remove_widget(layout: QtLayoutPtr, widget: QtWidgetPtr) -> t.State: pass @@ -498,7 +498,7 @@ def vqt6_qtoolbar_orientation(tb: QtToolBarPtr) -> int | t.State: pass # ============================================================ -# QMessageBox — 消息对话框(静态方法) +# QMessageBox — message dialog (static methods) # ============================================================ def vqt6_qmessagebox_information(parent: QtWidgetPtr, title: QtStringPtr, text: QtStringPtr, buttons: int) -> int | t.State: pass def vqt6_qmessagebox_warning(parent: QtWidgetPtr, title: QtStringPtr, text: QtStringPtr, buttons: int) -> int | t.State: pass @@ -509,7 +509,7 @@ def vqt6_qmessagebox_about_qt(parent: QtWidgetPtr) -> t.State: pass # ============================================================ -# QFileDialog — 文件选择对话框(静态方法) +# QFileDialog — file dialog (static methods) # ============================================================ def vqt6_qfiledialog_get_open_file_name(parent: QtWidgetPtr, caption: QtStringPtr, dir: QtStringPtr, filter: QtStringPtr) -> QtStringPtr | t.State: pass def vqt6_qfiledialog_get_save_file_name(parent: QtWidgetPtr, caption: QtStringPtr, dir: QtStringPtr, filter: QtStringPtr) -> QtStringPtr | t.State: pass @@ -518,7 +518,7 @@ def vqt6_qfiledialog_get_open_file_names(parent: QtWidgetPtr, caption: QtStringP # ============================================================ -# QColorDialog / QFontDialog / QInputDialog — 各种对话框 +# QColorDialog / QFontDialog / QInputDialog — assorted dialogs # ============================================================ def vqt6_qcolordialog_get_color(initial: QtColorPtr, parent: QtWidgetPtr, title: QtStringPtr) -> QtColorPtr | t.State: pass def vqt6_qfontdialog_get_font(initial: QtFontPtr, parent: QtWidgetPtr, title: QtStringPtr) -> QtFontPtr | t.State: pass @@ -529,8 +529,8 @@ def vqt6_qinputdialog_get_item(parent: QtWidgetPtr, title: QtStringPtr, label: Q # ============================================================ -# 信号连接(Qt 信号 → Viper 回调) -# 回调统一签名: def cb(slot_id: int, user_data: t.CPtr) +# Signal connections (Qt signal -> Viper callback) +# Unified callback signature: def cb(slot_id: int, user_data: t.CPtr) # ============================================================ def vqt6_qpushbutton_connect_clicked(btn: QtPushButtonPtr, cb: bytes, user_data: bytes) -> t.State: pass def vqt6_qpushbutton_connect_toggled(btn: QtPushButtonPtr, cb: bytes, user_data: bytes) -> t.State: pass @@ -550,8 +550,8 @@ def vqt6_qtreewidget_connect_item_clicked(tw: QtTreeWidgetPtr, cb: bytes, user_d # ============================================================ -# Canvas — 手绘板 / 28x28 灰度显示(自绘控件) -# 坐标约定: 内部 0..1 归一化, 与控件像素尺寸解耦 +# Canvas — freehand board / 28x28 grayscale display (custom-painted widget) +# Coordinate convention: normalized 0..1 internally, decoupled from pixel size # ============================================================ def vqt6_canvas_create() -> QtCanvasPtr | t.State: pass def vqt6_canvas_create_with_parent(parent: QtWidgetPtr) -> QtCanvasPtr | t.State: pass @@ -569,8 +569,8 @@ def vqt6_canvas_connect_changed(canvas: QtCanvasPtr, cb: bytes, user_data: bytes # ============================================================ -# Graph3D — 软件 3D 神经网络结构图(自绘控件) -# API 与渲染后端无关, 后续可替换为 OpenGL 实现 +# Graph3D — software 3D neural-network structure view (custom-painted widget) +# Renderer-backend independent; can later be replaced by an OpenGL implementation # ============================================================ def vqt6_graph3d_create() -> QtGraph3DPtr | t.State: pass def vqt6_graph3d_create_with_parent(parent: QtWidgetPtr) -> QtGraph3DPtr | t.State: pass @@ -593,7 +593,7 @@ def vqt6_graph3d_reset_view(g: QtGraph3DPtr) -> t.State: pass # ============================================================ -# ChartView — Qt Charts 折线图(loss/acc 曲线) +# ChartView — Qt Charts line chart (loss/acc curves) # ============================================================ def vqt6_chartview_create() -> QtChartViewPtr | t.State: pass def vqt6_chartview_create_with_parent(parent: QtWidgetPtr) -> QtChartViewPtr | t.State: pass diff --git a/vqt6/_types.vp b/vqt6/_types.vp index 1c892fa..22e9712 100644 --- a/vqt6/_types.vp +++ b/vqt6/_types.vp @@ -8,8 +8,8 @@ from stdint import * # 不暴露 C++ 类的内部布局,只暴露指针 # ============================================================ -# 不透明指针 typedef(Qt6 C++ 类的 Viper 端表示) -QtObjectPtr: t.CTypedef = t.CPtr # void* 通用不透明指针 +# Opaque pointer typedefs (Viper-side representation of Qt6 C++ classes) +QtObjectPtr: t.CTypedef = t.CPtr # void* generic opaque pointer QtStringPtr: t.CTypedef = t.CPtr # QString* QtByteArrayPtr: t.CTypedef = t.CPtr # QByteArray* QtVariantPtr: t.CTypedef = t.CPtr # QVariant* @@ -80,9 +80,9 @@ QtColorDialogPtr: t.CTypedef = t.CPtr # QColorDialog* QtFontDialogPtr: t.CTypedef = t.CPtr # QFontDialog* QtInputDialogPtr: t.CTypedef = t.CPtr # QInputDialog* -# vqt6 自绘控件(_bridge_canvas) -QtCanvasPtr: t.CTypedef = t.CPtr # Vqt6Canvas*(QWidget* 兼容) -QtGraph3DPtr: t.CTypedef = t.CPtr # Vqt6Graph3D*(QWidget* 兼容) +# vqt6 custom-painted widgets (_bridge_canvas) +QtCanvasPtr: t.CTypedef = t.CPtr # Vqt6Canvas* (QWidget*-compatible) +QtGraph3DPtr: t.CTypedef = t.CPtr # Vqt6Graph3D* (QWidget*-compatible) QtChartViewPtr: t.CTypedef = t.CPtr # QChartView* QtChartSeriesPtr: t.CTypedef = t.CPtr # QLineSeries* @@ -96,10 +96,10 @@ QtNetworkReplyPtr: t.CTypedef = t.CPtr # QNetworkReply* QtNetworkRequestPtr: t.CTypedef = t.CPtr # QNetworkRequest* # ============================================================ -# Qt 通用常量(C++ 枚举值的 C 兼容定义) +# Qt common constants (C-compatible definitions of C++ enum values) # ============================================================ -# Qt 键盘按键 (Qt::Key) +# Qt key codes (Qt::Key) define QT_KEY_ESCAPE = 0x01000000 define QT_KEY_TAB = 0x01000001 define QT_KEY_BACKTAB = 0x01000002 @@ -131,7 +131,7 @@ define QT_KEY_Z = 0x5a define QT_KEY_0 = 0x30 define QT_KEY_9 = 0x39 -# Qt 鼠标按键 (Qt::MouseButton) +# Qt mouse buttons (Qt::MouseButton) define QT_NO_BUTTON = 0x00000000 define QT_LEFT_BUTTON = 0x00000001 define QT_RIGHT_BUTTON = 0x00000002 @@ -150,7 +150,7 @@ define QT_ALIGN_CENTER = QT_ALIGN_HCENTER | QT_ALIGN_VCENTER define QT_HORIZONTAL = 0x1 define QT_VERTICAL = 0x2 -# Qt 焦点 +# Qt focus define QT_NO_FOCUS = 0x00 define QT_TAB_FOCUS = 0x01 define QT_CLICK_FOCUS = 0x02 @@ -174,14 +174,14 @@ define QT_WINDOW_MIN_MAX_BUTTONS = 0x00000004 define QT_WINDOW_CLOSE_BUTTON = 0x08000000 define QT_WINDOW_CONTEXT_HELP_BUTTON = 0x00000008 -# QMessageBox 标准按钮 +# QMessageBox standard buttons define QT_OK = 0x00000400 define QT_CANCEL = 0x00400000 define QT_YES = 0x00004000 define QT_NO = 0x00010000 define QT_CLOSE = 0x00200000 -# 事件类型 +# Event types define QT_EVENT_NONE = 0 define QT_EVENT_KEY = 6 define QT_EVENT_MOUSE_BUTTON_PRESS = 2 diff --git a/w32/fileio.vp b/w32/fileio.vp index fcabfe7..ddc1ea4 100644 --- a/w32/fileio.vp +++ b/w32/fileio.vp @@ -38,40 +38,29 @@ define SHARE_WRITE = 0x00000002 define SHARE_DELETE = 0x00000004 # ============================================================================= -# 环境变量展开(路径层唯一实现) +# Environment variable expansion (the only implementation in the path layer). # -# 用途:工程配置里的路径可写成 %temp%/viperepl/.tpv_build 这类形式,需在打开 -# 文件前展开为实路径。ExpandEnvironmentStringsA 由 kernel32 提供,与 -# SetConsoleOutputCP 同属进程启动即可用的 API(无额外初始化时序要求)。 -# -# 安全约束(保证既有路径语义逐字不变): -# * 只在路径含 '%' 时才调用 Win32(无 '%' 的绝大多数路径零开销); -# * 展开失败(返回 0)或结果长度超过缓冲上限(返回所需长度 > 上限,此时 -# 目标缓冲内容不可用)一律返回 None,调用方沿用原路径; -# * 不做任何前缀/分隔符规范化:UNC(\\server\share)、相对路径、盘符、 -# ".." 段、长路径都原样交给 Win32,展开成功时也只替换 %VAR% 片段; -# * A 系列展开结果与 CreateDirectoryA / CreateProcessA / FindFirstFileA 等 -# 既有 A 系列调用点同编码(系统 ANSI 代码页),不会引入编码错配。 -# 返回 stdlib.malloc 的新串(调用方负责 stdlib.free);无需展开或失败返回 None。 +# Expands "%VAR%" in paths before opening a file (Win32 only called when the path contains '%'); returns a stdlib.malloc'd string (caller frees) or None on failure / no expansion. +# UNC, relative, drive, ".." and long paths are passed through unchanged; A-series results share the ANSI code page of existing A-series call sites. # ============================================================================= extern def ExpandEnvironmentStringsA(lpSrc: LPCSTR, lpDst: CHARPTR, nSize: ULONG) -> ULONG -# 展开结果缓冲上限(含 NUL;超过即视为失败并退回原路径) +# Expansion buffer cap (incl. NUL); exceeding it is treated as failure and falls back to the original path define EXPAND_ENV_CAP = 4096 def ExpandEnvironmentPath(path: str) -> str: - """路径含 '%' 时按环境变量展开:返回新串(调用方 stdlib.free);无需/失败返回 None""" + """Expand env vars when path contains '%': returns a new string (caller stdlib.free) or None if not needed / on failure""" if path is None: return None - # 快速路径:无 '%' 直接返回 None(不调用 Win32,路径逐字不变) + # Fast path: no '%' -> return None (no Win32 call, path unchanged) if string.strchr(path, '%') is None: return None buf: str = stdlib.malloc(EXPAND_ENV_CAP) if buf is None: return None n: ULONG = ExpandEnvironmentStringsA(path, buf, ULONG(EXPAND_ENV_CAP)) - # n == 0:失败;n > 上限:结果所需的 TCHAR 数(含 NUL)超过缓冲,内容不可用 + # n == 0: failure; n > cap: required TCHAR count (incl. NUL) exceeds buffer, content unusable if n == 0 or int(n) > EXPAND_ENV_CAP: stdlib.free(buf) return None @@ -80,11 +69,8 @@ def ExpandEnvironmentPath(path: str) -> str: def create_file_utf8(path: str, access: ULONG, share: ULONG, disposition: ULONG, flags: ULONG) -> w32.win32base.HANDLE: - """按路径编码打开/创建文件。 - 含 '%' 的路径先做环境变量展开(ExpandEnvironmentPath,失败则原样使用); - 合法 UTF-8(源码字面量、Qt 返回的中文目录) → 转 UTF-16 走 CreateFileW; - 非法 UTF-8(如命令行 argv 的 ANSI 串) → 退回 CreateFileA。详见 win32base""" - # 展开结果的生命周期覆盖到 CreateFile 调用之后(句柄由内核持有,串用完即弃) + """Open/create a file with path-encoding fallback: '%' paths are expanded first (original on failure); valid UTF-8 -> UTF-16 CreateFileW, invalid UTF-8 (ANSI argv) -> CreateFileA. See win32base.""" + # Expanded string must live past the CreateFile call (kernel holds the handle; the string is discarded after) ep: str = ExpandEnvironmentPath(path) use_path: str = path if ep is not None: @@ -96,8 +82,8 @@ def create_file_utf8(path: str, access: ULONG, share: ULONG, disposition: ULONG, else: wp: w32.win32base.WCHARPTR = w32.win32base.utf8_to_utf16_dup(use_path) if wp is None: - # 宽字符缓冲分配失败: 走 A 路径, 非 ASCII 路径在此自然失败并返回 - # INVALID_HANDLE_VALUE(不掩盖错误, 只是复用同一失败出口) + # Wide-char buffer alloc failed: fall back to A path; non-ASCII paths fail naturally and return + # INVALID_HANDLE_VALUE (error not masked, just the same failure exit). h = w32.win32file.CreateFileA(use_path, access, share, None, disposition, flags, None) else: @@ -255,9 +241,8 @@ class File: bytes_read = 0 result: BOOL = w32.win32file.ReadFile(self.handle, buf + total, 1, t.CPtr(&bytes_read), None) if result == 0 or bytes_read == 0: break - # 明确字节读:下标访问按指针元素类型(i8)load 1 字节(Repl.vp - # _read_line 同款)。不用 c.Deref(buf + total)——后者的宽度取决于 - # 指针类型是否为 i8*,此处语义明确要求 1 字节。 + # Explicit byte read: indexing loads 1 byte by the pointer element type (i8), same as Repl.vp _read_line. + # Do not use c.Deref(buf + total): its width depends on whether the pointer is i8*, but 1 byte is required here. ch = buf[total] total += 1 if ch == 10: break @@ -266,14 +251,8 @@ class File: return LONG(total) def read_all(self, buf: bytes, buf_size: ULONG) -> LONG: - """把整个文件读进 buf 并写成 C 字符串(内部先 seek 到 0) - - 契约(注意与 write 的 count 口径不同): - buf_size 是缓冲区**容量, 含终止符** —— 最多读 buf_size-1 字节, - 再在 buf[返回值] 写一个 NUL 字节, 故返回值 <= buf_size-1。 - 文件比 buf_size-1 更长时**静默截断**, 不报错; 要区分"读完了"还是 - "被截断了", 请在调用前自己比一次 f.size()。 - 返回实际读到的字节数, 负数为错误码。""" + """Read the whole file into buf as a C string (seeks to 0 first); returns bytes read, negative on error. + buf_size is capacity incl. terminator: reads at most buf_size-1 bytes then writes a NUL at buf[ret]. A file longer than buf_size-1 is silently truncated; compare f.size() first to detect truncation.""" if self.closed: return FRESULT.ERR_CLOSED if not self.can_read: return FRESULT.ERR_PERM if buf_size < 2: return FRESULT.ERR @@ -439,9 +418,8 @@ class FileW: bytes_read = 0 result: BOOL = w32.win32file.ReadFile(self.handle, buf + total, 1, t.CPtr(&bytes_read), None) if result == 0 or bytes_read == 0: break - # 明确字节读:下标访问按指针元素类型(i8)load 1 字节(Repl.vp - # _read_line 同款)。不用 c.Deref(buf + total)——后者的宽度取决于 - # 指针类型是否为 i8*,此处语义明确要求 1 字节。 + # Explicit byte read: indexing loads 1 byte by the pointer element type (i8), same as Repl.vp _read_line. + # Do not use c.Deref(buf + total): its width depends on whether the pointer is i8*, but 1 byte is required here. ch = buf[total] total += 1 if ch == 10: break @@ -450,14 +428,8 @@ class FileW: return LONG(total) def read_all(self, buf: bytes, buf_size: ULONG) -> LONG: - """把整个文件读进 buf 并写成 C 字符串(内部先 seek 到 0) - - 契约(注意与 write 的 count 口径不同): - buf_size 是缓冲区**容量, 含终止符** —— 最多读 buf_size-1 字节, - 再在 buf[返回值] 写一个 NUL 字节, 故返回值 <= buf_size-1。 - 文件比 buf_size-1 更长时**静默截断**, 不报错; 要区分"读完了"还是 - "被截断了", 请在调用前自己比一次 f.size()。 - 返回实际读到的字节数, 负数为错误码。""" + """Read the whole file into buf as a C string (seeks to 0 first); returns bytes read, negative on error. + buf_size is capacity incl. terminator: reads at most buf_size-1 bytes then writes a NUL at buf[ret]. A file longer than buf_size-1 is silently truncated; compare f.size() first to detect truncation.""" if self.closed: return FRESULT.ERR_CLOSED if not self.can_read: return FRESULT.ERR_PERM if buf_size < 2: return FRESULT.ERR diff --git a/w32/win32base.vp b/w32/win32base.vp index c2251a8..64df56f 100644 --- a/w32/win32base.vp +++ b/w32/win32base.vp @@ -82,11 +82,8 @@ extern def GetCommandLineA() -> CHARPTR # ============================================================================= -# 路径编码: Viper 字符串统一 UTF-8, 而 Windows 的 A 系列 API 走系统 ANSI 代码页 -# (中文机器上为 GBK)。直接拿 UTF-8 路径调 CreateFileA / CreateDirectoryA, -# 遇到中文目录名会被按 GBK 误读 → 误报"文件/目录不存在"。故路径统一转 UTF-16 -# 走 W 系列 API; 仅当字节串不是合法 UTF-8(如命令行 argv 传来的 ANSI 串)时才 -# 退回 A 系列, 保持既有行为。 +# Path encoding: Viper strings are UTF-8 but Windows A APIs use the ANSI code page, so non-ASCII UTF-8 paths would be misread; convert paths to UTF-16 and use W APIs. +# Fall back to A APIs only when the bytes are not valid UTF-8 (e.g. ANSI argv), preserving existing behavior. # ============================================================================= define UTF8_CODEPAGE = 65001 define MB_ERR_INVALID_CHARS = 0x00000008 @@ -97,7 +94,7 @@ extern def MultiByteToWideChar(CodePage: UINT, dwFlags: DWORD, lpMultiByteStr: L def utf8_is_valid(s: str) -> int: - """整串(含结尾 NUL)是否为合法 UTF-8。1=是(按 UTF-8 处理), 0=否(按 ANSI)""" + """Whether the whole string (incl. trailing NUL) is valid UTF-8. 1=yes (treat as UTF-8), 0=no (ANSI).""" if s is None: return 0 n: int = MultiByteToWideChar(UTF8_CODEPAGE, MB_ERR_INVALID_CHARS, s, -1, @@ -108,8 +105,7 @@ def utf8_is_valid(s: str) -> int: def utf8_to_utf16_dup(s: str) -> WCHARPTR: - """UTF-8 → 新分配的 UTF-16 缓冲(含结尾 NUL), 调用方负责 stdlib.free; - 失败(含非法 UTF-8)返回 None""" + """UTF-8 -> newly allocated UTF-16 buffer (incl. trailing NUL); caller must stdlib.free; returns None on failure (incl. invalid UTF-8).""" if s is None: return None n: int = MultiByteToWideChar(UTF8_CODEPAGE, 0, s, -1, t.CPtr(0), 0) diff --git a/w32/win32console.vp b/w32/win32console.vp index d428fb3..347790c 100644 --- a/w32/win32console.vp +++ b/w32/win32console.vp @@ -32,12 +32,12 @@ define WINDOW_BUFFER_SIZE_EVENT = 0x0004 define MENU_EVENT = 0x0008 define FOCUS_EVENT = 0x0010 -# 控制事件类型(SetConsoleCtrlHandler 处理例程的入参 dwCtrlType;consoleapi.h) +# Control event types (dwCtrlType arg of the SetConsoleCtrlHandler routine; consoleapi.h) define CTRL_C_EVENT = 0# Ctrl+C define CTRL_BREAK_EVENT = 1# Ctrl+Break -define CTRL_CLOSE_EVENT = 2# 关闭控制台窗口 -define CTRL_LOGOFF_EVENT = 5# 用户注销 -define CTRL_SHUTDOWN_EVENT = 6# 系统关机 +define CTRL_CLOSE_EVENT = 2# console window closed +define CTRL_LOGOFF_EVENT = 5# user logoff +define CTRL_SHUTDOWN_EVENT = 6# system shutdown class COORD(): X: SHORT @@ -117,10 +117,6 @@ extern def FreeConsole() -> BOOL extern def SetConsoleWindowInfo(hConsoleOutput: HANDLE, bAbsolute: BOOL, lpConsoleWindow: SMALL_RECT*) -> BOOL extern def ScrollConsoleScreenBufferA(hConsoleOutput: HANDLE, lpScrollRectangle: SMALL_RECT*, lpClipRectangle: SMALL_RECT*, dwDestinationOrigin: COORD, lpFill: CHAR_INFO*) -> BOOL -# 控制事件处理例程注册/摘除(consoleapi.h:114-115 / kernel32.dll,本仓由 -lkernel32 链接) -# BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add); -# typedef BOOL (WINAPI *PHANDLER_ROUTINE)(DWORD dwCtrlType); -# 第一个形参按本仓"函数指针用 t.CPtr 传"的写法声明:实参直接写处理例程函数名, -# 由 TPV 把函数引用 bitcast 成函数指针(先例:VET/App/lib/VGuard.vp:319/896 同签名)。 -# 返回 0 = 失败(GetLastError 取原因);Add=FALSE 时摘除已注册的例程。 +# SetConsoleCtrlHandler registration/removal (consoleapi.h / kernel32.dll): pass the routine name directly, bitcast by TPV (function pointers declared as t.CPtr). +# Returns 0 on failure (see GetLastError); Add=FALSE removes the registered routine. extern def SetConsoleCtrlHandler(HandlerRoutine: t.CPtr, Add: BOOL) -> BOOL diff --git a/w32/win32dbghelp.vp b/w32/win32dbghelp.vp index f46f84c..fcfece8 100644 --- a/w32/win32dbghelp.vp +++ b/w32/win32dbghelp.vp @@ -32,16 +32,13 @@ from w32.win32base import * # 三者只用于传给 StackWalk64(它按自身定义写入),故尺寸/偏移必须与 C 侧逐字节一致。 # ============================================================================= -# IMAGE_FILE_MACHINE_* (winnt.h;x64 = 0x8664) +# IMAGE_FILE_MACHINE_* (winnt.h; x64 = 0x8664) define IMAGE_FILE_MACHINE_AMD64 = 0x8664 -# ADDRESS_MODE(dbghelp.h):AddrMode1616=0 / AddrMode1632=1 / AddrModeReal=2 / AddrModeFlat=3 +# ADDRESS_MODE (dbghelp.h): AddrMode1616=0 / AddrMode1632=1 / AddrModeReal=2 / AddrModeFlat=3 define ADDR_MODE_FLAT = 3 -# SYMOPT_*(dbghelp.h)——只列本仓用到的四项: -# DEFERRED_LOADS:SymInitialize(fInvadeProcess=TRUE) 时**只枚举模块、不加载符号/PDB** -# (模块枚举是 StackWalk64 的必要前提;符号本身本模块不需要); -# UNDNAME:修饰名解码为可读名(本模块只取地址,仅为了不改变 dbghelp 的默认语义); -# NO_PROMPTS:禁止 dbghelp 弹交互式对话框(否则缺符号时可能卡住进程启动)。 +# SYMOPT_* (dbghelp.h), only the three used here: +# DEFERRED_LOADS enumerates modules without loading symbols/PDB; UNDNAME keeps default name decoding; NO_PROMPTS forbids interactive dialogs (missing symbols can otherwise hang startup). define SYMOPT_UNDNAME = 0x00000002 define SYMOPT_DEFERRED_LOADS = 0x00000004 define SYMOPT_NO_PROMPTS = 0x00080000 @@ -49,9 +46,9 @@ define SYMOPT_NO_PROMPTS = 0x00080000 # typedef struct _ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; } ADDRESS64; class ADDRESS64(): - Offset: ULONGLONG # 0x00 地址(PC/帧/栈指针) - Segment: WORD # 0x08 段选择子(x64 恒 0) - Mode: DWORD # 0x0C 地址模式(ADDR_MODE_FLAT=3;enum 按 i32 占位) + Offset: ULONGLONG # 0x00 address (PC/frame/stack pointer) + Segment: WORD # 0x08 segment selector (always 0 on x64) + Mode: DWORD # 0x0C address mode (ADDR_MODE_FLAT=3; enum occupies i32) # typedef struct _KDHELP64 {...} KDHELP64; @@ -70,52 +67,43 @@ class KDHELP64(): BuildVersion: ULONGLONG # 0x48 Reserved0: ULONGLONG # 0x50 Reserved1: ULONGLONG # 0x58 - # 合计 0x60 = 96 字节 + # total 0x60 = 96 bytes # typedef struct _STACKFRAME64 {...} STACKFRAME64; -# 调用约定:调用方只需填 AddrPC.Offset/AddrFrame.Offset/AddrStack.Offset + 三者 Mode=3, -# 其余字段由 StackWalk64 内部维护(第一次调用前应整块清零)。 -# ⚠ 本结构**没有**任何"结构体尺寸/字段数"成员(NumFieldsInStruct 是堆栈遍历的 -# STACKFRAME_EX / StackWalkEx 才有的概念)。StackWalk64 按自己的 dbghelp.h 定义 -# 读写本结构,调用方无需、也无法填尺寸;故本仓只需保证 sizeof 与 C 侧一致 -# (x64 实测 sizeof(STACKFRAME64) = 0xF8 = 248 字节,见下方字段偏移)。 +# Caller must set AddrPC/AddrFrame/AddrStack .Offset plus Mode=3 on all three; other fields are maintained by StackWalk64 (zero the whole struct before the first call). +# This struct has no NumFieldsInStruct member (that is STACKFRAME_EX/StackWalkEx only); StackWalk64 reads/writes it by its own dbghelp.h, so only sizeof must match C (x64: 0xF8 = 248 bytes). class STACKFRAME64(): - AddrPC: ADDRESS64 # 0x000 当前指令指针 - AddrReturn: ADDRESS64 # 0x010 返回地址 - AddrFrame: ADDRESS64 # 0x020 帧指针(x64 = RBP) - AddrStack: ADDRESS64 # 0x030 栈指针(x64 = RSP) - AddrBStore: ADDRESS64 # 0x040 (x86 用;x64 恒 0) + AddrPC: ADDRESS64 # 0x000 current instruction pointer + AddrReturn: ADDRESS64 # 0x010 return address + AddrFrame: ADDRESS64 # 0x020 frame pointer (RBP on x64) + AddrStack: ADDRESS64 # 0x030 stack pointer (RSP on x64) + AddrBStore: ADDRESS64 # 0x040 (x86 only; always 0 on x64) FuncTableEntry: VOIDPTR # 0x050 Params: t.CArray[uint64, 4] # 0x058 Far: BOOL # 0x078 Virtual: BOOL # 0x07C Reserved: t.CArray[uint64, 3] # 0x080 KdHelp: KDHELP64 # 0x098 - # 合计 0xF8 = 248 字节 + # total 0xF8 = 248 bytes # ============================================================================= -# dbghelp.dll 导出(dbghelp.h / dbghelp.lib) +# dbghelp.dll exports (dbghelp.h / dbghelp.lib) # ============================================================================= # DWORD SymSetOptions(DWORD SymOptions); -# 必须在 SymInitialize **之前**调用(调用后初始化即固定该选项集)。 +# Must be called before SymInitialize (the option set is fixed at initialization). extern def SymSetOptions(SymOptions: DWORD) -> DWORD # BOOL SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess); -# fInvadeProcess=TRUE:枚举进程已加载模块并登记进 dbghelp 的模块表。**这是 -# StackWalk64 的硬前提**——fInvadeProcess=FALSE 时模块表为空,SymFunctionTableAccess64 -# 查不到 RUNTIME_FUNCTION(返回 NULL)、SymGetModuleBase64 返回 0,StackWalk64 会 -# 首次调用即失败、一帧都走不出。本模块不需要 PDB(只取返回地址),配合 -# SYMOPT_DEFERRED_LOADS 时枚举模块不会真的去加载任何符号文件。 +# fInvadeProcess=TRUE enumerates loaded modules into dbghelp's module table; this is required by StackWalk64. +# With fInvadeProcess=FALSE the table is empty: SymFunctionTableAccess64 returns NULL and StackWalk64 fails on the first frame. PDBs are not needed; with SYMOPT_DEFERRED_LOADS module enumeration loads no symbol files. extern def SymInitialize(hProcess: HANDLE, UserSearchPath: LPCSTR, fInvadeProcess: BOOL) -> BOOL # BOOL SymRefreshModuleList(HANDLE hProcess); -# DbgHelp 的进程上下文是**进程级**状态、按句柄唯一:同一句柄重复 SymInitialize 会被拒 -# (本仓实测 ASan 运行时先注册了自己的符号化器,故本模块的 SymInitialize 失败)。这不是 -# 冲突——状态是共享的,用同一个 GetCurrentProcess() 伪句柄调用本函数即可按其既有上下文 -# 重新枚举模块表,使 StackWalk64 拿到最新的 RUNTIME_FUNCTION 表。用法见 traceback.vp 的 Install。 +# DbgHelp's process context is process-wide and per-handle: calling SymInitialize twice on the same handle is rejected (e.g. the ASan runtime already registered a symbolizer). +# State is shared, so re-enumerate the module table via this function using the GetCurrentProcess() pseudo-handle; see traceback.vp Install. extern def SymRefreshModuleList(hProcess: HANDLE) -> BOOL # PVOID SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase); @@ -130,9 +118,8 @@ extern def SymGetModuleBase64(hProcess: HANDLE, AddrBase: ULONGLONG) -> ULONGLON # PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, # PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, # PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); -# 三个回调按本仓"函数指针用 t.CPtr 传"的写法声明:实参直接写函数名(ReadProcessMemory / -# SymFunctionTableAccess64 / SymGetModuleBase64),由 TPV bitcast 成函数指针; -# TranslateAddress 传 None(0)——x64 不需要地址翻译例程。 +# The three callbacks follow the "function pointers passed as t.CPtr" convention: pass the function name (ReadProcessMemory / SymFunctionTableAccess64 / SymGetModuleBase64), bitcast by TPV. +# TranslateAddress is None (0): x64 needs no address-translation routine. extern def StackWalk64(MachineType: DWORD, hProcess: HANDLE, hThread: HANDLE, StackFrame: STACKFRAME64*, ContextRecord: VOIDPTR, ReadMemoryRoutine: t.CPtr, FunctionTableAccessRoutine: t.CPtr, diff --git a/w32/win32memory.vp b/w32/win32memory.vp index 0e5df95..3979913 100644 --- a/w32/win32memory.vp +++ b/w32/win32memory.vp @@ -63,11 +63,6 @@ extern def GlobalSize(hMem: VOIDPTR) -> sizet extern def LocalAlloc(uFlags: UINT, dwBytes: sizet) -> VOIDPTR extern def LocalFree(hMem: VOIDPTR) -> VOIDPTR -# 跨进程/本进程读内存(memoryapi.h / kernel32.dll) -# BOOL ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, -# SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); -# 该导出的签名恰为 dbghelp 的 PREAD_PROCESS_MEMORY_ROUTINE64:StackWalk64 的回调槽 -# 要求填这个函数(includes/traceback.vp 在 Install 时用 GetProcAddress 取其地址传入, -# 见 includes/w32/win32dbghelp.vp 的 StackWalk64)。 -# 返回 0 = 失败(GetLastError 取原因)。 +# Read memory (memoryapi.h / kernel32.dll): BOOL ReadProcessMemory(HANDLE, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead). +# Its signature matches dbghelp's PREAD_PROCESS_MEMORY_ROUTINE64 (the StackWalk64 callback slot); returns 0 on failure (see GetLastError). extern def ReadProcessMemory(hProcess: HANDLE, lpBaseAddress: t.CConst | VOIDPTR, lpBuffer: VOIDPTR, nSize: sizet, lpNumberOfBytesRead: sizet*) -> BOOL diff --git a/w32/win32process.vp b/w32/win32process.vp index 3d86124..77b1a7b 100644 --- a/w32/win32process.vp +++ b/w32/win32process.vp @@ -112,39 +112,8 @@ extern def TlsGetValue(dwTlsIndex: ULONG) -> VOIDPTR extern def TlsSetValue(dwTlsIndex: ULONG, lpTlsValue: VOIDPTR) -> BOOL # ============================================================================= -# x86-64 CONTEXT(线程上下文;winnt.h 的 DECLSPEC_ALIGN(16) CONTEXT) -# -# 用途:SuspendThread 主线程后用 GetThreadContext 取 RIP/RSP/RBP,供栈回溯。 -# 字段顺序与偏移**逐字段**对齐 winnt.h 的 AMD64 定义(偏移见每行注释);字段数 -# 不能裁剪——GetThreadContext(..., CONTEXT_FULL) 会把整块(含 FltSave/向量寄存器) -# 写回调用方缓冲,缓冲小于 sizeof(CONTEXT)=0x4D0=1232 即写穿堆。同理 -# dbghelp!StackWalk64 每走一帧都会把它自己那份 CONTEXT(同为 0x4D0)写回传入的 -# ContextRecord —— 本结构原先漏了 0x4B0/0x4B8 的 LastBranchToRip/LastBranchFromRip, -# 于是尺寸只有 0x4C0,被 ASan 抓成 "heap-buffer-overflow ... WRITE of size 1232", -# 故这两个字段**必须**保留。 -# -# 布局核对(x64,非 packed,自然对齐): -# 0x000 P1Home..P6Home 6×u64 -# 0x030 ContextFlags, MxCsr 2×u32 -# 0x038 SegCs..SegSs 6×u16 -# 0x044 EFlags 1×u32 -# 0x048 Dr0..Dr3,Dr6,Dr7 6×u64 -# 0x078 Rax..R15 16×u64 -# 0x0F8 Rip 1×u64 -# 0x100 FltSave (XMM_SAVE_AREA32) 512 字节 -# 0x300 VectorRegister[26] (M128A) 416 字节 -# 0x4A0 VectorControl 1×u64 -# 0x4A8 DebugControl 1×u64 -# 0x4B0 LastBranchToRip 1×u64 -# 0x4B8 LastBranchFromRip 1×u64 -# 0x4C0 LastExceptionToRip 1×u64 -# 0x4C8 LastExceptionFromRip 1×u64 -# 合计 0x4D0 = 1232 字节(16 的倍数;结构自身对齐要求 16,见下方调用方约定) -# -# FltSave / VectorRegister 只需占位保证偏移与总长:本模块不读其内部字段, -# 故用定长字节数组表示(起始偏移 0x100 / 0x300 本身即 16 的倍数,不引入对齐偏差)。 -# 调用方约定:GetThreadContext 要求 lpContext 16 字节对齐 —— 本仓由 -# stdlib.malloc/calloc 分配(x64 CRT 保证 16 字节对齐),不要用栈上局部变量。 +# x86-64 CONTEXT (winnt.h DECLSPEC_ALIGN(16)); after SuspendThread, GetThreadContext reads RIP/RSP/RBP for stack walking. +# Fields/offsets must match winnt.h AMD64 exactly and must not be trimmed: GetThreadContext(CONTEXT_FULL) and StackWalk64 write sizeof(CONTEXT)=0x4D0=1232 bytes, so LastBranchToRip/LastBranchFromRip (0x4B0/0x4B8) are required. Allocate 16-byte aligned (stdlib.malloc/calloc), never a stack local. # ============================================================================= define CONTEXT_AMD64 = 0x00100000 define CONTEXT_CONTROL = 0x00100001 @@ -202,23 +171,11 @@ class CONTEXT(): LastExceptionToRip: ULONGLONG # 0x4C0 LastExceptionFromRip: ULONGLONG # 0x4C8 -# 取线程上下文(processthreadsapi.h)。hThread 需带 THREAD_GET_CONTEXT 权限; -# ContextFlags 必须先写入 CONTEXT_AMD64 | ...(x64;本仓用 CONTEXT_FULL)。 -# 返回 0 = 失败(GetLastError 取原因)。 +# Get thread context (processthreadsapi.h). hThread needs THREAD_GET_CONTEXT; ContextFlags must be set to CONTEXT_AMD64 | ... first (x64; CONTEXT_FULL here). +# Returns 0 on failure (see GetLastError). extern def GetThreadContext(hThread: HANDLE, lpContext: CONTEXT*) -> BOOL # LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER); -# 注册进程级「未处理异常」过滤器(errhandlingapi.h,kernel32)。 -# 回调签名:LONG CALLBACK Filter(struct _EXCEPTION_POINTERS *) -# struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; // +0x00 -# PCONTEXT ContextRecord; // +0x08 } -# struct _EXCEPTION_RECORD { DWORD ExceptionCode; // +0x00 -# DWORD ExceptionFlags; // +0x04 -# struct _EXCEPTION_RECORD* Nested; // +0x08 -# PVOID ExceptionAddress; // +0x10 -# ... } // 共 0x98 字节 -# 回调在**出错线程**上运行;返回 EXCEPTION_CONTINUE_SEARCH(0) = 交还系统默认处理(不吞异常)。 -# 第一个形参按本仓「函数指针用 t.CPtr 传」声明(先例同 SetConsoleCtrlHandler);返回值是 -# **上一个**过滤器(首次注册时为 NULL),故本仓不检查返回值。用法见 includes/traceback.vp -# 的 Install / DumpCrashThread(崩溃栈转储)。 +# SetUnhandledExceptionFilter registers a process-level unhandled-exception filter (errhandlingapi.h/kernel32); the callback runs on the faulting thread and returns EXCEPTION_CONTINUE_SEARCH(0) to use the default handler (does not swallow). +# Callback: LONG CALLBACK Filter(_EXCEPTION_POINTERS*): ExceptionRecord@0x00, ContextRecord@0x08 (EXCEPTION_RECORD=0x98). First arg declared as t.CPtr; return ignored (previous filter). See traceback.vp Install/DumpCrashThread. extern def SetUnhandledExceptionFilter(TopLevelExceptionFilter: t.CPtr) -> t.CPtr diff --git a/w32/win32sync.vp b/w32/win32sync.vp index fc50484..34402a0 100644 --- a/w32/win32sync.vp +++ b/w32/win32sync.vp @@ -39,10 +39,8 @@ extern def WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: ULONG) -> ULONG extern def WaitForSingleObjectEx(hHandle: HANDLE, dwMilliseconds: ULONG, bAlertable: BOOL) -> ULONG extern def WaitForMultipleObjects(nCount: ULONG, lpHandles: VOIDPTR, bWaitAll: BOOL, dwMilliseconds: ULONG) -> ULONG extern def WaitForMultipleObjectsEx(nCount: ULONG, lpHandles: VOIDPTR, bWaitAll: BOOL, dwMilliseconds: ULONG, bAlertable: BOOL) -> ULONG -# SRWLOCK / CONDITION_VARIABLE 在 Windows 头文件里都是"单个 PVOID 的不透明结构" -# (sizeof == 8),API 收的是**存储地址**(PSRWLOCK / PCONDITION_VARIABLE)。 -# 故必须建模成结构体:若声明成 VOIDPTR,`c.Addr(self.lock)` 会得到 i8**(多一层), -# 与形参 i8* 层数不符(指针隐式转换收紧后直接报 CastError)。 +# SRWLOCK / CONDITION_VARIABLE are opaque single-PVOID structs (sizeof == 8) and the APIs take their storage address. Model them as structs. +# Declaring them as VOIDPTR would make c.Addr(self.lock) yield i8** (one level too many) and fail with a CastError. class SRWLOCK: Ptr: VOIDPTR @@ -55,7 +53,7 @@ extern def AcquireSRWLockShared(SRWLock: SRWLOCK*) -> VOID extern def ReleaseSRWLockExclusive(SRWLock: SRWLOCK*) -> VOID extern def ReleaseSRWLockShared(SRWLock: SRWLOCK*) -> VOID -# 条件变量 (Condition Variables) - Vista+ +# Condition variables - Vista+ define CONDITION_VARIABLE_LOCKMODE_SHARED = 0x1 extern def InitializeConditionVariable(ConditionVariable: CONDITION_VARIABLE*) -> VOID @@ -64,7 +62,7 @@ extern def SleepConditionVariableSRW(ConditionVariable: CONDITION_VARIABLE*, SRW extern def WakeConditionVariable(ConditionVariable: CONDITION_VARIABLE*) -> VOID extern def WakeAllConditionVariable(ConditionVariable: CONDITION_VARIABLE*) -> VOID -# 一次性初始化 (One-Time Initialization) - Vista+ +# One-time initialization - Vista+ define INIT_ONCE_STATIC_INIT = 0x00000001 define INIT_ONCE_CHECK_ONLY = 0x00000002 define INIT_ONCE_ASYNC = 0x00000004 diff --git a/w32/winsock2.vp b/w32/winsock2.vp index 940be40..74c40a0 100644 --- a/w32/winsock2.vp +++ b/w32/winsock2.vp @@ -7,46 +7,46 @@ from w32.win32base import * # w32/winsock2.py — WinSock2 API 声明 (ws2_32.dll) # ============================================================ -# 版本常量 +# Version constants define WINSOCK_VERSION = 0x0202# 2.2 -# 地址族 +# Address families define AF_INET = 2 define AF_INET6 = 23 -# 套接字类型 +# Socket types define SOCK_STREAM = 1# TCP define SOCK_DGRAM = 2# UDP -define SOCK_RAW = 3# 原始套接字 +define SOCK_RAW = 3# raw socket -# 协议 +# Protocols define IPPROTO_TCP = 6 define IPPROTO_UDP = 17 -# 选项级别 -define SOL_SOCKET = 0xFFFF# WinSock2 值 -define SO_RCVTIMEO = 0x1006# WinSock2 值 -define SO_SNDTIMEO = 0x1005# WinSock2 值 -define SO_REUSEADDR = 0x0004# WinSock2 值 +# Option levels +define SOL_SOCKET = 0xFFFF# WinSock2 value +define SO_RCVTIMEO = 0x1006# WinSock2 value +define SO_SNDTIMEO = 0x1005# WinSock2 value +define SO_REUSEADDR = 0x0004# WinSock2 value -# 特殊值 +# Special values define INADDR_ANY = 0 define SOCKET_ERROR = -1 define INVALID_SOCKET = 0xFFFFFFFF# WinSock2: ~0 -# 发送/接收标志 -define MSG_NOSIGNAL = 0# Windows 不支持,设为 0 +# Send/receive flags +define MSG_NOSIGNAL = 0# Unsupported on Windows, set to 0 -# 关闭方式 +# Shutdown modes define SD_SEND = 1 define SD_RECV = 0 define SD_BOTH = 2 # ============================================================ -# 结构体 +# Structures # ============================================================ -# struct sockaddr_in — 16 字节 +# struct sockaddr_in -- 16 bytes class WSASocketAddr: family: u16 # sin_family port: u16 # sin_port @@ -54,7 +54,7 @@ class WSASocketAddr: zero: u64 # sin_zero -# struct WSADATA — 400 字节 +# struct WSADATA -- 400 bytes class WSAData: wVersion: WORD # @ 0 wHighVersion: WORD # @ 2 @@ -62,10 +62,10 @@ class WSAData: szSystemStatus: BYTE # @ 261 (129 bytes + padding) iMaxSockets: u16 # @ 390 iMaxUdpDg: u16 # @ 392 - lpVendorInfo: CHARPTR # @ 394 (实际上指针是 8 字节对齐) + lpVendorInfo: CHARPTR # @ 394 (pointer is 8-byte aligned) -# struct hostent — 自然对齐: h_name(8) + h_aliases(8) + h_addrtype(2) + h_length(2) + auto-pad(4) + h_addr_list(8) = 32 +# struct hostent -- natural alignment: h_name(8) + h_aliases(8) + h_addrtype(2) + h_length(2) + auto-pad(4) + h_addr_list(8) = 32 class WSAHostEnt: h_name: CHARPTR h_aliases: CHARPTR @@ -76,12 +76,12 @@ class WSAHostEnt: # struct timeval class WinTimeVal: - tv_sec: LONG # 秒 - tv_usec: LONG # 微秒 + tv_sec: LONG # seconds + tv_usec: LONG # microseconds # ============================================================ -# WinSock2 API 函数 +# WinSock2 API functions # ============================================================ extern def WSAStartup(wVersionRequested: WORD, lpWSAData: WSAData*) -> INT diff --git a/zipfile.vp b/zipfile.vp index 343dd0e..374e842 100644 --- a/zipfile.vp +++ b/zipfile.vp @@ -204,19 +204,18 @@ def zf_read_whole(path: str, out_len: sizet*) -> uint8*: # ============================================================ -# 条目信息(目录项) -# 注意: 节点为显式 calloc 分配 -# 由 ZipFile 负责释放——避免 GC 实例经 Ptr 字段引用被回收。 +# Entry info (central directory item). Nodes are explicit calloc allocations, owned and +# freed by ZipFile (avoids GC reclaiming instances referenced via Ptr fields). # ============================================================ class ZipInfo: - filename: bytes # 原始字节名(含 \0,zipfile 持有并负责释放) + filename: bytes # raw byte name (incl \0; owned and freed by zipfile) name_len: sizet compress_type: int # ZIP_STORED / ZIP_DEFLATED crc32: uint32 compress_size: sizet file_size: sizet - header_offset: sizet # 本地文件头偏移 - next: ZipInfo* # 链表(保持写入/目录顺序) + header_offset: sizet # local file header offset + next: ZipInfo* # linked list (keeps write/directory order) # ============================================================ @@ -224,22 +223,22 @@ class ZipInfo: # ============================================================ class ZipFile: path: bytes - mode: int = -1 # 0 = 读,1 = 写 - compression: int # write() 未显式指定时的压缩类型 - fh: wb.HANDLE = None # 写模式句柄(裸 win32 CreateFileA) - data: bytes = None # 读模式:整个归档的内存副本 + mode: int = -1 # 0 = read, 1 = write + compression: int # compression used when write() does not specify one + fh: wb.HANDLE = None # write-mode handle (raw win32 CreateFileA) + data: bytes = None # read mode: in-memory copy of whole archive data_len: sizet = 0 info_head: ZipInfo* = None info_tail: ZipInfo* = None info_count: sizet = 0 - pos: sizet = 0 # 写模式当前偏移 - ok: int = 0 # 1 = 可正常使用 + pos: sizet = 0 # write-mode current offset + ok: int = 0 # 1 = usable closed: int = 0 def __init__(self, path: str, mode: str = "r", compression: int = ZIP_DEFLATED): self.path = path self.compression = compression - # 注意: 字符串必须用 strcmp 比较(== 只会比较指针) + # Note: strings must be compared with strcmp (== compares pointers only). if string.strcmp(mode, "r") == 0: self.mode = 0 elif string.strcmp(mode, "w") == 0: self.mode = 1 else: @@ -298,14 +297,14 @@ class ZipFile: self.info_count += 1 return node - # ---- 读模式:解析中央目录 ---- + # ---- Read mode: parse central directory ---- def _parse(self) -> int: if self.data_len < 22: zipfile_set_error(ZIP_ERR_NOT_ZIP, "file too small to be a zip") return ZIP_ERR_NOT_ZIP d: bytes = self.data - # 从尾部回扫 EOCD(容忍尾部注释) + # Scan back from tail for EOCD (tolerates trailing comment) max_back: sizet = self.data_len - 22 if max_back > _ZIP_MAX_COMMENT + 20: max_back = _ZIP_MAX_COMMENT + 20 @@ -371,7 +370,7 @@ class ZipFile: cp = cp + rec_len return ZIP_ERR_OK - # ---- 公共查询 ---- + # ---- Public queries ---- def namelist_at(self, i: sizet) -> bytes: if self.ok == 0 or self.info_count == 0 or i >= self.info_count: return None @@ -394,7 +393,7 @@ class ZipFile: n = n.next return None - # ---- 读成员:解压 + CRC32 校验 ---- + # ---- Read member: decompress + CRC32 check ---- def read(self, name: str, out_len: sizet*) -> bytes: if not out_len: zipfile_set_error(ZIP_ERR_INVALID, "out_len is None") @@ -463,7 +462,7 @@ class ZipFile: return None return out - # ---- 写成员 ---- + # ---- Write member ---- def write(self, arcname: str, data: bytes, data_len: sizet, compress_type: int = -1) -> int: if self.closed or self.ok == 0: @@ -549,7 +548,7 @@ class ZipFile: node.header_offset = hoff return ZIP_ERR_OK - # ---- 写模式收尾:中央目录 + EOCD ---- + # ---- Write-mode finish: central directory + EOCD ---- def _finish_write(self) -> int: if self.info_count > _ZIP_MAX_COMMENT: zipfile_set_error(ZIP_ERR_UNSUPPORTED, "too many entries (>65535)") diff --git a/zlib/pyzlib.vp b/zlib/pyzlib.vp index 3a62d4f..8054722 100644 --- a/zlib/pyzlib.vp +++ b/zlib/pyzlib.vp @@ -140,7 +140,7 @@ class Compress: if not out_len: set_error(Z_STREAM_ERROR, "out_len is None") return None - s: rawptr[zdeflate.zdeflate_stream] = self.stream # 隐式类型转换 + s: rawptr[zdeflate.zdeflate_stream] = self.stream # implicit type conversion if mode == Z_FINISH: if not self.header_written: self.header_written = 1 @@ -231,7 +231,7 @@ class Compress: set_error(Z_MEM_ERROR, "out of memory") return None copy_obj.pool = self.pool - s: rawptr[zdeflate.zdeflate_stream] = self.stream # 隐式类型转换 + s: rawptr[zdeflate.zdeflate_stream] = self.stream # implicit type conversion copy_obj.stream = s.copy() if not copy_obj.stream: if self.pool == None: @@ -274,7 +274,7 @@ class Compress: def delete(self): if self.is_initialized and self.stream: - s: rawptr[zdeflate.zdeflate_stream] = self.stream # 隐式类型转换 + s: rawptr[zdeflate.zdeflate_stream] = self.stream # implicit type conversion s.destroy() if self.pool == None: stdlib.free(self.zdict) @@ -482,10 +482,7 @@ def clone_bytes(pool: rawptr[memhub.MemManager], src: BYTEPTR, length: sizet) -> if dst: string.memcpy(dst, src, length) return dst -# buf 是"指针的指针"(i8**):不要写成 `BYTE | t.CPtr[t.CPtr]`——联合里嵌 -# `t.CPtr[...]` 下标时声明侧(StubGen/VpiReader)不认作 CPtr 标记,会坍缩成 -# 首段 BYTE = i8,与定义侧 i8** 不一致(跨模块调用即 ABI 错位,见 -# includes/zlib/zinflate.vp decompress 处说明)。改用"指针别名 | t.CPtr"。 +# buf is a "pointer to pointer" (i8**). Do NOT write `BYTE | t.CPtr[t.CPtr]`: with an embedded `t.CPtr[...]` subscript the declare side (StubGen/VpiReader) does not recognize it as a CPtr marker and collapses it to BYTE = i8, mismatching the definition side's i8** (cross-module ABI mismatch; see includes/zlib/zinflate.vp decompress). Use "pointer alias | t.CPtr" instead. def append_bytes(pool: rawptr[memhub.MemManager], buf: BYTEPTR*, length: sizet*, cap: sizet*, data: BYTEPTR, data_len: sizet) -> int: if not data or data_len == 0: return 0 diff --git a/zlib/zdeflate.vp b/zlib/zdeflate.vp index 11d8921..c086474 100644 --- a/zlib/zdeflate.vp +++ b/zlib/zdeflate.vp @@ -52,9 +52,7 @@ class zdeflate_stream: return best_len if (best_len >= zdef.ZDEFLATE_MIN_MATCH) else 0 def update_hash(self, data: BYTE*, pos: sizet, data_len: sizet): - # 只在还能作为匹配起点的位置维护 hash(pos+2 >= data_len 的尾部 - # 字节不会再被 find_match 引用,且 zdeflate_hash 会读 pos..pos+2, - # 越过末尾则越界——见 ASan heap-buffer-overflow) + # Only maintain the hash at positions that can still start a match: tail bytes with pos+2 >= data_len are never referenced by find_match, and zdeflate_hash reads pos..pos+2, so going past the end overflows (see ASan heap-buffer-overflow). if pos + 2 >= data_len: return h: uint = zdeflate_hash(data + pos) self.hash_prev[pos & zdef.ZDEFLATE_WINDOW_MASK] = self.hash_head[h] @@ -165,8 +163,7 @@ class zdeflate_stream: def write_stored_block(self, data: UINT8PTR, length: sizet, final: int): self.writer.stored_block(data, length, final) - # out 是"指针的指针"(i8**):见 zinflate.vp decompress 处的说明—— - # 联合里嵌 `t.CPtr[...]` 会让声明侧(VpiReader)坍缩成 i8,与定义侧 i8** 不一致。 + # out is a "pointer to pointer" (i8**): see zinflate.vp decompress -- an embedded `t.CPtr[...]` collapses the declare side (VpiReader) to i8, mismatching the definition side's i8**. def compress(self, data: UINT8PTR, length: sizet, out: BYTEPTR*, out_len: sizet*) -> int: if self.is_finished: return -2 @@ -184,7 +181,7 @@ class zdeflate_stream: string.memcpy(*out, self.writer.buf, *out_len) return 0 - # out 同 compress:i8**(声明侧/定义侧必须一致)。 + # out same as compress: i8** (declare side and definition side must match). def flush(self, mode: int, out: BYTEPTR*, out_len: sizet*) -> int: if mode == 4: if not self.is_finished: @@ -335,8 +332,7 @@ def zdeflate_count_cl_freqs(all_lengths: INTPTR, total: int, cl_freqs: INTPTR): cl_freqs[16] += 1 run -= count i += count - # 剩余的 1~2 个相同长度值不静默跳过,交由外层循环 - # 按字面量(符号 val)逐个统计,保证总覆盖数恰为 total + # Remaining 1~2 equal-length values are not silently skipped; the outer loop counts them literally (symbol val), so the covered total is exactly total def zdeflate_write_cl_encoded(all_lengths: INTPTR, total: int, w: rawptr[zdef.zbit_writer], cl_tree: rawptr[zhuff.zhuff_tree]): @@ -375,8 +371,7 @@ def zdeflate_write_cl_encoded(all_lengths: INTPTR, total: int, w.write_bits(count - 3, 2) run -= count i += count - # 剩余的 1~2 个相同长度值不静默跳过,交由外层循环 - # 按字面量(符号 val)逐个编码,保证 RLE 覆盖总数恰为 total + # Remaining 1~2 equal-length values are not silently skipped; the outer loop encodes them literally (symbol val), so RLE coverage totals exactly total diff --git a/zlib/zinflate.vp b/zlib/zinflate.vp index 2fc9cae..ba6418c 100644 --- a/zlib/zinflate.vp +++ b/zlib/zinflate.vp @@ -69,7 +69,7 @@ class zinflate_stream: self.is_gzip = 1 if self.input_pos + 10 > self.input_len: return -3 self.input_pos += 10 - # FLG 位于 gzip 头第 4 字节(偏移 3),即 self.input_pos - 7 + # FLG is at byte 4 of the gzip header (offset 3), i.e. self.input_pos - 7 flg: BYTE = self.input_data[self.input_pos - 7] if flg & 0x04: if self.input_pos + 2 > self.input_len: return -3 @@ -375,14 +375,10 @@ class zinflate_stream: self.input_pos += 4 return 0 - # out 是"指针的指针"(i8**)。不要写成 `t.CUInt8T | t.CPtr[t.CPtr]`: - # 联合里嵌 `t.CPtr[...]` 下标时,定义侧按 PtrCount 累加得 i8**,而 stub/declare - # 侧(StubGen/VpiReader)只把整段等于 `t.CPtr`/`CPtr` 的段认作 CPtr 标记, - # 于是坍缩成首段 `t.CUInt8T` = i8 → 跨模块 ABI 不一致(调用方只传 1 字节, - # 被调用方按 8 字节指针写入 → ASan heap-buffer-overflow)。 - # 改用"指针别名 | t.CPtr":两侧都按"追加 1 层指针"解析,同为 i8**。 - # 同形先例:includes/w32/win32sync.vp:65 `Context: VOIDPTR | t.CPtr`、 - # includes/posix.vp:199 `argv: CHARPTR | t.CPtr`。 + # out is a "pointer to pointer" (i8**). Do NOT write `t.CUInt8T | t.CPtr[t.CPtr]`: with an embedded `t.CPtr[...]` subscript the definition side accumulates PtrCount to i8**, but the stub/declare side (StubGen/VpiReader) only recognizes a segment exactly equal to `t.CPtr`/`CPtr` as the CPtr marker, collapsing it to t.CUInt8T = i8 -> cross-module ABI mismatch (caller passes 1 byte, callee writes an 8-byte pointer -> ASan heap-buffer-overflow). + # Use "pointer alias | t.CPtr" instead: both sides append one pointer level and resolve to i8**. + # Same-shaped precedents: includes/w32/win32sync.vp:65 `Context: VOIDPTR | t.CPtr`, + # includes/posix.vp:199 `argv: CHARPTR | t.CPtr`. def decompress(self, data: UINT8PTR, length: sizet, max_length: sizet, out: BYTEPTR*, out_len: sizet*) -> int: if not self.is_initialized: return -2