From 785e82551895dfe755dbecc9d579a4f9f59583f2 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 16 Sep 2026 16:07:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=E4=BA=86=20t.CVoid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _dict.vp | 34 +- _list.vp | 2 +- argparse.vp | 4 +- asm.vp | 4 +- ast/__init__.vp | 238 +- ast/astaux.vp | 674 ++--- ast/base.vp | 1020 +++---- ast/exprs.vp | 2578 +++++++++--------- ast/lexer.vp | 1688 ++++++------ ast/match.vp | 726 ++--- ast/parser.vp | 5804 ++++++++++++++++++++-------------------- ast/stmts.vp | 2314 ++++++++-------- ast/tokens.vp | 60 +- atom.vp | 12 +- builtins.vp | 116 +- coro.vp | 2 +- hashlib/__md5.vp | 4 +- hashlib/__sha1.vp | 6 +- hashlib/__sha256.vp | 4 +- hashlib/__sha512.vp | 2 +- hashtable.vp | 30 +- json/__init__.vp | 474 ++-- json/__parser.vp | 582 ++-- json/__writer.vp | 238 +- llvmlite/__builder.vp | 3736 +++++++++++++------------- llvmlite/__function.vp | 2840 ++++++++++---------- llvmlite/__init__.vp | 922 +++---- llvmlite/__module.vp | 1550 +++++------ llvmlite/__types.vp | 1560 +++++------ llvmlite/__values.vp | 468 ++-- llvmlite/__verify.vp | 860 +++--- memhub.vp | 207 +- openssl/types.vp | 82 +- posix.vp | 4 +- requests/__init__.vp | 2 +- requests/download.vp | 10 +- requests/https.vp | 2 +- socket.vp | 18 +- stdint.vp | 2 +- string.vp | 8 +- sys.vp | 4 +- torch/__init__.vp | 1850 ++++++------- vector.vp | 8 +- viperlib.vp | 2 - vipersimd.vp | 62 +- vqt6/_qtcore.vp | 6 +- vqt6/_qtgui.vp | 320 +-- vqt6/_qtnetwork.vp | 26 +- vqt6/_types.vp | 150 +- w32/winsock2.vp | 6 +- 50 files changed, 15657 insertions(+), 15664 deletions(-) diff --git a/_dict.vp b/_dict.vp index b8002e2..0f856f7 100644 --- a/_dict.vp +++ b/_dict.vp @@ -114,7 +114,7 @@ class dict: def set_int(self, key: str, val: int): """Store integer value (VARIANT_INT). Value is copied to persistent storage within __mbuddy__.""" storage: t.CPtr = self.__mbuddy__.alloc(8) - int_ptr: int | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) + int_ptr: int | t.CPtr = t.CPtr(t.CUInt64T(storage)) int_ptr[0] = val self._set_variant_raw(key, VARIANT_INT, storage) @@ -125,14 +125,14 @@ class dict: def set_float(self, key: str, val: float): """Store float value (VARIANT_FLOAT). Value is copied to persistent storage within __mbuddy__.""" storage: t.CPtr = self.__mbuddy__.alloc(8) - float_ptr: float | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) + float_ptr: float | t.CPtr = t.CPtr(t.CUInt64T(storage)) float_ptr[0] = val self._set_variant_raw(key, VARIANT_FLOAT, storage) def set_double(self, key: str, val: t.CDouble): """Store double value (VARIANT_DOUBLE). Value is copied to persistent storage within __mbuddy__.""" storage: t.CPtr = self.__mbuddy__.alloc(8) - dbl_ptr: t.CDouble | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) + dbl_ptr: t.CDouble | t.CPtr = t.CPtr(t.CUInt64T(storage)) dbl_ptr[0] = val self._set_variant_raw(key, VARIANT_DOUBLE, storage) @@ -147,7 +147,7 @@ class dict: def set_bool(self, key: str, val: t.CInt): """Store boolean value (VARIANT_BOOL). Value is copied to persistent storage within __mbuddy__.""" storage: t.CPtr = self.__mbuddy__.alloc(8) - bool_ptr: t.CInt | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) + bool_ptr: t.CInt | t.CPtr = t.CPtr(t.CUInt64T(storage)) bool_ptr[0] = val self._set_variant_raw(key, VARIANT_BOOL, storage) @@ -160,7 +160,7 @@ class dict: idx: t.CSizeT = self._find(key) if idx >= self.__count__: return VARIANT_NONE - vtype_ptr: t.CInt | t.CPtr = t.CVoid(t.CUInt64T(self.__values__) + idx * _VARIANT_SIZE, t.CPtr) + vtype_ptr: t.CInt | t.CPtr = t.CPtr(t.CUInt64T(self.__values__) + idx * _VARIANT_SIZE) return vtype_ptr[0] def get(self, key: str, default: t.CPtr) -> t.CPtr: @@ -168,7 +168,7 @@ class dict: idx: t.CSizeT = self._find(key) if idx >= self.__count__: return default - val_ptr: str | t.CPtr = t.CVoid(t.CUInt64T(self.__values__) + idx * _VARIANT_SIZE + 8, t.CPtr) + val_ptr: str | t.CPtr = t.CPtr(t.CUInt64T(self.__values__) + idx * _VARIANT_SIZE + 8) return val_ptr[0] def contains(self, key: str) -> t.CInt: @@ -187,7 +187,7 @@ class dict: raise StopIteration idx: t.CSizeT = self.__iter_index__ self.__iter_index__ = idx + 1 - key_ptr: str | t.CPtr = t.CVoid(t.CUInt64T(self.__keys__) + idx * 8, t.CPtr) + key_ptr: str | t.CPtr = t.CPtr(t.CUInt64T(self.__keys__) + idx * 8) return key_ptr[0] # ============================================================ @@ -200,32 +200,32 @@ class dict: obj: json.JsonValue | t.CPtr = json.object(pool) i: t.CSizeT = 0 while i < self.__count__: - key_ptr: str | t.CPtr = t.CVoid(t.CUInt64T(self.__keys__) + i * 8, t.CPtr) - vtype_ptr: t.CInt | t.CPtr = t.CVoid(t.CUInt64T(self.__values__) + i * _VARIANT_SIZE, t.CPtr) - val_ptr: str | t.CPtr = t.CVoid(t.CUInt64T(self.__values__) + i * _VARIANT_SIZE + 8, t.CPtr) + key_ptr: str | t.CPtr = t.CPtr(t.CUInt64T(self.__keys__) + i * 8) + vtype_ptr: t.CInt | t.CPtr = t.CPtr(t.CUInt64T(self.__values__) + i * _VARIANT_SIZE) + val_ptr: str | t.CPtr = t.CPtr(t.CUInt64T(self.__values__) + i * _VARIANT_SIZE + 8) vt: t.CInt = vtype_ptr[0] vp: t.CPtr = val_ptr[0] jv: json.JsonValue | t.CPtr = json.null(pool) if jv != None: if vt == VARIANT_INT: jv.vtype = 2 # JSON_INT - ip: int | t.CPtr = t.CVoid(t.CUInt64T(vp), t.CPtr) + ip: int | t.CPtr = t.CPtr(t.CUInt64T(vp)) jv.int_val = ip[0] elif vt == VARIANT_STR: jv.vtype = 4 # JSON_STRING jv.str_val = vp elif vt == VARIANT_BOOL: jv.vtype = 1 # JSON_BOOL - bp: t.CInt | t.CPtr = t.CVoid(t.CUInt64T(vp), t.CPtr) + bp: t.CInt | t.CPtr = t.CPtr(t.CUInt64T(vp)) jv.bool_val = bp[0] elif vt == VARIANT_FLOAT: jv.vtype = 3 # JSON_FLOAT - fp: float | t.CPtr = t.CVoid(t.CUInt64T(vp), t.CPtr) + fp: float | t.CPtr = t.CPtr(t.CUInt64T(vp)) jv.float_val = fp[0] elif vt == VARIANT_PTR: # Fallback: treat raw pointer as integer jv.vtype = 2 # JSON_INT - ip: int | t.CPtr = t.CVoid(t.CUInt64T(vp), t.CPtr) + ip: int | t.CPtr = t.CPtr(t.CUInt64T(vp)) jv.int_val = ip[0] json.object_set(pool, obj, key_ptr[0], jv) i += 1 @@ -243,19 +243,19 @@ class dict: if cur.key != None: if cur.vtype == 2: # JSON_INT storage: t.CPtr = self.__mbuddy__.alloc(8) - ip: int | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) + ip: int | t.CPtr = t.CPtr(t.CUInt64T(storage)) ip[0] = cur.int_val self._set_variant_raw(cur.key, VARIANT_INT, storage) elif cur.vtype == 4: # JSON_STRING self._set_variant_raw(cur.key, VARIANT_STR, cur.str_val) elif cur.vtype == 1: # JSON_BOOL storage: t.CPtr = self.__mbuddy__.alloc(8) - bp: t.CInt | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) + bp: t.CInt | t.CPtr = t.CPtr(t.CUInt64T(storage)) bp[0] = cur.bool_val self._set_variant_raw(cur.key, VARIANT_BOOL, storage) elif cur.vtype == 3: # JSON_FLOAT storage: t.CPtr = self.__mbuddy__.alloc(8) - fp: float | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) + fp: float | t.CPtr = t.CPtr(t.CUInt64T(storage)) fp[0] = cur.float_val self._set_variant_raw(cur.key, VARIANT_FLOAT, storage) cur = cur.next diff --git a/_list.vp b/_list.vp index 4c38202..19a5aec 100644 --- a/_list.vp +++ b/_list.vp @@ -21,7 +21,7 @@ class list[T]: For pointer types (e.g. AST | t.CPtr), pass elem_size=8 to avoid object slicing: lst: list[AST | t.CPtr] = list[AST | t.CPtr](pool, 8) """ - __data__: t.CVoid | t.CPtr + __data__: t.CPtr __count__: t.CSizeT __capacity__: t.CSizeT __pool__: memhub.MemManager | t.CPtr diff --git a/argparse.vp b/argparse.vp index 1e8bdd7..c9efd7b 100644 --- a/argparse.vp +++ b/argparse.vp @@ -456,5 +456,5 @@ def release(args: ParsedArgs | t.CPtr): ht: hashtable.HashTable | t.CPtr = args._ht if ht.__slots__ is not None: args.__mbuddy__.free(ht.__slots__) - args.__mbuddy__.free(t.CVoid(t.CUInt64T(ht), t.CPtr)) - args.__mbuddy__.free(t.CVoid(t.CUInt64T(args), t.CPtr)) + args.__mbuddy__.free(t.CPtr(t.CUInt64T(ht))) + args.__mbuddy__.free(t.CPtr(t.CUInt64T(args))) diff --git a/asm.vp b/asm.vp index 2b13600..2179d3d 100644 --- a/asm.vp +++ b/asm.vp @@ -74,7 +74,7 @@ def io_wait(): # 的 clobber 列表**不允许声明 RSP**(保留寄存器)⇒ 编译器无从得知 RSP 已被改写, # 若两块之间出现任何编译器生成的栈使用(spill/调用),会覆盖刚压栈的寄存器值。 # 故只可在**编译期不产生任何栈使用**的上下文(如 naked 汇编区)里成对使用。 -def pushall() -> t.CInline | t.CVoid: +inline def pushall(): c.Asm(f""" push rax push rbx @@ -94,7 +94,7 @@ def pushall() -> t.CInline | t.CVoid: [t.ASM_DESCR.CLOBBER_MEMORY] ) -def popall() -> t.CInline | t.CVoid: +inline def popall(): c.Asm(f""" pop r15 pop r14 diff --git a/ast/__init__.vp b/ast/__init__.vp index 05149af..02c9cb9 100644 --- a/ast/__init__.vp +++ b/ast/__init__.vp @@ -1,119 +1,119 @@ -import t, c -from stdint import * -import memhub -import string - -# Import submodule -from .tokens import ( - Token, TokenType, Keyword, TokOp, _init_tables, -) -# Common Section: Base Classes, Enums, Constants, Helper Functions, Dump -from .base import ( - AST, Stmt, ExprNode, ASTKind, ASTCtx, OpKind, ASTFlag, - # Constants - CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR, - FLAG_IS_ASYNC, FLAG_SIMPLE, FLAG_HAS_STAR, - # Helper Functions - _init_ast, _copy_str, _set_pos, _inherit_pos, _set_parent_list, - _binop_from_op, _augop_from_op, _cmpop_from_op, - _emit, _emit_str, _emit_int, _dump_list, - dump, _op_name, -) -# Statement Nodes: Module, Expression, Interactive, FunctionType -from .stmts import ( - # Module Layer - Module, Expression, Interactive, FunctionType, - # Statement - FunctionDef, ClassDef, Return, Delete, Assign, AugAssign, AnnAssign, - For, While, If, With, Raise, Try, Assert, Global, Nonlocal, - Pass, Break, Continue, Expr, Import, ImportFrom, Match, -) -# Expression Nodes: BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set -from .exprs import ( - # Expression Layer - BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set, - ListComp, SetComp, DictComp, GeneratorExp, - Await, Yield, YieldFrom, FormattedValue, JoinedStr, - Constant, NamedExpr, Attribute, Subscript, Starred, Name, - List, Tuple, Slice, - Call, Compare, OpNode, - - name_get_id, constant_get_str_val, - binop_get_left, binop_get_right, - attribute_get_attr, attribute_get_value, -) -# Auxiliary Nodes -from .astaux import ( - ExceptHandler, Arguments, Arg, Keyword, Alias, WithItem, - Comprehension, -) -# Pattern Matching -from .match import ( - MatchCase, MatchValue, MatchSingleton, MatchSequence, - MatchMapping, MatchClass, MatchStar, MatchAs, MatchOr, -) -# from .visitor import ASTVisitor -from .lexer import Lexer, _lexer_init, tokenize, new_lexer -from .parser import Parser, _parser_init, parse_tokens, new_parser -from .parser import _parse_test -from .parser import _dbg_lookup_lit_val, _dbg_lit_found -from .parser import get_parse_err_line, get_parse_err_col, get_parse_err_msg - -# ============================================================ -# Global Memory Pool Pointer (follows project rules: declare _mbuddy) -# User initializes in main: ast._mbuddy = memhub.MemBuddy(arena, size) -# ============================================================ -_mbuddy: rowptr[memhub.MemBuddy] - - -# ============================================================ -# Common API: parse -# ============================================================ - -def parse(src: str, pool: rowptr[memhub.MemManager]) -> rowptr[AST]: - """Parse Python source code, return the AST Module node. - - Parameters: - src: NUL source code string, terminated with NUL - pool: memhub.MemManager instance, to allocate AST nodes - - Returns: - AST Module node, or None - """ - if src is None or pool is None: - return None - _init_tables(pool) - lx: rowptr[Lexer] = new_lexer(pool) - if lx is None: - return None - _lexer_init(lx, src, pool) - tokens: rowptr[Token] = tokenize(lx) - if tokens is None: - return None - tree: rowptr[AST] = parse_tokens(pool, tokens) - return tree - - -def parse_expression(src: str, pool: rowptr[memhub.MemManager]) -> rowptr[Expression]: - """Parse a single expression, return the Expression node. - - Parameters: - src: NUL expression string, terminated with NUL - pool: memhub.MemManager instance, to allocate AST nodes - - Returns: - Expression node (包装节点,body 字段为实际表达式), or None - """ - if src is None or pool is None: - return None - _init_tables(pool) - lx: rowptr[Lexer] = new_lexer(pool) - if lx is None: return None - _lexer_init(lx, src, pool) - tokens: rowptr[Token] = tokenize(lx) - if tokens is None: return None - ps: rowptr[Parser] = new_parser(pool) - if ps is None: return None - _parser_init(ps, tokens, pool) - body: rowptr[AST] = _parse_test(ps) - return Expression(pool, body) +import t, c +from stdint import * +import memhub +import string + +# Import submodule +from .tokens import ( + Token, TokenType, Keyword, TokOp, _init_tables, +) +# Common Section: Base Classes, Enums, Constants, Helper Functions, Dump +from .base import ( + AST, Stmt, ExprNode, ASTKind, ASTCtx, OpKind, ASTFlag, + # Constants + CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR, + FLAG_IS_ASYNC, FLAG_SIMPLE, FLAG_HAS_STAR, + # Helper Functions + _init_ast, _copy_str, _set_pos, _inherit_pos, _set_parent_list, + _binop_from_op, _augop_from_op, _cmpop_from_op, + _emit, _emit_str, _emit_int, _dump_list, + dump, _op_name, +) +# Statement Nodes: Module, Expression, Interactive, FunctionType +from .stmts import ( + # Module Layer + Module, Expression, Interactive, FunctionType, + # Statement + FunctionDef, ClassDef, Return, Delete, Assign, AugAssign, AnnAssign, + For, While, If, With, Raise, Try, Assert, Global, Nonlocal, + Pass, Break, Continue, Expr, Import, ImportFrom, Match, +) +# Expression Nodes: BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set +from .exprs import ( + # Expression Layer + BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set, + ListComp, SetComp, DictComp, GeneratorExp, + Await, Yield, YieldFrom, FormattedValue, JoinedStr, + Constant, NamedExpr, Attribute, Subscript, Starred, Name, + List, Tuple, Slice, + Call, Compare, OpNode, + + name_get_id, constant_get_str_val, + binop_get_left, binop_get_right, + attribute_get_attr, attribute_get_value, +) +# Auxiliary Nodes +from .astaux import ( + ExceptHandler, Arguments, Arg, Keyword, Alias, WithItem, + Comprehension, +) +# Pattern Matching +from .match import ( + MatchCase, MatchValue, MatchSingleton, MatchSequence, + MatchMapping, MatchClass, MatchStar, MatchAs, MatchOr, +) +# from .visitor import ASTVisitor +from .lexer import Lexer, _lexer_init, tokenize, new_lexer +from .parser import Parser, _parser_init, parse_tokens, new_parser +from .parser import _parse_test +from .parser import _dbg_lookup_lit_val, _dbg_lit_found +from .parser import get_parse_err_line, get_parse_err_col, get_parse_err_msg + +# ============================================================ +# Global Memory Pool Pointer (follows project rules: declare _mbuddy) +# User initializes in main: ast._mbuddy = memhub.MemBuddy(arena, size) +# ============================================================ +_mbuddy: rawptr[memhub.MemBuddy] + + +# ============================================================ +# Common API: parse +# ============================================================ + +def parse(src: str, pool: rawptr[memhub.MemManager]) -> rawptr[AST]: + """Parse Python source code, return the AST Module node. + + Parameters: + src: NUL source code string, terminated with NUL + pool: memhub.MemManager instance, to allocate AST nodes + + Returns: + AST Module node, or None + """ + if src is None or pool is None: + return None + _init_tables(pool) + lx: rawptr[Lexer] = new_lexer(pool) + if lx is None: + return None + _lexer_init(lx, src, pool) + tokens: rawptr[Token] = tokenize(lx) + if tokens is None: + return None + tree: rawptr[AST] = parse_tokens(pool, tokens) + return tree + + +def parse_expression(src: str, pool: rawptr[memhub.MemManager]) -> rawptr[Expression]: + """Parse a single expression, return the Expression node. + + Parameters: + src: NUL expression string, terminated with NUL + pool: memhub.MemManager instance, to allocate AST nodes + + Returns: + Expression node (包装节点,body 字段为实际表达式), or None + """ + if src is None or pool is None: + return None + _init_tables(pool) + lx: rawptr[Lexer] = new_lexer(pool) + if lx is None: return None + _lexer_init(lx, src, pool) + tokens: rawptr[Token] = tokenize(lx) + if tokens is None: return None + ps: rawptr[Parser] = new_parser(pool) + if ps is None: return None + _parser_init(ps, tokens, pool) + body: rawptr[AST] = _parse_test(ps) + return Expression(pool, body) diff --git a/ast/astaux.vp b/ast/astaux.vp index 06275a6..62fda68 100644 --- a/ast/astaux.vp +++ b/ast/astaux.vp @@ -1,337 +1,337 @@ -import t, c -from stdint import * -import memhub -import string -from .base import ( - AST, ASTKind, - _init_ast, _copy_str, _set_parent_list, - _emit, _emit_str, _emit_int, _dump_list, -) - - -# ============================================================ -# ExceptHandler Node -# ============================================================ - -class ExceptHandler(AST): - """ExceptHandler(type: AST, name: str)""" - type: rowptr[AST] - name: str - - def __new__(self, pool: rowptr[memhub.MemManager], - type: rowptr[AST], name: str): - ptr: rowptr[ExceptHandler] = pool.alloc(ExceptHandler.__sizeof__()) - if ptr: - string.memset(ptr, 0, ExceptHandler.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - type: rowptr[AST], name: str): - _init_ast(self, pool, 0, 0) - self.type = type - self.name = _copy_str(pool, name) - - def kind(self) -> int: - return ASTKind.ExceptHandler - - def type_name(self) -> str: - return "ExceptHandler" - - def dump(self, buf: bytes, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "ExceptHandler(type=") - if self.type is not None: - pos = self.type.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", name='") - pos = _emit_str(buf, size, pos, self.name) - pos = _emit(buf, size, pos, "', body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Arguments Node -# ============================================================ - -class Arguments(AST): - """Arguments(args: list[AST | CPtr], vararg: AST, kwarg: AST, - defaults: list[AST | CPtr], kw_defaults: list[AST | CPtr])""" - args: rowptr[list[rowptr[AST]]] - vararg: rowptr[AST] - kwarg: rowptr[AST] - defaults: rowptr[list[rowptr[AST]]] - kw_defaults: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - args: rowptr[list[rowptr[AST]]], - vararg: rowptr[AST], kwarg: rowptr[AST], - defaults: rowptr[list[rowptr[AST]]], - kw_defaults: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Arguments] = pool.alloc(Arguments.__sizeof__()) - if ptr: - string.memset(ptr, 0, Arguments.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - args: rowptr[list[rowptr[AST]]], - vararg: rowptr[AST], kwarg: rowptr[AST], - defaults: rowptr[list[rowptr[AST]]], - kw_defaults: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.args = args - self.vararg = vararg - self.kwarg = kwarg - self.defaults = defaults - self.kw_defaults = kw_defaults - _set_parent_list(args, self) - _set_parent_list(defaults, self) - _set_parent_list(kw_defaults, self) - - def kind(self) -> int: - return ASTKind.Arguments - - def type_name(self) -> str: - return "Arguments" - - def dump(self, buf: bytes, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Arguments(args=") - pos = _dump_list(self.args, buf, size, pos) - pos = _emit(buf, size, pos, ", vararg=") - if self.vararg is not None: - pos = self.vararg.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", kwarg=") - if self.kwarg is not None: - pos = self.kwarg.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", defaults=") - pos = _dump_list(self.defaults, buf, size, pos) - pos = _emit(buf, size, pos, ", kw_defaults=") - pos = _dump_list(self.kw_defaults, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Arg Node -# ============================================================ - -class Arg(AST): - """Arg(arg: str, annotation: AST)""" - arg: str - annotation: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - arg: str, annotation: rowptr[AST]): - ptr: rowptr[Arg] = pool.alloc(Arg.__sizeof__()) - if ptr: - string.memset(ptr, 0, Arg.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - arg: str, annotation: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.arg = _copy_str(pool, arg) - self.annotation = annotation - - def kind(self) -> int: - return ASTKind.Arg - - def type_name(self) -> str: - return "Arg" - - def dump(self, buf: bytes, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Arg(arg='") - pos = _emit_str(buf, size, pos, self.arg) - pos = _emit(buf, size, pos, "', annotation=") - if self.annotation is not None: - pos = self.annotation.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Keyword Node -# ============================================================ - -class Keyword(AST): - """Keyword(arg: str, value: AST)""" - arg: str - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - arg: str, value: rowptr[AST]): - ptr: rowptr[Keyword] = pool.alloc(Keyword.__sizeof__()) - if ptr: - string.memset(ptr, 0, Keyword.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - arg: str, value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.arg = _copy_str(pool, arg) - self.value = value - - def kind(self) -> int: - return ASTKind.Keyword - - def type_name(self) -> str: - return "Keyword" - - def dump(self, buf: bytes, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Keyword(arg='") - pos = _emit_str(buf, size, pos, self.arg) - pos = _emit(buf, size, pos, "', value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Alias Node -# ============================================================ - -class Alias(AST): - """Alias(name: str, asname: str)""" - name: str - asname: str - - def __new__(self, pool: rowptr[memhub.MemManager], - name: str, asname: str): - ptr: rowptr[Alias] = pool.alloc(Alias.__sizeof__()) - if ptr: - string.memset(ptr, 0, Alias.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - name: str, asname: str): - _init_ast(self, pool, 0, 0) - self.name = _copy_str(pool, name) - self.asname = _copy_str(pool, asname) - - def kind(self) -> int: - return ASTKind.Alias - - def type_name(self) -> str: - return "Alias" - - def dump(self, buf: bytes, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Alias(name='") - pos = _emit_str(buf, size, pos, self.name) - pos = _emit(buf, size, pos, "', asname='") - pos = _emit_str(buf, size, pos, self.asname) - pos = _emit(buf, size, pos, "')") - return pos - - -# ============================================================ -# WithItem Node -# ============================================================ - -class WithItem(AST): - """WithItem(context_expr: AST, optional_vars: AST)""" - context_expr: rowptr[AST] - optional_vars: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - context_expr: rowptr[AST], optional_vars: rowptr[AST]): - ptr: rowptr[WithItem] = pool.alloc(WithItem.__sizeof__()) - if ptr: - string.memset(ptr, 0, WithItem.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - context_expr: rowptr[AST], optional_vars: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.context_expr = context_expr - self.optional_vars = optional_vars - - def kind(self) -> int: - return ASTKind.WithItem - - def type_name(self) -> str: - return "WithItem" - - def dump(self, buf: bytes, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "WithItem(context_expr=") - if self.context_expr is not None: - pos = self.context_expr.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", optional_vars=") - if self.optional_vars is not None: - pos = self.optional_vars.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Comprehension Node -# ============================================================ - -class Comprehension(AST): - """Comprehension(target: AST, iter: AST, ifs: list[AST | CPtr], is_async: CInt)""" - target: rowptr[AST] - iter: rowptr[AST] - ifs: rowptr[list[rowptr[AST]]] - is_async: int - - def __new__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], iter: rowptr[AST], - ifs: rowptr[list[rowptr[AST]]], is_async: int): - ptr: rowptr[Comprehension] = pool.alloc(Comprehension.__sizeof__()) - if ptr: - string.memset(ptr, 0, Comprehension.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], iter: rowptr[AST], - ifs: rowptr[list[rowptr[AST]]], is_async: int): - _init_ast(self, pool, 0, 0) - self.target = target - self.iter = iter - self.ifs = ifs - self.is_async = is_async - _set_parent_list(ifs, self) - - def kind(self) -> int: - return ASTKind.Comprehension - - def type_name(self) -> str: - return "Comprehension" - - def dump(self, buf: bytes, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Comprehension(target=") - if self.target is not None: - pos = self.target.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", iter=") - if self.iter is not None: - pos = self.iter.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", ifs=") - pos = _dump_list(self.ifs, buf, size, pos) - pos = _emit(buf, size, pos, ", is_async=") - pos = _emit_int(buf, size, pos, int64(self.is_async)) - pos = _emit(buf, size, pos, ")") - return pos +import t, c +from stdint import * +import memhub +import string +from .base import ( + AST, ASTKind, + _init_ast, _copy_str, _set_parent_list, + _emit, _emit_str, _emit_int, _dump_list, +) + + +# ============================================================ +# ExceptHandler Node +# ============================================================ + +class ExceptHandler(AST): + """ExceptHandler(type: AST, name: str)""" + type: rawptr[AST] + name: str + + def __new__(self, pool: rawptr[memhub.MemManager], + type: rawptr[AST], name: str): + ptr: rawptr[ExceptHandler] = pool.alloc(ExceptHandler.__sizeof__()) + if ptr: + string.memset(ptr, 0, ExceptHandler.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + type: rawptr[AST], name: str): + _init_ast(self, pool, 0, 0) + self.type = type + self.name = _copy_str(pool, name) + + def kind(self) -> int: + return ASTKind.ExceptHandler + + def type_name(self) -> str: + return "ExceptHandler" + + def dump(self, buf: bytes, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "ExceptHandler(type=") + if self.type is not None: + pos = self.type.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", name='") + pos = _emit_str(buf, size, pos, self.name) + pos = _emit(buf, size, pos, "', body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Arguments Node +# ============================================================ + +class Arguments(AST): + """Arguments(args: list[AST | CPtr], vararg: AST, kwarg: AST, + defaults: list[AST | CPtr], kw_defaults: list[AST | CPtr])""" + args: rawptr[list[rawptr[AST]]] + vararg: rawptr[AST] + kwarg: rawptr[AST] + defaults: rawptr[list[rawptr[AST]]] + kw_defaults: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + args: rawptr[list[rawptr[AST]]], + vararg: rawptr[AST], kwarg: rawptr[AST], + defaults: rawptr[list[rawptr[AST]]], + kw_defaults: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Arguments] = pool.alloc(Arguments.__sizeof__()) + if ptr: + string.memset(ptr, 0, Arguments.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + args: rawptr[list[rawptr[AST]]], + vararg: rawptr[AST], kwarg: rawptr[AST], + defaults: rawptr[list[rawptr[AST]]], + kw_defaults: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.args = args + self.vararg = vararg + self.kwarg = kwarg + self.defaults = defaults + self.kw_defaults = kw_defaults + _set_parent_list(args, self) + _set_parent_list(defaults, self) + _set_parent_list(kw_defaults, self) + + def kind(self) -> int: + return ASTKind.Arguments + + def type_name(self) -> str: + return "Arguments" + + def dump(self, buf: bytes, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Arguments(args=") + pos = _dump_list(self.args, buf, size, pos) + pos = _emit(buf, size, pos, ", vararg=") + if self.vararg is not None: + pos = self.vararg.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", kwarg=") + if self.kwarg is not None: + pos = self.kwarg.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", defaults=") + pos = _dump_list(self.defaults, buf, size, pos) + pos = _emit(buf, size, pos, ", kw_defaults=") + pos = _dump_list(self.kw_defaults, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Arg Node +# ============================================================ + +class Arg(AST): + """Arg(arg: str, annotation: AST)""" + arg: str + annotation: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + arg: str, annotation: rawptr[AST]): + ptr: rawptr[Arg] = pool.alloc(Arg.__sizeof__()) + if ptr: + string.memset(ptr, 0, Arg.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + arg: str, annotation: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.arg = _copy_str(pool, arg) + self.annotation = annotation + + def kind(self) -> int: + return ASTKind.Arg + + def type_name(self) -> str: + return "Arg" + + def dump(self, buf: bytes, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Arg(arg='") + pos = _emit_str(buf, size, pos, self.arg) + pos = _emit(buf, size, pos, "', annotation=") + if self.annotation is not None: + pos = self.annotation.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Keyword Node +# ============================================================ + +class Keyword(AST): + """Keyword(arg: str, value: AST)""" + arg: str + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + arg: str, value: rawptr[AST]): + ptr: rawptr[Keyword] = pool.alloc(Keyword.__sizeof__()) + if ptr: + string.memset(ptr, 0, Keyword.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + arg: str, value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.arg = _copy_str(pool, arg) + self.value = value + + def kind(self) -> int: + return ASTKind.Keyword + + def type_name(self) -> str: + return "Keyword" + + def dump(self, buf: bytes, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Keyword(arg='") + pos = _emit_str(buf, size, pos, self.arg) + pos = _emit(buf, size, pos, "', value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Alias Node +# ============================================================ + +class Alias(AST): + """Alias(name: str, asname: str)""" + name: str + asname: str + + def __new__(self, pool: rawptr[memhub.MemManager], + name: str, asname: str): + ptr: rawptr[Alias] = pool.alloc(Alias.__sizeof__()) + if ptr: + string.memset(ptr, 0, Alias.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + name: str, asname: str): + _init_ast(self, pool, 0, 0) + self.name = _copy_str(pool, name) + self.asname = _copy_str(pool, asname) + + def kind(self) -> int: + return ASTKind.Alias + + def type_name(self) -> str: + return "Alias" + + def dump(self, buf: bytes, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Alias(name='") + pos = _emit_str(buf, size, pos, self.name) + pos = _emit(buf, size, pos, "', asname='") + pos = _emit_str(buf, size, pos, self.asname) + pos = _emit(buf, size, pos, "')") + return pos + + +# ============================================================ +# WithItem Node +# ============================================================ + +class WithItem(AST): + """WithItem(context_expr: AST, optional_vars: AST)""" + context_expr: rawptr[AST] + optional_vars: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + context_expr: rawptr[AST], optional_vars: rawptr[AST]): + ptr: rawptr[WithItem] = pool.alloc(WithItem.__sizeof__()) + if ptr: + string.memset(ptr, 0, WithItem.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + context_expr: rawptr[AST], optional_vars: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.context_expr = context_expr + self.optional_vars = optional_vars + + def kind(self) -> int: + return ASTKind.WithItem + + def type_name(self) -> str: + return "WithItem" + + def dump(self, buf: bytes, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "WithItem(context_expr=") + if self.context_expr is not None: + pos = self.context_expr.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", optional_vars=") + if self.optional_vars is not None: + pos = self.optional_vars.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Comprehension Node +# ============================================================ + +class Comprehension(AST): + """Comprehension(target: AST, iter: AST, ifs: list[AST | CPtr], is_async: CInt)""" + target: rawptr[AST] + iter: rawptr[AST] + ifs: rawptr[list[rawptr[AST]]] + is_async: int + + def __new__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], iter: rawptr[AST], + ifs: rawptr[list[rawptr[AST]]], is_async: int): + ptr: rawptr[Comprehension] = pool.alloc(Comprehension.__sizeof__()) + if ptr: + string.memset(ptr, 0, Comprehension.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], iter: rawptr[AST], + ifs: rawptr[list[rawptr[AST]]], is_async: int): + _init_ast(self, pool, 0, 0) + self.target = target + self.iter = iter + self.ifs = ifs + self.is_async = is_async + _set_parent_list(ifs, self) + + def kind(self) -> int: + return ASTKind.Comprehension + + def type_name(self) -> str: + return "Comprehension" + + def dump(self, buf: bytes, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Comprehension(target=") + if self.target is not None: + pos = self.target.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", iter=") + if self.iter is not None: + pos = self.iter.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", ifs=") + pos = _dump_list(self.ifs, buf, size, pos) + pos = _emit(buf, size, pos, ", is_async=") + pos = _emit_int(buf, size, pos, int64(self.is_async)) + pos = _emit(buf, size, pos, ")") + return pos diff --git a/ast/base.vp b/ast/base.vp index 0e60d5d..d07e563 100644 --- a/ast/base.vp +++ b/ast/base.vp @@ -1,510 +1,510 @@ -import t, c -from stdint import * -import memhub -import string -import viperlib -from .tokens import TokOp - - -# ============================================================ -# ASTKind - Node type enum (kind() virtual function return value) -# ============================================================ - -class ASTKind(t.CEnum): - # Core 15 nodes - Module: t.State - FunctionDef: t.State - ClassDef: t.State - Assign: t.State - If: t.State - For: t.State - While: t.State - Return: t.State - Expr: t.State - Name: t.State - Constant: t.State - BinOp: t.State - UnaryOp: t.State - Call: t.State - Compare: t.State - # Module Layer - Expression: t.State - Interactive: t.State - FunctionType: t.State - # Statement - Delete: t.State - AugAssign: t.State - AnnAssign: t.State - With: t.State - Raise: t.State - Try: t.State - Assert: t.State - Global: t.State - Nonlocal: t.State - Pass: t.State - Break: t.State - Continue: t.State - Import: t.State - ImportFrom: t.State - Match: t.State - # Expression Layer - BoolOp: t.State - Lambda: t.State - IfExp: t.State - Dict: t.State - Set: t.State - ListComp: t.State - SetComp: t.State - DictComp: t.State - GeneratorExp: t.State - Await: t.State - Yield: t.State - YieldFrom: t.State - FormattedValue: t.State - JoinedStr: t.State - Attribute: t.State - Subscript: t.State - Starred: t.State - List: t.State - Tuple: t.State - Slice: t.State - NamedExpr: t.State - # Auxiliary Nodes - ExceptHandler: t.State - Arguments: t.State - Arg: t.State - Keyword: t.State - Alias: t.State - WithItem: t.State - Comprehension: t.State - OpNode: t.State - # Pattern Matching - MatchCase: t.State - MatchValue: t.State - MatchSingleton: t.State - MatchSequence: t.State - MatchMapping: t.State - MatchClass: t.State - MatchStar: t.State - MatchAs: t.State - MatchOr: t.State - - -# ============================================================ -# ASTCtx - Name context (Load/Store/Del) -# ============================================================ - -class ASTCtx(t.CEnum): - Load: t.State - Store: t.State - Del: t.State - - -# ============================================================ -# OpKind - Operator enum (BinOp/UnaryOp/Compare) -# ============================================================ - -class OpKind(t.CEnum): - # Binary arithmetic operators - Add: t.State - Sub: t.State - Mult: t.State - MatMult: t.State - Div: t.State - Mod: t.State - Pow: t.State - LShift: t.State - RShift: t.State - BitOr: t.State - BitXor: t.State - BitAnd: t.State - FloorDiv: t.State - # Boolean operators - And: t.State - Or: t.State - # Comparison operators - Eq: t.State - Ne: t.State - Lt: t.State - Le: t.State - Gt: t.State - Ge: t.State - Is: t.State - IsNot: t.State - In: t.State - NotIn: t.State - # Unary operators - Not: t.State - UAdd: t.State - USub: t.State - Invert: t.State - Deref: t.State - AddrOf: t.State - # None - NoneOp: t.State - - -# Constant subtype identifiers -CONST_INT: t.CDefine = 1 -CONST_FLOAT: t.CDefine = 2 -CONST_STR: t.CDefine = 3 -CONST_BOOL: t.CDefine = 4 -CONST_NONE: t.CDefine = 5 -CONST_CHAR: t.CDefine = 6 - -# Node flags -FLAG_IS_ASYNC: t.CDefine = 1 -FLAG_SIMPLE: t.CDefine = 2 -FLAG_HAS_STAR: t.CDefine = 4 -FLAG_IS_EXTERN: t.CDefine = 8 -FLAG_IS_EXPORT: t.CDefine = 16 -FLAG_IS_DEFINE: t.CDefine = 32 - - -# ============================================================ -# ASTFlag - Node flags enum -# ============================================================ - -class ASTFlag(t.CEnum): - IsAsync: t.State - Simple: t.State - HasStar: t.State - - -# ============================================================ -# Helper Functions -# ============================================================ - -def _copy_str(pool: rowptr[memhub.MemManager], src: str) -> str: - """Allocate and copy a C string from pool.""" - if src is None: return None - slen: t.CSizeT = string.strlen(src) - buf: str = pool.alloc(slen + 1) - if buf is None: return None - string.memcpy(buf, src, slen + 1) - return buf - - -def _set_pos(node: rowptr[AST], lineno: int, col_offset: int, - end_lineno: int, end_col_offset: int): - if node is None: return - node.lineno = lineno - node.col_offset = col_offset - node.end_lineno = end_lineno - node.end_col_offset = end_col_offset - - -def _inherit_pos(node: rowptr[AST], ref: rowptr[AST]): - if node is None or ref is None: return - node.lineno = ref.lineno - node.col_offset = ref.col_offset - node.end_lineno = ref.end_lineno - node.end_col_offset = ref.end_col_offset - - -def _init_ast(node: rowptr[AST], pool: rowptr[memhub.MemManager], - lineno: int, col: int): - """Initialize AST base fields.""" - node.parent = None - node.pool = pool - node.lineno = lineno - node.col_offset = col - node.end_lineno = lineno - node.end_col_offset = col - node.children = None - - -def _set_parent_list(lst: list[rowptr[AST]] | t.CPtr, parent: rowptr[AST]): - """Set list children parent pointers.""" - if lst is None: return - n: t.CSizeT = lst.__len__() - i: t.CSizeT = 0 - while i < n: - child: rowptr[AST] = lst.get(i) - if child is not None: - child.parent = parent - i += 1 - - -def _append_child(lst: list[rowptr[AST]] | t.CPtr, node: rowptr[AST], - pool: rowptr[memhub.MemManager]): - """Append child nodes to a list (module-level function, to avoid conflicts with the AST.append method name). - - Call this function within the AST.append method to bypass the compiler mistaking list.append for a virtual function call to AST.append when doing type inference.""" - if lst is None or node is None: - return - lst.append(node) - - -# ============================================================ -# Operator Mapping: TokOp -> OpKind -# ============================================================ - -def _binop_from_op(op_id: int) -> int: - if op_id == TokOp.Plus: return OpKind.Add - if op_id == TokOp.Minus: return OpKind.Sub - if op_id == TokOp.Star: return OpKind.Mult - if op_id == TokOp.At: return OpKind.MatMult - if op_id == TokOp.Slash: return OpKind.Div - if op_id == TokOp.Percent: return OpKind.Mod - if op_id == TokOp.StarStar: return OpKind.Pow - if op_id == TokOp.LtLt: return OpKind.LShift - if op_id == TokOp.GtGt: return OpKind.RShift - if op_id == TokOp.VBar: return OpKind.BitOr - if op_id == TokOp.Caret: return OpKind.BitXor - if op_id == TokOp.Amp: return OpKind.BitAnd - if op_id == TokOp.DSlash: return OpKind.FloorDiv - return OpKind.NoneOp - - -def _augop_from_op(op_id: int) -> int: - if op_id == TokOp.PlusEq: return OpKind.Add - if op_id == TokOp.MinusEq: return OpKind.Sub - if op_id == TokOp.StarEq: return OpKind.Mult - if op_id == TokOp.AtEq: return OpKind.MatMult - if op_id == TokOp.SlashEq: return OpKind.Div - if op_id == TokOp.PercentEq: return OpKind.Mod - if op_id == TokOp.StarEqEq: return OpKind.Pow - if op_id == TokOp.LtLtEq: return OpKind.LShift - if op_id == TokOp.GtGtEq: return OpKind.RShift - if op_id == TokOp.VBarEq: return OpKind.BitOr - if op_id == TokOp.CaretEq: return OpKind.BitXor - if op_id == TokOp.AmpEq: return OpKind.BitAnd - if op_id == TokOp.DSlashEq: return OpKind.FloorDiv - return OpKind.NoneOp - - -def _cmpop_from_op(op_id: int) -> int: - if op_id == TokOp.EqEq: return OpKind.Eq - if op_id == TokOp.ExclaimEq: return OpKind.Ne - if op_id == TokOp.Less: return OpKind.Lt - if op_id == TokOp.LessEq: return OpKind.Le - if op_id == TokOp.Greater: return OpKind.Gt - if op_id == TokOp.GreaterEq: return OpKind.Ge - return OpKind.NoneOp - - -# ============================================================ -# dump Helper Functions -# ============================================================ - -def _emit(buf: str, size: t.CSizeT, pos: t.CSizeT, - text: str) -> t.CSizeT: - """Append literal to buf+pos.""" - if buf is None or pos >= size: - return pos - cur: str = str(uint64(buf) + pos) - rem: t.CSizeT = size - pos - n: int = viperlib.snprintf(cur, rem, "%s", text) - if n < 0: - return pos - return pos + t.CSizeT(n) - - -def _emit_str(buf: str, size: t.CSizeT, pos: t.CSizeT, - text: str) -> t.CSizeT: - """Append a string value to buf pos (using %s format)""" - if buf is None or pos >= size: - return pos - cur: str = str(uint64(buf) + pos) - rem: t.CSizeT = size - pos - n: int = viperlib.snprintf(cur, rem, "%s", text) - if n < 0: - return pos - return pos + t.CSizeT(n) - - -def _emit_int(buf: str, size: t.CSizeT, pos: t.CSizeT, - val: int64) -> t.CSizeT: - """Append an integer value to buf pos (using %lld format).""" - if buf is None or pos >= size: - return pos - cur: str = str(uint64(buf) + pos) - rem: t.CSizeT = size - pos - n: int = viperlib.snprintf(cur, rem, "%lld", val) - if n < 0: - return pos - return pos + t.CSizeT(n) - - -def _op_name(op: int) -> str: - """Return the string name of an OpKind.""" - if op == OpKind.Add: return "Add" - if op == OpKind.Sub: return "Sub" - if op == OpKind.Mult: return "Mult" - if op == OpKind.MatMult: return "MatMult" - if op == OpKind.Div: return "Div" - if op == OpKind.Mod: return "Mod" - if op == OpKind.Pow: return "Pow" - if op == OpKind.LShift: return "LShift" - if op == OpKind.RShift: return "RShift" - if op == OpKind.BitOr: return "BitOr" - if op == OpKind.BitXor: return "BitXor" - if op == OpKind.BitAnd: return "BitAnd" - if op == OpKind.FloorDiv: return "FloorDiv" - if op == OpKind.And: return "And" - if op == OpKind.Or: return "Or" - if op == OpKind.Eq: return "Eq" - if op == OpKind.Ne: return "Ne" - if op == OpKind.Lt: return "Lt" - if op == OpKind.Le: return "Le" - if op == OpKind.Gt: return "Gt" - if op == OpKind.Ge: return "Ge" - if op == OpKind.Is: return "Is" - if op == OpKind.IsNot: return "IsNot" - if op == OpKind.In: return "In" - if op == OpKind.NotIn: return "NotIn" - if op == OpKind.Not: return "Not" - if op == OpKind.UAdd: return "UAdd" - if op == OpKind.USub: return "USub" - if op == OpKind.Invert: return "Invert" - if op == OpKind.Deref: return "Deref" - if op == OpKind.AddrOf: return "AddrOf" - return "NoneOp" - - -def _dump_list(lst: list[rowptr[AST]] | t.CPtr, buf: str, - size: t.CSizeT, pos: t.CSizeT) -> t.CSizeT: - """Traverse a list[AST | CPtr] container, polymorphically dump each element.""" - if lst is None: - pos = _emit(buf, size, pos, "[]") - return pos - pos = _emit(buf, size, pos, "[") - n: t.CSizeT = lst.__len__() - i: t.CSizeT = 0 - while i < n: - if i > 0: - pos = _emit(buf, size, pos, ", ") - child: rowptr[AST] = lst.get(i) - if child is not None: - pos = child.dump(buf, size, pos) - i += 1 - pos = _emit(buf, size, pos, "]") - return pos - - -def _dump_op_list(lst: t.CPtr, buf: str, - size: t.CSizeT, pos: t.CSizeT) -> t.CSizeT: - """Traverse a list[CInt] (OpKind values), output a list of op names.""" - if lst is None: - pos = _emit(buf, size, pos, "[]") - return pos - ops_list: rowptr[list[int]] = (rowptr[list[int]])(lst) - pos = _emit(buf, size, pos, "[") - n: t.CSizeT = ops_list.__len__() - i: t.CSizeT = 0 - while i < n: - if i > 0: - pos = _emit(buf, size, pos, ", ") - op: int = ops_list.get(i) - pos = _emit(buf, size, pos, _op_name(op)) - i += 1 - pos = _emit(buf, size, pos, "]") - return pos - - -# ============================================================ -# AST - Polymorphic Base Class - -# @t.CVTable enables vtable, supporting virtual functions like kind()/type_name()/dump()/accept() -# Child nodes are stored in a list[AST | CPtr] container (O(1) random access, more efficient than linked lists) -# The parent field maintains a pointer to the parent (used for upward traversal) -# ============================================================ - -@t.CVTable -class AST: - """AST node base class. All specific node classes inherit from this. - - Fields: - parent: Pointer to the parent node (for upward traversal) - pool: Allocator (used for allocating child nodes/strings) - lineno/col_offset: Starting position - end_lineno/end_col_offset: Ending position - children: List of child nodes (body block, added via append) - """ - parent: rowptr[AST] - pool: rowptr[memhub.MemManager] - lineno: int - col_offset: int - end_lineno: int - end_col_offset: int - children: list[rowptr[AST]] | t.CPtr - - def kind(self) -> int: - """Returns the node type (ASTKind value), overridden by subclasses""" - return 0 - - def type_name(self) -> str: - """Return the node type name, subclasses override""" - return "AST" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - """Polymorphic dump: subclasses override to define custom format. Returns the written pos""" - return pos - - def accept(self, visitor: t.CPtr): - """Visitor pattern entry point: subclasses override to dispatch to ASTVisitor in the visitor pattern. - Current implementation is empty. override this method to dispatch to visitor.visit_X(self)""" - pass - - def append(self, node: rowptr[AST]): - """Append node to the children list. - - Used for parser to build statement blocks: module.append(stmt), if_node.append(stmt), etc. - The children list is lazily initialized on first call. - - Note: list[AST|CPtr] is currently stored by value (compiler limitation), so you must set - node.parent before appending, so the parent in the copy is correct.""" - if self is None or node is None: - return - if self.children is None: - self.children = list[rowptr[AST]](self.pool, 8) - node.parent = self - _append_child(self.children, node, self.pool) - - -# ============================================================ -# Stmt - Statement Base Class -# -# 所有语句节点(Assign/If/For/While/Return/...)继承 Stmt。 -# isinstance(node, ast.Stmt) 可在运行时区分"语句"与"表达式" -# (依赖 TPV isinstance 子类集合判定 + 三层 vtable 继承)。 -# 不 override kind()(继承 AST 的 0),具体节点各自返回 ASTKind 值。 -# ============================================================ - -@t.CVTable -class Stmt(AST): - """语句节点基类(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)的既有语义。 -# ============================================================ - -@t.CVTable -class ExprNode(AST): - """表达式节点基类(Name/Constant/Call/BinOp/...)""" - - -# ============================================================ -# Module dump function (for ast.dump(node, buf, size)) -# ============================================================ - -def dump(node: rowptr[AST], buf: str, size: t.CSizeT): - """Serialize AST node to buf (with capacity size)""" - if node is None or buf is None or size == 0: - return - buf[0] = '\0' - final_pos: t.CSizeT = node.dump(buf, size, 0) - # NUL-terminate to prevent printf overflow - if final_pos < size: - buf[final_pos] = '\0' - else: - buf[size - 1] = '\0' +import t, c +from stdint import * +import memhub +import string +import viperlib +from .tokens import TokOp + + +# ============================================================ +# ASTKind - Node type enum (kind() virtual function return value) +# ============================================================ + +class ASTKind(t.CEnum): + # Core 15 nodes + Module: t.State + FunctionDef: t.State + ClassDef: t.State + Assign: t.State + If: t.State + For: t.State + While: t.State + Return: t.State + Expr: t.State + Name: t.State + Constant: t.State + BinOp: t.State + UnaryOp: t.State + Call: t.State + Compare: t.State + # Module Layer + Expression: t.State + Interactive: t.State + FunctionType: t.State + # Statement + Delete: t.State + AugAssign: t.State + AnnAssign: t.State + With: t.State + Raise: t.State + Try: t.State + Assert: t.State + Global: t.State + Nonlocal: t.State + Pass: t.State + Break: t.State + Continue: t.State + Import: t.State + ImportFrom: t.State + Match: t.State + # Expression Layer + BoolOp: t.State + Lambda: t.State + IfExp: t.State + Dict: t.State + Set: t.State + ListComp: t.State + SetComp: t.State + DictComp: t.State + GeneratorExp: t.State + Await: t.State + Yield: t.State + YieldFrom: t.State + FormattedValue: t.State + JoinedStr: t.State + Attribute: t.State + Subscript: t.State + Starred: t.State + List: t.State + Tuple: t.State + Slice: t.State + NamedExpr: t.State + # Auxiliary Nodes + ExceptHandler: t.State + Arguments: t.State + Arg: t.State + Keyword: t.State + Alias: t.State + WithItem: t.State + Comprehension: t.State + OpNode: t.State + # Pattern Matching + MatchCase: t.State + MatchValue: t.State + MatchSingleton: t.State + MatchSequence: t.State + MatchMapping: t.State + MatchClass: t.State + MatchStar: t.State + MatchAs: t.State + MatchOr: t.State + + +# ============================================================ +# ASTCtx - Name context (Load/Store/Del) +# ============================================================ + +class ASTCtx(t.CEnum): + Load: t.State + Store: t.State + Del: t.State + + +# ============================================================ +# OpKind - Operator enum (BinOp/UnaryOp/Compare) +# ============================================================ + +class OpKind(t.CEnum): + # Binary arithmetic operators + Add: t.State + Sub: t.State + Mult: t.State + MatMult: t.State + Div: t.State + Mod: t.State + Pow: t.State + LShift: t.State + RShift: t.State + BitOr: t.State + BitXor: t.State + BitAnd: t.State + FloorDiv: t.State + # Boolean operators + And: t.State + Or: t.State + # Comparison operators + Eq: t.State + Ne: t.State + Lt: t.State + Le: t.State + Gt: t.State + Ge: t.State + Is: t.State + IsNot: t.State + In: t.State + NotIn: t.State + # Unary operators + Not: t.State + UAdd: t.State + USub: t.State + Invert: t.State + Deref: t.State + AddrOf: t.State + # None + NoneOp: t.State + + +# Constant subtype identifiers +CONST_INT: t.CDefine = 1 +CONST_FLOAT: t.CDefine = 2 +CONST_STR: t.CDefine = 3 +CONST_BOOL: t.CDefine = 4 +CONST_NONE: t.CDefine = 5 +CONST_CHAR: t.CDefine = 6 + +# Node flags +FLAG_IS_ASYNC: t.CDefine = 1 +FLAG_SIMPLE: t.CDefine = 2 +FLAG_HAS_STAR: t.CDefine = 4 +FLAG_IS_EXTERN: t.CDefine = 8 +FLAG_IS_EXPORT: t.CDefine = 16 +FLAG_IS_DEFINE: t.CDefine = 32 + + +# ============================================================ +# ASTFlag - Node flags enum +# ============================================================ + +class ASTFlag(t.CEnum): + IsAsync: t.State + Simple: t.State + HasStar: t.State + + +# ============================================================ +# Helper Functions +# ============================================================ + +def _copy_str(pool: rawptr[memhub.MemManager], src: str) -> str: + """Allocate and copy a C string from pool.""" + if src is None: return None + slen: t.CSizeT = string.strlen(src) + buf: str = pool.alloc(slen + 1) + if buf is None: return None + string.memcpy(buf, src, slen + 1) + return buf + + +def _set_pos(node: rawptr[AST], lineno: int, col_offset: int, + end_lineno: int, end_col_offset: int): + if node is None: return + node.lineno = lineno + node.col_offset = col_offset + node.end_lineno = end_lineno + node.end_col_offset = end_col_offset + + +def _inherit_pos(node: rawptr[AST], ref: rawptr[AST]): + if node is None or ref is None: return + node.lineno = ref.lineno + node.col_offset = ref.col_offset + node.end_lineno = ref.end_lineno + node.end_col_offset = ref.end_col_offset + + +def _init_ast(node: rawptr[AST], pool: rawptr[memhub.MemManager], + lineno: int, col: int): + """Initialize AST base fields.""" + node.parent = None + node.pool = pool + node.lineno = lineno + node.col_offset = col + node.end_lineno = lineno + node.end_col_offset = col + node.children = None + + +def _set_parent_list(lst: list[rawptr[AST]] | t.CPtr, parent: rawptr[AST]): + """Set list children parent pointers.""" + if lst is None: return + n: t.CSizeT = lst.__len__() + i: t.CSizeT = 0 + while i < n: + child: rawptr[AST] = lst.get(i) + if child is not None: + child.parent = parent + i += 1 + + +def _append_child(lst: list[rawptr[AST]] | t.CPtr, node: rawptr[AST], + pool: rawptr[memhub.MemManager]): + """Append child nodes to a list (module-level function, to avoid conflicts with the AST.append method name). + + Call this function within the AST.append method to bypass the compiler mistaking list.append for a virtual function call to AST.append when doing type inference.""" + if lst is None or node is None: + return + lst.append(node) + + +# ============================================================ +# Operator Mapping: TokOp -> OpKind +# ============================================================ + +def _binop_from_op(op_id: int) -> int: + if op_id == TokOp.Plus: return OpKind.Add + if op_id == TokOp.Minus: return OpKind.Sub + if op_id == TokOp.Star: return OpKind.Mult + if op_id == TokOp.At: return OpKind.MatMult + if op_id == TokOp.Slash: return OpKind.Div + if op_id == TokOp.Percent: return OpKind.Mod + if op_id == TokOp.StarStar: return OpKind.Pow + if op_id == TokOp.LtLt: return OpKind.LShift + if op_id == TokOp.GtGt: return OpKind.RShift + if op_id == TokOp.VBar: return OpKind.BitOr + if op_id == TokOp.Caret: return OpKind.BitXor + if op_id == TokOp.Amp: return OpKind.BitAnd + if op_id == TokOp.DSlash: return OpKind.FloorDiv + return OpKind.NoneOp + + +def _augop_from_op(op_id: int) -> int: + if op_id == TokOp.PlusEq: return OpKind.Add + if op_id == TokOp.MinusEq: return OpKind.Sub + if op_id == TokOp.StarEq: return OpKind.Mult + if op_id == TokOp.AtEq: return OpKind.MatMult + if op_id == TokOp.SlashEq: return OpKind.Div + if op_id == TokOp.PercentEq: return OpKind.Mod + if op_id == TokOp.StarEqEq: return OpKind.Pow + if op_id == TokOp.LtLtEq: return OpKind.LShift + if op_id == TokOp.GtGtEq: return OpKind.RShift + if op_id == TokOp.VBarEq: return OpKind.BitOr + if op_id == TokOp.CaretEq: return OpKind.BitXor + if op_id == TokOp.AmpEq: return OpKind.BitAnd + if op_id == TokOp.DSlashEq: return OpKind.FloorDiv + return OpKind.NoneOp + + +def _cmpop_from_op(op_id: int) -> int: + if op_id == TokOp.EqEq: return OpKind.Eq + if op_id == TokOp.ExclaimEq: return OpKind.Ne + if op_id == TokOp.Less: return OpKind.Lt + if op_id == TokOp.LessEq: return OpKind.Le + if op_id == TokOp.Greater: return OpKind.Gt + if op_id == TokOp.GreaterEq: return OpKind.Ge + return OpKind.NoneOp + + +# ============================================================ +# dump Helper Functions +# ============================================================ + +def _emit(buf: str, size: t.CSizeT, pos: t.CSizeT, + text: str) -> t.CSizeT: + """Append literal to buf+pos.""" + if buf is None or pos >= size: + return pos + cur: str = str(uint64(buf) + pos) + rem: t.CSizeT = size - pos + n: int = viperlib.snprintf(cur, rem, "%s", text) + if n < 0: + return pos + return pos + t.CSizeT(n) + + +def _emit_str(buf: str, size: t.CSizeT, pos: t.CSizeT, + text: str) -> t.CSizeT: + """Append a string value to buf pos (using %s format)""" + if buf is None or pos >= size: + return pos + cur: str = str(uint64(buf) + pos) + rem: t.CSizeT = size - pos + n: int = viperlib.snprintf(cur, rem, "%s", text) + if n < 0: + return pos + return pos + t.CSizeT(n) + + +def _emit_int(buf: str, size: t.CSizeT, pos: t.CSizeT, + val: int64) -> t.CSizeT: + """Append an integer value to buf pos (using %lld format).""" + if buf is None or pos >= size: + return pos + cur: str = str(uint64(buf) + pos) + rem: t.CSizeT = size - pos + n: int = viperlib.snprintf(cur, rem, "%lld", val) + if n < 0: + return pos + return pos + t.CSizeT(n) + + +def _op_name(op: int) -> str: + """Return the string name of an OpKind.""" + if op == OpKind.Add: return "Add" + if op == OpKind.Sub: return "Sub" + if op == OpKind.Mult: return "Mult" + if op == OpKind.MatMult: return "MatMult" + if op == OpKind.Div: return "Div" + if op == OpKind.Mod: return "Mod" + if op == OpKind.Pow: return "Pow" + if op == OpKind.LShift: return "LShift" + if op == OpKind.RShift: return "RShift" + if op == OpKind.BitOr: return "BitOr" + if op == OpKind.BitXor: return "BitXor" + if op == OpKind.BitAnd: return "BitAnd" + if op == OpKind.FloorDiv: return "FloorDiv" + if op == OpKind.And: return "And" + if op == OpKind.Or: return "Or" + if op == OpKind.Eq: return "Eq" + if op == OpKind.Ne: return "Ne" + if op == OpKind.Lt: return "Lt" + if op == OpKind.Le: return "Le" + if op == OpKind.Gt: return "Gt" + if op == OpKind.Ge: return "Ge" + if op == OpKind.Is: return "Is" + if op == OpKind.IsNot: return "IsNot" + if op == OpKind.In: return "In" + if op == OpKind.NotIn: return "NotIn" + if op == OpKind.Not: return "Not" + if op == OpKind.UAdd: return "UAdd" + if op == OpKind.USub: return "USub" + if op == OpKind.Invert: return "Invert" + if op == OpKind.Deref: return "Deref" + if op == OpKind.AddrOf: return "AddrOf" + return "NoneOp" + + +def _dump_list(lst: list[rawptr[AST]] | t.CPtr, buf: str, + size: t.CSizeT, pos: t.CSizeT) -> t.CSizeT: + """Traverse a list[AST | CPtr] container, polymorphically dump each element.""" + if lst is None: + pos = _emit(buf, size, pos, "[]") + return pos + pos = _emit(buf, size, pos, "[") + n: t.CSizeT = lst.__len__() + i: t.CSizeT = 0 + while i < n: + if i > 0: + pos = _emit(buf, size, pos, ", ") + child: rawptr[AST] = lst.get(i) + if child is not None: + pos = child.dump(buf, size, pos) + i += 1 + pos = _emit(buf, size, pos, "]") + return pos + + +def _dump_op_list(lst: t.CPtr, buf: str, + size: t.CSizeT, pos: t.CSizeT) -> t.CSizeT: + """Traverse a list[CInt] (OpKind values), output a list of op names.""" + if lst is None: + pos = _emit(buf, size, pos, "[]") + return pos + ops_list: rawptr[list[int]] = (rawptr[list[int]])(lst) + pos = _emit(buf, size, pos, "[") + n: t.CSizeT = ops_list.__len__() + i: t.CSizeT = 0 + while i < n: + if i > 0: + pos = _emit(buf, size, pos, ", ") + op: int = ops_list.get(i) + pos = _emit(buf, size, pos, _op_name(op)) + i += 1 + pos = _emit(buf, size, pos, "]") + return pos + + +# ============================================================ +# AST - Polymorphic Base Class + +# @t.CVTable enables vtable, supporting virtual functions like kind()/type_name()/dump()/accept() +# Child nodes are stored in a list[AST | CPtr] container (O(1) random access, more efficient than linked lists) +# The parent field maintains a pointer to the parent (used for upward traversal) +# ============================================================ + +@t.CVTable +class AST: + """AST node base class. All specific node classes inherit from this. + + Fields: + parent: Pointer to the parent node (for upward traversal) + pool: Allocator (used for allocating child nodes/strings) + lineno/col_offset: Starting position + end_lineno/end_col_offset: Ending position + children: List of child nodes (body block, added via append) + """ + parent: rawptr[AST] + pool: rawptr[memhub.MemManager] + lineno: int + col_offset: int + end_lineno: int + end_col_offset: int + children: list[rawptr[AST]] | t.CPtr + + def kind(self) -> int: + """Returns the node type (ASTKind value), overridden by subclasses""" + return 0 + + def type_name(self) -> str: + """Return the node type name, subclasses override""" + return "AST" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + """Polymorphic dump: subclasses override to define custom format. Returns the written pos""" + return pos + + def accept(self, visitor: t.CPtr): + """Visitor pattern entry point: subclasses override to dispatch to ASTVisitor in the visitor pattern. + Current implementation is empty. override this method to dispatch to visitor.visit_X(self)""" + pass + + def append(self, node: rawptr[AST]): + """Append node to the children list. + + Used for parser to build statement blocks: module.append(stmt), if_node.append(stmt), etc. + The children list is lazily initialized on first call. + + Note: list[AST|CPtr] is currently stored by value (compiler limitation), so you must set + node.parent before appending, so the parent in the copy is correct.""" + if self is None or node is None: + return + if self.children is None: + self.children = list[rawptr[AST]](self.pool, 8) + node.parent = self + _append_child(self.children, node, self.pool) + + +# ============================================================ +# Stmt - Statement Base Class +# +# 所有语句节点(Assign/If/For/While/Return/...)继承 Stmt。 +# isinstance(node, ast.Stmt) 可在运行时区分"语句"与"表达式" +# (依赖 TPV isinstance 子类集合判定 + 三层 vtable 继承)。 +# 不 override kind()(继承 AST 的 0),具体节点各自返回 ASTKind 值。 +# ============================================================ + +@t.CVTable +class Stmt(AST): + """语句节点基类(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)的既有语义。 +# ============================================================ + +@t.CVTable +class ExprNode(AST): + """表达式节点基类(Name/Constant/Call/BinOp/...)""" + + +# ============================================================ +# Module dump function (for ast.dump(node, buf, size)) +# ============================================================ + +def dump(node: rawptr[AST], buf: str, size: t.CSizeT): + """Serialize AST node to buf (with capacity size)""" + if node is None or buf is None or size == 0: + return + buf[0] = '\0' + final_pos: t.CSizeT = node.dump(buf, size, 0) + # NUL-terminate to prevent printf overflow + if final_pos < size: + buf[final_pos] = '\0' + else: + buf[size - 1] = '\0' diff --git a/ast/exprs.vp b/ast/exprs.vp index ad57b0f..f8de024 100644 --- a/ast/exprs.vp +++ b/ast/exprs.vp @@ -1,1289 +1,1289 @@ -import t, c -from stdint import * -import memhub -import string -import stdio -from .base import ( - AST, ExprNode, ASTKind, ASTCtx, OpKind, - _init_ast, _copy_str, _set_parent_list, - _emit, _emit_str, _emit_int, _dump_list, _dump_op_list, _op_name, - CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR -) - - -# ============================================================ -# Name node -# ============================================================ - -class Name(ExprNode): - """Name(id: str, ctx: ASTCtx)""" - id: str - ctx: int - - def __new__(self, pool: rowptr[memhub.MemManager], - id: str, ctx: int): - ptr: rowptr[Name] = pool.alloc(Name.__sizeof__()) - if ptr: - string.memset(ptr, 0, Name.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - id: str, ctx: int): - _init_ast(self, pool, 0, 0) - self.id = _copy_str(pool, id) - self.ctx = ctx - - def kind(self) -> int: - return ASTKind.Name - - def type_name(self) -> str: - return "Name" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Name(id='") - pos = _emit_str(buf, size, pos, self.id) - pos = _emit(buf, size, pos, "', ctx=") - if self.ctx == ASTCtx.Load: - pos = _emit(buf, size, pos, "Load") - elif self.ctx == ASTCtx.Store: - pos = _emit(buf, size, pos, "Store") - else: - pos = _emit(buf, size, pos, "Del") - pos = _emit(buf, size, pos, ")") - return pos - - -def name_get_id(node: rowptr[Name]) -> str: - """获取 Name.id 字段(跨模块访问器,stub 字段访问陷阱防护)""" - if node is None: - return None - return node.id - - -def constant_get_str_val(node: rowptr[Constant]) -> str: - """获取 Constant.str_val 字段(跨模块访问器)""" - if node is None: - return None - return node.str_val - - -def binop_get_left(node: rowptr[BinOp]) -> rowptr[AST]: - """获取 BinOp.left 字段(跨模块访问器)""" - if node is None: - return None - return node.left - - -def binop_get_right(node: rowptr[BinOp]) -> rowptr[AST]: - """获取 BinOp.right 字段(跨模块访问器)""" - if node is None: - return None - return node.right - - -# ============================================================ -# Constant nodes (subtype marked by const_kind) -# ============================================================ - -class Constant(ExprNode): - """Constant(int_val/float_val/str_val, const_kind)""" - int_val: int64 - float_val: double - str_val: str - const_kind: int - - def __new__(self, pool: rowptr[memhub.MemManager], - const_kind: int, int_val: int64, - float_val: double, str_val: str, - lineno: int, col: int): - ptr: rowptr[Constant] = pool.alloc(Constant.__sizeof__()) - if ptr: - string.memset(ptr, 0, Constant.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - const_kind: int, int_val: int64, - float_val: double, str_val: str, - lineno: int, col: int): - _init_ast(self, pool, lineno, col) - self.const_kind = const_kind - self.int_val = int_val - self.float_val = float_val - if str_val is not None: - self.str_val = _copy_str(pool, str_val) - else: - self.str_val = None - - def kind(self) -> int: - return ASTKind.Constant - - def type_name(self) -> str: - return "Constant" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Constant(value=") - if self.const_kind == CONST_INT: - pos = _emit_int(buf, size, pos, self.int_val) - elif self.const_kind == CONST_FLOAT: - pos = _emit(buf, size, pos, "") - elif self.const_kind == CONST_STR: - pos = _emit(buf, size, pos, "'") - if self.str_val is not None: - pos = _emit_str(buf, size, pos, self.str_val) - else: - pos = _emit(buf, size, pos, "(null)") - pos = _emit(buf, size, pos, "'") - elif self.const_kind == CONST_CHAR: - # Single character → display as character ASCII value - pos = _emit(buf, size, pos, "char(") - pos = _emit_int(buf, size, pos, self.int_val) - pos = _emit(buf, size, pos, ")") - elif self.const_kind == CONST_BOOL: - if self.int_val != 0: - pos = _emit(buf, size, pos, "True") - else: - pos = _emit(buf, size, pos, "False") - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# BinOp Node -# ============================================================ - -class BinOp(ExprNode): - """BinOp(left: AST, op: OpKind, right: AST)""" - left: rowptr[AST] - op: int - right: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - left: rowptr[AST], op: int, right: rowptr[AST]): - ptr: rowptr[BinOp] = pool.alloc(BinOp.__sizeof__()) - if ptr: - string.memset(ptr, 0, BinOp.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - left: rowptr[AST], op: int, right: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.left = left - self.op = op - self.right = right - - def kind(self) -> int: - return ASTKind.BinOp - - def type_name(self) -> str: - return "BinOp" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "BinOp(left=") - if self.left is not None: - pos = self.left.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", op=") - pos = _emit(buf, size, pos, _op_name(self.op)) - pos = _emit(buf, size, pos, ", right=") - if self.right is not None: - pos = self.right.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# UnaryOp Node -# ============================================================ - -class UnaryOp(ExprNode): - """UnaryOp(op: OpKind, operand: AST)""" - op: int - operand: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - op: int, operand: rowptr[AST]): - ptr: rowptr[UnaryOp] = pool.alloc(UnaryOp.__sizeof__()) - if ptr: - string.memset(ptr, 0, UnaryOp.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - op: int, operand: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.op = op - self.operand = operand - - def kind(self) -> int: - return ASTKind.UnaryOp - - def type_name(self) -> str: - return "UnaryOp" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "UnaryOp(op=") - pos = _emit(buf, size, pos, _op_name(self.op)) - pos = _emit(buf, size, pos, ", operand=") - if self.operand is not None: - pos = self.operand.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Compare Node -# ============================================================ - -class Compare(ExprNode): - """Compare(left: AST, ops: list[CInt], comparators: list[AST | CPtr])""" - left: rowptr[AST] - ops: t.CPtr - comparators: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - left: rowptr[AST], - ops: t.CPtr, - comparators: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Compare] = pool.alloc(Compare.__sizeof__()) - if ptr: - string.memset(ptr, 0, Compare.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - left: rowptr[AST], - ops: t.CPtr, - comparators: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.left = left - self.ops = ops - self.comparators = comparators - _set_parent_list(comparators, self) - - def kind(self) -> int: - return ASTKind.Compare - - def type_name(self) -> str: - return "Compare" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Compare(left=") - if self.left is not None: - pos = self.left.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", ops=") - pos = _dump_op_list(self.ops, buf, size, pos) - pos = _emit(buf, size, pos, ", comparators=") - pos = _dump_list(self.comparators, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Call Node -# ============================================================ - -class Call(ExprNode): - """Call(func: AST, args: list[AST | CPtr], keywords: list[AST | CPtr])""" - func: rowptr[AST] - args: rowptr[list[rowptr[AST]]] - keywords: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - func: rowptr[AST], - args: rowptr[list[rowptr[AST]]], - keywords: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Call] = pool.alloc(Call.__sizeof__()) - if ptr: - string.memset(ptr, 0, Call.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - func: rowptr[AST], - args: rowptr[list[rowptr[AST]]], - keywords: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.func = func - self.args = args - self.keywords = keywords - _set_parent_list(args, self) - _set_parent_list(keywords, self) - - def kind(self) -> int: - return ASTKind.Call - - def type_name(self) -> str: - return "Call" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Call(func=") - if self.func is not None: - pos = self.func.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", args=") - pos = _dump_list(self.args, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# BoolOp Node -# ============================================================ - -class BoolOp(ExprNode): - """BoolOp(op: CInt, values: list[AST | CPtr])""" - op: int - values: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - op: int, - values: rowptr[list[rowptr[AST]]]): - ptr: rowptr[BoolOp] = pool.alloc(BoolOp.__sizeof__()) - if ptr: - string.memset(ptr, 0, BoolOp.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - op: int, - values: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.op = op - self.values = values - _set_parent_list(values, self) - - def kind(self) -> int: - return ASTKind.BoolOp - - def type_name(self) -> str: - return "BoolOp" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "BoolOp(op=") - pos = _emit(buf, size, pos, _op_name(self.op)) - pos = _emit(buf, size, pos, ", values=") - pos = _dump_list(self.values, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Lambda Node -# ============================================================ - -class Lambda(ExprNode): - """Lambda(args: AST, body: AST)""" - args: rowptr[AST] - body: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - args: rowptr[AST], body: rowptr[AST]): - ptr: rowptr[Lambda] = pool.alloc(Lambda.__sizeof__()) - if ptr: - string.memset(ptr, 0, Lambda.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - args: rowptr[AST], body: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.args = args - self.body = body - - def kind(self) -> int: - return ASTKind.Lambda - - def type_name(self) -> str: - return "Lambda" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Lambda(args=") - if self.args is not None: - pos = self.args.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", body=") - if self.body is not None: - pos = self.body.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# IfExp Node -# ============================================================ - -class IfExp(ExprNode): - """IfExp(test: AST, body: AST, orelse: AST)""" - test: rowptr[AST] - body: rowptr[AST] - orelse: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - test: rowptr[AST], body: rowptr[AST], - orelse: rowptr[AST]): - ptr: rowptr[IfExp] = pool.alloc(IfExp.__sizeof__()) - if ptr: - string.memset(ptr, 0, IfExp.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - test: rowptr[AST], body: rowptr[AST], - orelse: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.test = test - self.body = body - self.orelse = orelse - - def kind(self) -> int: - return ASTKind.IfExp - - def type_name(self) -> str: - return "IfExp" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "IfExp(test=") - if self.test is not None: - pos = self.test.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", body=") - if self.body is not None: - pos = self.body.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", orelse=") - if self.orelse is not None: - pos = self.orelse.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Dict Node -# ============================================================ - -class Dict(ExprNode): - """Dict(keys: list[AST | CPtr], values: list[AST | CPtr])""" - keys: rowptr[list[rowptr[AST]]] - values: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - keys: rowptr[list[rowptr[AST]]], - values: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Dict] = pool.alloc(Dict.__sizeof__()) - if ptr: - string.memset(ptr, 0, Dict.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - keys: rowptr[list[rowptr[AST]]], - values: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.keys = keys - self.values = values - _set_parent_list(keys, self) - _set_parent_list(values, self) - - def kind(self) -> int: - return ASTKind.Dict - - def type_name(self) -> str: - return "Dict" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Dict(keys=") - pos = _dump_list(self.keys, buf, size, pos) - pos = _emit(buf, size, pos, ", values=") - pos = _dump_list(self.values, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Set Node -# ============================================================ - -class Set(ExprNode): - """Set(elts: list[AST | CPtr])""" - elts: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - elts: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Set] = pool.alloc(Set.__sizeof__()) - if ptr: - string.memset(ptr, 0, Set.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - elts: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.elts = elts - _set_parent_list(elts, self) - - def kind(self) -> int: - return ASTKind.Set - - def type_name(self) -> str: - return "Set" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Set(elts=") - pos = _dump_list(self.elts, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# ListComp Node -# ============================================================ - -class ListComp(ExprNode): - """ListComp(elt: AST, generators: list[AST | CPtr])""" - elt: rowptr[AST] - generators: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - elt: rowptr[AST], - generators: rowptr[list[rowptr[AST]]]): - ptr: rowptr[ListComp] = pool.alloc(ListComp.__sizeof__()) - if ptr: - string.memset(ptr, 0, ListComp.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - elt: rowptr[AST], - generators: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.elt = elt - self.generators = generators - _set_parent_list(generators, self) - - def kind(self) -> int: - return ASTKind.ListComp - - def type_name(self) -> str: - return "ListComp" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "ListComp(elt=") - if self.elt is not None: - pos = self.elt.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", generators=") - pos = _dump_list(self.generators, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# SetComp Node -# ============================================================ - -class SetComp(ExprNode): - """SetComp(elt: AST, generators: list[AST | CPtr])""" - elt: rowptr[AST] - generators: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - elt: rowptr[AST], - generators: rowptr[list[rowptr[AST]]]): - ptr: rowptr[SetComp] = pool.alloc(SetComp.__sizeof__()) - if ptr: - string.memset(ptr, 0, SetComp.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - elt: rowptr[AST], - generators: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.elt = elt - self.generators = generators - _set_parent_list(generators, self) - - def kind(self) -> int: - return ASTKind.SetComp - - def type_name(self) -> str: - return "SetComp" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "SetComp(elt=") - if self.elt is not None: - pos = self.elt.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", generators=") - pos = _dump_list(self.generators, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# DictComp Node -# ============================================================ - -class DictComp(ExprNode): - """DictComp(key: AST, value: AST, generators: list[AST | CPtr])""" - key: rowptr[AST] - value: rowptr[AST] - generators: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - key: rowptr[AST], value: rowptr[AST], - generators: rowptr[list[rowptr[AST]]]): - ptr: rowptr[DictComp] = pool.alloc(DictComp.__sizeof__()) - if ptr: - string.memset(ptr, 0, DictComp.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - key: rowptr[AST], value: rowptr[AST], - generators: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.key = key - self.value = value - self.generators = generators - _set_parent_list(generators, self) - - def kind(self) -> int: - return ASTKind.DictComp - - def type_name(self) -> str: - return "DictComp" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "DictComp(key=") - if self.key is not None: - pos = self.key.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", generators=") - pos = _dump_list(self.generators, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# GeneratorExp Node -# ============================================================ - -class GeneratorExp(ExprNode): - """GeneratorExp(elt: AST, generators: list[AST | CPtr])""" - elt: rowptr[AST] - generators: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - elt: rowptr[AST], - generators: rowptr[list[rowptr[AST]]]): - ptr: rowptr[GeneratorExp] = pool.alloc(GeneratorExp.__sizeof__()) - if ptr: - string.memset(ptr, 0, GeneratorExp.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - elt: rowptr[AST], - generators: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.elt = elt - self.generators = generators - _set_parent_list(generators, self) - - def kind(self) -> int: - return ASTKind.GeneratorExp - - def type_name(self) -> str: - return "GeneratorExp" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "GeneratorExp(elt=") - if self.elt is not None: - pos = self.elt.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", generators=") - pos = _dump_list(self.generators, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Await Node -# ============================================================ - -class Await(ExprNode): - """Await(value: AST)""" - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - ptr: rowptr[Await] = pool.alloc(Await.__sizeof__()) - if ptr: - string.memset(ptr, 0, Await.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.value = value - - def kind(self) -> int: - return ASTKind.Await - - def type_name(self) -> str: - return "Await" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Await(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Yield Node -# ============================================================ - -class Yield(ExprNode): - """Yield(value: AST)""" - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - ptr: rowptr[Yield] = pool.alloc(Yield.__sizeof__()) - if ptr: - string.memset(ptr, 0, Yield.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.value = value - - def kind(self) -> int: - return ASTKind.Yield - - def type_name(self) -> str: - return "Yield" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Yield(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# YieldFrom Node -# ============================================================ - -class YieldFrom(ExprNode): - """YieldFrom(value: AST)""" - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - ptr: rowptr[YieldFrom] = pool.alloc(YieldFrom.__sizeof__()) - if ptr: - string.memset(ptr, 0, YieldFrom.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.value = value - - def kind(self) -> int: - return ASTKind.YieldFrom - - def type_name(self) -> str: - return "YieldFrom" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "YieldFrom(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# FormattedValue Node -# ============================================================ - -class FormattedValue(ExprNode): - """FormattedValue(value: AST, conversion: CInt, format_spec: AST)""" - value: rowptr[AST] - conversion: int - format_spec: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - value: rowptr[AST], conversion: int, - format_spec: rowptr[AST]): - ptr: rowptr[FormattedValue] = pool.alloc(FormattedValue.__sizeof__()) - if ptr: - string.memset(ptr, 0, FormattedValue.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - value: rowptr[AST], conversion: int, - format_spec: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.value = value - self.conversion = conversion - self.format_spec = format_spec - - def kind(self) -> int: - return ASTKind.FormattedValue - - def type_name(self) -> str: - return "FormattedValue" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "FormattedValue(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", conversion=") - pos = _emit_int(buf, size, pos, int64(self.conversion)) - pos = _emit(buf, size, pos, ", format_spec=") - if self.format_spec is not None: - pos = self.format_spec.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# JoinedStr Node -# ============================================================ - -class JoinedStr(ExprNode): - """JoinedStr(values: list[AST | CPtr])""" - values: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - values: rowptr[list[rowptr[AST]]]): - ptr: rowptr[JoinedStr] = pool.alloc(JoinedStr.__sizeof__()) - if ptr: - string.memset(ptr, 0, JoinedStr.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - values: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.values = values - _set_parent_list(values, self) - - def kind(self) -> int: - return ASTKind.JoinedStr - - def type_name(self) -> str: - return "JoinedStr" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "JoinedStr(values=") - pos = _dump_list(self.values, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Attribute Node -# ============================================================ - -class Attribute(ExprNode): - """Attribute(value: AST, attr: str, ctx: CInt)""" - value: rowptr[AST] - attr: str - ctx: int - - def __new__(self, pool: rowptr[memhub.MemManager], - value: rowptr[AST], attr: str, ctx: int): - ptr: rowptr[Attribute] = pool.alloc(Attribute.__sizeof__()) - if ptr: - string.memset(ptr, 0, Attribute.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - value: rowptr[AST], attr: str, ctx: int): - _init_ast(self, pool, 0, 0) - self.value = value - self.attr = _copy_str(pool, attr) - self.ctx = ctx - - def kind(self) -> int: - return ASTKind.Attribute - - def type_name(self) -> str: - return "Attribute" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Attribute(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", attr='") - pos = _emit_str(buf, size, pos, self.attr) - pos = _emit(buf, size, pos, "', ctx=") - pos = _emit_int(buf, size, pos, int64(self.ctx)) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# 字段访问器(跨模块 stub 字段访问陷阱防护) -# -# 其他模块(如 TypeGenCType)直接访问 ast.Attribute 的字段时, -# TPC 可能按 stub 类型生成错误 GEP(读垃圾地址 → 0xC0000005 崩溃)。 -# 本模块是完整类型,字段偏移正确;跨模块调用方用这些访问器。 -# ============================================================ -def attribute_get_value(node: rowptr[Attribute]) -> rowptr[AST]: - """获取 Attribute.value 字段(跨模块访问器)""" - if node is None: - return None - return node.value - - -def attribute_get_attr(node: rowptr[Attribute]) -> str: - """获取 Attribute.attr 字段(跨模块访问器)""" - if node is None: - return None - return node.attr - - -# ============================================================ -# Subscript Node -# ============================================================ - -class Subscript(ExprNode): - """Subscript(value: AST, slice: AST, ctx: CInt)""" - value: rowptr[AST] - slice: rowptr[AST] - ctx: int - - def __new__(self, pool: rowptr[memhub.MemManager], - value: rowptr[AST], slice: rowptr[AST], ctx: int): - ptr: rowptr[Subscript] = pool.alloc(Subscript.__sizeof__()) - if ptr: - string.memset(ptr, 0, Subscript.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - value: rowptr[AST], slice: rowptr[AST], ctx: int): - _init_ast(self, pool, 0, 0) - self.value = value - self.slice = slice - self.ctx = ctx - - def kind(self) -> int: - return ASTKind.Subscript - - def type_name(self) -> str: - return "Subscript" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Subscript(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", slice=") - if self.slice is not None: - pos = self.slice.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", ctx=") - pos = _emit_int(buf, size, pos, int64(self.ctx)) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Starred Node -# ============================================================ - -class Starred(ExprNode): - """Starred(value: AST, ctx: CInt)""" - value: rowptr[AST] - ctx: int - - def __new__(self, pool: rowptr[memhub.MemManager], - value: rowptr[AST], ctx: int): - ptr: rowptr[Starred] = pool.alloc(Starred.__sizeof__()) - if ptr: - string.memset(ptr, 0, Starred.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - value: rowptr[AST], ctx: int): - _init_ast(self, pool, 0, 0) - self.value = value - self.ctx = ctx - - def kind(self) -> int: - return ASTKind.Starred - - def type_name(self) -> str: - return "Starred" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Starred(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", ctx=") - pos = _emit_int(buf, size, pos, int64(self.ctx)) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# List Node -# ============================================================ - -class List(ExprNode): - """List(elts: list[AST | CPtr], ctx: CInt)""" - elts: rowptr[list[rowptr[AST]]] - ctx: int - - def __new__(self, pool: rowptr[memhub.MemManager], - elts: rowptr[list[rowptr[AST]]], ctx: int): - ptr: rowptr[List] = pool.alloc(List.__sizeof__()) - if ptr: - string.memset(ptr, 0, List.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - elts: rowptr[list[rowptr[AST]]], ctx: int): - _init_ast(self, pool, 0, 0) - self.elts = elts - self.ctx = ctx - _set_parent_list(elts, self) - - def kind(self) -> int: - return ASTKind.List - - def type_name(self) -> str: - return "List" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "List(elts=") - pos = _dump_list(self.elts, buf, size, pos) - pos = _emit(buf, size, pos, ", ctx=") - pos = _emit_int(buf, size, pos, int64(self.ctx)) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Tuple Node -# ============================================================ - -class Tuple(ExprNode): - """Tuple(elts: list[AST | CPtr], ctx: CInt)""" - elts: rowptr[list[rowptr[AST]]] - ctx: int - - def __new__(self, pool: rowptr[memhub.MemManager], - elts: rowptr[list[rowptr[AST]]], ctx: int): - ptr: rowptr[Tuple] = pool.alloc(Tuple.__sizeof__()) - if ptr: - string.memset(ptr, 0, Tuple.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - elts: rowptr[list[rowptr[AST]]], ctx: int): - _init_ast(self, pool, 0, 0) - self.elts = elts - self.ctx = ctx - _set_parent_list(elts, self) - - def kind(self) -> int: - return ASTKind.Tuple - - def type_name(self) -> str: - return "Tuple" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Tuple(elts=") - pos = _dump_list(self.elts, buf, size, pos) - pos = _emit(buf, size, pos, ", ctx=") - pos = _emit_int(buf, size, pos, int64(self.ctx)) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Slice Node -# ============================================================ - -class Slice(ExprNode): - """Slice(lower: AST, upper: AST, step: AST)""" - lower: rowptr[AST] - upper: rowptr[AST] - step: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - lower: rowptr[AST], upper: rowptr[AST], - step: rowptr[AST]): - ptr: rowptr[Slice] = pool.alloc(Slice.__sizeof__()) - if ptr: - string.memset(ptr, 0, Slice.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - lower: rowptr[AST], upper: rowptr[AST], - step: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.lower = lower - self.upper = upper - self.step = step - - def kind(self) -> int: - return ASTKind.Slice - - def type_name(self) -> str: - return "Slice" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Slice(lower=") - if self.lower is not None: - pos = self.lower.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", upper=") - if self.upper is not None: - pos = self.upper.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", step=") - if self.step is not None: - pos = self.step.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# NamedExpr Node -# ============================================================ - -class NamedExpr(ExprNode): - """NamedExpr(target: AST, value: AST)""" - target: rowptr[AST] - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], value: rowptr[AST]): - ptr: rowptr[NamedExpr] = pool.alloc(NamedExpr.__sizeof__()) - if ptr: - string.memset(ptr, 0, NamedExpr.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.target = target - self.value = value - - def kind(self) -> int: - return ASTKind.NamedExpr - - def type_name(self) -> str: - return "NamedExpr" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "NamedExpr(target=") - if self.target is not None: - pos = self.target.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# OpNode Node -# ============================================================ - -class OpNode(AST): - """OpNode(op: CInt)""" - op: int - - def __new__(self, pool: rowptr[memhub.MemManager], op: int): - ptr: rowptr[OpNode] = pool.alloc(OpNode.__sizeof__()) - if ptr: - string.memset(ptr, 0, OpNode.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], op: int): - _init_ast(self, pool, 0, 0) - self.op = op - - def kind(self) -> int: - return ASTKind.OpNode - - def type_name(self) -> str: - return "OpNode" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "OpNode(op=") - pos = _emit(buf, size, pos, _op_name(self.op)) - pos = _emit(buf, size, pos, ")") - return pos +import t, c +from stdint import * +import memhub +import string +import stdio +from .base import ( + AST, ExprNode, ASTKind, ASTCtx, OpKind, + _init_ast, _copy_str, _set_parent_list, + _emit, _emit_str, _emit_int, _dump_list, _dump_op_list, _op_name, + CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR +) + + +# ============================================================ +# Name node +# ============================================================ + +class Name(ExprNode): + """Name(id: str, ctx: ASTCtx)""" + id: str + ctx: int + + def __new__(self, pool: rawptr[memhub.MemManager], + id: str, ctx: int): + ptr: rawptr[Name] = pool.alloc(Name.__sizeof__()) + if ptr: + string.memset(ptr, 0, Name.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + id: str, ctx: int): + _init_ast(self, pool, 0, 0) + self.id = _copy_str(pool, id) + self.ctx = ctx + + def kind(self) -> int: + return ASTKind.Name + + def type_name(self) -> str: + return "Name" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Name(id='") + pos = _emit_str(buf, size, pos, self.id) + pos = _emit(buf, size, pos, "', ctx=") + if self.ctx == ASTCtx.Load: + pos = _emit(buf, size, pos, "Load") + elif self.ctx == ASTCtx.Store: + pos = _emit(buf, size, pos, "Store") + else: + pos = _emit(buf, size, pos, "Del") + pos = _emit(buf, size, pos, ")") + return pos + + +def name_get_id(node: rawptr[Name]) -> str: + """获取 Name.id 字段(跨模块访问器,stub 字段访问陷阱防护)""" + if node is None: + return None + return node.id + + +def constant_get_str_val(node: rawptr[Constant]) -> str: + """获取 Constant.str_val 字段(跨模块访问器)""" + if node is None: + return None + return node.str_val + + +def binop_get_left(node: rawptr[BinOp]) -> rawptr[AST]: + """获取 BinOp.left 字段(跨模块访问器)""" + if node is None: + return None + return node.left + + +def binop_get_right(node: rawptr[BinOp]) -> rawptr[AST]: + """获取 BinOp.right 字段(跨模块访问器)""" + if node is None: + return None + return node.right + + +# ============================================================ +# Constant nodes (subtype marked by const_kind) +# ============================================================ + +class Constant(ExprNode): + """Constant(int_val/float_val/str_val, const_kind)""" + int_val: int64 + float_val: double + str_val: str + const_kind: int + + def __new__(self, pool: rawptr[memhub.MemManager], + const_kind: int, int_val: int64, + float_val: double, str_val: str, + lineno: int, col: int): + ptr: rawptr[Constant] = pool.alloc(Constant.__sizeof__()) + if ptr: + string.memset(ptr, 0, Constant.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + const_kind: int, int_val: int64, + float_val: double, str_val: str, + lineno: int, col: int): + _init_ast(self, pool, lineno, col) + self.const_kind = const_kind + self.int_val = int_val + self.float_val = float_val + if str_val is not None: + self.str_val = _copy_str(pool, str_val) + else: + self.str_val = None + + def kind(self) -> int: + return ASTKind.Constant + + def type_name(self) -> str: + return "Constant" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Constant(value=") + if self.const_kind == CONST_INT: + pos = _emit_int(buf, size, pos, self.int_val) + elif self.const_kind == CONST_FLOAT: + pos = _emit(buf, size, pos, "") + elif self.const_kind == CONST_STR: + pos = _emit(buf, size, pos, "'") + if self.str_val is not None: + pos = _emit_str(buf, size, pos, self.str_val) + else: + pos = _emit(buf, size, pos, "(null)") + pos = _emit(buf, size, pos, "'") + elif self.const_kind == CONST_CHAR: + # Single character → display as character ASCII value + pos = _emit(buf, size, pos, "char(") + pos = _emit_int(buf, size, pos, self.int_val) + pos = _emit(buf, size, pos, ")") + elif self.const_kind == CONST_BOOL: + if self.int_val != 0: + pos = _emit(buf, size, pos, "True") + else: + pos = _emit(buf, size, pos, "False") + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# BinOp Node +# ============================================================ + +class BinOp(ExprNode): + """BinOp(left: AST, op: OpKind, right: AST)""" + left: rawptr[AST] + op: int + right: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + left: rawptr[AST], op: int, right: rawptr[AST]): + ptr: rawptr[BinOp] = pool.alloc(BinOp.__sizeof__()) + if ptr: + string.memset(ptr, 0, BinOp.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + left: rawptr[AST], op: int, right: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.left = left + self.op = op + self.right = right + + def kind(self) -> int: + return ASTKind.BinOp + + def type_name(self) -> str: + return "BinOp" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "BinOp(left=") + if self.left is not None: + pos = self.left.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", op=") + pos = _emit(buf, size, pos, _op_name(self.op)) + pos = _emit(buf, size, pos, ", right=") + if self.right is not None: + pos = self.right.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# UnaryOp Node +# ============================================================ + +class UnaryOp(ExprNode): + """UnaryOp(op: OpKind, operand: AST)""" + op: int + operand: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + op: int, operand: rawptr[AST]): + ptr: rawptr[UnaryOp] = pool.alloc(UnaryOp.__sizeof__()) + if ptr: + string.memset(ptr, 0, UnaryOp.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + op: int, operand: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.op = op + self.operand = operand + + def kind(self) -> int: + return ASTKind.UnaryOp + + def type_name(self) -> str: + return "UnaryOp" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "UnaryOp(op=") + pos = _emit(buf, size, pos, _op_name(self.op)) + pos = _emit(buf, size, pos, ", operand=") + if self.operand is not None: + pos = self.operand.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Compare Node +# ============================================================ + +class Compare(ExprNode): + """Compare(left: AST, ops: list[CInt], comparators: list[AST | CPtr])""" + left: rawptr[AST] + ops: t.CPtr + comparators: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + left: rawptr[AST], + ops: t.CPtr, + comparators: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Compare] = pool.alloc(Compare.__sizeof__()) + if ptr: + string.memset(ptr, 0, Compare.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + left: rawptr[AST], + ops: t.CPtr, + comparators: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.left = left + self.ops = ops + self.comparators = comparators + _set_parent_list(comparators, self) + + def kind(self) -> int: + return ASTKind.Compare + + def type_name(self) -> str: + return "Compare" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Compare(left=") + if self.left is not None: + pos = self.left.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", ops=") + pos = _dump_op_list(self.ops, buf, size, pos) + pos = _emit(buf, size, pos, ", comparators=") + pos = _dump_list(self.comparators, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Call Node +# ============================================================ + +class Call(ExprNode): + """Call(func: AST, args: list[AST | CPtr], keywords: list[AST | CPtr])""" + func: rawptr[AST] + args: rawptr[list[rawptr[AST]]] + keywords: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + func: rawptr[AST], + args: rawptr[list[rawptr[AST]]], + keywords: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Call] = pool.alloc(Call.__sizeof__()) + if ptr: + string.memset(ptr, 0, Call.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + func: rawptr[AST], + args: rawptr[list[rawptr[AST]]], + keywords: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.func = func + self.args = args + self.keywords = keywords + _set_parent_list(args, self) + _set_parent_list(keywords, self) + + def kind(self) -> int: + return ASTKind.Call + + def type_name(self) -> str: + return "Call" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Call(func=") + if self.func is not None: + pos = self.func.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", args=") + pos = _dump_list(self.args, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# BoolOp Node +# ============================================================ + +class BoolOp(ExprNode): + """BoolOp(op: CInt, values: list[AST | CPtr])""" + op: int + values: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + op: int, + values: rawptr[list[rawptr[AST]]]): + ptr: rawptr[BoolOp] = pool.alloc(BoolOp.__sizeof__()) + if ptr: + string.memset(ptr, 0, BoolOp.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + op: int, + values: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.op = op + self.values = values + _set_parent_list(values, self) + + def kind(self) -> int: + return ASTKind.BoolOp + + def type_name(self) -> str: + return "BoolOp" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "BoolOp(op=") + pos = _emit(buf, size, pos, _op_name(self.op)) + pos = _emit(buf, size, pos, ", values=") + pos = _dump_list(self.values, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Lambda Node +# ============================================================ + +class Lambda(ExprNode): + """Lambda(args: AST, body: AST)""" + args: rawptr[AST] + body: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + args: rawptr[AST], body: rawptr[AST]): + ptr: rawptr[Lambda] = pool.alloc(Lambda.__sizeof__()) + if ptr: + string.memset(ptr, 0, Lambda.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + args: rawptr[AST], body: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.args = args + self.body = body + + def kind(self) -> int: + return ASTKind.Lambda + + def type_name(self) -> str: + return "Lambda" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Lambda(args=") + if self.args is not None: + pos = self.args.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", body=") + if self.body is not None: + pos = self.body.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# IfExp Node +# ============================================================ + +class IfExp(ExprNode): + """IfExp(test: AST, body: AST, orelse: AST)""" + test: rawptr[AST] + body: rawptr[AST] + orelse: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + test: rawptr[AST], body: rawptr[AST], + orelse: rawptr[AST]): + ptr: rawptr[IfExp] = pool.alloc(IfExp.__sizeof__()) + if ptr: + string.memset(ptr, 0, IfExp.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + test: rawptr[AST], body: rawptr[AST], + orelse: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.test = test + self.body = body + self.orelse = orelse + + def kind(self) -> int: + return ASTKind.IfExp + + def type_name(self) -> str: + return "IfExp" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "IfExp(test=") + if self.test is not None: + pos = self.test.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", body=") + if self.body is not None: + pos = self.body.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", orelse=") + if self.orelse is not None: + pos = self.orelse.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Dict Node +# ============================================================ + +class Dict(ExprNode): + """Dict(keys: list[AST | CPtr], values: list[AST | CPtr])""" + keys: rawptr[list[rawptr[AST]]] + values: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + keys: rawptr[list[rawptr[AST]]], + values: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Dict] = pool.alloc(Dict.__sizeof__()) + if ptr: + string.memset(ptr, 0, Dict.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + keys: rawptr[list[rawptr[AST]]], + values: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.keys = keys + self.values = values + _set_parent_list(keys, self) + _set_parent_list(values, self) + + def kind(self) -> int: + return ASTKind.Dict + + def type_name(self) -> str: + return "Dict" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Dict(keys=") + pos = _dump_list(self.keys, buf, size, pos) + pos = _emit(buf, size, pos, ", values=") + pos = _dump_list(self.values, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Set Node +# ============================================================ + +class Set(ExprNode): + """Set(elts: list[AST | CPtr])""" + elts: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + elts: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Set] = pool.alloc(Set.__sizeof__()) + if ptr: + string.memset(ptr, 0, Set.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + elts: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.elts = elts + _set_parent_list(elts, self) + + def kind(self) -> int: + return ASTKind.Set + + def type_name(self) -> str: + return "Set" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Set(elts=") + pos = _dump_list(self.elts, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# ListComp Node +# ============================================================ + +class ListComp(ExprNode): + """ListComp(elt: AST, generators: list[AST | CPtr])""" + elt: rawptr[AST] + generators: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + elt: rawptr[AST], + generators: rawptr[list[rawptr[AST]]]): + ptr: rawptr[ListComp] = pool.alloc(ListComp.__sizeof__()) + if ptr: + string.memset(ptr, 0, ListComp.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + elt: rawptr[AST], + generators: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.elt = elt + self.generators = generators + _set_parent_list(generators, self) + + def kind(self) -> int: + return ASTKind.ListComp + + def type_name(self) -> str: + return "ListComp" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "ListComp(elt=") + if self.elt is not None: + pos = self.elt.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", generators=") + pos = _dump_list(self.generators, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# SetComp Node +# ============================================================ + +class SetComp(ExprNode): + """SetComp(elt: AST, generators: list[AST | CPtr])""" + elt: rawptr[AST] + generators: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + elt: rawptr[AST], + generators: rawptr[list[rawptr[AST]]]): + ptr: rawptr[SetComp] = pool.alloc(SetComp.__sizeof__()) + if ptr: + string.memset(ptr, 0, SetComp.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + elt: rawptr[AST], + generators: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.elt = elt + self.generators = generators + _set_parent_list(generators, self) + + def kind(self) -> int: + return ASTKind.SetComp + + def type_name(self) -> str: + return "SetComp" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "SetComp(elt=") + if self.elt is not None: + pos = self.elt.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", generators=") + pos = _dump_list(self.generators, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# DictComp Node +# ============================================================ + +class DictComp(ExprNode): + """DictComp(key: AST, value: AST, generators: list[AST | CPtr])""" + key: rawptr[AST] + value: rawptr[AST] + generators: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + key: rawptr[AST], value: rawptr[AST], + generators: rawptr[list[rawptr[AST]]]): + ptr: rawptr[DictComp] = pool.alloc(DictComp.__sizeof__()) + if ptr: + string.memset(ptr, 0, DictComp.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + key: rawptr[AST], value: rawptr[AST], + generators: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.key = key + self.value = value + self.generators = generators + _set_parent_list(generators, self) + + def kind(self) -> int: + return ASTKind.DictComp + + def type_name(self) -> str: + return "DictComp" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "DictComp(key=") + if self.key is not None: + pos = self.key.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", generators=") + pos = _dump_list(self.generators, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# GeneratorExp Node +# ============================================================ + +class GeneratorExp(ExprNode): + """GeneratorExp(elt: AST, generators: list[AST | CPtr])""" + elt: rawptr[AST] + generators: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + elt: rawptr[AST], + generators: rawptr[list[rawptr[AST]]]): + ptr: rawptr[GeneratorExp] = pool.alloc(GeneratorExp.__sizeof__()) + if ptr: + string.memset(ptr, 0, GeneratorExp.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + elt: rawptr[AST], + generators: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.elt = elt + self.generators = generators + _set_parent_list(generators, self) + + def kind(self) -> int: + return ASTKind.GeneratorExp + + def type_name(self) -> str: + return "GeneratorExp" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "GeneratorExp(elt=") + if self.elt is not None: + pos = self.elt.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", generators=") + pos = _dump_list(self.generators, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Await Node +# ============================================================ + +class Await(ExprNode): + """Await(value: AST)""" + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + ptr: rawptr[Await] = pool.alloc(Await.__sizeof__()) + if ptr: + string.memset(ptr, 0, Await.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.value = value + + def kind(self) -> int: + return ASTKind.Await + + def type_name(self) -> str: + return "Await" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Await(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Yield Node +# ============================================================ + +class Yield(ExprNode): + """Yield(value: AST)""" + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + ptr: rawptr[Yield] = pool.alloc(Yield.__sizeof__()) + if ptr: + string.memset(ptr, 0, Yield.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.value = value + + def kind(self) -> int: + return ASTKind.Yield + + def type_name(self) -> str: + return "Yield" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Yield(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# YieldFrom Node +# ============================================================ + +class YieldFrom(ExprNode): + """YieldFrom(value: AST)""" + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + ptr: rawptr[YieldFrom] = pool.alloc(YieldFrom.__sizeof__()) + if ptr: + string.memset(ptr, 0, YieldFrom.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.value = value + + def kind(self) -> int: + return ASTKind.YieldFrom + + def type_name(self) -> str: + return "YieldFrom" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "YieldFrom(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# FormattedValue Node +# ============================================================ + +class FormattedValue(ExprNode): + """FormattedValue(value: AST, conversion: CInt, format_spec: AST)""" + value: rawptr[AST] + conversion: int + format_spec: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + value: rawptr[AST], conversion: int, + format_spec: rawptr[AST]): + ptr: rawptr[FormattedValue] = pool.alloc(FormattedValue.__sizeof__()) + if ptr: + string.memset(ptr, 0, FormattedValue.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + value: rawptr[AST], conversion: int, + format_spec: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.value = value + self.conversion = conversion + self.format_spec = format_spec + + def kind(self) -> int: + return ASTKind.FormattedValue + + def type_name(self) -> str: + return "FormattedValue" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "FormattedValue(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", conversion=") + pos = _emit_int(buf, size, pos, int64(self.conversion)) + pos = _emit(buf, size, pos, ", format_spec=") + if self.format_spec is not None: + pos = self.format_spec.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# JoinedStr Node +# ============================================================ + +class JoinedStr(ExprNode): + """JoinedStr(values: list[AST | CPtr])""" + values: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + values: rawptr[list[rawptr[AST]]]): + ptr: rawptr[JoinedStr] = pool.alloc(JoinedStr.__sizeof__()) + if ptr: + string.memset(ptr, 0, JoinedStr.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + values: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.values = values + _set_parent_list(values, self) + + def kind(self) -> int: + return ASTKind.JoinedStr + + def type_name(self) -> str: + return "JoinedStr" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "JoinedStr(values=") + pos = _dump_list(self.values, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Attribute Node +# ============================================================ + +class Attribute(ExprNode): + """Attribute(value: AST, attr: str, ctx: CInt)""" + value: rawptr[AST] + attr: str + ctx: int + + def __new__(self, pool: rawptr[memhub.MemManager], + value: rawptr[AST], attr: str, ctx: int): + ptr: rawptr[Attribute] = pool.alloc(Attribute.__sizeof__()) + if ptr: + string.memset(ptr, 0, Attribute.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + value: rawptr[AST], attr: str, ctx: int): + _init_ast(self, pool, 0, 0) + self.value = value + self.attr = _copy_str(pool, attr) + self.ctx = ctx + + def kind(self) -> int: + return ASTKind.Attribute + + def type_name(self) -> str: + return "Attribute" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Attribute(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", attr='") + pos = _emit_str(buf, size, pos, self.attr) + pos = _emit(buf, size, pos, "', ctx=") + pos = _emit_int(buf, size, pos, int64(self.ctx)) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# 字段访问器(跨模块 stub 字段访问陷阱防护) +# +# 其他模块(如 TypeGenCType)直接访问 ast.Attribute 的字段时, +# TPC 可能按 stub 类型生成错误 GEP(读垃圾地址 → 0xC0000005 崩溃)。 +# 本模块是完整类型,字段偏移正确;跨模块调用方用这些访问器。 +# ============================================================ +def attribute_get_value(node: rawptr[Attribute]) -> rawptr[AST]: + """获取 Attribute.value 字段(跨模块访问器)""" + if node is None: + return None + return node.value + + +def attribute_get_attr(node: rawptr[Attribute]) -> str: + """获取 Attribute.attr 字段(跨模块访问器)""" + if node is None: + return None + return node.attr + + +# ============================================================ +# Subscript Node +# ============================================================ + +class Subscript(ExprNode): + """Subscript(value: AST, slice: AST, ctx: CInt)""" + value: rawptr[AST] + slice: rawptr[AST] + ctx: int + + def __new__(self, pool: rawptr[memhub.MemManager], + value: rawptr[AST], slice: rawptr[AST], ctx: int): + ptr: rawptr[Subscript] = pool.alloc(Subscript.__sizeof__()) + if ptr: + string.memset(ptr, 0, Subscript.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + value: rawptr[AST], slice: rawptr[AST], ctx: int): + _init_ast(self, pool, 0, 0) + self.value = value + self.slice = slice + self.ctx = ctx + + def kind(self) -> int: + return ASTKind.Subscript + + def type_name(self) -> str: + return "Subscript" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Subscript(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", slice=") + if self.slice is not None: + pos = self.slice.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", ctx=") + pos = _emit_int(buf, size, pos, int64(self.ctx)) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Starred Node +# ============================================================ + +class Starred(ExprNode): + """Starred(value: AST, ctx: CInt)""" + value: rawptr[AST] + ctx: int + + def __new__(self, pool: rawptr[memhub.MemManager], + value: rawptr[AST], ctx: int): + ptr: rawptr[Starred] = pool.alloc(Starred.__sizeof__()) + if ptr: + string.memset(ptr, 0, Starred.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + value: rawptr[AST], ctx: int): + _init_ast(self, pool, 0, 0) + self.value = value + self.ctx = ctx + + def kind(self) -> int: + return ASTKind.Starred + + def type_name(self) -> str: + return "Starred" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Starred(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", ctx=") + pos = _emit_int(buf, size, pos, int64(self.ctx)) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# List Node +# ============================================================ + +class List(ExprNode): + """List(elts: list[AST | CPtr], ctx: CInt)""" + elts: rawptr[list[rawptr[AST]]] + ctx: int + + def __new__(self, pool: rawptr[memhub.MemManager], + elts: rawptr[list[rawptr[AST]]], ctx: int): + ptr: rawptr[List] = pool.alloc(List.__sizeof__()) + if ptr: + string.memset(ptr, 0, List.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + elts: rawptr[list[rawptr[AST]]], ctx: int): + _init_ast(self, pool, 0, 0) + self.elts = elts + self.ctx = ctx + _set_parent_list(elts, self) + + def kind(self) -> int: + return ASTKind.List + + def type_name(self) -> str: + return "List" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "List(elts=") + pos = _dump_list(self.elts, buf, size, pos) + pos = _emit(buf, size, pos, ", ctx=") + pos = _emit_int(buf, size, pos, int64(self.ctx)) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Tuple Node +# ============================================================ + +class Tuple(ExprNode): + """Tuple(elts: list[AST | CPtr], ctx: CInt)""" + elts: rawptr[list[rawptr[AST]]] + ctx: int + + def __new__(self, pool: rawptr[memhub.MemManager], + elts: rawptr[list[rawptr[AST]]], ctx: int): + ptr: rawptr[Tuple] = pool.alloc(Tuple.__sizeof__()) + if ptr: + string.memset(ptr, 0, Tuple.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + elts: rawptr[list[rawptr[AST]]], ctx: int): + _init_ast(self, pool, 0, 0) + self.elts = elts + self.ctx = ctx + _set_parent_list(elts, self) + + def kind(self) -> int: + return ASTKind.Tuple + + def type_name(self) -> str: + return "Tuple" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Tuple(elts=") + pos = _dump_list(self.elts, buf, size, pos) + pos = _emit(buf, size, pos, ", ctx=") + pos = _emit_int(buf, size, pos, int64(self.ctx)) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Slice Node +# ============================================================ + +class Slice(ExprNode): + """Slice(lower: AST, upper: AST, step: AST)""" + lower: rawptr[AST] + upper: rawptr[AST] + step: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + lower: rawptr[AST], upper: rawptr[AST], + step: rawptr[AST]): + ptr: rawptr[Slice] = pool.alloc(Slice.__sizeof__()) + if ptr: + string.memset(ptr, 0, Slice.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + lower: rawptr[AST], upper: rawptr[AST], + step: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.lower = lower + self.upper = upper + self.step = step + + def kind(self) -> int: + return ASTKind.Slice + + def type_name(self) -> str: + return "Slice" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Slice(lower=") + if self.lower is not None: + pos = self.lower.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", upper=") + if self.upper is not None: + pos = self.upper.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", step=") + if self.step is not None: + pos = self.step.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# NamedExpr Node +# ============================================================ + +class NamedExpr(ExprNode): + """NamedExpr(target: AST, value: AST)""" + target: rawptr[AST] + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], value: rawptr[AST]): + ptr: rawptr[NamedExpr] = pool.alloc(NamedExpr.__sizeof__()) + if ptr: + string.memset(ptr, 0, NamedExpr.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.target = target + self.value = value + + def kind(self) -> int: + return ASTKind.NamedExpr + + def type_name(self) -> str: + return "NamedExpr" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "NamedExpr(target=") + if self.target is not None: + pos = self.target.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# OpNode Node +# ============================================================ + +class OpNode(AST): + """OpNode(op: CInt)""" + op: int + + def __new__(self, pool: rawptr[memhub.MemManager], op: int): + ptr: rawptr[OpNode] = pool.alloc(OpNode.__sizeof__()) + if ptr: + string.memset(ptr, 0, OpNode.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], op: int): + _init_ast(self, pool, 0, 0) + self.op = op + + def kind(self) -> int: + return ASTKind.OpNode + + def type_name(self) -> str: + return "OpNode" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "OpNode(op=") + pos = _emit(buf, size, pos, _op_name(self.op)) + pos = _emit(buf, size, pos, ")") + return pos diff --git a/ast/lexer.vp b/ast/lexer.vp index adfb893..71cc2eb 100644 --- a/ast/lexer.vp +++ b/ast/lexer.vp @@ -1,844 +1,844 @@ -import t, c -from stdint import * -import memhub -import string -from .tokens import ( - Token, TokenType, TokOp, _init_tables, _kw_lookup, _op_head, - OpEntry, new_token, token_set_str, token_set_str_literal, -) - - -# Maximum Indentation Stack Depth -LEXER_MAX_INDENT: t.CDefine = 256 - - -# ============================================================ -# Lexer State Structure -# ============================================================ -class Lexer: - src: str # Source code (NUL-terminated) - pos: t.CSizeT # Current position - len: t.CSizeT # Source code length - lineno: int # Current line number (1-based) - col: int # Current column number (0-based) - pool: rowptr[memhub.MemManager] # Memory pool - indent_stack: rowptr[int] # Indent stack (int array, allocated from pool) - indent_top: int # Stack top (-1 for empty stack) - paren_depth: int # Paren depth (>0 for no TokenType.NewLine) - tokens_head: rowptr[Token] # token list head - tokens_tail: rowptr[Token] # token list tail - at_line_start: int # 1=line start (need indent) - error_count: int # Error count - - def __new__(self, pool: rowptr[memhub.MemManager]): - ptr: rowptr[Lexer] = pool.alloc(Lexer.__sizeof__()) - if ptr: - string.memset(ptr, 0, Lexer.__sizeof__()) - return ptr - - -def new_lexer(pool: rowptr[memhub.MemManager]) -> rowptr[Lexer]: - """Allocate from the pool and return a Lexer (cross-module safe wrapper)""" - return Lexer(pool) - - -def _lexer_init(lx: rowptr[Lexer], src: str, - pool: rowptr[memhub.MemManager]): - """Initialize Lexer""" - if lx == None: return - lx.src = src - lx.pos = 0 - lx.len = string.strlen(src) if src != None else 0 - lx.lineno = 1 - lx.col = 0 - lx.pool = pool - # Allocate indentation stack - lx.indent_stack = pool.alloc(LEXER_MAX_INDENT * 8) - if lx.indent_stack != None: - string.memset(lx.indent_stack, 0, LEXER_MAX_INDENT * 8) - lx.indent_top = -1 - # Push 0 (base indent) - if lx.indent_stack != None: - lx.indent_stack[0] = 0 - lx.indent_top = 0 - lx.paren_depth = 0 - lx.tokens_head = None - lx.tokens_tail = None - lx.at_line_start = 1 - lx.error_count = 0 - _init_tables(pool) - - -# ============================================================ -# Auxiliary Functions -# ============================================================ - -def _peek(lx: rowptr[Lexer], offset: t.CSizeT) -> t.CChar: - """Peek character at position pos+offset, return '\0' if out of bounds""" - p: t.CSizeT = lx.pos + offset - if p >= lx.len: - return '\0' - return lx.src[p] - - -def _peek_cur(lx: rowptr[Lexer]) -> t.CChar: - if lx.pos >= lx.len: - return '\0' - return lx.src[lx.pos] - - -def _advance(lx: rowptr[Lexer]) -> t.CChar: - """Advance one character, return the character""" - if lx.pos >= lx.len: - return '\0' - ch: t.CChar = lx.src[lx.pos] - lx.pos += 1 - if ch == '\n': - lx.lineno += 1 - lx.col = 0 - else: - lx.col += 1 - return ch - - -def _is_alpha(ch: t.CChar) -> int: - if ch >= 'a' and ch <= 'z': return 1 - if ch >= 'A' and ch <= 'Z': return 1 - if ch == '_': return 1 - return 0 - - -def _is_digit(ch: t.CChar) -> int: - if ch >= '0' and ch <= '9': return 1 - return 0 - - -def _is_alnum(ch: t.CChar) -> int: - if _is_alpha(ch): return 1 - if _is_digit(ch): return 1 - return 0 - - -def _is_hex(ch: t.CChar) -> int: - if ch >= '0' and ch <= '9': return 1 - if ch >= 'a' and ch <= 'f': return 1 - if ch >= 'A' and ch <= 'F': return 1 - return 0 - - -def _hex_val(ch: t.CChar) -> int: - """十六进制字符转数值 0-15(供 \xNN 转义解析)""" - 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 - return 0 - - -def _is_space(ch: t.CChar) -> int: - if ch == ' ': return 1 - if ch == '\t': return 1 - if ch == '\f': return 1 - if ch == '\v': return 1 - if ch == '\r': return 1 - return 0 - - -def _emit(lx: rowptr[Lexer], tok: rowptr[Token]): - """Append token to token list""" - if tok == None: return - if lx.tokens_head == None: - lx.tokens_head = tok - lx.tokens_tail = tok - else: - lx.tokens_tail.next = tok - lx.tokens_tail = tok - - -def _make_op_token(lx: rowptr[Lexer], op_id: int, length: int, - lineno: int, col: int) -> rowptr[Token]: - tok: rowptr[Token] = new_token(lx.pool, TokenType.Op, lineno, col) - if tok == None: return None - tok.op_subtype = op_id - tok.end_col_offset = col + length - return tok - - -# ============================================================ -# Indent Handling -# ============================================================ - -def _handle_indent(lx: rowptr[Lexer]): - """Handle line start indent, generate TokenType.Indent/TokenType.Dedent. - Skip empty lines and comment lines.""" - # Skip empty lines and comment lines - while lx.pos < lx.len: - # Save line start position for non-empty lines - line_start_pos: t.CSizeT = lx.pos - line_start_col: int = lx.col - # Skip whitespace, not across lines - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if ch == ' ' or ch == '\t' or ch == '\f' or ch == '\v': - lx.pos += 1 - lx.col += 1 - elif ch == '\r': - # CRLF line end \r, skip \n to trigger empty line processing - lx.pos += 1 - else: - break - if lx.pos >= lx.len: - break - ch = lx.src[lx.pos] - if ch == '\n': - # Empty line, skip - lx.pos += 1 - lx.lineno += 1 - lx.col = 0 - continue - if ch == '\r': - # CRLF line end \r, skip \n to trigger empty line processing - lx.pos += 1 - if lx.pos < lx.len and lx.src[lx.pos] == '\n': - lx.pos += 1 - lx.lineno += 1 - lx.col = 0 - continue - if ch == '#': - # Comment line, skip to end of line - while lx.pos < lx.len and lx.src[lx.pos] != '\n': - lx.pos += 1 - continue - # Non-empty line: backtrack to line start to allow indent calculation - lx.pos = line_start_pos - lx.col = line_start_col - break - - if lx.pos >= lx.len: - # End of file, no indent to handle - lx.at_line_start = 0 - return - - # Calculate current line indent - indent: int = 0 - save_pos: t.CSizeT = lx.pos - save_col: int = lx.col - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if ch == ' ': - indent += 1 - lx.pos += 1 - lx.col += 1 - elif ch == '\t': - indent += 8 - (indent % 8) - lx.pos += 1 - lx.col += 1 - else: - break - - # If line end (empty line), skip indent calculation - if lx.pos < lx.len: - ch = lx.src[lx.pos] - if ch == '\n' or ch == '\r' or ch == '#': - # Empty line or comment line, backtrack position to allow loop to handle indent - lx.pos = save_pos - lx.col = save_col - lx.at_line_start = 0 - return - - # Compare indent with top of indent stack - top_indent: int = lx.indent_stack[lx.indent_top] - if indent > top_indent: - # TokenType.Indent - if lx.indent_top + 1 >= LEXER_MAX_INDENT: - lx.error_count += 1 - return - lx.indent_top += 1 - lx.indent_stack[lx.indent_top] = indent - tok: rowptr[Token] = new_token(lx.pool, TokenType.Indent, lx.lineno, 0) - _emit(lx, tok) - else: - while indent < lx.indent_stack[lx.indent_top] and lx.indent_top > 0: - lx.indent_top -= 1 - tok: rowptr[Token] = new_token(lx.pool, TokenType.Dedent, lx.lineno, 0) - _emit(lx, tok) - lx.at_line_start = 0 - - -# ============================================================ -# Identifier/Keyword Handling -# ============================================================ - -def _read_name(lx: rowptr[Lexer]): - start: t.CSizeT = lx.pos - start_lineno: int = lx.lineno - start_col: int = lx.col - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if _is_alnum(ch): - lx.pos += 1 - lx.col += 1 - else: - break - length: t.CSizeT = lx.pos - start - tok: rowptr[Token] = new_token(lx.pool, TokenType.Name, start_lineno, start_col) - if tok == None: return - token_set_str(lx.pool, tok, lx.src, start, length) - tok.end_lineno = lx.lineno - tok.end_col_offset = start_col + int(length) - # Look up keyword if any - kw_id: int = _kw_lookup(tok.str_val) - if kw_id: - tok.kw_subtype = kw_id - _emit(lx, tok) - - -# ============================================================ -# Number Handling -# ============================================================ - -def _read_number(lx: rowptr[Lexer]): - start: t.CSizeT = lx.pos - start_lineno: int = lx.lineno - start_col: int = lx.col - is_float: int = 0 - is_complex: int = 0 - - ch0: t.CChar = lx.src[lx.pos] - # Handle 0x/0o/0b prefix - if ch0 == '0' and lx.pos + 1 < lx.len: - ch1: t.CChar = lx.src[lx.pos + 1] - if ch1 == 'x' or ch1 == 'X': - lx.pos += 2 - lx.col += 2 - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if _is_hex(ch) or ch == '_': - lx.pos += 1 - lx.col += 1 - else: - break - _emit_number(lx, start, start_lineno, start_col, 0, 0, 0) - return - if ch1 == 'o' or ch1 == 'O': - lx.pos += 2 - lx.col += 2 - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if ch >= '0' and ch <= '7': - lx.pos += 1 - lx.col += 1 - elif ch == '_': - lx.pos += 1 - lx.col += 1 - else: - break - _emit_number(lx, start, start_lineno, start_col, 0, 0, 0) - return - if ch1 == 'b' or ch1 == 'B': - lx.pos += 2 - lx.col += 2 - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if ch == '0' or ch == '1' or ch == '_': - lx.pos += 1 - lx.col += 1 - else: - break - _emit_number(lx, start, start_lineno, start_col, 0, 0, 0) - return - - # Decimal integer/float - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if _is_digit(ch) or ch == '_': - lx.pos += 1 - lx.col += 1 - else: - break - - # Decimal part - if lx.pos < lx.len and lx.src[lx.pos] == '.': - is_float = 1 - lx.pos += 1 - lx.col += 1 - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if _is_digit(ch) or ch == '_': - lx.pos += 1 - lx.col += 1 - else: - break - - # Exponent part - if lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if ch == 'e' or ch == 'E': - is_float = 1 - lx.pos += 1 - lx.col += 1 - if lx.pos < lx.len: - ch = lx.src[lx.pos] - if ch == '+' or ch == '-': - lx.pos += 1 - lx.col += 1 - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if _is_digit(ch) or ch == '_': - lx.pos += 1 - lx.col += 1 - else: - break - - # Complex suffix part - if lx.pos < lx.len and lx.src[lx.pos] == 'j': - is_complex = 1 - is_float = 1 - lx.pos += 1 - lx.col += 1 - - _emit_number(lx, start, start_lineno, start_col, is_float, is_complex, 0) - - -def _emit_number(lx: rowptr[Lexer], start: t.CSizeT, - start_lineno: int, start_col: int, - is_float: int, is_complex: int, is_hex: int): - length: t.CSizeT = lx.pos - start - tok: rowptr[Token] = new_token(lx.pool, TokenType.Number, start_lineno, start_col) - if tok == None: return - token_set_str(lx.pool, tok, lx.src, start, length) - tok.is_float = is_float - tok.is_complex = is_complex - tok.end_lineno = lx.lineno - tok.end_col_offset = start_col + int(length) - # Parse number - if is_float: - tok.float_val = string.atof(tok.str_val) - else: - tok.int_val = string.atoll(tok.str_val) - _emit(lx, tok) - - -# ============================================================ -# String Handling -# ============================================================ - -def _read_string(lx: rowptr[Lexer]): - """Read string literal. - Supports prefixes: r, b, u, f, rb, br, rf, fr (case insensitive) - Supports quotes: ', ", ''', - Later we need to support string prefixes like i16/i24""" - start_lineno: int = lx.lineno - start_col: int = lx.col - prefix_start: t.CSizeT = lx.pos - - # Read prefix (r/b/u/f) - is_raw: int = 0 - is_bytes: int = 0 - is_fstring: int = 0 - while lx.pos < lx.len: - ch: t.CChar = lx.src[lx.pos] - if ch == 'r' or ch == 'R': - is_raw = 1 - lx.pos += 1 - lx.col += 1 - elif ch == 'b' or ch == 'B': - is_bytes = 1 - lx.pos += 1 - lx.col += 1 - elif ch == 'u' or ch == 'U': - lx.pos += 1 - lx.col += 1 - elif ch == 'f' or ch == 'F': - is_fstring = 1 - lx.pos += 1 - lx.col += 1 - else: - break - - if lx.pos >= lx.len: - lx.error_count += 1 - return - - # Determine quote type - quote: t.CChar = lx.src[lx.pos] - if quote != '\'' and quote != '"': - lx.error_count += 1 - return - - # Check triple quote - is_triple: int = 0 - if lx.pos + 2 < lx.len: - if lx.src[lx.pos] == quote and lx.src[lx.pos + 1] == quote and lx.src[lx.pos + 2] == quote: - is_triple = 1 - - content_start: t.CSizeT = lx.pos - if is_triple: - lx.pos += 3 - lx.col += 3 - else: - lx.pos += 1 - lx.col += 1 - - # Read string content - content_buf_start: t.CSizeT = lx.pos - # f-string {} 表达式块嵌套深度(PEP 701 同引号嵌套字符串支持用) - fstr_depth: int = 0 - while lx.pos < lx.len: - ch = lx.src[lx.pos] - if is_triple: - if ch == quote: - if lx.pos + 2 < lx.len: - if lx.src[lx.pos + 1] == quote and lx.src[lx.pos + 2] == quote: - break - elif lx.pos + 2 == lx.len: - break - if ch == '\\': - lx.pos += 1 - lx.col += 1 - if lx.pos < lx.len: - if lx.src[lx.pos] == '\n': - lx.lineno += 1 - lx.col = 0 - lx.pos += 1 - lx.col += 1 - continue - if ch == '\n': - lx.lineno += 1 - lx.col = 0 - lx.pos += 1 - lx.col += 1 - else: - if is_fstring: - # {{ 与 }} 是转义字面量,不参与深度计数 - if ch == '{' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '{': - lx.pos += 2 - lx.col += 2 - continue - if ch == '}' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '}': - lx.pos += 2 - lx.col += 2 - continue - if ch == '{': - fstr_depth += 1 - lx.pos += 1 - lx.col += 1 - continue - if ch == '}': - if fstr_depth > 0: - fstr_depth -= 1 - lx.pos += 1 - lx.col += 1 - continue - if fstr_depth > 0 and (ch == '\'' or ch == '"'): - # {} 表达式块内允许嵌套字符串字面量(PEP 701 同引号嵌套): - # 跳过到匹配引号,块内引号不终止外层 f-string。 - nq: t.CChar = ch - lx.pos += 1 - lx.col += 1 - while lx.pos < lx.len: - c2: t.CChar = lx.src[lx.pos] - if c2 == '\\': - lx.pos += 2 - lx.col += 2 - continue - if c2 == nq: - lx.pos += 1 - lx.col += 1 - break - if c2 == '\n': - # 单行字符串不能跨行 - lx.error_count += 1 - return - lx.pos += 1 - lx.col += 1 - continue - if ch == quote: - break - if ch == '\n': - # Single quote string cannot span multiple lines (unless continued) - lx.error_count += 1 - return - if ch == '\\' and not is_raw: - lx.pos += 1 - lx.col += 1 - if lx.pos < lx.len: - lx.pos += 1 - lx.col += 1 - continue - lx.pos += 1 - lx.col += 1 - - content_end: t.CSizeT = lx.pos - # Skip the closing quote - if is_triple: - if lx.pos + 2 < lx.len: - lx.pos += 3 - lx.col += 3 - else: - lx.pos = lx.len - else: - if lx.pos < lx.len: - lx.pos += 1 - lx.col += 1 - - # Create token - tok: rowptr[Token] = new_token(lx.pool, TokenType.String, start_lineno, start_col) - if tok == None: return - content_len: t.CSizeT = content_end - content_buf_start - # esc_len: Actual bytes after escaping (used to check single character) - esc_len: t.CSizeT = content_len - - # Handle escape sequences (non-raw strings) - if is_raw == 0 and content_len > 0: - # Allocate conversion buffer (max case: each char is plain, no extension) - conv_buf: str = lx.pool.alloc(content_len + 1) - if conv_buf == None: - token_set_str(lx.pool, tok, lx.src, content_buf_start, content_len) - else: - wpos: t.CSizeT = 0 - rpos: t.CSizeT = 0 - while rpos < content_len: - ch: t.CChar = lx.src[content_buf_start + rpos] - if ch == '\\' and rpos + 1 < content_len: - rpos += 1 - next_ch: t.CChar = lx.src[content_buf_start + rpos] - if next_ch == 'n': - conv_buf[wpos] = '\n' - elif next_ch == 't': - conv_buf[wpos] = '\t' - elif next_ch == 'r': - conv_buf[wpos] = '\r' - elif next_ch == 'f': - conv_buf[wpos] = '\f' - elif next_ch == 'v': - conv_buf[wpos] = '\v' - elif next_ch == 'a': - conv_buf[wpos] = '\a' - elif next_ch == 'b': - conv_buf[wpos] = '\b' - elif next_ch == '\\': - conv_buf[wpos] = '\\' - elif next_ch == '\'': - conv_buf[wpos] = '\'' - elif next_ch == '"': - conv_buf[wpos] = '"' - 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 根因)。 - h1: t.CChar = lx.src[content_buf_start + rpos + 1] - h2: t.CChar = lx.src[content_buf_start + rpos + 2] - if _is_hex(h1) != 0 and _is_hex(h2) != 0: - hex_v: int = (_hex_val(h1) << 4) | _hex_val(h2) - conv_buf[wpos] = t.CChar(hex_v) - wpos += 1 - rpos += 3 - continue - # 非法十六进制('\x' 后不足 2 位 hex):保留原样 - conv_buf[wpos] = '\\' - wpos += 1 - conv_buf[wpos] = next_ch - else: - # Unknown escape sequence, keep original (includes \xNN) - conv_buf[wpos] = '\\' - wpos += 1 - conv_buf[wpos] = next_ch - wpos += 1 - rpos += 1 - else: - conv_buf[wpos] = ch - wpos += 1 - rpos += 1 - conv_buf[wpos] = '\0' - token_set_str_literal(lx.pool, tok, conv_buf) - esc_len = wpos - else: - token_set_str(lx.pool, tok, lx.src, content_buf_start, content_len) - tok.end_lineno = lx.lineno - tok.end_col_offset = lx.col - # flags field: bit0=raw, bit1=bytes, bit2=fstring, bit3=triple, bit4=dquote, bit5=single char candidate - if is_raw: tok.kw_subtype = tok.kw_subtype | 1 - if is_bytes: tok.kw_subtype = tok.kw_subtype | 2 - if is_fstring: tok.kw_subtype = tok.kw_subtype | 4 - if is_triple: tok.kw_subtype = tok.kw_subtype | 8 - # bit4 (16) = double quote=single quote - if quote == '"': tok.kw_subtype = tok.kw_subtype | 16 - # bit5 (32) = single-character candidate (not triple quotes, not f-string, exactly 1 byte after escaping) - # Used for CONST_CHAR checks to prevent '0' and other NUL bytes from being mistaken as empty strings by strlen - if is_triple == 0 and is_fstring == 0 and esc_len == 1: - tok.kw_subtype = tok.kw_subtype | 32 - _emit(lx, tok) - - -# ============================================================ -# Operator Matching -# ============================================================ - -def _read_operator(lx: rowptr[Lexer]): - """Greedy match operator""" - start_lineno: int = lx.lineno - start_col: int = lx.col - remaining: t.CSizeT = lx.len - lx.pos - - # Traverse operator table, find longest match - best_entry: rowptr[OpEntry] = None - best_len: int = 0 - cur: rowptr[OpEntry] = _op_head - while cur != None: - if cur.length <= int(remaining): - # Compare cur.name with src[pos:pos+cur.length] - match: int = 1 - i: t.CSizeT = 0 - while i < t.CSizeT(cur.length): - if lx.src[lx.pos + i] != cur.name[i]: - match = 0 - break - i += 1 - if match: - if cur.length > best_len: - best_len = cur.length - best_entry = cur - cur = cur.next - - if best_entry == None: - # Unknown character, report error and skip - lx.error_count += 1 - lx.pos += 1 - lx.col += 1 - return - - # Update parenthesis depth - if best_entry.op_id == TokOp.LPar or best_entry.op_id == TokOp.Lsqb or best_entry.op_id == TokOp.LBrace: - lx.paren_depth += 1 - elif best_entry.op_id == TokOp.RPar or best_entry.op_id == TokOp.Rsqb or best_entry.op_id == TokOp.RBrace: - if lx.paren_depth > 0: - lx.paren_depth -= 1 - - tok: rowptr[Token] = _make_op_token(lx, best_entry.op_id, best_entry.length, - start_lineno, start_col) - if tok == None: return - token_set_str(lx.pool, tok, lx.src, lx.pos, t.CSizeT(best_len)) - lx.pos += t.CSizeT(best_len) - lx.col += best_len - _emit(lx, tok) - - -# ============================================================ -# Main Lexical Analysis Entry Point -# ============================================================ - -def tokenize(lx: rowptr[Lexer]) -> rowptr[Token]: - """Execute tokenization, return token list head""" - if lx == None: return None - while lx.pos < lx.len: - # Handle line start indent - if lx.at_line_start and lx.paren_depth == 0: - _handle_indent(lx) - if lx.pos >= lx.len: - break - - ch: t.CChar = _peek_cur(lx) - if ch == '\0': - break - - # New line - if ch == '\n': - if lx.paren_depth > 0: - # # In bi brackets, generates TokenType.Nl - tok: rowptr[Token] = new_token(lx.pool, TokenType.Nl, lx.lineno, lx.col) - _emit(lx, tok) - else: - # Logical new line - tok: rowptr[Token] = new_token(lx.pool, TokenType.NewLine, lx.lineno, lx.col) - _emit(lx, tok) - lx.at_line_start = 1 - lx.pos += 1 - lx.lineno += 1 - lx.col = 0 - continue - - # Backslash continuation line - # 支持 LF 与 CRLF(Windows 文件行尾):CRLF 下 `\` 后是 '\r'(非 '\n'), - # 只查 '\n' 会失效 → `\` 被当未知字符跳过、'\n' 产生 NEWLINE → 表达式 - # 中断(VpiReader.vp L324 `or \` 行尾续行根因,CRLF 文件)。 - if ch == '\\' and lx.pos + 1 < lx.len: - if lx.src[lx.pos + 1] == '\n': - lx.pos += 2 - lx.lineno += 1 - lx.col = 0 - continue - if lx.src[lx.pos + 1] == '\r': - # \ + \r\n:跳 \、\r、\n - lx.pos += 2 - if lx.pos < lx.len and lx.src[lx.pos] == '\n': - lx.pos += 1 - lx.lineno += 1 - lx.col = 0 - continue - - # Whitespace - if _is_space(ch): - lx.pos += 1 - lx.col += 1 - continue - - # Comment - if ch == '#': - while lx.pos < lx.len and lx.src[lx.pos] != '\n': - lx.pos += 1 - continue - - # String prefixes (like r"...") or strings - if ch == '\'' or ch == '"': - _read_string(lx) - continue - # A string prefix letter followed by a quote (must be checked before _is_alpha, otherwise f/r/b/u will be treated as TokenType.Name) - if (ch == 'r' or ch == 'R' or ch == 'b' or ch == 'B' or - ch == 'u' or ch == 'U' or ch == 'f' or ch == 'F'): - if lx.pos + 1 < lx.len: - ch1: t.CChar = lx.src[lx.pos + 1] - if ch1 == '\'' or ch1 == '"': - _read_string(lx) - continue - # Double prefixes like rb, br, rf, fr - if lx.pos + 2 < lx.len: - ch2: t.CChar = lx.src[lx.pos + 2] - if ((ch1 == 'r' or ch1 == 'R' or ch1 == 'b' or ch1 == 'B' or - ch1 == 'f' or ch1 == 'F' or ch1 == 'u' or ch1 == 'U') and - (ch2 == '\'' or ch2 == '"')): - _read_string(lx) - continue - - # Identifier/keyword - if _is_alpha(ch): - _read_name(lx) - continue - - # Number - if _is_digit(ch): - _read_number(lx) - continue - - # Operator - _read_operator(lx) - - # End of file: generate TokenType.NewLine (if there's content) - if lx.tokens_tail != None: - if lx.tokens_tail.type != TokenType.NewLine and lx.tokens_tail.type != TokenType.Nl: - tok: rowptr[Token] = new_token(lx.pool, TokenType.NewLine, lx.lineno, lx.col) - _emit(lx, tok) - - # Generate remaining remaining TokenType.Dedent - while lx.indent_top > 0: - lx.indent_top -= 1 - tok: rowptr[Token] = new_token(lx.pool, TokenType.Dedent, lx.lineno, 0) - _emit(lx, tok) - - # TokenType.EndMarker - tok: rowptr[Token] = new_token(lx.pool, TokenType.EndMarker, lx.lineno, 0) - _emit(lx, tok) - - return lx.tokens_head +import t, c +from stdint import * +import memhub +import string +from .tokens import ( + Token, TokenType, TokOp, _init_tables, _kw_lookup, _op_head, + OpEntry, new_token, token_set_str, token_set_str_literal, +) + + +# Maximum Indentation Stack Depth +LEXER_MAX_INDENT: t.CDefine = 256 + + +# ============================================================ +# Lexer State Structure +# ============================================================ +class Lexer: + src: str # Source code (NUL-terminated) + pos: t.CSizeT # Current position + len: t.CSizeT # Source code length + lineno: int # Current line number (1-based) + col: int # Current column number (0-based) + pool: rawptr[memhub.MemManager] # Memory pool + indent_stack: rawptr[int] # Indent stack (int array, allocated from pool) + indent_top: int # Stack top (-1 for empty stack) + paren_depth: int # Paren depth (>0 for no TokenType.NewLine) + tokens_head: rawptr[Token] # token list head + tokens_tail: rawptr[Token] # token list tail + at_line_start: int # 1=line start (need indent) + error_count: int # Error count + + def __new__(self, pool: rawptr[memhub.MemManager]): + ptr: rawptr[Lexer] = pool.alloc(Lexer.__sizeof__()) + if ptr: + string.memset(ptr, 0, Lexer.__sizeof__()) + return ptr + + +def new_lexer(pool: rawptr[memhub.MemManager]) -> rawptr[Lexer]: + """Allocate from the pool and return a Lexer (cross-module safe wrapper)""" + return Lexer(pool) + + +def _lexer_init(lx: rawptr[Lexer], src: str, + pool: rawptr[memhub.MemManager]): + """Initialize Lexer""" + if lx == None: return + lx.src = src + lx.pos = 0 + lx.len = string.strlen(src) if src != None else 0 + lx.lineno = 1 + lx.col = 0 + lx.pool = pool + # Allocate indentation stack + lx.indent_stack = pool.alloc(LEXER_MAX_INDENT * 8) + if lx.indent_stack != None: + string.memset(lx.indent_stack, 0, LEXER_MAX_INDENT * 8) + lx.indent_top = -1 + # Push 0 (base indent) + if lx.indent_stack != None: + lx.indent_stack[0] = 0 + lx.indent_top = 0 + lx.paren_depth = 0 + lx.tokens_head = None + lx.tokens_tail = None + lx.at_line_start = 1 + lx.error_count = 0 + _init_tables(pool) + + +# ============================================================ +# Auxiliary Functions +# ============================================================ + +def _peek(lx: rawptr[Lexer], offset: t.CSizeT) -> t.CChar: + """Peek character at position pos+offset, return '\0' if out of bounds""" + p: t.CSizeT = lx.pos + offset + if p >= lx.len: + return '\0' + return lx.src[p] + + +def _peek_cur(lx: rawptr[Lexer]) -> t.CChar: + if lx.pos >= lx.len: + return '\0' + return lx.src[lx.pos] + + +def _advance(lx: rawptr[Lexer]) -> t.CChar: + """Advance one character, return the character""" + if lx.pos >= lx.len: + return '\0' + ch: t.CChar = lx.src[lx.pos] + lx.pos += 1 + if ch == '\n': + lx.lineno += 1 + lx.col = 0 + else: + lx.col += 1 + return ch + + +def _is_alpha(ch: t.CChar) -> int: + if ch >= 'a' and ch <= 'z': return 1 + if ch >= 'A' and ch <= 'Z': return 1 + if ch == '_': return 1 + return 0 + + +def _is_digit(ch: t.CChar) -> int: + if ch >= '0' and ch <= '9': return 1 + return 0 + + +def _is_alnum(ch: t.CChar) -> int: + if _is_alpha(ch): return 1 + if _is_digit(ch): return 1 + return 0 + + +def _is_hex(ch: t.CChar) -> int: + if ch >= '0' and ch <= '9': return 1 + if ch >= 'a' and ch <= 'f': return 1 + if ch >= 'A' and ch <= 'F': return 1 + return 0 + + +def _hex_val(ch: t.CChar) -> int: + """十六进制字符转数值 0-15(供 \xNN 转义解析)""" + 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 + return 0 + + +def _is_space(ch: t.CChar) -> int: + if ch == ' ': return 1 + if ch == '\t': return 1 + if ch == '\f': return 1 + if ch == '\v': return 1 + if ch == '\r': return 1 + return 0 + + +def _emit(lx: rawptr[Lexer], tok: rawptr[Token]): + """Append token to token list""" + if tok == None: return + if lx.tokens_head == None: + lx.tokens_head = tok + lx.tokens_tail = tok + else: + lx.tokens_tail.next = tok + lx.tokens_tail = tok + + +def _make_op_token(lx: rawptr[Lexer], op_id: int, length: int, + lineno: int, col: int) -> rawptr[Token]: + tok: rawptr[Token] = new_token(lx.pool, TokenType.Op, lineno, col) + if tok == None: return None + tok.op_subtype = op_id + tok.end_col_offset = col + length + return tok + + +# ============================================================ +# Indent Handling +# ============================================================ + +def _handle_indent(lx: rawptr[Lexer]): + """Handle line start indent, generate TokenType.Indent/TokenType.Dedent. + Skip empty lines and comment lines.""" + # Skip empty lines and comment lines + while lx.pos < lx.len: + # Save line start position for non-empty lines + line_start_pos: t.CSizeT = lx.pos + line_start_col: int = lx.col + # Skip whitespace, not across lines + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if ch == ' ' or ch == '\t' or ch == '\f' or ch == '\v': + lx.pos += 1 + lx.col += 1 + elif ch == '\r': + # CRLF line end \r, skip \n to trigger empty line processing + lx.pos += 1 + else: + break + if lx.pos >= lx.len: + break + ch = lx.src[lx.pos] + if ch == '\n': + # Empty line, skip + lx.pos += 1 + lx.lineno += 1 + lx.col = 0 + continue + if ch == '\r': + # CRLF line end \r, skip \n to trigger empty line processing + lx.pos += 1 + if lx.pos < lx.len and lx.src[lx.pos] == '\n': + lx.pos += 1 + lx.lineno += 1 + lx.col = 0 + continue + if ch == '#': + # Comment line, skip to end of line + while lx.pos < lx.len and lx.src[lx.pos] != '\n': + lx.pos += 1 + continue + # Non-empty line: backtrack to line start to allow indent calculation + lx.pos = line_start_pos + lx.col = line_start_col + break + + if lx.pos >= lx.len: + # End of file, no indent to handle + lx.at_line_start = 0 + return + + # Calculate current line indent + indent: int = 0 + save_pos: t.CSizeT = lx.pos + save_col: int = lx.col + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if ch == ' ': + indent += 1 + lx.pos += 1 + lx.col += 1 + elif ch == '\t': + indent += 8 - (indent % 8) + lx.pos += 1 + lx.col += 1 + else: + break + + # If line end (empty line), skip indent calculation + if lx.pos < lx.len: + ch = lx.src[lx.pos] + if ch == '\n' or ch == '\r' or ch == '#': + # Empty line or comment line, backtrack position to allow loop to handle indent + lx.pos = save_pos + lx.col = save_col + lx.at_line_start = 0 + return + + # Compare indent with top of indent stack + top_indent: int = lx.indent_stack[lx.indent_top] + if indent > top_indent: + # TokenType.Indent + if lx.indent_top + 1 >= LEXER_MAX_INDENT: + lx.error_count += 1 + return + lx.indent_top += 1 + lx.indent_stack[lx.indent_top] = indent + tok: rawptr[Token] = new_token(lx.pool, TokenType.Indent, lx.lineno, 0) + _emit(lx, tok) + else: + while indent < lx.indent_stack[lx.indent_top] and lx.indent_top > 0: + lx.indent_top -= 1 + tok: rawptr[Token] = new_token(lx.pool, TokenType.Dedent, lx.lineno, 0) + _emit(lx, tok) + lx.at_line_start = 0 + + +# ============================================================ +# Identifier/Keyword Handling +# ============================================================ + +def _read_name(lx: rawptr[Lexer]): + start: t.CSizeT = lx.pos + start_lineno: int = lx.lineno + start_col: int = lx.col + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if _is_alnum(ch): + lx.pos += 1 + lx.col += 1 + else: + break + length: t.CSizeT = lx.pos - start + tok: rawptr[Token] = new_token(lx.pool, TokenType.Name, start_lineno, start_col) + if tok == None: return + token_set_str(lx.pool, tok, lx.src, start, length) + tok.end_lineno = lx.lineno + tok.end_col_offset = start_col + int(length) + # Look up keyword if any + kw_id: int = _kw_lookup(tok.str_val) + if kw_id: + tok.kw_subtype = kw_id + _emit(lx, tok) + + +# ============================================================ +# Number Handling +# ============================================================ + +def _read_number(lx: rawptr[Lexer]): + start: t.CSizeT = lx.pos + start_lineno: int = lx.lineno + start_col: int = lx.col + is_float: int = 0 + is_complex: int = 0 + + ch0: t.CChar = lx.src[lx.pos] + # Handle 0x/0o/0b prefix + if ch0 == '0' and lx.pos + 1 < lx.len: + ch1: t.CChar = lx.src[lx.pos + 1] + if ch1 == 'x' or ch1 == 'X': + lx.pos += 2 + lx.col += 2 + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if _is_hex(ch) or ch == '_': + lx.pos += 1 + lx.col += 1 + else: + break + _emit_number(lx, start, start_lineno, start_col, 0, 0, 0) + return + if ch1 == 'o' or ch1 == 'O': + lx.pos += 2 + lx.col += 2 + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if ch >= '0' and ch <= '7': + lx.pos += 1 + lx.col += 1 + elif ch == '_': + lx.pos += 1 + lx.col += 1 + else: + break + _emit_number(lx, start, start_lineno, start_col, 0, 0, 0) + return + if ch1 == 'b' or ch1 == 'B': + lx.pos += 2 + lx.col += 2 + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if ch == '0' or ch == '1' or ch == '_': + lx.pos += 1 + lx.col += 1 + else: + break + _emit_number(lx, start, start_lineno, start_col, 0, 0, 0) + return + + # Decimal integer/float + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if _is_digit(ch) or ch == '_': + lx.pos += 1 + lx.col += 1 + else: + break + + # Decimal part + if lx.pos < lx.len and lx.src[lx.pos] == '.': + is_float = 1 + lx.pos += 1 + lx.col += 1 + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if _is_digit(ch) or ch == '_': + lx.pos += 1 + lx.col += 1 + else: + break + + # Exponent part + if lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if ch == 'e' or ch == 'E': + is_float = 1 + lx.pos += 1 + lx.col += 1 + if lx.pos < lx.len: + ch = lx.src[lx.pos] + if ch == '+' or ch == '-': + lx.pos += 1 + lx.col += 1 + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if _is_digit(ch) or ch == '_': + lx.pos += 1 + lx.col += 1 + else: + break + + # Complex suffix part + if lx.pos < lx.len and lx.src[lx.pos] == 'j': + is_complex = 1 + is_float = 1 + lx.pos += 1 + lx.col += 1 + + _emit_number(lx, start, start_lineno, start_col, is_float, is_complex, 0) + + +def _emit_number(lx: rawptr[Lexer], start: t.CSizeT, + start_lineno: int, start_col: int, + is_float: int, is_complex: int, is_hex: int): + length: t.CSizeT = lx.pos - start + tok: rawptr[Token] = new_token(lx.pool, TokenType.Number, start_lineno, start_col) + if tok == None: return + token_set_str(lx.pool, tok, lx.src, start, length) + tok.is_float = is_float + tok.is_complex = is_complex + tok.end_lineno = lx.lineno + tok.end_col_offset = start_col + int(length) + # Parse number + if is_float: + tok.float_val = string.atof(tok.str_val) + else: + tok.int_val = string.atoll(tok.str_val) + _emit(lx, tok) + + +# ============================================================ +# String Handling +# ============================================================ + +def _read_string(lx: rawptr[Lexer]): + """Read string literal. + Supports prefixes: r, b, u, f, rb, br, rf, fr (case insensitive) + Supports quotes: ', ", ''', + Later we need to support string prefixes like i16/i24""" + start_lineno: int = lx.lineno + start_col: int = lx.col + prefix_start: t.CSizeT = lx.pos + + # Read prefix (r/b/u/f) + is_raw: int = 0 + is_bytes: int = 0 + is_fstring: int = 0 + while lx.pos < lx.len: + ch: t.CChar = lx.src[lx.pos] + if ch == 'r' or ch == 'R': + is_raw = 1 + lx.pos += 1 + lx.col += 1 + elif ch == 'b' or ch == 'B': + is_bytes = 1 + lx.pos += 1 + lx.col += 1 + elif ch == 'u' or ch == 'U': + lx.pos += 1 + lx.col += 1 + elif ch == 'f' or ch == 'F': + is_fstring = 1 + lx.pos += 1 + lx.col += 1 + else: + break + + if lx.pos >= lx.len: + lx.error_count += 1 + return + + # Determine quote type + quote: t.CChar = lx.src[lx.pos] + if quote != '\'' and quote != '"': + lx.error_count += 1 + return + + # Check triple quote + is_triple: int = 0 + if lx.pos + 2 < lx.len: + if lx.src[lx.pos] == quote and lx.src[lx.pos + 1] == quote and lx.src[lx.pos + 2] == quote: + is_triple = 1 + + content_start: t.CSizeT = lx.pos + if is_triple: + lx.pos += 3 + lx.col += 3 + else: + lx.pos += 1 + lx.col += 1 + + # Read string content + content_buf_start: t.CSizeT = lx.pos + # f-string {} 表达式块嵌套深度(PEP 701 同引号嵌套字符串支持用) + fstr_depth: int = 0 + while lx.pos < lx.len: + ch = lx.src[lx.pos] + if is_triple: + if ch == quote: + if lx.pos + 2 < lx.len: + if lx.src[lx.pos + 1] == quote and lx.src[lx.pos + 2] == quote: + break + elif lx.pos + 2 == lx.len: + break + if ch == '\\': + lx.pos += 1 + lx.col += 1 + if lx.pos < lx.len: + if lx.src[lx.pos] == '\n': + lx.lineno += 1 + lx.col = 0 + lx.pos += 1 + lx.col += 1 + continue + if ch == '\n': + lx.lineno += 1 + lx.col = 0 + lx.pos += 1 + lx.col += 1 + else: + if is_fstring: + # {{ 与 }} 是转义字面量,不参与深度计数 + if ch == '{' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '{': + lx.pos += 2 + lx.col += 2 + continue + if ch == '}' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '}': + lx.pos += 2 + lx.col += 2 + continue + if ch == '{': + fstr_depth += 1 + lx.pos += 1 + lx.col += 1 + continue + if ch == '}': + if fstr_depth > 0: + fstr_depth -= 1 + lx.pos += 1 + lx.col += 1 + continue + if fstr_depth > 0 and (ch == '\'' or ch == '"'): + # {} 表达式块内允许嵌套字符串字面量(PEP 701 同引号嵌套): + # 跳过到匹配引号,块内引号不终止外层 f-string。 + nq: t.CChar = ch + lx.pos += 1 + lx.col += 1 + while lx.pos < lx.len: + c2: t.CChar = lx.src[lx.pos] + if c2 == '\\': + lx.pos += 2 + lx.col += 2 + continue + if c2 == nq: + lx.pos += 1 + lx.col += 1 + break + if c2 == '\n': + # 单行字符串不能跨行 + lx.error_count += 1 + return + lx.pos += 1 + lx.col += 1 + continue + if ch == quote: + break + if ch == '\n': + # Single quote string cannot span multiple lines (unless continued) + lx.error_count += 1 + return + if ch == '\\' and not is_raw: + lx.pos += 1 + lx.col += 1 + if lx.pos < lx.len: + lx.pos += 1 + lx.col += 1 + continue + lx.pos += 1 + lx.col += 1 + + content_end: t.CSizeT = lx.pos + # Skip the closing quote + if is_triple: + if lx.pos + 2 < lx.len: + lx.pos += 3 + lx.col += 3 + else: + lx.pos = lx.len + else: + if lx.pos < lx.len: + lx.pos += 1 + lx.col += 1 + + # Create token + tok: rawptr[Token] = new_token(lx.pool, TokenType.String, start_lineno, start_col) + if tok == None: return + content_len: t.CSizeT = content_end - content_buf_start + # esc_len: Actual bytes after escaping (used to check single character) + esc_len: t.CSizeT = content_len + + # Handle escape sequences (non-raw strings) + if is_raw == 0 and content_len > 0: + # Allocate conversion buffer (max case: each char is plain, no extension) + conv_buf: str = lx.pool.alloc(content_len + 1) + if conv_buf == None: + token_set_str(lx.pool, tok, lx.src, content_buf_start, content_len) + else: + wpos: t.CSizeT = 0 + rpos: t.CSizeT = 0 + while rpos < content_len: + ch: t.CChar = lx.src[content_buf_start + rpos] + if ch == '\\' and rpos + 1 < content_len: + rpos += 1 + next_ch: t.CChar = lx.src[content_buf_start + rpos] + if next_ch == 'n': + conv_buf[wpos] = '\n' + elif next_ch == 't': + conv_buf[wpos] = '\t' + elif next_ch == 'r': + conv_buf[wpos] = '\r' + elif next_ch == 'f': + conv_buf[wpos] = '\f' + elif next_ch == 'v': + conv_buf[wpos] = '\v' + elif next_ch == 'a': + conv_buf[wpos] = '\a' + elif next_ch == 'b': + conv_buf[wpos] = '\b' + elif next_ch == '\\': + conv_buf[wpos] = '\\' + elif next_ch == '\'': + conv_buf[wpos] = '\'' + elif next_ch == '"': + conv_buf[wpos] = '"' + 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 根因)。 + h1: t.CChar = lx.src[content_buf_start + rpos + 1] + h2: t.CChar = lx.src[content_buf_start + rpos + 2] + if _is_hex(h1) != 0 and _is_hex(h2) != 0: + hex_v: int = (_hex_val(h1) << 4) | _hex_val(h2) + conv_buf[wpos] = t.CChar(hex_v) + wpos += 1 + rpos += 3 + continue + # 非法十六进制('\x' 后不足 2 位 hex):保留原样 + conv_buf[wpos] = '\\' + wpos += 1 + conv_buf[wpos] = next_ch + else: + # Unknown escape sequence, keep original (includes \xNN) + conv_buf[wpos] = '\\' + wpos += 1 + conv_buf[wpos] = next_ch + wpos += 1 + rpos += 1 + else: + conv_buf[wpos] = ch + wpos += 1 + rpos += 1 + conv_buf[wpos] = '\0' + token_set_str_literal(lx.pool, tok, conv_buf) + esc_len = wpos + else: + token_set_str(lx.pool, tok, lx.src, content_buf_start, content_len) + tok.end_lineno = lx.lineno + tok.end_col_offset = lx.col + # flags field: bit0=raw, bit1=bytes, bit2=fstring, bit3=triple, bit4=dquote, bit5=single char candidate + if is_raw: tok.kw_subtype = tok.kw_subtype | 1 + if is_bytes: tok.kw_subtype = tok.kw_subtype | 2 + if is_fstring: tok.kw_subtype = tok.kw_subtype | 4 + if is_triple: tok.kw_subtype = tok.kw_subtype | 8 + # bit4 (16) = double quote=single quote + if quote == '"': tok.kw_subtype = tok.kw_subtype | 16 + # bit5 (32) = single-character candidate (not triple quotes, not f-string, exactly 1 byte after escaping) + # Used for CONST_CHAR checks to prevent '0' and other NUL bytes from being mistaken as empty strings by strlen + if is_triple == 0 and is_fstring == 0 and esc_len == 1: + tok.kw_subtype = tok.kw_subtype | 32 + _emit(lx, tok) + + +# ============================================================ +# Operator Matching +# ============================================================ + +def _read_operator(lx: rawptr[Lexer]): + """Greedy match operator""" + start_lineno: int = lx.lineno + start_col: int = lx.col + remaining: t.CSizeT = lx.len - lx.pos + + # Traverse operator table, find longest match + best_entry: rawptr[OpEntry] = None + best_len: int = 0 + cur: rawptr[OpEntry] = _op_head + while cur != None: + if cur.length <= int(remaining): + # Compare cur.name with src[pos:pos+cur.length] + match: int = 1 + i: t.CSizeT = 0 + while i < t.CSizeT(cur.length): + if lx.src[lx.pos + i] != cur.name[i]: + match = 0 + break + i += 1 + if match: + if cur.length > best_len: + best_len = cur.length + best_entry = cur + cur = cur.next + + if best_entry == None: + # Unknown character, report error and skip + lx.error_count += 1 + lx.pos += 1 + lx.col += 1 + return + + # Update parenthesis depth + if best_entry.op_id == TokOp.LPar or best_entry.op_id == TokOp.Lsqb or best_entry.op_id == TokOp.LBrace: + lx.paren_depth += 1 + elif best_entry.op_id == TokOp.RPar or best_entry.op_id == TokOp.Rsqb or best_entry.op_id == TokOp.RBrace: + if lx.paren_depth > 0: + lx.paren_depth -= 1 + + tok: rawptr[Token] = _make_op_token(lx, best_entry.op_id, best_entry.length, + start_lineno, start_col) + if tok == None: return + token_set_str(lx.pool, tok, lx.src, lx.pos, t.CSizeT(best_len)) + lx.pos += t.CSizeT(best_len) + lx.col += best_len + _emit(lx, tok) + + +# ============================================================ +# Main Lexical Analysis Entry Point +# ============================================================ + +def tokenize(lx: rawptr[Lexer]) -> rawptr[Token]: + """Execute tokenization, return token list head""" + if lx == None: return None + while lx.pos < lx.len: + # Handle line start indent + if lx.at_line_start and lx.paren_depth == 0: + _handle_indent(lx) + if lx.pos >= lx.len: + break + + ch: t.CChar = _peek_cur(lx) + if ch == '\0': + break + + # New line + if ch == '\n': + if lx.paren_depth > 0: + # # In bi brackets, generates TokenType.Nl + tok: rawptr[Token] = new_token(lx.pool, TokenType.Nl, lx.lineno, lx.col) + _emit(lx, tok) + else: + # Logical new line + tok: rawptr[Token] = new_token(lx.pool, TokenType.NewLine, lx.lineno, lx.col) + _emit(lx, tok) + lx.at_line_start = 1 + lx.pos += 1 + lx.lineno += 1 + lx.col = 0 + continue + + # Backslash continuation line + # 支持 LF 与 CRLF(Windows 文件行尾):CRLF 下 `\` 后是 '\r'(非 '\n'), + # 只查 '\n' 会失效 → `\` 被当未知字符跳过、'\n' 产生 NEWLINE → 表达式 + # 中断(VpiReader.vp L324 `or \` 行尾续行根因,CRLF 文件)。 + if ch == '\\' and lx.pos + 1 < lx.len: + if lx.src[lx.pos + 1] == '\n': + lx.pos += 2 + lx.lineno += 1 + lx.col = 0 + continue + if lx.src[lx.pos + 1] == '\r': + # \ + \r\n:跳 \、\r、\n + lx.pos += 2 + if lx.pos < lx.len and lx.src[lx.pos] == '\n': + lx.pos += 1 + lx.lineno += 1 + lx.col = 0 + continue + + # Whitespace + if _is_space(ch): + lx.pos += 1 + lx.col += 1 + continue + + # Comment + if ch == '#': + while lx.pos < lx.len and lx.src[lx.pos] != '\n': + lx.pos += 1 + continue + + # String prefixes (like r"...") or strings + if ch == '\'' or ch == '"': + _read_string(lx) + continue + # A string prefix letter followed by a quote (must be checked before _is_alpha, otherwise f/r/b/u will be treated as TokenType.Name) + if (ch == 'r' or ch == 'R' or ch == 'b' or ch == 'B' or + ch == 'u' or ch == 'U' or ch == 'f' or ch == 'F'): + if lx.pos + 1 < lx.len: + ch1: t.CChar = lx.src[lx.pos + 1] + if ch1 == '\'' or ch1 == '"': + _read_string(lx) + continue + # Double prefixes like rb, br, rf, fr + if lx.pos + 2 < lx.len: + ch2: t.CChar = lx.src[lx.pos + 2] + if ((ch1 == 'r' or ch1 == 'R' or ch1 == 'b' or ch1 == 'B' or + ch1 == 'f' or ch1 == 'F' or ch1 == 'u' or ch1 == 'U') and + (ch2 == '\'' or ch2 == '"')): + _read_string(lx) + continue + + # Identifier/keyword + if _is_alpha(ch): + _read_name(lx) + continue + + # Number + if _is_digit(ch): + _read_number(lx) + continue + + # Operator + _read_operator(lx) + + # End of file: generate TokenType.NewLine (if there's content) + if lx.tokens_tail != None: + if lx.tokens_tail.type != TokenType.NewLine and lx.tokens_tail.type != TokenType.Nl: + tok: rawptr[Token] = new_token(lx.pool, TokenType.NewLine, lx.lineno, lx.col) + _emit(lx, tok) + + # Generate remaining remaining TokenType.Dedent + while lx.indent_top > 0: + lx.indent_top -= 1 + tok: rawptr[Token] = new_token(lx.pool, TokenType.Dedent, lx.lineno, 0) + _emit(lx, tok) + + # TokenType.EndMarker + tok: rawptr[Token] = new_token(lx.pool, TokenType.EndMarker, lx.lineno, 0) + _emit(lx, tok) + + return lx.tokens_head diff --git a/ast/match.vp b/ast/match.vp index 733c6b7..3d711a2 100644 --- a/ast/match.vp +++ b/ast/match.vp @@ -1,363 +1,363 @@ -import t, c -from stdint import * -import memhub -import string -from .base import ( - AST, ASTKind, - _init_ast, _copy_str, _set_parent_list, - _emit, _emit_str, _emit_int, _dump_list, -) - - -# ============================================================ -# MatchCase 节点Node -# ============================================================ - -class MatchCase(AST): - """MatchCase(pattern: AST, guard: AST)""" - pattern: rowptr[AST] - guard: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - pattern: rowptr[AST], guard: rowptr[AST]): - ptr: rowptr[MatchCase] = pool.alloc(MatchCase.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchCase.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - pattern: rowptr[AST], guard: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.pattern = pattern - self.guard = guard - - def kind(self) -> int: - return ASTKind.MatchCase - - def type_name(self) -> str: - return "MatchCase" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchCase(pattern=") - if self.pattern is not None: - pos = self.pattern.dump(buf, size, pos) - pos = _emit(buf, size, pos, ", guard=") - if self.guard is not None: - pos = self.guard.dump(buf, size, pos) - pos = _emit(buf, size, pos, ", body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# MatchValue Node -# ============================================================ - -class MatchValue(AST): - """MatchValue(value: AST)""" - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - ptr: rowptr[MatchValue] = pool.alloc(MatchValue.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchValue.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.value = value - - def kind(self) -> int: - return ASTKind.MatchValue - - def type_name(self) -> str: - return "MatchValue" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchValue(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# MatchSingleton Node -# ============================================================ - -class MatchSingleton(AST): - """MatchSingleton(value: AST)""" - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - ptr: rowptr[MatchSingleton] = pool.alloc(MatchSingleton.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchSingleton.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.value = value - - def kind(self) -> int: - return ASTKind.MatchSingleton - - def type_name(self) -> str: - return "MatchSingleton" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchSingleton(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# MatchSequence Node -# ============================================================ - -class MatchSequence(AST): - """MatchSequence(patterns: list[rowptr[AST]])""" - patterns: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - patterns: rowptr[list[rowptr[AST]]]): - ptr: rowptr[MatchSequence] = pool.alloc(MatchSequence.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchSequence.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - patterns: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.patterns = patterns - _set_parent_list(patterns, self) - - def kind(self) -> int: - return ASTKind.MatchSequence - - def type_name(self) -> str: - return "MatchSequence" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchSequence(patterns=") - pos = _dump_list(self.patterns, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# MatchMapping Node -# ============================================================ - -class MatchMapping(AST): - """MatchMapping(keys: list[rowptr[AST]], patterns: list[rowptr[AST]], rest: str)""" - keys: rowptr[list[rowptr[AST]]] - patterns: rowptr[list[rowptr[AST]]] - rest: str - - def __new__(self, pool: rowptr[memhub.MemManager], - keys: rowptr[list[rowptr[AST]]], - patterns: rowptr[list[rowptr[AST]]], - rest: str): - ptr: rowptr[MatchMapping] = pool.alloc(MatchMapping.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchMapping.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - keys: rowptr[list[rowptr[AST]]], - patterns: rowptr[list[rowptr[AST]]], - rest: str): - _init_ast(self, pool, 0, 0) - self.keys = keys - self.patterns = patterns - self.rest = _copy_str(pool, rest) - _set_parent_list(keys, self) - _set_parent_list(patterns, self) - - def kind(self) -> int: - return ASTKind.MatchMapping - - def type_name(self) -> str: - return "MatchMapping" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchMapping(keys=") - pos = _dump_list(self.keys, buf, size, pos) - pos = _emit(buf, size, pos, ", patterns=") - pos = _dump_list(self.patterns, buf, size, pos) - pos = _emit(buf, size, pos, ", rest='") - pos = _emit_str(buf, size, pos, self.rest) - pos = _emit(buf, size, pos, "')") - return pos - - -# ============================================================ -# MatchClass Node -# ============================================================ - -class MatchClass(AST): - """MatchClass(cls: AST, patterns: list[rowptr[AST]], - kwd_attrs: list[rowptr[AST]], kwd_patterns: list[rowptr[AST]])""" - cls: rowptr[AST] - patterns: rowptr[list[rowptr[AST]]] - kwd_attrs: rowptr[list[rowptr[AST]]] - kwd_patterns: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - cls: rowptr[AST], - patterns: rowptr[list[rowptr[AST]]], - kwd_attrs: rowptr[list[rowptr[AST]]], - kwd_patterns: rowptr[list[rowptr[AST]]]): - ptr: rowptr[MatchClass] = pool.alloc(MatchClass.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchClass.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - cls: rowptr[AST], - patterns: rowptr[list[rowptr[AST]]], - kwd_attrs: rowptr[list[rowptr[AST]]], - kwd_patterns: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.cls = cls - self.patterns = patterns - self.kwd_attrs = kwd_attrs - self.kwd_patterns = kwd_patterns - _set_parent_list(patterns, self) - _set_parent_list(kwd_attrs, self) - _set_parent_list(kwd_patterns, self) - - def kind(self) -> int: - return ASTKind.MatchClass - - def type_name(self) -> str: - return "MatchClass" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchClass(cls=") - if self.cls is not None: - pos = self.cls.dump(buf, size, pos) - pos = _emit(buf, size, pos, ", patterns=") - pos = _dump_list(self.patterns, buf, size, pos) - pos = _emit(buf, size, pos, ", kwd_attrs=") - pos = _dump_list(self.kwd_attrs, buf, size, pos) - pos = _emit(buf, size, pos, ", kwd_patterns=") - pos = _dump_list(self.kwd_patterns, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# MatchStar Node -# ============================================================ - -class MatchStar(AST): - """MatchStar(name: str)""" - name: str - - def __new__(self, pool: rowptr[memhub.MemManager], name: str): - ptr: rowptr[MatchStar] = pool.alloc(MatchStar.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchStar.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], name: str): - _init_ast(self, pool, 0, 0) - self.name = _copy_str(pool, name) - - def kind(self) -> int: - return ASTKind.MatchStar - - def type_name(self) -> str: - return "MatchStar" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchStar(name='") - pos = _emit_str(buf, size, pos, self.name) - pos = _emit(buf, size, pos, "')") - return pos - - -# ============================================================ -# MatchAs Node -# ============================================================ - -class MatchAs(AST): - """MatchAs(pattern: AST, name: str)""" - pattern: rowptr[AST] - name: str - - def __new__(self, pool: rowptr[memhub.MemManager], - pattern: rowptr[AST], name: str): - ptr: rowptr[MatchAs] = pool.alloc(MatchAs.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchAs.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - pattern: rowptr[AST], name: str): - _init_ast(self, pool, 0, 0) - self.pattern = pattern - self.name = _copy_str(pool, name) - - def kind(self) -> int: - return ASTKind.MatchAs - - def type_name(self) -> str: - return "MatchAs" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchAs(pattern=") - if self.pattern is not None: - pos = self.pattern.dump(buf, size, pos) - pos = _emit(buf, size, pos, ", name='") - pos = _emit_str(buf, size, pos, self.name) - pos = _emit(buf, size, pos, "')") - return pos - - -# ============================================================ -# MatchOr Node -# ============================================================ - -class MatchOr(AST): - """MatchOr(patterns: list[rowptr[AST]])""" - patterns: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - patterns: rowptr[list[rowptr[AST]]]): - ptr: rowptr[MatchOr] = pool.alloc(MatchOr.__sizeof__()) - if ptr: - string.memset(ptr, 0, MatchOr.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - patterns: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.patterns = patterns - _set_parent_list(patterns, self) - - def kind(self) -> int: - return ASTKind.MatchOr - - def type_name(self) -> str: - return "MatchOr" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "MatchOr(patterns=") - pos = _dump_list(self.patterns, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos +import t, c +from stdint import * +import memhub +import string +from .base import ( + AST, ASTKind, + _init_ast, _copy_str, _set_parent_list, + _emit, _emit_str, _emit_int, _dump_list, +) + + +# ============================================================ +# MatchCase 节点Node +# ============================================================ + +class MatchCase(AST): + """MatchCase(pattern: AST, guard: AST)""" + pattern: rawptr[AST] + guard: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + pattern: rawptr[AST], guard: rawptr[AST]): + ptr: rawptr[MatchCase] = pool.alloc(MatchCase.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchCase.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + pattern: rawptr[AST], guard: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.pattern = pattern + self.guard = guard + + def kind(self) -> int: + return ASTKind.MatchCase + + def type_name(self) -> str: + return "MatchCase" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchCase(pattern=") + if self.pattern is not None: + pos = self.pattern.dump(buf, size, pos) + pos = _emit(buf, size, pos, ", guard=") + if self.guard is not None: + pos = self.guard.dump(buf, size, pos) + pos = _emit(buf, size, pos, ", body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# MatchValue Node +# ============================================================ + +class MatchValue(AST): + """MatchValue(value: AST)""" + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + ptr: rawptr[MatchValue] = pool.alloc(MatchValue.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchValue.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.value = value + + def kind(self) -> int: + return ASTKind.MatchValue + + def type_name(self) -> str: + return "MatchValue" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchValue(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# MatchSingleton Node +# ============================================================ + +class MatchSingleton(AST): + """MatchSingleton(value: AST)""" + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + ptr: rawptr[MatchSingleton] = pool.alloc(MatchSingleton.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchSingleton.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.value = value + + def kind(self) -> int: + return ASTKind.MatchSingleton + + def type_name(self) -> str: + return "MatchSingleton" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchSingleton(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# MatchSequence Node +# ============================================================ + +class MatchSequence(AST): + """MatchSequence(patterns: list[rawptr[AST]])""" + patterns: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + patterns: rawptr[list[rawptr[AST]]]): + ptr: rawptr[MatchSequence] = pool.alloc(MatchSequence.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchSequence.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + patterns: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.patterns = patterns + _set_parent_list(patterns, self) + + def kind(self) -> int: + return ASTKind.MatchSequence + + def type_name(self) -> str: + return "MatchSequence" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchSequence(patterns=") + pos = _dump_list(self.patterns, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# MatchMapping Node +# ============================================================ + +class MatchMapping(AST): + """MatchMapping(keys: list[rawptr[AST]], patterns: list[rawptr[AST]], rest: str)""" + keys: rawptr[list[rawptr[AST]]] + patterns: rawptr[list[rawptr[AST]]] + rest: str + + def __new__(self, pool: rawptr[memhub.MemManager], + keys: rawptr[list[rawptr[AST]]], + patterns: rawptr[list[rawptr[AST]]], + rest: str): + ptr: rawptr[MatchMapping] = pool.alloc(MatchMapping.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchMapping.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + keys: rawptr[list[rawptr[AST]]], + patterns: rawptr[list[rawptr[AST]]], + rest: str): + _init_ast(self, pool, 0, 0) + self.keys = keys + self.patterns = patterns + self.rest = _copy_str(pool, rest) + _set_parent_list(keys, self) + _set_parent_list(patterns, self) + + def kind(self) -> int: + return ASTKind.MatchMapping + + def type_name(self) -> str: + return "MatchMapping" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchMapping(keys=") + pos = _dump_list(self.keys, buf, size, pos) + pos = _emit(buf, size, pos, ", patterns=") + pos = _dump_list(self.patterns, buf, size, pos) + pos = _emit(buf, size, pos, ", rest='") + pos = _emit_str(buf, size, pos, self.rest) + pos = _emit(buf, size, pos, "')") + return pos + + +# ============================================================ +# MatchClass Node +# ============================================================ + +class MatchClass(AST): + """MatchClass(cls: AST, patterns: list[rawptr[AST]], + kwd_attrs: list[rawptr[AST]], kwd_patterns: list[rawptr[AST]])""" + cls: rawptr[AST] + patterns: rawptr[list[rawptr[AST]]] + kwd_attrs: rawptr[list[rawptr[AST]]] + kwd_patterns: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + cls: rawptr[AST], + patterns: rawptr[list[rawptr[AST]]], + kwd_attrs: rawptr[list[rawptr[AST]]], + kwd_patterns: rawptr[list[rawptr[AST]]]): + ptr: rawptr[MatchClass] = pool.alloc(MatchClass.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchClass.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + cls: rawptr[AST], + patterns: rawptr[list[rawptr[AST]]], + kwd_attrs: rawptr[list[rawptr[AST]]], + kwd_patterns: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.cls = cls + self.patterns = patterns + self.kwd_attrs = kwd_attrs + self.kwd_patterns = kwd_patterns + _set_parent_list(patterns, self) + _set_parent_list(kwd_attrs, self) + _set_parent_list(kwd_patterns, self) + + def kind(self) -> int: + return ASTKind.MatchClass + + def type_name(self) -> str: + return "MatchClass" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchClass(cls=") + if self.cls is not None: + pos = self.cls.dump(buf, size, pos) + pos = _emit(buf, size, pos, ", patterns=") + pos = _dump_list(self.patterns, buf, size, pos) + pos = _emit(buf, size, pos, ", kwd_attrs=") + pos = _dump_list(self.kwd_attrs, buf, size, pos) + pos = _emit(buf, size, pos, ", kwd_patterns=") + pos = _dump_list(self.kwd_patterns, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# MatchStar Node +# ============================================================ + +class MatchStar(AST): + """MatchStar(name: str)""" + name: str + + def __new__(self, pool: rawptr[memhub.MemManager], name: str): + ptr: rawptr[MatchStar] = pool.alloc(MatchStar.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchStar.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], name: str): + _init_ast(self, pool, 0, 0) + self.name = _copy_str(pool, name) + + def kind(self) -> int: + return ASTKind.MatchStar + + def type_name(self) -> str: + return "MatchStar" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchStar(name='") + pos = _emit_str(buf, size, pos, self.name) + pos = _emit(buf, size, pos, "')") + return pos + + +# ============================================================ +# MatchAs Node +# ============================================================ + +class MatchAs(AST): + """MatchAs(pattern: AST, name: str)""" + pattern: rawptr[AST] + name: str + + def __new__(self, pool: rawptr[memhub.MemManager], + pattern: rawptr[AST], name: str): + ptr: rawptr[MatchAs] = pool.alloc(MatchAs.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchAs.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + pattern: rawptr[AST], name: str): + _init_ast(self, pool, 0, 0) + self.pattern = pattern + self.name = _copy_str(pool, name) + + def kind(self) -> int: + return ASTKind.MatchAs + + def type_name(self) -> str: + return "MatchAs" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchAs(pattern=") + if self.pattern is not None: + pos = self.pattern.dump(buf, size, pos) + pos = _emit(buf, size, pos, ", name='") + pos = _emit_str(buf, size, pos, self.name) + pos = _emit(buf, size, pos, "')") + return pos + + +# ============================================================ +# MatchOr Node +# ============================================================ + +class MatchOr(AST): + """MatchOr(patterns: list[rawptr[AST]])""" + patterns: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + patterns: rawptr[list[rawptr[AST]]]): + ptr: rawptr[MatchOr] = pool.alloc(MatchOr.__sizeof__()) + if ptr: + string.memset(ptr, 0, MatchOr.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + patterns: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.patterns = patterns + _set_parent_list(patterns, self) + + def kind(self) -> int: + return ASTKind.MatchOr + + def type_name(self) -> str: + return "MatchOr" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "MatchOr(patterns=") + pos = _dump_list(self.patterns, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos diff --git a/ast/parser.vp b/ast/parser.vp index e65b559..7a1129b 100644 --- a/ast/parser.vp +++ b/ast/parser.vp @@ -1,2902 +1,2902 @@ -import t, c -from stdint import * -import memhub -import string -import stdio -import stdlib -from .tokens import (Token, TokenType, Keyword, TokOp) -from .base import ( - AST, ASTCtx, ASTKind, OpKind, ASTFlag, - _set_pos, _binop_from_op, _augop_from_op, _cmpop_from_op, _copy_str, _inherit_pos, - _init_ast, - CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR, - FLAG_IS_ASYNC, FLAG_IS_EXTERN, FLAG_IS_EXPORT, FLAG_IS_DEFINE, -) -from .exprs import ( - BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set, - ListComp, SetComp, DictComp, GeneratorExp, - Await, Yield, YieldFrom, FormattedValue, JoinedStr, - Constant, NamedExpr, Attribute, Subscript, Starred, Name, - List, Tuple, Slice, Call, Compare, OpNode, -) -from .stmts import ( - Module, Expression, Interactive, FunctionType, - FunctionDef, ClassDef, Return, Delete, Assign, AugAssign, AnnAssign, - For, While, If, With, Raise, Try, Assert, Global, Nonlocal, - Pass, Break, Continue, Expr, Import, ImportFrom, Match, -) -from .astaux import ( - ExceptHandler, Arguments, Arg, Keyword as KwNode, Alias, WithItem, Comprehension, -) -from .match import ( - MatchCase, MatchValue, MatchSingleton, MatchSequence, - MatchMapping, MatchClass, MatchStar, MatchAs, MatchOr, -) -from .lexer import Lexer, _lexer_init, tokenize, new_lexer - - -# ============================================================ -# Parser State -# ============================================================ -class Parser: - tokens: rowptr[Token] # token linked list head - cur: rowptr[Token] # current token - pool: rowptr[memhub.MemManager] # memory pool - error_count: int # error count - pending_decorators: rowptr[list[rowptr[AST]]] # pending decorators list - - def __new__(self, pool: rowptr[memhub.MemManager]): - ptr: rowptr[Parser] = pool.alloc(Parser.__sizeof__()) - if ptr: - string.memset(ptr, 0, Parser.__sizeof__()) - return ptr - - -def new_parser(pool: rowptr[memhub.MemManager]) -> rowptr[Parser]: - """Allocate from the pool and return the Parser (cross-module safe wrapper)""" - return Parser(pool) - - -def _parser_init(ps: rowptr[Parser], tokens: rowptr[Token], - pool: rowptr[memhub.MemManager]): - if ps is None: return - ps.tokens = tokens - ps.cur = tokens - ps.pool = pool - ps.error_count = 0 - ps.pending_decorators = None - - -# ============================================================ -# Token Navigation Helpers -# ============================================================ - -def _advance(ps: rowptr[Parser]): - """Advance to the next token""" - if ps.cur is None: return - if ps.cur.next is not None: - ps.cur = ps.cur.next - - -def _cur_type(ps: rowptr[Parser]) -> int: - if ps.cur is None: return TokenType.EndMarker - return ps.cur.type - - -def _cur_op(ps: rowptr[Parser]) -> int: - if ps.cur is None: return 0 - return ps.cur.op_subtype - - -def _cur_kw(ps: rowptr[Parser]) -> int: - if ps.cur is None: return 0 - return ps.cur.kw_subtype - - -def _is_funcdef_kw(ps: rowptr[Parser], kw: int) -> int: - """判断当前 token 是否为函数定义关键字(def 及其别名 fn/fun/func/function, - 以及 extern/export 声明关键字) - - def 是硬关键字直接判定;fn/fun/func/function/extern/export 是软关键字, - 仅当下一个 token 是 Name 且再下一个是 '('(或泛型 '[T]' 前的 '[')时 - 判定为函数定义。extern 还允许 'extern def A(...)' 形式(下一个是 def), - export 同样允许 'export def A(...)' 形式。 - 否则按普通标识符处理,兼容既有代码把 func 用作 - 字段/参数名(如 AST 节点 func 字段、Call.func 注解 `func: rowptr[AST]`)。 - """ - if kw == Keyword.Def: - return 1 - if kw == Keyword.Extern or kw == Keyword.Export: - # extern/export def A(...) 或 extern/export A(...) - # (extern/export 后是 def 或 Name+'(/'[') - nxt: rowptr[Token] = ps.cur.next - if nxt is None: - return 0 - if nxt.kw_subtype == Keyword.Def: - return 1 - if nxt.type != TokenType.Name: - return 0 - nxt2: rowptr[Token] = nxt.next - if nxt2 is None: - return 0 - if nxt2.type == TokenType.Op \ - and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb): - return 1 - return 0 - if kw == Keyword.Fn or kw == Keyword.Fun \ - or kw == Keyword.Func or kw == Keyword.Function \ - or kw == Keyword.Define: - # 软关键字 lookahead: 下个是 Name,再下个是 '(' 或 '[T...]' 泛型参数(PEP 695) - # (define 宏函数与 def 语义一致,返回注解自动追加 t.CDefine) - nxt: rowptr[Token] = ps.cur.next - if nxt is None: - return 0 - if nxt.type != TokenType.Name: - return 0 - nxt2: rowptr[Token] = nxt.next - if nxt2 is None: - return 0 - if nxt2.type == TokenType.Op \ - and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb): - return 1 - return 0 - return 0 - - -def _is_match_stmt(ps: rowptr[Parser]) -> int: - """match 是软关键字(CPython 3.10+):lookahead 判断是否为 match 语句 - - match 后直接跟 ':'(变量注解,如 lexer.vp 的 `match: int = 1`)、 - '='(赋值,如 `match = 0`)、换行/文件尾 → 按普通标识符; - 否则扫描至行尾,在括号深度 0 处出现 ':' → match 语句(subject 后必须冒号)。 - case/type 同理为软关键字(case 仅在 match 套件内消费,type 无语句分发)。 - """ - nxt: rowptr[Token] = ps.cur.next - if nxt is None: - return 0 - if nxt.type == TokenType.NewLine or nxt.type == TokenType.EndMarker: - return 0 - if nxt.type == TokenType.Op: - if nxt.op_subtype == TokOp.Colon: - return 0 - if nxt.op_subtype == TokOp.Equal: - return 0 - walk: rowptr[Token] = nxt - depth: int = 0 - while walk is not None: - if walk.type == TokenType.NewLine or walk.type == TokenType.EndMarker: - return 0 - if walk.type == TokenType.Op: - wop: int = walk.op_subtype - if wop == TokOp.LPar or wop == TokOp.Lsqb or wop == TokOp.LBrace: - depth += 1 - elif wop == TokOp.RPar or wop == TokOp.Rsqb or wop == TokOp.RBrace: - if depth > 0: - depth -= 1 - elif wop == TokOp.Colon and depth == 0: - return 1 - walk = walk.next - return 0 - - -# 解析错误位置与消息(fail-fast 仿 Python SyntaxError 报告,首次错误记录) -_g_parse_err_line: int = 0 -_g_parse_err_col: int = 0 -_g_parse_err_msg: str = None - - -def _record_parse_err(ps: rowptr[Parser]): - """记录首个解析错误的位置与消息(只记录第一次,后续错误不覆盖)""" - global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg - if _g_parse_err_line != 0: - return - _g_parse_err_line = _cur_lineno(ps) - _g_parse_err_col = _cur_col(ps) - err_s: str = _cur_str(ps) - if err_s is None: - err_s = "(none)" - # 消息文本用 stdlib 拷贝(token 的 str_val 在 pool 中,生命周期覆盖本次编译但保险起见独立存储) - buf_m: str = stdlib.malloc(128) - if buf_m is not None: - stdio.snprintf(buf_m, 128, "意外的 token '%s'", err_s) - _g_parse_err_msg = buf_m - else: - _g_parse_err_msg = err_s - - -def get_parse_err_line() -> int: - """获取最近一次解析错误的行号(0=无错误)""" - return _g_parse_err_line - - -def get_parse_err_col() -> int: - """获取最近一次解析错误的列号(0=无错误)""" - return _g_parse_err_col - - -def get_parse_err_msg() -> str: - """获取最近一次解析错误的错误消息(仿 Python SyntaxError 消息文本)""" - return _g_parse_err_msg - - -def _cur_str(ps: rowptr[Parser]) -> str: - if ps.cur is None: return None - return ps.cur.str_val - - -def _cur_lineno(ps: rowptr[Parser]) -> int: - if ps.cur is None: return 0 - return ps.cur.lineno - - -def _cur_col(ps: rowptr[Parser]) -> int: - if ps.cur is None: return 0 - return ps.cur.col_offset - - -def _skip_nl(ps: rowptr[Parser]): - """Skip TokenType.Nl token""" - while ps.cur is not None and ps.cur.type == TokenType.Nl: - ps.cur = ps.cur.next - - -def _skip_nls_and_newlines(ps: rowptr[Parser]): - """Skip TokenType.Nl and TokenType.NewLine (used for line continuation)""" - while ps.cur is not None: - if ps.cur.type == TokenType.Nl or ps.cur.type == TokenType.NewLine: - ps.cur = ps.cur.next - else: - break - - -def _expect_type(ps: rowptr[Parser], ttype: int) -> int: - """Expect the current token to be of the specified type, advance and return 1, otherwise return 0""" - if _cur_type(ps) == ttype: - _advance(ps) - return 1 - _record_parse_err(ps) - ps.error_count += 1 - return 0 - - -def _expect_op(ps: rowptr[Parser], op_id: int) -> int: - """Expecting the current one to be some operator""" - if _cur_type(ps) == TokenType.Op and _cur_op(ps) == op_id: - _advance(ps) - return 1 - _record_parse_err(ps) - ps.error_count += 1 - return 0 - - -def _match_op(ps: rowptr[Parser], op_id: int) -> int: - """Check but not consume the current token""" - if _cur_type(ps) == TokenType.Op and _cur_op(ps) == op_id: - return 1 - return 0 - - -def _accept_op(ps: rowptr[Parser], op_id: int) -> int: - """If match, consume and return 1""" - if _match_op(ps, op_id): - _advance(ps) - return 1 - return 0 - - -# ============================================================ -# Constant Construction Helper (New API: Constant directly accepts lineno/col) -# ============================================================ - -def _new_const_int(pool: rowptr[memhub.MemManager], val: int64, - lineno: int, col: int) -> rowptr[AST]: - return Constant(pool, CONST_INT, val, 0.0, None, lineno, col) - - -def _new_const_float(pool: rowptr[memhub.MemManager], val: double, - lineno: int, col: int) -> rowptr[AST]: - global _g_dbg_pts, _g_dbg_vals, _g_dbg_cnt - new_c: rowptr[Constant] = Constant(pool, CONST_FLOAT, 0, val, None, lineno, col) - # 把数字文本存到 str_val(stdlib 分配,绕开 pool 对 float_val 字段的覆写) - # REG-SRC 用 string.atof(str_val) 恢复原始值 - lit_txt: str = stdlib.malloc(64) - if lit_txt is not None: - stdio.snprintf(lit_txt, 64, "%.17g", val) - new_c.str_val = lit_txt - # 记录浮点节点 ptr→val 到 stdlib 表(构造时值正确,供 REG-SRC 查表绕开 pool 覆写) - # 表满时重置:旧记录对应已翻译完的文件,从当前节点重新记录 - if _g_dbg_pts is None: - _g_dbg_pts = stdlib.malloc(4096 * 8) - _g_dbg_vals = stdlib.malloc(4096 * 8) - _g_dbg_cnt = 0 - elif _g_dbg_cnt >= 4096: - _g_dbg_cnt = 0 - if _g_dbg_pts is not None and _g_dbg_vals is not None: - _g_dbg_pts[_g_dbg_cnt] = uint64(new_c) - _g_dbg_vals[_g_dbg_cnt] = val - _g_dbg_cnt += 1 - return new_c - - -# 浮点节点构造值表(stdlib 分配,避免污染 pool) -_g_dbg_pts: rowptr[uint64] = None -_g_dbg_vals: rowptr[double] = None -_g_dbg_cnt: int = 0 -# 最近一次 _dbg_lookup_lit_val 是否命中 -_g_dbg_lit_found: int = 0 - - -def _dbg_lookup_lit_val(ptr: uint64) -> double: - """查浮点节点构造值表:返回 ptr 节点的原始构造值(未命中返回 0 并置 found=0) - - 用于绕开 pool 分配对 AST float_val 字段的覆写(构造时值正确,读取时可能被覆写)。 - """ - global _g_dbg_lit_found - _g_dbg_lit_found = 0 - if _g_dbg_pts is None or _g_dbg_vals is None: - return 0.0 - i: t.CSizeT = 0 - while i < _g_dbg_cnt: - if _g_dbg_pts[i] == ptr: - _g_dbg_lit_found = 1 - return _g_dbg_vals[i] - i += 1 - return 0.0 - - -def _dbg_lit_found() -> int: - """检查最近一次 _dbg_lookup_lit_val 是否命中""" - return _g_dbg_lit_found - - -def _new_const_str(pool: rowptr[memhub.MemManager], val: str, - lineno: int, col: int, - is_dquote: int = 1, - is_triple: int = 0, - is_char: int = 0) -> rowptr[AST]: - """Create a string/character constant node - - is_dquote: 0 = single quote, non-0 = double quote (default double quote) - is_triple: non-0 = triple-quoted string - is_char: non-0 = single-character candidate (lexer has confirmed that after escaping it's exactly 1 byte, including '0') - Semantic distinctions: - - 'x' (single quote, single character, not triple-quoted) → CONST_CHAR (i8 value) - - "x" (double quote, includes 0 terminator) → CONST_STR (i8* pointer, i8 x 2) - - 'ab' (single quotes, multiple characters) → CONST_STR (i8* pointer) - - '''x''' / \"\"\"x\"\"\" (triple quotes) → CONST_STR (i8* pointer) - - '' / "" (empty string) → CONST_STR (i8* pointer) - """ - # Single quotes, non-triple quotes, single-character candidates marked by lexer → CONST_CHAR - # Mark with is_char instead of strlen(val), because '0' becomes a NUL byte after escaping, strlen would return 0 - if is_dquote == 0 and is_triple == 0 and is_char != 0: - # Single quote # Single character → CONST_CHAR, int_val stores the character's ASCII value - # val[0] is a legal value of 0 even if it's NUL - ch_val: int64 = val[0] - return Constant(pool, CONST_CHAR, ch_val, 0.0, val, lineno, col) - # Other situations → CONST_STR - return Constant(pool, CONST_STR, 0, 0.0, val, lineno, col) - - -def _new_const_bool(pool: rowptr[memhub.MemManager], val: int, - lineno: int, col: int) -> rowptr[AST]: - return Constant(pool, CONST_BOOL, val, 0.0, None, lineno, col) - - -def _new_const_none(pool: rowptr[memhub.MemManager], - lineno: int, col: int) -> rowptr[AST]: - return Constant(pool, CONST_NONE, 0, 0.0, None, lineno, col) - - -# ============================================================ -# ctx Helper (Recursive Fixing Store Context) -# ============================================================ - -def _set_ctx(node: rowptr[AST], ctx: int): - """Set ctx field of node (based on kind conversion)""" - if node is None: return - k: int = node.kind() - if k == ASTKind.Name: - nm: rowptr[Name] = (rowptr[Name])(node) - nm.ctx = ctx - elif k == ASTKind.Attribute: - at: rowptr[Attribute] = (rowptr[Attribute])(node) - at.ctx = ctx - elif k == ASTKind.Subscript: - sb: rowptr[Subscript] = (rowptr[Subscript])(node) - sb.ctx = ctx - elif k == ASTKind.Tuple: - tp: rowptr[Tuple] = (rowptr[Tuple])(node) - tp.ctx = ctx - elif k == ASTKind.List: - ls: rowptr[List] = (rowptr[List])(node) - ls.ctx = ctx - elif k == ASTKind.Starred: - st: rowptr[Starred] = (rowptr[Starred])(node) - st.ctx = ctx - - -def _fix_store_ctx(node: rowptr[AST]): - """Fix ctx of store targets to be Store""" - if node is None: return - _set_ctx(node, ASTCtx.Store) - k: int = node.kind() - if k == ASTKind.Tuple or k == ASTKind.List: - tp_elts: rowptr[list[rowptr[AST]]] = None - if k == ASTKind.Tuple: - tp: rowptr[Tuple] = (rowptr[Tuple])(node) - tp_elts = tp.elts - else: - ls: rowptr[List] = (rowptr[List])(node) - tp_elts = ls.elts - if tp_elts is not None: - n: t.CSizeT = tp_elts.__len__() - i: t.CSizeT = 0 - while i < n: - cur: rowptr[AST] = tp_elts.get(i) - _fix_store_ctx(cur) - i += 1 - elif k == ASTKind.Starred: - st2: rowptr[Starred] = (rowptr[Starred])(node) - _fix_store_ctx(st2.value) - - -# ============================================================ -# Expression Parser -# ============================================================ - -def _parse_expr(ps: rowptr[Parser]) -> rowptr[AST]: - """expr: yield_expr | namedexpr_test""" - if _cur_kw(ps) == Keyword.Yield: - return _parse_yield(ps) - return _parse_namedexpr(ps) - - -def _parse_namedexpr(ps: rowptr[Parser]) -> rowptr[AST]: - """namedexpr_test: test [':=' test]""" - left: rowptr[AST] = _parse_test(ps) - if left is None: return None - if _match_op(ps, TokOp.ColonEq): - _advance(ps) - right: rowptr[AST] = _parse_test(ps) - node: rowptr[AST] = NamedExpr(ps.pool, left, right) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - return node - return left - - -def _parse_test(ps: rowptr[Parser]) -> rowptr[AST]: - """test: or_test ['if' or_test 'else' test] | lambdef""" - if _cur_kw(ps) == Keyword.Lambda: - return _parse_lambda(ps) - left: rowptr[AST] = _parse_or_test(ps) - if left is None: return None - if _cur_kw(ps) == Keyword.If: - _advance(ps) - test: rowptr[AST] = _parse_or_test(ps) - if _cur_kw(ps) == Keyword.Else: - _advance(ps) - orelse: rowptr[AST] = _parse_test(ps) - node: rowptr[AST] = IfExp(ps.pool, test, left, orelse) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - return node - ps.error_count += 1 - return left - return left - - -def _parse_lambda(ps: rowptr[Parser]) -> rowptr[AST]: - """lambdef: 'lambda' [varargslist] ':' test""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # lambda - args: rowptr[AST] = None - if not _match_op(ps, TokOp.Colon): - args = _parse_varargslist(ps, 1) - if not _expect_op(ps, TokOp.Colon): - return None - body: rowptr[AST] = _parse_test(ps) - node: rowptr[AST] = Lambda(ps.pool, args, body) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_or_test(ps: rowptr[Parser]) -> rowptr[AST]: - """or_test: and_test ('or' and_test)*""" - left: rowptr[AST] = _parse_and_test(ps) - if left is None: return None - values: rowptr[list[rowptr[AST]]] = None - while True: - # 括号内行首 or((A\n or B))与行尾 or(A or\n B)都支持: - # 每轮先 _skip_nl 再检查 or,否则 A or B\n or C 在 B 后停在 Nl 上 - # 循环退出,上层把续行 or 当非法 token(对齐 and_test / vast 实现)。 - _skip_nl(ps) - if _cur_kw(ps) != Keyword.Or: - break - if values is None: - values = list[rowptr[AST]](ps.pool, 8) - values.append(left) - _advance(ps) - _skip_nl(ps) # 括号内跨行: a or\n b - right: rowptr[AST] = _parse_and_test(ps) - if right is None: break - values.append(right) - if values is not None: - node: rowptr[AST] = BoolOp(ps.pool, OpKind.Or, values) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - return node - return left - - -def _parse_and_test(ps: rowptr[Parser]) -> rowptr[AST]: - """and_test: not_test ('and' not_test)*""" - left: rowptr[AST] = _parse_not_test(ps) - if left is None: return None - values: rowptr[list[rowptr[AST]]] = None - while True: - # 括号内行首 and((A\n and B))与行尾 and(A and\n B)都支持: - # 每轮先 _skip_nl 再检查 and,否则 A and B\n and C 在 B 后停在 - # Nl 上循环退出,上层把续行 and 当非法 token - # (ThreadingTest check 多行 and 链解析失败根因)。 - _skip_nl(ps) - if _cur_kw(ps) != Keyword.And: - break - if values is None: - values = list[rowptr[AST]](ps.pool, 8) - values.append(left) - _advance(ps) - _skip_nl(ps) # 括号内跨行: a and\n b - right: rowptr[AST] = _parse_not_test(ps) - if right is None: break - values.append(right) - if values is not None: - node: rowptr[AST] = BoolOp(ps.pool, OpKind.And, values) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - return node - return left - - -def _parse_not_test(ps: rowptr[Parser]) -> rowptr[AST]: - """not_test: 'not' not_test | comparison""" - if _cur_kw(ps) == Keyword.Not: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - operand: rowptr[AST] = _parse_not_test(ps) - node: rowptr[AST] = UnaryOp(ps.pool, OpKind.Not, operand) - _set_pos(node, lineno, col, 0, 0) - return node - return _parse_comparison(ps) - - -def _parse_comparison(ps: rowptr[Parser]) -> rowptr[AST]: - """comparison: bitor_expr (comp_op bitor_expr)* - - Single comparison design: a < b → Compare(left=a, op=Lt, right=b) - Chained comparison a < b < c → BoolOp(And, [Compare(a,<,b), Compare(b,<,c)]) - """ - left: rowptr[AST] = _parse_bitor(ps) - if left is None: return None - - _skip_nl(ps) # 括号内行首比较运算符: (a\n < b) - op_id: int = _match_comp_op(ps) - if op_id == OpKind.NoneOp: - return left - _advance_comp_op(ps) - _skip_nl(ps) # 括号内跨行: a ==\n b - right: rowptr[AST] = _parse_bitor(ps) - # Single comparison: construct ops and comparators list - ops: rowptr[list[int]] = list[int](ps.pool, 8) - ops.append(op_id) - comparators: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - comparators.append(right) - first_cmp: rowptr[AST] = Compare(ps.pool, left, ops, comparators) - _set_pos(first_cmp, left.lineno, left.col_offset, 0, 0) - - # Check chained comparison - _skip_nl(ps) # 括号内行首链式比较: (a < b\n < c) - next_op: int = _match_comp_op(ps) - if next_op == OpKind.NoneOp: - return first_cmp - - # Single comparison with chained conditions: a < b < c → BoolOp(And, [Compare(a,<,b), Compare(b,<,c), ...]) - values: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - values.append(first_cmp) - cur_left: rowptr[AST] = right - while next_op != OpKind.NoneOp: - _advance_comp_op(ps) - _skip_nl(ps) # 括号内跨行链式比较 - cur_right: rowptr[AST] = _parse_bitor(ps) - ops2: rowptr[list[int]] = list[int](ps.pool, 8) - ops2.append(next_op) - comparators2: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - comparators2.append(cur_right) - cmp: rowptr[AST] = Compare(ps.pool, cur_left, ops2, comparators2) - _set_pos(cmp, cur_left.lineno, cur_left.col_offset, 0, 0) - values.append(cmp) - cur_left = cur_right - _skip_nl(ps) # 括号内行首链式比较: (a < b\n < c) - next_op = _match_comp_op(ps) - node: rowptr[AST] = BoolOp(ps.pool, OpKind.And, values) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - return node - - -def _match_comp_op(ps: rowptr[Parser]) -> int: - """Check if the current one is a comparison operator, return OP_* or OpKind.NoneOp""" - if _cur_type(ps) == TokenType.Op: - op_id: int = _cur_op(ps) - if op_id == TokOp.EqEq: return OpKind.Eq - if op_id == TokOp.ExclaimEq: return OpKind.Ne - if op_id == TokOp.Less: return OpKind.Lt - if op_id == TokOp.LessEq: return OpKind.Le - if op_id == TokOp.Greater: return OpKind.Gt - if op_id == TokOp.GreaterEq: return OpKind.Ge - if _cur_kw(ps) == Keyword.Is: - if ps.cur.next is not None and ps.cur.next.type == TokenType.Name and ps.cur.next.kw_subtype == Keyword.Not: - return OpKind.IsNot - return OpKind.Is - if _cur_kw(ps) == Keyword.Not: - if ps.cur.next is not None and ps.cur.next.kw_subtype == Keyword.In: - return OpKind.NotIn - if _cur_kw(ps) == Keyword.In: - return OpKind.In - return OpKind.NoneOp - - -def _advance_comp_op(ps: rowptr[Parser]): - """Consume comparison operator token""" - if _cur_type(ps) == TokenType.Op: - _advance(ps) - return - if _cur_kw(ps) == Keyword.Is: - _advance(ps) - if _cur_kw(ps) == Keyword.Not: - _advance(ps) - return - if _cur_kw(ps) == Keyword.Not: - _advance(ps) - if _cur_kw(ps) == Keyword.In: - _advance(ps) - return - if _cur_kw(ps) == Keyword.In: - _advance(ps) - - -def _parse_bitor(ps: rowptr[Parser]) -> rowptr[AST]: - left: rowptr[AST] = _parse_bitxor(ps) - if left is None: return None - while True: - _skip_nl(ps) # 括号内行首 '|': (a\n | b) - if _match_op(ps, TokOp.VBar) == 0: - break - _advance(ps) - _skip_nl(ps) # 括号内跨行: a |\n b - right: rowptr[AST] = _parse_bitxor(ps) - node: rowptr[AST] = BinOp(ps.pool, left, OpKind.BitOr, right) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - left = node - return left - - -def _parse_bitxor(ps: rowptr[Parser]) -> rowptr[AST]: - left: rowptr[AST] = _parse_bitand(ps) - if left is None: return None - while True: - _skip_nl(ps) # 括号内行首 '^': (a\n ^ b) - if _match_op(ps, TokOp.Caret) == 0: - break - _advance(ps) - _skip_nl(ps) # 括号内跨行: a ^\n b - right: rowptr[AST] = _parse_bitand(ps) - node: rowptr[AST] = BinOp(ps.pool, left, OpKind.BitXor, right) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - left = node - return left - - -def _parse_bitand(ps: rowptr[Parser]) -> rowptr[AST]: - left: rowptr[AST] = _parse_shift(ps) - if left is None: return None - while True: - _skip_nl(ps) # 括号内行首 '&': (a\n & b) - if _match_op(ps, TokOp.Amp) == 0: - break - _advance(ps) - _skip_nl(ps) # 括号内跨行: a &\n b - right: rowptr[AST] = _parse_shift(ps) - node: rowptr[AST] = BinOp(ps.pool, left, OpKind.BitAnd, right) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - left = node - return left - - -def _parse_shift(ps: rowptr[Parser]) -> rowptr[AST]: - left: rowptr[AST] = _parse_arith(ps) - if left is None: return None - while True: - _skip_nl(ps) # 括号内行首 '<<'/'>>': (a\n << b) - if _match_op(ps, TokOp.LtLt) == 0 and _match_op(ps, TokOp.GtGt) == 0: - break - op_token: int = _cur_op(ps) - _advance(ps) - _skip_nl(ps) # 括号内跨行: a <<\n b - right: rowptr[AST] = _parse_arith(ps) - ast_op: int = OpKind.LShift - if op_token == TokOp.GtGt: - ast_op = OpKind.RShift - node: rowptr[AST] = BinOp(ps.pool, left, ast_op, right) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - left = node - return left - - -def _parse_arith(ps: rowptr[Parser]) -> rowptr[AST]: - left: rowptr[AST] = _parse_term(ps) - if left is None: return None - while True: - _skip_nl(ps) # 括号内行首 '+'/'-': (a\n + b) - if _match_op(ps, TokOp.Plus) == 0 and _match_op(ps, TokOp.Minus) == 0: - break - op_token: int = _cur_op(ps) - _advance(ps) - _skip_nl(ps) # 括号内跨行: a +\n b - right: rowptr[AST] = _parse_term(ps) - ast_op: int = OpKind.Add - if op_token == TokOp.Minus: - ast_op = OpKind.Sub - node: rowptr[AST] = BinOp(ps.pool, left, ast_op, right) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - left = node - return left - - -def _parse_term(ps: rowptr[Parser]) -> rowptr[AST]: - left: rowptr[AST] = _parse_factor(ps) - if left is None: return None - while True: - _skip_nl(ps) # 括号内行首 '*'/'/'/'//'/'%'/'@': (a\n * b) - op_token: int = 0 - if _match_op(ps, TokOp.Star): op_token = TokOp.Star - elif _match_op(ps, TokOp.Slash): op_token = TokOp.Slash - elif _match_op(ps, TokOp.DSlash): op_token = TokOp.DSlash - elif _match_op(ps, TokOp.Percent): op_token = TokOp.Percent - elif _match_op(ps, TokOp.At): op_token = TokOp.At - if op_token == 0: break - _advance(ps) - _skip_nl(ps) # 括号内跨行: a *\n b - right: rowptr[AST] = _parse_factor(ps) - ast_op: int = _binop_from_op(op_token) - node: rowptr[AST] = BinOp(ps.pool, left, ast_op, right) - _set_pos(node, left.lineno, left.col_offset, 0, 0) - left = node - return left - - -def _parse_factor(ps: rowptr[Parser]) -> rowptr[AST]: - """factor: '+' factor | '-' factor | '~' factor | '*' factor | '&' factor | power""" - if (_match_op(ps, TokOp.Plus) or _match_op(ps, TokOp.Minus) or _match_op(ps, TokOp.Tilde) - or _match_op(ps, TokOp.Star) or _match_op(ps, TokOp.Amp)): - op_token: int = _cur_op(ps) - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - operand: rowptr[AST] = _parse_factor(ps) - ast_op: int = OpKind.UAdd - if op_token == TokOp.Minus: - ast_op = OpKind.USub - elif op_token == TokOp.Tilde: - ast_op = OpKind.Invert - elif op_token == TokOp.Star: - ast_op = OpKind.Deref - elif op_token == TokOp.Amp: - ast_op = OpKind.AddrOf - node: rowptr[AST] = UnaryOp(ps.pool, ast_op, operand) - _set_pos(node, lineno, col, 0, 0) - return node - return _parse_power(ps) - - -def _parse_power(ps: rowptr[Parser]) -> rowptr[AST]: - """power: await_expr ['**' factor] | atom_expr ['**' factor]""" - base: rowptr[AST] = _parse_atom_expr(ps) - if base is None: return None - if _match_op(ps, TokOp.StarStar): - _advance(ps) - exp: rowptr[AST] = _parse_factor(ps) - node: rowptr[AST] = BinOp(ps.pool, base, OpKind.Pow, exp) - _set_pos(node, base.lineno, base.col_offset, 0, 0) - return node - return base - - -def _parse_atom_expr(ps: rowptr[Parser]) -> rowptr[AST]: - """atom_expr: ['await'] atom trailer*""" - if _cur_kw(ps) == Keyword.Await: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - inner: rowptr[AST] = _parse_atom_expr(ps) - node: rowptr[AST] = Await(ps.pool, inner) - _set_pos(node, lineno, col, 0, 0) - return node - base: rowptr[AST] = _parse_atom(ps) - if base is None: return None - # trailer loop - while True: - if _match_op(ps, TokOp.Dot): - _advance(ps) - if _cur_type(ps) != TokenType.Name: - ps.error_count += 1 - break - attr_name: str = _cur_str(ps) - attr_lineno: int = _cur_lineno(ps) - attr_col: int = _cur_col(ps) - _advance(ps) - ctx: int = ASTCtx.Load - if _match_op(ps, TokOp.Equal): - ctx = ASTCtx.Store - elif _match_op(ps, TokOp.PlusEq) or _match_op(ps, TokOp.MinusEq): - ctx = ASTCtx.Store - base = Attribute(ps.pool, base, attr_name, ctx) - _set_pos(base, attr_lineno, attr_col, 0, 0) - elif _match_op(ps, TokOp.LPar): - base = _parse_call(ps, base) - elif _match_op(ps, TokOp.Lsqb): - base = _parse_subscript(ps, base) - else: - break - return base - - -def _parse_call(ps: rowptr[Parser], func: rowptr[AST]) -> rowptr[AST]: - """Parse function call trailer. Both args and keywords are lists.""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # ( - _skip_nls_and_newlines(ps) # Skip the line break after '(' - args: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - keywords: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - - if not _match_op(ps, TokOp.RPar): - while True: - # **expr - if _match_op(ps, TokOp.StarStar): - _advance(ps) - _skip_nls_and_newlines(ps) - val: rowptr[AST] = _parse_expr(ps) - kw: rowptr[AST] = KwNode(ps.pool, None, val) - _set_pos(kw, _cur_lineno(ps), _cur_col(ps), 0, 0) - keywords.append(kw) - # *expr - elif _match_op(ps, TokOp.Star): - _advance(ps) - _skip_nls_and_newlines(ps) - val = _parse_expr(ps) - star: rowptr[AST] = Starred(ps.pool, val, ASTCtx.Load) - _set_pos(star, val.lineno, val.col_offset, 0, 0) - args.append(star) - # keyword = expr - elif _cur_type(ps) == TokenType.Name: - if ps.cur.next is not None and ps.cur.next.type == TokenType.Op and ps.cur.next.op_subtype == TokOp.Equal: - kw_name: str = _cur_str(ps) - kw_lineno: int = _cur_lineno(ps) - kw_col: int = _cur_col(ps) - _advance(ps) # name - _advance(ps) # = - _skip_nls_and_newlines(ps) - val = _parse_expr(ps) - kw = KwNode(ps.pool, kw_name, val) - _set_pos(kw, kw_lineno, kw_col, 0, 0) - keywords.append(kw) - else: - val = _parse_expr(ps) - args.append(val) - else: - val = _parse_expr(ps) - args.append(val) - _skip_nls_and_newlines(ps) # Skip the line break after the parameter - if not _accept_op(ps, TokOp.Comma): - break - _skip_nls_and_newlines(ps) # Skip the line break after the comma - _skip_nls_and_newlines(ps) # Skip the line break before ')' - _expect_op(ps, TokOp.RPar) - node: rowptr[AST] = Call(ps.pool, func, args, keywords) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_subscript(ps: rowptr[Parser], value: rowptr[AST]) -> rowptr[AST]: - """Parse subscript trailer.""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # [ - _skip_nl(ps) - slice_node: rowptr[AST] = _parse_slice(ps) - _expect_op(ps, TokOp.Rsqb) - ctx: int = ASTCtx.Load - if _match_op(ps, TokOp.Equal): - ctx = ASTCtx.Store - node: rowptr[AST] = Subscript(ps.pool, value, slice_node, ctx) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_slice(ps: rowptr[Parser]) -> rowptr[AST]: - """Parse slice: [expr] ':' [expr] [':' [expr]] | expr | expr, expr, ...""" - lower: rowptr[AST] = None - if not _match_op(ps, TokOp.Colon): - lower = _parse_expr(ps) - - # Check if there’s a comma (tuple syntax: a[x, y, ...]) - if _match_op(ps, TokOp.Comma): - elts: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - if lower is not None: - elts.append(lower) - while _accept_op(ps, TokOp.Comma): - if _match_op(ps, TokOp.Rsqb): - break # Trailing comma - elem: rowptr[AST] = _parse_expr(ps) - if elem is not None: - elts.append(elem) - lineno: int = 0 - if lower is not None: - lineno = lower.lineno - node: rowptr[AST] = Tuple(ps.pool, elts, ASTCtx.Load) - _set_pos(node, lineno, 0, 0, 0) - return node - - if _match_op(ps, TokOp.Colon): - _advance(ps) - upper: rowptr[AST] = None - if not _match_op(ps, TokOp.Colon) and not _match_op(ps, TokOp.Rsqb): - upper = _parse_expr(ps) - step: rowptr[AST] = None - if _accept_op(ps, TokOp.Colon): - if not _match_op(ps, TokOp.Rsqb): - step = _parse_expr(ps) - lineno: int = 0 - if lower is not None: - lineno = lower.lineno - elif upper is not None: - lineno = upper.lineno - else: - lineno = _cur_lineno(ps) - node: rowptr[AST] = Slice(ps.pool, lower, upper, step) - _set_pos(node, lineno, 0, 0, 0) - return node - return lower - - -def _parse_atom(ps: rowptr[Parser]) -> rowptr[AST]: - """atom: the smallest unit of expression""" - ttype: int = _cur_type(ps) - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - - if ttype == TokenType.Name: - kw: int = _cur_kw(ps) - if kw == Keyword.True_: - _advance(ps) - return _new_const_bool(ps.pool, 1, lineno, col) - if kw == Keyword.False_: - _advance(ps) - return _new_const_bool(ps.pool, 0, lineno, col) - if kw == Keyword.None_: - _advance(ps) - return _new_const_none(ps.pool, lineno, col) - name_str: str = _cur_str(ps) - _advance(ps) - node: rowptr[AST] = Name(ps.pool, name_str, ASTCtx.Load) - _set_pos(node, lineno, col, 0, 0) - return node - - if ttype == TokenType.Number: - tok: rowptr[Token] = ps.cur - _advance(ps) - if tok.is_float: - return _new_const_float(ps.pool, tok.float_val, lineno, col) - return _new_const_int(ps.pool, tok.int_val, lineno, col) - - if ttype == TokenType.String: - sval: str = _cur_str(ps) - is_fstr: int = ps.cur.kw_subtype & 4 - is_triple: int = ps.cur.kw_subtype & 8 - is_dquote: int = ps.cur.kw_subtype & 16 - is_char: int = ps.cur.kw_subtype & 32 - _advance(ps) - node: rowptr[AST] - if is_fstr: - node = _parse_fstring(ps, sval, lineno, col) - else: - node = _new_const_str(ps.pool, sval, lineno, col, is_dquote, is_triple, is_char) - # Python 相邻字符串字面量隐式拼接:"a" "b" → "ab" - # Nl 是括号内换行 token(允许括号内跨行拼接);语句级换行是 NewLine,不跳过 - while True: - _skip_nl(ps) - if _cur_type(ps) != TokenType.String: - break - n_sval: str = _cur_str(ps) - n_fstr: int = ps.cur.kw_subtype & 4 - n_triple: int = ps.cur.kw_subtype & 8 - n_dquote: int = ps.cur.kw_subtype & 16 - n_char: int = ps.cur.kw_subtype & 32 - _advance(ps) - n_node: rowptr[AST] - if n_fstr: - n_node = _parse_fstring(ps, n_sval, lineno, col) - else: - n_node = _new_const_str(ps.pool, n_sval, lineno, col, n_dquote, n_triple, n_char) - node = _concat_str_nodes(ps.pool, node, n_node) - return node - - if _match_op(ps, TokOp.Ellipsis): - _advance(ps) - return _new_const_none(ps.pool, lineno, col) - - if _match_op(ps, TokOp.LPar): - return _parse_paren(ps) - if _match_op(ps, TokOp.Lsqb): - return _parse_list(ps) - if _match_op(ps, TokOp.LBrace): - return _parse_brace(ps) - - _record_parse_err(ps) - ps.error_count += 1 - return None - - -def _parse_paren(ps: rowptr[Parser]) -> rowptr[AST]: - """Parenthesis expressions: () | (expr) | (expr,) | (expr, ...) | (yield)""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # ( - _skip_nl(ps) # Skip the line break after '(' (multi-line tuple) - if _match_op(ps, TokOp.RPar): - _advance(ps) - # Empty tuple - empty: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - node: rowptr[AST] = Tuple(ps.pool, empty, ASTCtx.Load) - _set_pos(node, lineno, col, 0, 0) - return node - # yield - if _cur_kw(ps) == Keyword.Yield: - y: rowptr[AST] = _parse_yield(ps) - _expect_op(ps, TokOp.RPar) - return y - first: rowptr[AST] = _parse_expr(ps) - if _match_op(ps, TokOp.Comma): - # Tuple - elts: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - elts.append(first) - while _accept_op(ps, TokOp.Comma): - _skip_nl(ps) - if _match_op(ps, TokOp.RPar): - break - e: rowptr[AST] = _parse_expr(ps) - elts.append(e) - _skip_nl(ps) # 末元素无尾逗号时收尾(多行元组) - _expect_op(ps, TokOp.RPar) - node = Tuple(ps.pool, elts, ASTCtx.Load) - _set_pos(node, lineno, col, 0, 0) - return node - _skip_nl(ps) # (expr\n) 单表达式括号跨行收尾 - _expect_op(ps, TokOp.RPar) - # Check for generator expression - if _cur_kw(ps) == Keyword.For: - return _parse_comprehension(ps, first, ASTKind.GeneratorExp, lineno, col) - return first - - -def _parse_list(ps: rowptr[Parser]) -> rowptr[AST]: - """List expressions: [] | [expr_list] | [expr_list comp_for]""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # [ - _skip_nl(ps) # Skip the line break after '[' (multi-line list) - if _match_op(ps, TokOp.Rsqb): - _advance(ps) - empty: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - node: rowptr[AST] = List(ps.pool, empty, ASTCtx.Load) - _set_pos(node, lineno, col, 0, 0) - return node - first: rowptr[AST] = _parse_expr(ps) - if _cur_kw(ps) == Keyword.For: - node = _parse_comprehension(ps, first, ASTKind.ListComp, lineno, col) - _expect_op(ps, TokOp.Rsqb) - return node - elts: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - elts.append(first) - while _accept_op(ps, TokOp.Comma): - _skip_nl(ps) - if _match_op(ps, TokOp.Rsqb): - break - e: rowptr[AST] = _parse_expr(ps) - elts.append(e) - _skip_nl(ps) # 末元素无尾逗号时收尾(base64.vp 多行列表) - _expect_op(ps, TokOp.Rsqb) - node = List(ps.pool, elts, ASTCtx.Load) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_brace(ps: rowptr[Parser]) -> rowptr[AST]: - """Brace expressions: {} | {set_items} | {dict_items} | {comp_for}""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # { - _skip_nl(ps) # Skip the line break after '{' (multi-line dict) - if _match_op(ps, TokOp.RBrace): - _advance(ps) - empty_k: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - empty_v: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - node: rowptr[AST] = Dict(ps.pool, empty_k, empty_v) - _set_pos(node, lineno, col, 0, 0) - return node - first: rowptr[AST] = _parse_expr(ps) - # dict: key: value - if _match_op(ps, TokOp.Colon): - _advance(ps) - val: rowptr[AST] = _parse_expr(ps) - if _cur_kw(ps) == Keyword.For: - # dict comprehension - gens: rowptr[list[rowptr[AST]]] = _parse_comp_for(ps) - node = DictComp(ps.pool, first, val, gens) - _set_pos(node, lineno, col, 0, 0) - _expect_op(ps, TokOp.RBrace) - return node - keys: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - values: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - keys.append(first) - values.append(val) - while _accept_op(ps, TokOp.Comma): - _skip_nl(ps) - if _match_op(ps, TokOp.RBrace): - break - k: rowptr[AST] = _parse_expr(ps) - _expect_op(ps, TokOp.Colon) - v: rowptr[AST] = _parse_expr(ps) - keys.append(k) - values.append(v) - _skip_nl(ps) # 末元素无尾逗号时收尾 - _expect_op(ps, TokOp.RBrace) - node = Dict(ps.pool, keys, values) - _set_pos(node, lineno, col, 0, 0) - return node - # set - if _cur_kw(ps) == Keyword.For: - node = _parse_comprehension(ps, first, ASTKind.SetComp, lineno, col) - _expect_op(ps, TokOp.RBrace) - return node - elts: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - elts.append(first) - while _accept_op(ps, TokOp.Comma): - _skip_nl(ps) # 括号内跨行集合字面量 - if _match_op(ps, TokOp.RBrace): - break - e: rowptr[AST] = _parse_expr(ps) - elts.append(e) - _skip_nl(ps) # 末元素无尾逗号时收尾 - _expect_op(ps, TokOp.RBrace) - node = Set(ps.pool, elts) - _set_pos(node, lineno, col, 0, 0) - return node - - -# ============================================================ -# f-string parsing -# ============================================================ - -def _parse_sub_expr(ps: rowptr[Parser], src: str) -> rowptr[AST]: - """Create a sub-lexer and parser to parse expressions (used for {expr} inside f-strings)""" - pool: rowptr[memhub.MemManager] = ps.pool - lx: rowptr[Lexer] = new_lexer(pool) - if lx is None: return None - _lexer_init(lx, src, pool) - tokens: rowptr[Token] = tokenize(lx) - if tokens is None: return None - sub_ps: rowptr[Parser] = new_parser(pool) - if sub_ps is None: return None - _parser_init(sub_ps, tokens, pool) - return _parse_test(sub_ps) - - -def _append_str_value(pool: rowptr[memhub.MemManager], - values: rowptr[list[rowptr[AST]]], - node: rowptr[AST]): - """将字符串节点展开加入 values(JoinedStr 展开其 values,其余直接追加)""" - if node is None: - return - if node.kind() == ASTKind.JoinedStr: - js_val: rowptr[JoinedStr] = (rowptr[JoinedStr])(node) - if js_val.values is not None: - n_v: t.CSizeT = js_val.values.__len__() - i_v: t.CSizeT - for i_v in range(n_v): - values.append(js_val.values.get(i_v)) - else: - values.append(node) - - -def _concat_str_nodes(pool: rowptr[memhub.MemManager], - a: rowptr[AST], b: rowptr[AST]) -> rowptr[AST]: - """合并相邻字符串字面量节点(Python 隐式拼接 "a" "b" → "ab") - - 纯常量字符串直接拼接文本;涉及 f-string(JoinedStr/FormattedValue)时 - 合并到 JoinedStr 的 values 列表。 - """ - if a is None: - return b - if b is None: - return a - # 两个都是常量字符串 → 直接拼接文本 - if a.kind() == ASTKind.Constant and b.kind() == ASTKind.Constant: - ca_c: rowptr[Constant] = (rowptr[Constant])(a) - cb_c: rowptr[Constant] = (rowptr[Constant])(b) - if ca_c.const_kind == CONST_STR and cb_c.const_kind == CONST_STR \ - and ca_c.str_val is not None and cb_c.str_val is not None: - la_c: t.CSizeT = string.strlen(ca_c.str_val) - lb_c: t.CSizeT = string.strlen(cb_c.str_val) - buf_c: str = pool.alloc(la_c + lb_c + 1) - if buf_c is not None: - string.memcpy(buf_c, ca_c.str_val, la_c) - string.memcpy(buf_c + la_c, cb_c.str_val, lb_c + 1) - return _new_const_str(pool, buf_c, a.lineno, a.col_offset, 0, 0, 0) - # 涉及 f-string → 合并到 JoinedStr - values_c: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](pool, 8) - _append_str_value(pool, values_c, a) - _append_str_value(pool, values_c, b) - if values_c.__len__() == 1: - return values_c.get(0) - node_c: rowptr[AST] = JoinedStr(pool, values_c) - _set_pos(node_c, a.lineno, a.col_offset, 0, 0) - return node_c - - -def _parse_fstring(ps: rowptr[Parser], content: str, - lineno: int, col: int) -> rowptr[AST]: - """Parse f-string content, building JoinedStr or FormattedValue nodes""" - pool: rowptr[memhub.MemManager] = ps.pool - values: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](pool, 8) - i: t.CSizeT = 0 - content_len: t.CSizeT = string.strlen(content) - # Literal text buffer for f-string content - lit_buf: str = pool.alloc(content_len + 1) - if lit_buf is None: return None - lit_pos: t.CSizeT = 0 - - while i < content_len: - ch: t.CChar = content[i] - if ch == '{': - # {{ → Literal { - if i + 1 < content_len and content[i + 1] == '{': - lit_buf[lit_pos] = '{' - lit_pos += 1 - i += 2 - continue - # Refresh literal buffer - if lit_pos > 0: - lit_buf[lit_pos] = '\0' - lit_node: rowptr[AST] = _new_const_str(pool, lit_buf, lineno, col) - values.append(lit_node) - lit_buf = pool.alloc(content_len + 1) - if lit_buf is None: break - lit_pos = 0 - # Find matching closing '}' while tracking nested '{}' and string literals inside the f-string - j: t.CSizeT = i + 1 - depth: int = 1 - conv_pos: t.CSizeT = 0 - fmt_pos: t.CSizeT = 0 - while j < content_len and depth > 0: - c: t.CChar = content[j] - if c == '\'' or c == '"': - # 跳过表达式块内的嵌套字符串字面量(PEP 701 同引号嵌套), - # 避免其内部字符干扰 {} 深度/转换/格式分隔符定位 - nq: t.CChar = c - j += 1 - while j < content_len: - c2: t.CChar = content[j] - if c2 == '\\': - j += 2 - continue - if c2 == nq: - j += 1 - break - j += 1 - continue - if c == '{': - depth += 1 - elif c == '}': - depth -= 1 - if depth == 0: - break - elif depth == 1: - if c == '!' and conv_pos == 0 and fmt_pos == 0: - if j + 1 < content_len: - nc: t.CChar = content[j + 1] - if nc == 's' or nc == 'r' or nc == 'a': - if j + 2 < content_len: - after: t.CChar = content[j + 2] - if after == ':' or after == '}': - conv_pos = j - elif j + 2 == content_len: - conv_pos = j - elif c == ':' and fmt_pos == 0: - fmt_pos = j - j += 1 - if depth != 0: - _record_parse_err(ps) - ps.error_count += 1 - break - # Extract expression text - expr_end: t.CSizeT = j - if conv_pos > 0: - expr_end = conv_pos - elif fmt_pos > 0: - expr_end = fmt_pos - expr_len: t.CSizeT = expr_end - i - 1 - if expr_len == 0: - _record_parse_err(ps) - ps.error_count += 1 - i = j + 1 - continue - expr_buf: str = pool.alloc(expr_len + 1) - if expr_buf is None: break - string.memcpy(expr_buf, content + i + 1, expr_len) - expr_buf[expr_len] = '\0' - # Parse expression text - expr_node: rowptr[AST] = _parse_sub_expr(ps, expr_buf) - # Conversion specifier - conversion: int = -1 - if conv_pos > 0: - conv_char: t.CChar = content[conv_pos + 1] - if conv_char == 's': - conversion = 115 - elif conv_char == 'r': - conversion = 114 - elif conv_char == 'a': - conversion = 97 - # Format specifier - format_spec: rowptr[AST] = None - if fmt_pos > 0: - spec_len: t.CSizeT = j - fmt_pos - 1 - if spec_len > 0: - spec_buf: str = pool.alloc(spec_len + 1) - if spec_buf is not None: - string.memcpy(spec_buf, content + fmt_pos + 1, spec_len) - spec_buf[spec_len] = '\0' - format_spec = _new_const_str(pool, spec_buf, lineno, col) - fv: rowptr[AST] = FormattedValue(pool, expr_node, conversion, format_spec) - _set_pos(fv, lineno, col, 0, 0) - values.append(fv) - i = j + 1 - elif ch == '}': - if i + 1 < content_len and content[i + 1] == '}': - lit_buf[lit_pos] = '}' - lit_pos += 1 - i += 2 - else: - _record_parse_err(ps) - ps.error_count += 1 - i += 1 - else: - lit_buf[lit_pos] = ch - lit_pos += 1 - i += 1 - - # Refresh remaining literal buffer - if lit_pos > 0: - lit_buf[lit_pos] = '\0' - lit_node2: rowptr[AST] = _new_const_str(pool, lit_buf, lineno, col) - values.append(lit_node2) - - # If no content, return empty string - if values.__len__() == 0: - return _new_const_str(pool, "", lineno, col) - # If only one FormattedValue and no literal, return FormattedValue - if values.__len__() == 1: - first: rowptr[AST] = values.get(0) - if first.kind() == ASTKind.FormattedValue: - return first - node: rowptr[AST] = JoinedStr(pool, values) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_comprehension(ps: rowptr[Parser], elt: rowptr[AST], - vtype: int, lineno: int, col: int) -> rowptr[AST]: - """General comprehension: elt comp_for""" - gens: rowptr[list[rowptr[AST]]] = _parse_comp_for(ps) - node: rowptr[AST] = None - if vtype == ASTKind.ListComp: - node = ListComp(ps.pool, elt, gens) - elif vtype == ASTKind.SetComp: - node = SetComp(ps.pool, elt, gens) - else: - node = GeneratorExp(ps.pool, elt, gens) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_comp_for(ps: rowptr[Parser]) -> rowptr[list[rowptr[AST]]]: - """comp_for: ['async'] 'for' exprlist 'in' or_test [comp_if] (comp_for)* - Returns generators list。""" - gens: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - while True: - is_async: int = 0 - if _cur_kw(ps) == Keyword.Async: - is_async = 1 - _advance(ps) - if _cur_kw(ps) != Keyword.For: - break - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - target: rowptr[AST] = _parse_target_list(ps) - if _cur_kw(ps) != Keyword.In: - ps.error_count += 1 - break - _advance(ps) - iter_: rowptr[AST] = _parse_or_test(ps) - # Collect ifs to list - ifs: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - while _cur_kw(ps) == Keyword.If: - _advance(ps) - cond: rowptr[AST] = _parse_or_test(ps) - ifs.append(cond) - comp: rowptr[AST] = Comprehension(ps.pool, target, iter_, ifs, is_async) - _set_pos(comp, lineno, col, 0, 0) - gens.append(comp) - return gens - - -def _parse_target_list(ps: rowptr[Parser]) -> rowptr[AST]: - """Parse assignment target list (comma-separated)""" - first: rowptr[AST] = _parse_target(ps) - if _match_op(ps, TokOp.Comma): - elts: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - elts.append(first) - while _accept_op(ps, TokOp.Comma): - if _cur_kw(ps) == Keyword.In: - break - e: rowptr[AST] = _parse_target(ps) - elts.append(e) - node: rowptr[AST] = Tuple(ps.pool, elts, ASTCtx.Store) - _set_pos(node, first.lineno, first.col_offset, 0, 0) - return node - return first - - -def _parse_target(ps: rowptr[Parser]) -> rowptr[AST]: - """Parse assignment target""" - if _match_op(ps, TokOp.Star): - _advance(ps) - inner: rowptr[AST] = _parse_atom_expr(ps) - node: rowptr[AST] = Starred(ps.pool, inner, ASTCtx.Store) - _set_pos(node, inner.lineno, inner.col_offset, 0, 0) - return node - if _match_op(ps, TokOp.LPar): - _advance(ps) - _skip_nl(ps) - t_: rowptr[AST] = _parse_target_list(ps) - _skip_nl(ps) - _expect_op(ps, TokOp.RPar) - return t_ - if _match_op(ps, TokOp.Lsqb): - _advance(ps) - _skip_nl(ps) - first: rowptr[AST] = _parse_target(ps) - elts: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - elts.append(first) - while _accept_op(ps, TokOp.Comma): - _skip_nl(ps) - if _match_op(ps, TokOp.Rsqb): - break - e: rowptr[AST] = _parse_target(ps) - elts.append(e) - _expect_op(ps, TokOp.Rsqb) - node = List(ps.pool, elts, ASTCtx.Store) - _set_pos(node, first.lineno, first.col_offset, 0, 0) - return node - # Normal atom_expr, ctx=Store - node = _parse_atom_expr(ps) - if node is not None: - _set_ctx(node, ASTCtx.Store) - return node - - -def _parse_yield(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # yield - if _cur_kw(ps) == Keyword.From: - _advance(ps) - val: rowptr[AST] = _parse_expr(ps) - node: rowptr[AST] = YieldFrom(ps.pool, val) - _set_pos(node, lineno, col, 0, 0) - return node - val2: rowptr[AST] = None - if not _match_op(ps, TokOp.RPar) and _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker: - if _cur_type(ps) == TokenType.Op and (_cur_op(ps) == TokOp.Comma or _cur_op(ps) == TokOp.Rsqb): - pass - else: - val2 = _parse_test(ps) - node = Yield(ps.pool, val2) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_varargslist(ps: rowptr[Parser], is_lambda: int = 0) -> rowptr[AST]: - """Parse function parameter list - New API: args/defaults/kw_defaults are lists, vararg/kwarg are fields.""" - args: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - vararg: rowptr[AST] = None - kwarg: rowptr[AST] = None - defaults: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - kw_defaults: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - - while True: - _skip_nl(ps) - if _match_op(ps, TokOp.RPar): - break - # *args 或 * (kwonly separated) - if _match_op(ps, TokOp.Star): - _advance(ps) - _skip_nl(ps) - if _cur_type(ps) == TokenType.Name: - vararg = _parse_arg_def(ps, is_lambda) - if not _accept_op(ps, TokOp.Comma): - break - _skip_nl(ps) - continue - # **kwargs - if _match_op(ps, TokOp.StarStar): - _advance(ps) - _skip_nl(ps) - kwarg = _parse_arg_def(ps, is_lambda) - if not _accept_op(ps, TokOp.Comma): - break - _skip_nl(ps) - continue - # / Delimiter (posonly end)— New API: posonly/regular are not distinguished - if _match_op(ps, TokOp.Slash): - _advance(ps) - if not _accept_op(ps, TokOp.Comma): - break - _skip_nl(ps) - continue - # Normal parameter - a: rowptr[AST] = _parse_arg_def(ps, is_lambda) - if a is None: break - args.append(a) - if _match_op(ps, TokOp.Equal): - _advance(ps) - d: rowptr[AST] = _parse_test(ps) - defaults.append(d) - if not _accept_op(ps, TokOp.Comma): - break - _skip_nl(ps) - return Arguments(ps.pool, args, vararg, kwarg, defaults, kw_defaults) - - -def _parse_type_ann(ps: rowptr[Parser]) -> rowptr[AST]: - """解析类型注解,支持 C 风格指针后缀(* / **)。 - - int* → rowptr[int] (i32*) - int** → (rowptr[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)。 - """ - before_ec: int = ps.error_count - base: rowptr[AST] = _parse_test(ps) - if base is None: - _record_parse_err(ps) # 注解解析失败:记录具体位置(error_count 已在 _parse_test 内增加) - return None - ptr_level: int = 0 - # 回收垃圾节点(_parse_test 对后缀 * / ** 的消费产物) - if base.kind() == ASTKind.BinOp: - gb: rowptr[BinOp] = (rowptr[BinOp])(base) - if gb.op == OpKind.Mult and gb.right is None: - ptr_level += 1 - base = gb.left - elif gb.op == OpKind.Pow: - if gb.right is None: - ptr_level += 2 - base = gb.left - elif gb.right.kind() == ASTKind.UnaryOp: - gu: rowptr[UnaryOp] = (rowptr[UnaryOp])(gb.right) - if gu.op == OpKind.Deref and gu.operand is None: - ptr_level += 3 - base = gb.left - if ptr_level > 0: - ps.error_count = before_ec # 回收成功,恢复错误计数避免整文件解析失败 - # 循环消费残留后缀('**' 是词法单 token 计 2 层,'*' 计 1 层) - while _match_op(ps, TokOp.StarStar): - _advance(ps) - ptr_level += 2 - while _match_op(ps, TokOp.Star): - _advance(ps) - ptr_level += 1 - # 嵌套构造 rowptr[base](n 层) - li_an: int = 0 - while li_an < ptr_level: - cptr_an: rowptr[AST] = Attribute(ps.pool, - Name(ps.pool, "t", ASTCtx.Load), - "CPtr", ASTCtx.Load) - base = BinOp(ps.pool, base, OpKind.BitOr, cptr_an) - li_an += 1 - return base - - -def _parse_arg_def(ps: rowptr[Parser], is_lambda: int = 0) -> rowptr[AST]: - """arg: TokenType.Name [':' annotation]""" - if _cur_type(ps) != TokenType.Name: - ps.error_count += 1 - return None - name_str: str = _cur_str(ps) - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - annotation: rowptr[AST] = None - if is_lambda == 0: - if _accept_op(ps, TokOp.Colon): - annotation = _parse_type_ann(ps) - result: rowptr[AST] = Arg(ps.pool, name_str, annotation) - _set_pos(result, lineno, col, 0, 0) - return result - - -# ============================================================ -# Statement parsing (Design: directly append to parent, do not return a list) -# ============================================================ - -def _parse_suite_into_children(ps: rowptr[Parser], parent: rowptr[AST]): - """Parse statement suite, directly append to parent's children. - suite: TokenType.NewLine TokenType.Indent stmt+ TokenType.Dedent | simple_stmt""" - _skip_nl(ps) - if _cur_type(ps) == TokenType.NewLine: - _advance(ps) - _skip_nl(ps) - if _cur_type(ps) != TokenType.Indent: - ps.error_count += 1 - return - _advance(ps) - sidx: int = 0 - while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker: - sidx += 1 - # Decorator (class body methods can have decorators) - if _match_op(ps, TokOp.At): - ps.pending_decorators = _parse_decorators(ps) - before_cur: rowptr[Token] = ps.cur - _parse_statement_into(ps, parent) - # Defensive: if parse did not advance token, force advance to avoid infinite loop - if ps.cur is before_cur: - ps.error_count += 1 - _advance(ps) - _skip_nl(ps) - _expect_type(ps, TokenType.Dedent) - return - # Single-line suite - _parse_simple_stmt_into(ps, parent) - - -def _parse_statement_into(ps: rowptr[Parser], parent: rowptr[AST]): - """Parse statement (compound or simple), append to the parent.""" - ttype: int = _cur_type(ps) - kw: int = _cur_kw(ps) - if ttype == TokenType.Name: - if kw == Keyword.If or kw == Keyword.While or kw == Keyword.For or kw == Keyword.Try: - stmt: rowptr[AST] = _parse_compound(ps) - if stmt is not None: - parent.append(stmt) - return - if kw == Keyword.With or _is_funcdef_kw(ps, kw) != 0 or kw == Keyword.Class: - stmt = _parse_compound(ps) - if stmt is not None: - parent.append(stmt) - return - if (kw == Keyword.Match and _is_match_stmt(ps) != 0) or kw == Keyword.Async: - stmt = _parse_compound(ps) - if stmt is not None: - parent.append(stmt) - return - _parse_simple_stmt_into(ps, parent) - - -def _parse_simple_stmt_into(ps: rowptr[Parser], parent: rowptr[AST]): - """simple_stmt: small_stmt (';' small_stmt)* [';'] TokenType.NewLine""" - first: rowptr[AST] = _parse_small_stmt(ps) - if first is not None: - parent.append(first) - while _accept_op(ps, TokOp.Semi): - if _cur_type(ps) == TokenType.NewLine or _cur_type(ps) == TokenType.EndMarker: - break - s: rowptr[AST] = _parse_small_stmt(ps) - if s is None: break - parent.append(s) - # 先跳过 Nl(括号内换行,如多行函数调用参数间的换行,属合法 token) - _skip_nl(ps) - # 语句结束检查:simple_stmt 必须以换行/文件尾结束。 - # 残留 token(如相邻表达式 `func12344444 avg3(...)` 被静默拆成多条语句、 - # `def` 缺失的关键字函数定义残留)是语法错误(fail-fast),禁止静默, - # 并仿 Python 报告错误位置(SyntaxError at line/col)。 - if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker: - _record_parse_err(ps) - err_str: str = _cur_str(ps) - if err_str is None: - err_str = "(none)" - # 定位:从链表头找 cur,记录前一个 token 与下一个 token 的上下文 - prev_tok: rowptr[Token] = None - walk_tok: rowptr[Token] = ps.tokens - while walk_tok is not None and walk_tok is not ps.cur: - prev_tok = walk_tok - walk_tok = walk_tok.next - pv_str: str = "(head)" - pv_line: int = 0 - pv_col: int = 0 - if prev_tok is not None: - pv_str = prev_tok.str_val - pv_line = prev_tok.lineno - pv_col = prev_tok.col_offset - nxt_tok: rowptr[Token] = ps.cur.next - nxt_str: str = "(none)" - nxt_line: int = 0 - nxt_col: int = 0 - if nxt_tok is not None: - nxt_str = nxt_tok.str_val - nxt_line = nxt_tok.lineno - nxt_col = nxt_tok.col_offset - stdio.printf("[AST] SyntaxError: line %d col %d (end %d %d), 意外的 token '%s' (type=%d) | prev='%s' (line %d col %d) | next='%s' (line %d col %d)\n", - _g_parse_err_line, _g_parse_err_col, ps.cur.end_lineno, ps.cur.end_col_offset, - err_str, _cur_type(ps), - pv_str, pv_line, pv_col, - nxt_str, nxt_line, nxt_col) - ps.error_count += 1 - return - if _cur_type(ps) == TokenType.NewLine: - _advance(ps) - - -def _parse_small_stmt(ps: rowptr[Parser]) -> rowptr[AST]: - """small_stmt: various simple statements""" - kw: int = _cur_kw(ps) - if kw == Keyword.Pass: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - node: rowptr[AST] = Pass(ps.pool) - _set_pos(node, lineno, col, 0, 0) - return node - if kw == Keyword.Break: - lineno = _cur_lineno(ps) - col = _cur_col(ps) - _advance(ps) - node = Break(ps.pool) - _set_pos(node, lineno, col, 0, 0) - return node - if kw == Keyword.Continue: - lineno = _cur_lineno(ps) - col = _cur_col(ps) - _advance(ps) - node = Continue(ps.pool) - _set_pos(node, lineno, col, 0, 0) - return node - if kw == Keyword.Return: - return _parse_return(ps) - if kw == Keyword.Raise: - return _parse_raise(ps) - if kw == Keyword.Global: - return _parse_global(ps) - if kw == Keyword.Nonlocal: - return _parse_nonlocal(ps) - if kw == Keyword.Import: - return _parse_import(ps) - if kw == Keyword.From: - return _parse_from_import(ps) - if kw == Keyword.Del: - return _parse_del(ps) - if kw == Keyword.Assert: - return _parse_assert(ps) - if kw == Keyword.Yield: - return _parse_expr_stmt(ps, _parse_yield(ps)) - # Expression statement / assignment / augassign / annassign - return _parse_expr_stmt(ps, None) - - -def _maybe_tuple(ps: rowptr[Parser], first: rowptr[AST]) -> rowptr[AST]: - """If there is a comma after first, collect the comma-separated expressions into a Tuple.""" - if first is None: return None - if not _match_op(ps, TokOp.Comma): - return first - lineno: int = first.lineno - col: int = first.col_offset - elts: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - elts.append(first) - _accept_op(ps, TokOp.Comma) - while True: - tt: int = _cur_type(ps) - if tt == TokenType.NewLine or tt == TokenType.EndMarker: - break - if tt == TokenType.Op: - op: int = _cur_op(ps) - if op == TokOp.Equal or op == TokOp.Semi: - break - e: rowptr[AST] = _parse_expr(ps) - if e is None: break - elts.append(e) - if not _accept_op(ps, TokOp.Comma): - break - node: rowptr[AST] = Tuple(ps.pool, elts, ASTCtx.Load) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_testlist(ps: rowptr[Parser]) -> rowptr[AST]: - """Parse a comma-separated list of expressions, if there’s more than one, form a tuple.""" - first: rowptr[AST] = _parse_expr(ps) - return _maybe_tuple(ps, first) - - -def _parse_expr_stmt(ps: rowptr[Parser], first: rowptr[AST]) -> rowptr[AST]: - """Expression statement, handle assignment/augassign/annassign""" - if first is None: - first = _parse_testlist(ps) - else: - first = _maybe_tuple(ps, first) - if first is None: - return None - lineno: int = first.lineno - col: int = first.col_offset - # Assignment - if _match_op(ps, TokOp.Equal): - targets: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - targets.append(first) - _fix_store_ctx(first) - value: rowptr[AST] = None - accept_result: int = _accept_op(ps, TokOp.Equal) - while accept_result: - v: rowptr[AST] = _parse_testlist(ps) - if v is None: break - if value is not None: - targets.append(value) - _fix_store_ctx(value) - value = v - accept_result = _accept_op(ps, TokOp.Equal) - node: rowptr[AST] = Assign(ps.pool, targets, value) - _set_pos(node, lineno, col, 0, 0) - return node - # augassign - if _cur_type(ps) == TokenType.Op: - op_id: int = _cur_op(ps) - aug_op: int = _augop_from_op(op_id) - if aug_op != OpKind.NoneOp: - _advance(ps) - value2: rowptr[AST] = _parse_testlist(ps) - _fix_store_ctx(first) - node2: rowptr[AST] = AugAssign(ps.pool, first, aug_op, value2) - _set_pos(node2, lineno, col, 0, 0) - return node2 - # annassign: target ':' annotation [= value] - if _match_op(ps, TokOp.Colon): - _advance(ps) - annotation: rowptr[AST] = _parse_type_ann(ps) - value3: rowptr[AST] = None - if _accept_op(ps, TokOp.Equal): - value3 = _parse_testlist(ps) - if ps.pool is None: - return None - _fix_store_ctx(first) - if ps.pool is None: - return None - node3: rowptr[AST] = AnnAssign(ps.pool, first, annotation, value3, 1) - _set_pos(node3, lineno, col, 0, 0) - return node3 - # Expression statement - node4: rowptr[AST] = Expr(ps.pool, first) - _set_pos(node4, lineno, col, 0, 0) - return node4 - - -def _parse_return(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - val: rowptr[AST] = None - if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker and _cur_type(ps) != TokenType.Op: - val = _parse_testlist(ps) - elif _cur_type(ps) == TokenType.Op and _cur_op(ps) != TokOp.Semi: - val = _parse_testlist(ps) - node: rowptr[AST] = Return(ps.pool, val) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_raise(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - exc: rowptr[AST] = None - cause: rowptr[AST] = None - if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker: - exc = _parse_test(ps) - if _cur_kw(ps) == Keyword.From: - _advance(ps) - cause = _parse_test(ps) - node: rowptr[AST] = Raise(ps.pool, exc, cause) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_global(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - names: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - if _cur_type(ps) == TokenType.Name: - n: rowptr[AST] = Name(ps.pool, _cur_str(ps), ASTCtx.Store) - _set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0) - names.append(n) - _advance(ps) - while _accept_op(ps, TokOp.Comma): - if _cur_type(ps) != TokenType.Name: break - n = Name(ps.pool, _cur_str(ps), ASTCtx.Store) - _set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0) - names.append(n) - _advance(ps) - node: rowptr[AST] = Global(ps.pool, names) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_nonlocal(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - names: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - if _cur_type(ps) == TokenType.Name: - n: rowptr[AST] = Name(ps.pool, _cur_str(ps), ASTCtx.Store) - _set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0) - names.append(n) - _advance(ps) - while _accept_op(ps, TokOp.Comma): - if _cur_type(ps) != TokenType.Name: break - n = Name(ps.pool, _cur_str(ps), ASTCtx.Store) - _set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0) - names.append(n) - _advance(ps) - node: rowptr[AST] = Nonlocal(ps.pool, names) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_import(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - names: rowptr[list[rowptr[AST]]] = _parse_alias_list(ps) - node: rowptr[AST] = Import(ps.pool, names) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_from_import(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - # Parse module (possibly with dots) - level: int = 0 - while _match_op(ps, TokOp.Dot): - level += 1 - _advance(ps) - while _match_op(ps, TokOp.Ellipsis): - level += 3 - _advance(ps) - module_name: str = None - # The token type for keywords (like import) is also Name, so they must be excluded using kw_subtype, - # otherwise the `import` in `from . import` would be mistakenly treated as a module_name. - # Note: only `import` itself should be excluded. Module names can be keywords (like in `from .match import ...`, - # where match is a Viper match statement keyword), requiring NotKeyword would cause - # `from .match import` to fail parsing and the dependency scan would miss that module. - if _cur_type(ps) == TokenType.Name and _cur_kw(ps) != Keyword.Import: - module_name = _cur_str(ps) - _advance(ps) - while _match_op(ps, TokOp.Dot): - _advance(ps) - if _cur_type(ps) == TokenType.Name and _cur_kw(ps) == Keyword.NotKeyword: - next_name: str = _cur_str(ps) - mlen: int = string.strlen(module_name) - nlen: int = string.strlen(next_name) - new_mod: str = ps.pool.alloc(mlen + 1 + nlen + 1) - if new_mod is not None: - string.strcpy(new_mod, module_name) - new_mod[mlen] = '.' - string.strcpy(new_mod + mlen + 1, next_name) - module_name = new_mod - _advance(ps) - if _cur_kw(ps) != Keyword.Import: - ps.error_count += 1 - return None - _advance(ps) - _skip_nl(ps) - # import * - if _match_op(ps, TokOp.Star): - _advance(ps) - star_alias: rowptr[AST] = Alias(ps.pool, "*", None) - _set_pos(star_alias, lineno, col, 0, 0) - star_names: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - star_names.append(star_alias) - node: rowptr[AST] = ImportFrom(ps.pool, module_name, star_names, level) - _set_pos(node, lineno, col, 0, 0) - return node - # ( names ) or names - if _match_op(ps, TokOp.LPar): - _advance(ps) - _skip_nl(ps) - names: rowptr[list[rowptr[AST]]] = _parse_alias_list(ps) - _skip_nl(ps) - _expect_op(ps, TokOp.RPar) - else: - names = _parse_alias_list(ps) - node: rowptr[AST] = ImportFrom(ps.pool, module_name, names, level) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_alias_list(ps: rowptr[Parser]) -> rowptr[list[rowptr[AST]]]: - """Parse alias list.""" - names: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - while True: - if _cur_type(ps) != TokenType.Name: - break - name_str: str = _cur_str(ps) - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - # dotted name - while _match_op(ps, TokOp.Dot): - _advance(ps) - if _cur_type(ps) == TokenType.Name: - next_n: str = _cur_str(ps) - nmlen: int = string.strlen(name_str) - nxtlen: int = string.strlen(next_n) - new_name: str = ps.pool.alloc(nmlen + 1 + nxtlen + 1) - if new_name is not None: - string.strcpy(new_name, name_str) - new_name[nmlen] = '.' - string.strcpy(new_name + nmlen + 1, next_n) - name_str = new_name - _advance(ps) - asname: str = None - if _cur_kw(ps) == Keyword.As: - _advance(ps) - if _cur_type(ps) == TokenType.Name: - asname = _cur_str(ps) - _advance(ps) - a: rowptr[AST] = Alias(ps.pool, name_str, asname) - _set_pos(a, lineno, col, 0, 0) - names.append(a) - if not _accept_op(ps, TokOp.Comma): - break - _skip_nl(ps) - return names - - -def _parse_del(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - targets: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - while True: - t_: rowptr[AST] = _parse_atom_expr(ps) - if t_ is None: break - _set_ctx(t_, ASTCtx.Del) - targets.append(t_) - if not _accept_op(ps, TokOp.Comma): - break - node: rowptr[AST] = Delete(ps.pool, targets) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_assert(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - test: rowptr[AST] = _parse_test(ps) - msg: rowptr[AST] = None - if _accept_op(ps, TokOp.Comma): - msg = _parse_test(ps) - node: rowptr[AST] = Assert(ps.pool, test, msg) - _set_pos(node, lineno, col, 0, 0) - return node - - -# ============================================================ -# Compound statements -# ============================================================ - -def _parse_compound(ps: rowptr[Parser]) -> rowptr[AST]: - kw: int = _cur_kw(ps) - if kw == Keyword.If: - return _parse_if(ps) - if kw == Keyword.While: - return _parse_while(ps) - if kw == Keyword.For: - return _parse_for(ps, 0) - if kw == Keyword.Try: - return _parse_try(ps) - if kw == Keyword.With: - return _parse_with(ps, 0) - if _is_funcdef_kw(ps, kw) != 0: - return _parse_funcdef(ps, 0) - if kw == Keyword.Class: - return _parse_classdef(ps) - if kw == Keyword.Async: - _advance(ps) - if _is_funcdef_kw(ps, _cur_kw(ps)) != 0: - return _parse_funcdef(ps, 1) - if _cur_kw(ps) == Keyword.For: - return _parse_for(ps, 1) - if _cur_kw(ps) == Keyword.With: - return _parse_with(ps, 1) - ps.error_count += 1 - return None - if kw == Keyword.Match and _is_match_stmt(ps) != 0: - return _parse_match(ps) - ps.error_count += 1 - return None - - -def _parse_if(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # if - test: rowptr[AST] = _parse_test(ps) - _expect_op(ps, TokOp.Colon) - # First construct with empty orelse, then fill it in later - empty: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - node: rowptr[If] = If(ps.pool, test, empty) - _parse_suite_into_children(ps, node) - orelse: rowptr[list[rowptr[AST]]] = _parse_if_tail(ps) - node.orelse = orelse - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_if_tail(ps: rowptr[Parser]) -> rowptr[list[rowptr[AST]]]: - """Parse elif/else chain and return orelse list. - - elif: orelse = list containing single If node (nested) - else: orelse = statement list - """ - if _cur_kw(ps) == Keyword.Elif: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) - elif_test: rowptr[AST] = _parse_test(ps) - _expect_op(ps, TokOp.Colon) - empty: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - elif_node: rowptr[If] = If(ps.pool, elif_test, empty) - _parse_suite_into_children(ps, elif_node) - elif_orelse: rowptr[list[rowptr[AST]]] = _parse_if_tail(ps) - elif_node.orelse = elif_orelse - _set_pos(elif_node, lineno, col, 0, 0) - orelse: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - orelse.append(elif_node) - return orelse - if _cur_kw(ps) == Keyword.Else: - _advance(ps) - _expect_op(ps, TokOp.Colon) - scratch: rowptr[Module] = Module(ps.pool) - _parse_suite_into_children(ps, scratch) - return scratch.children - return list[rowptr[AST]](ps.pool, 8) # 空 list - - -# ============================================================ -# Parsing Compound Statements (Continued) -# ============================================================ - -def _parse_while(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # while - test: rowptr[AST] = _parse_test(ps) - _expect_op(ps, TokOp.Colon) - empty: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - node: rowptr[While] = While(ps.pool, test, empty) - _parse_suite_into_children(ps, node) - if _cur_kw(ps) == Keyword.Else: - _advance(ps) - _expect_op(ps, TokOp.Colon) - scratch: rowptr[Module] = Module(ps.pool) - _parse_suite_into_children(ps, scratch) - node.orelse = scratch.children - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_for(ps: rowptr[Parser], is_async: int) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # for - target: rowptr[AST] = _parse_target_list(ps) - if _cur_kw(ps) != Keyword.In: - ps.error_count += 1 - return None - _advance(ps) - iter_: rowptr[AST] = _parse_or_test(ps) - _expect_op(ps, TokOp.Colon) - empty: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - node: rowptr[For] = For(ps.pool, target, iter_, empty, is_async) - _fix_store_ctx(target) - _parse_suite_into_children(ps, node) - if _cur_kw(ps) == Keyword.Else: - _advance(ps) - _expect_op(ps, TokOp.Colon) - scratch: rowptr[Module] = Module(ps.pool) - _parse_suite_into_children(ps, scratch) - node.orelse = scratch.children - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_try(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # try - _expect_op(ps, TokOp.Colon) - empty: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - node: rowptr[Try] = Try(ps.pool, empty, empty, empty) - _parse_suite_into_children(ps, node) - # handlers (except) - handlers: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - while _cur_kw(ps) == Keyword.Except: - h: rowptr[AST] = _parse_except_handler(ps) - if h is not None: - handlers.append(h) - node.handlers = handlers - # else - if _cur_kw(ps) == Keyword.Else: - _advance(ps) - _expect_op(ps, TokOp.Colon) - scratch: rowptr[Module] = Module(ps.pool) - _parse_suite_into_children(ps, scratch) - node.orelse = scratch.children - # finally - if _cur_kw(ps) == Keyword.Finally: - _advance(ps) - _expect_op(ps, TokOp.Colon) - scratch2: rowptr[Module] = Module(ps.pool) - _parse_suite_into_children(ps, scratch2) - node.finalbody = scratch2.children - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_except_handler(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # except - type_: rowptr[AST] = None - name: str = None - if _cur_op(ps) != TokOp.Colon: - type_ = _parse_test(ps) - if _cur_kw(ps) == Keyword.As: - _advance(ps) - if _cur_type(ps) == TokenType.Name: - name = _cur_str(ps) - _advance(ps) - _expect_op(ps, TokOp.Colon) - node: rowptr[AST] = ExceptHandler(ps.pool, type_, name) - _parse_suite_into_children(ps, node) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_with(ps: rowptr[Parser], is_async: int) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # with - items: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - # items - while True: - item: rowptr[AST] = _parse_with_item(ps) - if item is None: break - items.append(item) - if not _accept_op(ps, TokOp.Comma): - break - # Check if we can continue with a newline (with (...): form) - if _cur_type(ps) == TokenType.NewLine or _cur_type(ps) == TokenType.Nl: - break - _expect_op(ps, TokOp.Colon) - node: rowptr[With] = With(ps.pool, items, is_async) - _parse_suite_into_children(ps, node) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_with_item(ps: rowptr[Parser]) -> rowptr[AST]: - """with_item: test ['as' expr]""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - context_expr: rowptr[AST] = _parse_or_test(ps) - if context_expr is None: return None - optional_vars: rowptr[AST] = None - if _cur_kw(ps) == Keyword.As: - _advance(ps) - optional_vars = _parse_target(ps) - _fix_store_ctx(optional_vars) - node: rowptr[AST] = WithItem(ps.pool, context_expr, optional_vars) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_funcdef(ps: rowptr[Parser], is_async: int) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - is_extern_fd: int = 0 - is_export_fd: int = 0 - is_define_fd: int = 0 - if _cur_kw(ps) == Keyword.Extern: - # extern 声明:extern [def] A(...) [-> T] [": pass" 可选] - is_extern_fd = 1 - _advance(ps) # extern - if _cur_kw(ps) == Keyword.Def: - _advance(ps) - elif _cur_kw(ps) == Keyword.Export: - # export 声明:export [def] A(...) [-> T] [": pass" 可选] - is_export_fd = 1 - _advance(ps) # export - if _cur_kw(ps) == Keyword.Def: - _advance(ps) - elif _cur_kw(ps) == Keyword.Define: - # define 宏函数:语法与 def 完全一致,返回注解自动追加 t.CDefine - # 标记为宏函数(StubGen 保留原样 → C #define / alwaysinline 内联) - is_define_fd = 1 - _advance(ps) # define - else: - _advance(ps) # def / fn / ... - if _cur_type(ps) != TokenType.Name: - ps.error_count += 1 - return None - name: str = _cur_str(ps) - _advance(ps) - # PEP 695 泛型函数类型参数: def add[T](a: T, b: T) — 解析并存入 type_params。 - # (与 class 解析器一致;之前缺失此处理导致 _expect_op(Colon) 失败、函数体被 - # 误提升到模块级——b = Box(42) 被当作模块级全局注册,函数内赋值误报 - # "需用 global 声明"。翻译时 type_params 映射到 int 兜底。) - type_params_fd: rowptr[list[str]] = None - if _accept_op(ps, TokOp.Lsqb): - type_params_fd = list[str](ps.pool, 8) - while not _match_op(ps, TokOp.Rsqb): - if _cur_type(ps) == TokenType.EndMarker: - break - if _cur_type(ps) == TokenType.Name: - tp_name_fd: str = _cur_str(ps) - _advance(ps) - # 跳过可选 bound/约束: [T: bound] 或 [T: (A, B)] - if _match_op(ps, TokOp.Colon): - _advance(ps) - _parse_test(ps) - type_params_fd.append(tp_name_fd) - if not _accept_op(ps, TokOp.Comma): - break - _expect_op(ps, TokOp.Rsqb) - args: rowptr[AST] = None - if _accept_op(ps, TokOp.LPar): - args = _parse_varargslist(ps, 0) - _expect_op(ps, TokOp.RPar) - returns: rowptr[AST] = None - if _accept_op(ps, TokOp.RArrow): - returns = _parse_type_ann(ps) - if is_extern_fd: - # extern 声明:返回注解改写为 <原类型> | t.State,复用既有 - # State/外部 C 函数识别路径(vpi 注解含 "State" → 定义端裸名 + 不生成函数体) - state_attr_fd: rowptr[AST] = Attribute(ps.pool, - Name(ps.pool, "t", ASTCtx.Load), - "State", ASTCtx.Load) - if returns != None: - returns = BinOp(ps.pool, returns, OpKind.BitOr, state_attr_fd) - else: - returns = state_attr_fd - elif is_export_fd: - # export 声明:返回注解改写为 <原类型> | t.CExport,复用既有 - # CExport 识别路径(vpi 注解含 "CExport" → 定义端裸名 + 不加模块 SHA1 头) - export_attr_fd: rowptr[AST] = Attribute(ps.pool, - Name(ps.pool, "t", ASTCtx.Load), - "CExport", ASTCtx.Load) - if returns != None: - returns = BinOp(ps.pool, returns, OpKind.BitOr, export_attr_fd) - else: - returns = export_attr_fd - elif is_define_fd: - # define 宏函数:返回注解改写为 <原类型> | t.CDefine,复用既有 - # CDefine 宏函数识别路径(StubGen 保留原样 → C #define / alwaysinline) - define_attr_fd: rowptr[AST] = Attribute(ps.pool, - Name(ps.pool, "t", ASTCtx.Load), - "CDefine", ASTCtx.Load) - if returns != None: - returns = BinOp(ps.pool, returns, OpKind.BitOr, define_attr_fd) - else: - returns = define_attr_fd - dec_list: rowptr[list[rowptr[AST]]] = ps.pending_decorators - ps.pending_decorators = None - node: rowptr[FunctionDef] = FunctionDef(ps.pool, name, args, dec_list, returns, is_async, type_params_fd) - if is_extern_fd: - node.flags = node.flags | FLAG_IS_EXTERN - if is_export_fd: - node.flags = node.flags | FLAG_IS_EXPORT - if is_define_fd: - node.flags = node.flags | FLAG_IS_DEFINE - if (is_extern_fd == 0 and is_export_fd == 0 and is_define_fd == 0) or _match_op(ps, TokOp.Colon): - _expect_op(ps, TokOp.Colon) - _parse_suite_into_children(ps, node) - else: - # extern/export 无 ': pass' 形式(extern A(...) -> int):body 为空,仅声明。 - # 必须消费本行 NewLine:模块级/类体语句循环只 _skip_nl(Nl), - # 残留 NewLine 会被下一轮当表达式解析(_parse_atom → error_count++) - # → parse_tokens 返回 None(TPV 编译 extern 文件报 7 个扫描失败的根因) - if _cur_type(ps) == TokenType.NewLine: - _advance(ps) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_classdef(ps: rowptr[Parser]) -> rowptr[AST]: - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # class - if _cur_type(ps) != TokenType.Name: - ps.error_count += 1 - return None - name: str = _cur_str(ps) - _advance(ps) - # Parse type parameters [T] or [T, U] (PEP 695 generic class syntax) - type_params: rowptr[list[str]] = None - if _accept_op(ps, TokOp.Lsqb): - type_params = list[str](ps.pool, 8) - while not _match_op(ps, TokOp.Rsqb): - if _cur_type(ps) == TokenType.EndMarker: break - if _cur_type(ps) == TokenType.Name: - tp_name: str = _cur_str(ps) - _advance(ps) - # Skip optional bound/constraint: [T: bound] or [T: (A, B)] - if _match_op(ps, TokOp.Colon): - _advance(ps) - _parse_test(ps) - type_params.append(tp_name) - if not _accept_op(ps, TokOp.Comma): - break - _expect_op(ps, TokOp.Rsqb) - bases: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - keywords: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - # bases / keywords - if _accept_op(ps, TokOp.LPar): - while not _match_op(ps, TokOp.RPar): - # 基类括号内跨行(class X(A,\n B):):逗号后换行在此跳过, - # 否则 cur=NL 非 Name/Comma → break → _expect_op(RPar) 报 - # "意外的 token"(Generator.vp L34 多继承跨行根因) - _skip_nl(ps) - if _cur_type(ps) == TokenType.EndMarker: break - # base 或 keyword=value - if _cur_type(ps) == TokenType.Name: - # Parse as expression, if followed by '=', then as keyword=value - e: rowptr[AST] = _parse_or_test(ps) - if _match_op(ps, TokOp.Equal): - _advance(ps) - v: rowptr[AST] = _parse_test(ps) - kw_node: rowptr[AST] = KwNode(ps.pool, e, v) - keywords.append(kw_node) - else: - bases.append(e) - if not _accept_op(ps, TokOp.Comma): - break - _skip_nl(ps) # 末基类后换行收尾(class X(A,\n B):) - _expect_op(ps, TokOp.RPar) - _expect_op(ps, TokOp.Colon) - dec_list: rowptr[list[rowptr[AST]]] = ps.pending_decorators - ps.pending_decorators = None - node: rowptr[ClassDef] = ClassDef(ps.pool, name, bases, keywords, dec_list, type_params) - _parse_suite_into_children(ps, node) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_match(ps: rowptr[Parser]) -> rowptr[AST]: - """match statement: match subject ':' NewLine Indent case+ Dedent""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # match - subject: rowptr[AST] = _parse_test(ps) - _expect_op(ps, TokOp.Colon) - cases: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - node: rowptr[AST] = Match(ps.pool, subject, cases) - # Parsing body: Same NewLine/Indent/Dedent pattern as _parse_suite_into_children, - # but parses case clauses into a cases list instead of normal statements into children - _skip_nl(ps) - if _cur_type(ps) == TokenType.NewLine: - _advance(ps) - _skip_nl(ps) - if _cur_type(ps) == TokenType.Indent: - _advance(ps) - while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker: - if _cur_kw(ps) == Keyword.Case: - case_node: rowptr[AST] = _parse_match_case(ps) - if case_node is not None: - cases.append(case_node) - else: - _advance(ps) # Skip non-case token to avoid infinite loop - _expect_type(ps, TokenType.Dedent) - _set_pos(node, lineno, col, 0, 0) - return node - - -# ============================================================ -# Match pattern parsing -# ============================================================ - -def _make_name_ast(pool: rowptr[memhub.MemManager], node: rowptr[Name]) -> rowptr[AST]: - # Analysis: "Convert Name* to AST* (Solving the TransPyC Type Inference Problem)" - return node - - -def _parse_pattern(ps: rowptr[Parser]) -> rowptr[AST]: - """pattern: or_pattern ['as' NAME] - or_pattern: closed_pattern ('|' closed_pattern)*""" - pat: rowptr[AST] = _parse_closed_pattern(ps) - if pat is None: return None - # OR pattern - if _match_op(ps, TokOp.VBar): - patterns: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - patterns.append(pat) - while _accept_op(ps, TokOp.VBar): - p: rowptr[AST] = _parse_closed_pattern(ps) - if p is not None: - patterns.append(p) - or_node: rowptr[AST] = MatchOr(ps.pool, patterns) - _set_pos(or_node, pat.lineno, pat.col_offset, 0, 0) - pat = or_node - # AS pattern - if _cur_kw(ps) == Keyword.As: - _advance(ps) - if _cur_type(ps) != TokenType.Name: - ps.error_count += 1 - return pat - name_str: str = _cur_str(ps) - _advance(ps) - as_node: rowptr[AST] = MatchAs(ps.pool, pat, name_str) - _set_pos(as_node, pat.lineno, pat.col_offset, 0, 0) - return as_node - return pat - - -def _parse_closed_pattern(ps: rowptr[Parser]) -> rowptr[AST]: - """closed_pattern: literal | capture | wildcard | value | group | sequence | mapping | class""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - ttype: int = _cur_type(ps) - val: rowptr[AST] - node: rowptr[AST] - tok: rowptr[Token] - sval: str - - # Number literal pattern - if ttype == TokenType.Number: - tok = ps.cur - _advance(ps) - if tok.is_float: - val = _new_const_float(ps.pool, tok.float_val, lineno, col) - else: - val = _new_const_int(ps.pool, tok.int_val, lineno, col) - node = MatchValue(ps.pool, val) - _set_pos(node, lineno, col, 0, 0) - return node - - # String literal pattern - if ttype == TokenType.String: - sval = _cur_str(ps) - is_dquote_m: int = ps.cur.kw_subtype & 16 - _advance(ps) - val = _new_const_str(ps.pool, sval, lineno, col, is_dquote_m) - node = MatchValue(ps.pool, val) - _set_pos(node, lineno, col, 0, 0) - return node - - # Negative number literal pattern: -1, -1.5 - if _match_op(ps, TokOp.Minus): - _advance(ps) - if _cur_type(ps) == TokenType.Number: - tok = ps.cur - _advance(ps) - if tok.is_float: - val = _new_const_float(ps.pool, -tok.float_val, lineno, col) - else: - val = _new_const_int(ps.pool, -tok.int_val, lineno, col) - node = MatchValue(ps.pool, val) - _set_pos(node, lineno, col, 0, 0) - return node - ps.error_count += 1 - return None - - # Name: None/True/False/_ /capture/value/class - if ttype == TokenType.Name: - kw: int = _cur_kw(ps) - # None/True/False → MatchSingleton - if kw == Keyword.None_ or kw == Keyword.True_ or kw == Keyword.False_: - _advance(ps) - if kw == Keyword.None_: - val = _new_const_none(ps.pool, lineno, col) - elif kw == Keyword.True_: - val = _new_const_bool(ps.pool, 1, lineno, col) - else: - val = _new_const_bool(ps.pool, 0, lineno, col) - node = MatchSingleton(ps.pool, val) - _set_pos(node, lineno, col, 0, 0) - return node - name_str: str = _cur_str(ps) - # Wildcard pattern: _ - if string.strcmp(name_str, "_") == 0: - _advance(ps) - node = MatchAs(ps.pool, None, None) - _set_pos(node, lineno, col, 0, 0) - return node - _advance(ps) - # Check dotted name(value pattern 或 class pattern) - if _match_op(ps, TokOp.Dot): - name_node: rowptr[Name] = Name(ps.pool, name_str, ASTCtx.Load) - _set_pos(name_node, lineno, col, 0, 0) - base: rowptr[AST] = _make_name_ast(ps.pool, name_node) - while _match_op(ps, TokOp.Dot): - _advance(ps) - if _cur_type(ps) != TokenType.Name: - ps.error_count += 1 - break - attr_name: str = _cur_str(ps) - attr_lineno: int = _cur_lineno(ps) - attr_col: int = _cur_col(ps) - _advance(ps) - base = Attribute(ps.pool, base, attr_name, ASTCtx.Load) - _set_pos(base, attr_lineno, attr_col, 0, 0) - # class pattern: dotted.name(...) - if _match_op(ps, TokOp.LPar): - return _parse_class_pattern(ps, base, lineno, col) - # value pattern: dotted.name - node = MatchValue(ps.pool, base) - _set_pos(node, lineno, col, 0, 0) - return node - # class pattern: Name(...) - if _match_op(ps, TokOp.LPar): - cls_node: rowptr[Name] = Name(ps.pool, name_str, ASTCtx.Load) - _set_pos(cls_node, lineno, col, 0, 0) - cls: rowptr[AST] = _make_name_ast(ps.pool, cls_node) - return _parse_class_pattern(ps, cls, lineno, col) - # capture pattern: NAME - node = MatchAs(ps.pool, None, name_str) - _set_pos(node, lineno, col, 0, 0) - return node - - # group pattern / sequence pattern (tuple): (...) - if _match_op(ps, TokOp.LPar): - _advance(ps) - inner: rowptr[AST] = _parse_pattern(ps) - if _accept_op(ps, TokOp.Comma): - # sequence pattern (tuple) - patterns: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - if inner is not None: - patterns.append(inner) - while not _match_op(ps, TokOp.RPar) and _cur_type(ps) != TokenType.EndMarker: - p: rowptr[AST] = _parse_pattern(ps) - if p is not None: - patterns.append(p) - if not _accept_op(ps, TokOp.Comma): - break - _expect_op(ps, TokOp.RPar) - node = MatchSequence(ps.pool, patterns) - _set_pos(node, lineno, col, 0, 0) - return node - _expect_op(ps, TokOp.RPar) - return inner # group pattern - - # sequence pattern (list): [...] - if _match_op(ps, TokOp.Lsqb): - return _parse_sequence_pattern(ps, TokOp.Rsqb, lineno, col) - - # mapping pattern: {...} - if _match_op(ps, TokOp.LBrace): - return _parse_mapping_pattern(ps, lineno, col) - - ps.error_count += 1 - return None - - -def _parse_sequence_pattern(ps: rowptr[Parser], close_op: int, - lineno: int, col: int) -> rowptr[AST]: - """sequence_pattern: '[' [pattern_list] ']'""" - patterns: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - _advance(ps) # [ - while not _match_op(ps, close_op) and _cur_type(ps) != TokenType.EndMarker: - # *name (star pattern) - if _match_op(ps, TokOp.Star): - _advance(ps) - if _cur_type(ps) == TokenType.Name: - star_name: str = _cur_str(ps) - _advance(ps) - star_node: rowptr[AST] - if string.strcmp(star_name, "_") == 0: - star_node = MatchStar(ps.pool, None) - else: - star_node = MatchStar(ps.pool, star_name) - _set_pos(star_node, lineno, col, 0, 0) - patterns.append(star_node) - else: - p: rowptr[AST] = _parse_pattern(ps) - if p is not None: - patterns.append(p) - if not _accept_op(ps, TokOp.Comma): - break - _expect_op(ps, close_op) - node: rowptr[AST] = MatchSequence(ps.pool, patterns) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_mapping_pattern(ps: rowptr[Parser], - lineno: int, col: int) -> rowptr[AST]: - """mapping_pattern: '{' [items] '}' - items: key ':' pattern (',' key ':' pattern)* [',' '**' NAME] | '**' NAME""" - keys: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - patterns: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - rest: str = None - node: rowptr[AST] - key: rowptr[AST] - val_pat: rowptr[AST] - _advance(ps) # { - if _match_op(ps, TokOp.RBrace): - _advance(ps) - node = MatchMapping(ps.pool, keys, patterns, rest) - _set_pos(node, lineno, col, 0, 0) - return node - while _cur_type(ps) != TokenType.EndMarker: - if _match_op(ps, TokOp.StarStar): - _advance(ps) - if _cur_type(ps) == TokenType.Name: - rest = _cur_str(ps) - _advance(ps) - break - key = _parse_expr(ps) - _expect_op(ps, TokOp.Colon) - val_pat = _parse_pattern(ps) - if key is not None: - keys.append(key) - if val_pat is not None: - patterns.append(val_pat) - if not _accept_op(ps, TokOp.Comma): - break - _expect_op(ps, TokOp.RBrace) - node = MatchMapping(ps.pool, keys, patterns, rest) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_class_pattern(ps: rowptr[Parser], cls: rowptr[AST], - lineno: int, col: int) -> rowptr[AST]: - """class_pattern: cls '(' [pattern_list] ')' - pattern_list: positional_pattern (',' positional_pattern)* - [',' keyword_pattern]* | keyword_pattern (',' keyword_pattern)* - keyword_pattern: NAME '=' pattern""" - patterns: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - kwd_attrs: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - kwd_patterns: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - p: rowptr[AST] - _advance(ps) # ( - if not _match_op(ps, TokOp.RPar): - while _cur_type(ps) != TokenType.EndMarker: - # keyword pattern: NAME '=' pattern - if _cur_type(ps) == TokenType.Name and ps.cur is not None: - nxt: rowptr[Token] = ps.cur.next - if nxt is not None and nxt.type == TokenType.Op and nxt.op_subtype == TokOp.Equal: - attr_name: str = _cur_str(ps) - _advance(ps) # NAME - _advance(ps) # = - kwd_pat: rowptr[AST] = _parse_pattern(ps) - attr_n: rowptr[Name] = Name(ps.pool, attr_name, ASTCtx.Load) - _set_pos(attr_n, lineno, col, 0, 0) - attr_node: rowptr[AST] = _make_name_ast(ps.pool, attr_n) - kwd_attrs.append(attr_node) - if kwd_pat is not None: - kwd_patterns.append(kwd_pat) - else: - p = _parse_pattern(ps) - if p is not None: - patterns.append(p) - else: - p = _parse_pattern(ps) - if p is not None: - patterns.append(p) - if not _accept_op(ps, TokOp.Comma): - break - _expect_op(ps, TokOp.RPar) - node: rowptr[AST] = MatchClass(ps.pool, cls, patterns, kwd_attrs, kwd_patterns) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_match_case(ps: rowptr[Parser]) -> rowptr[AST]: - """case pattern [if guard] ':' suite""" - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # case - guard: rowptr[AST] = None - pattern: rowptr[AST] = _parse_pattern(ps) - if _cur_kw(ps) == Keyword.If: - _advance(ps) - guard = _parse_or_test(ps) - _expect_op(ps, TokOp.Colon) - node: rowptr[AST] = MatchCase(ps.pool, pattern, guard) - _parse_suite_into_children(ps, node) - _set_pos(node, lineno, col, 0, 0) - return node - - -def _parse_decorators(ps: rowptr[Parser]) -> rowptr[list[rowptr[AST]]]: - """解析装饰器链,返回 list。""" - decs: rowptr[list[rowptr[AST]]] = list[rowptr[AST]](ps.pool, 8) - while _match_op(ps, TokOp.At): - lineno: int = _cur_lineno(ps) - col: int = _cur_col(ps) - _advance(ps) # @ - expr: rowptr[AST] = _parse_namedexpr(ps) - _skip_nl(ps) - if _cur_type(ps) == TokenType.NewLine: - _advance(ps) - decs.append(expr) - return decs - - -# ============================================================ -# Module entry -# ============================================================ - -def _parse_module_body(ps: rowptr[Parser], module_node: rowptr[AST]): - """解析模块体,append 到 module_node。""" - _skip_nl(ps) - stmt_idx: int = 0 - while _cur_type(ps) != TokenType.EndMarker: - stmt_idx += 1 - # Decorator - if _match_op(ps, TokOp.At): - ps.pending_decorators = _parse_decorators(ps) - # Record current token pointer to check for infinite loop - before_cur: rowptr[Token] = ps.cur - _parse_statement_into(ps, module_node) - # Defense: If parsing doesn't advance the token (like when _parse_atom encounters an unrecognized token and returns None), - # force it to move forward to avoid an infinite loop in the main loop; accumulate error_count for the caller to diagnose - if ps.cur is before_cur: - ps.error_count += 1 - _advance(ps) - _skip_nl(ps) - - -def parse_tokens(pool: rowptr[memhub.MemManager], - tokens: rowptr[Token]) -> rowptr[AST]: - """Parse an AST Module from a token list.""" - if pool is None or tokens is None: - return None - global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg - _g_parse_err_line = 0 - _g_parse_err_col = 0 - _g_parse_err_msg = None - ps: rowptr[Parser] = new_parser(pool) - if ps is None: return None - _parser_init(ps, tokens, pool) - module_node: rowptr[Module] = Module(pool) - _parse_module_body(ps, module_node) - # fail-fast: 解析出错(error_count > 0)时返回 None,由调用方(TPV 等)报错终止, - # 避免错误代码静默编译(如 `func12344444 avg3(...)` 缺 def 关键字的函数定义残留)。 - if ps.error_count > 0: - return None - return module_node +import t, c +from stdint import * +import memhub +import string +import stdio +import stdlib +from .tokens import (Token, TokenType, Keyword, TokOp) +from .base import ( + AST, ASTCtx, ASTKind, OpKind, ASTFlag, + _set_pos, _binop_from_op, _augop_from_op, _cmpop_from_op, _copy_str, _inherit_pos, + _init_ast, + CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR, + FLAG_IS_ASYNC, FLAG_IS_EXTERN, FLAG_IS_EXPORT, FLAG_IS_DEFINE, +) +from .exprs import ( + BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set, + ListComp, SetComp, DictComp, GeneratorExp, + Await, Yield, YieldFrom, FormattedValue, JoinedStr, + Constant, NamedExpr, Attribute, Subscript, Starred, Name, + List, Tuple, Slice, Call, Compare, OpNode, +) +from .stmts import ( + Module, Expression, Interactive, FunctionType, + FunctionDef, ClassDef, Return, Delete, Assign, AugAssign, AnnAssign, + For, While, If, With, Raise, Try, Assert, Global, Nonlocal, + Pass, Break, Continue, Expr, Import, ImportFrom, Match, +) +from .astaux import ( + ExceptHandler, Arguments, Arg, Keyword as KwNode, Alias, WithItem, Comprehension, +) +from .match import ( + MatchCase, MatchValue, MatchSingleton, MatchSequence, + MatchMapping, MatchClass, MatchStar, MatchAs, MatchOr, +) +from .lexer import Lexer, _lexer_init, tokenize, new_lexer + + +# ============================================================ +# Parser State +# ============================================================ +class Parser: + tokens: rawptr[Token] # token linked list head + cur: rawptr[Token] # current token + pool: rawptr[memhub.MemManager] # memory pool + error_count: int # error count + pending_decorators: rawptr[list[rawptr[AST]]] # pending decorators list + + def __new__(self, pool: rawptr[memhub.MemManager]): + ptr: rawptr[Parser] = pool.alloc(Parser.__sizeof__()) + if ptr: + string.memset(ptr, 0, Parser.__sizeof__()) + return ptr + + +def new_parser(pool: rawptr[memhub.MemManager]) -> rawptr[Parser]: + """Allocate from the pool and return the Parser (cross-module safe wrapper)""" + return Parser(pool) + + +def _parser_init(ps: rawptr[Parser], tokens: rawptr[Token], + pool: rawptr[memhub.MemManager]): + if ps is None: return + ps.tokens = tokens + ps.cur = tokens + ps.pool = pool + ps.error_count = 0 + ps.pending_decorators = None + + +# ============================================================ +# Token Navigation Helpers +# ============================================================ + +def _advance(ps: rawptr[Parser]): + """Advance to the next token""" + if ps.cur is None: return + if ps.cur.next is not None: + ps.cur = ps.cur.next + + +def _cur_type(ps: rawptr[Parser]) -> int: + if ps.cur is None: return TokenType.EndMarker + return ps.cur.type + + +def _cur_op(ps: rawptr[Parser]) -> int: + if ps.cur is None: return 0 + return ps.cur.op_subtype + + +def _cur_kw(ps: rawptr[Parser]) -> int: + if ps.cur is None: return 0 + return ps.cur.kw_subtype + + +def _is_funcdef_kw(ps: rawptr[Parser], kw: int) -> int: + """判断当前 token 是否为函数定义关键字(def 及其别名 fn/fun/func/function, + 以及 extern/export 声明关键字) + + def 是硬关键字直接判定;fn/fun/func/function/extern/export 是软关键字, + 仅当下一个 token 是 Name 且再下一个是 '('(或泛型 '[T]' 前的 '[')时 + 判定为函数定义。extern 还允许 'extern def A(...)' 形式(下一个是 def), + export 同样允许 'export def A(...)' 形式。 + 否则按普通标识符处理,兼容既有代码把 func 用作 + 字段/参数名(如 AST 节点 func 字段、Call.func 注解 `func: rawptr[AST]`)。 + """ + if kw == Keyword.Def: + return 1 + if kw == Keyword.Extern or kw == Keyword.Export: + # extern/export def A(...) 或 extern/export A(...) + # (extern/export 后是 def 或 Name+'(/'[') + nxt: rawptr[Token] = ps.cur.next + if nxt is None: + return 0 + if nxt.kw_subtype == Keyword.Def: + return 1 + if nxt.type != TokenType.Name: + return 0 + nxt2: rawptr[Token] = nxt.next + if nxt2 is None: + return 0 + if nxt2.type == TokenType.Op \ + and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb): + return 1 + return 0 + if kw == Keyword.Fn or kw == Keyword.Fun \ + or kw == Keyword.Func or kw == Keyword.Function \ + or kw == Keyword.Define: + # 软关键字 lookahead: 下个是 Name,再下个是 '(' 或 '[T...]' 泛型参数(PEP 695) + # (define 宏函数与 def 语义一致,返回注解自动追加 t.CDefine) + nxt: rawptr[Token] = ps.cur.next + if nxt is None: + return 0 + if nxt.type != TokenType.Name: + return 0 + nxt2: rawptr[Token] = nxt.next + if nxt2 is None: + return 0 + if nxt2.type == TokenType.Op \ + and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb): + return 1 + return 0 + return 0 + + +def _is_match_stmt(ps: rawptr[Parser]) -> int: + """match 是软关键字(CPython 3.10+):lookahead 判断是否为 match 语句 + + match 后直接跟 ':'(变量注解,如 lexer.vp 的 `match: int = 1`)、 + '='(赋值,如 `match = 0`)、换行/文件尾 → 按普通标识符; + 否则扫描至行尾,在括号深度 0 处出现 ':' → match 语句(subject 后必须冒号)。 + case/type 同理为软关键字(case 仅在 match 套件内消费,type 无语句分发)。 + """ + nxt: rawptr[Token] = ps.cur.next + if nxt is None: + return 0 + if nxt.type == TokenType.NewLine or nxt.type == TokenType.EndMarker: + return 0 + if nxt.type == TokenType.Op: + if nxt.op_subtype == TokOp.Colon: + return 0 + if nxt.op_subtype == TokOp.Equal: + return 0 + walk: rawptr[Token] = nxt + depth: int = 0 + while walk is not None: + if walk.type == TokenType.NewLine or walk.type == TokenType.EndMarker: + return 0 + if walk.type == TokenType.Op: + wop: int = walk.op_subtype + if wop == TokOp.LPar or wop == TokOp.Lsqb or wop == TokOp.LBrace: + depth += 1 + elif wop == TokOp.RPar or wop == TokOp.Rsqb or wop == TokOp.RBrace: + if depth > 0: + depth -= 1 + elif wop == TokOp.Colon and depth == 0: + return 1 + walk = walk.next + return 0 + + +# 解析错误位置与消息(fail-fast 仿 Python SyntaxError 报告,首次错误记录) +_g_parse_err_line: int = 0 +_g_parse_err_col: int = 0 +_g_parse_err_msg: str = None + + +def _record_parse_err(ps: rawptr[Parser]): + """记录首个解析错误的位置与消息(只记录第一次,后续错误不覆盖)""" + global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg + if _g_parse_err_line != 0: + return + _g_parse_err_line = _cur_lineno(ps) + _g_parse_err_col = _cur_col(ps) + err_s: str = _cur_str(ps) + if err_s is None: + err_s = "(none)" + # 消息文本用 stdlib 拷贝(token 的 str_val 在 pool 中,生命周期覆盖本次编译但保险起见独立存储) + buf_m: str = stdlib.malloc(128) + if buf_m is not None: + stdio.snprintf(buf_m, 128, "意外的 token '%s'", err_s) + _g_parse_err_msg = buf_m + else: + _g_parse_err_msg = err_s + + +def get_parse_err_line() -> int: + """获取最近一次解析错误的行号(0=无错误)""" + return _g_parse_err_line + + +def get_parse_err_col() -> int: + """获取最近一次解析错误的列号(0=无错误)""" + return _g_parse_err_col + + +def get_parse_err_msg() -> str: + """获取最近一次解析错误的错误消息(仿 Python SyntaxError 消息文本)""" + return _g_parse_err_msg + + +def _cur_str(ps: rawptr[Parser]) -> str: + if ps.cur is None: return None + return ps.cur.str_val + + +def _cur_lineno(ps: rawptr[Parser]) -> int: + if ps.cur is None: return 0 + return ps.cur.lineno + + +def _cur_col(ps: rawptr[Parser]) -> int: + if ps.cur is None: return 0 + return ps.cur.col_offset + + +def _skip_nl(ps: rawptr[Parser]): + """Skip TokenType.Nl token""" + while ps.cur is not None and ps.cur.type == TokenType.Nl: + ps.cur = ps.cur.next + + +def _skip_nls_and_newlines(ps: rawptr[Parser]): + """Skip TokenType.Nl and TokenType.NewLine (used for line continuation)""" + while ps.cur is not None: + if ps.cur.type == TokenType.Nl or ps.cur.type == TokenType.NewLine: + ps.cur = ps.cur.next + else: + break + + +def _expect_type(ps: rawptr[Parser], ttype: int) -> int: + """Expect the current token to be of the specified type, advance and return 1, otherwise return 0""" + if _cur_type(ps) == ttype: + _advance(ps) + return 1 + _record_parse_err(ps) + ps.error_count += 1 + return 0 + + +def _expect_op(ps: rawptr[Parser], op_id: int) -> int: + """Expecting the current one to be some operator""" + if _cur_type(ps) == TokenType.Op and _cur_op(ps) == op_id: + _advance(ps) + return 1 + _record_parse_err(ps) + ps.error_count += 1 + return 0 + + +def _match_op(ps: rawptr[Parser], op_id: int) -> int: + """Check but not consume the current token""" + if _cur_type(ps) == TokenType.Op and _cur_op(ps) == op_id: + return 1 + return 0 + + +def _accept_op(ps: rawptr[Parser], op_id: int) -> int: + """If match, consume and return 1""" + if _match_op(ps, op_id): + _advance(ps) + return 1 + return 0 + + +# ============================================================ +# Constant Construction Helper (New API: Constant directly accepts lineno/col) +# ============================================================ + +def _new_const_int(pool: rawptr[memhub.MemManager], val: int64, + lineno: int, col: int) -> rawptr[AST]: + return Constant(pool, CONST_INT, val, 0.0, None, lineno, col) + + +def _new_const_float(pool: rawptr[memhub.MemManager], val: double, + lineno: int, col: int) -> rawptr[AST]: + global _g_dbg_pts, _g_dbg_vals, _g_dbg_cnt + new_c: rawptr[Constant] = Constant(pool, CONST_FLOAT, 0, val, None, lineno, col) + # 把数字文本存到 str_val(stdlib 分配,绕开 pool 对 float_val 字段的覆写) + # REG-SRC 用 string.atof(str_val) 恢复原始值 + lit_txt: str = stdlib.malloc(64) + if lit_txt is not None: + stdio.snprintf(lit_txt, 64, "%.17g", val) + new_c.str_val = lit_txt + # 记录浮点节点 ptr→val 到 stdlib 表(构造时值正确,供 REG-SRC 查表绕开 pool 覆写) + # 表满时重置:旧记录对应已翻译完的文件,从当前节点重新记录 + if _g_dbg_pts is None: + _g_dbg_pts = stdlib.malloc(4096 * 8) + _g_dbg_vals = stdlib.malloc(4096 * 8) + _g_dbg_cnt = 0 + elif _g_dbg_cnt >= 4096: + _g_dbg_cnt = 0 + if _g_dbg_pts is not None and _g_dbg_vals is not None: + _g_dbg_pts[_g_dbg_cnt] = uint64(new_c) + _g_dbg_vals[_g_dbg_cnt] = val + _g_dbg_cnt += 1 + return new_c + + +# 浮点节点构造值表(stdlib 分配,避免污染 pool) +_g_dbg_pts: rawptr[uint64] = None +_g_dbg_vals: rawptr[double] = None +_g_dbg_cnt: int = 0 +# 最近一次 _dbg_lookup_lit_val 是否命中 +_g_dbg_lit_found: int = 0 + + +def _dbg_lookup_lit_val(ptr: uint64) -> double: + """查浮点节点构造值表:返回 ptr 节点的原始构造值(未命中返回 0 并置 found=0) + + 用于绕开 pool 分配对 AST float_val 字段的覆写(构造时值正确,读取时可能被覆写)。 + """ + global _g_dbg_lit_found + _g_dbg_lit_found = 0 + if _g_dbg_pts is None or _g_dbg_vals is None: + return 0.0 + i: t.CSizeT = 0 + while i < _g_dbg_cnt: + if _g_dbg_pts[i] == ptr: + _g_dbg_lit_found = 1 + return _g_dbg_vals[i] + i += 1 + return 0.0 + + +def _dbg_lit_found() -> int: + """检查最近一次 _dbg_lookup_lit_val 是否命中""" + return _g_dbg_lit_found + + +def _new_const_str(pool: rawptr[memhub.MemManager], val: str, + lineno: int, col: int, + is_dquote: int = 1, + is_triple: int = 0, + is_char: int = 0) -> rawptr[AST]: + """Create a string/character constant node + + is_dquote: 0 = single quote, non-0 = double quote (default double quote) + is_triple: non-0 = triple-quoted string + is_char: non-0 = single-character candidate (lexer has confirmed that after escaping it's exactly 1 byte, including '0') + Semantic distinctions: + - 'x' (single quote, single character, not triple-quoted) → CONST_CHAR (i8 value) + - "x" (double quote, includes 0 terminator) → CONST_STR (i8* pointer, i8 x 2) + - 'ab' (single quotes, multiple characters) → CONST_STR (i8* pointer) + - '''x''' / \"\"\"x\"\"\" (triple quotes) → CONST_STR (i8* pointer) + - '' / "" (empty string) → CONST_STR (i8* pointer) + """ + # Single quotes, non-triple quotes, single-character candidates marked by lexer → CONST_CHAR + # Mark with is_char instead of strlen(val), because '0' becomes a NUL byte after escaping, strlen would return 0 + if is_dquote == 0 and is_triple == 0 and is_char != 0: + # Single quote # Single character → CONST_CHAR, int_val stores the character's ASCII value + # val[0] is a legal value of 0 even if it's NUL + ch_val: int64 = val[0] + return Constant(pool, CONST_CHAR, ch_val, 0.0, val, lineno, col) + # Other situations → CONST_STR + return Constant(pool, CONST_STR, 0, 0.0, val, lineno, col) + + +def _new_const_bool(pool: rawptr[memhub.MemManager], val: int, + lineno: int, col: int) -> rawptr[AST]: + return Constant(pool, CONST_BOOL, val, 0.0, None, lineno, col) + + +def _new_const_none(pool: rawptr[memhub.MemManager], + lineno: int, col: int) -> rawptr[AST]: + return Constant(pool, CONST_NONE, 0, 0.0, None, lineno, col) + + +# ============================================================ +# ctx Helper (Recursive Fixing Store Context) +# ============================================================ + +def _set_ctx(node: rawptr[AST], ctx: int): + """Set ctx field of node (based on kind conversion)""" + if node is None: return + k: int = node.kind() + if k == ASTKind.Name: + nm: rawptr[Name] = (rawptr[Name])(node) + nm.ctx = ctx + elif k == ASTKind.Attribute: + at: rawptr[Attribute] = (rawptr[Attribute])(node) + at.ctx = ctx + elif k == ASTKind.Subscript: + sb: rawptr[Subscript] = (rawptr[Subscript])(node) + sb.ctx = ctx + elif k == ASTKind.Tuple: + tp: rawptr[Tuple] = (rawptr[Tuple])(node) + tp.ctx = ctx + elif k == ASTKind.List: + ls: rawptr[List] = (rawptr[List])(node) + ls.ctx = ctx + elif k == ASTKind.Starred: + st: rawptr[Starred] = (rawptr[Starred])(node) + st.ctx = ctx + + +def _fix_store_ctx(node: rawptr[AST]): + """Fix ctx of store targets to be Store""" + if node is None: return + _set_ctx(node, ASTCtx.Store) + k: int = node.kind() + if k == ASTKind.Tuple or k == ASTKind.List: + tp_elts: rawptr[list[rawptr[AST]]] = None + if k == ASTKind.Tuple: + tp: rawptr[Tuple] = (rawptr[Tuple])(node) + tp_elts = tp.elts + else: + ls: rawptr[List] = (rawptr[List])(node) + tp_elts = ls.elts + if tp_elts is not None: + n: t.CSizeT = tp_elts.__len__() + i: t.CSizeT = 0 + while i < n: + cur: rawptr[AST] = tp_elts.get(i) + _fix_store_ctx(cur) + i += 1 + elif k == ASTKind.Starred: + st2: rawptr[Starred] = (rawptr[Starred])(node) + _fix_store_ctx(st2.value) + + +# ============================================================ +# Expression Parser +# ============================================================ + +def _parse_expr(ps: rawptr[Parser]) -> rawptr[AST]: + """expr: yield_expr | namedexpr_test""" + if _cur_kw(ps) == Keyword.Yield: + return _parse_yield(ps) + return _parse_namedexpr(ps) + + +def _parse_namedexpr(ps: rawptr[Parser]) -> rawptr[AST]: + """namedexpr_test: test [':=' test]""" + left: rawptr[AST] = _parse_test(ps) + if left is None: return None + if _match_op(ps, TokOp.ColonEq): + _advance(ps) + right: rawptr[AST] = _parse_test(ps) + node: rawptr[AST] = NamedExpr(ps.pool, left, right) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + return node + return left + + +def _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) + left: rawptr[AST] = _parse_or_test(ps) + if left is None: return None + if _cur_kw(ps) == Keyword.If: + _advance(ps) + test: rawptr[AST] = _parse_or_test(ps) + if _cur_kw(ps) == Keyword.Else: + _advance(ps) + orelse: rawptr[AST] = _parse_test(ps) + node: rawptr[AST] = IfExp(ps.pool, test, left, orelse) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + return node + ps.error_count += 1 + return left + return left + + +def _parse_lambda(ps: rawptr[Parser]) -> rawptr[AST]: + """lambdef: 'lambda' [varargslist] ':' test""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # lambda + args: rawptr[AST] = None + if not _match_op(ps, TokOp.Colon): + args = _parse_varargslist(ps, 1) + if not _expect_op(ps, TokOp.Colon): + return None + body: rawptr[AST] = _parse_test(ps) + node: rawptr[AST] = Lambda(ps.pool, args, body) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_or_test(ps: rawptr[Parser]) -> rawptr[AST]: + """or_test: and_test ('or' and_test)*""" + left: rawptr[AST] = _parse_and_test(ps) + if left is None: return None + values: rawptr[list[rawptr[AST]]] = None + while True: + # 括号内行首 or((A\n or B))与行尾 or(A or\n B)都支持: + # 每轮先 _skip_nl 再检查 or,否则 A or B\n or C 在 B 后停在 Nl 上 + # 循环退出,上层把续行 or 当非法 token(对齐 and_test / vast 实现)。 + _skip_nl(ps) + if _cur_kw(ps) != Keyword.Or: + break + if values is None: + values = list[rawptr[AST]](ps.pool, 8) + values.append(left) + _advance(ps) + _skip_nl(ps) # 括号内跨行: a or\n b + right: rawptr[AST] = _parse_and_test(ps) + if right is None: break + values.append(right) + if values is not None: + node: rawptr[AST] = BoolOp(ps.pool, OpKind.Or, values) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + return node + return left + + +def _parse_and_test(ps: rawptr[Parser]) -> rawptr[AST]: + """and_test: not_test ('and' not_test)*""" + left: rawptr[AST] = _parse_not_test(ps) + if left is None: return None + values: rawptr[list[rawptr[AST]]] = None + while True: + # 括号内行首 and((A\n and B))与行尾 and(A and\n B)都支持: + # 每轮先 _skip_nl 再检查 and,否则 A and B\n and C 在 B 后停在 + # Nl 上循环退出,上层把续行 and 当非法 token + # (ThreadingTest check 多行 and 链解析失败根因)。 + _skip_nl(ps) + if _cur_kw(ps) != Keyword.And: + break + if values is None: + values = list[rawptr[AST]](ps.pool, 8) + values.append(left) + _advance(ps) + _skip_nl(ps) # 括号内跨行: a and\n b + right: rawptr[AST] = _parse_not_test(ps) + if right is None: break + values.append(right) + if values is not None: + node: rawptr[AST] = BoolOp(ps.pool, OpKind.And, values) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + return node + return left + + +def _parse_not_test(ps: rawptr[Parser]) -> rawptr[AST]: + """not_test: 'not' not_test | comparison""" + if _cur_kw(ps) == Keyword.Not: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + operand: rawptr[AST] = _parse_not_test(ps) + node: rawptr[AST] = UnaryOp(ps.pool, OpKind.Not, operand) + _set_pos(node, lineno, col, 0, 0) + return node + return _parse_comparison(ps) + + +def _parse_comparison(ps: rawptr[Parser]) -> rawptr[AST]: + """comparison: bitor_expr (comp_op bitor_expr)* + + Single comparison design: a < b → Compare(left=a, op=Lt, right=b) + Chained comparison a < b < c → BoolOp(And, [Compare(a,<,b), Compare(b,<,c)]) + """ + left: rawptr[AST] = _parse_bitor(ps) + if left is None: return None + + _skip_nl(ps) # 括号内行首比较运算符: (a\n < b) + op_id: int = _match_comp_op(ps) + if op_id == OpKind.NoneOp: + return left + _advance_comp_op(ps) + _skip_nl(ps) # 括号内跨行: a ==\n b + right: rawptr[AST] = _parse_bitor(ps) + # Single comparison: construct ops and comparators list + ops: rawptr[list[int]] = list[int](ps.pool, 8) + ops.append(op_id) + comparators: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + comparators.append(right) + first_cmp: rawptr[AST] = Compare(ps.pool, left, ops, comparators) + _set_pos(first_cmp, left.lineno, left.col_offset, 0, 0) + + # Check chained comparison + _skip_nl(ps) # 括号内行首链式比较: (a < b\n < c) + next_op: int = _match_comp_op(ps) + if next_op == OpKind.NoneOp: + return first_cmp + + # Single comparison with chained conditions: a < b < c → BoolOp(And, [Compare(a,<,b), Compare(b,<,c), ...]) + values: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + values.append(first_cmp) + cur_left: rawptr[AST] = right + while next_op != OpKind.NoneOp: + _advance_comp_op(ps) + _skip_nl(ps) # 括号内跨行链式比较 + cur_right: rawptr[AST] = _parse_bitor(ps) + ops2: rawptr[list[int]] = list[int](ps.pool, 8) + ops2.append(next_op) + comparators2: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + comparators2.append(cur_right) + cmp: rawptr[AST] = Compare(ps.pool, cur_left, ops2, comparators2) + _set_pos(cmp, cur_left.lineno, cur_left.col_offset, 0, 0) + values.append(cmp) + cur_left = cur_right + _skip_nl(ps) # 括号内行首链式比较: (a < b\n < c) + next_op = _match_comp_op(ps) + node: rawptr[AST] = BoolOp(ps.pool, OpKind.And, values) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + return node + + +def _match_comp_op(ps: rawptr[Parser]) -> int: + """Check if the current one is a comparison operator, return OP_* or OpKind.NoneOp""" + if _cur_type(ps) == TokenType.Op: + op_id: int = _cur_op(ps) + if op_id == TokOp.EqEq: return OpKind.Eq + if op_id == TokOp.ExclaimEq: return OpKind.Ne + if op_id == TokOp.Less: return OpKind.Lt + if op_id == TokOp.LessEq: return OpKind.Le + if op_id == TokOp.Greater: return OpKind.Gt + if op_id == TokOp.GreaterEq: return OpKind.Ge + if _cur_kw(ps) == Keyword.Is: + if ps.cur.next is not None and ps.cur.next.type == TokenType.Name and ps.cur.next.kw_subtype == Keyword.Not: + return OpKind.IsNot + return OpKind.Is + if _cur_kw(ps) == Keyword.Not: + if ps.cur.next is not None and ps.cur.next.kw_subtype == Keyword.In: + return OpKind.NotIn + if _cur_kw(ps) == Keyword.In: + return OpKind.In + return OpKind.NoneOp + + +def _advance_comp_op(ps: rawptr[Parser]): + """Consume comparison operator token""" + if _cur_type(ps) == TokenType.Op: + _advance(ps) + return + if _cur_kw(ps) == Keyword.Is: + _advance(ps) + if _cur_kw(ps) == Keyword.Not: + _advance(ps) + return + if _cur_kw(ps) == Keyword.Not: + _advance(ps) + if _cur_kw(ps) == Keyword.In: + _advance(ps) + return + if _cur_kw(ps) == Keyword.In: + _advance(ps) + + +def _parse_bitor(ps: rawptr[Parser]) -> rawptr[AST]: + left: rawptr[AST] = _parse_bitxor(ps) + if left is None: return None + while True: + _skip_nl(ps) # 括号内行首 '|': (a\n | b) + if _match_op(ps, TokOp.VBar) == 0: + break + _advance(ps) + _skip_nl(ps) # 括号内跨行: a |\n b + right: rawptr[AST] = _parse_bitxor(ps) + node: rawptr[AST] = BinOp(ps.pool, left, OpKind.BitOr, right) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + left = node + return left + + +def _parse_bitxor(ps: rawptr[Parser]) -> rawptr[AST]: + left: rawptr[AST] = _parse_bitand(ps) + if left is None: return None + while True: + _skip_nl(ps) # 括号内行首 '^': (a\n ^ b) + if _match_op(ps, TokOp.Caret) == 0: + break + _advance(ps) + _skip_nl(ps) # 括号内跨行: a ^\n b + right: rawptr[AST] = _parse_bitand(ps) + node: rawptr[AST] = BinOp(ps.pool, left, OpKind.BitXor, right) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + left = node + return left + + +def _parse_bitand(ps: rawptr[Parser]) -> rawptr[AST]: + left: rawptr[AST] = _parse_shift(ps) + if left is None: return None + while True: + _skip_nl(ps) # 括号内行首 '&': (a\n & b) + if _match_op(ps, TokOp.Amp) == 0: + break + _advance(ps) + _skip_nl(ps) # 括号内跨行: a &\n b + right: rawptr[AST] = _parse_shift(ps) + node: rawptr[AST] = BinOp(ps.pool, left, OpKind.BitAnd, right) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + left = node + return left + + +def _parse_shift(ps: rawptr[Parser]) -> rawptr[AST]: + left: rawptr[AST] = _parse_arith(ps) + if left is None: return None + while True: + _skip_nl(ps) # 括号内行首 '<<'/'>>': (a\n << b) + if _match_op(ps, TokOp.LtLt) == 0 and _match_op(ps, TokOp.GtGt) == 0: + break + op_token: int = _cur_op(ps) + _advance(ps) + _skip_nl(ps) # 括号内跨行: a <<\n b + right: rawptr[AST] = _parse_arith(ps) + ast_op: int = OpKind.LShift + if op_token == TokOp.GtGt: + ast_op = OpKind.RShift + node: rawptr[AST] = BinOp(ps.pool, left, ast_op, right) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + left = node + return left + + +def _parse_arith(ps: rawptr[Parser]) -> rawptr[AST]: + left: rawptr[AST] = _parse_term(ps) + if left is None: return None + while True: + _skip_nl(ps) # 括号内行首 '+'/'-': (a\n + b) + if _match_op(ps, TokOp.Plus) == 0 and _match_op(ps, TokOp.Minus) == 0: + break + op_token: int = _cur_op(ps) + _advance(ps) + _skip_nl(ps) # 括号内跨行: a +\n b + right: rawptr[AST] = _parse_term(ps) + ast_op: int = OpKind.Add + if op_token == TokOp.Minus: + ast_op = OpKind.Sub + node: rawptr[AST] = BinOp(ps.pool, left, ast_op, right) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + left = node + return left + + +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) + op_token: int = 0 + if _match_op(ps, TokOp.Star): op_token = TokOp.Star + elif _match_op(ps, TokOp.Slash): op_token = TokOp.Slash + elif _match_op(ps, TokOp.DSlash): op_token = TokOp.DSlash + elif _match_op(ps, TokOp.Percent): op_token = TokOp.Percent + elif _match_op(ps, TokOp.At): op_token = TokOp.At + if op_token == 0: break + _advance(ps) + _skip_nl(ps) # 括号内跨行: a *\n b + right: rawptr[AST] = _parse_factor(ps) + ast_op: int = _binop_from_op(op_token) + node: rawptr[AST] = BinOp(ps.pool, left, ast_op, right) + _set_pos(node, left.lineno, left.col_offset, 0, 0) + left = node + return left + + +def _parse_factor(ps: rawptr[Parser]) -> rawptr[AST]: + """factor: '+' factor | '-' factor | '~' factor | '*' factor | '&' factor | power""" + if (_match_op(ps, TokOp.Plus) or _match_op(ps, TokOp.Minus) or _match_op(ps, TokOp.Tilde) + or _match_op(ps, TokOp.Star) or _match_op(ps, TokOp.Amp)): + op_token: int = _cur_op(ps) + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + operand: rawptr[AST] = _parse_factor(ps) + ast_op: int = OpKind.UAdd + if op_token == TokOp.Minus: + ast_op = OpKind.USub + elif op_token == TokOp.Tilde: + ast_op = OpKind.Invert + elif op_token == TokOp.Star: + ast_op = OpKind.Deref + elif op_token == TokOp.Amp: + ast_op = OpKind.AddrOf + node: rawptr[AST] = UnaryOp(ps.pool, ast_op, operand) + _set_pos(node, lineno, col, 0, 0) + return node + return _parse_power(ps) + + +def _parse_power(ps: rawptr[Parser]) -> rawptr[AST]: + """power: await_expr ['**' factor] | atom_expr ['**' factor]""" + base: rawptr[AST] = _parse_atom_expr(ps) + if base is None: return None + if _match_op(ps, TokOp.StarStar): + _advance(ps) + exp: rawptr[AST] = _parse_factor(ps) + node: rawptr[AST] = BinOp(ps.pool, base, OpKind.Pow, exp) + _set_pos(node, base.lineno, base.col_offset, 0, 0) + return node + return base + + +def _parse_atom_expr(ps: rawptr[Parser]) -> rawptr[AST]: + """atom_expr: ['await'] atom trailer*""" + if _cur_kw(ps) == Keyword.Await: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + inner: rawptr[AST] = _parse_atom_expr(ps) + node: rawptr[AST] = Await(ps.pool, inner) + _set_pos(node, lineno, col, 0, 0) + return node + base: rawptr[AST] = _parse_atom(ps) + if base is None: return None + # trailer loop + while True: + if _match_op(ps, TokOp.Dot): + _advance(ps) + if _cur_type(ps) != TokenType.Name: + ps.error_count += 1 + break + attr_name: str = _cur_str(ps) + attr_lineno: int = _cur_lineno(ps) + attr_col: int = _cur_col(ps) + _advance(ps) + ctx: int = ASTCtx.Load + if _match_op(ps, TokOp.Equal): + ctx = ASTCtx.Store + elif _match_op(ps, TokOp.PlusEq) or _match_op(ps, TokOp.MinusEq): + ctx = ASTCtx.Store + base = Attribute(ps.pool, base, attr_name, ctx) + _set_pos(base, attr_lineno, attr_col, 0, 0) + elif _match_op(ps, TokOp.LPar): + base = _parse_call(ps, base) + elif _match_op(ps, TokOp.Lsqb): + base = _parse_subscript(ps, base) + else: + break + return base + + +def _parse_call(ps: rawptr[Parser], func: rawptr[AST]) -> rawptr[AST]: + """Parse function call trailer. Both args and keywords are lists.""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # ( + _skip_nls_and_newlines(ps) # Skip the line break after '(' + args: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + keywords: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + + if not _match_op(ps, TokOp.RPar): + while True: + # **expr + if _match_op(ps, TokOp.StarStar): + _advance(ps) + _skip_nls_and_newlines(ps) + val: rawptr[AST] = _parse_expr(ps) + kw: rawptr[AST] = KwNode(ps.pool, None, val) + _set_pos(kw, _cur_lineno(ps), _cur_col(ps), 0, 0) + keywords.append(kw) + # *expr + elif _match_op(ps, TokOp.Star): + _advance(ps) + _skip_nls_and_newlines(ps) + val = _parse_expr(ps) + star: rawptr[AST] = Starred(ps.pool, val, ASTCtx.Load) + _set_pos(star, val.lineno, val.col_offset, 0, 0) + args.append(star) + # keyword = expr + elif _cur_type(ps) == TokenType.Name: + if ps.cur.next is not None and ps.cur.next.type == TokenType.Op and ps.cur.next.op_subtype == TokOp.Equal: + kw_name: str = _cur_str(ps) + kw_lineno: int = _cur_lineno(ps) + kw_col: int = _cur_col(ps) + _advance(ps) # name + _advance(ps) # = + _skip_nls_and_newlines(ps) + val = _parse_expr(ps) + kw = KwNode(ps.pool, kw_name, val) + _set_pos(kw, kw_lineno, kw_col, 0, 0) + keywords.append(kw) + else: + val = _parse_expr(ps) + args.append(val) + else: + val = _parse_expr(ps) + args.append(val) + _skip_nls_and_newlines(ps) # Skip the line break after the parameter + if not _accept_op(ps, TokOp.Comma): + break + _skip_nls_and_newlines(ps) # Skip the line break after the comma + _skip_nls_and_newlines(ps) # Skip the line break before ')' + _expect_op(ps, TokOp.RPar) + node: rawptr[AST] = Call(ps.pool, func, args, keywords) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_subscript(ps: rawptr[Parser], value: rawptr[AST]) -> rawptr[AST]: + """Parse subscript trailer.""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # [ + _skip_nl(ps) + slice_node: rawptr[AST] = _parse_slice(ps) + _expect_op(ps, TokOp.Rsqb) + ctx: int = ASTCtx.Load + if _match_op(ps, TokOp.Equal): + ctx = ASTCtx.Store + node: rawptr[AST] = Subscript(ps.pool, value, slice_node, ctx) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_slice(ps: rawptr[Parser]) -> rawptr[AST]: + """Parse slice: [expr] ':' [expr] [':' [expr]] | expr | expr, expr, ...""" + lower: rawptr[AST] = None + if not _match_op(ps, TokOp.Colon): + lower = _parse_expr(ps) + + # Check if there’s a comma (tuple syntax: a[x, y, ...]) + if _match_op(ps, TokOp.Comma): + elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + if lower is not None: + elts.append(lower) + while _accept_op(ps, TokOp.Comma): + if _match_op(ps, TokOp.Rsqb): + break # Trailing comma + elem: rawptr[AST] = _parse_expr(ps) + if elem is not None: + elts.append(elem) + lineno: int = 0 + if lower is not None: + lineno = lower.lineno + node: rawptr[AST] = Tuple(ps.pool, elts, ASTCtx.Load) + _set_pos(node, lineno, 0, 0, 0) + return node + + if _match_op(ps, TokOp.Colon): + _advance(ps) + upper: rawptr[AST] = None + if not _match_op(ps, TokOp.Colon) and not _match_op(ps, TokOp.Rsqb): + upper = _parse_expr(ps) + step: rawptr[AST] = None + if _accept_op(ps, TokOp.Colon): + if not _match_op(ps, TokOp.Rsqb): + step = _parse_expr(ps) + lineno: int = 0 + if lower is not None: + lineno = lower.lineno + elif upper is not None: + lineno = upper.lineno + else: + lineno = _cur_lineno(ps) + node: rawptr[AST] = Slice(ps.pool, lower, upper, step) + _set_pos(node, lineno, 0, 0, 0) + return node + return lower + + +def _parse_atom(ps: rawptr[Parser]) -> rawptr[AST]: + """atom: the smallest unit of expression""" + ttype: int = _cur_type(ps) + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + + if ttype == TokenType.Name: + kw: int = _cur_kw(ps) + if kw == Keyword.True_: + _advance(ps) + return _new_const_bool(ps.pool, 1, lineno, col) + if kw == Keyword.False_: + _advance(ps) + return _new_const_bool(ps.pool, 0, lineno, col) + if kw == Keyword.None_: + _advance(ps) + return _new_const_none(ps.pool, lineno, col) + name_str: str = _cur_str(ps) + _advance(ps) + node: rawptr[AST] = Name(ps.pool, name_str, ASTCtx.Load) + _set_pos(node, lineno, col, 0, 0) + return node + + if ttype == TokenType.Number: + tok: rawptr[Token] = ps.cur + _advance(ps) + if tok.is_float: + return _new_const_float(ps.pool, tok.float_val, lineno, col) + return _new_const_int(ps.pool, tok.int_val, lineno, col) + + if ttype == TokenType.String: + sval: str = _cur_str(ps) + is_fstr: int = ps.cur.kw_subtype & 4 + is_triple: int = ps.cur.kw_subtype & 8 + is_dquote: int = ps.cur.kw_subtype & 16 + is_char: int = ps.cur.kw_subtype & 32 + _advance(ps) + node: rawptr[AST] + if is_fstr: + node = _parse_fstring(ps, sval, lineno, col) + else: + node = _new_const_str(ps.pool, sval, lineno, col, is_dquote, is_triple, is_char) + # Python 相邻字符串字面量隐式拼接:"a" "b" → "ab" + # Nl 是括号内换行 token(允许括号内跨行拼接);语句级换行是 NewLine,不跳过 + while True: + _skip_nl(ps) + if _cur_type(ps) != TokenType.String: + break + n_sval: str = _cur_str(ps) + n_fstr: int = ps.cur.kw_subtype & 4 + n_triple: int = ps.cur.kw_subtype & 8 + n_dquote: int = ps.cur.kw_subtype & 16 + n_char: int = ps.cur.kw_subtype & 32 + _advance(ps) + n_node: rawptr[AST] + if n_fstr: + n_node = _parse_fstring(ps, n_sval, lineno, col) + else: + n_node = _new_const_str(ps.pool, n_sval, lineno, col, n_dquote, n_triple, n_char) + node = _concat_str_nodes(ps.pool, node, n_node) + return node + + if _match_op(ps, TokOp.Ellipsis): + _advance(ps) + return _new_const_none(ps.pool, lineno, col) + + if _match_op(ps, TokOp.LPar): + return _parse_paren(ps) + if _match_op(ps, TokOp.Lsqb): + return _parse_list(ps) + if _match_op(ps, TokOp.LBrace): + return _parse_brace(ps) + + _record_parse_err(ps) + ps.error_count += 1 + return None + + +def _parse_paren(ps: rawptr[Parser]) -> rawptr[AST]: + """Parenthesis expressions: () | (expr) | (expr,) | (expr, ...) | (yield)""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # ( + _skip_nl(ps) # Skip the line break after '(' (multi-line tuple) + if _match_op(ps, TokOp.RPar): + _advance(ps) + # Empty tuple + empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + node: rawptr[AST] = Tuple(ps.pool, empty, ASTCtx.Load) + _set_pos(node, lineno, col, 0, 0) + return node + # yield + if _cur_kw(ps) == Keyword.Yield: + y: rawptr[AST] = _parse_yield(ps) + _expect_op(ps, TokOp.RPar) + return y + first: rawptr[AST] = _parse_expr(ps) + if _match_op(ps, TokOp.Comma): + # Tuple + elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + elts.append(first) + while _accept_op(ps, TokOp.Comma): + _skip_nl(ps) + if _match_op(ps, TokOp.RPar): + break + e: rawptr[AST] = _parse_expr(ps) + elts.append(e) + _skip_nl(ps) # 末元素无尾逗号时收尾(多行元组) + _expect_op(ps, TokOp.RPar) + node = Tuple(ps.pool, elts, ASTCtx.Load) + _set_pos(node, lineno, col, 0, 0) + return node + _skip_nl(ps) # (expr\n) 单表达式括号跨行收尾 + _expect_op(ps, TokOp.RPar) + # Check for generator expression + if _cur_kw(ps) == Keyword.For: + return _parse_comprehension(ps, first, ASTKind.GeneratorExp, lineno, col) + return first + + +def _parse_list(ps: rawptr[Parser]) -> rawptr[AST]: + """List expressions: [] | [expr_list] | [expr_list comp_for]""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # [ + _skip_nl(ps) # Skip the line break after '[' (multi-line list) + if _match_op(ps, TokOp.Rsqb): + _advance(ps) + empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + node: rawptr[AST] = List(ps.pool, empty, ASTCtx.Load) + _set_pos(node, lineno, col, 0, 0) + return node + first: rawptr[AST] = _parse_expr(ps) + if _cur_kw(ps) == Keyword.For: + node = _parse_comprehension(ps, first, ASTKind.ListComp, lineno, col) + _expect_op(ps, TokOp.Rsqb) + return node + elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + elts.append(first) + while _accept_op(ps, TokOp.Comma): + _skip_nl(ps) + if _match_op(ps, TokOp.Rsqb): + break + e: rawptr[AST] = _parse_expr(ps) + elts.append(e) + _skip_nl(ps) # 末元素无尾逗号时收尾(base64.vp 多行列表) + _expect_op(ps, TokOp.Rsqb) + node = List(ps.pool, elts, ASTCtx.Load) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_brace(ps: rawptr[Parser]) -> rawptr[AST]: + """Brace expressions: {} | {set_items} | {dict_items} | {comp_for}""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # { + _skip_nl(ps) # Skip the line break after '{' (multi-line dict) + if _match_op(ps, TokOp.RBrace): + _advance(ps) + empty_k: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + empty_v: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + node: rawptr[AST] = Dict(ps.pool, empty_k, empty_v) + _set_pos(node, lineno, col, 0, 0) + return node + first: rawptr[AST] = _parse_expr(ps) + # dict: key: value + if _match_op(ps, TokOp.Colon): + _advance(ps) + val: rawptr[AST] = _parse_expr(ps) + if _cur_kw(ps) == Keyword.For: + # dict comprehension + gens: rawptr[list[rawptr[AST]]] = _parse_comp_for(ps) + node = DictComp(ps.pool, first, val, gens) + _set_pos(node, lineno, col, 0, 0) + _expect_op(ps, TokOp.RBrace) + return node + keys: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + values: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + keys.append(first) + values.append(val) + while _accept_op(ps, TokOp.Comma): + _skip_nl(ps) + if _match_op(ps, TokOp.RBrace): + break + k: rawptr[AST] = _parse_expr(ps) + _expect_op(ps, TokOp.Colon) + v: rawptr[AST] = _parse_expr(ps) + keys.append(k) + values.append(v) + _skip_nl(ps) # 末元素无尾逗号时收尾 + _expect_op(ps, TokOp.RBrace) + node = Dict(ps.pool, keys, values) + _set_pos(node, lineno, col, 0, 0) + return node + # set + if _cur_kw(ps) == Keyword.For: + node = _parse_comprehension(ps, first, ASTKind.SetComp, lineno, col) + _expect_op(ps, TokOp.RBrace) + return node + elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + elts.append(first) + while _accept_op(ps, TokOp.Comma): + _skip_nl(ps) # 括号内跨行集合字面量 + if _match_op(ps, TokOp.RBrace): + break + e: rawptr[AST] = _parse_expr(ps) + elts.append(e) + _skip_nl(ps) # 末元素无尾逗号时收尾 + _expect_op(ps, TokOp.RBrace) + node = Set(ps.pool, elts) + _set_pos(node, lineno, col, 0, 0) + return node + + +# ============================================================ +# f-string parsing +# ============================================================ + +def _parse_sub_expr(ps: rawptr[Parser], src: str) -> rawptr[AST]: + """Create a sub-lexer and parser to parse expressions (used for {expr} inside f-strings)""" + pool: rawptr[memhub.MemManager] = ps.pool + lx: rawptr[Lexer] = new_lexer(pool) + if lx is None: return None + _lexer_init(lx, src, pool) + tokens: rawptr[Token] = tokenize(lx) + if tokens is None: return None + sub_ps: rawptr[Parser] = new_parser(pool) + if sub_ps is None: return None + _parser_init(sub_ps, tokens, pool) + return _parse_test(sub_ps) + + +def _append_str_value(pool: rawptr[memhub.MemManager], + values: rawptr[list[rawptr[AST]]], + node: rawptr[AST]): + """将字符串节点展开加入 values(JoinedStr 展开其 values,其余直接追加)""" + if node is None: + return + if node.kind() == ASTKind.JoinedStr: + js_val: rawptr[JoinedStr] = (rawptr[JoinedStr])(node) + if js_val.values is not None: + n_v: t.CSizeT = js_val.values.__len__() + i_v: t.CSizeT + for i_v in range(n_v): + values.append(js_val.values.get(i_v)) + else: + values.append(node) + + +def _concat_str_nodes(pool: rawptr[memhub.MemManager], + a: rawptr[AST], b: rawptr[AST]) -> rawptr[AST]: + """合并相邻字符串字面量节点(Python 隐式拼接 "a" "b" → "ab") + + 纯常量字符串直接拼接文本;涉及 f-string(JoinedStr/FormattedValue)时 + 合并到 JoinedStr 的 values 列表。 + """ + if a is None: + return b + if b is None: + return a + # 两个都是常量字符串 → 直接拼接文本 + if a.kind() == ASTKind.Constant and b.kind() == ASTKind.Constant: + ca_c: rawptr[Constant] = (rawptr[Constant])(a) + cb_c: rawptr[Constant] = (rawptr[Constant])(b) + if ca_c.const_kind == CONST_STR and cb_c.const_kind == CONST_STR \ + and ca_c.str_val is not None and cb_c.str_val is not None: + la_c: t.CSizeT = string.strlen(ca_c.str_val) + lb_c: t.CSizeT = string.strlen(cb_c.str_val) + buf_c: str = pool.alloc(la_c + lb_c + 1) + if buf_c is not None: + string.memcpy(buf_c, ca_c.str_val, la_c) + string.memcpy(buf_c + la_c, cb_c.str_val, lb_c + 1) + return _new_const_str(pool, buf_c, a.lineno, a.col_offset, 0, 0, 0) + # 涉及 f-string → 合并到 JoinedStr + values_c: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](pool, 8) + _append_str_value(pool, values_c, a) + _append_str_value(pool, values_c, b) + if values_c.__len__() == 1: + return values_c.get(0) + node_c: rawptr[AST] = JoinedStr(pool, values_c) + _set_pos(node_c, a.lineno, a.col_offset, 0, 0) + return node_c + + +def _parse_fstring(ps: rawptr[Parser], content: str, + lineno: int, col: int) -> rawptr[AST]: + """Parse f-string content, building JoinedStr or FormattedValue nodes""" + pool: rawptr[memhub.MemManager] = ps.pool + values: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](pool, 8) + i: t.CSizeT = 0 + content_len: t.CSizeT = string.strlen(content) + # Literal text buffer for f-string content + lit_buf: str = pool.alloc(content_len + 1) + if lit_buf is None: return None + lit_pos: t.CSizeT = 0 + + while i < content_len: + ch: t.CChar = content[i] + if ch == '{': + # {{ → Literal { + if i + 1 < content_len and content[i + 1] == '{': + lit_buf[lit_pos] = '{' + lit_pos += 1 + i += 2 + continue + # Refresh literal buffer + if lit_pos > 0: + lit_buf[lit_pos] = '\0' + lit_node: rawptr[AST] = _new_const_str(pool, lit_buf, lineno, col) + values.append(lit_node) + lit_buf = pool.alloc(content_len + 1) + if lit_buf is None: break + lit_pos = 0 + # Find matching closing '}' while tracking nested '{}' and string literals inside the f-string + j: t.CSizeT = i + 1 + depth: int = 1 + conv_pos: t.CSizeT = 0 + fmt_pos: t.CSizeT = 0 + while j < content_len and depth > 0: + c: t.CChar = content[j] + if c == '\'' or c == '"': + # 跳过表达式块内的嵌套字符串字面量(PEP 701 同引号嵌套), + # 避免其内部字符干扰 {} 深度/转换/格式分隔符定位 + nq: t.CChar = c + j += 1 + while j < content_len: + c2: t.CChar = content[j] + if c2 == '\\': + j += 2 + continue + if c2 == nq: + j += 1 + break + j += 1 + continue + if c == '{': + depth += 1 + elif c == '}': + depth -= 1 + if depth == 0: + break + elif depth == 1: + if c == '!' and conv_pos == 0 and fmt_pos == 0: + if j + 1 < content_len: + nc: t.CChar = content[j + 1] + if nc == 's' or nc == 'r' or nc == 'a': + if j + 2 < content_len: + after: t.CChar = content[j + 2] + if after == ':' or after == '}': + conv_pos = j + elif j + 2 == content_len: + conv_pos = j + elif c == ':' and fmt_pos == 0: + fmt_pos = j + j += 1 + if depth != 0: + _record_parse_err(ps) + ps.error_count += 1 + break + # Extract expression text + expr_end: t.CSizeT = j + if conv_pos > 0: + expr_end = conv_pos + elif fmt_pos > 0: + expr_end = fmt_pos + expr_len: t.CSizeT = expr_end - i - 1 + if expr_len == 0: + _record_parse_err(ps) + ps.error_count += 1 + i = j + 1 + continue + expr_buf: str = pool.alloc(expr_len + 1) + if expr_buf is None: break + string.memcpy(expr_buf, content + i + 1, expr_len) + expr_buf[expr_len] = '\0' + # Parse expression text + expr_node: rawptr[AST] = _parse_sub_expr(ps, expr_buf) + # Conversion specifier + conversion: int = -1 + if conv_pos > 0: + conv_char: t.CChar = content[conv_pos + 1] + if conv_char == 's': + conversion = 115 + elif conv_char == 'r': + conversion = 114 + elif conv_char == 'a': + conversion = 97 + # Format specifier + format_spec: rawptr[AST] = None + if fmt_pos > 0: + spec_len: t.CSizeT = j - fmt_pos - 1 + if spec_len > 0: + spec_buf: str = pool.alloc(spec_len + 1) + if spec_buf is not None: + string.memcpy(spec_buf, content + fmt_pos + 1, spec_len) + spec_buf[spec_len] = '\0' + format_spec = _new_const_str(pool, spec_buf, lineno, col) + fv: rawptr[AST] = FormattedValue(pool, expr_node, conversion, format_spec) + _set_pos(fv, lineno, col, 0, 0) + values.append(fv) + i = j + 1 + elif ch == '}': + if i + 1 < content_len and content[i + 1] == '}': + lit_buf[lit_pos] = '}' + lit_pos += 1 + i += 2 + else: + _record_parse_err(ps) + ps.error_count += 1 + i += 1 + else: + lit_buf[lit_pos] = ch + lit_pos += 1 + i += 1 + + # Refresh remaining literal buffer + if lit_pos > 0: + lit_buf[lit_pos] = '\0' + lit_node2: rawptr[AST] = _new_const_str(pool, lit_buf, lineno, col) + values.append(lit_node2) + + # If no content, return empty string + if values.__len__() == 0: + return _new_const_str(pool, "", lineno, col) + # If only one FormattedValue and no literal, return FormattedValue + if values.__len__() == 1: + first: rawptr[AST] = values.get(0) + if first.kind() == ASTKind.FormattedValue: + return first + node: rawptr[AST] = JoinedStr(pool, values) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_comprehension(ps: rawptr[Parser], elt: rawptr[AST], + vtype: int, lineno: int, col: int) -> rawptr[AST]: + """General comprehension: elt comp_for""" + gens: rawptr[list[rawptr[AST]]] = _parse_comp_for(ps) + node: rawptr[AST] = None + if vtype == ASTKind.ListComp: + node = ListComp(ps.pool, elt, gens) + elif vtype == ASTKind.SetComp: + node = SetComp(ps.pool, elt, gens) + else: + node = GeneratorExp(ps.pool, elt, gens) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_comp_for(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]: + """comp_for: ['async'] 'for' exprlist 'in' or_test [comp_if] (comp_for)* + Returns generators list。""" + gens: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + while True: + is_async: int = 0 + if _cur_kw(ps) == Keyword.Async: + is_async = 1 + _advance(ps) + if _cur_kw(ps) != Keyword.For: + break + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + target: rawptr[AST] = _parse_target_list(ps) + if _cur_kw(ps) != Keyword.In: + ps.error_count += 1 + break + _advance(ps) + iter_: rawptr[AST] = _parse_or_test(ps) + # Collect ifs to list + ifs: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + while _cur_kw(ps) == Keyword.If: + _advance(ps) + cond: rawptr[AST] = _parse_or_test(ps) + ifs.append(cond) + comp: rawptr[AST] = Comprehension(ps.pool, target, iter_, ifs, is_async) + _set_pos(comp, lineno, col, 0, 0) + gens.append(comp) + return gens + + +def _parse_target_list(ps: rawptr[Parser]) -> rawptr[AST]: + """Parse assignment target list (comma-separated)""" + first: rawptr[AST] = _parse_target(ps) + if _match_op(ps, TokOp.Comma): + elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + elts.append(first) + while _accept_op(ps, TokOp.Comma): + if _cur_kw(ps) == Keyword.In: + break + e: rawptr[AST] = _parse_target(ps) + elts.append(e) + node: rawptr[AST] = Tuple(ps.pool, elts, ASTCtx.Store) + _set_pos(node, first.lineno, first.col_offset, 0, 0) + return node + return first + + +def _parse_target(ps: rawptr[Parser]) -> rawptr[AST]: + """Parse assignment target""" + if _match_op(ps, TokOp.Star): + _advance(ps) + inner: rawptr[AST] = _parse_atom_expr(ps) + node: rawptr[AST] = Starred(ps.pool, inner, ASTCtx.Store) + _set_pos(node, inner.lineno, inner.col_offset, 0, 0) + return node + if _match_op(ps, TokOp.LPar): + _advance(ps) + _skip_nl(ps) + t_: rawptr[AST] = _parse_target_list(ps) + _skip_nl(ps) + _expect_op(ps, TokOp.RPar) + return t_ + if _match_op(ps, TokOp.Lsqb): + _advance(ps) + _skip_nl(ps) + first: rawptr[AST] = _parse_target(ps) + elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + elts.append(first) + while _accept_op(ps, TokOp.Comma): + _skip_nl(ps) + if _match_op(ps, TokOp.Rsqb): + break + e: rawptr[AST] = _parse_target(ps) + elts.append(e) + _expect_op(ps, TokOp.Rsqb) + node = List(ps.pool, elts, ASTCtx.Store) + _set_pos(node, first.lineno, first.col_offset, 0, 0) + return node + # Normal atom_expr, ctx=Store + node = _parse_atom_expr(ps) + if node is not None: + _set_ctx(node, ASTCtx.Store) + return node + + +def _parse_yield(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # yield + if _cur_kw(ps) == Keyword.From: + _advance(ps) + val: rawptr[AST] = _parse_expr(ps) + node: rawptr[AST] = YieldFrom(ps.pool, val) + _set_pos(node, lineno, col, 0, 0) + return node + val2: rawptr[AST] = None + if not _match_op(ps, TokOp.RPar) and _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker: + if _cur_type(ps) == TokenType.Op and (_cur_op(ps) == TokOp.Comma or _cur_op(ps) == TokOp.Rsqb): + pass + else: + val2 = _parse_test(ps) + node = Yield(ps.pool, val2) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_varargslist(ps: rawptr[Parser], is_lambda: int = 0) -> rawptr[AST]: + """Parse function parameter list + New API: args/defaults/kw_defaults are lists, vararg/kwarg are fields.""" + args: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + vararg: rawptr[AST] = None + kwarg: rawptr[AST] = None + defaults: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + kw_defaults: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + + while True: + _skip_nl(ps) + if _match_op(ps, TokOp.RPar): + break + # *args 或 * (kwonly separated) + if _match_op(ps, TokOp.Star): + _advance(ps) + _skip_nl(ps) + if _cur_type(ps) == TokenType.Name: + vararg = _parse_arg_def(ps, is_lambda) + if not _accept_op(ps, TokOp.Comma): + break + _skip_nl(ps) + continue + # **kwargs + if _match_op(ps, TokOp.StarStar): + _advance(ps) + _skip_nl(ps) + kwarg = _parse_arg_def(ps, is_lambda) + if not _accept_op(ps, TokOp.Comma): + break + _skip_nl(ps) + continue + # / Delimiter (posonly end)— New API: posonly/regular are not distinguished + if _match_op(ps, TokOp.Slash): + _advance(ps) + if not _accept_op(ps, TokOp.Comma): + break + _skip_nl(ps) + continue + # Normal parameter + a: rawptr[AST] = _parse_arg_def(ps, is_lambda) + if a is None: break + args.append(a) + if _match_op(ps, TokOp.Equal): + _advance(ps) + d: rawptr[AST] = _parse_test(ps) + defaults.append(d) + if not _accept_op(ps, TokOp.Comma): + break + _skip_nl(ps) + return Arguments(ps.pool, args, vararg, kwarg, defaults, kw_defaults) + + +def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]: + """解析类型注解,支持 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)。 + """ + before_ec: int = ps.error_count + base: rawptr[AST] = _parse_test(ps) + if base is None: + _record_parse_err(ps) # 注解解析失败:记录具体位置(error_count 已在 _parse_test 内增加) + return None + ptr_level: int = 0 + # 回收垃圾节点(_parse_test 对后缀 * / ** 的消费产物) + if base.kind() == ASTKind.BinOp: + gb: rawptr[BinOp] = (rawptr[BinOp])(base) + if gb.op == OpKind.Mult and gb.right is None: + ptr_level += 1 + base = gb.left + elif gb.op == OpKind.Pow: + if gb.right is None: + ptr_level += 2 + base = gb.left + elif gb.right.kind() == ASTKind.UnaryOp: + gu: rawptr[UnaryOp] = (rawptr[UnaryOp])(gb.right) + if gu.op == OpKind.Deref and gu.operand is None: + ptr_level += 3 + base = gb.left + if ptr_level > 0: + ps.error_count = before_ec # 回收成功,恢复错误计数避免整文件解析失败 + # 循环消费残留后缀('**' 是词法单 token 计 2 层,'*' 计 1 层) + while _match_op(ps, TokOp.StarStar): + _advance(ps) + ptr_level += 2 + while _match_op(ps, TokOp.Star): + _advance(ps) + ptr_level += 1 + # 嵌套构造 rawptr[base](n 层) + li_an: int = 0 + while li_an < ptr_level: + cptr_an: rawptr[AST] = Attribute(ps.pool, + Name(ps.pool, "t", ASTCtx.Load), + "CPtr", ASTCtx.Load) + base = BinOp(ps.pool, base, OpKind.BitOr, cptr_an) + li_an += 1 + return base + + +def _parse_arg_def(ps: rawptr[Parser], is_lambda: int = 0) -> rawptr[AST]: + """arg: TokenType.Name [':' annotation]""" + if _cur_type(ps) != TokenType.Name: + ps.error_count += 1 + return None + name_str: str = _cur_str(ps) + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + annotation: rawptr[AST] = None + if is_lambda == 0: + if _accept_op(ps, TokOp.Colon): + annotation = _parse_type_ann(ps) + result: rawptr[AST] = Arg(ps.pool, name_str, annotation) + _set_pos(result, lineno, col, 0, 0) + return result + + +# ============================================================ +# Statement parsing (Design: directly append to parent, do not return a list) +# ============================================================ + +def _parse_suite_into_children(ps: rawptr[Parser], parent: rawptr[AST]): + """Parse statement suite, directly append to parent's children. + suite: TokenType.NewLine TokenType.Indent stmt+ TokenType.Dedent | simple_stmt""" + _skip_nl(ps) + if _cur_type(ps) == TokenType.NewLine: + _advance(ps) + _skip_nl(ps) + if _cur_type(ps) != TokenType.Indent: + ps.error_count += 1 + return + _advance(ps) + sidx: int = 0 + while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker: + sidx += 1 + # Decorator (class body methods can have decorators) + if _match_op(ps, TokOp.At): + ps.pending_decorators = _parse_decorators(ps) + before_cur: rawptr[Token] = ps.cur + _parse_statement_into(ps, parent) + # Defensive: if parse did not advance token, force advance to avoid infinite loop + if ps.cur is before_cur: + ps.error_count += 1 + _advance(ps) + _skip_nl(ps) + _expect_type(ps, TokenType.Dedent) + return + # Single-line suite + _parse_simple_stmt_into(ps, parent) + + +def _parse_statement_into(ps: rawptr[Parser], parent: rawptr[AST]): + """Parse statement (compound or simple), append to the parent.""" + ttype: int = _cur_type(ps) + kw: int = _cur_kw(ps) + if ttype == TokenType.Name: + if kw == Keyword.If or kw == Keyword.While or kw == Keyword.For or kw == Keyword.Try: + stmt: rawptr[AST] = _parse_compound(ps) + if stmt is not None: + parent.append(stmt) + return + if kw == Keyword.With or _is_funcdef_kw(ps, kw) != 0 or kw == Keyword.Class: + stmt = _parse_compound(ps) + if stmt is not None: + parent.append(stmt) + return + if (kw == Keyword.Match and _is_match_stmt(ps) != 0) or kw == Keyword.Async: + stmt = _parse_compound(ps) + if stmt is not None: + parent.append(stmt) + return + _parse_simple_stmt_into(ps, parent) + + +def _parse_simple_stmt_into(ps: rawptr[Parser], parent: rawptr[AST]): + """simple_stmt: small_stmt (';' small_stmt)* [';'] TokenType.NewLine""" + first: rawptr[AST] = _parse_small_stmt(ps) + if first is not None: + parent.append(first) + while _accept_op(ps, TokOp.Semi): + if _cur_type(ps) == TokenType.NewLine or _cur_type(ps) == TokenType.EndMarker: + break + s: rawptr[AST] = _parse_small_stmt(ps) + if s is None: break + parent.append(s) + # 先跳过 Nl(括号内换行,如多行函数调用参数间的换行,属合法 token) + _skip_nl(ps) + # 语句结束检查:simple_stmt 必须以换行/文件尾结束。 + # 残留 token(如相邻表达式 `func12344444 avg3(...)` 被静默拆成多条语句、 + # `def` 缺失的关键字函数定义残留)是语法错误(fail-fast),禁止静默, + # 并仿 Python 报告错误位置(SyntaxError at line/col)。 + if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker: + _record_parse_err(ps) + err_str: str = _cur_str(ps) + if err_str is None: + err_str = "(none)" + # 定位:从链表头找 cur,记录前一个 token 与下一个 token 的上下文 + prev_tok: rawptr[Token] = None + walk_tok: rawptr[Token] = ps.tokens + while walk_tok is not None and walk_tok is not ps.cur: + prev_tok = walk_tok + walk_tok = walk_tok.next + pv_str: str = "(head)" + pv_line: int = 0 + pv_col: int = 0 + if prev_tok is not None: + pv_str = prev_tok.str_val + pv_line = prev_tok.lineno + pv_col = prev_tok.col_offset + nxt_tok: rawptr[Token] = ps.cur.next + nxt_str: str = "(none)" + nxt_line: int = 0 + nxt_col: int = 0 + if nxt_tok is not None: + nxt_str = nxt_tok.str_val + nxt_line = nxt_tok.lineno + nxt_col = nxt_tok.col_offset + stdio.printf("[AST] SyntaxError: line %d col %d (end %d %d), 意外的 token '%s' (type=%d) | prev='%s' (line %d col %d) | next='%s' (line %d col %d)\n", + _g_parse_err_line, _g_parse_err_col, ps.cur.end_lineno, ps.cur.end_col_offset, + err_str, _cur_type(ps), + pv_str, pv_line, pv_col, + nxt_str, nxt_line, nxt_col) + ps.error_count += 1 + return + if _cur_type(ps) == TokenType.NewLine: + _advance(ps) + + +def _parse_small_stmt(ps: rawptr[Parser]) -> rawptr[AST]: + """small_stmt: various simple statements""" + kw: int = _cur_kw(ps) + if kw == Keyword.Pass: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + node: rawptr[AST] = Pass(ps.pool) + _set_pos(node, lineno, col, 0, 0) + return node + if kw == Keyword.Break: + lineno = _cur_lineno(ps) + col = _cur_col(ps) + _advance(ps) + node = Break(ps.pool) + _set_pos(node, lineno, col, 0, 0) + return node + if kw == Keyword.Continue: + lineno = _cur_lineno(ps) + col = _cur_col(ps) + _advance(ps) + node = Continue(ps.pool) + _set_pos(node, lineno, col, 0, 0) + return node + if kw == Keyword.Return: + return _parse_return(ps) + if kw == Keyword.Raise: + return _parse_raise(ps) + if kw == Keyword.Global: + return _parse_global(ps) + if kw == Keyword.Nonlocal: + return _parse_nonlocal(ps) + if kw == Keyword.Import: + return _parse_import(ps) + if kw == Keyword.From: + return _parse_from_import(ps) + if kw == Keyword.Del: + return _parse_del(ps) + if kw == Keyword.Assert: + return _parse_assert(ps) + if kw == Keyword.Yield: + return _parse_expr_stmt(ps, _parse_yield(ps)) + # Expression statement / assignment / augassign / annassign + return _parse_expr_stmt(ps, None) + + +def _maybe_tuple(ps: rawptr[Parser], first: rawptr[AST]) -> rawptr[AST]: + """If there is a comma after first, collect the comma-separated expressions into a Tuple.""" + if first is None: return None + if not _match_op(ps, TokOp.Comma): + return first + lineno: int = first.lineno + col: int = first.col_offset + elts: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + elts.append(first) + _accept_op(ps, TokOp.Comma) + while True: + tt: int = _cur_type(ps) + if tt == TokenType.NewLine or tt == TokenType.EndMarker: + break + if tt == TokenType.Op: + op: int = _cur_op(ps) + if op == TokOp.Equal or op == TokOp.Semi: + break + e: rawptr[AST] = _parse_expr(ps) + if e is None: break + elts.append(e) + if not _accept_op(ps, TokOp.Comma): + break + node: rawptr[AST] = Tuple(ps.pool, elts, ASTCtx.Load) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_testlist(ps: rawptr[Parser]) -> rawptr[AST]: + """Parse a comma-separated list of expressions, if there’s more than one, form a tuple.""" + first: rawptr[AST] = _parse_expr(ps) + return _maybe_tuple(ps, first) + + +def _parse_expr_stmt(ps: rawptr[Parser], first: rawptr[AST]) -> rawptr[AST]: + """Expression statement, handle assignment/augassign/annassign""" + if first is None: + first = _parse_testlist(ps) + else: + first = _maybe_tuple(ps, first) + if first is None: + return None + lineno: int = first.lineno + col: int = first.col_offset + # Assignment + if _match_op(ps, TokOp.Equal): + targets: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + targets.append(first) + _fix_store_ctx(first) + value: rawptr[AST] = None + accept_result: int = _accept_op(ps, TokOp.Equal) + while accept_result: + v: rawptr[AST] = _parse_testlist(ps) + if v is None: break + if value is not None: + targets.append(value) + _fix_store_ctx(value) + value = v + accept_result = _accept_op(ps, TokOp.Equal) + node: rawptr[AST] = Assign(ps.pool, targets, value) + _set_pos(node, lineno, col, 0, 0) + return node + # augassign + if _cur_type(ps) == TokenType.Op: + op_id: int = _cur_op(ps) + aug_op: int = _augop_from_op(op_id) + if aug_op != OpKind.NoneOp: + _advance(ps) + value2: rawptr[AST] = _parse_testlist(ps) + _fix_store_ctx(first) + node2: rawptr[AST] = AugAssign(ps.pool, first, aug_op, value2) + _set_pos(node2, lineno, col, 0, 0) + return node2 + # annassign: target ':' annotation [= value] + if _match_op(ps, TokOp.Colon): + _advance(ps) + annotation: rawptr[AST] = _parse_type_ann(ps) + value3: rawptr[AST] = None + if _accept_op(ps, TokOp.Equal): + value3 = _parse_testlist(ps) + if ps.pool is None: + return None + _fix_store_ctx(first) + if ps.pool is None: + return None + node3: rawptr[AST] = AnnAssign(ps.pool, first, annotation, value3, 1) + _set_pos(node3, lineno, col, 0, 0) + return node3 + # Expression statement + node4: rawptr[AST] = Expr(ps.pool, first) + _set_pos(node4, lineno, col, 0, 0) + return node4 + + +def _parse_return(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + val: rawptr[AST] = None + if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker and _cur_type(ps) != TokenType.Op: + val = _parse_testlist(ps) + elif _cur_type(ps) == TokenType.Op and _cur_op(ps) != TokOp.Semi: + val = _parse_testlist(ps) + node: rawptr[AST] = Return(ps.pool, val) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_raise(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + exc: rawptr[AST] = None + cause: rawptr[AST] = None + if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker: + exc = _parse_test(ps) + if _cur_kw(ps) == Keyword.From: + _advance(ps) + cause = _parse_test(ps) + node: rawptr[AST] = Raise(ps.pool, exc, cause) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_global(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + names: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + if _cur_type(ps) == TokenType.Name: + n: rawptr[AST] = Name(ps.pool, _cur_str(ps), ASTCtx.Store) + _set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0) + names.append(n) + _advance(ps) + while _accept_op(ps, TokOp.Comma): + if _cur_type(ps) != TokenType.Name: break + n = Name(ps.pool, _cur_str(ps), ASTCtx.Store) + _set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0) + names.append(n) + _advance(ps) + node: rawptr[AST] = Global(ps.pool, names) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_nonlocal(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + names: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + if _cur_type(ps) == TokenType.Name: + n: rawptr[AST] = Name(ps.pool, _cur_str(ps), ASTCtx.Store) + _set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0) + names.append(n) + _advance(ps) + while _accept_op(ps, TokOp.Comma): + if _cur_type(ps) != TokenType.Name: break + n = Name(ps.pool, _cur_str(ps), ASTCtx.Store) + _set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0) + names.append(n) + _advance(ps) + node: rawptr[AST] = Nonlocal(ps.pool, names) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_import(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + names: rawptr[list[rawptr[AST]]] = _parse_alias_list(ps) + node: rawptr[AST] = Import(ps.pool, names) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_from_import(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + # Parse module (possibly with dots) + level: int = 0 + while _match_op(ps, TokOp.Dot): + level += 1 + _advance(ps) + while _match_op(ps, TokOp.Ellipsis): + level += 3 + _advance(ps) + module_name: str = None + # The token type for keywords (like import) is also Name, so they must be excluded using kw_subtype, + # otherwise the `import` in `from . import` would be mistakenly treated as a module_name. + # Note: only `import` itself should be excluded. Module names can be keywords (like in `from .match import ...`, + # where match is a Viper match statement keyword), requiring NotKeyword would cause + # `from .match import` to fail parsing and the dependency scan would miss that module. + if _cur_type(ps) == TokenType.Name and _cur_kw(ps) != Keyword.Import: + module_name = _cur_str(ps) + _advance(ps) + while _match_op(ps, TokOp.Dot): + _advance(ps) + if _cur_type(ps) == TokenType.Name and _cur_kw(ps) == Keyword.NotKeyword: + next_name: str = _cur_str(ps) + mlen: int = string.strlen(module_name) + nlen: int = string.strlen(next_name) + new_mod: str = ps.pool.alloc(mlen + 1 + nlen + 1) + if new_mod is not None: + string.strcpy(new_mod, module_name) + new_mod[mlen] = '.' + string.strcpy(new_mod + mlen + 1, next_name) + module_name = new_mod + _advance(ps) + if _cur_kw(ps) != Keyword.Import: + ps.error_count += 1 + return None + _advance(ps) + _skip_nl(ps) + # import * + if _match_op(ps, TokOp.Star): + _advance(ps) + star_alias: rawptr[AST] = Alias(ps.pool, "*", None) + _set_pos(star_alias, lineno, col, 0, 0) + star_names: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + star_names.append(star_alias) + node: rawptr[AST] = ImportFrom(ps.pool, module_name, star_names, level) + _set_pos(node, lineno, col, 0, 0) + return node + # ( names ) or names + if _match_op(ps, TokOp.LPar): + _advance(ps) + _skip_nl(ps) + names: rawptr[list[rawptr[AST]]] = _parse_alias_list(ps) + _skip_nl(ps) + _expect_op(ps, TokOp.RPar) + else: + names = _parse_alias_list(ps) + node: rawptr[AST] = ImportFrom(ps.pool, module_name, names, level) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_alias_list(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]: + """Parse alias list.""" + names: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + while True: + if _cur_type(ps) != TokenType.Name: + break + name_str: str = _cur_str(ps) + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + # dotted name + while _match_op(ps, TokOp.Dot): + _advance(ps) + if _cur_type(ps) == TokenType.Name: + next_n: str = _cur_str(ps) + nmlen: int = string.strlen(name_str) + nxtlen: int = string.strlen(next_n) + new_name: str = ps.pool.alloc(nmlen + 1 + nxtlen + 1) + if new_name is not None: + string.strcpy(new_name, name_str) + new_name[nmlen] = '.' + string.strcpy(new_name + nmlen + 1, next_n) + name_str = new_name + _advance(ps) + asname: str = None + if _cur_kw(ps) == Keyword.As: + _advance(ps) + if _cur_type(ps) == TokenType.Name: + asname = _cur_str(ps) + _advance(ps) + a: rawptr[AST] = Alias(ps.pool, name_str, asname) + _set_pos(a, lineno, col, 0, 0) + names.append(a) + if not _accept_op(ps, TokOp.Comma): + break + _skip_nl(ps) + return names + + +def _parse_del(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + targets: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + while True: + t_: rawptr[AST] = _parse_atom_expr(ps) + if t_ is None: break + _set_ctx(t_, ASTCtx.Del) + targets.append(t_) + if not _accept_op(ps, TokOp.Comma): + break + node: rawptr[AST] = Delete(ps.pool, targets) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_assert(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + test: rawptr[AST] = _parse_test(ps) + msg: rawptr[AST] = None + if _accept_op(ps, TokOp.Comma): + msg = _parse_test(ps) + node: rawptr[AST] = Assert(ps.pool, test, msg) + _set_pos(node, lineno, col, 0, 0) + return node + + +# ============================================================ +# Compound statements +# ============================================================ + +def _parse_compound(ps: rawptr[Parser]) -> rawptr[AST]: + kw: int = _cur_kw(ps) + if kw == Keyword.If: + return _parse_if(ps) + if kw == Keyword.While: + return _parse_while(ps) + if kw == Keyword.For: + return _parse_for(ps, 0) + if kw == Keyword.Try: + return _parse_try(ps) + if kw == Keyword.With: + return _parse_with(ps, 0) + if _is_funcdef_kw(ps, kw) != 0: + return _parse_funcdef(ps, 0) + if kw == Keyword.Class: + return _parse_classdef(ps) + if kw == Keyword.Async: + _advance(ps) + if _is_funcdef_kw(ps, _cur_kw(ps)) != 0: + return _parse_funcdef(ps, 1) + if _cur_kw(ps) == Keyword.For: + return _parse_for(ps, 1) + if _cur_kw(ps) == Keyword.With: + return _parse_with(ps, 1) + ps.error_count += 1 + return None + if kw == Keyword.Match and _is_match_stmt(ps) != 0: + return _parse_match(ps) + ps.error_count += 1 + return None + + +def _parse_if(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # if + test: rawptr[AST] = _parse_test(ps) + _expect_op(ps, TokOp.Colon) + # First construct with empty orelse, then fill it in later + empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + node: rawptr[If] = If(ps.pool, test, empty) + _parse_suite_into_children(ps, node) + orelse: rawptr[list[rawptr[AST]]] = _parse_if_tail(ps) + node.orelse = orelse + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_if_tail(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]: + """Parse elif/else chain and return orelse list. + + elif: orelse = list containing single If node (nested) + else: orelse = statement list + """ + if _cur_kw(ps) == Keyword.Elif: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) + elif_test: rawptr[AST] = _parse_test(ps) + _expect_op(ps, TokOp.Colon) + empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + elif_node: rawptr[If] = If(ps.pool, elif_test, empty) + _parse_suite_into_children(ps, elif_node) + elif_orelse: rawptr[list[rawptr[AST]]] = _parse_if_tail(ps) + elif_node.orelse = elif_orelse + _set_pos(elif_node, lineno, col, 0, 0) + orelse: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + orelse.append(elif_node) + return orelse + if _cur_kw(ps) == Keyword.Else: + _advance(ps) + _expect_op(ps, TokOp.Colon) + scratch: rawptr[Module] = Module(ps.pool) + _parse_suite_into_children(ps, scratch) + return scratch.children + return list[rawptr[AST]](ps.pool, 8) # 空 list + + +# ============================================================ +# Parsing Compound Statements (Continued) +# ============================================================ + +def _parse_while(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # while + test: rawptr[AST] = _parse_test(ps) + _expect_op(ps, TokOp.Colon) + empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + node: rawptr[While] = While(ps.pool, test, empty) + _parse_suite_into_children(ps, node) + if _cur_kw(ps) == Keyword.Else: + _advance(ps) + _expect_op(ps, TokOp.Colon) + scratch: rawptr[Module] = Module(ps.pool) + _parse_suite_into_children(ps, scratch) + node.orelse = scratch.children + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_for(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # for + target: rawptr[AST] = _parse_target_list(ps) + if _cur_kw(ps) != Keyword.In: + ps.error_count += 1 + return None + _advance(ps) + iter_: rawptr[AST] = _parse_or_test(ps) + _expect_op(ps, TokOp.Colon) + empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + node: rawptr[For] = For(ps.pool, target, iter_, empty, is_async) + _fix_store_ctx(target) + _parse_suite_into_children(ps, node) + if _cur_kw(ps) == Keyword.Else: + _advance(ps) + _expect_op(ps, TokOp.Colon) + scratch: rawptr[Module] = Module(ps.pool) + _parse_suite_into_children(ps, scratch) + node.orelse = scratch.children + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_try(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # try + _expect_op(ps, TokOp.Colon) + empty: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + node: rawptr[Try] = Try(ps.pool, empty, empty, empty) + _parse_suite_into_children(ps, node) + # handlers (except) + handlers: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + while _cur_kw(ps) == Keyword.Except: + h: rawptr[AST] = _parse_except_handler(ps) + if h is not None: + handlers.append(h) + node.handlers = handlers + # else + if _cur_kw(ps) == Keyword.Else: + _advance(ps) + _expect_op(ps, TokOp.Colon) + scratch: rawptr[Module] = Module(ps.pool) + _parse_suite_into_children(ps, scratch) + node.orelse = scratch.children + # finally + if _cur_kw(ps) == Keyword.Finally: + _advance(ps) + _expect_op(ps, TokOp.Colon) + scratch2: rawptr[Module] = Module(ps.pool) + _parse_suite_into_children(ps, scratch2) + node.finalbody = scratch2.children + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_except_handler(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # except + type_: rawptr[AST] = None + name: str = None + if _cur_op(ps) != TokOp.Colon: + type_ = _parse_test(ps) + if _cur_kw(ps) == Keyword.As: + _advance(ps) + if _cur_type(ps) == TokenType.Name: + name = _cur_str(ps) + _advance(ps) + _expect_op(ps, TokOp.Colon) + node: rawptr[AST] = ExceptHandler(ps.pool, type_, name) + _parse_suite_into_children(ps, node) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_with(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # with + items: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + # items + while True: + item: rawptr[AST] = _parse_with_item(ps) + if item is None: break + items.append(item) + if not _accept_op(ps, TokOp.Comma): + break + # Check if we can continue with a newline (with (...): form) + if _cur_type(ps) == TokenType.NewLine or _cur_type(ps) == TokenType.Nl: + break + _expect_op(ps, TokOp.Colon) + node: rawptr[With] = With(ps.pool, items, is_async) + _parse_suite_into_children(ps, node) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_with_item(ps: rawptr[Parser]) -> rawptr[AST]: + """with_item: test ['as' expr]""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + context_expr: rawptr[AST] = _parse_or_test(ps) + if context_expr is None: return None + optional_vars: rawptr[AST] = None + if _cur_kw(ps) == Keyword.As: + _advance(ps) + optional_vars = _parse_target(ps) + _fix_store_ctx(optional_vars) + node: rawptr[AST] = WithItem(ps.pool, context_expr, optional_vars) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_funcdef(ps: rawptr[Parser], is_async: int) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + is_extern_fd: int = 0 + is_export_fd: int = 0 + is_define_fd: int = 0 + if _cur_kw(ps) == Keyword.Extern: + # extern 声明:extern [def] A(...) [-> T] [": pass" 可选] + is_extern_fd = 1 + _advance(ps) # extern + if _cur_kw(ps) == Keyword.Def: + _advance(ps) + elif _cur_kw(ps) == Keyword.Export: + # export 声明:export [def] A(...) [-> T] [": pass" 可选] + is_export_fd = 1 + _advance(ps) # export + if _cur_kw(ps) == Keyword.Def: + _advance(ps) + elif _cur_kw(ps) == Keyword.Define: + # define 宏函数:语法与 def 完全一致,返回注解自动追加 t.CDefine + # 标记为宏函数(StubGen 保留原样 → C #define / alwaysinline 内联) + is_define_fd = 1 + _advance(ps) # define + else: + _advance(ps) # def / fn / ... + if _cur_type(ps) != TokenType.Name: + ps.error_count += 1 + return None + name: str = _cur_str(ps) + _advance(ps) + # PEP 695 泛型函数类型参数: def add[T](a: T, b: T) — 解析并存入 type_params。 + # (与 class 解析器一致;之前缺失此处理导致 _expect_op(Colon) 失败、函数体被 + # 误提升到模块级——b = Box(42) 被当作模块级全局注册,函数内赋值误报 + # "需用 global 声明"。翻译时 type_params 映射到 int 兜底。) + type_params_fd: rawptr[list[str]] = None + if _accept_op(ps, TokOp.Lsqb): + type_params_fd = list[str](ps.pool, 8) + while not _match_op(ps, TokOp.Rsqb): + if _cur_type(ps) == TokenType.EndMarker: + break + if _cur_type(ps) == TokenType.Name: + tp_name_fd: str = _cur_str(ps) + _advance(ps) + # 跳过可选 bound/约束: [T: bound] 或 [T: (A, B)] + if _match_op(ps, TokOp.Colon): + _advance(ps) + _parse_test(ps) + type_params_fd.append(tp_name_fd) + if not _accept_op(ps, TokOp.Comma): + break + _expect_op(ps, TokOp.Rsqb) + args: rawptr[AST] = None + if _accept_op(ps, TokOp.LPar): + args = _parse_varargslist(ps, 0) + _expect_op(ps, TokOp.RPar) + returns: rawptr[AST] = None + if _accept_op(ps, TokOp.RArrow): + returns = _parse_type_ann(ps) + if is_extern_fd: + # extern 声明:返回注解改写为 <原类型> | t.State,复用既有 + # State/外部 C 函数识别路径(vpi 注解含 "State" → 定义端裸名 + 不生成函数体) + state_attr_fd: rawptr[AST] = Attribute(ps.pool, + Name(ps.pool, "t", ASTCtx.Load), + "State", ASTCtx.Load) + if returns != None: + returns = BinOp(ps.pool, returns, OpKind.BitOr, state_attr_fd) + else: + returns = state_attr_fd + elif is_export_fd: + # export 声明:返回注解改写为 <原类型> | t.CExport,复用既有 + # CExport 识别路径(vpi 注解含 "CExport" → 定义端裸名 + 不加模块 SHA1 头) + export_attr_fd: rawptr[AST] = Attribute(ps.pool, + Name(ps.pool, "t", ASTCtx.Load), + "CExport", ASTCtx.Load) + if returns != None: + returns = BinOp(ps.pool, returns, OpKind.BitOr, export_attr_fd) + else: + returns = export_attr_fd + elif is_define_fd: + # define 宏函数:返回注解改写为 <原类型> | t.CDefine,复用既有 + # CDefine 宏函数识别路径(StubGen 保留原样 → C #define / alwaysinline) + define_attr_fd: rawptr[AST] = Attribute(ps.pool, + Name(ps.pool, "t", ASTCtx.Load), + "CDefine", ASTCtx.Load) + if returns != None: + returns = BinOp(ps.pool, returns, OpKind.BitOr, define_attr_fd) + else: + returns = define_attr_fd + dec_list: rawptr[list[rawptr[AST]]] = ps.pending_decorators + ps.pending_decorators = None + node: rawptr[FunctionDef] = FunctionDef(ps.pool, name, args, dec_list, returns, is_async, type_params_fd) + if is_extern_fd: + node.flags = node.flags | FLAG_IS_EXTERN + if is_export_fd: + node.flags = node.flags | FLAG_IS_EXPORT + if is_define_fd: + node.flags = node.flags | FLAG_IS_DEFINE + if (is_extern_fd == 0 and is_export_fd == 0 and is_define_fd == 0) or _match_op(ps, TokOp.Colon): + _expect_op(ps, TokOp.Colon) + _parse_suite_into_children(ps, node) + else: + # extern/export 无 ': pass' 形式(extern A(...) -> int):body 为空,仅声明。 + # 必须消费本行 NewLine:模块级/类体语句循环只 _skip_nl(Nl), + # 残留 NewLine 会被下一轮当表达式解析(_parse_atom → error_count++) + # → parse_tokens 返回 None(TPV 编译 extern 文件报 7 个扫描失败的根因) + if _cur_type(ps) == TokenType.NewLine: + _advance(ps) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_classdef(ps: rawptr[Parser]) -> rawptr[AST]: + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # class + if _cur_type(ps) != TokenType.Name: + ps.error_count += 1 + return None + name: str = _cur_str(ps) + _advance(ps) + # Parse type parameters [T] or [T, U] (PEP 695 generic class syntax) + type_params: rawptr[list[str]] = None + if _accept_op(ps, TokOp.Lsqb): + type_params = list[str](ps.pool, 8) + while not _match_op(ps, TokOp.Rsqb): + if _cur_type(ps) == TokenType.EndMarker: break + if _cur_type(ps) == TokenType.Name: + tp_name: str = _cur_str(ps) + _advance(ps) + # Skip optional bound/constraint: [T: bound] or [T: (A, B)] + if _match_op(ps, TokOp.Colon): + _advance(ps) + _parse_test(ps) + type_params.append(tp_name) + if not _accept_op(ps, TokOp.Comma): + break + _expect_op(ps, TokOp.Rsqb) + bases: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + keywords: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + # bases / keywords + if _accept_op(ps, TokOp.LPar): + while not _match_op(ps, TokOp.RPar): + # 基类括号内跨行(class X(A,\n B):):逗号后换行在此跳过, + # 否则 cur=NL 非 Name/Comma → break → _expect_op(RPar) 报 + # "意外的 token"(Generator.vp L34 多继承跨行根因) + _skip_nl(ps) + if _cur_type(ps) == TokenType.EndMarker: break + # base 或 keyword=value + if _cur_type(ps) == TokenType.Name: + # Parse as expression, if followed by '=', then as keyword=value + e: rawptr[AST] = _parse_or_test(ps) + if _match_op(ps, TokOp.Equal): + _advance(ps) + v: rawptr[AST] = _parse_test(ps) + kw_node: rawptr[AST] = KwNode(ps.pool, e, v) + keywords.append(kw_node) + else: + bases.append(e) + if not _accept_op(ps, TokOp.Comma): + break + _skip_nl(ps) # 末基类后换行收尾(class X(A,\n B):) + _expect_op(ps, TokOp.RPar) + _expect_op(ps, TokOp.Colon) + dec_list: rawptr[list[rawptr[AST]]] = ps.pending_decorators + ps.pending_decorators = None + node: rawptr[ClassDef] = ClassDef(ps.pool, name, bases, keywords, dec_list, type_params) + _parse_suite_into_children(ps, node) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_match(ps: rawptr[Parser]) -> rawptr[AST]: + """match statement: match subject ':' NewLine Indent case+ Dedent""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # match + subject: rawptr[AST] = _parse_test(ps) + _expect_op(ps, TokOp.Colon) + cases: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + node: rawptr[AST] = Match(ps.pool, subject, cases) + # Parsing body: Same NewLine/Indent/Dedent pattern as _parse_suite_into_children, + # but parses case clauses into a cases list instead of normal statements into children + _skip_nl(ps) + if _cur_type(ps) == TokenType.NewLine: + _advance(ps) + _skip_nl(ps) + if _cur_type(ps) == TokenType.Indent: + _advance(ps) + while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker: + if _cur_kw(ps) == Keyword.Case: + case_node: rawptr[AST] = _parse_match_case(ps) + if case_node is not None: + cases.append(case_node) + else: + _advance(ps) # Skip non-case token to avoid infinite loop + _expect_type(ps, TokenType.Dedent) + _set_pos(node, lineno, col, 0, 0) + return node + + +# ============================================================ +# Match pattern parsing +# ============================================================ + +def _make_name_ast(pool: rawptr[memhub.MemManager], node: rawptr[Name]) -> rawptr[AST]: + # Analysis: "Convert Name* to AST* (Solving the TransPyC Type Inference Problem)" + return node + + +def _parse_pattern(ps: rawptr[Parser]) -> rawptr[AST]: + """pattern: or_pattern ['as' NAME] + or_pattern: closed_pattern ('|' closed_pattern)*""" + pat: rawptr[AST] = _parse_closed_pattern(ps) + if pat is None: return None + # OR pattern + if _match_op(ps, TokOp.VBar): + patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + patterns.append(pat) + while _accept_op(ps, TokOp.VBar): + p: rawptr[AST] = _parse_closed_pattern(ps) + if p is not None: + patterns.append(p) + or_node: rawptr[AST] = MatchOr(ps.pool, patterns) + _set_pos(or_node, pat.lineno, pat.col_offset, 0, 0) + pat = or_node + # AS pattern + if _cur_kw(ps) == Keyword.As: + _advance(ps) + if _cur_type(ps) != TokenType.Name: + ps.error_count += 1 + return pat + name_str: str = _cur_str(ps) + _advance(ps) + as_node: rawptr[AST] = MatchAs(ps.pool, pat, name_str) + _set_pos(as_node, pat.lineno, pat.col_offset, 0, 0) + return as_node + return pat + + +def _parse_closed_pattern(ps: rawptr[Parser]) -> rawptr[AST]: + """closed_pattern: literal | capture | wildcard | value | group | sequence | mapping | class""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + ttype: int = _cur_type(ps) + val: rawptr[AST] + node: rawptr[AST] + tok: rawptr[Token] + sval: str + + # Number literal pattern + if ttype == TokenType.Number: + tok = ps.cur + _advance(ps) + if tok.is_float: + val = _new_const_float(ps.pool, tok.float_val, lineno, col) + else: + val = _new_const_int(ps.pool, tok.int_val, lineno, col) + node = MatchValue(ps.pool, val) + _set_pos(node, lineno, col, 0, 0) + return node + + # String literal pattern + if ttype == TokenType.String: + sval = _cur_str(ps) + is_dquote_m: int = ps.cur.kw_subtype & 16 + _advance(ps) + val = _new_const_str(ps.pool, sval, lineno, col, is_dquote_m) + node = MatchValue(ps.pool, val) + _set_pos(node, lineno, col, 0, 0) + return node + + # Negative number literal pattern: -1, -1.5 + if _match_op(ps, TokOp.Minus): + _advance(ps) + if _cur_type(ps) == TokenType.Number: + tok = ps.cur + _advance(ps) + if tok.is_float: + val = _new_const_float(ps.pool, -tok.float_val, lineno, col) + else: + val = _new_const_int(ps.pool, -tok.int_val, lineno, col) + node = MatchValue(ps.pool, val) + _set_pos(node, lineno, col, 0, 0) + return node + ps.error_count += 1 + return None + + # Name: None/True/False/_ /capture/value/class + if ttype == TokenType.Name: + kw: int = _cur_kw(ps) + # None/True/False → MatchSingleton + if kw == Keyword.None_ or kw == Keyword.True_ or kw == Keyword.False_: + _advance(ps) + if kw == Keyword.None_: + val = _new_const_none(ps.pool, lineno, col) + elif kw == Keyword.True_: + val = _new_const_bool(ps.pool, 1, lineno, col) + else: + val = _new_const_bool(ps.pool, 0, lineno, col) + node = MatchSingleton(ps.pool, val) + _set_pos(node, lineno, col, 0, 0) + return node + name_str: str = _cur_str(ps) + # Wildcard pattern: _ + if string.strcmp(name_str, "_") == 0: + _advance(ps) + node = MatchAs(ps.pool, None, None) + _set_pos(node, lineno, col, 0, 0) + return node + _advance(ps) + # Check dotted name(value pattern 或 class pattern) + if _match_op(ps, TokOp.Dot): + name_node: rawptr[Name] = Name(ps.pool, name_str, ASTCtx.Load) + _set_pos(name_node, lineno, col, 0, 0) + base: rawptr[AST] = _make_name_ast(ps.pool, name_node) + while _match_op(ps, TokOp.Dot): + _advance(ps) + if _cur_type(ps) != TokenType.Name: + ps.error_count += 1 + break + attr_name: str = _cur_str(ps) + attr_lineno: int = _cur_lineno(ps) + attr_col: int = _cur_col(ps) + _advance(ps) + base = Attribute(ps.pool, base, attr_name, ASTCtx.Load) + _set_pos(base, attr_lineno, attr_col, 0, 0) + # class pattern: dotted.name(...) + if _match_op(ps, TokOp.LPar): + return _parse_class_pattern(ps, base, lineno, col) + # value pattern: dotted.name + node = MatchValue(ps.pool, base) + _set_pos(node, lineno, col, 0, 0) + return node + # class pattern: Name(...) + if _match_op(ps, TokOp.LPar): + cls_node: rawptr[Name] = Name(ps.pool, name_str, ASTCtx.Load) + _set_pos(cls_node, lineno, col, 0, 0) + cls: rawptr[AST] = _make_name_ast(ps.pool, cls_node) + return _parse_class_pattern(ps, cls, lineno, col) + # capture pattern: NAME + node = MatchAs(ps.pool, None, name_str) + _set_pos(node, lineno, col, 0, 0) + return node + + # group pattern / sequence pattern (tuple): (...) + if _match_op(ps, TokOp.LPar): + _advance(ps) + inner: rawptr[AST] = _parse_pattern(ps) + if _accept_op(ps, TokOp.Comma): + # sequence pattern (tuple) + patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + if inner is not None: + patterns.append(inner) + while not _match_op(ps, TokOp.RPar) and _cur_type(ps) != TokenType.EndMarker: + p: rawptr[AST] = _parse_pattern(ps) + if p is not None: + patterns.append(p) + if not _accept_op(ps, TokOp.Comma): + break + _expect_op(ps, TokOp.RPar) + node = MatchSequence(ps.pool, patterns) + _set_pos(node, lineno, col, 0, 0) + return node + _expect_op(ps, TokOp.RPar) + return inner # group pattern + + # sequence pattern (list): [...] + if _match_op(ps, TokOp.Lsqb): + return _parse_sequence_pattern(ps, TokOp.Rsqb, lineno, col) + + # mapping pattern: {...} + if _match_op(ps, TokOp.LBrace): + return _parse_mapping_pattern(ps, lineno, col) + + ps.error_count += 1 + return None + + +def _parse_sequence_pattern(ps: rawptr[Parser], close_op: int, + lineno: int, col: int) -> rawptr[AST]: + """sequence_pattern: '[' [pattern_list] ']'""" + patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + _advance(ps) # [ + while not _match_op(ps, close_op) and _cur_type(ps) != TokenType.EndMarker: + # *name (star pattern) + if _match_op(ps, TokOp.Star): + _advance(ps) + if _cur_type(ps) == TokenType.Name: + star_name: str = _cur_str(ps) + _advance(ps) + star_node: rawptr[AST] + if string.strcmp(star_name, "_") == 0: + star_node = MatchStar(ps.pool, None) + else: + star_node = MatchStar(ps.pool, star_name) + _set_pos(star_node, lineno, col, 0, 0) + patterns.append(star_node) + else: + p: rawptr[AST] = _parse_pattern(ps) + if p is not None: + patterns.append(p) + if not _accept_op(ps, TokOp.Comma): + break + _expect_op(ps, close_op) + node: rawptr[AST] = MatchSequence(ps.pool, patterns) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_mapping_pattern(ps: rawptr[Parser], + lineno: int, col: int) -> rawptr[AST]: + """mapping_pattern: '{' [items] '}' + items: key ':' pattern (',' key ':' pattern)* [',' '**' NAME] | '**' NAME""" + keys: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + rest: str = None + node: rawptr[AST] + key: rawptr[AST] + val_pat: rawptr[AST] + _advance(ps) # { + if _match_op(ps, TokOp.RBrace): + _advance(ps) + node = MatchMapping(ps.pool, keys, patterns, rest) + _set_pos(node, lineno, col, 0, 0) + return node + while _cur_type(ps) != TokenType.EndMarker: + if _match_op(ps, TokOp.StarStar): + _advance(ps) + if _cur_type(ps) == TokenType.Name: + rest = _cur_str(ps) + _advance(ps) + break + key = _parse_expr(ps) + _expect_op(ps, TokOp.Colon) + val_pat = _parse_pattern(ps) + if key is not None: + keys.append(key) + if val_pat is not None: + patterns.append(val_pat) + if not _accept_op(ps, TokOp.Comma): + break + _expect_op(ps, TokOp.RBrace) + node = MatchMapping(ps.pool, keys, patterns, rest) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_class_pattern(ps: rawptr[Parser], cls: rawptr[AST], + lineno: int, col: int) -> rawptr[AST]: + """class_pattern: cls '(' [pattern_list] ')' + pattern_list: positional_pattern (',' positional_pattern)* + [',' keyword_pattern]* | keyword_pattern (',' keyword_pattern)* + keyword_pattern: NAME '=' pattern""" + patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + kwd_attrs: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + kwd_patterns: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + p: rawptr[AST] + _advance(ps) # ( + if not _match_op(ps, TokOp.RPar): + while _cur_type(ps) != TokenType.EndMarker: + # keyword pattern: NAME '=' pattern + if _cur_type(ps) == TokenType.Name and ps.cur is not None: + nxt: rawptr[Token] = ps.cur.next + if nxt is not None and nxt.type == TokenType.Op and nxt.op_subtype == TokOp.Equal: + attr_name: str = _cur_str(ps) + _advance(ps) # NAME + _advance(ps) # = + kwd_pat: rawptr[AST] = _parse_pattern(ps) + attr_n: rawptr[Name] = Name(ps.pool, attr_name, ASTCtx.Load) + _set_pos(attr_n, lineno, col, 0, 0) + attr_node: rawptr[AST] = _make_name_ast(ps.pool, attr_n) + kwd_attrs.append(attr_node) + if kwd_pat is not None: + kwd_patterns.append(kwd_pat) + else: + p = _parse_pattern(ps) + if p is not None: + patterns.append(p) + else: + p = _parse_pattern(ps) + if p is not None: + patterns.append(p) + if not _accept_op(ps, TokOp.Comma): + break + _expect_op(ps, TokOp.RPar) + node: rawptr[AST] = MatchClass(ps.pool, cls, patterns, kwd_attrs, kwd_patterns) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_match_case(ps: rawptr[Parser]) -> rawptr[AST]: + """case pattern [if guard] ':' suite""" + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # case + guard: rawptr[AST] = None + pattern: rawptr[AST] = _parse_pattern(ps) + if _cur_kw(ps) == Keyword.If: + _advance(ps) + guard = _parse_or_test(ps) + _expect_op(ps, TokOp.Colon) + node: rawptr[AST] = MatchCase(ps.pool, pattern, guard) + _parse_suite_into_children(ps, node) + _set_pos(node, lineno, col, 0, 0) + return node + + +def _parse_decorators(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]: + """解析装饰器链,返回 list。""" + decs: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8) + while _match_op(ps, TokOp.At): + lineno: int = _cur_lineno(ps) + col: int = _cur_col(ps) + _advance(ps) # @ + expr: rawptr[AST] = _parse_namedexpr(ps) + _skip_nl(ps) + if _cur_type(ps) == TokenType.NewLine: + _advance(ps) + decs.append(expr) + return decs + + +# ============================================================ +# Module entry +# ============================================================ + +def _parse_module_body(ps: rawptr[Parser], module_node: rawptr[AST]): + """解析模块体,append 到 module_node。""" + _skip_nl(ps) + stmt_idx: int = 0 + while _cur_type(ps) != TokenType.EndMarker: + stmt_idx += 1 + # Decorator + if _match_op(ps, TokOp.At): + ps.pending_decorators = _parse_decorators(ps) + # Record current token pointer to check for infinite loop + before_cur: rawptr[Token] = ps.cur + _parse_statement_into(ps, module_node) + # Defense: If parsing doesn't advance the token (like when _parse_atom encounters an unrecognized token and returns None), + # force it to move forward to avoid an infinite loop in the main loop; accumulate error_count for the caller to diagnose + if ps.cur is before_cur: + ps.error_count += 1 + _advance(ps) + _skip_nl(ps) + + +def parse_tokens(pool: rawptr[memhub.MemManager], + tokens: rawptr[Token]) -> rawptr[AST]: + """Parse an AST Module from a token list.""" + if pool is None or tokens is None: + return None + global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg + _g_parse_err_line = 0 + _g_parse_err_col = 0 + _g_parse_err_msg = None + ps: rawptr[Parser] = new_parser(pool) + if ps is None: return None + _parser_init(ps, tokens, pool) + module_node: rawptr[Module] = Module(pool) + _parse_module_body(ps, module_node) + # fail-fast: 解析出错(error_count > 0)时返回 None,由调用方(TPV 等)报错终止, + # 避免错误代码静默编译(如 `func12344444 avg3(...)` 缺 def 关键字的函数定义残留)。 + if ps.error_count > 0: + return None + return module_node diff --git a/ast/stmts.vp b/ast/stmts.vp index 4b3d74d..1209c6f 100644 --- a/ast/stmts.vp +++ b/ast/stmts.vp @@ -1,1157 +1,1157 @@ -import t, c -from stdint import * -import memhub -import string -from .base import ( - AST, Stmt, ASTKind, ASTCtx, OpKind, - _init_ast, _copy_str, _set_parent_list, - _emit, _emit_str, _emit_int, _dump_list, - FLAG_IS_ASYNC, _op_name -) - - -# ============================================================ -# Module Node -# ============================================================ - -@t.CVTable -class Module(AST): - """Module(children: list[rowptr[AST]])""" - - def __new__(self, pool: rowptr[memhub.MemManager]): - ptr: rowptr[Module] = pool.alloc(Module.__sizeof__()) - if ptr: - string.memset(ptr, 0, Module.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager]): - _init_ast(self, pool, 0, 0) - - def kind(self) -> int: - return ASTKind.Module - - def type_name(self) -> str: - return "Module" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Module(body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Assign Node -# ============================================================ - -class Assign(Stmt): - """Assign(targets: list[rowptr[AST]], value: AST)""" - targets: rowptr[list[rowptr[AST]]] - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - targets: rowptr[list[rowptr[AST]]], value: rowptr[AST]): - ptr: rowptr[Assign] = pool.alloc(Assign.__sizeof__()) - if ptr: - string.memset(ptr, 0, Assign.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - targets: rowptr[list[rowptr[AST]]], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.targets = targets - self.value = value - _set_parent_list(targets, self) - - def kind(self) -> int: - return ASTKind.Assign - - def type_name(self) -> str: - return "Assign" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Assign(targets=") - pos = _dump_list(self.targets, buf, size, pos) - pos = _emit(buf, size, pos, ", value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# If Node -# ============================================================ - -class If(Stmt): - """If(test: AST, orelse: list[rowptr[AST]])""" - test: rowptr[AST] - orelse: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - test: rowptr[AST], - orelse: rowptr[list[rowptr[AST]]]): - ptr: rowptr[If] = pool.alloc(If.__sizeof__()) - if ptr: - string.memset(ptr, 0, If.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - test: rowptr[AST], - orelse: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.test = test - self.orelse = orelse - _set_parent_list(orelse, self) - - def kind(self) -> int: - return ASTKind.If - - def type_name(self) -> str: - return "If" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "If(test=") - if self.test is not None: - pos = self.test.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ", orelse=") - pos = _dump_list(self.orelse, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# For Node -# ============================================================ - -class For(Stmt): - """For(target: AST, iter: AST, orelse: list[rowptr[AST]], is_async: CInt)""" - target: rowptr[AST] - iter: rowptr[AST] - orelse: rowptr[list[rowptr[AST]]] - flags: int - - def __new__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], iter: rowptr[AST], - orelse: rowptr[list[rowptr[AST]]], - is_async: int): - ptr: rowptr[For] = pool.alloc(For.__sizeof__()) - if ptr: - string.memset(ptr, 0, For.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], iter: rowptr[AST], - orelse: rowptr[list[rowptr[AST]]], - is_async: int): - _init_ast(self, pool, 0, 0) - self.target = target - self.iter = iter - self.orelse = orelse - if is_async: - self.flags = FLAG_IS_ASYNC - else: - self.flags = 0 - _set_parent_list(orelse, self) - - def kind(self) -> int: - return ASTKind.For - - def type_name(self) -> str: - return "For" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "For(target=") - if self.target is not None: - pos = self.target.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", iter=") - if self.iter is not None: - pos = self.iter.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# While Node -# ============================================================ - -class While(Stmt): - """While(test: AST, orelse: list[rowptr[AST]])""" - test: rowptr[AST] - orelse: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - test: rowptr[AST], - orelse: rowptr[list[rowptr[AST]]]): - ptr: rowptr[While] = pool.alloc(While.__sizeof__()) - if ptr: - string.memset(ptr, 0, While.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - test: rowptr[AST], - orelse: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.test = test - self.orelse = orelse - _set_parent_list(orelse, self) - - def kind(self) -> int: - return ASTKind.While - - def type_name(self) -> str: - return "While" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "While(test=") - if self.test is not None: - pos = self.test.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Return Node -# ============================================================ - -class Return(Stmt): - """Return(value: AST | None)""" - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - ptr: rowptr[Return] = pool.alloc(Return.__sizeof__()) - if ptr: - string.memset(ptr, 0, Return.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.value = value - - def kind(self) -> int: - return ASTKind.Return - - def type_name(self) -> str: - return "Return" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Return(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Expr Node (expression statement) -# ============================================================ - -class Expr(Stmt): - """Expr(value: AST)""" - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - ptr: rowptr[Expr] = pool.alloc(Expr.__sizeof__()) - if ptr: - string.memset(ptr, 0, Expr.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.value = value - - def kind(self) -> int: - return ASTKind.Expr - - def type_name(self) -> str: - return "Expr" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Expr(value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# FunctionDef Node -# ============================================================ - -class FunctionDef(Stmt): - """FunctionDef(name: str, args: AST, - decorator_list: list[rowptr[AST]], returns: AST, flags: CInt, - type_params: rowptr[list[str]] = None) - - type_params: 泛型函数类型参数名列表(PEP 695 def f[T](...): 中的 [T]), - None 表示非泛型函数。""" - name: str - args: rowptr[AST] - decorator_list: rowptr[list[rowptr[AST]]] - returns: rowptr[AST] - flags: int - type_params: rowptr[list[str]] - - def __new__(self, pool: rowptr[memhub.MemManager], - name: str, args: rowptr[AST], - decorator_list: rowptr[list[rowptr[AST]]], - returns: rowptr[AST], is_async: int): - ptr: rowptr[FunctionDef] = pool.alloc(FunctionDef.__sizeof__()) - if ptr: - string.memset(ptr, 0, FunctionDef.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - name: str, args: rowptr[AST], - decorator_list: rowptr[list[rowptr[AST]]], - returns: rowptr[AST], is_async: int, - type_params: rowptr[list[str]] = None): - _init_ast(self, pool, 0, 0) - self.name = _copy_str(pool, name) - self.args = args - self.decorator_list = decorator_list - self.returns = returns - self.type_params = type_params - if is_async: - self.flags = FLAG_IS_ASYNC - else: - self.flags = 0 - _set_parent_list(decorator_list, self) - - def kind(self) -> int: - return ASTKind.FunctionDef - - def type_name(self) -> str: - return "FunctionDef" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "FunctionDef(name='") - pos = _emit_str(buf, size, pos, self.name) - pos = _emit(buf, size, pos, "', args=") - if self.args is not None: - pos = self.args.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ", decorator_list=") - pos = _dump_list(self.decorator_list, buf, size, pos) - pos = _emit(buf, size, pos, ", returns=") - if self.returns is not None: - pos = self.returns.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Expression Node -# ============================================================ - -class Expression(AST): - """Expression(body: AST)""" - body: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - body: rowptr[AST]): - ptr: rowptr[Expression] = pool.alloc(Expression.__sizeof__()) - if ptr: - string.memset(ptr, 0, Expression.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - body: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.body = body - - def kind(self) -> int: - return ASTKind.Expression - - def type_name(self) -> str: - return "Expression" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Expression(body=") - if self.body is not None: - pos = self.body.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Interactive Node -# ============================================================ - -class Interactive(AST): - """Interactive(body: AST)""" - body: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - body: rowptr[AST]): - ptr: rowptr[Interactive] = pool.alloc(Interactive.__sizeof__()) - if ptr: - string.memset(ptr, 0, Interactive.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - body: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.body = body - - def kind(self) -> int: - return ASTKind.Interactive - - def type_name(self) -> str: - return "Interactive" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Interactive(body=") - if self.body is not None: - pos = self.body.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# FunctionType Node -# ============================================================ - -class FunctionType(AST): - """FunctionType(argtypes: list[rowptr[AST]], returns: AST)""" - argtypes: rowptr[list[rowptr[AST]]] - returns: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - argtypes: rowptr[list[rowptr[AST]]], - returns: rowptr[AST]): - ptr: rowptr[FunctionType] = pool.alloc(FunctionType.__sizeof__()) - if ptr: - string.memset(ptr, 0, FunctionType.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - argtypes: rowptr[list[rowptr[AST]]], - returns: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.argtypes = argtypes - self.returns = returns - - def kind(self) -> int: - return ASTKind.FunctionType - - def type_name(self) -> str: - return "FunctionType" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "FunctionType(argtypes=") - pos = _dump_list(self.argtypes, buf, size, pos) - pos = _emit(buf, size, pos, ", returns=") - if self.returns is not None: - pos = self.returns.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# ClassDef Node -# ============================================================ - -class ClassDef(Stmt): - """ClassDef(name: str, bases: list[rowptr[AST]], keywords: list[rowptr[AST]], - decorator_list: list[rowptr[AST]], type_params: rowptr[list[str]]) - - type_params: 泛型类型参数名列表(PEP 695 class list[T]: 中的 [T]), - None 表示非泛型类。""" - name: str - bases: rowptr[list[rowptr[AST]]] - keywords: rowptr[list[rowptr[AST]]] - decorator_list: rowptr[list[rowptr[AST]]] - type_params: rowptr[list[str]] - - def __new__(self, pool: rowptr[memhub.MemManager], - name: str, - bases: rowptr[list[rowptr[AST]]], - keywords: rowptr[list[rowptr[AST]]], - decorator_list: rowptr[list[rowptr[AST]]], - type_params: rowptr[list[str]] = None): - ptr: rowptr[ClassDef] = pool.alloc(ClassDef.__sizeof__()) - if ptr: - string.memset(ptr, 0, ClassDef.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - name: str, - bases: rowptr[list[rowptr[AST]]], - keywords: rowptr[list[rowptr[AST]]], - decorator_list: rowptr[list[rowptr[AST]]], - type_params: rowptr[list[str]] = None): - _init_ast(self, pool, 0, 0) - self.name = _copy_str(pool, name) - self.bases = bases - self.keywords = keywords - self.decorator_list = decorator_list - self.type_params = type_params - _set_parent_list(decorator_list, self) - - def kind(self) -> int: - return ASTKind.ClassDef - - def type_name(self) -> str: - return "ClassDef" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "ClassDef(name='") - pos = _emit_str(buf, size, pos, self.name) - pos = _emit(buf, size, pos, "', bases=") - pos = _dump_list(self.bases, buf, size, pos) - pos = _emit(buf, size, pos, ", keywords=") - pos = _dump_list(self.keywords, buf, size, pos) - pos = _emit(buf, size, pos, ", body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ", decorator_list=") - pos = _dump_list(self.decorator_list, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Delete Node -# ============================================================ - -class Delete(Stmt): - """Delete(targets: list[rowptr[AST]])""" - targets: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - targets: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Delete] = pool.alloc(Delete.__sizeof__()) - if ptr: - string.memset(ptr, 0, Delete.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - targets: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.targets = targets - _set_parent_list(targets, self) - - def kind(self) -> int: - return ASTKind.Delete - - def type_name(self) -> str: - return "Delete" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Delete(targets=") - pos = _dump_list(self.targets, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# AugAssign Node -# ============================================================ - -class AugAssign(Stmt): - """AugAssign(target: AST, op: CInt, value: AST)""" - target: rowptr[AST] - op: int - value: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], op: int, value: rowptr[AST]): - ptr: rowptr[AugAssign] = pool.alloc(AugAssign.__sizeof__()) - if ptr: - string.memset(ptr, 0, AugAssign.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], op: int, value: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.target = target - self.op = op - self.value = value - - def kind(self) -> int: - return ASTKind.AugAssign - - def type_name(self) -> str: - return "AugAssign" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "AugAssign(target=") - if self.target is not None: - pos = self.target.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", op=") - pos = _emit(buf, size, pos, _op_name(self.op)) - pos = _emit(buf, size, pos, ", value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# AnnAssign Node -# ============================================================ - -class AnnAssign(Stmt): - """AnnAssign(target: AST, annotation: AST, value: AST, simple: CInt)""" - target: rowptr[AST] - annotation: rowptr[AST] - value: rowptr[AST] - simple: int - - def __new__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], annotation: rowptr[AST], - value: rowptr[AST], simple: int): - ptr: rowptr[AnnAssign] = pool.alloc(AnnAssign.__sizeof__()) - if ptr: - string.memset(ptr, 0, AnnAssign.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - target: rowptr[AST], annotation: rowptr[AST], - value: rowptr[AST], simple: int): - _init_ast(self, pool, 0, 0) - self.target = target - self.annotation = annotation - self.value = value - self.simple = simple - - def kind(self) -> int: - return ASTKind.AnnAssign - - def type_name(self) -> str: - return "AnnAssign" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "AnnAssign(target=") - if self.target is not None: - pos = self.target.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", annotation=") - if self.annotation is not None: - pos = self.annotation.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", value=") - if self.value is not None: - pos = self.value.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", simple=") - pos = _emit_int(buf, size, pos, int64(self.simple)) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# With Node -# ============================================================ - -class With(Stmt): - """With(items: list[rowptr[AST]], is_async: CInt)""" - items: rowptr[list[rowptr[AST]]] - flags: int - - def __new__(self, pool: rowptr[memhub.MemManager], - items: rowptr[list[rowptr[AST]]], - is_async: int): - ptr: rowptr[With] = pool.alloc(With.__sizeof__()) - if ptr: - string.memset(ptr, 0, With.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - items: rowptr[list[rowptr[AST]]], - is_async: int): - _init_ast(self, pool, 0, 0) - self.items = items - if is_async: - self.flags = FLAG_IS_ASYNC - else: - self.flags = 0 - _set_parent_list(items, self) - - def kind(self) -> int: - return ASTKind.With - - def type_name(self) -> str: - return "With" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "With(items=") - pos = _dump_list(self.items, buf, size, pos) - pos = _emit(buf, size, pos, ", body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Raise Node -# ============================================================ - -class Raise(Stmt): - """Raise(exc: AST, cause: AST)""" - exc: rowptr[AST] - cause: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - exc: rowptr[AST], cause: rowptr[AST]): - ptr: rowptr[Raise] = pool.alloc(Raise.__sizeof__()) - if ptr: - string.memset(ptr, 0, Raise.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - exc: rowptr[AST], cause: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.exc = exc - self.cause = cause - - def kind(self) -> int: - return ASTKind.Raise - - def type_name(self) -> str: - return "Raise" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Raise(exc=") - if self.exc is not None: - pos = self.exc.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", cause=") - if self.cause is not None: - pos = self.cause.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Try Node -# ============================================================ - -class Try(Stmt): - """Try(handlers: list[rowptr[AST]], - orelse: list[rowptr[AST]], finalbody: list[rowptr[AST]])""" - handlers: rowptr[list[rowptr[AST]]] - orelse: rowptr[list[rowptr[AST]]] - finalbody: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - handlers: rowptr[list[rowptr[AST]]], - orelse: rowptr[list[rowptr[AST]]], - finalbody: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Try] = pool.alloc(Try.__sizeof__()) - if ptr: - string.memset(ptr, 0, Try.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - handlers: rowptr[list[rowptr[AST]]], - orelse: rowptr[list[rowptr[AST]]], - finalbody: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.handlers = handlers - self.orelse = orelse - self.finalbody = finalbody - _set_parent_list(handlers, self) - _set_parent_list(orelse, self) - _set_parent_list(finalbody, self) - - def kind(self) -> int: - return ASTKind.Try - - def type_name(self) -> str: - return "Try" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Try(body=") - pos = _dump_list(self.children, buf, size, pos) - pos = _emit(buf, size, pos, ", handlers=") - pos = _dump_list(self.handlers, buf, size, pos) - pos = _emit(buf, size, pos, ", orelse=") - pos = _dump_list(self.orelse, buf, size, pos) - pos = _emit(buf, size, pos, ", finalbody=") - pos = _dump_list(self.finalbody, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Assert Node -# ============================================================ - -class Assert(Stmt): - """Assert(test: AST, msg: AST)""" - test: rowptr[AST] - msg: rowptr[AST] - - def __new__(self, pool: rowptr[memhub.MemManager], - test: rowptr[AST], msg: rowptr[AST]): - ptr: rowptr[Assert] = pool.alloc(Assert.__sizeof__()) - if ptr: - string.memset(ptr, 0, Assert.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - test: rowptr[AST], msg: rowptr[AST]): - _init_ast(self, pool, 0, 0) - self.test = test - self.msg = msg - - def kind(self) -> int: - return ASTKind.Assert - - def type_name(self) -> str: - return "Assert" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Assert(test=") - if self.test is not None: - pos = self.test.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", msg=") - if self.msg is not None: - pos = self.msg.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Global Node -# ============================================================ - -class Global(Stmt): - """Global(names: list[rowptr[AST]])""" - names: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - names: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Global] = pool.alloc(Global.__sizeof__()) - if ptr: - string.memset(ptr, 0, Global.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - names: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.names = names - _set_parent_list(names, self) - - def kind(self) -> int: - return ASTKind.Global - - def type_name(self) -> str: - return "Global" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Global(names=") - pos = _dump_list(self.names, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Nonlocal Node -# ============================================================ - -class Nonlocal(Stmt): - """Nonlocal(names: list[rowptr[AST]])""" - names: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - names: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Nonlocal] = pool.alloc(Nonlocal.__sizeof__()) - if ptr: - string.memset(ptr, 0, Nonlocal.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - names: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.names = names - _set_parent_list(names, self) - - def kind(self) -> int: - return ASTKind.Nonlocal - - def type_name(self) -> str: - return "Nonlocal" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Nonlocal(names=") - pos = _dump_list(self.names, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Pass Node -# ============================================================ - -class Pass(Stmt): - """Pass()""" - - def __new__(self, pool: rowptr[memhub.MemManager]): - ptr: rowptr[Pass] = pool.alloc(Pass.__sizeof__()) - if ptr: - string.memset(ptr, 0, Pass.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager]): - _init_ast(self, pool, 0, 0) - - def kind(self) -> int: - return ASTKind.Pass - - def type_name(self) -> str: - return "Pass" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Pass()") - return pos - - -# ============================================================ -# Break Node -# ============================================================ - -class Break(Stmt): - """Break()""" - - def __new__(self, pool: rowptr[memhub.MemManager]): - ptr: rowptr[Break] = pool.alloc(Break.__sizeof__()) - if ptr: - string.memset(ptr, 0, Break.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager]): - _init_ast(self, pool, 0, 0) - - def kind(self) -> int: - return ASTKind.Break - - def type_name(self) -> str: - return "Break" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Break()") - return pos - - -# ============================================================ -# Continue Node -# ============================================================ - -class Continue(Stmt): - """Continue()""" - - def __new__(self, pool: rowptr[memhub.MemManager]): - ptr: rowptr[Continue] = pool.alloc(Continue.__sizeof__()) - if ptr: - string.memset(ptr, 0, Continue.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager]): - _init_ast(self, pool, 0, 0) - - def kind(self) -> int: - return ASTKind.Continue - - def type_name(self) -> str: - return "Continue" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Continue()") - return pos - - -# ============================================================ -# Import Node -# ============================================================ - -class Import(Stmt): - """Import(names: list[rowptr[AST]])""" - names: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - names: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Import] = pool.alloc(Import.__sizeof__()) - if ptr: - string.memset(ptr, 0, Import.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - names: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.names = names - _set_parent_list(names, self) - - def kind(self) -> int: - return ASTKind.Import - - def type_name(self) -> str: - return "Import" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Import(names=") - pos = _dump_list(self.names, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# ImportFrom Node -# ============================================================ - -class ImportFrom(Stmt): - """ImportFrom(module: str, names: list[rowptr[AST]], level: CInt)""" - module: str - names: rowptr[list[rowptr[AST]]] - level: int - - def __new__(self, pool: rowptr[memhub.MemManager], - module: str, - names: rowptr[list[rowptr[AST]]], - level: int): - ptr: rowptr[ImportFrom] = pool.alloc(ImportFrom.__sizeof__()) - if ptr: - string.memset(ptr, 0, ImportFrom.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - module: str, - names: rowptr[list[rowptr[AST]]], - level: int): - _init_ast(self, pool, 0, 0) - self.module = _copy_str(pool, module) - self.names = names - self.level = level - _set_parent_list(names, self) - - def kind(self) -> int: - return ASTKind.ImportFrom - - def type_name(self) -> str: - return "ImportFrom" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "ImportFrom(module='") - pos = _emit_str(buf, size, pos, self.module) - pos = _emit(buf, size, pos, "', names=") - pos = _dump_list(self.names, buf, size, pos) - pos = _emit(buf, size, pos, ", level=") - pos = _emit_int(buf, size, pos, int64(self.level)) - pos = _emit(buf, size, pos, ")") - return pos - - -# ============================================================ -# Match Node -# ============================================================ - -class Match(Stmt): - """Match(subject: AST, cases: list[rowptr[AST]])""" - subject: rowptr[AST] - cases: rowptr[list[rowptr[AST]]] - - def __new__(self, pool: rowptr[memhub.MemManager], - subject: rowptr[AST], - cases: rowptr[list[rowptr[AST]]]): - ptr: rowptr[Match] = pool.alloc(Match.__sizeof__()) - if ptr: - string.memset(ptr, 0, Match.__sizeof__()) - return ptr - - def __init__(self, pool: rowptr[memhub.MemManager], - subject: rowptr[AST], - cases: rowptr[list[rowptr[AST]]]): - _init_ast(self, pool, 0, 0) - self.subject = subject - self.cases = cases - _set_parent_list(cases, self) - - def kind(self) -> int: - return ASTKind.Match - - def type_name(self) -> str: - return "Match" - - def dump(self, buf: str, size: t.CSizeT, - pos: t.CSizeT) -> t.CSizeT: - pos = _emit(buf, size, pos, "Match(subject=") - if self.subject is not None: - pos = self.subject.dump(buf, size, pos) - else: - pos = _emit(buf, size, pos, "None") - pos = _emit(buf, size, pos, ", cases=") - pos = _dump_list(self.cases, buf, size, pos) - pos = _emit(buf, size, pos, ")") - return pos +import t, c +from stdint import * +import memhub +import string +from .base import ( + AST, Stmt, ASTKind, ASTCtx, OpKind, + _init_ast, _copy_str, _set_parent_list, + _emit, _emit_str, _emit_int, _dump_list, + FLAG_IS_ASYNC, _op_name +) + + +# ============================================================ +# Module Node +# ============================================================ + +@t.CVTable +class Module(AST): + """Module(children: list[rawptr[AST]])""" + + def __new__(self, pool: rawptr[memhub.MemManager]): + ptr: rawptr[Module] = pool.alloc(Module.__sizeof__()) + if ptr: + string.memset(ptr, 0, Module.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager]): + _init_ast(self, pool, 0, 0) + + def kind(self) -> int: + return ASTKind.Module + + def type_name(self) -> str: + return "Module" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Module(body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Assign Node +# ============================================================ + +class Assign(Stmt): + """Assign(targets: list[rawptr[AST]], value: AST)""" + targets: rawptr[list[rawptr[AST]]] + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + targets: rawptr[list[rawptr[AST]]], value: rawptr[AST]): + ptr: rawptr[Assign] = pool.alloc(Assign.__sizeof__()) + if ptr: + string.memset(ptr, 0, Assign.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + targets: rawptr[list[rawptr[AST]]], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.targets = targets + self.value = value + _set_parent_list(targets, self) + + def kind(self) -> int: + return ASTKind.Assign + + def type_name(self) -> str: + return "Assign" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Assign(targets=") + pos = _dump_list(self.targets, buf, size, pos) + pos = _emit(buf, size, pos, ", value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# If Node +# ============================================================ + +class If(Stmt): + """If(test: AST, orelse: list[rawptr[AST]])""" + test: rawptr[AST] + orelse: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + test: rawptr[AST], + orelse: rawptr[list[rawptr[AST]]]): + ptr: rawptr[If] = pool.alloc(If.__sizeof__()) + if ptr: + string.memset(ptr, 0, If.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + test: rawptr[AST], + orelse: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.test = test + self.orelse = orelse + _set_parent_list(orelse, self) + + def kind(self) -> int: + return ASTKind.If + + def type_name(self) -> str: + return "If" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "If(test=") + if self.test is not None: + pos = self.test.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ", orelse=") + pos = _dump_list(self.orelse, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# For Node +# ============================================================ + +class For(Stmt): + """For(target: AST, iter: AST, orelse: list[rawptr[AST]], is_async: CInt)""" + target: rawptr[AST] + iter: rawptr[AST] + orelse: rawptr[list[rawptr[AST]]] + flags: int + + def __new__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], iter: rawptr[AST], + orelse: rawptr[list[rawptr[AST]]], + is_async: int): + ptr: rawptr[For] = pool.alloc(For.__sizeof__()) + if ptr: + string.memset(ptr, 0, For.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], iter: rawptr[AST], + orelse: rawptr[list[rawptr[AST]]], + is_async: int): + _init_ast(self, pool, 0, 0) + self.target = target + self.iter = iter + self.orelse = orelse + if is_async: + self.flags = FLAG_IS_ASYNC + else: + self.flags = 0 + _set_parent_list(orelse, self) + + def kind(self) -> int: + return ASTKind.For + + def type_name(self) -> str: + return "For" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "For(target=") + if self.target is not None: + pos = self.target.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", iter=") + if self.iter is not None: + pos = self.iter.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# While Node +# ============================================================ + +class While(Stmt): + """While(test: AST, orelse: list[rawptr[AST]])""" + test: rawptr[AST] + orelse: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + test: rawptr[AST], + orelse: rawptr[list[rawptr[AST]]]): + ptr: rawptr[While] = pool.alloc(While.__sizeof__()) + if ptr: + string.memset(ptr, 0, While.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + test: rawptr[AST], + orelse: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.test = test + self.orelse = orelse + _set_parent_list(orelse, self) + + def kind(self) -> int: + return ASTKind.While + + def type_name(self) -> str: + return "While" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "While(test=") + if self.test is not None: + pos = self.test.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Return Node +# ============================================================ + +class Return(Stmt): + """Return(value: AST | None)""" + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + ptr: rawptr[Return] = pool.alloc(Return.__sizeof__()) + if ptr: + string.memset(ptr, 0, Return.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.value = value + + def kind(self) -> int: + return ASTKind.Return + + def type_name(self) -> str: + return "Return" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Return(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Expr Node (expression statement) +# ============================================================ + +class Expr(Stmt): + """Expr(value: AST)""" + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + ptr: rawptr[Expr] = pool.alloc(Expr.__sizeof__()) + if ptr: + string.memset(ptr, 0, Expr.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.value = value + + def kind(self) -> int: + return ASTKind.Expr + + def type_name(self) -> str: + return "Expr" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Expr(value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# FunctionDef Node +# ============================================================ + +class FunctionDef(Stmt): + """FunctionDef(name: str, args: AST, + decorator_list: list[rawptr[AST]], returns: AST, flags: CInt, + type_params: rawptr[list[str]] = None) + + type_params: 泛型函数类型参数名列表(PEP 695 def f[T](...): 中的 [T]), + None 表示非泛型函数。""" + name: str + args: rawptr[AST] + decorator_list: rawptr[list[rawptr[AST]]] + returns: rawptr[AST] + flags: int + type_params: rawptr[list[str]] + + def __new__(self, pool: rawptr[memhub.MemManager], + name: str, args: rawptr[AST], + decorator_list: rawptr[list[rawptr[AST]]], + returns: rawptr[AST], is_async: int): + ptr: rawptr[FunctionDef] = pool.alloc(FunctionDef.__sizeof__()) + if ptr: + string.memset(ptr, 0, FunctionDef.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + name: str, args: rawptr[AST], + decorator_list: rawptr[list[rawptr[AST]]], + returns: rawptr[AST], is_async: int, + type_params: rawptr[list[str]] = None): + _init_ast(self, pool, 0, 0) + self.name = _copy_str(pool, name) + self.args = args + self.decorator_list = decorator_list + self.returns = returns + self.type_params = type_params + if is_async: + self.flags = FLAG_IS_ASYNC + else: + self.flags = 0 + _set_parent_list(decorator_list, self) + + def kind(self) -> int: + return ASTKind.FunctionDef + + def type_name(self) -> str: + return "FunctionDef" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "FunctionDef(name='") + pos = _emit_str(buf, size, pos, self.name) + pos = _emit(buf, size, pos, "', args=") + if self.args is not None: + pos = self.args.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ", decorator_list=") + pos = _dump_list(self.decorator_list, buf, size, pos) + pos = _emit(buf, size, pos, ", returns=") + if self.returns is not None: + pos = self.returns.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Expression Node +# ============================================================ + +class Expression(AST): + """Expression(body: AST)""" + body: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + body: rawptr[AST]): + ptr: rawptr[Expression] = pool.alloc(Expression.__sizeof__()) + if ptr: + string.memset(ptr, 0, Expression.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + body: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.body = body + + def kind(self) -> int: + return ASTKind.Expression + + def type_name(self) -> str: + return "Expression" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Expression(body=") + if self.body is not None: + pos = self.body.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Interactive Node +# ============================================================ + +class Interactive(AST): + """Interactive(body: AST)""" + body: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + body: rawptr[AST]): + ptr: rawptr[Interactive] = pool.alloc(Interactive.__sizeof__()) + if ptr: + string.memset(ptr, 0, Interactive.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + body: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.body = body + + def kind(self) -> int: + return ASTKind.Interactive + + def type_name(self) -> str: + return "Interactive" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Interactive(body=") + if self.body is not None: + pos = self.body.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# FunctionType Node +# ============================================================ + +class FunctionType(AST): + """FunctionType(argtypes: list[rawptr[AST]], returns: AST)""" + argtypes: rawptr[list[rawptr[AST]]] + returns: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + argtypes: rawptr[list[rawptr[AST]]], + returns: rawptr[AST]): + ptr: rawptr[FunctionType] = pool.alloc(FunctionType.__sizeof__()) + if ptr: + string.memset(ptr, 0, FunctionType.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + argtypes: rawptr[list[rawptr[AST]]], + returns: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.argtypes = argtypes + self.returns = returns + + def kind(self) -> int: + return ASTKind.FunctionType + + def type_name(self) -> str: + return "FunctionType" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "FunctionType(argtypes=") + pos = _dump_list(self.argtypes, buf, size, pos) + pos = _emit(buf, size, pos, ", returns=") + if self.returns is not None: + pos = self.returns.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# ClassDef Node +# ============================================================ + +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 表示非泛型类。""" + name: str + bases: rawptr[list[rawptr[AST]]] + keywords: rawptr[list[rawptr[AST]]] + decorator_list: rawptr[list[rawptr[AST]]] + type_params: rawptr[list[str]] + + def __new__(self, pool: rawptr[memhub.MemManager], + name: str, + bases: rawptr[list[rawptr[AST]]], + keywords: rawptr[list[rawptr[AST]]], + decorator_list: rawptr[list[rawptr[AST]]], + type_params: rawptr[list[str]] = None): + ptr: rawptr[ClassDef] = pool.alloc(ClassDef.__sizeof__()) + if ptr: + string.memset(ptr, 0, ClassDef.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + name: str, + bases: rawptr[list[rawptr[AST]]], + keywords: rawptr[list[rawptr[AST]]], + decorator_list: rawptr[list[rawptr[AST]]], + type_params: rawptr[list[str]] = None): + _init_ast(self, pool, 0, 0) + self.name = _copy_str(pool, name) + self.bases = bases + self.keywords = keywords + self.decorator_list = decorator_list + self.type_params = type_params + _set_parent_list(decorator_list, self) + + def kind(self) -> int: + return ASTKind.ClassDef + + def type_name(self) -> str: + return "ClassDef" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "ClassDef(name='") + pos = _emit_str(buf, size, pos, self.name) + pos = _emit(buf, size, pos, "', bases=") + pos = _dump_list(self.bases, buf, size, pos) + pos = _emit(buf, size, pos, ", keywords=") + pos = _dump_list(self.keywords, buf, size, pos) + pos = _emit(buf, size, pos, ", body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ", decorator_list=") + pos = _dump_list(self.decorator_list, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Delete Node +# ============================================================ + +class Delete(Stmt): + """Delete(targets: list[rawptr[AST]])""" + targets: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + targets: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Delete] = pool.alloc(Delete.__sizeof__()) + if ptr: + string.memset(ptr, 0, Delete.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + targets: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.targets = targets + _set_parent_list(targets, self) + + def kind(self) -> int: + return ASTKind.Delete + + def type_name(self) -> str: + return "Delete" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Delete(targets=") + pos = _dump_list(self.targets, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# AugAssign Node +# ============================================================ + +class AugAssign(Stmt): + """AugAssign(target: AST, op: CInt, value: AST)""" + target: rawptr[AST] + op: int + value: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], op: int, value: rawptr[AST]): + ptr: rawptr[AugAssign] = pool.alloc(AugAssign.__sizeof__()) + if ptr: + string.memset(ptr, 0, AugAssign.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], op: int, value: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.target = target + self.op = op + self.value = value + + def kind(self) -> int: + return ASTKind.AugAssign + + def type_name(self) -> str: + return "AugAssign" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "AugAssign(target=") + if self.target is not None: + pos = self.target.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", op=") + pos = _emit(buf, size, pos, _op_name(self.op)) + pos = _emit(buf, size, pos, ", value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# AnnAssign Node +# ============================================================ + +class AnnAssign(Stmt): + """AnnAssign(target: AST, annotation: AST, value: AST, simple: CInt)""" + target: rawptr[AST] + annotation: rawptr[AST] + value: rawptr[AST] + simple: int + + def __new__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], annotation: rawptr[AST], + value: rawptr[AST], simple: int): + ptr: rawptr[AnnAssign] = pool.alloc(AnnAssign.__sizeof__()) + if ptr: + string.memset(ptr, 0, AnnAssign.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + target: rawptr[AST], annotation: rawptr[AST], + value: rawptr[AST], simple: int): + _init_ast(self, pool, 0, 0) + self.target = target + self.annotation = annotation + self.value = value + self.simple = simple + + def kind(self) -> int: + return ASTKind.AnnAssign + + def type_name(self) -> str: + return "AnnAssign" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "AnnAssign(target=") + if self.target is not None: + pos = self.target.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", annotation=") + if self.annotation is not None: + pos = self.annotation.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", value=") + if self.value is not None: + pos = self.value.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", simple=") + pos = _emit_int(buf, size, pos, int64(self.simple)) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# With Node +# ============================================================ + +class With(Stmt): + """With(items: list[rawptr[AST]], is_async: CInt)""" + items: rawptr[list[rawptr[AST]]] + flags: int + + def __new__(self, pool: rawptr[memhub.MemManager], + items: rawptr[list[rawptr[AST]]], + is_async: int): + ptr: rawptr[With] = pool.alloc(With.__sizeof__()) + if ptr: + string.memset(ptr, 0, With.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + items: rawptr[list[rawptr[AST]]], + is_async: int): + _init_ast(self, pool, 0, 0) + self.items = items + if is_async: + self.flags = FLAG_IS_ASYNC + else: + self.flags = 0 + _set_parent_list(items, self) + + def kind(self) -> int: + return ASTKind.With + + def type_name(self) -> str: + return "With" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "With(items=") + pos = _dump_list(self.items, buf, size, pos) + pos = _emit(buf, size, pos, ", body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Raise Node +# ============================================================ + +class Raise(Stmt): + """Raise(exc: AST, cause: AST)""" + exc: rawptr[AST] + cause: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + exc: rawptr[AST], cause: rawptr[AST]): + ptr: rawptr[Raise] = pool.alloc(Raise.__sizeof__()) + if ptr: + string.memset(ptr, 0, Raise.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + exc: rawptr[AST], cause: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.exc = exc + self.cause = cause + + def kind(self) -> int: + return ASTKind.Raise + + def type_name(self) -> str: + return "Raise" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Raise(exc=") + if self.exc is not None: + pos = self.exc.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", cause=") + if self.cause is not None: + pos = self.cause.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Try Node +# ============================================================ + +class Try(Stmt): + """Try(handlers: list[rawptr[AST]], + orelse: list[rawptr[AST]], finalbody: list[rawptr[AST]])""" + handlers: rawptr[list[rawptr[AST]]] + orelse: rawptr[list[rawptr[AST]]] + finalbody: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + handlers: rawptr[list[rawptr[AST]]], + orelse: rawptr[list[rawptr[AST]]], + finalbody: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Try] = pool.alloc(Try.__sizeof__()) + if ptr: + string.memset(ptr, 0, Try.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + handlers: rawptr[list[rawptr[AST]]], + orelse: rawptr[list[rawptr[AST]]], + finalbody: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.handlers = handlers + self.orelse = orelse + self.finalbody = finalbody + _set_parent_list(handlers, self) + _set_parent_list(orelse, self) + _set_parent_list(finalbody, self) + + def kind(self) -> int: + return ASTKind.Try + + def type_name(self) -> str: + return "Try" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Try(body=") + pos = _dump_list(self.children, buf, size, pos) + pos = _emit(buf, size, pos, ", handlers=") + pos = _dump_list(self.handlers, buf, size, pos) + pos = _emit(buf, size, pos, ", orelse=") + pos = _dump_list(self.orelse, buf, size, pos) + pos = _emit(buf, size, pos, ", finalbody=") + pos = _dump_list(self.finalbody, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Assert Node +# ============================================================ + +class Assert(Stmt): + """Assert(test: AST, msg: AST)""" + test: rawptr[AST] + msg: rawptr[AST] + + def __new__(self, pool: rawptr[memhub.MemManager], + test: rawptr[AST], msg: rawptr[AST]): + ptr: rawptr[Assert] = pool.alloc(Assert.__sizeof__()) + if ptr: + string.memset(ptr, 0, Assert.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + test: rawptr[AST], msg: rawptr[AST]): + _init_ast(self, pool, 0, 0) + self.test = test + self.msg = msg + + def kind(self) -> int: + return ASTKind.Assert + + def type_name(self) -> str: + return "Assert" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Assert(test=") + if self.test is not None: + pos = self.test.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", msg=") + if self.msg is not None: + pos = self.msg.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Global Node +# ============================================================ + +class Global(Stmt): + """Global(names: list[rawptr[AST]])""" + names: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + names: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Global] = pool.alloc(Global.__sizeof__()) + if ptr: + string.memset(ptr, 0, Global.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + names: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.names = names + _set_parent_list(names, self) + + def kind(self) -> int: + return ASTKind.Global + + def type_name(self) -> str: + return "Global" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Global(names=") + pos = _dump_list(self.names, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Nonlocal Node +# ============================================================ + +class Nonlocal(Stmt): + """Nonlocal(names: list[rawptr[AST]])""" + names: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + names: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Nonlocal] = pool.alloc(Nonlocal.__sizeof__()) + if ptr: + string.memset(ptr, 0, Nonlocal.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + names: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.names = names + _set_parent_list(names, self) + + def kind(self) -> int: + return ASTKind.Nonlocal + + def type_name(self) -> str: + return "Nonlocal" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Nonlocal(names=") + pos = _dump_list(self.names, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Pass Node +# ============================================================ + +class Pass(Stmt): + """Pass()""" + + def __new__(self, pool: rawptr[memhub.MemManager]): + ptr: rawptr[Pass] = pool.alloc(Pass.__sizeof__()) + if ptr: + string.memset(ptr, 0, Pass.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager]): + _init_ast(self, pool, 0, 0) + + def kind(self) -> int: + return ASTKind.Pass + + def type_name(self) -> str: + return "Pass" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Pass()") + return pos + + +# ============================================================ +# Break Node +# ============================================================ + +class Break(Stmt): + """Break()""" + + def __new__(self, pool: rawptr[memhub.MemManager]): + ptr: rawptr[Break] = pool.alloc(Break.__sizeof__()) + if ptr: + string.memset(ptr, 0, Break.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager]): + _init_ast(self, pool, 0, 0) + + def kind(self) -> int: + return ASTKind.Break + + def type_name(self) -> str: + return "Break" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Break()") + return pos + + +# ============================================================ +# Continue Node +# ============================================================ + +class Continue(Stmt): + """Continue()""" + + def __new__(self, pool: rawptr[memhub.MemManager]): + ptr: rawptr[Continue] = pool.alloc(Continue.__sizeof__()) + if ptr: + string.memset(ptr, 0, Continue.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager]): + _init_ast(self, pool, 0, 0) + + def kind(self) -> int: + return ASTKind.Continue + + def type_name(self) -> str: + return "Continue" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Continue()") + return pos + + +# ============================================================ +# Import Node +# ============================================================ + +class Import(Stmt): + """Import(names: list[rawptr[AST]])""" + names: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + names: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Import] = pool.alloc(Import.__sizeof__()) + if ptr: + string.memset(ptr, 0, Import.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + names: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.names = names + _set_parent_list(names, self) + + def kind(self) -> int: + return ASTKind.Import + + def type_name(self) -> str: + return "Import" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Import(names=") + pos = _dump_list(self.names, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# ImportFrom Node +# ============================================================ + +class ImportFrom(Stmt): + """ImportFrom(module: str, names: list[rawptr[AST]], level: CInt)""" + module: str + names: rawptr[list[rawptr[AST]]] + level: int + + def __new__(self, pool: rawptr[memhub.MemManager], + module: str, + names: rawptr[list[rawptr[AST]]], + level: int): + ptr: rawptr[ImportFrom] = pool.alloc(ImportFrom.__sizeof__()) + if ptr: + string.memset(ptr, 0, ImportFrom.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + module: str, + names: rawptr[list[rawptr[AST]]], + level: int): + _init_ast(self, pool, 0, 0) + self.module = _copy_str(pool, module) + self.names = names + self.level = level + _set_parent_list(names, self) + + def kind(self) -> int: + return ASTKind.ImportFrom + + def type_name(self) -> str: + return "ImportFrom" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "ImportFrom(module='") + pos = _emit_str(buf, size, pos, self.module) + pos = _emit(buf, size, pos, "', names=") + pos = _dump_list(self.names, buf, size, pos) + pos = _emit(buf, size, pos, ", level=") + pos = _emit_int(buf, size, pos, int64(self.level)) + pos = _emit(buf, size, pos, ")") + return pos + + +# ============================================================ +# Match Node +# ============================================================ + +class Match(Stmt): + """Match(subject: AST, cases: list[rawptr[AST]])""" + subject: rawptr[AST] + cases: rawptr[list[rawptr[AST]]] + + def __new__(self, pool: rawptr[memhub.MemManager], + subject: rawptr[AST], + cases: rawptr[list[rawptr[AST]]]): + ptr: rawptr[Match] = pool.alloc(Match.__sizeof__()) + if ptr: + string.memset(ptr, 0, Match.__sizeof__()) + return ptr + + def __init__(self, pool: rawptr[memhub.MemManager], + subject: rawptr[AST], + cases: rawptr[list[rawptr[AST]]]): + _init_ast(self, pool, 0, 0) + self.subject = subject + self.cases = cases + _set_parent_list(cases, self) + + def kind(self) -> int: + return ASTKind.Match + + def type_name(self) -> str: + return "Match" + + def dump(self, buf: str, size: t.CSizeT, + pos: t.CSizeT) -> t.CSizeT: + pos = _emit(buf, size, pos, "Match(subject=") + if self.subject is not None: + pos = self.subject.dump(buf, size, pos) + else: + pos = _emit(buf, size, pos, "None") + pos = _emit(buf, size, pos, ", cases=") + pos = _dump_list(self.cases, buf, size, pos) + pos = _emit(buf, size, pos, ")") + return pos diff --git a/ast/tokens.vp b/ast/tokens.vp index c88d888..f75515b 100644 --- a/ast/tokens.vp +++ b/ast/tokens.vp @@ -159,11 +159,11 @@ class Token: col_offset: int # Starting column offset end_lineno: int # Ending line number end_col_offset: int # Ending column offset - next: rowptr[Token] # Next token in linked list + next: rawptr[Token] # Next token in linked list - def __new__(self, pool: rowptr[memhub.MemManager]): + def __new__(self, pool: rawptr[memhub.MemManager]): """Allocate zero‑initialized Token struct memory from mpool""" - ptr: rowptr[Token] = pool.alloc(Token.__sizeof__()) + ptr: rawptr[Token] = pool.alloc(Token.__sizeof__()) if ptr: string.memset(ptr, 0, Token.__sizeof__()) return ptr @@ -176,18 +176,18 @@ class Token: class KwEntry: name: str kw_id: int - next: rowptr[KwEntry] + next: rawptr[KwEntry] - def __new__(self, pool: rowptr[memhub.MemManager]): - ptr: rowptr[KwEntry] = pool.alloc(KwEntry.__sizeof__()) + def __new__(self, pool: rawptr[memhub.MemManager]): + ptr: rawptr[KwEntry] = pool.alloc(KwEntry.__sizeof__()) if ptr: string.memset(ptr, 0, KwEntry.__sizeof__()) return ptr # Global keyword linked list head (lazy initialization) -_kw_head: rowptr[KwEntry] = None -_kw_pool: rowptr[memhub.MemManager] = None +_kw_head: rawptr[KwEntry] = None +_kw_pool: rawptr[memhub.MemManager] = None # 关键字哈希索引(开放寻址 | 线性探测):槽为 u64,0=空槽,否则存 KwEntry 地址。 # 目的:把 _kw_lookup 的 45 项链表线性扫描降为期望 O(1)(不改链表本身)。索引与 @@ -213,7 +213,7 @@ def _kw_hash_str(key: str) -> uint64: return uint64(h) -def _kw_index_put(entry: rowptr[KwEntry]): +def _kw_index_put(entry: rawptr[KwEntry]): """把关键字条目写入哈希索引(索引未建成时静默跳过) 同键只保留首注册者(与查找"命中即返回"语义一致);冲突逐键 strcmp 区分, @@ -225,22 +225,22 @@ def _kw_index_put(entry: rowptr[KwEntry]): idx: t.CSizeT = t.CSizeT(h) & mask i: int = 0 while i < _KW_IDX_CAP: - slot: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(uint64(_kw_index) + uint64(idx * 8), t.CPtr)) + slot: sizet | t.CPtr = (sizet | t.CPtr)(t.CPtr(uint64(_kw_index) + uint64(idx * 8))) cur_s: sizet = slot[0] if cur_s == 0: slot[0] = sizet(entry) return - cur: KwEntry | t.CPtr = (KwEntry | t.CPtr)(t.CVoid(uint64(cur_s), t.CPtr)) + cur: KwEntry | t.CPtr = (KwEntry | t.CPtr)(t.CPtr(uint64(cur_s))) if string.strcmp(cur.name, entry.name) == 0: return idx = (idx + 1) & mask i += 1 -def _kw_intern(pool: rowptr[memhub.MemManager], name: str, kw_id: int): +def _kw_intern(pool: rawptr[memhub.MemManager], name: str, kw_id: int): """Register keyword name in the linked list""" global _kw_head - entry: rowptr[KwEntry] = KwEntry(pool) + entry: rawptr[KwEntry] = KwEntry(pool) if entry == None: return slen: t.CSizeT = string.strlen(name) buf: str = pool.alloc(slen + 1) @@ -265,17 +265,17 @@ def _kw_lookup(name: str) -> int: idx: t.CSizeT = t.CSizeT(h) & mask i: int = 0 while i < _KW_IDX_CAP: - slot: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(uint64(_kw_index) + uint64(idx * 8), t.CPtr)) + slot: sizet | t.CPtr = (sizet | t.CPtr)(t.CPtr(uint64(_kw_index) + uint64(idx * 8))) cur_s: sizet = slot[0] if cur_s == 0: return 0 - cur: KwEntry | t.CPtr = (KwEntry | t.CPtr)(t.CVoid(uint64(cur_s), t.CPtr)) + cur: KwEntry | t.CPtr = (KwEntry | t.CPtr)(t.CPtr(uint64(cur_s))) if string.strcmp(cur.name, name) == 0: return cur.kw_id idx = (idx + 1) & mask i += 1 return 0 - cur2: rowptr[KwEntry] = _kw_head + cur2: rawptr[KwEntry] = _kw_head while cur2 != None: if string.strcmp(cur2.name, name) == 0: return cur2.kw_id @@ -283,7 +283,7 @@ def _kw_lookup(name: str) -> int: return 0 -def _init_keywords(pool: rowptr[memhub.MemManager]): +def _init_keywords(pool: rawptr[memhub.MemManager]): """Initialize keyword linked list (only once)""" global _kw_head, _kw_pool, _kw_index if _kw_head != None: return @@ -350,22 +350,22 @@ class OpEntry: name: str # Operator text (e.g. "==", "+=") op_id: int length: int - next: rowptr[OpEntry] + next: rawptr[OpEntry] - def __new__(self, pool: rowptr[memhub.MemManager]): - ptr: rowptr[OpEntry] = pool.alloc(OpEntry.__sizeof__()) + def __new__(self, pool: rawptr[memhub.MemManager]): + ptr: rawptr[OpEntry] = pool.alloc(OpEntry.__sizeof__()) if ptr: string.memset(ptr, 0, OpEntry.__sizeof__()) return ptr -_op_head: rowptr[OpEntry] = None +_op_head: rawptr[OpEntry] = None -def _op_intern(pool: rowptr[memhub.MemManager], name: str, op_id: int): +def _op_intern(pool: rawptr[memhub.MemManager], name: str, op_id: int): global _op_head slen: t.CSizeT = string.strlen(name) - entry: rowptr[OpEntry] = OpEntry(pool) + entry: rawptr[OpEntry] = OpEntry(pool) if entry == None: return buf: str = pool.alloc(slen + 1) if buf == None: return @@ -377,7 +377,7 @@ def _op_intern(pool: rowptr[memhub.MemManager], name: str, op_id: int): _op_head = entry -def _init_operators(pool: rowptr[memhub.MemManager]): +def _init_operators(pool: rawptr[memhub.MemManager]): """Initialize the operator table (register in descending length order for easy greedy matching)""" global _op_head if _op_head != None: return @@ -433,7 +433,7 @@ def _init_operators(pool: rowptr[memhub.MemManager]): _op_intern(pool, "~", TokOp.Tilde) -def _init_tables(pool: rowptr[memhub.MemManager]): +def _init_tables(pool: rawptr[memhub.MemManager]): """Initialize all lookup tables (idempotent)""" _init_keywords(pool) _init_operators(pool) @@ -443,10 +443,10 @@ def _init_tables(pool: rowptr[memhub.MemManager]): # Token creation helper functions # ============================================================ -def new_token(pool: rowptr[memhub.MemManager], ttype: int, lineno: int, - col_offset: int) -> rowptr[Token]: +def new_token(pool: rawptr[memhub.MemManager], ttype: int, lineno: int, + col_offset: int) -> rawptr[Token]: """Create a new empty Token and fill in fields later""" - tok: rowptr[Token] = Token(pool) + tok: rawptr[Token] = Token(pool) if tok == None: return None tok.type = ttype tok.lineno = lineno @@ -456,7 +456,7 @@ def new_token(pool: rowptr[memhub.MemManager], ttype: int, lineno: int, return tok -def token_set_str(pool: rowptr[memhub.MemManager], tok: rowptr[Token], +def token_set_str(pool: rawptr[memhub.MemManager], tok: rawptr[Token], src: str, start: t.CSizeT, length: t.CSizeT): """Copy text from src[start:start + length] to token.str_val @@ -473,7 +473,7 @@ def token_set_str(pool: rowptr[memhub.MemManager], tok: rowptr[Token], tok.str_val = buf -def token_set_str_literal(pool: rowptr[memhub.MemManager], tok: rowptr[Token], +def token_set_str_literal(pool: rawptr[memhub.MemManager], tok: rawptr[Token], text: str): """Copy a C string to token.str_val""" if tok == None or text == None: return diff --git a/atom.vp b/atom.vp index 6c58de9..6a9bcb6 100644 --- a/atom.vp +++ b/atom.vp @@ -9,7 +9,7 @@ ATOMIC_ACQ_REL: t.CDefine = 4 ATOMIC_SEQ_CST: t.CDefine = 5 -def __atomic_test_and_set(ptr: t.CUInt64T | t.CPtr, order: t.CInt) -> t.CBool: +def __atomic_test_and_set(ptr: t.CUInt64T | t.CPtr, order: t.CInt) -> bool: result: t.CUInt8T = 1 c.Asm(f"""mov al, 1 xchg byte ptr [{c.AsmInp(ptr, t.ASM_DESCR.REG_ANY)}], al @@ -19,23 +19,23 @@ def __atomic_test_and_set(ptr: t.CUInt64T | t.CPtr, order: t.CInt) -> t.CBool: return t.CBool(result) -def __atomic_clear(ptr: t.CUInt64T | t.CPtr, order: t.CInt) -> t.CVoid: +def __atomic_clear(ptr: t.CUInt64T | t.CPtr, order: t.CInt): c.Asm(f"""mov byte ptr [{c.AsmInp(ptr, t.ASM_DESCR.REG_ANY)}], 0""", inp = [c.AsmInp(ptr, t.ASM_DESCR.REG_ANY)], op = [t.ASM_DESCR.CLOBBER_MEMORY]) -def __atomic_thread_fence(order: t.CInt) -> t.CVoid: +def __atomic_thread_fence(order: t.CInt): c.Asm("mfence", op=[t.ASM_DESCR.CLOBBER_MEMORY]) -def __atomic_signal_fence(order: t.CInt) -> t.CVoid: +def __atomic_signal_fence(order: t.CInt): pass -def __atomic_always_lock_free(size: t.CSizeT, ptr: t.CVoid | t.CPtr) -> t.CBool: +def __atomic_always_lock_free(size: t.CSizeT, ptr: t.CPtr) -> t.CBool: return t.CBool(1) -def __atomic_is_lock_free(size: t.CSizeT, ptr: t.CVoid | t.CPtr) -> t.CBool: +def __atomic_is_lock_free(size: t.CSizeT, ptr: t.CPtr) -> t.CBool: return t.CBool(1) diff --git a/builtins.vp b/builtins.vp index 30f1fcd..6de2682 100644 --- a/builtins.vp +++ b/builtins.vp @@ -1,58 +1,58 @@ -import stdint as __stdint -import t - -# ============================================================ -# 内置函数(Python 语义,自动与 builtins 关联,无需显式 import) -# ord: 字符 → ASCII 码(int) -# chr: ASCII 码 → 字符(t.CChar) -# min/max: 数值比较取小/大值(double 参数,int/float 实参经 -# coerce_to_type 无损提升;返回值由调用侧 coerce 到目标类型) -# ============================================================ - -def ord(s: t.CChar) -> int: - return s - -def chr(i: int) -> t.CChar: - return i - -def min(a: double, b: double) -> double: - if a < b: - return a - return b - -def max(a: double, b: double) -> double: - if a > b: - return a - return b - -# rowptr 已内置于编译器(TPC/TPV 双端),作为语法保留字,不可重定义: -# rowptr[T] 固定表示"裸指针,指向原始类型 T"(等价 t.CPtr[T])。 -size_t: t.CTypedef = t.CSizeT -uzt: t.CTypedef = t.CSizeT -sizet: t.CTypedef = t.CSizeT -int8: t.CTypedef = t.CInt8T -int16: t.CTypedef = t.CInt16T -int32: t.CTypedef = t.CInt32T -int64: t.CTypedef = t.CInt64T -uint8: t.CTypedef = t.CUInt8T -uint16: t.CTypedef = t.CUInt16T -uint32: t.CTypedef = t.CUInt32T -uint64: t.CTypedef = t.CUInt64T - -intfast8: t.CTypedef = t.CIntFast8T -intfast16: t.CTypedef = t.CIntFast16T -intfast32: t.CTypedef = t.CIntFast32T -intfast64: t.CTypedef = t.CIntFast64T -uintfast8: t.CTypedef = t.CUIntFast8T -uintfast16: t.CTypedef = t.CUIntFast16T -uintfast32: t.CTypedef = t.CUIntFast32T -uintfast64: t.CTypedef = t.CUIntFast64T - -intleast8: t.CTypedef = t.CIntLeast8T -intleast16: t.CTypedef = t.CIntLeast16T -intleast32: t.CTypedef = t.CIntLeast32T -intleast64: t.CTypedef = t.CIntLeast64T -uintleast8: t.CTypedef = t.CUIntLeast8T -uintleast16: t.CTypedef = t.CUIntLeast16T -uintleast32: t.CTypedef = t.CUIntLeast32T -uintleast64: t.CTypedef = t.CUIntLeast64T +import stdint as __stdint +import t + +# ============================================================ +# 内置函数(Python 语义,自动与 builtins 关联,无需显式 import) +# ord: 字符 → ASCII 码(int) +# chr: ASCII 码 → 字符(t.CChar) +# min/max: 数值比较取小/大值(double 参数,int/float 实参经 +# coerce_to_type 无损提升;返回值由调用侧 coerce 到目标类型) +# ============================================================ + +def ord(s: t.CChar) -> int: + return s + +def chr(i: int) -> t.CChar: + return i + +def min(a: double, b: double) -> double: + if a < b: + return a + return b + +def max(a: double, b: double) -> double: + if a > b: + return a + return b + +# rawptr 已内置于编译器(TPC/TPV 双端),作为语法保留字,不可重定义: +# rawptr[T] 固定表示"裸指针,指向原始类型 T"(等价 t.CPtr[T])。 +size_t: t.CTypedef = t.CSizeT +uzt: t.CTypedef = t.CSizeT +sizet: t.CTypedef = t.CSizeT +int8: t.CTypedef = t.CInt8T +int16: t.CTypedef = t.CInt16T +int32: t.CTypedef = t.CInt32T +int64: t.CTypedef = t.CInt64T +uint8: t.CTypedef = t.CUInt8T +uint16: t.CTypedef = t.CUInt16T +uint32: t.CTypedef = t.CUInt32T +uint64: t.CTypedef = t.CUInt64T + +intfast8: t.CTypedef = t.CIntFast8T +intfast16: t.CTypedef = t.CIntFast16T +intfast32: t.CTypedef = t.CIntFast32T +intfast64: t.CTypedef = t.CIntFast64T +uintfast8: t.CTypedef = t.CUIntFast8T +uintfast16: t.CTypedef = t.CUIntFast16T +uintfast32: t.CTypedef = t.CUIntFast32T +uintfast64: t.CTypedef = t.CUIntFast64T + +intleast8: t.CTypedef = t.CIntLeast8T +intleast16: t.CTypedef = t.CIntLeast16T +intleast32: t.CTypedef = t.CIntLeast32T +intleast64: t.CTypedef = t.CIntLeast64T +uintleast8: t.CTypedef = t.CUIntLeast8T +uintleast16: t.CTypedef = t.CUIntLeast16T +uintleast32: t.CTypedef = t.CUIntLeast32T +uintleast64: t.CTypedef = t.CUIntLeast64T diff --git a/coro.vp b/coro.vp index bef7fb6..c34b2d4 100644 --- a/coro.vp +++ b/coro.vp @@ -35,7 +35,7 @@ def coro_sleep(ms: t.CInt) -> t.CInt: return 0 -def coro_destroy(c: Coroutine) -> t.CVoid: +def coro_destroy(c: Coroutine): # M3 占位:编译器将 coro.coro_destroy(帧) 内联翻译为 free。 return diff --git a/hashlib/__md5.vp b/hashlib/__md5.vp index 6449933..745fa20 100644 --- a/hashlib/__md5.vp +++ b/hashlib/__md5.vp @@ -51,7 +51,7 @@ class md5: self.state[3] = 0x10325476 self.count = 0 # MD5 single-group compression transform - def transform(self, block: rowptr[uint8]): + def transform(self, block: rawptr[uint8]): a: uint32 = self.state[0] b: uint32 = self.state[1] c: uint32 = self.state[2] @@ -88,7 +88,7 @@ class md5: self.state[3] += d # Batch input data def update(self, s: str): - data: rowptr[uint8] = uint8(s, t.CPtr) + data: rawptr[uint8] = uint8(s, t.CPtr) length: t.CSizeT = len(s) idx: t.CSizeT = self.count % MD5_BLOCK_LEN self.count += length diff --git a/hashlib/__sha1.vp b/hashlib/__sha1.vp index efed5bd..18dce48 100644 --- a/hashlib/__sha1.vp +++ b/hashlib/__sha1.vp @@ -30,9 +30,9 @@ class sha1: self.state[4] = 0xc3d2e1f0 self.count = 0 # Single-group compression transform - def transform(self, block: rowptr[uint8]): + def transform(self, block: rawptr[uint8]): w: t.CArray[uint32, 80] - data: rowptr[uint8] = block + data: rawptr[uint8] = block # First 16 words for i in range(16): w[i] = (((uint32(data[i * 4 + 0])) << 24) | @@ -80,7 +80,7 @@ class sha1: self.state[3] += d self.state[4] += e def update(self, s: str): - data: rowptr[uint8] = uint8(s, t.CPtr) + data: rawptr[uint8] = uint8(s, t.CPtr) length: t.CSizeT = len(s) idx: t.CSizeT = self.count % SHA1_BLOCK_LEN self.count += length diff --git a/hashlib/__sha256.vp b/hashlib/__sha256.vp index a0b1ff8..e7cc2c9 100644 --- a/hashlib/__sha256.vp +++ b/hashlib/__sha256.vp @@ -47,7 +47,7 @@ class sha256: self.count = 0 # string.memset(self.buf, 0, SHA256_BLOCK_LEN) # SHA256 single-group compression transform - def transform(self, block: rowptr[uint8]): + def transform(self, block: rawptr[uint8]): w: t.CArray[uint32, 64] for i in range(16): w[i] = ((uint32(block[i * 4 + 0]) << 24) | @@ -85,7 +85,7 @@ class sha256: self.state[6] += g self.state[7] += h def update(self, s: str): - data: rowptr[uint8] = uint8(s, t.CPtr) + data: rawptr[uint8] = uint8(s, t.CPtr) length: t.CSizeT = len(s) idx: t.CSizeT = self.count % SHA256_BLOCK_LEN self.count += length diff --git a/hashlib/__sha512.vp b/hashlib/__sha512.vp index 2b33a4b..36a3716 100644 --- a/hashlib/__sha512.vp +++ b/hashlib/__sha512.vp @@ -102,7 +102,7 @@ class sha512: self.state[6] += g self.state[7] += h def update(self, s: str): - data: rowptr[uint8] = uint8(s, t.CPtr) + data: rawptr[uint8] = uint8(s, t.CPtr) length: t.CSizeT = len(s) idx: uint32 = uint32(self.count[0] % SHA512_BLOCK_LEN) self.count[0] += length diff --git a/hashtable.vp b/hashtable.vp index 2395ba8..8a7444e 100644 --- a/hashtable.vp +++ b/hashtable.vp @@ -47,7 +47,7 @@ class HashTable: p: t.CPtr = ht["x"] v: int = c.Deref(p) """ - __slots__: t.CVoid | t.CPtr # Base address of slot array + __slots__: t.CPtr # Base address of slot array __count__: t.CSizeT = 0 # Number of occupied entries __capacity__: t.CSizeT = 16 # Total slot count (power of two) __tombstones__: t.CSizeT = 0 # Tombstone entry count @@ -69,12 +69,12 @@ class HashTable: def _slot_hash(self, idx: t.CSizeT) -> t.CUInt64T: """Read hash value from target slot.""" - ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) + ptr: t.CUInt64T | t.CPtr = t.CPtr(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) return ptr[0] def _slot_key(self, idx: t.CSizeT) -> str: """Read key pointer from target slot.""" - ptr: str | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 8) + ptr: str | t.CPtr = t.CPtr(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 8) return ptr[0] def _slot_value(self, idx: t.CSizeT) -> t.CPtr: @@ -83,17 +83,17 @@ class HashTable: # 故 t.CUInt64T|t.CPtr|t.CPtr = Ptr(Ptr(i64)) = i64**,取下标得 i64*(指针), # 与返回类型 t.CPtr(i8*)同级指针可隐式 bitcast。若只写 t.CUInt64T|t.CPtr # (= i64*,存哈希整数用),取下标得标量 i64 → 撞「整数隐式转指针禁止」。 - ptr: t.CUInt64T | t.CPtr | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 16) + ptr: t.CUInt64T | t.CPtr | t.CPtr = t.CPtr(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 16) return ptr[0] def _set_slot(self, idx: t.CSizeT, h: t.CUInt64T, key: str, val: t.CPtr): """Assign three fields of the target slot.""" - h_ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) + h_ptr: t.CUInt64T | t.CPtr = t.CPtr(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) h_ptr[0] = h - k_ptr: str | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 8) + k_ptr: str | t.CPtr = t.CPtr(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 8) k_ptr[0] = key # 同 _slot_value:value 槽按「指针的指针」(i64**) 注解,元素才是指针 - v_ptr: t.CUInt64T | t.CPtr | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 16) + v_ptr: t.CUInt64T | t.CPtr | t.CPtr = t.CPtr(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 16) v_ptr[0] = val def _find_slot(self, key: str, h: t.CUInt64T) -> t.CSizeT: @@ -127,9 +127,9 @@ class HashTable: def _resize(self, new_cap: t.CSizeT): """Resize table and rehash all entries.""" - old_slots: t.CVoid | t.CPtr = self.__slots__ + old_slots: t.CPtr = self.__slots__ old_cap: t.CSizeT = self.__capacity__ - new_slots: t.CVoid | t.CPtr = self.__mbuddy__.alloc(new_cap * _HT_SLOT_SIZE) + new_slots: t.CPtr = self.__mbuddy__.alloc(new_cap * _HT_SLOT_SIZE) if new_slots == None: return string.memset(new_slots, 0, new_cap * _HT_SLOT_SIZE) @@ -139,12 +139,12 @@ class HashTable: self.__tombstones__ = 0 i: t.CSizeT = 0 while i < old_cap: - old_h_ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE) + old_h_ptr: t.CUInt64T | t.CPtr = t.CPtr(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE) h: t.CUInt64T = old_h_ptr[0] if h != _HT_EMPTY and h != _HT_TOMBSTONE: - old_k_ptr: str | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE + 8) + old_k_ptr: str | t.CPtr = t.CPtr(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE + 8) # 同 _slot_value:value 槽按「指针的指针」(i64**) 注解,元素才是指针 - old_v_ptr: t.CUInt64T | t.CPtr | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE + 16) + old_v_ptr: t.CUInt64T | t.CPtr | t.CPtr = t.CPtr(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE + 16) k: str = old_k_ptr[0] v: t.CPtr = old_v_ptr[0] idx: t.CSizeT = self._find_slot(k, h) @@ -203,7 +203,7 @@ class HashTable: sh: t.CUInt64T = self._slot_hash(idx) if sh == _HT_EMPTY or sh == _HT_TOMBSTONE: return 0 - h_ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) + h_ptr: t.CUInt64T | t.CPtr = t.CPtr(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) h_ptr[0] = _HT_TOMBSTONE self.__count__ -= 1 self.__tombstones__ += 1 @@ -238,8 +238,8 @@ class HashTable: def set_int(self, key: str, val: int): """Store integer value. Value is copied into mbuddy-managed storage.""" - storage: t.CVoid | t.CPtr = self.__mbuddy__.alloc(8) - int_ptr: int | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) + storage: t.CPtr = self.__mbuddy__.alloc(8) + int_ptr: int | t.CPtr = t.CPtr(t.CUInt64T(storage)) int_ptr[0] = val self[key] = storage diff --git a/json/__init__.vp b/json/__init__.vp index 1b93c36..42f02d9 100644 --- a/json/__init__.vp +++ b/json/__init__.vp @@ -1,237 +1,237 @@ -import t, c -from stdint import * -import memhub -import string -import viperio - -# ============================================================ -# JSON 值类型枚举 -# ============================================================ -JSON_NULL: t.CDefine = 0 -JSON_BOOL: t.CDefine = 1 -JSON_INT: t.CDefine = 2 -JSON_FLOAT: t.CDefine = 3 -JSON_STRING: t.CDefine = 4 -JSON_ARRAY: t.CDefine = 5 -JSON_OBJECT: t.CDefine = 6 - - -# ============================================================ -# JsonValue - JSON 值节点 -# 使用 mpool 分配:JsonValue(pool) 从池中分配内存 -# ============================================================ -class JsonValue: - vtype: int - bool_val: int - int_val: int64 - float_val: double - str_val: str - next: 'JsonValue' | t.CPtr - key: str - child: 'JsonValue' | t.CPtr - child_count: t.CSizeT - - def __new__(self, pool: rowptr[memhub.MemManager]): - """从 mpool 分配 JsonValue 结构体内存并零初始化""" - ptr: rowptr[JsonValue] = pool.alloc(JsonValue.__sizeof__()) - if ptr: - string.memset(ptr, 0, JsonValue.__sizeof__()) - return ptr - - def type(self) -> int: - return self.vtype - - def is_null(self) -> bool: - return self.vtype == JSON_NULL - - def is_bool(self) -> bool: - return self.vtype == JSON_BOOL - - def is_int(self) -> bool: - return self.vtype == JSON_INT - - def is_float(self) -> bool: - return self.vtype == JSON_FLOAT - - def is_string(self) -> bool: - return self.vtype == JSON_STRING - - def is_array(self) -> bool: - return self.vtype == JSON_ARRAY - - def is_object(self) -> bool: - return self.vtype == JSON_OBJECT - - def as_bool(self) -> bool: - return self.bool_val != 0 - - def as_int(self) -> int64: - return self.int_val - - def as_float(self) -> double: - return self.float_val - - def as_string(self) -> str: - return self.str_val - - def __len__(self) -> t.CSizeT: - return self.child_count - - def __getitem__(self, key: str) -> 'JsonValue' | t.CPtr: - """Python风格取值:v["key"] 取对象字段""" - if self.vtype != JSON_OBJECT: - return None - if key == None: - return None - cur: rowptr[JsonValue] = self.child - while cur != None: - if cur.key != None: - if string.strcmp(cur.key, key) == 0: - return cur - cur = cur.next - return None - - def __setitem__(self, key: str, val: 'JsonValue' | t.CPtr): - """Python风格赋值:v["key"] = val""" - if self.vtype != JSON_OBJECT or key == None or val == None: - return - cur: rowptr[JsonValue] = self.child - while cur != None: - if cur.key != None: - if string.strcmp(cur.key, key) == 0: - cur.vtype = val.vtype - cur.bool_val = val.bool_val - cur.int_val = val.int_val - cur.float_val = val.float_val - cur.str_val = val.str_val - cur.child = val.child - cur.child_count = val.child_count - return - cur = cur.next - # key 不存在,追加新子节点 - klen: t.CSizeT = string.strlen(key) - kbuf: str = self._pool_alloc(klen + 1) - if kbuf != None: - string.memcpy(kbuf, key, klen + 1) - val.key = kbuf - self._append_child(val) - - def get_item(self, index: t.CSizeT) -> 'JsonValue' | t.CPtr: - """按索引取数组元素:v.get_item(0)""" - if self.vtype != JSON_ARRAY and self.vtype != JSON_OBJECT: - return None - cur: rowptr[JsonValue] = self.child - i: t.CSizeT = 0 - while cur != None: - if i == index: - return cur - cur = cur.next - i += 1 - return None - - def _pool_alloc(self, size: t.CSizeT) -> str: - """内部方法:从当前对象的 mpool 分配内存(预留,暂返回 None)""" - return None - - def _append_child(self, child: 'JsonValue' | t.CPtr): - if self.child == None: - self.child = child - else: - cur: rowptr[JsonValue] = self.child - while cur.next != None: - cur = cur.next - cur.next = child - self.child_count += 1 - - -# ============================================================ -# 构造函数:创建各类 JsonValue(从 mpool 分配) -# ============================================================ - -def null(pool: rowptr[memhub.MemManager]) -> rowptr[JsonValue]: - v: rowptr[JsonValue] = JsonValue(pool) - if v == None: return None - v.vtype = JSON_NULL - return v - -def bool_val(pool: rowptr[memhub.MemManager], val: bool) -> rowptr[JsonValue]: - v: rowptr[JsonValue] = JsonValue(pool) - if v == None: return None - v.vtype = JSON_BOOL - v.bool_val = 1 if val else 0 - return v - -def int_val(pool: rowptr[memhub.MemManager], val: int64) -> rowptr[JsonValue]: - v: rowptr[JsonValue] = JsonValue(pool) - if v == None: return None - v.vtype = JSON_INT - v.int_val = val - return v - -def float_val(pool: rowptr[memhub.MemManager], val: double) -> rowptr[JsonValue]: - v: rowptr[JsonValue] = JsonValue(pool) - if v == None: return None - v.vtype = JSON_FLOAT - v.float_val = val - return v - -def string_val(pool: rowptr[memhub.MemManager], val: str) -> rowptr[JsonValue]: - v: rowptr[JsonValue] = JsonValue(pool) - if v == None: return None - v.vtype = JSON_STRING - if val != None: - slen: t.CSizeT = string.strlen(val) - buf: str = pool.alloc(slen + 1) - if buf != None: - string.memcpy(buf, val, slen + 1) - v.str_val = buf - return v - -def array(pool: rowptr[memhub.MemManager]) -> rowptr[JsonValue]: - v: rowptr[JsonValue] = JsonValue(pool) - if v == None: return None - v.vtype = JSON_ARRAY - return v - -def object(pool: rowptr[memhub.MemManager]) -> rowptr[JsonValue]: - v: rowptr[JsonValue] = JsonValue(pool) - if v == None: return None - v.vtype = JSON_OBJECT - return v - -# 向数组追加元素 -def array_append(pool: rowptr[memhub.MemManager], arr: rowptr[JsonValue], item: rowptr[JsonValue]): - if arr == None or item == None: return - if arr.vtype != JSON_ARRAY: return - arr._append_child(item) - -# 向对象设置键值对 -def object_set(pool: rowptr[memhub.MemManager], obj: rowptr[JsonValue], key: str, val: rowptr[JsonValue]): - if obj == None or key == None or val == None: return - if obj.vtype != JSON_OBJECT: return - cur: rowptr[JsonValue] = obj.child - while cur != None: - if cur.key != None: - if string.strcmp(cur.key, key) == 0: - cur.vtype = val.vtype - cur.bool_val = val.bool_val - cur.int_val = val.int_val - cur.float_val = val.float_val - cur.str_val = val.str_val - cur.child = val.child - cur.child_count = val.child_count - return - cur = cur.next - klen: t.CSizeT = string.strlen(key) - kbuf: str = pool.alloc(klen + 1) - if kbuf != None: - string.memcpy(kbuf, key, klen + 1) - val.key = kbuf - obj._append_child(val) - - -# ============================================================ -# 导入子模块 -# ============================================================ -from .__parser import parse -from .__writer import write +import t, c +from stdint import * +import memhub +import string +import viperio + +# ============================================================ +# JSON 值类型枚举 +# ============================================================ +JSON_NULL: t.CDefine = 0 +JSON_BOOL: t.CDefine = 1 +JSON_INT: t.CDefine = 2 +JSON_FLOAT: t.CDefine = 3 +JSON_STRING: t.CDefine = 4 +JSON_ARRAY: t.CDefine = 5 +JSON_OBJECT: t.CDefine = 6 + + +# ============================================================ +# JsonValue - JSON 值节点 +# 使用 mpool 分配:JsonValue(pool) 从池中分配内存 +# ============================================================ +class JsonValue: + vtype: int + bool_val: int + int_val: int64 + float_val: double + str_val: str + next: 'JsonValue' | t.CPtr + key: str + child: 'JsonValue' | t.CPtr + child_count: t.CSizeT + + def __new__(self, pool: rawptr[memhub.MemManager]): + """从 mpool 分配 JsonValue 结构体内存并零初始化""" + ptr: rawptr[JsonValue] = pool.alloc(JsonValue.__sizeof__()) + if ptr: + string.memset(ptr, 0, JsonValue.__sizeof__()) + return ptr + + def type(self) -> int: + return self.vtype + + def is_null(self) -> bool: + return self.vtype == JSON_NULL + + def is_bool(self) -> bool: + return self.vtype == JSON_BOOL + + def is_int(self) -> bool: + return self.vtype == JSON_INT + + def is_float(self) -> bool: + return self.vtype == JSON_FLOAT + + def is_string(self) -> bool: + return self.vtype == JSON_STRING + + def is_array(self) -> bool: + return self.vtype == JSON_ARRAY + + def is_object(self) -> bool: + return self.vtype == JSON_OBJECT + + def as_bool(self) -> bool: + return self.bool_val != 0 + + def as_int(self) -> int64: + return self.int_val + + def as_float(self) -> double: + return self.float_val + + def as_string(self) -> str: + return self.str_val + + def __len__(self) -> t.CSizeT: + return self.child_count + + def __getitem__(self, key: str) -> 'JsonValue' | t.CPtr: + """Python风格取值:v["key"] 取对象字段""" + if self.vtype != JSON_OBJECT: + return None + if key == None: + return None + cur: rawptr[JsonValue] = self.child + while cur != None: + if cur.key != None: + if string.strcmp(cur.key, key) == 0: + return cur + cur = cur.next + return None + + def __setitem__(self, key: str, val: 'JsonValue' | t.CPtr): + """Python风格赋值:v["key"] = val""" + if self.vtype != JSON_OBJECT or key == None or val == None: + return + cur: rawptr[JsonValue] = self.child + while cur != None: + if cur.key != None: + if string.strcmp(cur.key, key) == 0: + cur.vtype = val.vtype + cur.bool_val = val.bool_val + cur.int_val = val.int_val + cur.float_val = val.float_val + cur.str_val = val.str_val + cur.child = val.child + cur.child_count = val.child_count + return + cur = cur.next + # key 不存在,追加新子节点 + klen: t.CSizeT = string.strlen(key) + kbuf: str = self._pool_alloc(klen + 1) + if kbuf != None: + string.memcpy(kbuf, key, klen + 1) + val.key = kbuf + self._append_child(val) + + def get_item(self, index: t.CSizeT) -> 'JsonValue' | t.CPtr: + """按索引取数组元素:v.get_item(0)""" + if self.vtype != JSON_ARRAY and self.vtype != JSON_OBJECT: + return None + cur: rawptr[JsonValue] = self.child + i: t.CSizeT = 0 + while cur != None: + if i == index: + return cur + cur = cur.next + i += 1 + return None + + def _pool_alloc(self, size: t.CSizeT) -> str: + """内部方法:从当前对象的 mpool 分配内存(预留,暂返回 None)""" + return None + + def _append_child(self, child: 'JsonValue' | t.CPtr): + if self.child == None: + self.child = child + else: + cur: rawptr[JsonValue] = self.child + while cur.next != None: + cur = cur.next + cur.next = child + self.child_count += 1 + + +# ============================================================ +# 构造函数:创建各类 JsonValue(从 mpool 分配) +# ============================================================ + +def null(pool: rawptr[memhub.MemManager]) -> rawptr[JsonValue]: + v: rawptr[JsonValue] = JsonValue(pool) + if v == None: return None + v.vtype = JSON_NULL + return v + +def bool_val(pool: rawptr[memhub.MemManager], val: bool) -> rawptr[JsonValue]: + v: rawptr[JsonValue] = JsonValue(pool) + if v == None: return None + v.vtype = JSON_BOOL + v.bool_val = 1 if val else 0 + return v + +def int_val(pool: rawptr[memhub.MemManager], val: int64) -> rawptr[JsonValue]: + v: rawptr[JsonValue] = JsonValue(pool) + if v == None: return None + v.vtype = JSON_INT + v.int_val = val + return v + +def float_val(pool: rawptr[memhub.MemManager], val: double) -> rawptr[JsonValue]: + v: rawptr[JsonValue] = JsonValue(pool) + if v == None: return None + v.vtype = JSON_FLOAT + v.float_val = val + return v + +def string_val(pool: rawptr[memhub.MemManager], val: str) -> rawptr[JsonValue]: + v: rawptr[JsonValue] = JsonValue(pool) + if v == None: return None + v.vtype = JSON_STRING + if val != None: + slen: t.CSizeT = string.strlen(val) + buf: str = pool.alloc(slen + 1) + if buf != None: + string.memcpy(buf, val, slen + 1) + v.str_val = buf + return v + +def array(pool: rawptr[memhub.MemManager]) -> rawptr[JsonValue]: + v: rawptr[JsonValue] = JsonValue(pool) + if v == None: return None + v.vtype = JSON_ARRAY + return v + +def object(pool: rawptr[memhub.MemManager]) -> rawptr[JsonValue]: + v: rawptr[JsonValue] = JsonValue(pool) + if v == None: return None + v.vtype = JSON_OBJECT + return v + +# 向数组追加元素 +def array_append(pool: rawptr[memhub.MemManager], arr: rawptr[JsonValue], item: rawptr[JsonValue]): + if arr == None or item == None: return + if arr.vtype != JSON_ARRAY: return + arr._append_child(item) + +# 向对象设置键值对 +def object_set(pool: rawptr[memhub.MemManager], obj: rawptr[JsonValue], key: str, val: rawptr[JsonValue]): + if obj == None or key == None or val == None: return + if obj.vtype != JSON_OBJECT: return + cur: rawptr[JsonValue] = obj.child + while cur != None: + if cur.key != None: + if string.strcmp(cur.key, key) == 0: + cur.vtype = val.vtype + cur.bool_val = val.bool_val + cur.int_val = val.int_val + cur.float_val = val.float_val + cur.str_val = val.str_val + cur.child = val.child + cur.child_count = val.child_count + return + cur = cur.next + klen: t.CSizeT = string.strlen(key) + kbuf: str = pool.alloc(klen + 1) + if kbuf != None: + string.memcpy(kbuf, key, klen + 1) + val.key = kbuf + obj._append_child(val) + + +# ============================================================ +# 导入子模块 +# ============================================================ +from .__parser import parse +from .__writer import write diff --git a/json/__parser.vp b/json/__parser.vp index 5cf7198..4bc2bad 100644 --- a/json/__parser.vp +++ b/json/__parser.vp @@ -1,291 +1,291 @@ -import t, c -from stdint import * -import memhub -import string -from . import ( - JsonValue, JSON_NULL, JSON_BOOL, JSON_INT, JSON_FLOAT, - JSON_STRING, JSON_ARRAY, JSON_OBJECT, - null, bool_val, int_val, float_val, string_val, array, object, -) - - -class ParseState: - src: str - pos: t.CSizeT - len: t.CSizeT - pool: rowptr[memhub.MemManager] - - def __init__(self, src: str, pool: rowptr[memhub.MemManager]): - self.src = src - self.pos = 0 - self.len = string.strlen(src) if src != None else 0 - self.pool = pool - - -def _peek(s: rowptr[ParseState]) -> t.CChar: - if s.pos >= s.len: - return '\0' - return s.src[s.pos] - -def _advance(s: rowptr[ParseState]) -> t.CChar: - if s.pos >= s.len: - return '\0' - ch: t.CChar = s.src[s.pos] - s.pos += 1 - return ch - -def _skip_whitespace(s: rowptr[ParseState]): - while s.pos < s.len: - ch: t.CChar = s.src[s.pos] - if ch == ' ' or ch == '\t' or ch == '\n' or ch == '\r': - s.pos += 1 - else: - break - -def _expect(s: rowptr[ParseState], ch: t.CChar) -> bool: - _skip_whitespace(s) - if s.pos >= s.len: - return False - if s.src[s.pos] != ch: - return False - s.pos += 1 - return True - - -def _parse_string(s: rowptr[ParseState]) -> str: - if _peek(s) != '"': - return None - _advance(s) - - start: t.CSizeT = s.pos - slen: t.CSizeT = 0 - while s.pos < s.len: - ch: t.CChar = s.src[s.pos] - if ch == '"': - break - if ch == '\\': - s.pos += 1 - slen += 1 - slen += 1 - s.pos += 1 - - buf: str = s.pool.alloc(slen + 1) - if buf == None: - return None - - s.pos = start - di: t.CSizeT = 0 - while s.pos < s.len: - ch: t.CChar = s.src[s.pos] - if ch == '"': - s.pos += 1 - break - if ch == '\\': - s.pos += 1 - if s.pos < s.len: - esc: t.CChar = s.src[s.pos] - if esc == 'n': - buf[di] = '\n' - elif esc == 't': - buf[di] = '\t' - elif esc == 'r': - buf[di] = '\r' - elif esc == '\\': - buf[di] = '\\' - elif esc == '"': - buf[di] = '"' - elif esc == '/': - buf[di] = '/' - else: - buf[di] = esc - di += 1 - s.pos += 1 - continue - buf[di] = ch - di += 1 - s.pos += 1 - - buf[di] = '\0' - return buf - - -def _parse_number(s: rowptr[ParseState]) -> rowptr[JsonValue]: - _skip_whitespace(s) - is_float: bool = False - start: t.CSizeT = s.pos - - if s.pos < s.len and s.src[s.pos] == '-': - s.pos += 1 - - while s.pos < s.len: - ch: t.CChar = s.src[s.pos] - if ch >= '0' and ch <= '9': - s.pos += 1 - else: - break - - if s.pos < s.len and s.src[s.pos] == '.': - is_float = True - s.pos += 1 - while s.pos < s.len: - ch: t.CChar = s.src[s.pos] - if ch >= '0' and ch <= '9': - s.pos += 1 - else: - break - - if s.pos < s.len and (s.src[s.pos] == 'e' or s.src[s.pos] == 'E'): - is_float = True - s.pos += 1 - if s.pos < s.len and (s.src[s.pos] == '+' or s.src[s.pos] == '-'): - s.pos += 1 - while s.pos < s.len: - ch: t.CChar = s.src[s.pos] - if ch >= '0' and ch <= '9': - s.pos += 1 - else: - break - - num_len: t.CSizeT = s.pos - start - buf: str = s.pool.alloc(num_len + 1) - if buf == None: - return null(s.pool) - - i: t.CSizeT = 0 - while i < num_len: - buf[i] = s.src[start + i] - i += 1 - buf[num_len] = '\0' - - if is_float: - val: double = string.atof(buf) - return float_val(s.pool, val) - else: - ival: int64 = string.atoll(buf) - return int_val(s.pool, ival) - - -def _parse_value(s: rowptr[ParseState]) -> rowptr[JsonValue]: - _skip_whitespace(s) - if s.pos >= s.len: - return null(s.pool) - - ch: t.CChar = _peek(s) - - if ch == '"': - sv: str = _parse_string(s) - return string_val(s.pool, sv) - - if ch == '{': - return _parse_object(s) - - if ch == '[': - return _parse_array(s) - - if ch == '-' or (ch >= '0' and ch <= '9'): - return _parse_number(s) - - if ch == 't': - if s.pos + 3 < s.len: - if s.src[s.pos + 1] == 'r' and s.src[s.pos + 2] == 'u' and s.src[s.pos + 3] == 'e': - s.pos += 4 - return bool_val(s.pool, True) - return null(s.pool) - - if ch == 'f': - if s.pos + 4 < s.len: - if s.src[s.pos + 1] == 'a' and s.src[s.pos + 2] == 'l' and s.src[s.pos + 3] == 's' and s.src[s.pos + 4] == 'e': - s.pos += 5 - return bool_val(s.pool, False) - return null(s.pool) - - if ch == 'n': - if s.pos + 3 < s.len: - if s.src[s.pos + 1] == 'u' and s.src[s.pos + 2] == 'l' and s.src[s.pos + 3] == 'l': - s.pos += 4 - return null(s.pool) - return null(s.pool) - - return null(s.pool) - - -def _parse_array(s: rowptr[ParseState]) -> rowptr[JsonValue]: - if not _expect(s, '['): - return null(s.pool) - - arr: rowptr[JsonValue] = array(s.pool) - - _skip_whitespace(s) - if s.pos < s.len and _peek(s) == ']': - _advance(s) - return arr - - item: rowptr[JsonValue] = _parse_value(s) - arr._append_child(item) - - while True: - _skip_whitespace(s) - if s.pos >= s.len: - break - if _peek(s) != ',': - break - _advance(s) - item = _parse_value(s) - arr._append_child(item) - - _expect(s, ']') - return arr - - -def _parse_object(s: rowptr[ParseState]) -> rowptr[JsonValue]: - if not _expect(s, '{'): - return null(s.pool) - - obj: rowptr[JsonValue] = object(s.pool) - - _skip_whitespace(s) - if s.pos < s.len and _peek(s) == '}': - _advance(s) - return obj - - _skip_whitespace(s) - key: str = _parse_string(s) - _expect(s, ':') - val: rowptr[JsonValue] = _parse_value(s) - if key != None and val != None: - klen: t.CSizeT = string.strlen(key) - kbuf: str = s.pool.alloc(klen + 1) - if kbuf != None: - string.memcpy(kbuf, key, klen + 1) - val.key = kbuf - obj._append_child(val) - - while True: - _skip_whitespace(s) - if s.pos >= s.len: - break - if _peek(s) != ',': - break - _advance(s) - _skip_whitespace(s) - key = _parse_string(s) - _expect(s, ':') - val = _parse_value(s) - if key != None and val != None: - klen2: t.CSizeT = string.strlen(key) - kbuf2: str = s.pool.alloc(klen2 + 1) - if kbuf2 != None: - string.memcpy(kbuf2, key, klen2 + 1) - val.key = kbuf2 - obj._append_child(val) - - _expect(s, '}') - return obj - - -def parse(pool: rowptr[memhub.MemManager], src: str) -> rowptr[JsonValue]: - """解析 JSON 字符串,返回 JsonValue 树""" - if src == None: - return null(pool) - s: rowptr[ParseState] = ParseState(src, pool) - return _parse_value(s) +import t, c +from stdint import * +import memhub +import string +from . import ( + JsonValue, JSON_NULL, JSON_BOOL, JSON_INT, JSON_FLOAT, + JSON_STRING, JSON_ARRAY, JSON_OBJECT, + null, bool_val, int_val, float_val, string_val, array, object, +) + + +class ParseState: + src: str + pos: t.CSizeT + len: t.CSizeT + pool: rawptr[memhub.MemManager] + + def __init__(self, src: str, pool: rawptr[memhub.MemManager]): + self.src = src + self.pos = 0 + self.len = string.strlen(src) if src != None else 0 + self.pool = pool + + +def _peek(s: rawptr[ParseState]) -> t.CChar: + if s.pos >= s.len: + return '\0' + return s.src[s.pos] + +def _advance(s: rawptr[ParseState]) -> t.CChar: + if s.pos >= s.len: + return '\0' + ch: t.CChar = s.src[s.pos] + s.pos += 1 + return ch + +def _skip_whitespace(s: rawptr[ParseState]): + while s.pos < s.len: + ch: t.CChar = s.src[s.pos] + if ch == ' ' or ch == '\t' or ch == '\n' or ch == '\r': + s.pos += 1 + else: + break + +def _expect(s: rawptr[ParseState], ch: t.CChar) -> bool: + _skip_whitespace(s) + if s.pos >= s.len: + return False + if s.src[s.pos] != ch: + return False + s.pos += 1 + return True + + +def _parse_string(s: rawptr[ParseState]) -> str: + if _peek(s) != '"': + return None + _advance(s) + + start: t.CSizeT = s.pos + slen: t.CSizeT = 0 + while s.pos < s.len: + ch: t.CChar = s.src[s.pos] + if ch == '"': + break + if ch == '\\': + s.pos += 1 + slen += 1 + slen += 1 + s.pos += 1 + + buf: str = s.pool.alloc(slen + 1) + if buf == None: + return None + + s.pos = start + di: t.CSizeT = 0 + while s.pos < s.len: + ch: t.CChar = s.src[s.pos] + if ch == '"': + s.pos += 1 + break + if ch == '\\': + s.pos += 1 + if s.pos < s.len: + esc: t.CChar = s.src[s.pos] + if esc == 'n': + buf[di] = '\n' + elif esc == 't': + buf[di] = '\t' + elif esc == 'r': + buf[di] = '\r' + elif esc == '\\': + buf[di] = '\\' + elif esc == '"': + buf[di] = '"' + elif esc == '/': + buf[di] = '/' + else: + buf[di] = esc + di += 1 + s.pos += 1 + continue + buf[di] = ch + di += 1 + s.pos += 1 + + buf[di] = '\0' + return buf + + +def _parse_number(s: rawptr[ParseState]) -> rawptr[JsonValue]: + _skip_whitespace(s) + is_float: bool = False + start: t.CSizeT = s.pos + + if s.pos < s.len and s.src[s.pos] == '-': + s.pos += 1 + + while s.pos < s.len: + ch: t.CChar = s.src[s.pos] + if ch >= '0' and ch <= '9': + s.pos += 1 + else: + break + + if s.pos < s.len and s.src[s.pos] == '.': + is_float = True + s.pos += 1 + while s.pos < s.len: + ch: t.CChar = s.src[s.pos] + if ch >= '0' and ch <= '9': + s.pos += 1 + else: + break + + if s.pos < s.len and (s.src[s.pos] == 'e' or s.src[s.pos] == 'E'): + is_float = True + s.pos += 1 + if s.pos < s.len and (s.src[s.pos] == '+' or s.src[s.pos] == '-'): + s.pos += 1 + while s.pos < s.len: + ch: t.CChar = s.src[s.pos] + if ch >= '0' and ch <= '9': + s.pos += 1 + else: + break + + num_len: t.CSizeT = s.pos - start + buf: str = s.pool.alloc(num_len + 1) + if buf == None: + return null(s.pool) + + i: t.CSizeT = 0 + while i < num_len: + buf[i] = s.src[start + i] + i += 1 + buf[num_len] = '\0' + + if is_float: + val: double = string.atof(buf) + return float_val(s.pool, val) + else: + ival: int64 = string.atoll(buf) + return int_val(s.pool, ival) + + +def _parse_value(s: rawptr[ParseState]) -> rawptr[JsonValue]: + _skip_whitespace(s) + if s.pos >= s.len: + return null(s.pool) + + ch: t.CChar = _peek(s) + + if ch == '"': + sv: str = _parse_string(s) + return string_val(s.pool, sv) + + if ch == '{': + return _parse_object(s) + + if ch == '[': + return _parse_array(s) + + if ch == '-' or (ch >= '0' and ch <= '9'): + return _parse_number(s) + + if ch == 't': + if s.pos + 3 < s.len: + if s.src[s.pos + 1] == 'r' and s.src[s.pos + 2] == 'u' and s.src[s.pos + 3] == 'e': + s.pos += 4 + return bool_val(s.pool, True) + return null(s.pool) + + if ch == 'f': + if s.pos + 4 < s.len: + if s.src[s.pos + 1] == 'a' and s.src[s.pos + 2] == 'l' and s.src[s.pos + 3] == 's' and s.src[s.pos + 4] == 'e': + s.pos += 5 + return bool_val(s.pool, False) + return null(s.pool) + + if ch == 'n': + if s.pos + 3 < s.len: + if s.src[s.pos + 1] == 'u' and s.src[s.pos + 2] == 'l' and s.src[s.pos + 3] == 'l': + s.pos += 4 + return null(s.pool) + return null(s.pool) + + return null(s.pool) + + +def _parse_array(s: rawptr[ParseState]) -> rawptr[JsonValue]: + if not _expect(s, '['): + return null(s.pool) + + arr: rawptr[JsonValue] = array(s.pool) + + _skip_whitespace(s) + if s.pos < s.len and _peek(s) == ']': + _advance(s) + return arr + + item: rawptr[JsonValue] = _parse_value(s) + arr._append_child(item) + + while True: + _skip_whitespace(s) + if s.pos >= s.len: + break + if _peek(s) != ',': + break + _advance(s) + item = _parse_value(s) + arr._append_child(item) + + _expect(s, ']') + return arr + + +def _parse_object(s: rawptr[ParseState]) -> rawptr[JsonValue]: + if not _expect(s, '{'): + return null(s.pool) + + obj: rawptr[JsonValue] = object(s.pool) + + _skip_whitespace(s) + if s.pos < s.len and _peek(s) == '}': + _advance(s) + return obj + + _skip_whitespace(s) + key: str = _parse_string(s) + _expect(s, ':') + val: rawptr[JsonValue] = _parse_value(s) + if key != None and val != None: + klen: t.CSizeT = string.strlen(key) + kbuf: str = s.pool.alloc(klen + 1) + if kbuf != None: + string.memcpy(kbuf, key, klen + 1) + val.key = kbuf + obj._append_child(val) + + while True: + _skip_whitespace(s) + if s.pos >= s.len: + break + if _peek(s) != ',': + break + _advance(s) + _skip_whitespace(s) + key = _parse_string(s) + _expect(s, ':') + val = _parse_value(s) + if key != None and val != None: + klen2: t.CSizeT = string.strlen(key) + kbuf2: str = s.pool.alloc(klen2 + 1) + if kbuf2 != None: + string.memcpy(kbuf2, key, klen2 + 1) + val.key = kbuf2 + obj._append_child(val) + + _expect(s, '}') + return obj + + +def parse(pool: rawptr[memhub.MemManager], src: str) -> rawptr[JsonValue]: + """解析 JSON 字符串,返回 JsonValue 树""" + if src == None: + return null(pool) + s: rawptr[ParseState] = ParseState(src, pool) + return _parse_value(s) diff --git a/json/__writer.vp b/json/__writer.vp index 8508bc3..5870268 100644 --- a/json/__writer.vp +++ b/json/__writer.vp @@ -1,119 +1,119 @@ -import t, c -from stdint import * -import memhub -import string -from stdio import snprintf -from . import ( - JsonValue, JSON_NULL, JSON_BOOL, JSON_INT, JSON_FLOAT, - JSON_STRING, JSON_ARRAY, JSON_OBJECT, -) - - -def _write_char(buf: str, pos: t.CSizeT, ch: t.CChar) -> t.CSizeT: - buf[pos] = ch - return pos + 1 - -def _write_str(buf: str, pos: t.CSizeT, s: str) -> t.CSizeT: - if s == None: return pos - slen: t.CSizeT = string.strlen(s) - string.memcpy(t.CChar(t.CUInt64T(buf) + pos, t.CPtr), s, slen) - return pos + slen - -def _write_string_escaped(buf: str, pos: t.CSizeT, s: str) -> t.CSizeT: - if s == None: - pos = _write_str(buf, pos, "null") - return pos - pos = _write_char(buf, pos, '"') - i: t.CSizeT = 0 - slen: t.CSizeT = string.strlen(s) - while i < slen: - ch: t.CChar = s[i] - if ch == '"': - pos = _write_str(buf, pos, "\\\"") - elif ch == '\\': - pos = _write_str(buf, pos, "\\\\") - elif ch == '\n': - pos = _write_str(buf, pos, "\\n") - elif ch == '\r': - pos = _write_str(buf, pos, "\\r") - elif ch == '\t': - pos = _write_str(buf, pos, "\\t") - else: - pos = _write_char(buf, pos, ch) - i += 1 - pos = _write_char(buf, pos, '"') - return pos - - -def _write_value(buf: str, pos: t.CSizeT, val: rowptr[JsonValue], pool: rowptr[memhub.MemManager]) -> t.CSizeT: - if val == None: - pos = _write_str(buf, pos, "null") - return pos - - if val.vtype == JSON_NULL: - pos = _write_str(buf, pos, "null") - - elif val.vtype == JSON_BOOL: - if val.bool_val != 0: - pos = _write_str(buf, pos, "true") - else: - pos = _write_str(buf, pos, "false") - - elif val.vtype == JSON_INT: - tmp: str = pool.alloc(32) - if tmp != None: - snprintf(tmp, 32, "%lld", val.int_val) - pos = _write_str(buf, pos, tmp) - - elif val.vtype == JSON_FLOAT: - tmp2: str = pool.alloc(32) - if tmp2 != None: - snprintf(tmp2, 32, "%.6g", val.float_val) - pos = _write_str(buf, pos, tmp2) - - elif val.vtype == JSON_STRING: - pos = _write_string_escaped(buf, pos, val.str_val) - - elif val.vtype == JSON_ARRAY: - if val.child == None: - pos = _write_str(buf, pos, "[]") - return pos - pos = _write_char(buf, pos, '[') - cur: rowptr[JsonValue] = val.child - first: bool = True - while cur != None: - if not first: - pos = _write_char(buf, pos, ',') - pos = _write_value(buf, pos, cur, pool) - first = False - cur = cur.next - pos = _write_char(buf, pos, ']') - - elif val.vtype == JSON_OBJECT: - if val.child == None: - pos = _write_str(buf, pos, "{}") - return pos - pos = _write_char(buf, pos, '{') - cur2: rowptr[JsonValue] = val.child - first2: bool = True - while cur2 != None: - if not first2: - pos = _write_char(buf, pos, ',') - pos = _write_string_escaped(buf, pos, cur2.key) - pos = _write_char(buf, pos, ':') - pos = _write_value(buf, pos, cur2, pool) - first2 = False - cur2 = cur2.next - pos = _write_char(buf, pos, '}') - - return pos - - -def write(pool: rowptr[memhub.MemManager], val: rowptr[JsonValue], pretty: bool) -> str: - """将 JsonValue 序列化为 JSON 字符串""" - buf: str = pool.alloc(4096) - if buf == None: - return None - end_pos: t.CSizeT = _write_value(buf, 0, val, pool) - buf[end_pos] = '\0' - return buf +import t, c +from stdint import * +import memhub +import string +from stdio import snprintf +from . import ( + JsonValue, JSON_NULL, JSON_BOOL, JSON_INT, JSON_FLOAT, + JSON_STRING, JSON_ARRAY, JSON_OBJECT, +) + + +def _write_char(buf: str, pos: t.CSizeT, ch: t.CChar) -> t.CSizeT: + buf[pos] = ch + return pos + 1 + +def _write_str(buf: str, pos: t.CSizeT, s: str) -> t.CSizeT: + if s == None: return pos + slen: t.CSizeT = string.strlen(s) + string.memcpy(t.CChar(t.CUInt64T(buf) + pos, t.CPtr), s, slen) + return pos + slen + +def _write_string_escaped(buf: str, pos: t.CSizeT, s: str) -> t.CSizeT: + if s == None: + pos = _write_str(buf, pos, "null") + return pos + pos = _write_char(buf, pos, '"') + i: t.CSizeT = 0 + slen: t.CSizeT = string.strlen(s) + while i < slen: + ch: t.CChar = s[i] + if ch == '"': + pos = _write_str(buf, pos, "\\\"") + elif ch == '\\': + pos = _write_str(buf, pos, "\\\\") + elif ch == '\n': + pos = _write_str(buf, pos, "\\n") + elif ch == '\r': + pos = _write_str(buf, pos, "\\r") + elif ch == '\t': + pos = _write_str(buf, pos, "\\t") + else: + pos = _write_char(buf, pos, ch) + i += 1 + pos = _write_char(buf, pos, '"') + return pos + + +def _write_value(buf: str, pos: t.CSizeT, val: rawptr[JsonValue], pool: rawptr[memhub.MemManager]) -> t.CSizeT: + if val == None: + pos = _write_str(buf, pos, "null") + return pos + + if val.vtype == JSON_NULL: + pos = _write_str(buf, pos, "null") + + elif val.vtype == JSON_BOOL: + if val.bool_val != 0: + pos = _write_str(buf, pos, "true") + else: + pos = _write_str(buf, pos, "false") + + elif val.vtype == JSON_INT: + tmp: str = pool.alloc(32) + if tmp != None: + snprintf(tmp, 32, "%lld", val.int_val) + pos = _write_str(buf, pos, tmp) + + elif val.vtype == JSON_FLOAT: + tmp2: str = pool.alloc(32) + if tmp2 != None: + snprintf(tmp2, 32, "%.6g", val.float_val) + pos = _write_str(buf, pos, tmp2) + + elif val.vtype == JSON_STRING: + pos = _write_string_escaped(buf, pos, val.str_val) + + elif val.vtype == JSON_ARRAY: + if val.child == None: + pos = _write_str(buf, pos, "[]") + return pos + pos = _write_char(buf, pos, '[') + cur: rawptr[JsonValue] = val.child + first: bool = True + while cur != None: + if not first: + pos = _write_char(buf, pos, ',') + pos = _write_value(buf, pos, cur, pool) + first = False + cur = cur.next + pos = _write_char(buf, pos, ']') + + elif val.vtype == JSON_OBJECT: + if val.child == None: + pos = _write_str(buf, pos, "{}") + return pos + pos = _write_char(buf, pos, '{') + cur2: rawptr[JsonValue] = val.child + first2: bool = True + while cur2 != None: + if not first2: + pos = _write_char(buf, pos, ',') + pos = _write_string_escaped(buf, pos, cur2.key) + pos = _write_char(buf, pos, ':') + pos = _write_value(buf, pos, cur2, pool) + first2 = False + cur2 = cur2.next + pos = _write_char(buf, pos, '}') + + return pos + + +def write(pool: rawptr[memhub.MemManager], val: rawptr[JsonValue], pretty: bool) -> str: + """将 JsonValue 序列化为 JSON 字符串""" + buf: str = pool.alloc(4096) + if buf == None: + return None + end_pos: t.CSizeT = _write_value(buf, 0, val, pool) + buf[end_pos] = '\0' + return buf diff --git a/llvmlite/__builder.vp b/llvmlite/__builder.vp index f200ec0..a295a58 100644 --- a/llvmlite/__builder.vp +++ b/llvmlite/__builder.vp @@ -1,1868 +1,1868 @@ -import t, c -from stdint import * -import string -import viperlib -import memhub -import stdio -import stdlib -from linkedlist import GSListNode, GSList -from .__types import LLVMType, TypePrint -from .__values import Value, new_value, SSAValue -from .__function import Function, BasicBlock, block_append_text, block_set_terminated, block_get_terminated, function_move_block_to_end, _ll_name_needs_quote, block_insert_text_before_terminator - -# ============================================================ -# IRBuilder: 游标式 IR 指令发射器 -# -# 跟踪当前 BasicBlock,将指令文本追加到块的指令链表。 -# 维护递增的 SSA 名计数器(%0, %1, %2, ...)。 -# -# 用法: -# builder = new_builder(pool, func) -# builder.PositionAtEnd(entry_block) -# a = builder.build_alloca(i32_ty) -# b = builder.build_alloca(i32_ty) -# builder.build_store(ConstInt(42), a) -# r = builder.build_load(i32_ty, a) -# builder.build_ret(r) -# -# 注意: 加 @t.NoVTable 以与硬编码 BUILDER_SIZE=32 保持一致 -# (方法以实例方法实现,无虚表;文件末尾的同名顶层函数为一行委托)。 -# ============================================================ -@t.NoVTable -class IRBuilder: - Pool: rowptr[memhub.MemBuddy] # 内存池 - Func: rowptr[Function] # 所属函数 - CurBlock: rowptr[BasicBlock] # 当前插入点 - Counter: int # SSA 名计数器 - - # ============================================================ - # 内部辅助 - # ============================================================ - def _alloc_ssa_name(self) -> str: - """分配一个 SSA 名字符串 "%N" 并递增计数器""" - pool: rowptr[memhub.MemBuddy] = self.Pool - buf: str = pool.alloc(16) - if buf is None: return None - viperlib.snprintf(buf, 16, "%%%d", self.Counter) - self.Counter += 1 - return buf - - - def _type_str(self, ty: rowptr[LLVMType]) -> str: - """获取类型的 IR 文本(从 pool 分配 512 字节缓冲区) - - 注意:复杂结构体类型(如 13 方法的虚表 {i8*,i8*,...})可能超过 64 字节, - 必须使用足够大的缓冲区,否则 '}' 会被截断导致 IR 语法错误。 - """ - pool: rowptr[memhub.MemBuddy] = self.Pool - buf: str = pool.alloc(512) - if buf is None: return None - buf[0] = '\0' - TypePrint(buf, 512, ty, pool) - # [IR-FAIL] 校验护栏:LLVMType 类型对象可能被 MemBuddy 覆盖/悬空 - # (跨遍 reset 回收后 pool 内存复用),此时 TypePrint 输出首字符 - # 变为垃圾(如 't')或为空。立即 fail-fast 终止,防止生成 - # `bitcast i8* null t` 类垃圾 IR,避免 llc 报 "expected 'to' after - # cast value" 后难以定位根因。 - c0: int = buf[0] - if c0 == 0 or (c0 != '%' and c0 != '[' and c0 != '{' and c0 != 'f' and - c0 != 'd' and c0 != 'h' and c0 != 'i' and c0 != 'l' and - c0 != 'm' and c0 != 'v' and c0 != '.'): - stdio.printf("[IR-FAIL] _type_str: LLVMType 对象已损坏 ty=0x%llx 首字符=%d 输出='%s'\n", - t.CUInt64T(ty), c0, buf) - stdlib.exit(1) - return None - return buf - - - def _emit(self, text: str): - """将指令文本追加到当前块""" - if self is None or self.CurBlock is None: return - block_append_text(self.Pool, self.CurBlock, text) - - - def _mark_terminated(self): - """标记当前块已终止""" - if self is None or self.CurBlock is None: return - block_set_terminated(self.CurBlock, 1) - - - def _build_binop(self, op: str, - lhs: rowptr[Value], rhs: rowptr[Value]) -> rowptr[Value]: - """二元运算通用实现""" - if self is None or lhs is None or rhs is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(lhs.Ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = %s %s %s, %s", name, op, ty_s, lhs.Name, rhs.Name) - self._emit(line) - return SSAValue(pool, lhs.Ty, name) - - - # ============================================================ - # 块定位 - # ============================================================ - def position_at_end(self, block: rowptr[BasicBlock]): - """将插入点设为 block 的末尾,并把 block 移到函数块链表末尾。 - - 移动 block 是为了保证 BB 文本输出顺序与控制流(position)顺序一致, - 避免 SSA 名在文本中非单调递增导致 llc 编译失败。 - """ - if self is None: return - self.CurBlock = block - if self.Func is not None: - function_move_block_to_end(self.Func, block) - - - def builder_cur_block_is_terminated(self) -> int: - """检查当前块的 IsTerminated 标志 - - 通过 block_get_terminated 辅助函数访问,避免链式属性 - self.CurBlock.IsTerminated 在跨模块场景下 BasicBlock 为 opaque - 导致的 GEP 静默跳过问题。 - """ - if self is None or self.CurBlock is None: - return 1 - return block_get_terminated(self.CurBlock) - - - # ============================================================ - # 内存指令 - # ============================================================ - def build_alloca(self, ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = alloca - - 返回类型为 ty* 的 SSA 值(默认对齐,由后端决定)。 - 注意:TPC 并行编译时 include 模块的函数默认参数不会预注册(argN 名回退), - 因此本函数不设默认参数;需要显式对齐请用 build_alloca_aligned。 - """ - if self is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(ty) - line: str = pool.alloc(128) - if line is None: return None - viperlib.snprintf(line, 128, "%s = alloca %s", name, ty_s) - self._emit(line) - - # 结果类型是 ty* - result_ty: rowptr[LLVMType] = LLVMType.Ptr(ty) - result_ty_ptr: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if result_ty_ptr is not None: - string.memset(result_ty_ptr, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty_ptr, result_ty) - return SSAValue(pool, result_ty_ptr, name) - - - def build_alloca_aligned(self, ty: rowptr[LLVMType], - align: int) -> rowptr[Value]: - """%N = alloca , align N - - 返回类型为 ty* 的 SSA 值;显式指定对齐(如 va_list 24 字节缓冲区需 16 对齐)。 - align 不设默认值(TPC 并行编译时 include 默认参数不预注册),调用方必须显式传入。 - """ - if self is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(ty) - line: str = pool.alloc(128) - if line is None: return None - viperlib.snprintf(line, 128, "%s = alloca %s, align %d", name, ty_s, align) - self._emit(line) - - # 结果类型是 ty* - result_ty: rowptr[LLVMType] = LLVMType.Ptr(ty) - result_ty_ptr: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if result_ty_ptr is not None: - string.memset(result_ty_ptr, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty_ptr, result_ty) - return SSAValue(pool, result_ty_ptr, name) - - - def build_alloca_at_entry(self, ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = alloca (在函数入口块生成,确保支配性) - - 在入口块的终止指令之前插入 alloca,避免在条件分支内生成 alloca - 导致的支配性违规(Instruction does not dominate all uses)。 - """ - if self is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - func: rowptr[Function] = self.Func - if func is None or func.Blocks is None: - return self.build_alloca(ty) - blocks: rowptr[GSList[BasicBlock]] = func.Blocks - entry_blk: rowptr[BasicBlock] = blocks.Head - if entry_blk is None: - return self.build_alloca(ty) - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(ty) - line_text: str = pool.alloc(128) - if line_text is None: return None - viperlib.snprintf(line_text, 128, "%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 - # 在块未终止时等同追加,已终止时插到终止指令之前。 - block_insert_text_before_terminator(pool, entry_blk, line_text) - # 结果类型是 ty* - result_ty: rowptr[LLVMType] = LLVMType.Ptr(ty) - result_ty_ptr: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if result_ty_ptr is not None: - string.memset(result_ty_ptr, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty_ptr, result_ty) - return SSAValue(pool, result_ty_ptr, name) - - - def build_load(self, ty: rowptr[LLVMType], - ptr: rowptr[Value]) -> rowptr[Value]: - """%N = load , """ - if self is None or ptr is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(ty) - ptr_ty_s: str = self._type_str(ptr.Ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = load %s, %s %s", name, ty_s, ptr_ty_s, ptr.Name) - self._emit(line) - return SSAValue(pool, ty, name) - - - def build_store(self, val: rowptr[Value], ptr: rowptr[Value]): - """store , """ - if self is None or val is None or ptr is None: return - pool: rowptr[memhub.MemBuddy] = self.Pool - val_ty_s: str = self._type_str(val.Ty) - ptr_ty_s: str = self._type_str(ptr.Ty) - line: str = pool.alloc(256) - if line is None: return - viperlib.snprintf(line, 256, "store %s %s, %s %s", val_ty_s, val.Name, ptr_ty_s, ptr.Name) - self._emit(line) - - - # ============================================================ - # 二元运算指令 - # ============================================================ - def build_add(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = add , """ - return self._build_binop("add", lhs, rhs) - - - def build_sub(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = sub , """ - return self._build_binop("sub", lhs, rhs) - - - def build_mul(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = mul , """ - return self._build_binop("mul", lhs, rhs) - - - def build_sdiv(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = sdiv , """ - return self._build_binop("sdiv", lhs, rhs) - - - def build_udiv(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = udiv , """ - return self._build_binop("udiv", lhs, rhs) - - - def build_srem(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = srem , """ - return self._build_binop("srem", lhs, rhs) - - - def build_urem(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = urem , """ - return self._build_binop("urem", lhs, rhs) - - - def build_and(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = and , """ - return self._build_binop("and", lhs, rhs) - - - def build_or(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = or , """ - return self._build_binop("or", lhs, rhs) - - - def build_xor(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = xor , """ - return self._build_binop("xor", lhs, rhs) - - - def build_shl(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = shl , """ - return self._build_binop("shl", lhs, rhs) - - - def build_lshr(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = lshr , """ - return self._build_binop("lshr", lhs, rhs) - - - def build_ashr(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = ashr , """ - return self._build_binop("ashr", lhs, rhs) - - - # ============================================================ - # 浮点二元运算指令 - # ============================================================ - def build_fadd(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fadd , """ - return self._build_binop("fadd", lhs, rhs) - - - def build_fsub(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fsub , """ - return self._build_binop("fsub", lhs, rhs) - - - def build_fmul(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fmul , """ - return self._build_binop("fmul", lhs, rhs) - - - def build_fdiv(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fdiv , """ - return self._build_binop("fdiv", lhs, rhs) - - - def build_frem(self, lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = frem , """ - return self._build_binop("frem", lhs, rhs) - - - def build_fneg(self, val: rowptr[Value]) -> rowptr[Value]: - """%N = fneg """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(val.Ty) - line: str = pool.alloc(128) - if line is None: return None - viperlib.snprintf(line, 128, "%s = fneg %s %s", name, ty_s, val.Name) - self._emit(line) - return SSAValue(pool, val.Ty, name) - - - # ============================================================ - # 比较指令 - # ============================================================ - def build_icmp(self, pred: int, - lhs: rowptr[Value], rhs: rowptr[Value]) -> rowptr[Value]: - """%N = icmp , - - 返回类型为 i1 的 SSA 值 - """ - if self is None or lhs is None or rhs is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(lhs.Ty) - pred_s: str = _icmp_pred_str(pred) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = icmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) - self._emit(line) - - # 结果类型是 i1 - i1_ty: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if i1_ty is not None: - string.memset(i1_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(i1_ty, LLVMType.Int(1)) - return SSAValue(pool, i1_ty, name) - - - def build_fcmp(self, pred: int, - lhs: rowptr[Value], rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fcmp , - - 返回类型为 i1 的 SSA 值 - """ - if self is None or lhs is None or rhs is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(lhs.Ty) - pred_s: str = _fcmp_pred_str(pred) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fcmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) - self._emit(line) - - # 结果类型是 i1 - i1_ty: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if i1_ty is not None: - string.memset(i1_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(i1_ty, LLVMType.Int(1)) - return SSAValue(pool, i1_ty, name) - - - # ============================================================ - # 终止指令 - # ============================================================ - def build_br(self, target: rowptr[BasicBlock]): - """br label %""" - if self is None or target is None: return - pool: rowptr[memhub.MemBuddy] = self.Pool - line: str = pool.alloc(64) - if line is None: return - viperlib.snprintf(line, 64, "br label %%%s", target.Name) - self._emit(line) - self._mark_terminated() - - - def build_cond_br(self, cond: rowptr[Value], - then_blk: rowptr[BasicBlock], else_blk: rowptr[BasicBlock]): - """br i1 , label %, label %""" - if self is None or cond is None or then_blk is None or else_blk is None: return - pool: rowptr[memhub.MemBuddy] = self.Pool - line: str = pool.alloc(128) - if line is None: return - viperlib.snprintf(line, 128, "br i1 %s, label %%%s, label %%%s", - cond.Name, then_blk.Name, else_blk.Name) - self._emit(line) - self._mark_terminated() - - - def build_ret(self, val: rowptr[Value]): - """ret """ - if self is None or val is None: return - pool: rowptr[memhub.MemBuddy] = self.Pool - ty_s: str = self._type_str(val.Ty) - line: str = pool.alloc(128) - if line is None: return - viperlib.snprintf(line, 128, "ret %s %s", ty_s, val.Name) - self._emit(line) - self._mark_terminated() - - - def build_ret_void(self): - """ret void""" - if self is None: return - pool: rowptr[memhub.MemBuddy] = self.Pool - line: str = pool.alloc(16) - if line is None: return - string.strcpy(line, "ret void") - self._emit(line) - self._mark_terminated() - - - # ============================================================ - # 调用指令 - # ============================================================ - def build_call(self, callee: str, - args: rowptr[Value], arg_count: int, - ret_ty: rowptr[LLVMType], - is_variadic: int = 0) -> rowptr[Value]: - """%N = call @() - - args 是 Value 链表头,arg_count 是参数数量。 - 若 ret_ty 是 void,不分配 SSA 名(使用 call 而非 %N = call)。 - is_variadic=1 时发射函数类型签名(如 i32 (i8*, ...) @printf)。 - """ - if self is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - - # 判断返回类型是否为 void - is_void: int = 0 - match ret_ty: - case LLVMType.Void(): - is_void = 1 - case _: - is_void = 0 - - name: str = None - if is_void == 0: - name = self._alloc_ssa_name() - - ret_ty_s: str = self._type_str(ret_ty) - - # 构建参数列表文本 + 变参函数类型签名 - args_buf: str = pool.alloc(1024) - if args_buf is None: return None - args_buf[0] = '\0' - func_ty_buf: str = pool.alloc(1024) - if func_ty_buf is None: return None - func_ty_buf[0] = '\0' - cur: rowptr[Value] = args - first: int = 1 - while cur is not None: - if first == 0: - _append_cstr(args_buf, 1024, ", ") - _append_cstr(func_ty_buf, 1024, ", ") - arg_ty_s: str = self._type_str(cur.Ty) - _append_cstr(args_buf, 1024, arg_ty_s) - _append_cstr(args_buf, 1024, " ") - if cur.Name is not None: - _append_cstr(args_buf, 1024, cur.Name) - _append_cstr(func_ty_buf, 1024, arg_ty_s) - cur = cur.Next - first = 0 - if is_variadic == 1: - if first == 0: - _append_cstr(func_ty_buf, 1024, ", ") - _append_cstr(func_ty_buf, 1024, "...") - - # 构造带引号的 callee 名字(SHA1 前缀名含 '.' 或以数字开头需要加引号) - callee_buf: str = pool.alloc(128) - if callee_buf is None: return None - callee_buf[0] = '\0' - if _ll_name_needs_quote(callee) != 0: - _append_cstr(callee_buf, 128, "\"") - _append_cstr(callee_buf, 128, callee) - _append_cstr(callee_buf, 128, "\"") - else: - _append_cstr(callee_buf, 128, callee) - - line: str = pool.alloc(2048) - if line is None: return None - if is_variadic == 1: - if is_void == 1: - viperlib.snprintf(line, 2048, "call %s (%s) @%s(%s)", ret_ty_s, func_ty_buf, callee_buf, args_buf) - else: - viperlib.snprintf(line, 2048, "%s = call %s (%s) @%s(%s)", name, ret_ty_s, func_ty_buf, callee_buf, args_buf) - else: - if is_void == 1: - viperlib.snprintf(line, 2048, "call %s @%s(%s)", ret_ty_s, callee_buf, args_buf) - else: - viperlib.snprintf(line, 2048, "%s = call %s @%s(%s)", name, ret_ty_s, callee_buf, args_buf) - self._emit(line) - - if is_void == 1: - return None - return SSAValue(pool, ret_ty, name) - - - # ============================================================ - # 间接调用指令: call %callee() - # - # callee_val 是函数指针 SSA 值(必须已是函数指针类型,如 i32(i8*)*) - # ============================================================ - def build_call_indirect(self, callee_val: rowptr[Value], - args: rowptr[Value], arg_count: int, - ret_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = call %callee() - - 间接调用:callee_val 是函数指针 SSA 值。 - """ - if self is None or callee_val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - - is_void: int = 0 - match ret_ty: - case LLVMType.Void(): - is_void = 1 - case _: - is_void = 0 - - name: str = None - if is_void == 0: - name = self._alloc_ssa_name() - - ret_ty_s: str = self._type_str(ret_ty) - - # 构建参数列表文本 - args_buf: str = pool.alloc(1024) - if args_buf is None: return None - args_buf[0] = '\0' - cur: rowptr[Value] = args - first: int = 1 - while cur is not None: - if first == 0: - _append_cstr(args_buf, 1024, ", ") - arg_ty_s: str = self._type_str(cur.Ty) - _append_cstr(args_buf, 1024, arg_ty_s) - _append_cstr(args_buf, 1024, " ") - if cur.Name is not None: - _append_cstr(args_buf, 1024, cur.Name) - cur = cur.Next - first = 0 - - line: str = pool.alloc(2048) - if line is None: return None - if is_void == 1: - viperlib.snprintf(line, 2048, "call %s %s(%s)", ret_ty_s, callee_val.Name, args_buf) - else: - viperlib.snprintf(line, 2048, "%s = call %s %s(%s)", name, ret_ty_s, callee_val.Name, args_buf) - self._emit(line) - - if is_void == 1: - return None - return SSAValue(pool, ret_ty, name) - - - # ============================================================ - # 类型转换指令 - # ============================================================ - def build_bitcast(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = bitcast to """ - if self is None or val is None: - return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - if val.Name is None: - return None - viperlib.snprintf(line, 256, "%s = bitcast %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - ret: rowptr[Value] = SSAValue(pool, to_ty, name) - return ret - - - def build_sext(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = sext to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = sext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_trunc(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = trunc to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = trunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_zext(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = zext to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = zext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_ptrtoint(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = ptrtoint to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = ptrtoint %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_inttoptr(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = inttoptr to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = inttoptr %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - # ============================================================ - # 浮点转换指令 - # ============================================================ - def build_fpext(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = fpext to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fpext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_fptrunc(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = fptrunc to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fptrunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_fp2si(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = fptosi to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fptosi %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_si2fp(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = sitofp to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = sitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_fp2ui(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = fptoui to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = fptoui %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - def build_ui2fp(self, val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = uitofp to """ - if self is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - from_ty_s: str = self._type_str(val.Ty) - to_ty_s: str = self._type_str(to_ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = uitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s) - self._emit(line) - return SSAValue(pool, to_ty, name) - - - # ============================================================ - # GEP 指令 - # ============================================================ - def build_gep(self, elem_ty: rowptr[LLVMType], - ptr: rowptr[Value], idx: rowptr[Value]) -> rowptr[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 编译失败 - if _is_ptr_type(ptr.Ty) == 0: - return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - elem_ty_s: str = self._type_str(elem_ty) - ptr_ty_s: str = self._type_str(ptr.Ty) - idx_ty_s: str = self._type_str(idx.Ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, %s %s", - name, elem_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) - self._emit(line) - - # GEP 结果类型是 elem_ty* - result_ty: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if result_ty is not None: - string.memset(result_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty, LLVMType.Ptr(elem_ty)) - return SSAValue(pool, result_ty, name) - - - # ============================================================ - # build_gep_array — 数组双索引 GEP: getelementptr [N x ty], [N x ty]* %ptr, i32 0, i32 %idx - # ============================================================ - def build_gep_array(self, array_ty: rowptr[LLVMType], - elem_ty: rowptr[LLVMType], - ptr: rowptr[Value], idx: rowptr[Value]) -> rowptr[Value]: - """%N = getelementptr [N x elem_ty], [N x elem_ty]* %ptr, i32 0, i32 %idx - - 结果类型是 elem_ty* - """ - if self is None or ptr is None or idx is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - array_ty_s: str = self._type_str(array_ty) - ptr_ty_s: str = self._type_str(ptr.Ty) - idx_ty_s: str = self._type_str(idx.Ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, i32 0, %s %s", - name, array_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) - self._emit(line) - - # GEP 结果类型是 elem_ty* - result_ty: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if result_ty is not None: - string.memset(result_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty, LLVMType.Ptr(elem_ty)) - return SSAValue(pool, result_ty, name) - - - # ============================================================ - # build_gep_struct — 结构体字段 GEP: getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx - # ============================================================ - def build_gep_struct(self, struct_ty: rowptr[LLVMType], - field_ty: rowptr[LLVMType], - ptr: rowptr[Value], field_idx: int) -> rowptr[Value]: - """%N = getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx - - 结果类型是 field_ty* - """ - if self is None or ptr is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - struct_ty_s: str = self._type_str(struct_ty) - ptr_ty_s: str = self._type_str(ptr.Ty) - line: str = pool.alloc(2048) - if line is None: - stdio.printf("[BGS] line alloc None\n") - return None - viperlib.snprintf(line, 2048, "%s = getelementptr %s, %s %s, i32 0, i32 %d", - name, struct_ty_s, ptr_ty_s, ptr.Name, field_idx) - self._emit(line) - - # GEP 结果类型是 field_ty* - result_ty: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if result_ty is None: - stdio.printf("[BGS] result_ty alloc None\n") - else: - string.memset(result_ty, 0, LLVMType.__sizeof__()) - c.DerefAs(result_ty, LLVMType.Ptr(field_ty)) - rv: rowptr[Value] = SSAValue(pool, result_ty, name) - if rv is None: - stdio.printf("[BGS] SSAValue None\n") - return rv - - - # ============================================================ - # Phi 指令 - # ============================================================ - def build_phi(self, ty: rowptr[LLVMType], - incoming_head: rowptr[PhiIncoming], count: int) -> rowptr[Value]: - """%N = phi [val1, %blk1], [val2, %blk2], ... - - incoming_head 是 PhiIncoming 链表头,count 是入边数量。 - """ - if self is None: return None - pool: rowptr[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),需要大缓冲区 - parts: str = pool.alloc(4096) - if parts is None: return None - parts[0] = '\0' - cur: rowptr[PhiIncoming] = incoming_head - first: int = 1 - while cur is not None: - if first == 0: - _append_cstr(parts, 4096, ", ") - _append_cstr(parts, 4096, "[") - # 使用中间变量避免链式结构体成员访问被编译器丢弃 - inc_val: rowptr[Value] = cur.Val - if inc_val is not None: - inc_val_name: str = inc_val.Name - if inc_val_name is not None: - _append_cstr(parts, 4096, inc_val_name) - _append_cstr(parts, 4096, ", %") - inc_blk: rowptr[BasicBlock] = cur.Block - if inc_blk is not None: - inc_blk_name: str = inc_blk.Name - if inc_blk_name is not None: - _append_cstr(parts, 4096, inc_blk_name) - _append_cstr(parts, 4096, "]") - cur = cur.Next - first = 0 - - line: str = pool.alloc(8192) - if line is None: return None - viperlib.snprintf(line, 8192, "%s = phi %s %s", name, ty_s, parts) - self._emit(line) - return SSAValue(pool, ty, name) - - - # ============================================================ - # Switch 指令 - # ============================================================ - def build_switch(self, val: rowptr[Value], - default_blk: rowptr[BasicBlock], - cases_head: rowptr[SwitchCase], case_count: int): - """switch , label % [, label % ...]""" - if self is None or val is None or default_blk is None: return - pool: rowptr[memhub.MemBuddy] = self.Pool - ty_s: str = self._type_str(val.Ty) - - # 构建主体: switch , label %default - line: str = pool.alloc(512) - if line is None: return - viperlib.snprintf(line, 512, "switch %s %s, label %%%s", - ty_s, val.Name, default_blk.Name) - _append_cstr(line, 512, " [") - _append_cstr(line, 512, "\n") - - # 遍历 case 列表 - cur: rowptr[SwitchCase] = cases_head - case_ty_s: str = self._type_str(val.Ty) - while cur is not None: - case_line: str = pool.alloc(128) - if case_line is not None: - # 使用中间变量避免链式结构体成员访问被编译器丢弃 - cv: rowptr[Value] = cur.CaseVal - cb: rowptr[BasicBlock] = cur.Block - if cv is not None and cb is not None: - cv_name: str = cv.Name - cb_name: str = cb.Name - if cv_name is not None and cb_name is not None: - viperlib.snprintf(case_line, 128, " %s %s, label %%%s\n", - case_ty_s, cv_name, cb_name) - _append_cstr(line, 512, case_line) - cur = cur.Next - - _append_cstr(line, 512, "]") - self._emit(line) - self._mark_terminated() - - - # ============================================================ - # Select 指令 - # ============================================================ - def build_select(self, cond: rowptr[Value], - true_val: rowptr[Value], false_val: rowptr[Value]) -> rowptr[Value]: - """%N = select i1 , , """ - if self is None or cond is None or true_val is None or false_val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ty_s: str = self._type_str(true_val.Ty) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = select i1 %s, %s %s, %s %s", - name, cond.Name, ty_s, true_val.Name, ty_s, false_val.Name) - self._emit(line) - return SSAValue(pool, true_val.Ty, name) - - - # ============================================================ - # Unreachable 指令 - # ============================================================ - def build_unreachable(self): - """unreachable""" - if self is None: return - pool: rowptr[memhub.MemBuddy] = self.Pool - line: str = pool.alloc(16) - if line is None: return - string.strcpy(line, "unreachable") - self._emit(line) - self._mark_terminated() - - - # ============================================================ - # 原子指令 (Phase 3) - # ============================================================ - def build_atomicrmw(self, op: int, - ptr: rowptr[Value], val: rowptr[Value], - order: int) -> rowptr[Value]: - """%N = atomicrmw , """ - if self is None or ptr is None or val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ptr_ty_s: str = self._type_str(ptr.Ty) - val_ty_s: str = self._type_str(val.Ty) - op_s: str = _atomic_op_str(op) - ord_s: str = _atomic_order_str(order) - line: str = pool.alloc(256) - if line is None: return None - viperlib.snprintf(line, 256, "%s = atomicrmw %s %s %s, %s %s %s", - name, op_s, ptr_ty_s, ptr.Name, val_ty_s, val.Name, ord_s) - self._emit(line) - return SSAValue(pool, val.Ty, name) - - - def build_cmpxchg(self, ptr: rowptr[Value], - cmp_val: rowptr[Value], new_val: rowptr[Value], - succ_order: int, fail_order: int) -> rowptr[Value]: - """%N = cmpxchg , , - - 返回 {, i1} 类型的 SSA 值(简化为返回 i64 表示的值对) - """ - if self is None or ptr is None or cmp_val is None or new_val is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - ptr_ty_s: str = self._type_str(ptr.Ty) - cmp_ty_s: str = self._type_str(cmp_val.Ty) - new_ty_s: str = self._type_str(new_val.Ty) - succ_s: str = _atomic_order_str(succ_order) - fail_s: str = _atomic_order_str(fail_order) - line: str = pool.alloc(384) - if line is None: return None - viperlib.snprintf(line, 384, "%s = cmpxchg %s %s, %s %s, %s %s %s %s", - 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 SSAValue(pool, new_val.Ty, name) - - - # ============================================================ - # va_arg 指令: 从 va_list 读取下一个变长参数 - # ============================================================ - def build_va_arg(self, - va_list_ptr: rowptr[Value], - arg_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = va_arg , """ - if self is None or va_list_ptr is None or arg_ty is None: - return None - pool: rowptr[memhub.MemBuddy] = self.Pool - name: str = self._alloc_ssa_name() - va_list_ty_s: str = self._type_str(va_list_ptr.Ty) - arg_ty_s: str = self._type_str(arg_ty) - line: str = pool.alloc(256) - if line is None: return None - if va_list_ptr.Name is None: - return None - viperlib.snprintf(line, 256, "%s = va_arg %s %s, %s", - name, va_list_ty_s, va_list_ptr.Name, arg_ty_s) - self._emit(line) - return SSAValue(pool, arg_ty, name) - - - # ============================================================ - # 内联汇编 (Phase 3) - # ============================================================ - def build_inline_asm(self, ret_ty: rowptr[LLVMType], - asm_text: str, constraints: str, - has_side_effects: int) -> rowptr[Value]: - """%N = call asm [sideeffect] "" ""() - - 简化的内联汇编调用,无参数版本。 - """ - if self is None: return None - pool: rowptr[memhub.MemBuddy] = self.Pool - - # 判断返回类型是否为 void - is_void: int = 0 - match ret_ty: - case LLVMType.Void(): - is_void = 1 - case _: - is_void = 0 - - name: str = None - if is_void == 0: - name = self._alloc_ssa_name() - - ret_ty_s: str = self._type_str(ret_ty) - line: str = pool.alloc(512) - if line is None: return None - - if has_side_effects != 0: - if is_void == 1: - viperlib.snprintf(line, 512, "call %s asm sideeffect \"%s\", \"%s\"()", - ret_ty_s, asm_text, constraints) - else: - viperlib.snprintf(line, 512, "%s = call %s asm sideeffect \"%s\", \"%s\"()", - name, ret_ty_s, asm_text, constraints) - else: - if is_void == 1: - viperlib.snprintf(line, 512, "call %s asm \"%s\", \"%s\"()", - ret_ty_s, asm_text, constraints) - else: - viperlib.snprintf(line, 512, "%s = call %s asm \"%s\", \"%s\"()", - name, ret_ty_s, asm_text, constraints) - self._emit(line) - - if is_void == 1: - return None - return SSAValue(pool, ret_ty, name) - - -# ============================================================ -# 工厂函数 -# ============================================================ -# IRBuilder 硬编码大小: Pool(8) + Func(8) + CurBlock(8) + Counter(4) = 32 字节 -BUILDER_SIZE: t.CDefine = 32 - -def new_builder(pool: rowptr[memhub.MemBuddy], func: rowptr[Function]) -> rowptr[IRBuilder]: - """创建 IRBuilder,关联到函数 func""" - ptr: rowptr[IRBuilder] = pool.alloc(BUILDER_SIZE) - if ptr is None: return None - string.memset(ptr, 0, BUILDER_SIZE) - ptr.Pool = pool - ptr.Func = func - ptr.CurBlock = None - ptr.Counter = 0 - return ptr - - -# ============================================================ -# 块定位 -# ============================================================ -def position_at_end(builder: rowptr[IRBuilder], block: rowptr[BasicBlock]): - """将插入点设为 block 的末尾,并把 block 移到函数块链表末尾。 - - 移动 block 是为了保证 BB 文本输出顺序与控制流(position)顺序一致, - 避免 SSA 名在文本中非单调递增导致 llc 编译失败。 - """ - builder.position_at_end(block) - - -# ============================================================ -# 内部辅助 -# ============================================================ -def _alloc_ssa_name(builder: rowptr[IRBuilder]) -> str: - """分配一个 SSA 名字符串 "%N" 并递增计数器""" - return builder._alloc_ssa_name() - - -def _type_str(builder: rowptr[IRBuilder], ty: rowptr[LLVMType]) -> str: - """获取类型的 IR 文本(从 pool 分配 512 字节缓冲区) - - 注意:复杂结构体类型(如 13 方法的虚表 {i8*,i8*,...})可能超过 64 字节, - 必须使用足够大的缓冲区,否则 '}' 会被截断导致 IR 语法错误。 - """ - return builder._type_str(ty) - - -def _emit(builder: rowptr[IRBuilder], text: str): - """将指令文本追加到当前块""" - builder._emit(text) - - -def _mark_terminated(builder: rowptr[IRBuilder]): - """标记当前块已终止""" - builder._mark_terminated() - - -def builder_cur_block_is_terminated(builder: rowptr[IRBuilder]) -> int: - """检查当前块的 IsTerminated 标志 - - 通过 block_get_terminated 辅助函数访问,避免链式属性 - builder.CurBlock.IsTerminated 在跨模块场景下 BasicBlock 为 opaque - 导致的 GEP 静默跳过问题。 - """ - return builder.builder_cur_block_is_terminated() - - -# ============================================================ -# 内存指令 -# ============================================================ -def build_alloca(builder: rowptr[IRBuilder], ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = alloca - - 返回类型为 ty* 的 SSA 值(默认对齐,由后端决定)。 - 注意:TPC 并行编译时 include 模块的函数默认参数不会预注册(argN 名回退), - 因此本函数不设默认参数;需要显式对齐请用 build_alloca_aligned。 - """ - return builder.build_alloca(ty) - - -def build_alloca_aligned(builder: rowptr[IRBuilder], ty: rowptr[LLVMType], - align: int) -> rowptr[Value]: - """%N = alloca , align N - - 返回类型为 ty* 的 SSA 值;显式指定对齐(如 va_list 24 字节缓冲区需 16 对齐)。 - align 不设默认值(TPC 并行编译时 include 默认参数不预注册),调用方必须显式传入。 - """ - return builder.build_alloca_aligned(ty, align) - - -def build_alloca_at_entry(builder: rowptr[IRBuilder], ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = alloca (在函数入口块生成,确保支配性) - - 在入口块的终止指令之前插入 alloca,避免在条件分支内生成 alloca - 导致的支配性违规(Instruction does not dominate all uses)。 - """ - return builder.build_alloca_at_entry(ty) - - -def build_load(builder: rowptr[IRBuilder], ty: rowptr[LLVMType], - ptr: rowptr[Value]) -> rowptr[Value]: - """%N = load , """ - return builder.build_load(ty, ptr) - - -def build_store(builder: rowptr[IRBuilder], val: rowptr[Value], ptr: rowptr[Value]): - """store , """ - builder.build_store(val, ptr) - - -# ============================================================ -# 二元运算指令 -# ============================================================ -def build_add(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = add , """ - return builder.build_add(lhs, rhs) - - -def build_sub(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = sub , """ - return builder.build_sub(lhs, rhs) - - -def build_mul(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = mul , """ - return builder.build_mul(lhs, rhs) - - -def build_sdiv(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = sdiv , """ - return builder.build_sdiv(lhs, rhs) - - -def build_udiv(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = udiv , """ - return builder.build_udiv(lhs, rhs) - - -def build_srem(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = srem , """ - return builder.build_srem(lhs, rhs) - - -def build_urem(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = urem , """ - return builder.build_urem(lhs, rhs) - - -def build_and(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = and , """ - return builder.build_and(lhs, rhs) - - -def build_or(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = or , """ - return builder.build_or(lhs, rhs) - - -def build_xor(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = xor , """ - return builder.build_xor(lhs, rhs) - - -def build_shl(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = shl , """ - return builder.build_shl(lhs, rhs) - - -def build_lshr(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = lshr , """ - return builder.build_lshr(lhs, rhs) - - -def build_ashr(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = ashr , """ - return builder.build_ashr(lhs, rhs) - - -# ============================================================ -# 浮点二元运算指令 -# ============================================================ -def build_fadd(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fadd , """ - return builder.build_fadd(lhs, rhs) - - -def build_fsub(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fsub , """ - return builder.build_fsub(lhs, rhs) - - -def build_fmul(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fmul , """ - return builder.build_fmul(lhs, rhs) - - -def build_fdiv(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fdiv , """ - return builder.build_fdiv(lhs, rhs) - - -def build_frem(builder: rowptr[IRBuilder], lhs: rowptr[Value], - rhs: rowptr[Value]) -> rowptr[Value]: - """%N = frem , """ - return builder.build_frem(lhs, rhs) - - -def build_fneg(builder: rowptr[IRBuilder], val: rowptr[Value]) -> rowptr[Value]: - """%N = fneg """ - return builder.build_fneg(val) - - -def _build_binop(builder: rowptr[IRBuilder], op: str, - lhs: rowptr[Value], rhs: rowptr[Value]) -> rowptr[Value]: - """二元运算通用实现""" - return builder._build_binop(op, lhs, rhs) - - -# ============================================================ -# 比较指令 -# ============================================================ -# ICMP 谓词常量 -ICMP_EQ: t.CDefine = 0 -ICMP_NE: t.CDefine = 1 -ICMP_SGT: t.CDefine = 2 -ICMP_SGE: t.CDefine = 3 -ICMP_SLT: t.CDefine = 4 -ICMP_SLE: t.CDefine = 5 -ICMP_UGT: t.CDefine = 6 -ICMP_UGE: t.CDefine = 7 -ICMP_ULT: t.CDefine = 8 -ICMP_ULE: t.CDefine = 9 - -# FCMP 谓词常量 -FCMP_FALSE: t.CDefine = 0 -FCMP_OEQ: t.CDefine = 1 -FCMP_OGT: t.CDefine = 2 -FCMP_OGE: t.CDefine = 3 -FCMP_OLT: t.CDefine = 4 -FCMP_OLE: t.CDefine = 5 -FCMP_ONE: t.CDefine = 6 -FCMP_ORD: t.CDefine = 7 -FCMP_UNO: t.CDefine = 8 -FCMP_UEQ: t.CDefine = 9 -FCMP_UGT: t.CDefine = 10 -FCMP_UGE: t.CDefine = 11 -FCMP_ULT: t.CDefine = 12 -FCMP_ULE: t.CDefine = 13 -FCMP_UNE: t.CDefine = 14 -FCMP_TRUE: t.CDefine = 15 - - -def _icmp_pred_str(pred: int) -> str: - if pred == ICMP_EQ: return "eq" - if pred == ICMP_NE: return "ne" - if pred == ICMP_SGT: return "sgt" - if pred == ICMP_SGE: return "sge" - if pred == ICMP_SLT: return "slt" - if pred == ICMP_SLE: return "sle" - if pred == ICMP_UGT: return "ugt" - if pred == ICMP_UGE: return "uge" - if pred == ICMP_ULT: return "ult" - if pred == ICMP_ULE: return "ule" - return "eq" - - -def _fcmp_pred_str(pred: int) -> str: - if pred == FCMP_FALSE: return "false" - if pred == FCMP_OEQ: return "oeq" - if pred == FCMP_OGT: return "ogt" - if pred == FCMP_OGE: return "oge" - if pred == FCMP_OLT: return "olt" - if pred == FCMP_OLE: return "ole" - if pred == FCMP_ONE: return "one" - if pred == FCMP_ORD: return "ord" - if pred == FCMP_UNO: return "uno" - if pred == FCMP_UEQ: return "ueq" - if pred == FCMP_UGT: return "ugt" - if pred == FCMP_UGE: return "uge" - if pred == FCMP_ULT: return "ult" - if pred == FCMP_ULE: return "ule" - if pred == FCMP_UNE: return "une" - if pred == FCMP_TRUE: return "true" - return "oeq" - - -def build_icmp(builder: rowptr[IRBuilder], pred: int, - lhs: rowptr[Value], rhs: rowptr[Value]) -> rowptr[Value]: - """%N = icmp , - - 返回类型为 i1 的 SSA 值 - """ - return builder.build_icmp(pred, lhs, rhs) - - -def build_fcmp(builder: rowptr[IRBuilder], pred: int, - lhs: rowptr[Value], rhs: rowptr[Value]) -> rowptr[Value]: - """%N = fcmp , - - 返回类型为 i1 的 SSA 值 - """ - return builder.build_fcmp(pred, lhs, rhs) - - -# ============================================================ -# 终止指令 -# ============================================================ -def build_br(builder: rowptr[IRBuilder], target: rowptr[BasicBlock]): - """br label %""" - builder.build_br(target) - - -def build_cond_br(builder: rowptr[IRBuilder], cond: rowptr[Value], - then_blk: rowptr[BasicBlock], else_blk: rowptr[BasicBlock]): - """br i1 , label %, label %""" - builder.build_cond_br(cond, then_blk, else_blk) - - -def build_ret(builder: rowptr[IRBuilder], val: rowptr[Value]): - """ret """ - builder.build_ret(val) - - -def build_ret_void(builder: rowptr[IRBuilder]): - """ret void""" - builder.build_ret_void() - - -# ============================================================ -# 调用指令 -# ============================================================ -def build_call(builder: rowptr[IRBuilder], callee: str, - args: rowptr[Value], arg_count: int, - ret_ty: rowptr[LLVMType], - is_variadic: int = 0) -> rowptr[Value]: - """%N = call @() - - args 是 Value 链表头,arg_count 是参数数量。 - 若 ret_ty 是 void,不分配 SSA 名(使用 call 而非 %N = call)。 - is_variadic=1 时发射函数类型签名(如 i32 (i8*, ...) @printf)。 - """ - return builder.build_call(callee, args, arg_count, ret_ty, is_variadic) - - -# ============================================================ -# 间接调用指令: call %callee() -# -# callee_val 是函数指针 SSA 值(必须已是函数指针类型,如 i32(i8*)*) -# ============================================================ -def build_call_indirect(builder: rowptr[IRBuilder], callee_val: rowptr[Value], - args: rowptr[Value], arg_count: int, - ret_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = call %callee() - - 间接调用:callee_val 是函数指针 SSA 值。 - """ - return builder.build_call_indirect(callee_val, args, arg_count, ret_ty) - - -# ============================================================ -# 类型转换指令 -# ============================================================ -def build_bitcast(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = bitcast to """ - return builder.build_bitcast(val, to_ty) - - -def build_sext(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = sext to """ - return builder.build_sext(val, to_ty) - - -def build_trunc(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = trunc to """ - return builder.build_trunc(val, to_ty) - - -def build_zext(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = zext to """ - return builder.build_zext(val, to_ty) - - -def build_ptrtoint(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = ptrtoint to """ - return builder.build_ptrtoint(val, to_ty) - - -def build_inttoptr(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = inttoptr to """ - return builder.build_inttoptr(val, to_ty) - - -# ============================================================ -# 浮点转换指令 -# ============================================================ -def build_fpext(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = fpext to """ - return builder.build_fpext(val, to_ty) - - -def build_fptrunc(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = fptrunc to """ - return builder.build_fptrunc(val, to_ty) - - -def build_fp2si(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = fptosi to """ - return builder.build_fp2si(val, to_ty) - - -def build_si2fp(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = sitofp to """ - return builder.build_si2fp(val, to_ty) - - -def build_fp2ui(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = fptoui to """ - return builder.build_fp2ui(val, to_ty) - - -def build_ui2fp(builder: rowptr[IRBuilder], val: rowptr[Value], - to_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = uitofp to """ - return builder.build_ui2fp(val, to_ty) - - -# ============================================================ -# GEP 指令 -# ============================================================ -def _is_ptr_type(ty: rowptr[LLVMType]) -> int: - """检查 ty 是否是 Ptr 类型(独立函数,避免嵌套 match 的编译器 BUG)""" - if ty is None: - return 0 - match ty: - case LLVMType.Ptr(pointee): - return 1 - case _: - return 0 - - -def build_gep(builder: rowptr[IRBuilder], elem_ty: rowptr[LLVMType], - ptr: rowptr[Value], idx: rowptr[Value]) -> rowptr[Value]: - """%N = getelementptr , , """ - return builder.build_gep(elem_ty, ptr, idx) - - -# ============================================================ -# build_gep_array — 数组双索引 GEP: getelementptr [N x ty], [N x ty]* %ptr, i32 0, i32 %idx -# ============================================================ -def build_gep_array(builder: rowptr[IRBuilder], array_ty: rowptr[LLVMType], - elem_ty: rowptr[LLVMType], - ptr: rowptr[Value], idx: rowptr[Value]) -> rowptr[Value]: - """%N = getelementptr [N x elem_ty], [N x elem_ty]* %ptr, i32 0, i32 %idx - - 结果类型是 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 -# ============================================================ -def build_gep_struct(builder: rowptr[IRBuilder], struct_ty: rowptr[LLVMType], - field_ty: rowptr[LLVMType], - ptr: rowptr[Value], field_idx: int) -> rowptr[Value]: - """%N = getelementptr struct_ty, struct_ty* %ptr, i32 0, i32 %field_idx - - 结果类型是 field_ty* - """ - return builder.build_gep_struct(struct_ty, field_ty, ptr, field_idx) - - -# ============================================================ -# PhiIncoming: phi 节点的入边 (value, block) 对 -# 继承 GSListNode[PhiIncoming] 获取强类型 Next -# -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 -# ============================================================ -@t.NoVTable -class PhiIncoming(GSListNode[PhiIncoming]): - Val: rowptr[Value] - Block: rowptr[BasicBlock] - - -# PhiIncoming 硬编码大小: Next(8) + Val(8) + Block(8) = 24 字节 -PHIINC_SIZE: t.CDefine = 24 - -def new_phi_incoming(pool: rowptr[memhub.MemBuddy], - val: rowptr[Value], block: rowptr[BasicBlock]) -> rowptr[PhiIncoming]: - ptr: rowptr[PhiIncoming] = pool.alloc(PHIINC_SIZE) - if ptr is None: return None - string.memset(ptr, 0, PHIINC_SIZE) - ptr.Val = val - ptr.Block = block - return ptr - - -# ============================================================ -# Phi 指令 -# ============================================================ -def build_phi(builder: rowptr[IRBuilder], ty: rowptr[LLVMType], - incoming_head: rowptr[PhiIncoming], count: int) -> rowptr[Value]: - """%N = phi [val1, %blk1], [val2, %blk2], ... - - incoming_head 是 PhiIncoming 链表头,count 是入边数量。 - """ - return builder.build_phi(ty, incoming_head, count) - - -# ============================================================ -# SwitchCase: switch 指令的 case 分支 -# -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 -# ============================================================ -@t.NoVTable -class SwitchCase(GSListNode[SwitchCase]): - CaseVal: rowptr[Value] - Block: rowptr[BasicBlock] - - -# SwitchCase 硬编码大小: Next(8) + CaseVal(8) + Block(8) = 24 字节 -SWCASE_SIZE: t.CDefine = 24 - -def new_switch_case(pool: rowptr[memhub.MemBuddy], - case_val: rowptr[Value], block: rowptr[BasicBlock]) -> rowptr[SwitchCase]: - ptr: rowptr[SwitchCase] = pool.alloc(SWCASE_SIZE) - if ptr is None: return None - string.memset(ptr, 0, SWCASE_SIZE) - ptr.CaseVal = case_val - ptr.Block = block - return ptr - - -# ============================================================ -# Switch 指令 -# ============================================================ -def build_switch(builder: rowptr[IRBuilder], val: rowptr[Value], - default_blk: rowptr[BasicBlock], - cases_head: rowptr[SwitchCase], case_count: int): - """switch , label % [, label % ...]""" - builder.build_switch(val, default_blk, cases_head, case_count) - - -# ============================================================ -# Select 指令 -# ============================================================ -def build_select(builder: rowptr[IRBuilder], cond: rowptr[Value], - true_val: rowptr[Value], false_val: rowptr[Value]) -> rowptr[Value]: - """%N = select i1 , , """ - return builder.build_select(cond, true_val, false_val) - - -# ============================================================ -# Unreachable 指令 -# ============================================================ -def build_unreachable(builder: rowptr[IRBuilder]): - """unreachable""" - builder.build_unreachable() - - -# ============================================================ -# 原子指令 (Phase 3) -# ============================================================ -# AtomicRMW 操作常量 -ATOMIC_XCHG: t.CDefine = 0 -ATOMIC_ADD: t.CDefine = 1 -ATOMIC_SUB: t.CDefine = 2 -ATOMIC_AND: t.CDefine = 3 -ATOMIC_NAND: t.CDefine = 4 -ATOMIC_OR: t.CDefine = 5 -ATOMIC_XOR: t.CDefine = 6 -ATOMIC_MAX: t.CDefine = 7 -ATOMIC_MIN: t.CDefine = 8 -ATOMIC_UMAX: t.CDefine = 9 -ATOMIC_UMIN: t.CDefine = 10 - -# 内存序常量 -ATOMIC_ORDER_NOTATOMIC: t.CDefine = 0 -ATOMIC_ORDER_UNORDERED: t.CDefine = 1 -ATOMIC_ORDER_MONOTONIC: t.CDefine = 2 -ATOMIC_ORDER_ACQUIRE: t.CDefine = 3 -ATOMIC_ORDER_RELEASE: t.CDefine = 4 -ATOMIC_ORDER_ACQ_REL: t.CDefine = 5 -ATOMIC_ORDER_SEQ_CST: t.CDefine = 6 - - -def _atomic_op_str(op: int) -> str: - if op == ATOMIC_XCHG: return "xchg" - if op == ATOMIC_ADD: return "add" - if op == ATOMIC_SUB: return "sub" - if op == ATOMIC_AND: return "and" - if op == ATOMIC_NAND: return "nand" - if op == ATOMIC_OR: return "or" - if op == ATOMIC_XOR: return "xor" - if op == ATOMIC_MAX: return "max" - if op == ATOMIC_MIN: return "min" - if op == ATOMIC_UMAX: return "umax" - if op == ATOMIC_UMIN: return "umin" - return "xchg" - - -def _atomic_order_str(ord: int) -> str: - if ord == ATOMIC_ORDER_UNORDERED: return "unordered" - if ord == ATOMIC_ORDER_MONOTONIC: return "monotonic" - if ord == ATOMIC_ORDER_ACQUIRE: return "acquire" - if ord == ATOMIC_ORDER_RELEASE: return "release" - if ord == ATOMIC_ORDER_ACQ_REL: return "acq_rel" - if ord == ATOMIC_ORDER_SEQ_CST: return "seq_cst" - return "monotonic" - - -def build_atomicrmw(builder: rowptr[IRBuilder], op: int, - ptr: rowptr[Value], val: rowptr[Value], - order: int) -> rowptr[Value]: - """%N = atomicrmw , """ - return builder.build_atomicrmw(op, ptr, val, order) - - -def build_cmpxchg(builder: rowptr[IRBuilder], ptr: rowptr[Value], - cmp_val: rowptr[Value], new_val: rowptr[Value], - succ_order: int, fail_order: int) -> rowptr[Value]: - """%N = cmpxchg , , - - 返回 {, i1} 类型的 SSA 值(简化为返回 i64 表示的值对) - """ - return builder.build_cmpxchg(ptr, cmp_val, new_val, succ_order, fail_order) - - -# ============================================================ -# va_arg 指令: 从 va_list 读取下一个变长参数 -# ============================================================ -def build_va_arg(builder: rowptr[IRBuilder], - va_list_ptr: rowptr[Value], - arg_ty: rowptr[LLVMType]) -> rowptr[Value]: - """%N = va_arg , """ - return builder.build_va_arg(va_list_ptr, arg_ty) - - -# ============================================================ -# 内联汇编 (Phase 3) -# ============================================================ -def build_inline_asm(builder: rowptr[IRBuilder], ret_ty: rowptr[LLVMType], - asm_text: str, constraints: str, - has_side_effects: int) -> rowptr[Value]: - """%N = call asm [sideeffect] "" ""() - - 简化的内联汇编调用,无参数版本。 - """ - return builder.build_inline_asm(ret_ty, asm_text, constraints, has_side_effects) - - -# ============================================================ -# 内部辅助 -# ============================================================ -# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, -# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 -# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 -_g_last_dst: t.CUInt64T = 0 -_g_last_pos: t.CSizeT = 0 - -def _append_cstr(dst: str, dst_size: t.CSizeT, src: str): - """将 C 字符串 src 追加到 dst 末尾""" - global _g_last_dst, _g_last_pos - if dst is None or src is None: return - dlen: t.CSizeT = 0 - if _g_last_dst == t.CUInt64T(dst): - dlen = _g_last_pos - # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 - if dlen > 0 and dst[0] == '\0': - dlen = 0 - # 缓存一致性校验(同 __module/__function 版):缓存 pos 过期会追加到首个 NUL - # 之后使内容不可见,校验失败回退 strlen;正常热路径仍 O(1)。 - if dlen == 0: - if dst[0] != '\0': - dlen = string.strlen(dst) - elif dst[dlen] != '\0' or dst[dlen - 1] == '\0': - dlen = string.strlen(dst) - else: - dlen = string.strlen(dst) - _g_last_dst = t.CUInt64T(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 - if dlen >= dst_size: - return - slen: t.CSizeT = string.strlen(src) - remain: t.CSizeT = dst_size - dlen - if remain <= 0: return - i: t.CSizeT = 0 - while i < slen and i + 1 < remain: - dst[dlen + i] = src[i] - i += 1 - dst[dlen + i] = '\0' - _g_last_pos = dlen + i +import t, c +from stdint import * +import string +import viperlib +import memhub +import stdio +import stdlib +from linkedlist import GSListNode, GSList +from .__types import LLVMType, TypePrint +from .__values import Value, new_value, SSAValue +from .__function import Function, BasicBlock, block_append_text, block_set_terminated, block_get_terminated, function_move_block_to_end, _ll_name_needs_quote, block_insert_text_before_terminator + +# ============================================================ +# IRBuilder: 游标式 IR 指令发射器 +# +# 跟踪当前 BasicBlock,将指令文本追加到块的指令链表。 +# 维护递增的 SSA 名计数器(%0, %1, %2, ...)。 +# +# 用法: +# builder = new_builder(pool, func) +# builder.PositionAtEnd(entry_block) +# a = builder.build_alloca(i32_ty) +# b = builder.build_alloca(i32_ty) +# builder.build_store(ConstInt(42), a) +# r = builder.build_load(i32_ty, a) +# builder.build_ret(r) +# +# 注意: 加 @t.NoVTable 以与硬编码 BUILDER_SIZE=32 保持一致 +# (方法以实例方法实现,无虚表;文件末尾的同名顶层函数为一行委托)。 +# ============================================================ +@t.NoVTable +class IRBuilder: + Pool: rawptr[memhub.MemBuddy] # 内存池 + Func: rawptr[Function] # 所属函数 + CurBlock: rawptr[BasicBlock] # 当前插入点 + Counter: int # SSA 名计数器 + + # ============================================================ + # 内部辅助 + # ============================================================ + def _alloc_ssa_name(self) -> str: + """分配一个 SSA 名字符串 "%N" 并递增计数器""" + pool: rawptr[memhub.MemBuddy] = self.Pool + buf: str = pool.alloc(16) + if buf is None: return None + viperlib.snprintf(buf, 16, "%%%d", self.Counter) + self.Counter += 1 + return buf + + + def _type_str(self, ty: rawptr[LLVMType]) -> str: + """获取类型的 IR 文本(从 pool 分配 512 字节缓冲区) + + 注意:复杂结构体类型(如 13 方法的虚表 {i8*,i8*,...})可能超过 64 字节, + 必须使用足够大的缓冲区,否则 '}' 会被截断导致 IR 语法错误。 + """ + pool: rawptr[memhub.MemBuddy] = self.Pool + buf: str = pool.alloc(512) + if buf is None: return None + buf[0] = '\0' + TypePrint(buf, 512, ty, pool) + # [IR-FAIL] 校验护栏:LLVMType 类型对象可能被 MemBuddy 覆盖/悬空 + # (跨遍 reset 回收后 pool 内存复用),此时 TypePrint 输出首字符 + # 变为垃圾(如 't')或为空。立即 fail-fast 终止,防止生成 + # `bitcast i8* null t` 类垃圾 IR,避免 llc 报 "expected 'to' after + # cast value" 后难以定位根因。 + c0: int = buf[0] + if c0 == 0 or (c0 != '%' and c0 != '[' and c0 != '{' and c0 != 'f' and + c0 != 'd' and c0 != 'h' and c0 != 'i' and c0 != 'l' and + c0 != 'm' and c0 != 'v' and c0 != '.'): + stdio.printf("[IR-FAIL] _type_str: LLVMType 对象已损坏 ty=0x%llx 首字符=%d 输出='%s'\n", + t.CUInt64T(ty), c0, buf) + stdlib.exit(1) + return None + return buf + + + def _emit(self, text: str): + """将指令文本追加到当前块""" + if self is None or self.CurBlock is None: return + block_append_text(self.Pool, self.CurBlock, text) + + + def _mark_terminated(self): + """标记当前块已终止""" + 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]: + """二元运算通用实现""" + if self is None or lhs is None or rhs is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(lhs.Ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = %s %s %s, %s", name, op, ty_s, lhs.Name, rhs.Name) + self._emit(line) + return SSAValue(pool, lhs.Ty, name) + + + # ============================================================ + # 块定位 + # ============================================================ + def position_at_end(self, block: rawptr[BasicBlock]): + """将插入点设为 block 的末尾,并把 block 移到函数块链表末尾。 + + 移动 block 是为了保证 BB 文本输出顺序与控制流(position)顺序一致, + 避免 SSA 名在文本中非单调递增导致 llc 编译失败。 + """ + if self is None: return + self.CurBlock = block + if self.Func is not None: + function_move_block_to_end(self.Func, block) + + + def builder_cur_block_is_terminated(self) -> int: + """检查当前块的 IsTerminated 标志 + + 通过 block_get_terminated 辅助函数访问,避免链式属性 + self.CurBlock.IsTerminated 在跨模块场景下 BasicBlock 为 opaque + 导致的 GEP 静默跳过问题。 + """ + if self is None or self.CurBlock is None: + return 1 + return block_get_terminated(self.CurBlock) + + + # ============================================================ + # 内存指令 + # ============================================================ + def build_alloca(self, ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = alloca + + 返回类型为 ty* 的 SSA 值(默认对齐,由后端决定)。 + 注意:TPC 并行编译时 include 模块的函数默认参数不会预注册(argN 名回退), + 因此本函数不设默认参数;需要显式对齐请用 build_alloca_aligned。 + """ + if self is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(ty) + line: str = pool.alloc(128) + if line is None: return None + viperlib.snprintf(line, 128, "%s = alloca %s", name, ty_s) + self._emit(line) + + # 结果类型是 ty* + result_ty: rawptr[LLVMType] = LLVMType.Ptr(ty) + result_ty_ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if result_ty_ptr is not None: + string.memset(result_ty_ptr, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty_ptr, result_ty) + return SSAValue(pool, result_ty_ptr, name) + + + 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 默认参数不预注册),调用方必须显式传入。 + """ + if self is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(ty) + line: str = pool.alloc(128) + if line is None: return None + viperlib.snprintf(line, 128, "%s = alloca %s, align %d", name, ty_s, align) + self._emit(line) + + # 结果类型是 ty* + result_ty: rawptr[LLVMType] = LLVMType.Ptr(ty) + result_ty_ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if result_ty_ptr is not None: + string.memset(result_ty_ptr, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty_ptr, result_ty) + return SSAValue(pool, result_ty_ptr, name) + + + def build_alloca_at_entry(self, ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = alloca (在函数入口块生成,确保支配性) + + 在入口块的终止指令之前插入 alloca,避免在条件分支内生成 alloca + 导致的支配性违规(Instruction does not dominate all uses)。 + """ + if self is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + func: rawptr[Function] = self.Func + if func is None or func.Blocks is None: + return self.build_alloca(ty) + blocks: rawptr[GSList[BasicBlock]] = func.Blocks + entry_blk: rawptr[BasicBlock] = blocks.Head + if entry_blk is None: + return self.build_alloca(ty) + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(ty) + line_text: str = pool.alloc(128) + if line_text is None: return None + viperlib.snprintf(line_text, 128, "%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 + # 在块未终止时等同追加,已终止时插到终止指令之前。 + block_insert_text_before_terminator(pool, entry_blk, line_text) + # 结果类型是 ty* + result_ty: rawptr[LLVMType] = LLVMType.Ptr(ty) + result_ty_ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if result_ty_ptr is not None: + string.memset(result_ty_ptr, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty_ptr, result_ty) + return SSAValue(pool, result_ty_ptr, name) + + + def build_load(self, ty: rawptr[LLVMType], + ptr: rawptr[Value]) -> rawptr[Value]: + """%N = load , """ + if self is None or ptr is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(ty) + ptr_ty_s: str = self._type_str(ptr.Ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = load %s, %s %s", name, ty_s, ptr_ty_s, ptr.Name) + self._emit(line) + return SSAValue(pool, ty, name) + + + def build_store(self, val: rawptr[Value], ptr: rawptr[Value]): + """store , """ + if self is None or val is None or ptr is None: return + pool: rawptr[memhub.MemBuddy] = self.Pool + val_ty_s: str = self._type_str(val.Ty) + ptr_ty_s: str = self._type_str(ptr.Ty) + line: str = pool.alloc(256) + if line is None: return + viperlib.snprintf(line, 256, "store %s %s, %s %s", val_ty_s, val.Name, ptr_ty_s, ptr.Name) + self._emit(line) + + + # ============================================================ + # 二元运算指令 + # ============================================================ + def build_add(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = add , """ + return self._build_binop("add", lhs, rhs) + + + def build_sub(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = sub , """ + return self._build_binop("sub", lhs, rhs) + + + def build_mul(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = mul , """ + return self._build_binop("mul", lhs, rhs) + + + def build_sdiv(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = sdiv , """ + return self._build_binop("sdiv", lhs, rhs) + + + def build_udiv(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = udiv , """ + return self._build_binop("udiv", lhs, rhs) + + + def build_srem(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = srem , """ + return self._build_binop("srem", lhs, rhs) + + + def build_urem(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = urem , """ + return self._build_binop("urem", lhs, rhs) + + + def build_and(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = and , """ + return self._build_binop("and", lhs, rhs) + + + def build_or(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = or , """ + return self._build_binop("or", lhs, rhs) + + + def build_xor(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = xor , """ + return self._build_binop("xor", lhs, rhs) + + + def build_shl(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = shl , """ + return self._build_binop("shl", lhs, rhs) + + + def build_lshr(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = lshr , """ + return self._build_binop("lshr", lhs, rhs) + + + def build_ashr(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = ashr , """ + return self._build_binop("ashr", lhs, rhs) + + + # ============================================================ + # 浮点二元运算指令 + # ============================================================ + def build_fadd(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fadd , """ + return self._build_binop("fadd", lhs, rhs) + + + def build_fsub(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fsub , """ + return self._build_binop("fsub", lhs, rhs) + + + def build_fmul(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fmul , """ + return self._build_binop("fmul", lhs, rhs) + + + def build_fdiv(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fdiv , """ + return self._build_binop("fdiv", lhs, rhs) + + + def build_frem(self, lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = frem , """ + return self._build_binop("frem", lhs, rhs) + + + def build_fneg(self, val: rawptr[Value]) -> rawptr[Value]: + """%N = fneg """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(val.Ty) + line: str = pool.alloc(128) + if line is None: return None + viperlib.snprintf(line, 128, "%s = fneg %s %s", name, ty_s, val.Name) + self._emit(line) + return SSAValue(pool, val.Ty, name) + + + # ============================================================ + # 比较指令 + # ============================================================ + def build_icmp(self, pred: int, + lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: + """%N = icmp , + + 返回类型为 i1 的 SSA 值 + """ + if self is None or lhs is None or rhs is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(lhs.Ty) + pred_s: str = _icmp_pred_str(pred) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = icmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) + self._emit(line) + + # 结果类型是 i1 + i1_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if i1_ty is not None: + string.memset(i1_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(i1_ty, LLVMType.Int(1)) + return SSAValue(pool, i1_ty, name) + + + def build_fcmp(self, pred: int, + lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fcmp , + + 返回类型为 i1 的 SSA 值 + """ + if self is None or lhs is None or rhs is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(lhs.Ty) + pred_s: str = _fcmp_pred_str(pred) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fcmp %s %s %s, %s", name, pred_s, ty_s, lhs.Name, rhs.Name) + self._emit(line) + + # 结果类型是 i1 + i1_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if i1_ty is not None: + string.memset(i1_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(i1_ty, LLVMType.Int(1)) + return SSAValue(pool, i1_ty, name) + + + # ============================================================ + # 终止指令 + # ============================================================ + def build_br(self, target: rawptr[BasicBlock]): + """br label %""" + if self is None or target is None: return + pool: rawptr[memhub.MemBuddy] = self.Pool + line: str = pool.alloc(64) + if line is None: return + viperlib.snprintf(line, 64, "br label %%%s", target.Name) + self._emit(line) + self._mark_terminated() + + + def build_cond_br(self, cond: rawptr[Value], + then_blk: rawptr[BasicBlock], else_blk: rawptr[BasicBlock]): + """br i1 , label %, label %""" + if self is None or cond is None or then_blk is None or else_blk is None: return + pool: rawptr[memhub.MemBuddy] = self.Pool + line: str = pool.alloc(128) + if line is None: return + viperlib.snprintf(line, 128, "br i1 %s, label %%%s, label %%%s", + cond.Name, then_blk.Name, else_blk.Name) + self._emit(line) + self._mark_terminated() + + + def build_ret(self, val: rawptr[Value]): + """ret """ + if self is None or val is None: return + pool: rawptr[memhub.MemBuddy] = self.Pool + ty_s: str = self._type_str(val.Ty) + line: str = pool.alloc(128) + if line is None: return + viperlib.snprintf(line, 128, "ret %s %s", ty_s, val.Name) + self._emit(line) + self._mark_terminated() + + + def build_ret_void(self): + """ret void""" + if self is None: return + pool: rawptr[memhub.MemBuddy] = self.Pool + line: str = pool.alloc(16) + if line is None: return + string.strcpy(line, "ret void") + self._emit(line) + self._mark_terminated() + + + # ============================================================ + # 调用指令 + # ============================================================ + 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)。 + """ + if self is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + + # 判断返回类型是否为 void + is_void: int = 0 + match ret_ty: + case LLVMType.Void(): + is_void = 1 + case _: + is_void = 0 + + name: str = None + if is_void == 0: + name = self._alloc_ssa_name() + + ret_ty_s: str = self._type_str(ret_ty) + + # 构建参数列表文本 + 变参函数类型签名 + args_buf: str = pool.alloc(1024) + if args_buf is None: return None + args_buf[0] = '\0' + func_ty_buf: str = pool.alloc(1024) + if func_ty_buf is None: return None + func_ty_buf[0] = '\0' + cur: rawptr[Value] = args + first: int = 1 + while cur is not None: + if first == 0: + _append_cstr(args_buf, 1024, ", ") + _append_cstr(func_ty_buf, 1024, ", ") + arg_ty_s: str = self._type_str(cur.Ty) + _append_cstr(args_buf, 1024, arg_ty_s) + _append_cstr(args_buf, 1024, " ") + if cur.Name is not None: + _append_cstr(args_buf, 1024, cur.Name) + _append_cstr(func_ty_buf, 1024, arg_ty_s) + cur = cur.Next + first = 0 + if is_variadic == 1: + if first == 0: + _append_cstr(func_ty_buf, 1024, ", ") + _append_cstr(func_ty_buf, 1024, "...") + + # 构造带引号的 callee 名字(SHA1 前缀名含 '.' 或以数字开头需要加引号) + callee_buf: str = pool.alloc(128) + if callee_buf is None: return None + callee_buf[0] = '\0' + if _ll_name_needs_quote(callee) != 0: + _append_cstr(callee_buf, 128, "\"") + _append_cstr(callee_buf, 128, callee) + _append_cstr(callee_buf, 128, "\"") + else: + _append_cstr(callee_buf, 128, callee) + + line: str = pool.alloc(2048) + if line is None: return None + if is_variadic == 1: + if is_void == 1: + viperlib.snprintf(line, 2048, "call %s (%s) @%s(%s)", ret_ty_s, func_ty_buf, callee_buf, args_buf) + else: + viperlib.snprintf(line, 2048, "%s = call %s (%s) @%s(%s)", name, ret_ty_s, func_ty_buf, callee_buf, args_buf) + else: + if is_void == 1: + viperlib.snprintf(line, 2048, "call %s @%s(%s)", ret_ty_s, callee_buf, args_buf) + else: + viperlib.snprintf(line, 2048, "%s = call %s @%s(%s)", name, ret_ty_s, callee_buf, args_buf) + self._emit(line) + + if is_void == 1: + return None + return SSAValue(pool, ret_ty, name) + + + # ============================================================ + # 间接调用指令: call %callee() + # + # callee_val 是函数指针 SSA 值(必须已是函数指针类型,如 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 值。 + """ + if self is None or callee_val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + + is_void: int = 0 + match ret_ty: + case LLVMType.Void(): + is_void = 1 + case _: + is_void = 0 + + name: str = None + if is_void == 0: + name = self._alloc_ssa_name() + + ret_ty_s: str = self._type_str(ret_ty) + + # 构建参数列表文本 + args_buf: str = pool.alloc(1024) + if args_buf is None: return None + args_buf[0] = '\0' + cur: rawptr[Value] = args + first: int = 1 + while cur is not None: + if first == 0: + _append_cstr(args_buf, 1024, ", ") + arg_ty_s: str = self._type_str(cur.Ty) + _append_cstr(args_buf, 1024, arg_ty_s) + _append_cstr(args_buf, 1024, " ") + if cur.Name is not None: + _append_cstr(args_buf, 1024, cur.Name) + cur = cur.Next + first = 0 + + line: str = pool.alloc(2048) + if line is None: return None + if is_void == 1: + viperlib.snprintf(line, 2048, "call %s %s(%s)", ret_ty_s, callee_val.Name, args_buf) + else: + viperlib.snprintf(line, 2048, "%s = call %s %s(%s)", name, ret_ty_s, callee_val.Name, args_buf) + self._emit(line) + + if is_void == 1: + return None + return SSAValue(pool, ret_ty, name) + + + # ============================================================ + # 类型转换指令 + # ============================================================ + def build_bitcast(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = bitcast to """ + if self is None or val is None: + return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + if val.Name is None: + return None + viperlib.snprintf(line, 256, "%s = bitcast %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + ret: rawptr[Value] = SSAValue(pool, to_ty, name) + return ret + + + def build_sext(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = sext to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = sext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_trunc(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = trunc to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = trunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_zext(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = zext to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = zext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_ptrtoint(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = ptrtoint to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = ptrtoint %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_inttoptr(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = inttoptr to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = inttoptr %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + # ============================================================ + # 浮点转换指令 + # ============================================================ + def build_fpext(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = fpext to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fpext %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_fptrunc(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = fptrunc to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fptrunc %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_fp2si(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = fptosi to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fptosi %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_si2fp(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = sitofp to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = sitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_fp2ui(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = fptoui to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = fptoui %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + def build_ui2fp(self, val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = uitofp to """ + if self is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + from_ty_s: str = self._type_str(val.Ty) + to_ty_s: str = self._type_str(to_ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = uitofp %s %s to %s", name, from_ty_s, val.Name, to_ty_s) + self._emit(line) + return SSAValue(pool, to_ty, name) + + + # ============================================================ + # GEP 指令 + # ============================================================ + 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 编译失败 + if _is_ptr_type(ptr.Ty) == 0: + return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + elem_ty_s: str = self._type_str(elem_ty) + ptr_ty_s: str = self._type_str(ptr.Ty) + idx_ty_s: str = self._type_str(idx.Ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, %s %s", + name, elem_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) + self._emit(line) + + # GEP 结果类型是 elem_ty* + result_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if result_ty is not None: + string.memset(result_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty, LLVMType.Ptr(elem_ty)) + return SSAValue(pool, result_ty, name) + + + # ============================================================ + # build_gep_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* + """ + if self is None or ptr is None or idx is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + array_ty_s: str = self._type_str(array_ty) + ptr_ty_s: str = self._type_str(ptr.Ty) + idx_ty_s: str = self._type_str(idx.Ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = getelementptr %s, %s %s, i32 0, %s %s", + name, array_ty_s, ptr_ty_s, ptr.Name, idx_ty_s, idx.Name) + self._emit(line) + + # GEP 结果类型是 elem_ty* + result_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if result_ty is not None: + string.memset(result_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty, LLVMType.Ptr(elem_ty)) + return SSAValue(pool, result_ty, name) + + + # ============================================================ + # build_gep_struct — 结构体字段 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* + """ + if self is None or ptr is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + struct_ty_s: str = self._type_str(struct_ty) + ptr_ty_s: str = self._type_str(ptr.Ty) + line: str = pool.alloc(2048) + if line is None: + stdio.printf("[BGS] line alloc None\n") + return None + viperlib.snprintf(line, 2048, "%s = getelementptr %s, %s %s, i32 0, i32 %d", + name, struct_ty_s, ptr_ty_s, ptr.Name, field_idx) + self._emit(line) + + # GEP 结果类型是 field_ty* + result_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if result_ty is None: + stdio.printf("[BGS] result_ty alloc None\n") + else: + string.memset(result_ty, 0, LLVMType.__sizeof__()) + c.DerefAs(result_ty, LLVMType.Ptr(field_ty)) + rv: rawptr[Value] = SSAValue(pool, result_ty, name) + if rv is None: + stdio.printf("[BGS] SSAValue None\n") + return rv + + + # ============================================================ + # Phi 指令 + # ============================================================ + 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 是入边数量。 + """ + if self is None: return None + pool: rawptr[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),需要大缓冲区 + parts: str = pool.alloc(4096) + if parts is None: return None + parts[0] = '\0' + cur: rawptr[PhiIncoming] = incoming_head + first: int = 1 + while cur is not None: + if first == 0: + _append_cstr(parts, 4096, ", ") + _append_cstr(parts, 4096, "[") + # 使用中间变量避免链式结构体成员访问被编译器丢弃 + inc_val: rawptr[Value] = cur.Val + if inc_val is not None: + inc_val_name: str = inc_val.Name + if inc_val_name is not None: + _append_cstr(parts, 4096, inc_val_name) + _append_cstr(parts, 4096, ", %") + inc_blk: rawptr[BasicBlock] = cur.Block + if inc_blk is not None: + inc_blk_name: str = inc_blk.Name + if inc_blk_name is not None: + _append_cstr(parts, 4096, inc_blk_name) + _append_cstr(parts, 4096, "]") + cur = cur.Next + first = 0 + + line: str = pool.alloc(8192) + if line is None: return None + viperlib.snprintf(line, 8192, "%s = phi %s %s", name, ty_s, parts) + self._emit(line) + return SSAValue(pool, ty, name) + + + # ============================================================ + # Switch 指令 + # ============================================================ + def build_switch(self, val: rawptr[Value], + default_blk: rawptr[BasicBlock], + cases_head: rawptr[SwitchCase], case_count: int): + """switch , label % [, label % ...]""" + if self is None or val is None or default_blk is None: return + pool: rawptr[memhub.MemBuddy] = self.Pool + ty_s: str = self._type_str(val.Ty) + + # 构建主体: switch , label %default + line: str = pool.alloc(512) + if line is None: return + viperlib.snprintf(line, 512, "switch %s %s, label %%%s", + ty_s, val.Name, default_blk.Name) + _append_cstr(line, 512, " [") + _append_cstr(line, 512, "\n") + + # 遍历 case 列表 + cur: rawptr[SwitchCase] = cases_head + case_ty_s: str = self._type_str(val.Ty) + while cur is not None: + case_line: str = pool.alloc(128) + if case_line is not None: + # 使用中间变量避免链式结构体成员访问被编译器丢弃 + cv: rawptr[Value] = cur.CaseVal + cb: rawptr[BasicBlock] = cur.Block + if cv is not None and cb is not None: + cv_name: str = cv.Name + cb_name: str = cb.Name + if cv_name is not None and cb_name is not None: + viperlib.snprintf(case_line, 128, " %s %s, label %%%s\n", + case_ty_s, cv_name, cb_name) + _append_cstr(line, 512, case_line) + cur = cur.Next + + _append_cstr(line, 512, "]") + self._emit(line) + self._mark_terminated() + + + # ============================================================ + # Select 指令 + # ============================================================ + def build_select(self, cond: rawptr[Value], + true_val: rawptr[Value], false_val: rawptr[Value]) -> rawptr[Value]: + """%N = select i1 , , """ + if self is None or cond is None or true_val is None or false_val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ty_s: str = self._type_str(true_val.Ty) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = select i1 %s, %s %s, %s %s", + name, cond.Name, ty_s, true_val.Name, ty_s, false_val.Name) + self._emit(line) + return SSAValue(pool, true_val.Ty, name) + + + # ============================================================ + # Unreachable 指令 + # ============================================================ + def build_unreachable(self): + """unreachable""" + if self is None: return + pool: rawptr[memhub.MemBuddy] = self.Pool + line: str = pool.alloc(16) + if line is None: return + string.strcpy(line, "unreachable") + self._emit(line) + self._mark_terminated() + + + # ============================================================ + # 原子指令 (Phase 3) + # ============================================================ + def build_atomicrmw(self, op: int, + ptr: rawptr[Value], val: rawptr[Value], + order: int) -> rawptr[Value]: + """%N = atomicrmw , """ + if self is None or ptr is None or val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ptr_ty_s: str = self._type_str(ptr.Ty) + val_ty_s: str = self._type_str(val.Ty) + op_s: str = _atomic_op_str(op) + ord_s: str = _atomic_order_str(order) + line: str = pool.alloc(256) + if line is None: return None + viperlib.snprintf(line, 256, "%s = atomicrmw %s %s %s, %s %s %s", + name, op_s, ptr_ty_s, ptr.Name, val_ty_s, val.Name, ord_s) + self._emit(line) + return SSAValue(pool, val.Ty, name) + + + def build_cmpxchg(self, ptr: rawptr[Value], + cmp_val: rawptr[Value], new_val: rawptr[Value], + succ_order: int, fail_order: int) -> rawptr[Value]: + """%N = cmpxchg , , + + 返回 {, i1} 类型的 SSA 值(简化为返回 i64 表示的值对) + """ + if self is None or ptr is None or cmp_val is None or new_val is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + ptr_ty_s: str = self._type_str(ptr.Ty) + cmp_ty_s: str = self._type_str(cmp_val.Ty) + new_ty_s: str = self._type_str(new_val.Ty) + succ_s: str = _atomic_order_str(succ_order) + fail_s: str = _atomic_order_str(fail_order) + line: str = pool.alloc(384) + if line is None: return None + viperlib.snprintf(line, 384, "%s = cmpxchg %s %s, %s %s, %s %s %s %s", + 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 SSAValue(pool, new_val.Ty, name) + + + # ============================================================ + # va_arg 指令: 从 va_list 读取下一个变长参数 + # ============================================================ + def build_va_arg(self, + va_list_ptr: rawptr[Value], + arg_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = va_arg , """ + if self is None or va_list_ptr is None or arg_ty is None: + return None + pool: rawptr[memhub.MemBuddy] = self.Pool + name: str = self._alloc_ssa_name() + va_list_ty_s: str = self._type_str(va_list_ptr.Ty) + arg_ty_s: str = self._type_str(arg_ty) + line: str = pool.alloc(256) + if line is None: return None + if va_list_ptr.Name is None: + return None + viperlib.snprintf(line, 256, "%s = va_arg %s %s, %s", + name, va_list_ty_s, va_list_ptr.Name, arg_ty_s) + self._emit(line) + return SSAValue(pool, arg_ty, name) + + + # ============================================================ + # 内联汇编 (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] "" ""() + + 简化的内联汇编调用,无参数版本。 + """ + if self is None: return None + pool: rawptr[memhub.MemBuddy] = self.Pool + + # 判断返回类型是否为 void + is_void: int = 0 + match ret_ty: + case LLVMType.Void(): + is_void = 1 + case _: + is_void = 0 + + name: str = None + if is_void == 0: + name = self._alloc_ssa_name() + + ret_ty_s: str = self._type_str(ret_ty) + line: str = pool.alloc(512) + if line is None: return None + + if has_side_effects != 0: + if is_void == 1: + viperlib.snprintf(line, 512, "call %s asm sideeffect \"%s\", \"%s\"()", + ret_ty_s, asm_text, constraints) + else: + viperlib.snprintf(line, 512, "%s = call %s asm sideeffect \"%s\", \"%s\"()", + name, ret_ty_s, asm_text, constraints) + else: + if is_void == 1: + viperlib.snprintf(line, 512, "call %s asm \"%s\", \"%s\"()", + ret_ty_s, asm_text, constraints) + else: + viperlib.snprintf(line, 512, "%s = call %s asm \"%s\", \"%s\"()", + name, ret_ty_s, asm_text, constraints) + self._emit(line) + + if is_void == 1: + return None + return SSAValue(pool, ret_ty, name) + + +# ============================================================ +# 工厂函数 +# ============================================================ +# IRBuilder 硬编码大小: Pool(8) + Func(8) + CurBlock(8) + Counter(4) = 32 字节 +BUILDER_SIZE: t.CDefine = 32 + +def new_builder(pool: rawptr[memhub.MemBuddy], func: rawptr[Function]) -> rawptr[IRBuilder]: + """创建 IRBuilder,关联到函数 func""" + ptr: rawptr[IRBuilder] = pool.alloc(BUILDER_SIZE) + if ptr is None: return None + string.memset(ptr, 0, BUILDER_SIZE) + ptr.Pool = pool + ptr.Func = func + ptr.CurBlock = None + ptr.Counter = 0 + return ptr + + +# ============================================================ +# 块定位 +# ============================================================ +def position_at_end(builder: rawptr[IRBuilder], block: rawptr[BasicBlock]): + """将插入点设为 block 的末尾,并把 block 移到函数块链表末尾。 + + 移动 block 是为了保证 BB 文本输出顺序与控制流(position)顺序一致, + 避免 SSA 名在文本中非单调递增导致 llc 编译失败。 + """ + builder.position_at_end(block) + + +# ============================================================ +# 内部辅助 +# ============================================================ +def _alloc_ssa_name(builder: rawptr[IRBuilder]) -> str: + """分配一个 SSA 名字符串 "%N" 并递增计数器""" + return builder._alloc_ssa_name() + + +def _type_str(builder: rawptr[IRBuilder], ty: rawptr[LLVMType]) -> str: + """获取类型的 IR 文本(从 pool 分配 512 字节缓冲区) + + 注意:复杂结构体类型(如 13 方法的虚表 {i8*,i8*,...})可能超过 64 字节, + 必须使用足够大的缓冲区,否则 '}' 会被截断导致 IR 语法错误。 + """ + return builder._type_str(ty) + + +def _emit(builder: rawptr[IRBuilder], text: str): + """将指令文本追加到当前块""" + builder._emit(text) + + +def _mark_terminated(builder: rawptr[IRBuilder]): + """标记当前块已终止""" + builder._mark_terminated() + + +def builder_cur_block_is_terminated(builder: rawptr[IRBuilder]) -> int: + """检查当前块的 IsTerminated 标志 + + 通过 block_get_terminated 辅助函数访问,避免链式属性 + builder.CurBlock.IsTerminated 在跨模块场景下 BasicBlock 为 opaque + 导致的 GEP 静默跳过问题。 + """ + return builder.builder_cur_block_is_terminated() + + +# ============================================================ +# 内存指令 +# ============================================================ +def build_alloca(builder: rawptr[IRBuilder], ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = alloca + + 返回类型为 ty* 的 SSA 值(默认对齐,由后端决定)。 + 注意:TPC 并行编译时 include 模块的函数默认参数不会预注册(argN 名回退), + 因此本函数不设默认参数;需要显式对齐请用 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 默认参数不预注册),调用方必须显式传入。 + """ + 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)。 + """ + return builder.build_alloca_at_entry(ty) + + +def build_load(builder: rawptr[IRBuilder], ty: rawptr[LLVMType], + ptr: rawptr[Value]) -> rawptr[Value]: + """%N = load , """ + return builder.build_load(ty, ptr) + + +def build_store(builder: rawptr[IRBuilder], val: rawptr[Value], ptr: rawptr[Value]): + """store , """ + builder.build_store(val, ptr) + + +# ============================================================ +# 二元运算指令 +# ============================================================ +def build_add(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = add , """ + return builder.build_add(lhs, rhs) + + +def build_sub(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = sub , """ + return builder.build_sub(lhs, rhs) + + +def build_mul(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = mul , """ + return builder.build_mul(lhs, rhs) + + +def build_sdiv(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = sdiv , """ + return builder.build_sdiv(lhs, rhs) + + +def build_udiv(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = udiv , """ + return builder.build_udiv(lhs, rhs) + + +def build_srem(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = srem , """ + return builder.build_srem(lhs, rhs) + + +def build_urem(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = urem , """ + return builder.build_urem(lhs, rhs) + + +def build_and(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = and , """ + return builder.build_and(lhs, rhs) + + +def build_or(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = or , """ + return builder.build_or(lhs, rhs) + + +def build_xor(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = xor , """ + return builder.build_xor(lhs, rhs) + + +def build_shl(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = shl , """ + return builder.build_shl(lhs, rhs) + + +def build_lshr(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = lshr , """ + return builder.build_lshr(lhs, rhs) + + +def build_ashr(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = ashr , """ + return builder.build_ashr(lhs, rhs) + + +# ============================================================ +# 浮点二元运算指令 +# ============================================================ +def build_fadd(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fadd , """ + return builder.build_fadd(lhs, rhs) + + +def build_fsub(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fsub , """ + return builder.build_fsub(lhs, rhs) + + +def build_fmul(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fmul , """ + return builder.build_fmul(lhs, rhs) + + +def build_fdiv(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = fdiv , """ + return builder.build_fdiv(lhs, rhs) + + +def build_frem(builder: rawptr[IRBuilder], lhs: rawptr[Value], + rhs: rawptr[Value]) -> rawptr[Value]: + """%N = frem , """ + return builder.build_frem(lhs, rhs) + + +def build_fneg(builder: rawptr[IRBuilder], val: rawptr[Value]) -> rawptr[Value]: + """%N = fneg """ + return builder.build_fneg(val) + + +def _build_binop(builder: rawptr[IRBuilder], op: str, + lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: + """二元运算通用实现""" + return builder._build_binop(op, lhs, rhs) + + +# ============================================================ +# 比较指令 +# ============================================================ +# ICMP 谓词常量 +ICMP_EQ: t.CDefine = 0 +ICMP_NE: t.CDefine = 1 +ICMP_SGT: t.CDefine = 2 +ICMP_SGE: t.CDefine = 3 +ICMP_SLT: t.CDefine = 4 +ICMP_SLE: t.CDefine = 5 +ICMP_UGT: t.CDefine = 6 +ICMP_UGE: t.CDefine = 7 +ICMP_ULT: t.CDefine = 8 +ICMP_ULE: t.CDefine = 9 + +# FCMP 谓词常量 +FCMP_FALSE: t.CDefine = 0 +FCMP_OEQ: t.CDefine = 1 +FCMP_OGT: t.CDefine = 2 +FCMP_OGE: t.CDefine = 3 +FCMP_OLT: t.CDefine = 4 +FCMP_OLE: t.CDefine = 5 +FCMP_ONE: t.CDefine = 6 +FCMP_ORD: t.CDefine = 7 +FCMP_UNO: t.CDefine = 8 +FCMP_UEQ: t.CDefine = 9 +FCMP_UGT: t.CDefine = 10 +FCMP_UGE: t.CDefine = 11 +FCMP_ULT: t.CDefine = 12 +FCMP_ULE: t.CDefine = 13 +FCMP_UNE: t.CDefine = 14 +FCMP_TRUE: t.CDefine = 15 + + +def _icmp_pred_str(pred: int) -> str: + if pred == ICMP_EQ: return "eq" + if pred == ICMP_NE: return "ne" + if pred == ICMP_SGT: return "sgt" + if pred == ICMP_SGE: return "sge" + if pred == ICMP_SLT: return "slt" + if pred == ICMP_SLE: return "sle" + if pred == ICMP_UGT: return "ugt" + if pred == ICMP_UGE: return "uge" + if pred == ICMP_ULT: return "ult" + if pred == ICMP_ULE: return "ule" + return "eq" + + +def _fcmp_pred_str(pred: int) -> str: + if pred == FCMP_FALSE: return "false" + if pred == FCMP_OEQ: return "oeq" + if pred == FCMP_OGT: return "ogt" + if pred == FCMP_OGE: return "oge" + if pred == FCMP_OLT: return "olt" + if pred == FCMP_OLE: return "ole" + if pred == FCMP_ONE: return "one" + if pred == FCMP_ORD: return "ord" + if pred == FCMP_UNO: return "uno" + if pred == FCMP_UEQ: return "ueq" + if pred == FCMP_UGT: return "ugt" + if pred == FCMP_UGE: return "uge" + if pred == FCMP_ULT: return "ult" + if pred == FCMP_ULE: return "ule" + if pred == FCMP_UNE: return "une" + if pred == FCMP_TRUE: return "true" + return "oeq" + + +def build_icmp(builder: rawptr[IRBuilder], pred: int, + lhs: rawptr[Value], rhs: rawptr[Value]) -> rawptr[Value]: + """%N = icmp , + + 返回类型为 i1 的 SSA 值 + """ + 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 值 + """ + return builder.build_fcmp(pred, lhs, rhs) + + +# ============================================================ +# 终止指令 +# ============================================================ +def build_br(builder: rawptr[IRBuilder], target: rawptr[BasicBlock]): + """br label %""" + builder.build_br(target) + + +def build_cond_br(builder: rawptr[IRBuilder], cond: rawptr[Value], + then_blk: rawptr[BasicBlock], else_blk: rawptr[BasicBlock]): + """br i1 , label %, label %""" + builder.build_cond_br(cond, then_blk, else_blk) + + +def build_ret(builder: rawptr[IRBuilder], val: rawptr[Value]): + """ret """ + builder.build_ret(val) + + +def build_ret_void(builder: rawptr[IRBuilder]): + """ret void""" + builder.build_ret_void() + + +# ============================================================ +# 调用指令 +# ============================================================ +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)。 + """ + return builder.build_call(callee, args, arg_count, ret_ty, is_variadic) + + +# ============================================================ +# 间接调用指令: call %callee() +# +# callee_val 是函数指针 SSA 值(必须已是函数指针类型,如 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 值。 + """ + return builder.build_call_indirect(callee_val, args, arg_count, ret_ty) + + +# ============================================================ +# 类型转换指令 +# ============================================================ +def build_bitcast(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = bitcast to """ + return builder.build_bitcast(val, to_ty) + + +def build_sext(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = sext to """ + return builder.build_sext(val, to_ty) + + +def build_trunc(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = trunc to """ + return builder.build_trunc(val, to_ty) + + +def build_zext(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = zext to """ + return builder.build_zext(val, to_ty) + + +def build_ptrtoint(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = ptrtoint to """ + return builder.build_ptrtoint(val, to_ty) + + +def build_inttoptr(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = inttoptr to """ + return builder.build_inttoptr(val, to_ty) + + +# ============================================================ +# 浮点转换指令 +# ============================================================ +def build_fpext(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = fpext to """ + return builder.build_fpext(val, to_ty) + + +def build_fptrunc(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = fptrunc to """ + return builder.build_fptrunc(val, to_ty) + + +def build_fp2si(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = fptosi to """ + return builder.build_fp2si(val, to_ty) + + +def build_si2fp(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = sitofp to """ + return builder.build_si2fp(val, to_ty) + + +def build_fp2ui(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = fptoui to """ + return builder.build_fp2ui(val, to_ty) + + +def build_ui2fp(builder: rawptr[IRBuilder], val: rawptr[Value], + to_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = uitofp to """ + return builder.build_ui2fp(val, to_ty) + + +# ============================================================ +# GEP 指令 +# ============================================================ +def _is_ptr_type(ty: rawptr[LLVMType]) -> int: + """检查 ty 是否是 Ptr 类型(独立函数,避免嵌套 match 的编译器 BUG)""" + if ty is None: + return 0 + match ty: + case LLVMType.Ptr(pointee): + return 1 + case _: + return 0 + + +def build_gep(builder: rawptr[IRBuilder], elem_ty: rawptr[LLVMType], + ptr: rawptr[Value], idx: rawptr[Value]) -> rawptr[Value]: + """%N = getelementptr , , """ + return builder.build_gep(elem_ty, ptr, idx) + + +# ============================================================ +# build_gep_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* + """ + 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 +# ============================================================ +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* + """ + return builder.build_gep_struct(struct_ty, field_ty, ptr, field_idx) + + +# ============================================================ +# PhiIncoming: phi 节点的入边 (value, block) 对 +# 继承 GSListNode[PhiIncoming] 获取强类型 Next +# +# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 +# ============================================================ +@t.NoVTable +class PhiIncoming(GSListNode[PhiIncoming]): + Val: rawptr[Value] + Block: rawptr[BasicBlock] + + +# PhiIncoming 硬编码大小: Next(8) + Val(8) + Block(8) = 24 字节 +PHIINC_SIZE: t.CDefine = 24 + +def new_phi_incoming(pool: rawptr[memhub.MemBuddy], + val: rawptr[Value], block: rawptr[BasicBlock]) -> rawptr[PhiIncoming]: + ptr: rawptr[PhiIncoming] = pool.alloc(PHIINC_SIZE) + if ptr is None: return None + string.memset(ptr, 0, PHIINC_SIZE) + ptr.Val = val + ptr.Block = block + return ptr + + +# ============================================================ +# Phi 指令 +# ============================================================ +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 是入边数量。 + """ + return builder.build_phi(ty, incoming_head, count) + + +# ============================================================ +# SwitchCase: switch 指令的 case 分支 +# +# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 +# ============================================================ +@t.NoVTable +class SwitchCase(GSListNode[SwitchCase]): + CaseVal: rawptr[Value] + Block: rawptr[BasicBlock] + + +# SwitchCase 硬编码大小: Next(8) + CaseVal(8) + Block(8) = 24 字节 +SWCASE_SIZE: t.CDefine = 24 + +def new_switch_case(pool: rawptr[memhub.MemBuddy], + case_val: rawptr[Value], block: rawptr[BasicBlock]) -> rawptr[SwitchCase]: + ptr: rawptr[SwitchCase] = pool.alloc(SWCASE_SIZE) + if ptr is None: return None + string.memset(ptr, 0, SWCASE_SIZE) + ptr.CaseVal = case_val + ptr.Block = block + return ptr + + +# ============================================================ +# Switch 指令 +# ============================================================ +def build_switch(builder: rawptr[IRBuilder], val: rawptr[Value], + default_blk: rawptr[BasicBlock], + cases_head: rawptr[SwitchCase], case_count: int): + """switch , label % [, label % ...]""" + builder.build_switch(val, default_blk, cases_head, case_count) + + +# ============================================================ +# Select 指令 +# ============================================================ +def build_select(builder: rawptr[IRBuilder], cond: rawptr[Value], + true_val: rawptr[Value], false_val: rawptr[Value]) -> rawptr[Value]: + """%N = select i1 , , """ + return builder.build_select(cond, true_val, false_val) + + +# ============================================================ +# Unreachable 指令 +# ============================================================ +def build_unreachable(builder: rawptr[IRBuilder]): + """unreachable""" + builder.build_unreachable() + + +# ============================================================ +# 原子指令 (Phase 3) +# ============================================================ +# AtomicRMW 操作常量 +ATOMIC_XCHG: t.CDefine = 0 +ATOMIC_ADD: t.CDefine = 1 +ATOMIC_SUB: t.CDefine = 2 +ATOMIC_AND: t.CDefine = 3 +ATOMIC_NAND: t.CDefine = 4 +ATOMIC_OR: t.CDefine = 5 +ATOMIC_XOR: t.CDefine = 6 +ATOMIC_MAX: t.CDefine = 7 +ATOMIC_MIN: t.CDefine = 8 +ATOMIC_UMAX: t.CDefine = 9 +ATOMIC_UMIN: t.CDefine = 10 + +# 内存序常量 +ATOMIC_ORDER_NOTATOMIC: t.CDefine = 0 +ATOMIC_ORDER_UNORDERED: t.CDefine = 1 +ATOMIC_ORDER_MONOTONIC: t.CDefine = 2 +ATOMIC_ORDER_ACQUIRE: t.CDefine = 3 +ATOMIC_ORDER_RELEASE: t.CDefine = 4 +ATOMIC_ORDER_ACQ_REL: t.CDefine = 5 +ATOMIC_ORDER_SEQ_CST: t.CDefine = 6 + + +def _atomic_op_str(op: int) -> str: + if op == ATOMIC_XCHG: return "xchg" + if op == ATOMIC_ADD: return "add" + if op == ATOMIC_SUB: return "sub" + if op == ATOMIC_AND: return "and" + if op == ATOMIC_NAND: return "nand" + if op == ATOMIC_OR: return "or" + if op == ATOMIC_XOR: return "xor" + if op == ATOMIC_MAX: return "max" + if op == ATOMIC_MIN: return "min" + if op == ATOMIC_UMAX: return "umax" + if op == ATOMIC_UMIN: return "umin" + return "xchg" + + +def _atomic_order_str(ord: int) -> str: + if ord == ATOMIC_ORDER_UNORDERED: return "unordered" + if ord == ATOMIC_ORDER_MONOTONIC: return "monotonic" + if ord == ATOMIC_ORDER_ACQUIRE: return "acquire" + if ord == ATOMIC_ORDER_RELEASE: return "release" + if ord == ATOMIC_ORDER_ACQ_REL: return "acq_rel" + if ord == ATOMIC_ORDER_SEQ_CST: return "seq_cst" + return "monotonic" + + +def build_atomicrmw(builder: rawptr[IRBuilder], op: int, + ptr: rawptr[Value], val: rawptr[Value], + order: int) -> rawptr[Value]: + """%N = atomicrmw , """ + return builder.build_atomicrmw(op, ptr, val, order) + + +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 表示的值对) + """ + return builder.build_cmpxchg(ptr, cmp_val, new_val, succ_order, fail_order) + + +# ============================================================ +# va_arg 指令: 从 va_list 读取下一个变长参数 +# ============================================================ +def build_va_arg(builder: rawptr[IRBuilder], + va_list_ptr: rawptr[Value], + arg_ty: rawptr[LLVMType]) -> rawptr[Value]: + """%N = va_arg , """ + return builder.build_va_arg(va_list_ptr, arg_ty) + + +# ============================================================ +# 内联汇编 (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] "" ""() + + 简化的内联汇编调用,无参数版本。 + """ + return builder.build_inline_asm(ret_ty, asm_text, constraints, has_side_effects) + + +# ============================================================ +# 内部辅助 +# ============================================================ +# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, +# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 +# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 +_g_last_dst: t.CUInt64T = 0 +_g_last_pos: t.CSizeT = 0 + +def _append_cstr(dst: str, dst_size: t.CSizeT, src: str): + """将 C 字符串 src 追加到 dst 末尾""" + global _g_last_dst, _g_last_pos + if dst is None or src is None: return + dlen: t.CSizeT = 0 + if _g_last_dst == t.CUInt64T(dst): + dlen = _g_last_pos + # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 + if dlen > 0 and dst[0] == '\0': + dlen = 0 + # 缓存一致性校验(同 __module/__function 版):缓存 pos 过期会追加到首个 NUL + # 之后使内容不可见,校验失败回退 strlen;正常热路径仍 O(1)。 + if dlen == 0: + if dst[0] != '\0': + dlen = string.strlen(dst) + elif dst[dlen] != '\0' or dst[dlen - 1] == '\0': + dlen = string.strlen(dst) + else: + dlen = string.strlen(dst) + _g_last_dst = t.CUInt64T(dst) + # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, + # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + if dlen >= dst_size: + return + slen: t.CSizeT = string.strlen(src) + remain: t.CSizeT = dst_size - dlen + if remain <= 0: return + i: t.CSizeT = 0 + while i < slen and i + 1 < remain: + dst[dlen + i] = src[i] + i += 1 + dst[dlen + i] = '\0' + _g_last_pos = dlen + i diff --git a/llvmlite/__function.vp b/llvmlite/__function.vp index be2318b..6b1016a 100644 --- a/llvmlite/__function.vp +++ b/llvmlite/__function.vp @@ -1,1420 +1,1420 @@ -import t, c -from stdint import * -import string -import viperlib -import memhub -import stdio -import stdlib -from linkedlist import GSListNode, GSList -from .__types import LLVMType, ParamNode, TypePrint - - -# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast,与 __module.vp 对齐)。 -# 函数/基本块/指令行链的 Next 被越界写破坏成环时,遍历无限循环挂起; -# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位"。 -IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000 - -# 窥孔优化总开关(T2 转发/ret 归零、T3 no-op bitcast 消除)。 -# TPV 命令行 --no-llvmpeephole 会将其置 0:FunctionPrint 序列化时跳过全部本地窥孔, -# 便于与关闭窥孔的 IR 对比调试(RenumberSSA 仍保留,保证输出 SSA 编号合法)。 -G_PeepEnable: bool = True - - -# ============================================================ -# Line: 指令文本行 -# -# 每条 LLVM IR 指令序列化为一行文本,通过链表组织。 -# 继承 GSListNode[Line] 获取强类型 Next: Line|CPtr -# -# 注意: 必须加 @t.NoVTable。否则 TransPyC 为子类添加 vtable 指针(T*), -# 但完全丢失父类 GSListNode[T] 的 Next 字段,导致 struct 布局错位, -# 所有字段访问读取垃圾值(见续 XV 会话根因分析)。 -# ============================================================ -@t.NoVTable -class Line(GSListNode[Line]): - Buf: str # 行文本(NUL 结尾) - Len: t.CSizeT # 文本长度(不含 NUL) - - -# ============================================================ -# BasicBlock: 基本块 -# -# 持有名称 + 指令行链表 + 终止指令标志。 -# 继承 GSListNode[BasicBlock] 获取强类型 Next: BasicBlock|CPtr -# Lines 为 GSList[Line] 指针,提供 O(1) 追加。 -# -# 注意: 必须加 @t.NoVTable(理由同 Line)。 -# ============================================================ -@t.NoVTable -class BasicBlock(GSListNode[BasicBlock]): - Name: str # 块名(如 "entry"/"if.then") - Lines: rowptr[GSList[Line]] # 指令行链表容器(Head/Tail/Count) - IsTerminated: int # 1=已有终止指令(br/ret) - - -# ============================================================ -# Param: 函数参数 -# -# 继承 GSListNode[Param] 获取强类型 Next: Param|CPtr -# Attrs 存储参数属性文本(如 "noundef"/"nocapture"/"readonly"),None=无属性 -# -# 注意: 必须加 @t.NoVTable(理由同 Line)。 -# ============================================================ -@t.NoVTable -class Param(GSListNode[Param]): - Ty: rowptr[LLVMType] # 参数类型 - Name: str # 参数名(如 "%0"/"%a") - Attrs: str # 参数属性文本(None=无属性) - - -# ============================================================ -# Function: 函数 -# -# 持有名称 + 返回类型 + 参数链表 + 基本块链表。 -# 继承 GSListNode[Function] 获取强类型 Next: Function|CPtr -# Params/Blocks 为 GSList 指针,提供 O(1) 追加。 -# Attrs 存储函数级属性文本(如 "nounwind"/"noinline"),None=无属性 -# -# 注意: 必须加 @t.NoVTable(理由同 Line)。 -# ============================================================ -@t.NoVTable -class Function(GSListNode[Function]): - Name: str # 函数名 - RetTy: rowptr[LLVMType] # 返回类型 - Params: rowptr[GSList[Param]] # 参数链表容器 - Blocks: rowptr[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: t.CSizeT # 函数名长度(创建时一次算好,恒等于 strlen(Name);追加在末尾,既有字段偏移不变) - - -# ============================================================ -# 工厂函数 -# ============================================================ -# Line 硬编码大小: Next(8) + Buf(8) + Len(8) = 24 字节 -LINE_SIZE: t.CDefine = 24 - -def new_line(pool: rowptr[memhub.MemBuddy], text: str) -> rowptr[Line]: - """创建一个指令行,复制 text 到新分配的缓冲区""" - ptr: rowptr[Line] = pool.alloc(LINE_SIZE) - if ptr is None: return None - string.memset(ptr, 0, LINE_SIZE) - if text is not None: - slen: t.CSizeT = string.strlen(text) - buf: str = pool.alloc(slen + 1) - if buf is not None: - string.strcpy(buf, text) - ptr.Buf = buf - ptr.Len = slen - return ptr - - -def _new_gslist(pool: rowptr[memhub.MemBuddy], list_size: t.CSizeT) -> t.CPtr: - """从 pool 分配并零初始化一个 GSList 容器(Head/Tail/Count 全零)""" - 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 字节 -BLOCK_SIZE: t.CDefine = 32 - -def new_basic_block(pool: rowptr[memhub.MemBuddy], name: str) -> rowptr[BasicBlock]: - """创建一个基本块""" - ptr: rowptr[BasicBlock] = pool.alloc(BLOCK_SIZE) - if ptr is None: return None - string.memset(ptr, 0, BLOCK_SIZE) - ptr.Name = name - ptr.Lines = _new_gslist(pool, 24) # GSList: Head+Tail+Count - ptr.IsTerminated = 0 - return ptr - - -# Param 硬编码大小: Next(8) + Ty(8) + Name(8) + Attrs(8) = 32 字节 -PARAM_SIZE: t.CDefine = 32 - -def new_param(pool: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType], - name: str) -> rowptr[Param]: - """创建一个函数参数""" - ptr: rowptr[Param] = pool.alloc(PARAM_SIZE) - if ptr is None: return None - string.memset(ptr, 0, PARAM_SIZE) - ptr.Ty = ty - ptr.Name = name - ptr.Attrs = None - 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) = 80 字节 -FUNC_SIZE: t.CDefine = 80 - -def new_function(pool: rowptr[memhub.MemBuddy], name: str, - ret_ty: rowptr[LLVMType]) -> rowptr[Function]: - """创建一个函数""" - ptr: rowptr[Function] = pool.alloc(FUNC_SIZE) - if ptr is None: return None - string.memset(ptr, 0, FUNC_SIZE) - ptr.Name = name - ptr.RetTy = ret_ty - ptr.Params = _new_gslist(pool, 24) # GSList: Head+Tail+Count - ptr.Blocks = _new_gslist(pool, 24) - ptr.IsDeclared = 0 - 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 已归零,此处显式赋值以固定语义、不依赖分配器行为)。 - if name is not None: - ptr.NameLen = string.strlen(name) - else: - ptr.NameLen = 0 - return ptr - - -# ============================================================ -# 链表操作(委托 GSList.append) -# ============================================================ -def function_add_param(func: rowptr[Function], param: rowptr[Param]): - """将参数追加到函数参数链表""" - if func is None or param is None: return - # 注意:必须先用局部变量 + 类型参数(对齐 function_move_block_to_end 注释), - # 链式 func.Params.append() 会被编译器解析成 LinkedNode.append 且 this 前移 - # sizeof(LinkedNode)(兄弟 Mixin 兜底误匹配),写穿容器前 48 字节破坏数据结构。 - params: rowptr[GSList[Param]] = func.Params - if params is None: return - params.append(param) - - -def function_add_block(func: rowptr[Function], block: rowptr[BasicBlock]): - """将基本块追加到函数块链表""" - if func is None or block is None: return - blocks: rowptr[GSList[BasicBlock]] = func.Blocks - if blocks is None: return - blocks.append(block) - - -def function_move_block_to_end(func: rowptr[Function], block: rowptr[BasicBlock]): - """将 block 移动到函数块链表末尾(保证 BB 文本顺序与控制流顺序一致)。 - - 用于解决 SSA 名非单调问题:BB 按创建顺序排列,但指令按控制流顺序生成, - 导致 SSA 编号在文本输出时非单调递增,llc 报错。 - 在 position_at_end 时调用此函数,使 BB 顺序与 position 顺序一致。 - - 注意:必须使用 GSList[BasicBlock] 带类型参数,否则 TransPyC 创建不完整 - struct 类型,导致 blocks.Head/Tail/Count 字段访问读取垃圾值。 - """ - if func is None or block is None: return - blocks: rowptr[GSList[BasicBlock]] = func.Blocks - if blocks is None: return - - # 已经是 Tail,无需移动 - if blocks.Tail is block: - return - - # 从链表中移除 block - if blocks.Head is block: - # block 是 Head,直接后移 Head - blocks.Head = block.Next - else: - # 遍历找前驱 - prev: rowptr[BasicBlock] = blocks.Head - while prev is not None and prev.Next is not block: - prev = prev.Next - if prev is None: - # block 不在链表中,直接 append - blocks.append(block) - return - prev.Next = block.Next - - # block.Next 由移除逻辑处理,重置为 None - block.Next = None - - # append 到末尾(不调用 blocks.append 以避免 Count 重复递增) - if blocks.Tail is not None: - blocks.Tail.Next = block - blocks.Tail = block - - -def block_append_line(block: rowptr[BasicBlock], line: rowptr[Line]): - """将指令行追加到基本块""" - if block is None or line is None: return - lines: rowptr[GSList[Line]] = block.Lines - if lines is None: return - lines.append(line) - - -def block_set_terminated(block: rowptr[BasicBlock], val: int): - """设置块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)""" - if block is None: return - block.IsTerminated = val - - -def block_get_terminated(block: rowptr[BasicBlock]) -> int: - """获取块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)""" - if block is None: - return 1 - return block.IsTerminated - - -def block_append_text(pool: rowptr[memhub.MemBuddy], block: rowptr[BasicBlock], - text: str): - """创建指令行并追加到基本块""" - if block is None or text is None: return - line: rowptr[Line] = new_line(pool, text) - if line is not None: - block_append_line(block, line) - - -def block_insert_text_before_terminator(pool: rowptr[memhub.MemBuddy], - block: rowptr[BasicBlock], - text: str): - """创建指令行并插入到基本块终止指令之前 - - 如果块未终止,追加到末尾。 - 如果块已终止(有 br/ret),在终止指令之前插入,确保 alloca 等指令 - 不出现在终止指令之后。 - """ - if block is None or text is None: return - if block.Lines is None: return - line: rowptr[Line] = new_line(pool, text) - if line is None: return - # 块未终止:直接追加 - if block.IsTerminated == 0: - block_append_line(block, line) - return - # 块已终止:在 Tail(终止指令)之前插入 - lines: rowptr[GSList[Line]] = block.Lines - if lines.Head is None: - block_append_line(block, line) - return - # 只有一个节点:在 Head 之前插入 - if lines.Head is lines.Tail: - line.Next = lines.Head - lines.Head = line - lines.Count += 1 - return - # 找到 Tail 的前驱节点 - prev: rowptr[Line] = lines.Head - while prev is not None and prev.Next is not lines.Tail: - prev = prev.Next - if prev is None: - # 找不到前驱,回退到追加 - block_append_line(block, line) - return - # 在 prev 和 Tail 之间插入 line - line.Next = lines.Tail - prev.Next = line - lines.Count += 1 - - -# ============================================================ -# 属性设置 -# ============================================================ -def function_set_attrs(func: rowptr[Function], attrs: str): - """设置函数级属性文本(如 "nounwind"/"noinline")""" - if func is None: return - func.Attrs = attrs - - -_g_attr_group_counter: int = 0 - -def function_set_attr_group(func: rowptr[Function], group: str): - """设置函数字符串属性组(如 '"section"=".text.startup"')并分配组编号 #N。 - - LLVM 字符串属性(带值的,如 section)不能内联在函数签名行, - 必须用属性组:函数行输出 #N,组定义输出在函数尾。 - """ - global _g_attr_group_counter - if func is None or group is None: - return - func.AttrGroup = group - _g_attr_group_counter += 1 - func.AttrGroupId = _g_attr_group_counter - - -def param_set_attrs(param: rowptr[Param], attrs: str): - """设置参数属性文本(如 "noundef"/"nocapture"/"readonly")""" - if param is None: return - param.Attrs = attrs - - -# ============================================================ -# _ll_name_needs_quote - 检查 LLVM 标识符是否需要加引号 -# -# LLVM IR 规范:标识符合法字符集为 [-a-zA-Z$._][-a-zA-Z$._0-9]* -# 以数字开头或含非合法字符(如 '['、']'、' '、'@' 等)时必须用双引号包围。 -# '.' 是合法字符,不需要加引号(如 @.str、@llvm.memcpy 都不加引号)。 -# SHA1 命名空间的函数名(如 "4dad2ba72ed7ba09.GSListNode[Param]")含 '['、']', -# 需要加引号;但纯 hex+点 的名字(如 "abc.def")不需要。 -# ============================================================ -def _ll_name_needs_quote(name: str) -> int: - """检查 LLVM 标识符是否需要加引号(以数字开头或含非合法字符) - - LLVM IR 合法字符:a-z, A-Z, 0-9, -, $, ., _ - 首字符是数字时需要加引号(LLVM 要求局部/全局标识符首字符非数字,除非加引号)。 - 含其他字符(如 '['、']'、空格)时需要加引号。 - """ - if name is None: - return 0 - c0: t.CChar = name[0] - if c0 >= '0' and c0 <= '9': - return 1 - name_len: t.CSizeT = string.strlen(name) - i: t.CSizeT = 0 - while i < name_len: - ch: t.CChar = name[i] - # 合法字符:a-z, A-Z, 0-9, -, $, ., _ - if not ((ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or - (ch >= '0' and ch <= '9') or ch == '-' or ch == '$' or - ch == '.' or ch == '_'): - return 1 - i += 1 - return 0 - - -# ============================================================ -# RenumberSSA: 重编号函数内数字 SSA 名 -# -# 解决 build_alloca_at_entry 在入口块终止指令前插入 alloca 导致 -# SSA 名在文本输出中非单调递增的问题(llc 报错: -# "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 拒绝编译。 -# -# 算法: -# 1. 扫描所有 BB 指令行,按文本输出顺序收集数字 SSA 名 %N -# 2. 按首次出现顺序建立 old→new 映射(从 0 递增) -# 3. 替换所有指令行文本中的 %N 为新编号 -# -# 注意: -# - 仅重编号纯数字 SSA 名(%0, %1, ...) -# - 命名 SSA 名(%entry, %.tmp, %self)、块标签、全局符号 @N 不重编号 -# - 仅对 define(有函数体)调用,declare 无函数体无需处理 -# - 当前 TransPyV 参数多为命名(%self/%env/%n),非数字,无需重编号参数 -# ============================================================ - -# 判定字符是否为十进制数字 -def _is_digit_ch(ch: t.CChar) -> int: - """判断字符是否是 '0'-'9' - - ch 是值类型 (t.CChar = i8),TPC 已修复值类型 is None 处理: - 值类型 is None 恒为 false(生成 i1 0 常量),不会解引用空指针。 - """ - if ch is None: - return 0 - if ch >= '0' and ch <= '9': - return 1 - return 0 - - -# ============================================================ -# i32 数组的安全访问(显式字节偏移) -# -# TransPyC 的 ptr[i] 指针索引可能被当作字节索引(i8*)而非 i32* 索引, -# 导致内存损坏。必须显式计算字节偏移:addr = base + idx * 4,再转型为 i32*。 -# 参见 HandlesTranslator.py:358 的 t.CUInt64T(ptr) + offset * sizeof 用法。 -# ============================================================ -def _map_get_i32(map_arr: str, idx: int) -> int: - """读取 i32 数组 map_arr[idx](显式字节偏移 idx*4) - - 使用 (rowptr[int])(uint64) 强转将字节偏移转为 i32*,再用 c.Deref 解引用。 - TPC 已修复类型联合强转(_HandleTypeUnionCastLlvm 添加 fallback),此写法可用。 - """ - if map_arr is None: - return -1 - byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4 - elem_ptr: rowptr[int] = (rowptr[int])(byte_off) - return c.Deref(elem_ptr) - - -def _map_set_i32(map_arr: str, idx: int, val: int): - """写入 map_arr[idx] = val(i32 数组,显式字节偏移 idx*4) - - 使用 (rowptr[int])(uint64) 强转 + c.DerefAs 解引用赋值。 - TPC 已修复类型联合强转,此写法可用。 - """ - if map_arr is None: - return - byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4 - elem_ptr: rowptr[int] = (rowptr[int])(byte_off) - c.DerefAs(elem_ptr, val) - - -def _scan_max_ssa_in_text(text: str, cur_max: int) -> int: - """扫描文本,找出 %N(N 为数字)中最大的 N,与 cur_max 比较返回更大值 - - 仅匹配 '%' 后紧跟数字的情况,跳过命名 SSA 名(%entry 等)。 - """ - result: int = cur_max - p: str = text - while p is not None and p[0] != '\0': - if p[0] == '%': - nxt: str = p + 1 - if nxt is not None and _is_digit_ch(nxt[0]) != 0: - num: int = string.atoi(nxt) - if num > result: - result = num - p = p + 1 - return result - - -def _find_max_ssa_number(func: rowptr[Function]) -> int: - """遍历所有 BB 指令行,找出最大的 %N 数字;无数字 SSA 名返回 -1""" - max_n: int = -1 - if func is None or func.Blocks is None: - return max_n - blocks: rowptr[GSList[BasicBlock]] = func.Blocks - blk: rowptr[BasicBlock] = blocks.Head - blk_steps: t.CSizeT = 0 - while blk is not None: - blk_steps += 1 - if blk_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 扫描终止)\n", func.Name) - stdlib.exit(1) - if blk.Lines is not None: - lines: rowptr[GSList[Line]] = blk.Lines - line: rowptr[Line] = lines.Head - line_steps: t.CSizeT = 0 - while line is not None: - line_steps += 1 - if line_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 扫描终止)\n", - func.Name, blk.Name) - stdlib.exit(1) - if line.Buf is not None: - max_n = _scan_max_ssa_in_text(line.Buf, max_n) - line = line.Next - blk = blk.Next - return max_n - - -def _assign_new_numbers(text: str, - map_arr: str, - max_n: int, next_new: int) -> int: - """扫描文本,对每个 %N(数字),若 map_arr[N]==-1 则分配 next_new - - 返回更新后的 next_new。按文本首次出现顺序建立映射。 - """ - result: int = next_new - p: str = text - while p is not None and p[0] != '\0': - if p[0] == '%': - nxt: str = p + 1 - if nxt is not None and _is_digit_ch(nxt[0]) != 0: - num: int = string.atoi(nxt) - if num >= 0 and num <= max_n: - if _map_get_i32(map_arr, num) == -1: - _map_set_i32(map_arr, num, result) - result += 1 - p = p + 1 - return result - - -def _replace_ssa_numbers(text: str, - pool: rowptr[memhub.MemBuddy], - map_arr: str, - max_n: int) -> str: - """构建新文本,将 %N 替换为映射后的新编号 - - 逐字符扫描:遇到 %N(数字)查映射表替换,其他字符原样复制。 - 返回新分配的缓冲区(调用方无需释放旧 Buf,pool 统一管理)。 - """ - src_len: t.CSizeT = string.strlen(text) - # 新缓冲区:最坏情况每个数字变长(位数增加),分配 src_len*2 + 64 - new_size: t.CSizeT = src_len * 2 + 64 - new_buf: str = pool.alloc(new_size) - if new_buf is None: - return None - # 一次性分配数字缓冲区(循环中复用) - num_buf: str = pool.alloc(16) - if num_buf is None: - new_buf[0] = '\0' - return new_buf - - dst_idx: t.CSizeT = 0 - src_p: str = text - while src_p is not None and src_p[0] != '\0': - if dst_idx + 16 >= new_size: - # 缓冲区不足,截断 - 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 字节,先读后查会越界读远端内存 - if num >= 0 and num <= max_n: - new_num: int = _map_get_i32(map_arr, num) - if new_num != -1: - # 写入 '%' - new_buf[dst_idx] = '%' - dst_idx += 1 - # 写入新编号 - num_buf[0] = '\0' - viperlib.snprintf(num_buf, 16, "%d", new_num) - k: t.CSizeT = 0 - while num_buf[k] != '\0' and dst_idx + 1 < new_size: - new_buf[dst_idx] = num_buf[k] - dst_idx += 1 - k += 1 - # 跳过旧数字字符 - 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 - # 普通字符:直接复制 - new_buf[dst_idx] = src_p[0] - dst_idx += 1 - src_p = src_p + 1 - - new_buf[dst_idx] = '\0' - return new_buf - - -def RenumberSSA(func: rowptr[Function], pool: rowptr[memhub.MemBuddy]): - """重编号函数内所有数字 SSA 名,确保按文本输出顺序单调递增 - - 在 FunctionPrint 序列化函数体之前调用。 - 仅对 define(IsDeclared==0)且 Blocks 非空 的函数生效。 - """ - if func is None or pool is None: - return - if func.IsDeclared == 1: - return # declare 无函数体 - if func.Blocks is None: - return - - # 1. 第一遍扫描:找出最大 SSA 编号 - max_n: int = _find_max_ssa_number(func) - if max_n < 0: - return # 无数字 SSA 名 - - # 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: t.CSizeT = (max_n + 1) * 4 # int = 4 字节 - 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. 第二遍扫描:按文本顺序分配新编号 - next_new: int = 0 - blocks: rowptr[GSList[BasicBlock]] = func.Blocks - blk: rowptr[BasicBlock] = blocks.Head - blk_steps: t.CSizeT = 0 - while blk is not None: - blk_steps += 1 - if blk_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 重编号终止)\n", func.Name) - stdlib.exit(1) - if blk.Lines is not None: - lines: rowptr[GSList[Line]] = blk.Lines - line: rowptr[Line] = lines.Head - line_steps: t.CSizeT = 0 - while line is not None: - line_steps += 1 - if line_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 重编号终止)\n", - func.Name, blk.Name) - stdlib.exit(1) - if line.Buf is not None: - next_new = _assign_new_numbers(line.Buf, map_arr, max_n, next_new) - line = line.Next - blk = blk.Next - - # 4. 检查是否有需要重编号的(old != new) - need_renumber: int = 0 - i: int = 0 - while i <= max_n: - v: int = _map_get_i32(map_arr, i) - if v != -1 and v != i: - need_renumber = 1 - break - i += 1 - if need_renumber == 0: - return # 编号已单调,无需替换 - - # 5. 第三遍扫描:替换所有指令行文本 - blk = blocks.Head - blk_steps = 0 - while blk is not None: - blk_steps += 1 - if blk_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 替换终止)\n", func.Name) - stdlib.exit(1) - if blk.Lines is not None: - lines: rowptr[GSList[Line]] = blk.Lines - line: rowptr[Line] = lines.Head - line_steps = 0 - while line is not None: - line_steps += 1 - if line_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 替换终止)\n", - func.Name, blk.Name) - stdlib.exit(1) - if line.Buf is not None: - new_buf: str = _replace_ssa_numbers( - line.Buf, pool, map_arr, max_n) - if new_buf is not None: - line.Buf = new_buf - line.Len = string.strlen(new_buf) - line = line.Next - blk = blk.Next - - -# ============================================================ -# 本地窥孔优化(文本层,输出前执行,保持语义) -# 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 任意、无其它取址/别名用途。 -# ============================================================ -def _peep_is_startwith(s: str, pfx: str) -> int: - if s is None or pfx is None: - return 0 - pl: t.CSizeT = string.strlen(pfx) - if pl == 0: - return 1 - if string.strlen(s) < pl: - return 0 - return 1 if string.strncmp(s, pfx, pl) == 0 else 0 - - -def _peep_replace_token(pool: rowptr[memhub.MemBuddy], - line_buf: str, tok: str, repl: str, - tlen: t.CSizeT, rlen: t.CSizeT) -> str: - """将 line_buf 中独立记号 % 替换为 repl,返回新缓冲(无变化返回原指针) - - tlen/rlen = strlen(tok)/strlen(repl):由调用方在行循环外预先求好(同一 - ltok/safe_val 会作用于本函数处理的所有行),避免每行重复求长。 - """ - 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 从未排除任一可能命中,去掉后命中判定不变。 - hit: int = 0 - i: t.CSizeT = 0 - while line_buf[i] != '\0': - if line_buf[i] == '%' and line_buf[i + 1] == tok[0] \ - and string.strncmp(line_buf + i + 1, tok, tlen) == 0: - nxt: int = 0 - if line_buf[i + 1 + tlen] != '\0': - cc: int = line_buf[i + 1 + tlen] - if (cc >= 48 and cc <= 57) or (cc >= 65 and cc <= 90) or (cc >= 97 and cc <= 122) or cc == 95: - nxt = 1 - if nxt == 0: - hit = 1 - break - i += 1 - if hit == 0: - return line_buf - ln: t.CSizeT = string.strlen(line_buf) - # 缓冲大小上界:单行最多 ⌊ln/2⌋ 次替换,每次去掉 ≥2 字节(%+名)换入 rlen 字节; - # 最坏输出 ≤ ln + (ln/2)·rlen + NUL。ln + 1 + rlen·(ln+1) 恒覆盖该上界, - # 避免 K≥2 且替换串更长时把新缓冲写穿(随机内存破坏)。 - out: str = pool.alloc(ln + 1 + rlen * (ln + 1)) - if out is None: - return line_buf - op: t.CSizeT = 0 - oq: t.CSizeT = 0 - i = 0 - while i < ln: - if line_buf[i] == '%' and i + 1 + tlen <= ln \ - and line_buf[i + 1] == tok[0] \ - and string.strncmp(line_buf + i + 1, tok, tlen) == 0: - nxt2: int = 0 - if i + 1 + tlen < ln: - cc2: int = line_buf[i + 1 + tlen] - if (cc2 >= 48 and cc2 <= 57) or (cc2 >= 65 and cc2 <= 90) or (cc2 >= 97 and cc2 <= 122) or cc2 == 95: - nxt2 = 1 - if nxt2 == 0: - j: t.CSizeT = 0 - while j < rlen: - out[oq] = repl[j] - oq += 1 - j += 1 - i += 1 + tlen - continue - out[oq] = line_buf[i] - oq += 1 - i += 1 - out[oq] = '\0' - return out - - -def _peep_last_ref_eq(sb: str, a_name: str, nl: t.CSizeT) -> int: - """行内最后一个 '%name' 记号是否等于 a_name(store/load 的指针操作数在行尾) - - nl = strlen(a_name):由调用方在 alloca 槽全量行扫描外预先求好(a_name 在 - 同一次槽扫描中不变),避免每行调用重复 strlen。 - """ - if sb is None or a_name is None: - return 0 - # 单趟定位最后一个 '%'(原实现反复 strchr 逐个跳转,每个 '%' 一次函数调用) - r: str = None - probe: str = sb - while probe[0] != '\0': - if probe[0] == '%': - r = probe - probe += 1 - if r is None: - return 0 - # 先比首字符(廉价拒绝),首字符相同才进入 strncmp - 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: - return 1 - return 0 - - -def _peep_alloc_name(eb: str, pool: rowptr[memhub.MemBuddy]) -> str: - """从 '%name = alloca ...' 提取 name(不含 %),失败返回 None""" - if eb is None or eb[0] != '%': - return None - eqpos: str = string.strstr(eb, " = alloca ") - if eqpos is None: - return None - nlen: t.CSizeT = t.CSizeT(t.CUInt64T(eqpos) - t.CUInt64T(eb)) - 1 - if nlen == 0: - return None - nm: str = pool.alloc(nlen + 1) - if nm is None: - return None - string.strncpy(nm, eb + 1, nlen) - nm[nlen] = '\0' - return nm - - -def _peep_is_alloc_of(eb: str, a_name: str, nl: t.CSizeT) -> int: - """判断 eb 是否为 a_name 的 alloca 定义行,等价于对 _peep_alloc_name(eb) 与 - a_name 做 strcmp 是否为 0(不再分配名字缓冲)。 - - 先比名字(strncmp,名字不同立即拒绝),仅当名字逐字节一致时才做 - " = alloca " 子串定位——把原实现「每一个 '%' 行都调一次 strstr」的 - O(槽数×行数) 次 strstr 降为每槽仅命中行才调用。 - nl = strlen(a_name);nlen==0(名字为空)在原实现返回 None,故此处同样拒绝。 - 等价性:_peep_alloc_name 取第一个 " = alloca " 之前、'%' 之后的字节为名字, - 故名字等长且一致 ⟺ strncmp 相同且该子串恰起于 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)。 - if eb[1] != a_name[0]: - return 0 - if string.strncmp(eb + 1, a_name, nl) != 0: - return 0 - eqpos: str = string.strstr(eb, " = alloca ") - if eqpos is not None and t.CUInt64T(eqpos) == t.CUInt64T(eb) + 1 + nl: - return 1 - return 0 - - -def _peephole_opt(func: rowptr[Function], pool: rowptr[memhub.MemBuddy]): - """文本层本地窥孔:T1 ret zeroinitializer;T2 入口槽 store→load 转发""" - if func is None or func.Blocks is None or pool is None: - return - # 注:TPC 对 "变量 = 指针.字段.成员" 两级自动解引用链会静默丢弃赋值 - # (编译出的 IR 直接读未初始化栈变量 → 随机栈错误)。必须拆成单级: - # 先取字段到局部 rowptr,再判空后取 .Head(与 FunctionPrint 同一模式)。 - func_blocks: rowptr[GSList[BasicBlock]] = func.Blocks - blk: rowptr[BasicBlock] = None - if func_blocks is not None: - blk = func_blocks.Head - - # ---------- T1: ret zeroinitializer ---------- - blk_t1 = blk - while blk_t1 is not None: - line1: rowptr[Line] = None - if blk_t1.Lines is not None: - t1_lines: rowptr[GSList[Line]] = blk_t1.Lines - line1 = t1_lines.Head - while line1 is not None: - if line1.Buf is not None: - zi: str = string.strstr(line1.Buf, " zeroinitializer") - if zi is not None and _peep_is_startwith(line1.Buf, "ret ") != 0: - ty_len: t.CSizeT = t.CSizeT(t.CUInt64T(zi) - t.CUInt64T(line1.Buf)) - 4 - if ty_len > 0: - ty_buf: str = pool.alloc(ty_len + 1) - if ty_buf is not None: - string.strncpy(ty_buf, line1.Buf + 4, ty_len) - ty_buf[ty_len] = '\0' - lit: str = None - if ty_buf[0] == 'i': - lit = "0" - elif string.strchr(ty_buf, 42) is not None: # '*' - lit = "null" - if lit is not None: - nb: str = pool.alloc(ty_len + 16) - if nb is not None: - nb[0] = '\0' - string.strcat(nb, "ret ") - string.strcat(nb, ty_buf) - string.strcat(nb, " ") - string.strcat(nb, lit) - line1.Buf = nb - line1 = line1.Next - blk_t1 = blk_t1.Next - - # ---------- T2: entry alloca 槽转发 ---------- - entry_blk: rowptr[BasicBlock] = blk - if entry_blk is None: - return - entry_lines: rowptr[GSList[Line]] = entry_blk.Lines - if entry_lines is None: - return - e_line: rowptr[Line] = entry_lines.Head - while e_line is not None: - eb: str = e_line.Buf - if eb is not None: - a_name: str = _peep_alloc_name(eb, pool) - if a_name is not None: - # a_name 在本槽的全量行扫描中不变,strlen 只求一次(原每行各求一次) - a_name_len: t.CSizeT = string.strlen(a_name) - st_val: str = None - st_ok: int = 0 - st_count: int = 0 - st_line_order: int = -1 - load_ltoks: str = pool.alloc(8 * 64) - load_cnt: int = 0 - load_entry_order: t.CInt64T = 0 - alloca_order: int = -1 - order: int = 0 - unsafe: int = 0 - # 第一遍:统计本 alloca 的 store/load/其它引用及行序 - blk_s = blk - while blk_s is not None: - l_s: rowptr[Line] = None - if blk_s.Lines is not None: - scan_lines: rowptr[GSList[Line]] = blk_s.Lines - l_s = scan_lines.Head - while l_s is not None: - sb: str = l_s.Buf - if sb is not None: - is_alloc_def: int = 0 - if sb[0] == '%': - if _peep_is_alloc_of(sb, a_name, a_name_len) != 0: - is_alloc_def = 1 - if is_alloc_def == 0 and _peep_last_ref_eq(sb, a_name, a_name_len) != 0: - if _peep_is_startwith(sb, "store ") != 0: - st_count += 1 - if st_count == 1: - vcomma: str = string.strstr(sb + 6, ", ") - if vcomma is not None: - vlen: t.CSizeT = t.CSizeT(t.CUInt64T(vcomma) - t.CUInt64T(sb + 6)) - if vlen > 0: - vbuf: str = pool.alloc(vlen + 1) - if vbuf is not None: - string.strncpy(vbuf, sb + 6, vlen) - vbuf[vlen] = '\0' - st_val = vbuf - st_ok = 1 - else: - unsafe = 1 - if blk_s == entry_blk: - st_line_order = order - elif string.strstr(sb, " = load ") is not None: - ls: str = string.strstr(sb, "%") - if ls is not None and load_cnt < 64: - lname_len: t.CSizeT = t.CSizeT( - t.CUInt64T(string.strstr(sb, " = load ")) - t.CUInt64T(ls)) - lname: str = pool.alloc(lname_len + 1) - if lname is not None: - string.strncpy(lname, ls + 1, lname_len - 1) - lname[lname_len - 1] = '\0' - slotp: str | t.CPtr = t.CPtr(t.CUInt64T(load_ltoks) + load_cnt * 8) - slotp[0] = lname - load_cnt += 1 - if blk_s == entry_blk and st_line_order < 0: - load_entry_order += 1 - else: - # lname 分配失败:该 load 不会进 load_ltoks,但删除阶段 - # 仍会按槽名删其 def 行,use 无法改写为 store 值 → 悬空。 - # 保守放弃本 alloca 槽的 T2 转发。 - 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 仍合法)。 - unsafe = 1 - else: - unsafe = 1 - if sb is not None: - order += 1 - l_s = l_s.Next - blk_s = blk_s.Next - # 判据 - 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: - pct: str = string.strchr(st_val, '%') - if pct is None: - sp1: str = string.strchr(st_val, ' ') - if sp1 is not None and sp1[1] != 0: - safe_val = sp1 + 1 - else: - safe_val = st_val - else: - 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 只求一次(原每行各求一次) - safe_len: t.CSizeT = string.strlen(safe_val) - li2: int = 0 - while li2 < load_cnt: - slotsp: str | t.CPtr = t.CPtr(t.CUInt64T(load_ltoks) + li2 * 8) - ltok: str = slotsp[0] - if ltok is not None: - # ltok 在该 load 名的全函数行扫描中不变,strlen 只求一次 - ltok_len: t.CSizeT = string.strlen(ltok) - blk_r = blk - while blk_r is not None: - lr: rowptr[Line] = None - if blk_r.Lines is not None: - repl_lines: rowptr[GSList[Line]] = blk_r.Lines - lr = repl_lines.Head - while lr is not None: - if lr.Buf is not None: - nb2: str = _peep_replace_token(pool, lr.Buf, ltok, safe_val, - ltok_len, safe_len) - if nb2 is not None and nb2 is not lr.Buf: - lr.Buf = nb2 - lr = lr.Next - blk_r = blk_r.Next - li2 += 1 - # 删除:alloca def / 唯一 store / 全部 load 行 - blk_d = blk - while blk_d is not None: - ld: rowptr[Line] = None - if blk_d.Lines is not None: - kill_lines: rowptr[GSList[Line]] = blk_d.Lines - ld = kill_lines.Head - while ld is not None: - db: str = ld.Buf - if db is not None: - kill: int = 0 - if db[0] == '%' and db[1] == a_name[0]: - if _peep_is_alloc_of(db, a_name, a_name_len) != 0: - kill = 1 - if kill == 0 and _peep_last_ref_eq(db, a_name, a_name_len) != 0: - if _peep_is_startwith(db, "store ") != 0 \ - or string.strstr(db, " = load ") is not None: - kill = 1 - if kill != 0: - ld.Buf = None - ld = ld.Next - blk_d = blk_d.Next - e_line = e_line.Next - - # ---------- T3: no-op bitcast 消除 ---------- - _peep_noop_bitcast(func, pool) - - -def _peep_noop_bitcast(func: rowptr[Function], pool: rowptr[memhub.MemBuddy]): - """T3: 消除 no-op bitcast(bitcast %x to ,两侧类型相同)。 - - 生成端(虚调用/联合位宽转换)会产生同类型恒等 bitcast(如 - `%6 = bitcast i8* %ok_msg to i8*`),纯冗余指令。把 %x 的所有引用 - 替换为源操作数(%SSA/@global/常量 token)后删除该行。 - 保守限制:类型与操作数均为不含空格的简单 token。 - """ - if func is None or pool is None or func.Blocks is None: - return - # 单趟前向扫描:命中合法候选 → 全函数替换引用 + 删行,游标从下一行续扫; - # 遇到首个不满足条件的 bitcast 即停止。与原实现"每删一行后从块头整函数 - # 重扫、遇无效候选即 return"的删除结果逐一等价(替换只改操作数 token, - # 不会把游标之前的非候选行变成候选),只是消除了 O(K·C) 的重扫二次方。 - blk: rowptr[BasicBlock] = func.Blocks.Head - while blk is not None: - ln: rowptr[Line] = None - if blk.Lines is not None: - ln = blk.Lines.Head - while ln is not None: - nxt: rowptr[Line] = ln.Next - if ln.Buf is not None: - eqpos: str = string.strstr(ln.Buf, " = bitcast ") - if eqpos is not None: - nlen: t.CSizeT = t.CSizeT(t.CUInt64T(eqpos) - t.CUInt64T(ln.Buf)) - 1 - nm_buf: str = None - src_buf: str = None - if nlen > 0: - after: str = eqpos + 11 # 跳过 " = bitcast " - sp1: str = string.strchr(after, ' ') - tpos: str = string.strstr(after, " to ") - if sp1 is not None and tpos is not None \ - and t.CUInt64T(sp1) < t.CUInt64T(tpos): - tyA_len: t.CSizeT = t.CSizeT(t.CUInt64T(sp1) - t.CUInt64T(after)) - src_st: str = sp1 + 1 - src_len: t.CSizeT = t.CSizeT(t.CUInt64T(tpos) - t.CUInt64T(src_st)) - tyB: str = tpos + 4 - tyB_len: t.CSizeT = string.strlen(tyB) - if src_len > 0 and string.strchr(tyB, ' ') is None \ - and t.CUInt64T(string.strchr(src_st, ' ')) == t.CUInt64T(tpos) \ - and tyA_len == tyB_len \ - and string.strncmp(after, tyB, tyA_len) == 0: - nm_buf = pool.alloc(nlen + 1) - if nm_buf is not None: - string.strncpy(nm_buf, ln.Buf + 1, nlen) - nm_buf[nlen] = '\0' - src_buf = pool.alloc(src_len + 1) - if src_buf is not None: - string.strncpy(src_buf, src_st, src_len) - src_buf[src_len] = '\0' - if nm_buf is None or src_buf is None: - # 首个不满足条件的 bitcast:停止(与原语义一致) - return - # 全函数把 %nm 的引用替换为源操作数 - # nm_buf/src_buf 在全函数行扫描中不变,strlen 只求一次(原每行各求一次) - nm_buf_len: t.CSizeT = string.strlen(nm_buf) - src_buf_len: t.CSizeT = string.strlen(src_buf) - blk_r: rowptr[BasicBlock] = func.Blocks.Head - while blk_r is not None: - if blk_r.Lines is not None: - ln2: rowptr[Line] = blk_r.Lines.Head - while ln2 is not None: - if ln2.Buf is not None: - rb: str = _peep_replace_token(pool, ln2.Buf, nm_buf, src_buf, - nm_buf_len, src_buf_len) - if rb is not None and rb is not ln2.Buf: - ln2.Buf = rb - ln2.Len = string.strlen(rb) - ln2 = ln2.Next - blk_r = blk_r.Next - ln.Buf = None - ln = nxt - blk = blk.Next - - -# ============================================================ -# FunctionPrint: 将函数序列化为 IR 文本 -# -# 格式: -# define @() { -# : -# -# -# : -# -# } -# ============================================================ -def FunctionPrint(buf: str, size: t.CSizeT, - func: rowptr[Function], pool: rowptr[memhub.MemBuddy]): - """将函数 func 序列化为 IR 文本写入 buf""" - if func is None or buf is None or size == 0: return - - # 先本地窥孔(ret zeroinitializer→0/null、入口槽 store→load 转发、 - # no-op bitcast 消除):窥孔按"名称一致性"匹配、删除行置 Buf=None; - # RenumberSSA 遍历时已跳过 Buf=None 行 → 窥孔之后再重编号,输出 SSA - # 编号连续(窥孔删行后不再出现 %1→%5 空洞,文件更紧凑)。 - # RenumberSSA 内部会跳过 declare 函数(IsDeclared==1),无需在此判断。 - if func.IsDeclared == 0 and G_PeepEnable != 0: - _peephole_opt(func, pool) - - RenumberSSA(func, pool) - - ty_buf: str = pool.alloc(128) - if ty_buf is None: return - ty_buf[0] = '\0' - - # define @() - if func.IsDeclared == 1: - _append_cstr(buf, size, "declare ") - else: - # 泛型特化类方法(如 GSList[NameEntry].__before_init__):多个模块各自 - # 特化生成同名定义 → ld.lld duplicate symbol warning。特化方法代码由 - # 相同模板+类型参数生成,定义相同,输出 linkonce_odr 让链接器合并。 - if func.Name is not None and string.strchr(func.Name, 91) is not None: - _append_cstr(buf, size, "define linkonce_odr ") - else: - _append_cstr(buf, size, "define ") - ty_buf[0] = '\0' - TypePrint(ty_buf, 128, func.RetTy, pool) - _append_cstr(buf, size, ty_buf) - _append_cstr(buf, size, " @") - if func.Name is not None: - if _ll_name_needs_quote(func.Name) != 0: - _append_cstr(buf, size, "\"") - _append_cstr(buf, size, func.Name) - _append_cstr(buf, size, "\"") - else: - _append_cstr(buf, size, func.Name) - _append_cstr(buf, size, "(") - - # 参数列表(从 GSList[Param].Head 遍历) - params: rowptr[GSList[Param]] = func.Params - cur: rowptr[Param] = None - if params is not None: - cur = params.Head - first: int = 1 - cur_steps: t.CSizeT = 0 - while cur is not None: - cur_steps += 1 - if cur_steps > IR_DUMP_CHAIN_LIMIT: - if params is not None: - stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params.Count=%zu,遍历超 %d 步,print 终止)\n", - func.Name, params.Count, IR_DUMP_CHAIN_LIMIT) - else: - stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params=None,遍历超 %d 步,print 终止)\n", - func.Name, IR_DUMP_CHAIN_LIMIT) - stdlib.exit(1) - if first == 0: - _append_cstr(buf, size, ", ") - # 参数属性(如 "noundef"/"nocapture")在类型之前 - if cur.Attrs is not None: - _append_cstr(buf, size, cur.Attrs) - _append_cstr(buf, size, " ") - ty_buf[0] = '\0' - TypePrint(ty_buf, 128, cur.Ty, pool) - _append_cstr(buf, size, ty_buf) - # declare 和 define 都打印参数名(如果有) - if cur.Name is not None: - _append_cstr(buf, size, " ") - # LLVM IR 要求参数名以 % 开头 - if cur.Name[0] != '%': - _append_cstr(buf, size, "%") - _append_cstr(buf, size, cur.Name) - cur = cur.Next - first = 0 - # 变参函数追加 ... - 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, ")") - - # 函数级属性(如 "nounwind"/"noinline")在 ) 之后 - if func.Attrs is not None: - _append_cstr(buf, size, " ") - _append_cstr(buf, size, func.Attrs) - # B5:字符串属性(section 等)必须用属性组 #N 引用(LLVM 不接受内联 - # section="..."),函数行输出 #N,组定义在函数尾/模块中输出。 - if func.AttrGroup is not None and func.AttrGroupId > 0: - ag_ref: str = pool.alloc(32) - if ag_ref is not None: - viperlib.snprintf(ag_ref, 32, " #%d", func.AttrGroupId) - _append_cstr(buf, size, ag_ref) - - if func.IsDeclared == 1: - _append_cstr(buf, size, "\n") - _append_attr_group_def(buf, size, pool, func) - return - - # 函数体 - _append_cstr(buf, size, " {\n") - - blocks: rowptr[GSList[BasicBlock]] = func.Blocks - blk: rowptr[BasicBlock] = None - if blocks is not None: - blk = blocks.Head - blk_steps: t.CSizeT = 0 - while blk is not None: - blk_steps += 1 - if blk_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,print 终止)\n", func.Name) - stdlib.exit(1) - # 块标签 - if blk.Name is not None: - _append_cstr(buf, size, blk.Name) - _append_cstr(buf, size, ":\n") - # 指令行(从 GSList[Line].Head 遍历) - line: rowptr[Line] = None - if blk.Lines is not None: - lines: rowptr[GSList[Line]] = blk.Lines - line = lines.Head - has_lines: int = 0 - line_steps: t.CSizeT = 0 - while line is not None: - line_steps += 1 - if line_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,print 终止)\n", - func.Name, blk.Name) - stdlib.exit(1) - if line.Buf is not None: - _append_cstr(buf, size, " ") - _append_cstr(buf, size, line.Buf) - _append_cstr(buf, size, "\n") - has_lines = 1 - line = line.Next - # 块未终止兜底:LLVM IR 要求每个基本块以终止指令(br/ret/unreachable)结尾。 - # 两种情况需要补 unreachable: - # 1. 空块(has_lines==0):标签后无任何指令 - # 2. 有指令但未终止(IsTerminated==0):如 break/continue 后的 dead.N 块 - # 被后续语句填入指令但缺少终止符,导致下一个标签处报 - # "expected instruction opcode" - if has_lines == 0 or blk.IsTerminated == 0: - _append_cstr(buf, size, " unreachable\n") - blk = blk.Next - - _append_cstr(buf, size, "}\n") - _append_attr_group_def(buf, size, pool, func) - - -def _append_attr_group_def(buf: str, size: t.CSizeT, - pool: rowptr[memhub.MemBuddy], - func: rowptr[Function]): - """输出字符串属性组定义: attributes #N = { "section"="..." }""" - if func is None or func.AttrGroup is None or func.AttrGroupId <= 0: - return - ag_buf: str = pool.alloc(192) - if ag_buf is None: - return - viperlib.snprintf(ag_buf, 192, "attributes #%d = { %s }\n", - func.AttrGroupId, func.AttrGroup) - _append_cstr(buf, size, ag_buf) - - -# ============================================================ -# 内部辅助 -# ============================================================ -# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, -# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 -# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 -_g_last_dst: t.CUInt64T = 0 -_g_last_pos: t.CSizeT = 0 - -def _append_cstr(dst: str, dst_size: t.CSizeT, src: str): - """将 C 字符串 src 追加到 dst 末尾""" - global _g_last_dst, _g_last_pos - if dst is None or src is None: return - dlen: t.CSizeT = 0 - if _g_last_dst == t.CUInt64T(dst): - dlen = _g_last_pos - # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 - if dlen > 0 and dst[0] == '\0': - dlen = 0 - # 缓存一致性校验:缓存结尾必须仍是当前文本末端(末端 NUL 且前一字符非 NUL)。 - # 多个模块轮流写同一 buf、或 pool 复用地址残留旧内容时缓存 pos 会过期, - # 追加落到首个 NUL 之后内容"看不见"(LLvmLiteTest Test5/9 根因)。失败回退 strlen。 - if dlen == 0: - if dst[0] != '\0': - dlen = string.strlen(dst) - elif dst[dlen] != '\0' or dst[dlen - 1] == '\0': - dlen = string.strlen(dst) - else: - dlen = string.strlen(dst) - _g_last_dst = t.CUInt64T(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 - if dlen >= dst_size: - return - slen: t.CSizeT = string.strlen(src) - remain: t.CSizeT = dst_size - dlen - if remain <= 0: return - i: t.CSizeT = 0 - while i < slen and i + 1 < remain: - dst[dlen + i] = src[i] - i += 1 - dst[dlen + i] = '\0' - _g_last_pos = dlen + i - - -# ============================================================ -# 字段访问器(供其他模块绕过 stub 类型限制使用) -# -# 跨模块编译时,使用方模块只有 Function/Param 的 stub 类型 -# (字段不足且字段类型可能错误),导致 TransPyC 静默跳过 -# 字段访问(不生成 GEP,不报错),字段值永远为 null。 -# -# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问 -# 所有字段。其他模块通过函数调用获取字段值,绕过 stub 限制。 -# ============================================================ -def function_get_name(func: rowptr[Function]) -> str: - """获取函数名""" - if func is None: - return None - return func.Name - - -def function_get_name_len(func: rowptr[Function]) -> t.CSizeT: - """获取函数名长度(创建时一次算好,恒等于 strlen(Name)) - - 对齐 function_get_name 的跨模块 stub 绕过模式:使用方模块只有 Function - 的 stub 类型(字段不足且字段类型可能错误),直接读 Name/NameLen 字段会被 - TransPyC 静默跳过;本访问器在拥有完整类型定义的本模块内取字段,跨模块经 - 函数调用取得长度,避免对同一函数名反复 strlen(全程序 strlen 热点)。 - """ - if func is None: - return 0 - return func.NameLen - - -def function_get_ret_ty(func: rowptr[Function]) -> rowptr[LLVMType]: - """获取函数返回类型""" - if func is None: - return None - return func.RetTy - - -def function_get_params(func: rowptr[Function]) -> rowptr[GSList[Param]]: - """获取函数参数链表容器""" - if func is None: - return None - return func.Params - - -def function_get_param_head(func: rowptr[Function]) -> rowptr[Param]: - """获取函数参数链表头节点(一步到位,避免调用方访问 GSList.Head)""" - if func is None: - return None - params: rowptr[GSList[Param]] = func.Params - if params is None: - return None - return params.Head - - -def function_get_next(func: rowptr[Function]) -> rowptr[Function]: - """获取链表下一个函数""" - if func is None: - return None - return func.Next - - -def function_is_declared(func: rowptr[Function]) -> int: - """获取函数是否为声明(1=declare 仅声明, 0=define 定义)""" - if func is None: - return 0 - return func.IsDeclared - - -def param_get_name(param: rowptr[Param]) -> str: - """获取参数名""" - if param is None: - return None - return param.Name - - -def param_get_ty(param: rowptr[Param]) -> rowptr[LLVMType]: - """获取参数类型""" - if param is None: - return None - return param.Ty - - -def param_get_next(param: rowptr[Param]) -> rowptr[Param]: - """获取链表下一个参数""" - if param is None: - return None - return param.Next +import t, c +from stdint import * +import string +import viperlib +import memhub +import stdio +import stdlib +from linkedlist import GSListNode, GSList +from .__types import LLVMType, ParamNode, TypePrint + + +# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast,与 __module.vp 对齐)。 +# 函数/基本块/指令行链的 Next 被越界写破坏成环时,遍历无限循环挂起; +# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位"。 +IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000 + +# 窥孔优化总开关(T2 转发/ret 归零、T3 no-op bitcast 消除)。 +# TPV 命令行 --no-llvmpeephole 会将其置 0:FunctionPrint 序列化时跳过全部本地窥孔, +# 便于与关闭窥孔的 IR 对比调试(RenumberSSA 仍保留,保证输出 SSA 编号合法)。 +G_PeepEnable: bool = True + + +# ============================================================ +# Line: 指令文本行 +# +# 每条 LLVM IR 指令序列化为一行文本,通过链表组织。 +# 继承 GSListNode[Line] 获取强类型 Next: Line|CPtr +# +# 注意: 必须加 @t.NoVTable。否则 TransPyC 为子类添加 vtable 指针(T*), +# 但完全丢失父类 GSListNode[T] 的 Next 字段,导致 struct 布局错位, +# 所有字段访问读取垃圾值(见续 XV 会话根因分析)。 +# ============================================================ +@t.NoVTable +class Line(GSListNode[Line]): + Buf: str # 行文本(NUL 结尾) + Len: t.CSizeT # 文本长度(不含 NUL) + + +# ============================================================ +# BasicBlock: 基本块 +# +# 持有名称 + 指令行链表 + 终止指令标志。 +# 继承 GSListNode[BasicBlock] 获取强类型 Next: BasicBlock|CPtr +# Lines 为 GSList[Line] 指针,提供 O(1) 追加。 +# +# 注意: 必须加 @t.NoVTable(理由同 Line)。 +# ============================================================ +@t.NoVTable +class BasicBlock(GSListNode[BasicBlock]): + Name: str # 块名(如 "entry"/"if.then") + Lines: rawptr[GSList[Line]] # 指令行链表容器(Head/Tail/Count) + IsTerminated: int # 1=已有终止指令(br/ret) + + +# ============================================================ +# Param: 函数参数 +# +# 继承 GSListNode[Param] 获取强类型 Next: Param|CPtr +# Attrs 存储参数属性文本(如 "noundef"/"nocapture"/"readonly"),None=无属性 +# +# 注意: 必须加 @t.NoVTable(理由同 Line)。 +# ============================================================ +@t.NoVTable +class Param(GSListNode[Param]): + Ty: rawptr[LLVMType] # 参数类型 + Name: str # 参数名(如 "%0"/"%a") + Attrs: str # 参数属性文本(None=无属性) + + +# ============================================================ +# Function: 函数 +# +# 持有名称 + 返回类型 + 参数链表 + 基本块链表。 +# 继承 GSListNode[Function] 获取强类型 Next: Function|CPtr +# Params/Blocks 为 GSList 指针,提供 O(1) 追加。 +# Attrs 存储函数级属性文本(如 "nounwind"/"noinline"),None=无属性 +# +# 注意: 必须加 @t.NoVTable(理由同 Line)。 +# ============================================================ +@t.NoVTable +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: t.CSizeT # 函数名长度(创建时一次算好,恒等于 strlen(Name);追加在末尾,既有字段偏移不变) + + +# ============================================================ +# 工厂函数 +# ============================================================ +# Line 硬编码大小: Next(8) + Buf(8) + Len(8) = 24 字节 +LINE_SIZE: t.CDefine = 24 + +def new_line(pool: rawptr[memhub.MemBuddy], text: str) -> rawptr[Line]: + """创建一个指令行,复制 text 到新分配的缓冲区""" + ptr: rawptr[Line] = pool.alloc(LINE_SIZE) + if ptr is None: return None + string.memset(ptr, 0, LINE_SIZE) + if text is not None: + slen: t.CSizeT = string.strlen(text) + buf: str = pool.alloc(slen + 1) + if buf is not None: + string.strcpy(buf, text) + ptr.Buf = buf + ptr.Len = slen + return ptr + + +def _new_gslist(pool: rawptr[memhub.MemBuddy], list_size: t.CSizeT) -> t.CPtr: + """从 pool 分配并零初始化一个 GSList 容器(Head/Tail/Count 全零)""" + 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 字节 +BLOCK_SIZE: t.CDefine = 32 + +def new_basic_block(pool: rawptr[memhub.MemBuddy], name: str) -> rawptr[BasicBlock]: + """创建一个基本块""" + ptr: rawptr[BasicBlock] = pool.alloc(BLOCK_SIZE) + if ptr is None: return None + string.memset(ptr, 0, BLOCK_SIZE) + ptr.Name = name + ptr.Lines = _new_gslist(pool, 24) # GSList: Head+Tail+Count + ptr.IsTerminated = 0 + return ptr + + +# Param 硬编码大小: Next(8) + Ty(8) + Name(8) + Attrs(8) = 32 字节 +PARAM_SIZE: t.CDefine = 32 + +def new_param(pool: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType], + name: str) -> rawptr[Param]: + """创建一个函数参数""" + ptr: rawptr[Param] = pool.alloc(PARAM_SIZE) + if ptr is None: return None + string.memset(ptr, 0, PARAM_SIZE) + ptr.Ty = ty + ptr.Name = name + ptr.Attrs = None + 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) = 80 字节 +FUNC_SIZE: t.CDefine = 80 + +def new_function(pool: rawptr[memhub.MemBuddy], name: str, + ret_ty: rawptr[LLVMType]) -> rawptr[Function]: + """创建一个函数""" + ptr: rawptr[Function] = pool.alloc(FUNC_SIZE) + if ptr is None: return None + string.memset(ptr, 0, FUNC_SIZE) + ptr.Name = name + ptr.RetTy = ret_ty + ptr.Params = _new_gslist(pool, 24) # GSList: Head+Tail+Count + ptr.Blocks = _new_gslist(pool, 24) + ptr.IsDeclared = 0 + 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 已归零,此处显式赋值以固定语义、不依赖分配器行为)。 + if name is not None: + ptr.NameLen = string.strlen(name) + else: + ptr.NameLen = 0 + return ptr + + +# ============================================================ +# 链表操作(委托 GSList.append) +# ============================================================ +def function_add_param(func: rawptr[Function], param: rawptr[Param]): + """将参数追加到函数参数链表""" + if func is None or param is None: return + # 注意:必须先用局部变量 + 类型参数(对齐 function_move_block_to_end 注释), + # 链式 func.Params.append() 会被编译器解析成 LinkedNode.append 且 this 前移 + # sizeof(LinkedNode)(兄弟 Mixin 兜底误匹配),写穿容器前 48 字节破坏数据结构。 + params: rawptr[GSList[Param]] = func.Params + if params is None: return + params.append(param) + + +def function_add_block(func: rawptr[Function], block: rawptr[BasicBlock]): + """将基本块追加到函数块链表""" + if func is None or block is None: return + blocks: rawptr[GSList[BasicBlock]] = func.Blocks + if blocks is None: return + blocks.append(block) + + +def function_move_block_to_end(func: rawptr[Function], block: rawptr[BasicBlock]): + """将 block 移动到函数块链表末尾(保证 BB 文本顺序与控制流顺序一致)。 + + 用于解决 SSA 名非单调问题:BB 按创建顺序排列,但指令按控制流顺序生成, + 导致 SSA 编号在文本输出时非单调递增,llc 报错。 + 在 position_at_end 时调用此函数,使 BB 顺序与 position 顺序一致。 + + 注意:必须使用 GSList[BasicBlock] 带类型参数,否则 TransPyC 创建不完整 + struct 类型,导致 blocks.Head/Tail/Count 字段访问读取垃圾值。 + """ + if func is None or block is None: return + blocks: rawptr[GSList[BasicBlock]] = func.Blocks + if blocks is None: return + + # 已经是 Tail,无需移动 + if blocks.Tail is block: + return + + # 从链表中移除 block + if blocks.Head is block: + # block 是 Head,直接后移 Head + blocks.Head = block.Next + else: + # 遍历找前驱 + 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 + blocks.append(block) + return + prev.Next = block.Next + + # block.Next 由移除逻辑处理,重置为 None + block.Next = None + + # append 到末尾(不调用 blocks.append 以避免 Count 重复递增) + if blocks.Tail is not None: + blocks.Tail.Next = block + blocks.Tail = block + + +def block_append_line(block: rawptr[BasicBlock], line: rawptr[Line]): + """将指令行追加到基本块""" + if block is None or line is None: return + lines: rawptr[GSList[Line]] = block.Lines + if lines is None: return + lines.append(line) + + +def block_set_terminated(block: rawptr[BasicBlock], val: int): + """设置块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)""" + if block is None: return + block.IsTerminated = val + + +def block_get_terminated(block: rawptr[BasicBlock]) -> int: + """获取块的终止标志(供跨模块调用,避免链式属性访问的 opaque 类型问题)""" + if block is None: + return 1 + return block.IsTerminated + + +def block_append_text(pool: rawptr[memhub.MemBuddy], block: rawptr[BasicBlock], + text: str): + """创建指令行并追加到基本块""" + if block is None or text is None: return + line: rawptr[Line] = new_line(pool, text) + if line is not None: + block_append_line(block, line) + + +def block_insert_text_before_terminator(pool: rawptr[memhub.MemBuddy], + block: rawptr[BasicBlock], + text: str): + """创建指令行并插入到基本块终止指令之前 + + 如果块未终止,追加到末尾。 + 如果块已终止(有 br/ret),在终止指令之前插入,确保 alloca 等指令 + 不出现在终止指令之后。 + """ + 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 + # 块未终止:直接追加 + if block.IsTerminated == 0: + block_append_line(block, line) + return + # 块已终止:在 Tail(终止指令)之前插入 + lines: rawptr[GSList[Line]] = block.Lines + if lines.Head is None: + block_append_line(block, line) + return + # 只有一个节点:在 Head 之前插入 + if lines.Head is lines.Tail: + line.Next = lines.Head + lines.Head = line + lines.Count += 1 + return + # 找到 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: + # 找不到前驱,回退到追加 + block_append_line(block, line) + return + # 在 prev 和 Tail 之间插入 line + line.Next = lines.Tail + prev.Next = line + lines.Count += 1 + + +# ============================================================ +# 属性设置 +# ============================================================ +def function_set_attrs(func: rawptr[Function], attrs: str): + """设置函数级属性文本(如 "nounwind"/"noinline")""" + if func is None: return + func.Attrs = attrs + + +_g_attr_group_counter: int = 0 + +def function_set_attr_group(func: rawptr[Function], group: str): + """设置函数字符串属性组(如 '"section"=".text.startup"')并分配组编号 #N。 + + LLVM 字符串属性(带值的,如 section)不能内联在函数签名行, + 必须用属性组:函数行输出 #N,组定义输出在函数尾。 + """ + global _g_attr_group_counter + if func is None or group is None: + return + func.AttrGroup = group + _g_attr_group_counter += 1 + func.AttrGroupId = _g_attr_group_counter + + +def param_set_attrs(param: rawptr[Param], attrs: str): + """设置参数属性文本(如 "noundef"/"nocapture"/"readonly")""" + if param is None: return + param.Attrs = attrs + + +# ============================================================ +# _ll_name_needs_quote - 检查 LLVM 标识符是否需要加引号 +# +# LLVM IR 规范:标识符合法字符集为 [-a-zA-Z$._][-a-zA-Z$._0-9]* +# 以数字开头或含非合法字符(如 '['、']'、' '、'@' 等)时必须用双引号包围。 +# '.' 是合法字符,不需要加引号(如 @.str、@llvm.memcpy 都不加引号)。 +# SHA1 命名空间的函数名(如 "4dad2ba72ed7ba09.GSListNode[Param]")含 '['、']', +# 需要加引号;但纯 hex+点 的名字(如 "abc.def")不需要。 +# ============================================================ +def _ll_name_needs_quote(name: str) -> int: + """检查 LLVM 标识符是否需要加引号(以数字开头或含非合法字符) + + LLVM IR 合法字符:a-z, A-Z, 0-9, -, $, ., _ + 首字符是数字时需要加引号(LLVM 要求局部/全局标识符首字符非数字,除非加引号)。 + 含其他字符(如 '['、']'、空格)时需要加引号。 + """ + if name is None: + return 0 + c0: t.CChar = name[0] + if c0 >= '0' and c0 <= '9': + return 1 + name_len: t.CSizeT = string.strlen(name) + i: t.CSizeT = 0 + while i < name_len: + ch: t.CChar = name[i] + # 合法字符:a-z, A-Z, 0-9, -, $, ., _ + if not ((ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or + (ch >= '0' and ch <= '9') or ch == '-' or ch == '$' or + ch == '.' or ch == '_'): + return 1 + i += 1 + return 0 + + +# ============================================================ +# RenumberSSA: 重编号函数内数字 SSA 名 +# +# 解决 build_alloca_at_entry 在入口块终止指令前插入 alloca 导致 +# SSA 名在文本输出中非单调递增的问题(llc 报错: +# "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 拒绝编译。 +# +# 算法: +# 1. 扫描所有 BB 指令行,按文本输出顺序收集数字 SSA 名 %N +# 2. 按首次出现顺序建立 old→new 映射(从 0 递增) +# 3. 替换所有指令行文本中的 %N 为新编号 +# +# 注意: +# - 仅重编号纯数字 SSA 名(%0, %1, ...) +# - 命名 SSA 名(%entry, %.tmp, %self)、块标签、全局符号 @N 不重编号 +# - 仅对 define(有函数体)调用,declare 无函数体无需处理 +# - 当前 TransPyV 参数多为命名(%self/%env/%n),非数字,无需重编号参数 +# ============================================================ + +# 判定字符是否为十进制数字 +def _is_digit_ch(ch: t.CChar) -> int: + """判断字符是否是 '0'-'9' + + ch 是值类型 (t.CChar = i8),TPC 已修复值类型 is None 处理: + 值类型 is None 恒为 false(生成 i1 0 常量),不会解引用空指针。 + """ + if ch is None: + return 0 + if ch >= '0' and ch <= '9': + return 1 + return 0 + + +# ============================================================ +# i32 数组的安全访问(显式字节偏移) +# +# TransPyC 的 ptr[i] 指针索引可能被当作字节索引(i8*)而非 i32* 索引, +# 导致内存损坏。必须显式计算字节偏移:addr = base + idx * 4,再转型为 i32*。 +# 参见 HandlesTranslator.py:358 的 t.CUInt64T(ptr) + offset * sizeof 用法。 +# ============================================================ +def _map_get_i32(map_arr: str, idx: int) -> int: + """读取 i32 数组 map_arr[idx](显式字节偏移 idx*4) + + 使用 (rawptr[int])(uint64) 强转将字节偏移转为 i32*,再用 c.Deref 解引用。 + TPC 已修复类型联合强转(_HandleTypeUnionCastLlvm 添加 fallback),此写法可用。 + """ + if map_arr is None: + return -1 + byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4 + elem_ptr: rawptr[int] = (rawptr[int])(byte_off) + return c.Deref(elem_ptr) + + +def _map_set_i32(map_arr: str, idx: int, val: int): + """写入 map_arr[idx] = val(i32 数组,显式字节偏移 idx*4) + + 使用 (rawptr[int])(uint64) 强转 + c.DerefAs 解引用赋值。 + TPC 已修复类型联合强转,此写法可用。 + """ + if map_arr is None: + return + byte_off: t.CUInt64T = t.CUInt64T(map_arr) + idx * 4 + elem_ptr: rawptr[int] = (rawptr[int])(byte_off) + c.DerefAs(elem_ptr, val) + + +def _scan_max_ssa_in_text(text: str, cur_max: int) -> int: + """扫描文本,找出 %N(N 为数字)中最大的 N,与 cur_max 比较返回更大值 + + 仅匹配 '%' 后紧跟数字的情况,跳过命名 SSA 名(%entry 等)。 + """ + result: int = cur_max + p: str = text + while p is not None and p[0] != '\0': + if p[0] == '%': + nxt: str = p + 1 + if nxt is not None and _is_digit_ch(nxt[0]) != 0: + num: int = string.atoi(nxt) + if num > result: + result = num + p = p + 1 + return result + + +def _find_max_ssa_number(func: rawptr[Function]) -> int: + """遍历所有 BB 指令行,找出最大的 %N 数字;无数字 SSA 名返回 -1""" + max_n: int = -1 + if func is None or func.Blocks is None: + return max_n + blocks: rawptr[GSList[BasicBlock]] = func.Blocks + blk: rawptr[BasicBlock] = blocks.Head + blk_steps: t.CSizeT = 0 + while blk is not None: + blk_steps += 1 + if blk_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 扫描终止)\n", func.Name) + stdlib.exit(1) + if blk.Lines is not None: + lines: rawptr[GSList[Line]] = blk.Lines + line: rawptr[Line] = lines.Head + line_steps: t.CSizeT = 0 + while line is not None: + line_steps += 1 + if line_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 扫描终止)\n", + func.Name, blk.Name) + stdlib.exit(1) + if line.Buf is not None: + max_n = _scan_max_ssa_in_text(line.Buf, max_n) + line = line.Next + blk = blk.Next + return max_n + + +def _assign_new_numbers(text: str, + map_arr: str, + max_n: int, next_new: int) -> int: + """扫描文本,对每个 %N(数字),若 map_arr[N]==-1 则分配 next_new + + 返回更新后的 next_new。按文本首次出现顺序建立映射。 + """ + result: int = next_new + p: str = text + while p is not None and p[0] != '\0': + if p[0] == '%': + nxt: str = p + 1 + if nxt is not None and _is_digit_ch(nxt[0]) != 0: + num: int = string.atoi(nxt) + if num >= 0 and num <= max_n: + if _map_get_i32(map_arr, num) == -1: + _map_set_i32(map_arr, num, result) + result += 1 + p = p + 1 + return result + + +def _replace_ssa_numbers(text: str, + pool: rawptr[memhub.MemBuddy], + map_arr: str, + max_n: int) -> str: + """构建新文本,将 %N 替换为映射后的新编号 + + 逐字符扫描:遇到 %N(数字)查映射表替换,其他字符原样复制。 + 返回新分配的缓冲区(调用方无需释放旧 Buf,pool 统一管理)。 + """ + src_len: t.CSizeT = string.strlen(text) + # 新缓冲区:最坏情况每个数字变长(位数增加),分配 src_len*2 + 64 + new_size: t.CSizeT = src_len * 2 + 64 + new_buf: str = pool.alloc(new_size) + if new_buf is None: + return None + # 一次性分配数字缓冲区(循环中复用) + num_buf: str = pool.alloc(16) + if num_buf is None: + new_buf[0] = '\0' + return new_buf + + dst_idx: t.CSizeT = 0 + src_p: str = text + while src_p is not None and src_p[0] != '\0': + if dst_idx + 16 >= new_size: + # 缓冲区不足,截断 + 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 字节,先读后查会越界读远端内存 + if num >= 0 and num <= max_n: + new_num: int = _map_get_i32(map_arr, num) + if new_num != -1: + # 写入 '%' + new_buf[dst_idx] = '%' + dst_idx += 1 + # 写入新编号 + num_buf[0] = '\0' + viperlib.snprintf(num_buf, 16, "%d", new_num) + k: t.CSizeT = 0 + while num_buf[k] != '\0' and dst_idx + 1 < new_size: + new_buf[dst_idx] = num_buf[k] + dst_idx += 1 + k += 1 + # 跳过旧数字字符 + 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 + # 普通字符:直接复制 + new_buf[dst_idx] = src_p[0] + dst_idx += 1 + src_p = src_p + 1 + + new_buf[dst_idx] = '\0' + return new_buf + + +def RenumberSSA(func: rawptr[Function], pool: rawptr[memhub.MemBuddy]): + """重编号函数内所有数字 SSA 名,确保按文本输出顺序单调递增 + + 在 FunctionPrint 序列化函数体之前调用。 + 仅对 define(IsDeclared==0)且 Blocks 非空 的函数生效。 + """ + if func is None or pool is None: + return + if func.IsDeclared == 1: + return # declare 无函数体 + if func.Blocks is None: + return + + # 1. 第一遍扫描:找出最大 SSA 编号 + max_n: int = _find_max_ssa_number(func) + if max_n < 0: + return # 无数字 SSA 名 + + # 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: t.CSizeT = (max_n + 1) * 4 # int = 4 字节 + 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. 第二遍扫描:按文本顺序分配新编号 + next_new: int = 0 + blocks: rawptr[GSList[BasicBlock]] = func.Blocks + blk: rawptr[BasicBlock] = blocks.Head + blk_steps: t.CSizeT = 0 + while blk is not None: + blk_steps += 1 + if blk_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 重编号终止)\n", func.Name) + stdlib.exit(1) + if blk.Lines is not None: + lines: rawptr[GSList[Line]] = blk.Lines + line: rawptr[Line] = lines.Head + line_steps: t.CSizeT = 0 + while line is not None: + line_steps += 1 + if line_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 重编号终止)\n", + func.Name, blk.Name) + stdlib.exit(1) + if line.Buf is not None: + next_new = _assign_new_numbers(line.Buf, map_arr, max_n, next_new) + line = line.Next + blk = blk.Next + + # 4. 检查是否有需要重编号的(old != new) + need_renumber: int = 0 + i: int = 0 + while i <= max_n: + v: int = _map_get_i32(map_arr, i) + if v != -1 and v != i: + need_renumber = 1 + break + i += 1 + if need_renumber == 0: + return # 编号已单调,无需替换 + + # 5. 第三遍扫描:替换所有指令行文本 + blk = blocks.Head + blk_steps = 0 + while blk is not None: + blk_steps += 1 + if blk_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 替换终止)\n", func.Name) + stdlib.exit(1) + if blk.Lines is not None: + lines: rawptr[GSList[Line]] = blk.Lines + line: rawptr[Line] = lines.Head + line_steps = 0 + while line is not None: + line_steps += 1 + if line_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 替换终止)\n", + func.Name, blk.Name) + stdlib.exit(1) + if line.Buf is not None: + new_buf: str = _replace_ssa_numbers( + line.Buf, pool, map_arr, max_n) + if new_buf is not None: + line.Buf = new_buf + line.Len = string.strlen(new_buf) + line = line.Next + blk = blk.Next + + +# ============================================================ +# 本地窥孔优化(文本层,输出前执行,保持语义) +# 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 任意、无其它取址/别名用途。 +# ============================================================ +def _peep_is_startwith(s: str, pfx: str) -> int: + if s is None or pfx is None: + return 0 + pl: t.CSizeT = string.strlen(pfx) + if pl == 0: + return 1 + if string.strlen(s) < pl: + return 0 + return 1 if string.strncmp(s, pfx, pl) == 0 else 0 + + +def _peep_replace_token(pool: rawptr[memhub.MemBuddy], + line_buf: str, tok: str, repl: str, + tlen: t.CSizeT, rlen: t.CSizeT) -> str: + """将 line_buf 中独立记号 % 替换为 repl,返回新缓冲(无变化返回原指针) + + tlen/rlen = strlen(tok)/strlen(repl):由调用方在行循环外预先求好(同一 + ltok/safe_val 会作用于本函数处理的所有行),避免每行重复求长。 + """ + 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 从未排除任一可能命中,去掉后命中判定不变。 + hit: int = 0 + i: t.CSizeT = 0 + while line_buf[i] != '\0': + if line_buf[i] == '%' and line_buf[i + 1] == tok[0] \ + and string.strncmp(line_buf + i + 1, tok, tlen) == 0: + nxt: int = 0 + if line_buf[i + 1 + tlen] != '\0': + cc: int = line_buf[i + 1 + tlen] + if (cc >= 48 and cc <= 57) or (cc >= 65 and cc <= 90) or (cc >= 97 and cc <= 122) or cc == 95: + nxt = 1 + if nxt == 0: + hit = 1 + break + i += 1 + if hit == 0: + return line_buf + ln: t.CSizeT = string.strlen(line_buf) + # 缓冲大小上界:单行最多 ⌊ln/2⌋ 次替换,每次去掉 ≥2 字节(%+名)换入 rlen 字节; + # 最坏输出 ≤ ln + (ln/2)·rlen + NUL。ln + 1 + rlen·(ln+1) 恒覆盖该上界, + # 避免 K≥2 且替换串更长时把新缓冲写穿(随机内存破坏)。 + out: str = pool.alloc(ln + 1 + rlen * (ln + 1)) + if out is None: + return line_buf + op: t.CSizeT = 0 + oq: t.CSizeT = 0 + i = 0 + while i < ln: + if line_buf[i] == '%' and i + 1 + tlen <= ln \ + and line_buf[i + 1] == tok[0] \ + and string.strncmp(line_buf + i + 1, tok, tlen) == 0: + nxt2: int = 0 + if i + 1 + tlen < ln: + cc2: int = line_buf[i + 1 + tlen] + if (cc2 >= 48 and cc2 <= 57) or (cc2 >= 65 and cc2 <= 90) or (cc2 >= 97 and cc2 <= 122) or cc2 == 95: + nxt2 = 1 + if nxt2 == 0: + j: t.CSizeT = 0 + while j < rlen: + out[oq] = repl[j] + oq += 1 + j += 1 + i += 1 + tlen + continue + out[oq] = line_buf[i] + oq += 1 + i += 1 + out[oq] = '\0' + return out + + +def _peep_last_ref_eq(sb: str, a_name: str, nl: t.CSizeT) -> int: + """行内最后一个 '%name' 记号是否等于 a_name(store/load 的指针操作数在行尾) + + nl = strlen(a_name):由调用方在 alloca 槽全量行扫描外预先求好(a_name 在 + 同一次槽扫描中不变),避免每行调用重复 strlen。 + """ + if sb is None or a_name is None: + return 0 + # 单趟定位最后一个 '%'(原实现反复 strchr 逐个跳转,每个 '%' 一次函数调用) + r: str = None + probe: str = sb + while probe[0] != '\0': + if probe[0] == '%': + r = probe + probe += 1 + if r is None: + return 0 + # 先比首字符(廉价拒绝),首字符相同才进入 strncmp + 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: + return 1 + return 0 + + +def _peep_alloc_name(eb: str, pool: rawptr[memhub.MemBuddy]) -> str: + """从 '%name = alloca ...' 提取 name(不含 %),失败返回 None""" + if eb is None or eb[0] != '%': + return None + eqpos: str = string.strstr(eb, " = alloca ") + if eqpos is None: + return None + nlen: t.CSizeT = t.CSizeT(t.CUInt64T(eqpos) - t.CUInt64T(eb)) - 1 + if nlen == 0: + return None + nm: str = pool.alloc(nlen + 1) + if nm is None: + return None + string.strncpy(nm, eb + 1, nlen) + nm[nlen] = '\0' + return nm + + +def _peep_is_alloc_of(eb: str, a_name: str, nl: t.CSizeT) -> int: + """判断 eb 是否为 a_name 的 alloca 定义行,等价于对 _peep_alloc_name(eb) 与 + a_name 做 strcmp 是否为 0(不再分配名字缓冲)。 + + 先比名字(strncmp,名字不同立即拒绝),仅当名字逐字节一致时才做 + " = alloca " 子串定位——把原实现「每一个 '%' 行都调一次 strstr」的 + O(槽数×行数) 次 strstr 降为每槽仅命中行才调用。 + nl = strlen(a_name);nlen==0(名字为空)在原实现返回 None,故此处同样拒绝。 + 等价性:_peep_alloc_name 取第一个 " = alloca " 之前、'%' 之后的字节为名字, + 故名字等长且一致 ⟺ strncmp 相同且该子串恰起于 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)。 + if eb[1] != a_name[0]: + return 0 + if string.strncmp(eb + 1, a_name, nl) != 0: + return 0 + eqpos: str = string.strstr(eb, " = alloca ") + if eqpos is not None and t.CUInt64T(eqpos) == t.CUInt64T(eb) + 1 + nl: + return 1 + return 0 + + +def _peephole_opt(func: rawptr[Function], pool: rawptr[memhub.MemBuddy]): + """文本层本地窥孔:T1 ret zeroinitializer;T2 入口槽 store→load 转发""" + if func is None or func.Blocks is None or pool is None: + return + # 注:TPC 对 "变量 = 指针.字段.成员" 两级自动解引用链会静默丢弃赋值 + # (编译出的 IR 直接读未初始化栈变量 → 随机栈错误)。必须拆成单级: + # 先取字段到局部 rawptr,再判空后取 .Head(与 FunctionPrint 同一模式)。 + func_blocks: rawptr[GSList[BasicBlock]] = func.Blocks + blk: rawptr[BasicBlock] = None + if func_blocks is not None: + blk = func_blocks.Head + + # ---------- T1: ret zeroinitializer ---------- + blk_t1 = blk + while blk_t1 is not None: + line1: rawptr[Line] = None + if blk_t1.Lines is not None: + t1_lines: rawptr[GSList[Line]] = blk_t1.Lines + line1 = t1_lines.Head + while line1 is not None: + if line1.Buf is not None: + zi: str = string.strstr(line1.Buf, " zeroinitializer") + if zi is not None and _peep_is_startwith(line1.Buf, "ret ") != 0: + ty_len: t.CSizeT = t.CSizeT(t.CUInt64T(zi) - t.CUInt64T(line1.Buf)) - 4 + if ty_len > 0: + ty_buf: str = pool.alloc(ty_len + 1) + if ty_buf is not None: + string.strncpy(ty_buf, line1.Buf + 4, ty_len) + ty_buf[ty_len] = '\0' + lit: str = None + if ty_buf[0] == 'i': + lit = "0" + elif string.strchr(ty_buf, 42) is not None: # '*' + lit = "null" + if lit is not None: + nb: str = pool.alloc(ty_len + 16) + if nb is not None: + nb[0] = '\0' + string.strcat(nb, "ret ") + string.strcat(nb, ty_buf) + string.strcat(nb, " ") + string.strcat(nb, lit) + line1.Buf = nb + line1 = line1.Next + blk_t1 = blk_t1.Next + + # ---------- T2: entry alloca 槽转发 ---------- + entry_blk: rawptr[BasicBlock] = blk + if entry_blk is None: + return + entry_lines: rawptr[GSList[Line]] = entry_blk.Lines + if entry_lines is None: + return + e_line: rawptr[Line] = entry_lines.Head + while e_line is not None: + eb: str = e_line.Buf + if eb is not None: + a_name: str = _peep_alloc_name(eb, pool) + if a_name is not None: + # a_name 在本槽的全量行扫描中不变,strlen 只求一次(原每行各求一次) + a_name_len: t.CSizeT = string.strlen(a_name) + st_val: str = None + st_ok: int = 0 + st_count: int = 0 + st_line_order: int = -1 + load_ltoks: str = pool.alloc(8 * 64) + load_cnt: int = 0 + load_entry_order: t.CInt64T = 0 + alloca_order: int = -1 + order: int = 0 + unsafe: int = 0 + # 第一遍:统计本 alloca 的 store/load/其它引用及行序 + blk_s = blk + while blk_s is not None: + l_s: rawptr[Line] = None + if blk_s.Lines is not None: + scan_lines: rawptr[GSList[Line]] = blk_s.Lines + l_s = scan_lines.Head + while l_s is not None: + sb: str = l_s.Buf + if sb is not None: + is_alloc_def: int = 0 + if sb[0] == '%': + if _peep_is_alloc_of(sb, a_name, a_name_len) != 0: + is_alloc_def = 1 + if is_alloc_def == 0 and _peep_last_ref_eq(sb, a_name, a_name_len) != 0: + if _peep_is_startwith(sb, "store ") != 0: + st_count += 1 + if st_count == 1: + vcomma: str = string.strstr(sb + 6, ", ") + if vcomma is not None: + vlen: t.CSizeT = t.CSizeT(t.CUInt64T(vcomma) - t.CUInt64T(sb + 6)) + if vlen > 0: + vbuf: str = pool.alloc(vlen + 1) + if vbuf is not None: + string.strncpy(vbuf, sb + 6, vlen) + vbuf[vlen] = '\0' + st_val = vbuf + st_ok = 1 + else: + unsafe = 1 + if blk_s == entry_blk: + st_line_order = order + elif string.strstr(sb, " = load ") is not None: + ls: str = string.strstr(sb, "%") + if ls is not None and load_cnt < 64: + lname_len: t.CSizeT = t.CSizeT( + t.CUInt64T(string.strstr(sb, " = load ")) - t.CUInt64T(ls)) + lname: str = pool.alloc(lname_len + 1) + if lname is not None: + string.strncpy(lname, ls + 1, lname_len - 1) + lname[lname_len - 1] = '\0' + slotp: str | t.CPtr = t.CPtr(t.CUInt64T(load_ltoks) + load_cnt * 8) + slotp[0] = lname + load_cnt += 1 + if blk_s == entry_blk and st_line_order < 0: + load_entry_order += 1 + else: + # lname 分配失败:该 load 不会进 load_ltoks,但删除阶段 + # 仍会按槽名删其 def 行,use 无法改写为 store 值 → 悬空。 + # 保守放弃本 alloca 槽的 T2 转发。 + 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 仍合法)。 + unsafe = 1 + else: + unsafe = 1 + if sb is not None: + order += 1 + l_s = l_s.Next + blk_s = blk_s.Next + # 判据 + 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: + pct: str = string.strchr(st_val, '%') + if pct is None: + sp1: str = string.strchr(st_val, ' ') + if sp1 is not None and sp1[1] != 0: + safe_val = sp1 + 1 + else: + safe_val = st_val + else: + 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 只求一次(原每行各求一次) + safe_len: t.CSizeT = string.strlen(safe_val) + li2: int = 0 + while li2 < load_cnt: + slotsp: str | t.CPtr = t.CPtr(t.CUInt64T(load_ltoks) + li2 * 8) + ltok: str = slotsp[0] + if ltok is not None: + # ltok 在该 load 名的全函数行扫描中不变,strlen 只求一次 + ltok_len: t.CSizeT = string.strlen(ltok) + blk_r = blk + while blk_r is not None: + lr: rawptr[Line] = None + if blk_r.Lines is not None: + repl_lines: rawptr[GSList[Line]] = blk_r.Lines + lr = repl_lines.Head + while lr is not None: + if lr.Buf is not None: + nb2: str = _peep_replace_token(pool, lr.Buf, ltok, safe_val, + ltok_len, safe_len) + if nb2 is not None and nb2 is not lr.Buf: + lr.Buf = nb2 + lr = lr.Next + blk_r = blk_r.Next + li2 += 1 + # 删除:alloca def / 唯一 store / 全部 load 行 + blk_d = blk + while blk_d is not None: + ld: rawptr[Line] = None + if blk_d.Lines is not None: + kill_lines: rawptr[GSList[Line]] = blk_d.Lines + ld = kill_lines.Head + while ld is not None: + db: str = ld.Buf + if db is not None: + kill: int = 0 + if db[0] == '%' and db[1] == a_name[0]: + if _peep_is_alloc_of(db, a_name, a_name_len) != 0: + kill = 1 + if kill == 0 and _peep_last_ref_eq(db, a_name, a_name_len) != 0: + if _peep_is_startwith(db, "store ") != 0 \ + or string.strstr(db, " = load ") is not None: + kill = 1 + if kill != 0: + ld.Buf = None + ld = ld.Next + blk_d = blk_d.Next + e_line = e_line.Next + + # ---------- T3: no-op bitcast 消除 ---------- + _peep_noop_bitcast(func, pool) + + +def _peep_noop_bitcast(func: rawptr[Function], pool: rawptr[memhub.MemBuddy]): + """T3: 消除 no-op bitcast(bitcast %x to ,两侧类型相同)。 + + 生成端(虚调用/联合位宽转换)会产生同类型恒等 bitcast(如 + `%6 = bitcast i8* %ok_msg to i8*`),纯冗余指令。把 %x 的所有引用 + 替换为源操作数(%SSA/@global/常量 token)后删除该行。 + 保守限制:类型与操作数均为不含空格的简单 token。 + """ + if func is None or pool is None or func.Blocks is None: + return + # 单趟前向扫描:命中合法候选 → 全函数替换引用 + 删行,游标从下一行续扫; + # 遇到首个不满足条件的 bitcast 即停止。与原实现"每删一行后从块头整函数 + # 重扫、遇无效候选即 return"的删除结果逐一等价(替换只改操作数 token, + # 不会把游标之前的非候选行变成候选),只是消除了 O(K·C) 的重扫二次方。 + blk: rawptr[BasicBlock] = func.Blocks.Head + while blk is not None: + ln: rawptr[Line] = None + if blk.Lines is not None: + ln = blk.Lines.Head + while ln is not None: + nxt: rawptr[Line] = ln.Next + if ln.Buf is not None: + eqpos: str = string.strstr(ln.Buf, " = bitcast ") + if eqpos is not None: + nlen: t.CSizeT = t.CSizeT(t.CUInt64T(eqpos) - t.CUInt64T(ln.Buf)) - 1 + nm_buf: str = None + src_buf: str = None + if nlen > 0: + after: str = eqpos + 11 # 跳过 " = bitcast " + sp1: str = string.strchr(after, ' ') + tpos: str = string.strstr(after, " to ") + if sp1 is not None and tpos is not None \ + and t.CUInt64T(sp1) < t.CUInt64T(tpos): + tyA_len: t.CSizeT = t.CSizeT(t.CUInt64T(sp1) - t.CUInt64T(after)) + src_st: str = sp1 + 1 + src_len: t.CSizeT = t.CSizeT(t.CUInt64T(tpos) - t.CUInt64T(src_st)) + tyB: str = tpos + 4 + tyB_len: t.CSizeT = string.strlen(tyB) + if src_len > 0 and string.strchr(tyB, ' ') is None \ + and t.CUInt64T(string.strchr(src_st, ' ')) == t.CUInt64T(tpos) \ + and tyA_len == tyB_len \ + and string.strncmp(after, tyB, tyA_len) == 0: + nm_buf = pool.alloc(nlen + 1) + if nm_buf is not None: + string.strncpy(nm_buf, ln.Buf + 1, nlen) + nm_buf[nlen] = '\0' + src_buf = pool.alloc(src_len + 1) + if src_buf is not None: + string.strncpy(src_buf, src_st, src_len) + src_buf[src_len] = '\0' + if nm_buf is None or src_buf is None: + # 首个不满足条件的 bitcast:停止(与原语义一致) + return + # 全函数把 %nm 的引用替换为源操作数 + # nm_buf/src_buf 在全函数行扫描中不变,strlen 只求一次(原每行各求一次) + nm_buf_len: t.CSizeT = string.strlen(nm_buf) + src_buf_len: t.CSizeT = string.strlen(src_buf) + blk_r: rawptr[BasicBlock] = func.Blocks.Head + while blk_r is not None: + if blk_r.Lines is not None: + ln2: rawptr[Line] = blk_r.Lines.Head + while ln2 is not None: + if ln2.Buf is not None: + rb: str = _peep_replace_token(pool, ln2.Buf, nm_buf, src_buf, + nm_buf_len, src_buf_len) + if rb is not None and rb is not ln2.Buf: + ln2.Buf = rb + ln2.Len = string.strlen(rb) + ln2 = ln2.Next + blk_r = blk_r.Next + ln.Buf = None + ln = nxt + blk = blk.Next + + +# ============================================================ +# FunctionPrint: 将函数序列化为 IR 文本 +# +# 格式: +# define @() { +# : +# +# +# : +# +# } +# ============================================================ +def FunctionPrint(buf: str, size: t.CSizeT, + func: rawptr[Function], pool: rawptr[memhub.MemBuddy]): + """将函数 func 序列化为 IR 文本写入 buf""" + if func is None or buf is None or size == 0: return + + # 先本地窥孔(ret zeroinitializer→0/null、入口槽 store→load 转发、 + # no-op bitcast 消除):窥孔按"名称一致性"匹配、删除行置 Buf=None; + # RenumberSSA 遍历时已跳过 Buf=None 行 → 窥孔之后再重编号,输出 SSA + # 编号连续(窥孔删行后不再出现 %1→%5 空洞,文件更紧凑)。 + # RenumberSSA 内部会跳过 declare 函数(IsDeclared==1),无需在此判断。 + if func.IsDeclared == 0 and G_PeepEnable != 0: + _peephole_opt(func, pool) + + RenumberSSA(func, pool) + + ty_buf: str = pool.alloc(128) + if ty_buf is None: return + ty_buf[0] = '\0' + + # define @() + if func.IsDeclared == 1: + _append_cstr(buf, size, "declare ") + else: + # 泛型特化类方法(如 GSList[NameEntry].__before_init__):多个模块各自 + # 特化生成同名定义 → ld.lld duplicate symbol warning。特化方法代码由 + # 相同模板+类型参数生成,定义相同,输出 linkonce_odr 让链接器合并。 + if func.Name is not None and string.strchr(func.Name, 91) is not None: + _append_cstr(buf, size, "define linkonce_odr ") + else: + _append_cstr(buf, size, "define ") + ty_buf[0] = '\0' + TypePrint(ty_buf, 128, func.RetTy, pool) + _append_cstr(buf, size, ty_buf) + _append_cstr(buf, size, " @") + if func.Name is not None: + if _ll_name_needs_quote(func.Name) != 0: + _append_cstr(buf, size, "\"") + _append_cstr(buf, size, func.Name) + _append_cstr(buf, size, "\"") + else: + _append_cstr(buf, size, func.Name) + _append_cstr(buf, size, "(") + + # 参数列表(从 GSList[Param].Head 遍历) + params: rawptr[GSList[Param]] = func.Params + cur: rawptr[Param] = None + if params is not None: + cur = params.Head + first: int = 1 + cur_steps: t.CSizeT = 0 + while cur is not None: + cur_steps += 1 + if cur_steps > IR_DUMP_CHAIN_LIMIT: + if params is not None: + stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params.Count=%zu,遍历超 %d 步,print 终止)\n", + func.Name, params.Count, IR_DUMP_CHAIN_LIMIT) + else: + stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params=None,遍历超 %d 步,print 终止)\n", + func.Name, IR_DUMP_CHAIN_LIMIT) + stdlib.exit(1) + if first == 0: + _append_cstr(buf, size, ", ") + # 参数属性(如 "noundef"/"nocapture")在类型之前 + if cur.Attrs is not None: + _append_cstr(buf, size, cur.Attrs) + _append_cstr(buf, size, " ") + ty_buf[0] = '\0' + TypePrint(ty_buf, 128, cur.Ty, pool) + _append_cstr(buf, size, ty_buf) + # declare 和 define 都打印参数名(如果有) + if cur.Name is not None: + _append_cstr(buf, size, " ") + # LLVM IR 要求参数名以 % 开头 + if cur.Name[0] != '%': + _append_cstr(buf, size, "%") + _append_cstr(buf, size, cur.Name) + cur = cur.Next + first = 0 + # 变参函数追加 ... + 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, ")") + + # 函数级属性(如 "nounwind"/"noinline")在 ) 之后 + if func.Attrs is not None: + _append_cstr(buf, size, " ") + _append_cstr(buf, size, func.Attrs) + # B5:字符串属性(section 等)必须用属性组 #N 引用(LLVM 不接受内联 + # section="..."),函数行输出 #N,组定义在函数尾/模块中输出。 + if func.AttrGroup is not None and func.AttrGroupId > 0: + ag_ref: str = pool.alloc(32) + if ag_ref is not None: + viperlib.snprintf(ag_ref, 32, " #%d", func.AttrGroupId) + _append_cstr(buf, size, ag_ref) + + if func.IsDeclared == 1: + _append_cstr(buf, size, "\n") + _append_attr_group_def(buf, size, pool, func) + return + + # 函数体 + _append_cstr(buf, size, " {\n") + + blocks: rawptr[GSList[BasicBlock]] = func.Blocks + blk: rawptr[BasicBlock] = None + if blocks is not None: + blk = blocks.Head + blk_steps: t.CSizeT = 0 + while blk is not None: + blk_steps += 1 + if blk_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,print 终止)\n", func.Name) + stdlib.exit(1) + # 块标签 + if blk.Name is not None: + _append_cstr(buf, size, blk.Name) + _append_cstr(buf, size, ":\n") + # 指令行(从 GSList[Line].Head 遍历) + line: rawptr[Line] = None + if blk.Lines is not None: + lines: rawptr[GSList[Line]] = blk.Lines + line = lines.Head + has_lines: int = 0 + line_steps: t.CSizeT = 0 + while line is not None: + line_steps += 1 + if line_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,print 终止)\n", + func.Name, blk.Name) + stdlib.exit(1) + if line.Buf is not None: + _append_cstr(buf, size, " ") + _append_cstr(buf, size, line.Buf) + _append_cstr(buf, size, "\n") + has_lines = 1 + line = line.Next + # 块未终止兜底:LLVM IR 要求每个基本块以终止指令(br/ret/unreachable)结尾。 + # 两种情况需要补 unreachable: + # 1. 空块(has_lines==0):标签后无任何指令 + # 2. 有指令但未终止(IsTerminated==0):如 break/continue 后的 dead.N 块 + # 被后续语句填入指令但缺少终止符,导致下一个标签处报 + # "expected instruction opcode" + if has_lines == 0 or blk.IsTerminated == 0: + _append_cstr(buf, size, " unreachable\n") + blk = blk.Next + + _append_cstr(buf, size, "}\n") + _append_attr_group_def(buf, size, pool, func) + + +def _append_attr_group_def(buf: str, size: t.CSizeT, + pool: rawptr[memhub.MemBuddy], + func: rawptr[Function]): + """输出字符串属性组定义: attributes #N = { "section"="..." }""" + if func is None or func.AttrGroup is None or func.AttrGroupId <= 0: + return + ag_buf: str = pool.alloc(192) + if ag_buf is None: + return + viperlib.snprintf(ag_buf, 192, "attributes #%d = { %s }\n", + func.AttrGroupId, func.AttrGroup) + _append_cstr(buf, size, ag_buf) + + +# ============================================================ +# 内部辅助 +# ============================================================ +# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, +# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 +# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 +_g_last_dst: t.CUInt64T = 0 +_g_last_pos: t.CSizeT = 0 + +def _append_cstr(dst: str, dst_size: t.CSizeT, src: str): + """将 C 字符串 src 追加到 dst 末尾""" + global _g_last_dst, _g_last_pos + if dst is None or src is None: return + dlen: t.CSizeT = 0 + if _g_last_dst == t.CUInt64T(dst): + dlen = _g_last_pos + # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 + if dlen > 0 and dst[0] == '\0': + dlen = 0 + # 缓存一致性校验:缓存结尾必须仍是当前文本末端(末端 NUL 且前一字符非 NUL)。 + # 多个模块轮流写同一 buf、或 pool 复用地址残留旧内容时缓存 pos 会过期, + # 追加落到首个 NUL 之后内容"看不见"(LLvmLiteTest Test5/9 根因)。失败回退 strlen。 + if dlen == 0: + if dst[0] != '\0': + dlen = string.strlen(dst) + elif dst[dlen] != '\0' or dst[dlen - 1] == '\0': + dlen = string.strlen(dst) + else: + dlen = string.strlen(dst) + _g_last_dst = t.CUInt64T(dst) + # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, + # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + if dlen >= dst_size: + return + slen: t.CSizeT = string.strlen(src) + remain: t.CSizeT = dst_size - dlen + if remain <= 0: return + i: t.CSizeT = 0 + while i < slen and i + 1 < remain: + dst[dlen + i] = src[i] + i += 1 + dst[dlen + i] = '\0' + _g_last_pos = dlen + i + + +# ============================================================ +# 字段访问器(供其他模块绕过 stub 类型限制使用) +# +# 跨模块编译时,使用方模块只有 Function/Param 的 stub 类型 +# (字段不足且字段类型可能错误),导致 TransPyC 静默跳过 +# 字段访问(不生成 GEP,不报错),字段值永远为 null。 +# +# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问 +# 所有字段。其他模块通过函数调用获取字段值,绕过 stub 限制。 +# ============================================================ +def function_get_name(func: rawptr[Function]) -> str: + """获取函数名""" + if func is None: + return None + return func.Name + + +def function_get_name_len(func: rawptr[Function]) -> t.CSizeT: + """获取函数名长度(创建时一次算好,恒等于 strlen(Name)) + + 对齐 function_get_name 的跨模块 stub 绕过模式:使用方模块只有 Function + 的 stub 类型(字段不足且字段类型可能错误),直接读 Name/NameLen 字段会被 + TransPyC 静默跳过;本访问器在拥有完整类型定义的本模块内取字段,跨模块经 + 函数调用取得长度,避免对同一函数名反复 strlen(全程序 strlen 热点)。 + """ + if func is None: + return 0 + return func.NameLen + + +def function_get_ret_ty(func: rawptr[Function]) -> rawptr[LLVMType]: + """获取函数返回类型""" + if func is None: + return None + return func.RetTy + + +def function_get_params(func: rawptr[Function]) -> rawptr[GSList[Param]]: + """获取函数参数链表容器""" + if func is None: + return None + return func.Params + + +def function_get_param_head(func: rawptr[Function]) -> rawptr[Param]: + """获取函数参数链表头节点(一步到位,避免调用方访问 GSList.Head)""" + if func is None: + return None + params: rawptr[GSList[Param]] = func.Params + if params is None: + return None + return params.Head + + +def function_get_next(func: rawptr[Function]) -> rawptr[Function]: + """获取链表下一个函数""" + if func is None: + return None + return func.Next + + +def function_is_declared(func: rawptr[Function]) -> int: + """获取函数是否为声明(1=declare 仅声明, 0=define 定义)""" + if func is None: + return 0 + return func.IsDeclared + + +def param_get_name(param: rawptr[Param]) -> str: + """获取参数名""" + if param is None: + return None + return param.Name + + +def param_get_ty(param: rawptr[Param]) -> rawptr[LLVMType]: + """获取参数类型""" + if param is None: + return None + return param.Ty + + +def param_get_next(param: rawptr[Param]) -> rawptr[Param]: + """获取链表下一个参数""" + if param is None: + return None + return param.Next diff --git a/llvmlite/__init__.vp b/llvmlite/__init__.vp index 30da0b0..451c6cc 100644 --- a/llvmlite/__init__.vp +++ b/llvmlite/__init__.vp @@ -1,461 +1,461 @@ -import t, c -from stdint import * -import memhub -import string -import viperlib - -# 导入子模块 -from .__types import ( - LLVMType, ParamNode, LLVMTypeCore, - Int1, Int8, Int16, Int32, Int64, Ptr, Func, Void, Array, Struct, - Half, Float, Double, FP128, Label, - new_param_node, param_list_append, - paramnode_get_ty, paramnode_get_next, paramnode_set_ty, paramnode_set_next, - TypePrint, PrintStructDefinition, get_struct_name, - get_struct_fields, get_struct_fcount, DeepCopyType, -) -from .__values import ( - Value, new_value, ConstInt, ConstNull, ConstZero, ConstFloat, SSAValue, ValuePrint, - value_get_ty, value_get_next, - value_set_ty, value_set_next, value_set_name, value_set_isconst, -) -from .__function import ( - Line, BasicBlock, Param, Function, - new_line, new_basic_block, new_param, new_function, - function_add_param, function_add_block, - block_append_line, block_append_text, - function_set_attrs, param_set_attrs, - function_set_attr_group, - function_get_name, function_get_name_len, function_get_ret_ty, function_get_params, - function_get_param_head, function_get_next, function_is_declared, - param_get_name, param_get_ty, param_get_next, - FunctionPrint, G_PeepEnable, -) -from .__module import ( - LLVMModule, GlobalVariable, NamedTypeNode, new_module, new_global_variable, - module_add_function, module_add_global, module_add_named_type, - module_set_named_type_packed, - module_set_target, module_set_datalayout, - global_set_constant, global_set_linkage, global_set_unnamed_addr, global_set_section, - LLVMModulePrint, OUTPUT_FULL, OUTPUT_STUB, OUTPUT_TEXT, - module_ensure_opaque_for_type, -) -from .__builder import ( - IRBuilder, new_builder, position_at_end, - builder_cur_block_is_terminated, - build_alloca, build_alloca_aligned, build_alloca_at_entry, build_load, build_store, - build_add, build_sub, build_mul, build_sdiv, build_udiv, build_srem, build_urem, - build_and, build_or, build_xor, build_shl, build_lshr, build_ashr, - build_fadd, build_fsub, build_fmul, build_fdiv, build_frem, build_fneg, - build_icmp, build_fcmp, - build_br, build_cond_br, build_ret, build_ret_void, - build_call, build_call_indirect, build_bitcast, build_sext, build_trunc, build_zext, - build_ptrtoint, build_inttoptr, - build_fpext, build_fptrunc, build_fp2si, build_si2fp, build_fp2ui, build_ui2fp, - build_gep, build_gep_array, build_gep_struct, - build_phi, build_switch, build_select, build_unreachable, - build_atomicrmw, build_cmpxchg, build_inline_asm, build_va_arg, - PhiIncoming, new_phi_incoming, SwitchCase, new_switch_case, - ICMP_EQ, ICMP_NE, ICMP_SGT, ICMP_SGE, ICMP_SLT, ICMP_SLE, - ICMP_UGT, ICMP_UGE, ICMP_ULT, ICMP_ULE, - FCMP_FALSE, FCMP_OEQ, FCMP_OGT, FCMP_OGE, FCMP_OLT, FCMP_OLE, - FCMP_ONE, FCMP_ORD, FCMP_UNO, FCMP_UEQ, FCMP_UGT, FCMP_UGE, - FCMP_ULT, FCMP_ULE, FCMP_UNE, FCMP_TRUE, - ATOMIC_XCHG, ATOMIC_ADD, ATOMIC_SUB, ATOMIC_AND, ATOMIC_NAND, - ATOMIC_OR, ATOMIC_XOR, ATOMIC_MAX, ATOMIC_MIN, ATOMIC_UMAX, ATOMIC_UMIN, - ATOMIC_ORDER_NOTATOMIC, ATOMIC_ORDER_UNORDERED, ATOMIC_ORDER_MONOTONIC, - ATOMIC_ORDER_ACQUIRE, ATOMIC_ORDER_RELEASE, ATOMIC_ORDER_ACQ_REL, - ATOMIC_ORDER_SEQ_CST, -) -from .__verify import ( - VerifyResult, NameEntry, - verify_function, verify_module, - VERIFY_OK, VERIFY_ERR_NO_TERMINATOR, VERIFY_ERR_DUPLICATE_DEF, - VERIFY_ERR_UNDEFINED_USE, VERIFY_ERR_PHI_NOT_FIRST, - _new_verify_result, -) - - -# ============================================================ -# 便捷工厂: 字符串复制 -# ============================================================ -def _str_dup(pool: rowptr[memhub.MemBuddy], src: str) -> str: - """从 pool 分配并复制一个 C 字符串""" - if src is None: return None - slen: t.CSizeT = string.strlen(src) - buf: str = pool.alloc(slen + 1) - if buf is None: return None - string.strcpy(buf, src) - return buf - - -# ============================================================ -# 便捷工厂: 创建带名称的基本块 -# ============================================================ -def create_block(pool: rowptr[memhub.MemBuddy], func: rowptr[Function], - name: str) -> rowptr[BasicBlock]: - """创建基本块并添加到函数""" - # 块名与函数参数值名冲突防护:LLVM 中块标签 "entry:" 即值 %entry, - # 与参数 %entry 重名 → llc "unable to create block named 'entry'" - # (CallGenCall._field_byte_offset(self, pool, entry, ...) 根因)。 - # 冲突时追加 .1/.2/... 后缀去重(Param.Name 可能带 % 前缀,剥离比较)。 - use_name: str = name - if func is not None and func.Params is not None and name is not None \ - and name[0] != '\0': - cb_len: t.CSizeT = string.strlen(name) + 16 - cb_buf: str = pool.alloc(cb_len) - if cb_buf is not None: - cb_k: int = 1 - cb_collide: int = 1 - while cb_collide != 0: - cb_collide = 0 - cb_p: rowptr[Param] = function_get_param_head(func) - while cb_p is not None: - cb_pnm: str = param_get_name(cb_p) - if cb_pnm is not None: - cb_pv: str = cb_pnm - if cb_pv[0] == '%': - cb_pv = cb_pv + 1 - if string.strcmp(cb_pv, use_name) == 0: - cb_collide = 1 - break - cb_p = param_get_next(cb_p) - if cb_collide != 0: - viperlib.snprintf(cb_buf, cb_len, "%s.%d", name, cb_k) - use_name = cb_buf - cb_k += 1 - name_dup: str = _str_dup(pool, use_name) - blk: rowptr[BasicBlock] = new_basic_block(pool, name_dup) - if blk is not None: - function_add_block(func, blk) - return blk - - -# ============================================================ -# 便捷工厂: 创建整数常量值 -# ============================================================ -def const_int(pool: rowptr[memhub.MemBuddy], bits: int, - val: int64) -> rowptr[Value]: - """创建整数常量值(自动创建类型 + 名字文本) - - 参数: - bits: 位宽 (1/8/16/32/64) - val: 整数值 - """ - # 创建类型 - ty: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if ty is None: return None - string.memset(ty, 0, LLVMType.__sizeof__()) - c.DerefAs(ty, LLVMType.Int(bits)) - - # 创建名字文本 - 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 即输出无符号位模式)。 - if bits == 64 and val == -9223372036854775807 - 1: - viperlib.snprintf(name, 32, "9223372036854775808") - else: - viperlib.snprintf(name, 32, "%lld", val) - - return ConstInt(pool, ty, val, name) - - -def const_int32(pool: rowptr[memhub.MemBuddy], val: int64) -> rowptr[Value]: - """便捷: 创建 i32 整数常量""" - return const_int(pool, 32, val) - - -def const_int64(pool: rowptr[memhub.MemBuddy], val: int64) -> rowptr[Value]: - """便捷: 创建 i64 整数常量""" - return const_int(pool, 64, val) - - -# ============================================================ -# 便捷工厂: 创建函数并添加到模块 -# ============================================================ -def create_function(pool: rowptr[memhub.MemBuddy], mod: rowptr[LLVMModule], - name: str, ret_ty: rowptr[LLVMType]) -> rowptr[Function]: - """创建函数并添加到模块""" - name_dup: str = _str_dup(pool, name) - func: rowptr[Function] = new_function(pool, name_dup, ret_ty) - if func is not None: - module_add_function(mod, func) - return func - - -def add_param(pool: rowptr[memhub.MemBuddy], func: rowptr[Function], - ty: rowptr[LLVMType], name: str) -> rowptr[Param]: - """创建参数并添加到函数""" - name_dup: str = _str_dup(pool, name) - param: rowptr[Param] = new_param(pool, ty, name_dup) - if param is not None: - function_add_param(func, param) - return param - - -# ============================================================ -# 便捷工厂: 创建函数声明(declare) -# ============================================================ -def create_declare(pool: rowptr[memhub.MemBuddy], mod: rowptr[LLVMModule], - name: str, ret_ty: rowptr[LLVMType]) -> rowptr[Function]: - """创建函数声明(declare,无函数体)并添加到模块 - - 与 create_function 不同,声明不生成函数体,仅生成: - declare @() - """ - name_dup: str = _str_dup(pool, name) - func: rowptr[Function] = new_function(pool, name_dup, ret_ty) - if func is not None: - func.IsDeclared = 1 - module_add_function(mod, func) - return func - - -# ============================================================ -# 便捷工厂: 创建字符串常量全局变量 -# -# 生成 LLVM IR: -# @.str = private unnamed_addr constant [N x i8] c"contents\00" -# ============================================================ -def create_global_string(pool: rowptr[memhub.MemBuddy], mod: rowptr[LLVMModule], - name: str, text: str) -> rowptr[GlobalVariable]: - """创建字符串常量全局变量并添加到模块 - - 参数: - name: 全局变量名(如 ".str") - text: 字符串内容(C 字符串,不含末尾 NUL,NUL 由本函数自动添加) - - 返回: GlobalVariable 指针,其类型为 [len+1 x i8] - """ - if text is None: return None - slen: t.CSizeT = string.strlen(text) - count: int = slen + 1 # 含末尾 NUL - - # 创建 i8 类型 - i8_ty: rowptr[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] 数组类型 - arr_ty: rowptr[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)) - - # 创建全局变量 - name_dup: str = _str_dup(pool, name) - gv: rowptr[GlobalVariable] = new_global_variable(pool, name_dup, arr_ty) - if gv is None: return None - - # 设置属性: 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 + " - init_buf: str = pool.alloc(slen * 4 + 8) - if init_buf is None: return None - pos: t.CSizeT = 0 - init_buf[pos] = 'c' - pos += 1 - init_buf[pos] = '"' - pos += 1 - i: t.CSizeT = 0 - while i < slen: - ch: t.CUInt8T = text[i] - if ch == 0x22 or ch == 0x5C or ch < 0x20 or ch >= 0x7F: - # 需要转义: " → \22, \ → \5c, 非打印字符 → \xx - init_buf[pos] = '\\' - pos += 1 - hi: int = (ch >> 4) & 0x0F - lo: int = ch & 0x0F - if hi < 10: - init_buf[pos] = 0x30 + hi - else: - init_buf[pos] = 0x61 + hi - 10 - pos += 1 - if lo < 10: - init_buf[pos] = 0x30 + lo - else: - init_buf[pos] = 0x61 + lo - 10 - pos += 1 - else: - init_buf[pos] = ch - pos += 1 - i += 1 - # 末尾 NUL 字节 - init_buf[pos] = '\\' - pos += 1 - init_buf[pos] = '0' - pos += 1 - init_buf[pos] = '0' - pos += 1 - init_buf[pos] = '"' - pos += 1 - init_buf[pos] = '\0' - gv.Initializer = init_buf - - module_add_global(mod, gv) - return gv - - -# ============================================================ -# 便捷工厂: 创建带初始化值的整数全局变量 -# ============================================================ -def create_global_int(pool: rowptr[memhub.MemBuddy], mod: rowptr[LLVMModule], - name: str, bits: int, val: int64) -> rowptr[GlobalVariable]: - """创建整数全局变量并添加到模块 - - 生成 LLVM IR: - @ = global i - """ - # 创建整数类型 - ty: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if ty is None: return None - string.memset(ty, 0, LLVMType.__sizeof__()) - c.DerefAs(ty, LLVMType.Int(bits)) - - name_dup: str = _str_dup(pool, name) - gv: rowptr[GlobalVariable] = new_global_variable(pool, name_dup, ty) - if gv is None: return None - - # 构造初始化值文本: "42" (类型由 _print_global 单独打印) - init_buf: str = pool.alloc(48) - if init_buf is None: return None - viperlib.snprintf(init_buf, 48, "%lld", val) - gv.Initializer = init_buf - - module_add_global(mod, gv) - return gv - - -# ============================================================ -# LLVMModuleCore: 模块/函数/值创建核心,封装 pool 避免重复传参 -# -# 用法: -# mcore: rowptr[LLVMModuleCore] = LLVMModuleCore(pool) -# mod: rowptr[LLVMModule] = mcore.NewModule("test") -# func: rowptr[Function] = mcore.CreateFunction(mod, "add", ty_i32) -# val: rowptr[Value] = mcore.ConstInt32(42) -# ============================================================ -@t.NoVTable -class LLVMModuleCore: - """模块/函数/值创建核心,封装 pool 避免每次创建都传 pool""" - Pool: rowptr[memhub.MemBuddy] - - def __init__(self, pool: rowptr[memhub.MemBuddy]): - self.Pool = pool - - # ---- 模块操作 ---- - def NewModule(self, name: str) -> rowptr[LLVMModule]: - return new_module(self.Pool, name) - - def CreateFunction(self, mod: rowptr[LLVMModule], name: str, - ret_ty: rowptr[LLVMType]) -> rowptr[Function]: - return create_function(self.Pool, mod, name, ret_ty) - - def CreateDeclare(self, mod: rowptr[LLVMModule], name: str, - ret_ty: rowptr[LLVMType]) -> rowptr[Function]: - """创建函数声明(declare,无函数体)""" - return create_declare(self.Pool, mod, name, ret_ty) - - def AddParam(self, func: rowptr[Function], ty: rowptr[LLVMType], - name: str) -> rowptr[Param]: - return add_param(self.Pool, func, ty, name) - - def FunctionSetAttrs(self, func: rowptr[Function], attrs: str): - """设置函数级属性文本""" - function_set_attrs(func, attrs) - - def ParamSetAttrs(self, param: rowptr[Param], attrs: str): - """设置参数属性文本""" - param_set_attrs(param, attrs) - - def CreateBlock(self, func: rowptr[Function], - name: str) -> rowptr[BasicBlock]: - return create_block(self.Pool, func, name) - - def NewBuilder(self, func: rowptr[Function]) -> rowptr[IRBuilder]: - return new_builder(self.Pool, func) - - # ---- 全局变量 ---- - def NewGlobal(self, mod: rowptr[LLVMModule], name: str, - ty: rowptr[LLVMType]) -> rowptr[GlobalVariable]: - """创建全局变量并添加到模块""" - name_dup: str = _str_dup(self.Pool, name) - gv: rowptr[GlobalVariable] = new_global_variable(self.Pool, name_dup, ty) - if gv is not None: - module_add_global(mod, gv) - return gv - - def CreateGlobalString(self, mod: rowptr[LLVMModule], name: str, - text: str) -> rowptr[GlobalVariable]: - """创建字符串常量全局变量""" - return create_global_string(self.Pool, mod, name, text) - - def CreateGlobalInt(self, mod: rowptr[LLVMModule], name: str, - bits: int, val: int64) -> rowptr[GlobalVariable]: - """创建带初始化值的整数全局变量""" - return create_global_int(self.Pool, mod, name, bits, val) - - def GlobalSetConstant(self, gv: rowptr[GlobalVariable], is_const: int): - global_set_constant(gv, is_const) - - def GlobalSetLinkage(self, gv: rowptr[GlobalVariable], linkage: str): - global_set_linkage(gv, linkage) - - def GlobalSetUnnamedAddr(self, gv: rowptr[GlobalVariable], level: int): - global_set_unnamed_addr(gv, level) - - def GlobalSetSection(self, gv: rowptr[GlobalVariable], section: str): - global_set_section(gv, section) - - # ---- 值创建 ---- - def ConstInt(self, bits: int, val: int64) -> rowptr[Value]: - return const_int(self.Pool, bits, val) - - def ConstInt32(self, val: int64) -> rowptr[Value]: - return const_int32(self.Pool, val) - - def ConstInt64(self, val: int64) -> rowptr[Value]: - return const_int64(self.Pool, val) - - def ConstFloat(self, ty: rowptr[LLVMType], val: t.CDouble) -> rowptr[Value]: - return ConstFloat(self.Pool, ty, val) - - def SSAValue(self, ty: rowptr[LLVMType], name: str) -> rowptr[Value]: - return SSAValue(self.Pool, ty, name) - - def ConstNull(self, ty: rowptr[LLVMType], name: str) -> rowptr[Value]: - return ConstNull(self.Pool, ty, name) - - # ---- 打印 ---- - def PrintValue(self, buf: str, size: t.CSizeT, val: rowptr[Value]): - ValuePrint(buf, size, val, self.Pool) - - def PrintFunction(self, buf: str, size: t.CSizeT, func: rowptr[Function]): - FunctionPrint(buf, size, func, self.Pool) - - def PrintModule(self, buf: str, size: t.CSizeT, mod: rowptr[LLVMModule]): - LLVMModulePrint(buf, size, mod, self.Pool, OUTPUT_FULL) - - # ---- SSA 验证 ---- - def NewVerifyResult(self, buf_size: t.CSizeT) -> rowptr[VerifyResult]: - """创建验证结果对象(buf_size 为错误消息缓冲区大小)""" - return _new_verify_result(self.Pool, buf_size) - - def VerifyFunction(self, func: rowptr[Function], - result: rowptr[VerifyResult]) -> int: - """验证单个函数的 SSA 正确性,返回错误数(0=通过)""" - return verify_function(self.Pool, func, result) - - def VerifyModule(self, mod: rowptr[LLVMModule], - result: rowptr[VerifyResult]) -> int: - """验证模块中所有函数的 SSA 正确性,返回错误数(0=通过)""" - return verify_module(self.Pool, mod, result) +import t, c +from stdint import * +import memhub +import string +import viperlib + +# 导入子模块 +from .__types import ( + LLVMType, ParamNode, LLVMTypeCore, + Int1, Int8, Int16, Int32, Int64, Ptr, Func, Void, Array, Struct, + Half, Float, Double, FP128, Label, + new_param_node, param_list_append, + paramnode_get_ty, paramnode_get_next, paramnode_set_ty, paramnode_set_next, + TypePrint, PrintStructDefinition, get_struct_name, + get_struct_fields, get_struct_fcount, DeepCopyType, +) +from .__values import ( + Value, new_value, ConstInt, ConstNull, ConstZero, ConstFloat, SSAValue, ValuePrint, + value_get_ty, value_get_next, + value_set_ty, value_set_next, value_set_name, value_set_isconst, +) +from .__function import ( + Line, BasicBlock, Param, Function, + new_line, new_basic_block, new_param, new_function, + function_add_param, function_add_block, + block_append_line, block_append_text, + function_set_attrs, param_set_attrs, + function_set_attr_group, + function_get_name, function_get_name_len, function_get_ret_ty, function_get_params, + function_get_param_head, function_get_next, function_is_declared, + param_get_name, param_get_ty, param_get_next, + FunctionPrint, G_PeepEnable, +) +from .__module import ( + LLVMModule, GlobalVariable, NamedTypeNode, new_module, new_global_variable, + module_add_function, module_add_global, module_add_named_type, + module_set_named_type_packed, + module_set_target, module_set_datalayout, + global_set_constant, global_set_linkage, global_set_unnamed_addr, global_set_section, + LLVMModulePrint, OUTPUT_FULL, OUTPUT_STUB, OUTPUT_TEXT, + module_ensure_opaque_for_type, +) +from .__builder import ( + IRBuilder, new_builder, position_at_end, + builder_cur_block_is_terminated, + build_alloca, build_alloca_aligned, build_alloca_at_entry, build_load, build_store, + build_add, build_sub, build_mul, build_sdiv, build_udiv, build_srem, build_urem, + build_and, build_or, build_xor, build_shl, build_lshr, build_ashr, + build_fadd, build_fsub, build_fmul, build_fdiv, build_frem, build_fneg, + build_icmp, build_fcmp, + build_br, build_cond_br, build_ret, build_ret_void, + build_call, build_call_indirect, build_bitcast, build_sext, build_trunc, build_zext, + build_ptrtoint, build_inttoptr, + build_fpext, build_fptrunc, build_fp2si, build_si2fp, build_fp2ui, build_ui2fp, + build_gep, build_gep_array, build_gep_struct, + build_phi, build_switch, build_select, build_unreachable, + build_atomicrmw, build_cmpxchg, build_inline_asm, build_va_arg, + PhiIncoming, new_phi_incoming, SwitchCase, new_switch_case, + ICMP_EQ, ICMP_NE, ICMP_SGT, ICMP_SGE, ICMP_SLT, ICMP_SLE, + ICMP_UGT, ICMP_UGE, ICMP_ULT, ICMP_ULE, + FCMP_FALSE, FCMP_OEQ, FCMP_OGT, FCMP_OGE, FCMP_OLT, FCMP_OLE, + FCMP_ONE, FCMP_ORD, FCMP_UNO, FCMP_UEQ, FCMP_UGT, FCMP_UGE, + FCMP_ULT, FCMP_ULE, FCMP_UNE, FCMP_TRUE, + ATOMIC_XCHG, ATOMIC_ADD, ATOMIC_SUB, ATOMIC_AND, ATOMIC_NAND, + ATOMIC_OR, ATOMIC_XOR, ATOMIC_MAX, ATOMIC_MIN, ATOMIC_UMAX, ATOMIC_UMIN, + ATOMIC_ORDER_NOTATOMIC, ATOMIC_ORDER_UNORDERED, ATOMIC_ORDER_MONOTONIC, + ATOMIC_ORDER_ACQUIRE, ATOMIC_ORDER_RELEASE, ATOMIC_ORDER_ACQ_REL, + ATOMIC_ORDER_SEQ_CST, +) +from .__verify import ( + VerifyResult, NameEntry, + verify_function, verify_module, + VERIFY_OK, VERIFY_ERR_NO_TERMINATOR, VERIFY_ERR_DUPLICATE_DEF, + VERIFY_ERR_UNDEFINED_USE, VERIFY_ERR_PHI_NOT_FIRST, + _new_verify_result, +) + + +# ============================================================ +# 便捷工厂: 字符串复制 +# ============================================================ +def _str_dup(pool: rawptr[memhub.MemBuddy], src: str) -> str: + """从 pool 分配并复制一个 C 字符串""" + if src is None: return None + slen: t.CSizeT = string.strlen(src) + buf: str = pool.alloc(slen + 1) + if buf is None: return None + string.strcpy(buf, src) + return buf + + +# ============================================================ +# 便捷工厂: 创建带名称的基本块 +# ============================================================ +def create_block(pool: rawptr[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 可能带 % 前缀,剥离比较)。 + use_name: str = name + if func is not None and func.Params is not None and name is not None \ + and name[0] != '\0': + cb_len: t.CSizeT = string.strlen(name) + 16 + cb_buf: str = pool.alloc(cb_len) + if cb_buf is not None: + cb_k: int = 1 + cb_collide: int = 1 + while cb_collide != 0: + cb_collide = 0 + cb_p: rawptr[Param] = function_get_param_head(func) + while cb_p is not None: + cb_pnm: str = param_get_name(cb_p) + if cb_pnm is not None: + cb_pv: str = cb_pnm + if cb_pv[0] == '%': + cb_pv = cb_pv + 1 + if string.strcmp(cb_pv, use_name) == 0: + cb_collide = 1 + break + cb_p = param_get_next(cb_p) + if cb_collide != 0: + viperlib.snprintf(cb_buf, cb_len, "%s.%d", name, cb_k) + use_name = cb_buf + cb_k += 1 + name_dup: str = _str_dup(pool, use_name) + blk: rawptr[BasicBlock] = new_basic_block(pool, name_dup) + if blk is not None: + function_add_block(func, blk) + return blk + + +# ============================================================ +# 便捷工厂: 创建整数常量值 +# ============================================================ +def const_int(pool: rawptr[memhub.MemBuddy], bits: int, + val: int64) -> rawptr[Value]: + """创建整数常量值(自动创建类型 + 名字文本) + + 参数: + bits: 位宽 (1/8/16/32/64) + val: 整数值 + """ + # 创建类型 + 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)) + + # 创建名字文本 + 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 即输出无符号位模式)。 + if bits == 64 and val == -9223372036854775807 - 1: + viperlib.snprintf(name, 32, "9223372036854775808") + else: + viperlib.snprintf(name, 32, "%lld", val) + + return ConstInt(pool, ty, val, name) + + +def const_int32(pool: rawptr[memhub.MemBuddy], val: int64) -> rawptr[Value]: + """便捷: 创建 i32 整数常量""" + return const_int(pool, 32, val) + + +def const_int64(pool: rawptr[memhub.MemBuddy], val: int64) -> rawptr[Value]: + """便捷: 创建 i64 整数常量""" + return const_int(pool, 64, val) + + +# ============================================================ +# 便捷工厂: 创建函数并添加到模块 +# ============================================================ +def create_function(pool: rawptr[memhub.MemBuddy], mod: rawptr[LLVMModule], + name: str, ret_ty: rawptr[LLVMType]) -> rawptr[Function]: + """创建函数并添加到模块""" + name_dup: str = _str_dup(pool, name) + func: rawptr[Function] = new_function(pool, name_dup, ret_ty) + if func is not None: + module_add_function(mod, func) + return func + + +def add_param(pool: rawptr[memhub.MemBuddy], func: rawptr[Function], + ty: rawptr[LLVMType], name: str) -> rawptr[Param]: + """创建参数并添加到函数""" + name_dup: str = _str_dup(pool, name) + param: rawptr[Param] = new_param(pool, ty, name_dup) + if param is not None: + function_add_param(func, param) + return param + + +# ============================================================ +# 便捷工厂: 创建函数声明(declare) +# ============================================================ +def create_declare(pool: rawptr[memhub.MemBuddy], mod: rawptr[LLVMModule], + name: str, ret_ty: rawptr[LLVMType]) -> rawptr[Function]: + """创建函数声明(declare,无函数体)并添加到模块 + + 与 create_function 不同,声明不生成函数体,仅生成: + declare @() + """ + name_dup: str = _str_dup(pool, name) + func: rawptr[Function] = new_function(pool, name_dup, ret_ty) + if func is not None: + func.IsDeclared = 1 + module_add_function(mod, func) + return func + + +# ============================================================ +# 便捷工厂: 创建字符串常量全局变量 +# +# 生成 LLVM IR: +# @.str = private unnamed_addr constant [N x i8] c"contents\00" +# ============================================================ +def create_global_string(pool: rawptr[memhub.MemBuddy], mod: rawptr[LLVMModule], + name: str, text: str) -> rawptr[GlobalVariable]: + """创建字符串常量全局变量并添加到模块 + + 参数: + name: 全局变量名(如 ".str") + text: 字符串内容(C 字符串,不含末尾 NUL,NUL 由本函数自动添加) + + 返回: GlobalVariable 指针,其类型为 [len+1 x i8] + """ + if text is None: return None + slen: t.CSizeT = string.strlen(text) + count: int = slen + 1 # 含末尾 NUL + + # 创建 i8 类型 + 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] 数组类型 + 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)) + + # 创建全局变量 + 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 + 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 + " + init_buf: str = pool.alloc(slen * 4 + 8) + if init_buf is None: return None + pos: t.CSizeT = 0 + init_buf[pos] = 'c' + pos += 1 + init_buf[pos] = '"' + pos += 1 + i: t.CSizeT = 0 + while i < slen: + ch: t.CUInt8T = text[i] + if ch == 0x22 or ch == 0x5C or ch < 0x20 or ch >= 0x7F: + # 需要转义: " → \22, \ → \5c, 非打印字符 → \xx + init_buf[pos] = '\\' + pos += 1 + hi: int = (ch >> 4) & 0x0F + lo: int = ch & 0x0F + if hi < 10: + init_buf[pos] = 0x30 + hi + else: + init_buf[pos] = 0x61 + hi - 10 + pos += 1 + if lo < 10: + init_buf[pos] = 0x30 + lo + else: + init_buf[pos] = 0x61 + lo - 10 + pos += 1 + else: + init_buf[pos] = ch + pos += 1 + i += 1 + # 末尾 NUL 字节 + init_buf[pos] = '\\' + pos += 1 + init_buf[pos] = '0' + pos += 1 + init_buf[pos] = '0' + pos += 1 + init_buf[pos] = '"' + pos += 1 + init_buf[pos] = '\0' + gv.Initializer = init_buf + + module_add_global(mod, gv) + return gv + + +# ============================================================ +# 便捷工厂: 创建带初始化值的整数全局变量 +# ============================================================ +def create_global_int(pool: rawptr[memhub.MemBuddy], mod: rawptr[LLVMModule], + name: str, bits: int, val: int64) -> rawptr[GlobalVariable]: + """创建整数全局变量并添加到模块 + + 生成 LLVM IR: + @ = global i + """ + # 创建整数类型 + 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)) + + name_dup: str = _str_dup(pool, name) + gv: rawptr[GlobalVariable] = new_global_variable(pool, name_dup, ty) + if gv is None: return None + + # 构造初始化值文本: "42" (类型由 _print_global 单独打印) + init_buf: str = pool.alloc(48) + if init_buf is None: return None + viperlib.snprintf(init_buf, 48, "%lld", val) + gv.Initializer = init_buf + + module_add_global(mod, gv) + return gv + + +# ============================================================ +# LLVMModuleCore: 模块/函数/值创建核心,封装 pool 避免重复传参 +# +# 用法: +# mcore: rawptr[LLVMModuleCore] = LLVMModuleCore(pool) +# mod: rawptr[LLVMModule] = mcore.NewModule("test") +# func: rawptr[Function] = mcore.CreateFunction(mod, "add", ty_i32) +# val: rawptr[Value] = mcore.ConstInt32(42) +# ============================================================ +@t.NoVTable +class LLVMModuleCore: + """模块/函数/值创建核心,封装 pool 避免每次创建都传 pool""" + Pool: rawptr[memhub.MemBuddy] + + def __init__(self, pool: rawptr[memhub.MemBuddy]): + self.Pool = pool + + # ---- 模块操作 ---- + def NewModule(self, name: str) -> rawptr[LLVMModule]: + return new_module(self.Pool, name) + + def CreateFunction(self, mod: rawptr[LLVMModule], name: str, + ret_ty: rawptr[LLVMType]) -> rawptr[Function]: + return create_function(self.Pool, mod, name, ret_ty) + + def CreateDeclare(self, mod: rawptr[LLVMModule], name: str, + ret_ty: rawptr[LLVMType]) -> rawptr[Function]: + """创建函数声明(declare,无函数体)""" + return create_declare(self.Pool, mod, name, ret_ty) + + def AddParam(self, func: rawptr[Function], ty: rawptr[LLVMType], + name: str) -> rawptr[Param]: + return add_param(self.Pool, func, ty, name) + + def FunctionSetAttrs(self, func: rawptr[Function], attrs: str): + """设置函数级属性文本""" + function_set_attrs(func, attrs) + + def ParamSetAttrs(self, param: rawptr[Param], attrs: str): + """设置参数属性文本""" + param_set_attrs(param, attrs) + + def CreateBlock(self, func: rawptr[Function], + name: str) -> rawptr[BasicBlock]: + return create_block(self.Pool, func, name) + + def NewBuilder(self, func: rawptr[Function]) -> rawptr[IRBuilder]: + return new_builder(self.Pool, func) + + # ---- 全局变量 ---- + def NewGlobal(self, mod: rawptr[LLVMModule], name: str, + ty: rawptr[LLVMType]) -> rawptr[GlobalVariable]: + """创建全局变量并添加到模块""" + name_dup: str = _str_dup(self.Pool, name) + gv: rawptr[GlobalVariable] = new_global_variable(self.Pool, name_dup, ty) + if gv is not None: + module_add_global(mod, gv) + return gv + + def CreateGlobalString(self, mod: rawptr[LLVMModule], name: str, + text: str) -> rawptr[GlobalVariable]: + """创建字符串常量全局变量""" + return create_global_string(self.Pool, mod, name, text) + + def CreateGlobalInt(self, mod: rawptr[LLVMModule], name: str, + bits: int, val: int64) -> rawptr[GlobalVariable]: + """创建带初始化值的整数全局变量""" + return create_global_int(self.Pool, mod, name, bits, val) + + def GlobalSetConstant(self, gv: rawptr[GlobalVariable], is_const: int): + global_set_constant(gv, is_const) + + def GlobalSetLinkage(self, gv: rawptr[GlobalVariable], linkage: str): + global_set_linkage(gv, linkage) + + def GlobalSetUnnamedAddr(self, gv: rawptr[GlobalVariable], level: int): + global_set_unnamed_addr(gv, level) + + def GlobalSetSection(self, gv: rawptr[GlobalVariable], section: str): + global_set_section(gv, section) + + # ---- 值创建 ---- + def ConstInt(self, bits: int, val: int64) -> rawptr[Value]: + return const_int(self.Pool, bits, val) + + def ConstInt32(self, val: int64) -> rawptr[Value]: + return const_int32(self.Pool, val) + + def ConstInt64(self, val: int64) -> rawptr[Value]: + return const_int64(self.Pool, val) + + def ConstFloat(self, ty: rawptr[LLVMType], val: t.CDouble) -> rawptr[Value]: + return ConstFloat(self.Pool, ty, val) + + def SSAValue(self, ty: rawptr[LLVMType], name: str) -> rawptr[Value]: + return SSAValue(self.Pool, ty, name) + + def ConstNull(self, ty: rawptr[LLVMType], name: str) -> rawptr[Value]: + return ConstNull(self.Pool, ty, name) + + # ---- 打印 ---- + def PrintValue(self, buf: str, size: t.CSizeT, val: rawptr[Value]): + ValuePrint(buf, size, val, self.Pool) + + def PrintFunction(self, buf: str, size: t.CSizeT, func: rawptr[Function]): + FunctionPrint(buf, size, func, self.Pool) + + def PrintModule(self, buf: str, size: t.CSizeT, mod: rawptr[LLVMModule]): + LLVMModulePrint(buf, size, mod, self.Pool, OUTPUT_FULL) + + # ---- SSA 验证 ---- + def NewVerifyResult(self, buf_size: t.CSizeT) -> rawptr[VerifyResult]: + """创建验证结果对象(buf_size 为错误消息缓冲区大小)""" + return _new_verify_result(self.Pool, buf_size) + + def VerifyFunction(self, func: rawptr[Function], + result: rawptr[VerifyResult]) -> int: + """验证单个函数的 SSA 正确性,返回错误数(0=通过)""" + return verify_function(self.Pool, func, result) + + def VerifyModule(self, mod: rawptr[LLVMModule], + result: rawptr[VerifyResult]) -> int: + """验证模块中所有函数的 SSA 正确性,返回错误数(0=通过)""" + return verify_module(self.Pool, mod, result) diff --git a/llvmlite/__module.vp b/llvmlite/__module.vp index ec53b1f..0c14a3e 100644 --- a/llvmlite/__module.vp +++ b/llvmlite/__module.vp @@ -1,776 +1,776 @@ -import t, c -from stdint import * -import string -import stdio -import stdlib -import viperlib -import memhub -from linkedlist import GSList, GSListNode -from .__types import LLVMType, TypePrint, Array, PrintStructDefinition, get_struct_name, Struct, ParamNode -from .__function import Function, FunctionPrint, _ll_name_needs_quote, Param, param_get_next, param_get_ty - - -# ============================================================ -# GlobalVariable: LLVM IR 全局变量 -# -# 持有名称 + 类型 + 初始化值 + 常量标志 + 链接类型。 -# 继承 GSListNode[GlobalVariable] 获取强类型 Next。 -# ============================================================ -@t.NoVTable -class GlobalVariable(GSListNode[GlobalVariable]): - Name: str # 全局变量名(如 ".str" / "my_global") - Ty: rowptr[LLVMType] # 变量类型 - Initializer: rowptr[t.CVoid] # 初始化值 Value*(None = 无初始化) - IsConstant: int # 1=constant, 0=variable - Linkage: str # 链接类型("private"/"internal"/"external"/...) - Section: str # 段名(None = 默认) - UnnamedAddr: int # 1=unnamed_addr, 2=local_unnamed_addr, 0=none - Align: int # 对齐字节数(0=默认对齐,如 @c.Attribute(t.attr.aligned(16))) - - -# ============================================================ -# NamedTypeNode: 命名结构体类型链表节点 -# -# 持有 LLVMType* 指针(必须是命名 Struct 类型)。 -# 继承 GSListNode[NamedTypeNode] 获取强类型 Next。 -# ============================================================ -@t.NoVTable -class NamedTypeNode(GSListNode[NamedTypeNode]): - Ty: rowptr[LLVMType] # 命名结构体类型指针 - IsPacked: int # 1=packed 结构体(输出 <{...}>,无字段填充) - - -# ============================================================ -# LLVMModule: LLVM IR 模块容器 -# -# 使用嵌入式 Head/Tail/Count 字段代替 GSList 指针, -# 避免泛型 GSList 的 __sizeof__ 返回 0 和 vtable 偏移不一致问题。 -# LLVMModulePrint 输出完整 .ll 文件内容。 -# 注意: 类名必须为 LLVMModule 而非 Module,避免与 ast.Module 命名冲突 -# ============================================================ -@t.NoVTable -class LLVMModule: - Name: str # 模块名 - FuncHead: rowptr[Function] # 函数链表头 - FuncTail: rowptr[Function] # 函数链表尾 - FuncCount: t.CSizeT # 函数计数 - GlobalHead: rowptr[GlobalVariable] # 全局变量链表头 - GlobalTail: rowptr[GlobalVariable] # 全局变量链表尾 - GlobalCount: t.CSizeT # 全局变量计数 - TargetTriple: str # 目标三元组(如 "x86_64-pc-linux-gnu") - DataLayout: str # 数据布局(如 "e-m:e-p270:32:32-...") - NamedTypeHead: rowptr[NamedTypeNode] # 命名结构体类型链表头 - NamedTypeTail: rowptr[NamedTypeNode] # 命名结构体类型链表尾 - NamedTypeCount: t.CSizeT # 命名结构体类型计数 - # 总计: 12 字段 * 8 = 96 字节(无 vtable) - - # ============================================================ - # 实例方法(对象化重构:原 module_* 顶层函数改写为方法, - # 原顶层函数保留为一行委托) - # ============================================================ - - # ---- 链表操作 ---- - def add_function(self, func: rowptr[Function]): - """将函数追加到模块函数链表(O(1))""" - if self is None or func is None: return - func.Next = None - if self.FuncHead is None: - self.FuncHead = func - else: - self.FuncTail.Next = func - self.FuncTail = func - self.FuncCount += 1 - - def add_global(self, gv: rowptr[GlobalVariable]): - """将全局变量追加到模块全局变量链表(O(1))""" - if self is None or gv is None: return - gv.Next = None - if self.GlobalHead is None: - self.GlobalHead = gv - else: - self.GlobalTail.Next = gv - self.GlobalTail = gv - self.GlobalCount += 1 - - # ---- 目标属性 ---- - def set_target(self, triple: str): - """设置目标三元组""" - if self is None: return - self.TargetTriple = triple - - def set_datalayout(self, layout: str): - """设置数据布局""" - if self is None: return - self.DataLayout = layout - - # ---- 命名结构体类型管理 ---- - def add_named_type(self, pool: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType]): - """将命名结构体类型添加到模块(用于输出 %"name" = type { ... } 定义行) - - 非命名结构体(Name 为 None 或非 Struct 类型)会被忽略。 - 按 Name 去重;同名时 opaque 声明可被完整定义替换(供跨模块值类型 alloca)。 - """ - 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 声明可被完整定义替换) - cur: rowptr[NamedTypeNode] = self.NamedTypeHead - cnt_dedup: int = 0 - while cur is not None: - # 步数守卫:NamedTypeHead 链表成环(内存损坏/插入 bug)会导致 - # 无限遍历挂起(G3 编译 LinkedNode.prepend 挂起根因)。 - # 正常链表节点数远小于此上限,超限即链表损坏 → 立即报错终止。 - cnt_dedup += 1 - if cnt_dedup > 100000: - stdio.printf("[ANT-LOOP] NamedTypeHead 链表疑似成环, name=%s, 遍历节点=%d, 强制终止\n", - name if name is not None else "(null)", cnt_dedup) - stdlib.exit(1) - if cur.Ty is not None: - existing_name: str = get_struct_name(cur.Ty) - if existing_name is not None: - if string.strcmp(existing_name, name) == 0: - # 已有完整定义则无需替换 - if _is_opaque_struct(cur.Ty) == 0: - return - # 已有 opaque,新类型也是 opaque 则不重复 - if _is_opaque_struct(ty) != 0: - return - # 用完整定义替换 opaque 声明(原地替换 Ty 指针) - cur.Ty = ty - return - cur = cur.Next - # 创建新节点 - node: rowptr[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"。 - # 完整定义追加到链表尾部,保持原有顺序。 - if _is_opaque_struct(ty) != 0: - node.Next = self.NamedTypeHead - if self.NamedTypeHead is None: - self.NamedTypeTail = node - self.NamedTypeHead = node - else: - node.Next = None - if self.NamedTypeHead is None: - self.NamedTypeHead = node - else: - self.NamedTypeTail.Next = node - self.NamedTypeTail = node - self.NamedTypeCount += 1 - - def _is_type_defined_in_module(self, name: str) -> int: - """检查模块中是否已定义指定名称的结构体类型,返回 1=已定义 / 0=未定义""" - if self is None or name is None: - return 0 - cur: rowptr[NamedTypeNode] = self.NamedTypeHead - cnt_tdm: int = 0 - while cur is not None: - # 步数守卫:同上,防 NamedTypeHead 链表成环无限遍历 - cnt_tdm += 1 - if cnt_tdm > 100000: - stdio.printf("[TDM-LOOP] NamedTypeHead 链表疑似成环, name=%s, 强制终止\n", - name if name is not None else "(null)") - stdlib.exit(1) - if cur.Ty is not None: - existing_name: str = get_struct_name(cur.Ty) - if existing_name is not None: - # 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。 - if existing_name[0] == name[0] and string.strcmp(existing_name, name) == 0: - return 1 - cur = cur.Next - return 0 - - def ensure_opaque_for_type(self, pool: rowptr[memhub.MemBuddy], - ty: rowptr[LLVMType]): - """为类型中的跨模块结构体引用添加 opaque 声明 - - 当类型引用了 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时, - 如果该名称未在模块中定义,则添加 opaque 声明。 - """ - _scan_type_for_cross_module_refs(pool, self, ty) - - # ---- 打印 ---- - def PrintModule(self, buf: str, size: t.CSizeT, - pool: rowptr[memhub.MemBuddy], mode: int): - """将模块序列化为 .ll 文本写入 buf""" - if self is None or buf is None or size == 0: return - - # 头部注释(stub 和 full 模式输出,text 模式不输出) - if mode == OUTPUT_FULL or mode == OUTPUT_STUB: - _append_cstr(buf, size, "; ModuleID = '") - if self.Name is not None: - _append_cstr(buf, size, self.Name) - _append_cstr(buf, size, "'\n") - - # 目标三元组 - if self.TargetTriple is not None: - _append_cstr(buf, size, "target triple = \"") - _append_cstr(buf, size, self.TargetTriple) - _append_cstr(buf, size, "\"\n") - - # 数据布局 - if self.DataLayout is not None: - _append_cstr(buf, size, "target datalayout = \"") - _append_cstr(buf, size, self.DataLayout) - _append_cstr(buf, size, "\"\n") - - # 空行 - _append_cstr(buf, size, "\n") - - # 命名结构体类型定义(stub 和 full 模式输出,text 模式不输出) - if mode == OUTPUT_FULL or mode == OUTPUT_STUB: - # 扫描所有函数签名,为跨模块结构体引用添加 opaque 定义 - # 确保 stub.ll 包含被 declare/define 引用的跨模块类型定义 - # 修复:alloc_buf 返回 viperio.Buf,但 Buf 类型未在 NamedTypeHead 中注册 - func_scan: rowptr[Function] = self.FuncHead - func_scan_steps: t.CSizeT = 0 - while func_scan is not None: - func_scan_steps += 1 - if func_scan_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: module funcs(FuncCount=%zu,dump 终止)\n", - self.FuncCount) - stdlib.exit(1) - if func_scan.RetTy is not None: - self.ensure_opaque_for_type(pool, func_scan.RetTy) - if func_scan.Params is not None: - params_scan: rowptr[GSList[Param]] = func_scan.Params - param_scan: rowptr[Param] = params_scan.Head - param_scan_steps: t.CSizeT = 0 - while param_scan is not None: - param_scan_steps += 1 - if param_scan_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params.Count=%zu,param=%llu next=%llu,遍历超 %d 步,终止)\n", - func_scan.Name, params_scan.Count, - t.CUInt64T(param_scan), t.CUInt64T(param_scan.Next), IR_DUMP_CHAIN_LIMIT) - stdlib.exit(1) - # 用访问器(__function.vp 完整类型内)绕开 stub 字段访问 - # 陷阱:跨模块 struct 字段访问可能生成错误 GEP/静默跳过, - # 导致 param_scan 永不更新死循环。 - if param_get_ty(param_scan) is not None: - self.ensure_opaque_for_type(pool, param_get_ty(param_scan)) - param_scan = param_get_next(param_scan) - func_scan = func_scan.Next - nt: rowptr[NamedTypeNode] = self.NamedTypeHead - # 结构体定义缓冲区:48 字段 × ~50 字节/字段类型名 ≈ 2400 字节,4096 足够 - # 在循环外分配一次并复用,减少 pool 碎片 - ty_buf: str = pool.alloc(4096) - nt_steps: t.CSizeT = 0 - while nt is not None: - nt_steps += 1 - if nt_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: module named types(NamedTypeCount=%zu,dump 终止)\n", - self.NamedTypeCount) - stdlib.exit(1) - if nt.Ty is not None: - if ty_buf is not None: - ty_buf[0] = '\0' - PrintStructDefinition(ty_buf, 4096, nt.Ty, pool, nt.IsPacked) - _append_cstr(buf, size, ty_buf) - _append_cstr(buf, size, "\n") - nt = nt.Next - - if self.NamedTypeHead is not None: - _append_cstr(buf, size, "\n") - - # 全局变量 - gv: rowptr[GlobalVariable] = self.GlobalHead - gv_steps: t.CSizeT = 0 - if mode == OUTPUT_STUB: - # stub 模式:输出 external global(无初值) - while gv is not None: - gv_steps += 1 - if gv_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: module globals stub(GlobalCount=%zu,dump 终止)\n", - self.GlobalCount) - stdlib.exit(1) - _print_global_stub(buf, size, gv, pool) - _append_cstr(buf, size, "\n") - gv = gv.Next - else: - # full 和 text 模式:输出完整全局变量 - # text 模式跳过无初值的全局变量(纯声明) - gv_idx: int = 0 - while gv is not None: - gv_steps += 1 - if gv_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: module globals(GlobalCount=%zu,dump 终止)\n", - self.GlobalCount) - stdlib.exit(1) - if mode == OUTPUT_TEXT and gv.Initializer is None: - gv = gv.Next - gv_idx += 1 - continue - _print_global(buf, size, gv, pool) - _append_cstr(buf, size, "\n") - gv = gv.Next - gv_idx += 1 - - if self.GlobalHead is not None and (mode == OUTPUT_FULL or mode == OUTPUT_STUB): - _append_cstr(buf, size, "\n") - elif self.GlobalHead is not None and mode == OUTPUT_TEXT: - # text 模式下如果输出了全局变量,也加空行 - had_global: int = 0 - gv2: rowptr[GlobalVariable] = self.GlobalHead - gv2_steps: t.CSizeT = 0 - while gv2 is not None: - gv2_steps += 1 - if gv2_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: module globals text(GlobalCount=%zu,dump 终止)\n", - self.GlobalCount) - stdlib.exit(1) - if gv2.Initializer is not None: - had_global = 1 - break - gv2 = gv2.Next - if had_global != 0: - _append_cstr(buf, size, "\n") - - # 遍历函数 - func: rowptr[Function] = self.FuncHead - func_idx: int = 0 - func_steps: t.CSizeT = 0 - while func is not None: - func_steps += 1 - if func_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: module funcs print(FuncCount=%zu,dump 终止)\n", - self.FuncCount) - stdlib.exit(1) - if mode == OUTPUT_STUB: - # stub 模式:仅输出 declare(IsDeclared==1) - # FunctionPrint 的 declare 分支已以 "\n" 收尾(L768),这里不再 - # 补换行,否则每个 declare 之间多出一个空行(美观/文件瘦身)。 - if func.IsDeclared == 1: - FunctionPrint(buf, size, func, pool) - elif mode == OUTPUT_TEXT: - # text 模式:仅输出 define(IsDeclared==0) - if func.IsDeclared == 0: - FunctionPrint(buf, size, func, pool) - _append_cstr(buf, size, "\n") - else: - # full 模式:输出所有函数 - FunctionPrint(buf, size, func, pool) - _append_cstr(buf, size, "\n") - func = func.Next - func_idx += 1 - - -# ============================================================ -# 工厂函数 -# ============================================================ -def new_module(pool: rowptr[memhub.MemBuddy], name: str) -> rowptr[LLVMModule]: - """创建一个模块""" - # 硬编码 96 字节:12 字段 * 8 字节(无 vtable,@t.NoVTable) - ptr: rowptr[LLVMModule] = pool.alloc(96) - if ptr is None: return None - string.memset(ptr, 0, 96) - ptr.Name = name - ptr.FuncHead = None - ptr.FuncTail = None - ptr.FuncCount = 0 - ptr.GlobalHead = None - ptr.GlobalTail = None - ptr.GlobalCount = 0 - ptr.TargetTriple = None - ptr.DataLayout = None - ptr.NamedTypeHead = None - ptr.NamedTypeTail = None - ptr.NamedTypeCount = 0 - return ptr - - -# GlobalVariable 硬编码大小: Next(8) + Name(8) + Ty(8) + Initializer(8) + IsConstant(4) + Linkage(8) + Section(8) + UnnamedAddr(4) + Align(4) = 72 字节 -GV_SIZE: t.CDefine = 72 - -# NamedTypeNode 硬编码大小: Next(8) + Ty(8) + IsPacked(8) = 24 字节 -NAMED_TYPE_NODE_SIZE: t.CDefine = 24 - -def new_global_variable(pool: rowptr[memhub.MemBuddy], - name: str, ty: rowptr[LLVMType]) -> rowptr[GlobalVariable]: - """创建一个全局变量""" - ptr: rowptr[GlobalVariable] = pool.alloc(GV_SIZE) - if ptr is None: return None - string.memset(ptr, 0, GV_SIZE) - ptr.Name = name - ptr.Ty = ty - ptr.Initializer = None - ptr.IsConstant = 0 - ptr.Linkage = "external" - ptr.Section = None - ptr.UnnamedAddr = 0 - ptr.Align = 0 - return ptr - - -# ============================================================ -# 链表操作(嵌入式 Head/Tail/Count,O(1) 追加) -# ============================================================ -def module_add_function(mod: rowptr[LLVMModule], func: rowptr[Function]): - mod.add_function(func) - - -def module_get_func_head(mod: rowptr[LLVMModule]) -> rowptr[Function]: - """获取模块函数链表头(跨模块访问器,绕 stub 字段访问陷阱)""" - if mod is None: - return None - return mod.FuncHead - - -def module_add_global(mod: rowptr[LLVMModule], gv: rowptr[GlobalVariable]): - mod.add_global(gv) - - -def module_set_target(mod: rowptr[LLVMModule], triple: str): - mod.set_target(triple) - - -def module_set_datalayout(mod: rowptr[LLVMModule], layout: str): - mod.set_datalayout(layout) - - -# ============================================================ -# 命名结构体类型管理 -# ============================================================ - -# ============================================================ -# _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=否 -# ============================================================ -def _is_opaque_struct(ty: rowptr[LLVMType]) -> int: - """检查类型是否是 opaque 结构体 - - opaque 是跨模块前向声明(仅名称无字段定义),由 - _scan_type_for_cross_module_refs 创建,fcount=-1 哨兵标记。 - 空结构体(fcount==0, fields==None,无字段类)是合法完整定义 - (LLVM {} 类型,可 alloca),必须与 opaque 区分——此前混淆导致 - 空类结构体被输出为 "= type opaque",alloca 报 Cannot allocate - unsized type。 - """ - if ty is None: - return 0 - match ty: - case LLVMType.Struct(fields, fcount, name): - # opaque: fcount == -1(哨兵) - if fcount < 0: - return 1 - return 0 - case _: - return 0 - - -def module_add_named_type(mod: rowptr[LLVMModule], pool: rowptr[memhub.MemBuddy], - ty: rowptr[LLVMType]): - mod.add_named_type(pool, ty) - - -def module_set_named_type_packed(mod: rowptr[LLVMModule], pool: rowptr[memhub.MemBuddy], - ty: rowptr[LLVMType], is_packed: int): - """按名称设置命名结构体的 packed 标记(输出 %"name" = type <{...}>)。 - - 编译器解析 @c.Attribute(t.attr.packed) 后调用,使 llc 按 packed 布局 - (无字段填充)计算字段偏移/GEP。ty 为命名 Struct 类型(按 get_struct_name - 定位已注册节点);未找到则静默跳过(opaque/跨模块 stub 场景无布局需求)。 - """ - if mod is None or ty is None: - return - name: str = get_struct_name(ty) - if name is None: - return - cur: rowptr[NamedTypeNode] = mod.NamedTypeHead - cnt_p: int = 0 - while cur is not None: - cnt_p += 1 - if cnt_p > 100000: - return - if cur.Ty is not None: - existing_name: str = get_struct_name(cur.Ty) - if existing_name is not None and string.strcmp(existing_name, name) == 0: - cur.IsPacked = is_packed - return - cur = cur.Next - - -# ============================================================ -# module_ensure_opaque_for_type - 为类型中的跨模块结构体引用添加 opaque 声明 -# -# 递归扫描 LLVMType 树,当遇到 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时, -# 如果该名称未在模块中定义,则添加 opaque 声明(%"name" = type opaque)。 -# 这解决了跨模块结构体引用导致的 llc "use of undefined type" 错误。 -# ============================================================ -def _is_type_defined_in_module(mod: rowptr[LLVMModule], name: str) -> int: - return mod._is_type_defined_in_module(name) - - -def _scan_type_for_cross_module_refs(pool: rowptr[memhub.MemBuddy], - mod: rowptr[LLVMModule], - ty: rowptr[LLVMType], - depth: int = 0): - """递归扫描类型树,为跨模块结构体引用添加 opaque 声明""" - if ty is None or mod is None or pool is None: - return - # 深度守卫:自引用结构体(链表/树节点,如 Node{...; Ptr(Node) next}) - # 会无限递归栈溢出,与其他递归(TypePrint/DeepCopyType)一致设上限 - if depth >= 100: - return - match ty: - case LLVMType.Ptr(pointee): - if pointee is not None: - match pointee: - case LLVMType.Struct(sfields, sfcount, sname): - if sname is not None: - # 检查是否为跨模块引用(名称包含 '.') - if string.strchr(sname, 46) is not None: - # 检查是否已在模块中定义 - if _is_type_defined_in_module(mod, sname) == 0: - # 创建 opaque 类型并添加到模块 - # fcount=-1 哨兵标记 opaque(区别于空结构体 {}, - # 后者 fcount==0 是合法完整定义,可 alloca) - opaque_ty: rowptr[LLVMType] = Struct(pool, None, -1, sname) - if opaque_ty is not None: - module_add_named_type(mod, pool, opaque_ty) - # 递归扫描结构体字段中的跨模块引用 - # 例如 TypeRegistry{HashTable*, MemManager*} 中的 HashTable 引用 - if sfields is not None: - fld: rowptr[ParamNode] = sfields - fld_steps: t.CSizeT = 0 - while fld is not None: - fld_steps += 1 - if fld_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: struct fields(sname=%s,dump 终止)\n", - sname) - stdlib.exit(1) - if fld.Ty is not None: - _scan_type_for_cross_module_refs(pool, mod, fld.Ty, depth + 1) - fld = fld.Next - case _: - _scan_type_for_cross_module_refs(pool, mod, pointee, depth + 1) - case LLVMType.Struct(sfields2, sfcount2, sname2): - # 顶层结构体(非指针):递归扫描字段 - if sfields2 is not None: - fld2: rowptr[ParamNode] = sfields2 - fld2_steps: t.CSizeT = 0 - while fld2 is not None: - fld2_steps += 1 - if fld2_steps > IR_DUMP_CHAIN_LIMIT: - stdio.printf("[IR-DUMP] 链表成环: struct fields2(sname=%s,dump 终止)\n", - sname2) - stdlib.exit(1) - if fld2.Ty is not None: - _scan_type_for_cross_module_refs(pool, mod, fld2.Ty, depth + 1) - fld2 = fld2.Next - case LLVMType.Array(elem_ty, acount): - if elem_ty is not None: - _scan_type_for_cross_module_refs(pool, mod, elem_ty, depth + 1) - case _: - pass - - -def module_ensure_opaque_for_type(mod: rowptr[LLVMModule], - pool: rowptr[memhub.MemBuddy], - ty: rowptr[LLVMType]): - mod.ensure_opaque_for_type(pool, ty) - - -# ============================================================ -# 全局变量属性设置 -# ============================================================ -def global_set_constant(gv: rowptr[GlobalVariable], is_const: int): - """设置全局变量是否为常量""" - if gv is None: return - gv.IsConstant = is_const - - -def global_set_linkage(gv: rowptr[GlobalVariable], linkage: str): - """设置链接类型 (private/internal/external/... )""" - if gv is None: return - gv.Linkage = linkage - - -def global_set_unnamed_addr(gv: rowptr[GlobalVariable], level: int): - """设置 unnamed_addr 属性 (0=none, 1=unnamed_addr, 2=local_unnamed_addr)""" - if gv is None: return - gv.UnnamedAddr = level - - -def global_set_section(gv: rowptr[GlobalVariable], section: str): - """设置段名""" - if gv is None: return - gv.Section = section - - -# ============================================================ -# 输出模式常量 -# ============================================================ -OUTPUT_FULL: t.CDefine = 0 # 完整 IR(stub + text) -OUTPUT_STUB: t.CDefine = 1 # 仅声明(header + 类型定义 + declare + external global) -OUTPUT_TEXT: t.CDefine = 2 # 仅代码(define + global 带初值) - -# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast)。 -# 任何一条链(函数/全局/命名类型/结构体字段)的 Next 被越界写破坏成环时, -# 遍历会无限循环导致 tpv.exe 挂起(builtins.vp dump 卡死根因排查)。 -# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位" -# (对齐 memhub.self_check 的 max_steps 哲学)。 -IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000 - - -# ============================================================ -# LLVMModulePrint: 将模块序列化为 .ll 文本 -# -# mode: OUTPUT_FULL=完整, OUTPUT_STUB=仅声明, OUTPUT_TEXT=仅代码 -# ============================================================ -def LLVMModulePrint(buf: str, size: t.CSizeT, - mod: rowptr[LLVMModule], pool: rowptr[memhub.MemBuddy], - mode: int): - mod.PrintModule(buf, size, pool, mode) - - -# ============================================================ -# 全局变量打印 -# ============================================================ -def _print_global(buf: str, size: t.CSizeT, - gv: rowptr[GlobalVariable], pool: rowptr[memhub.MemBuddy]): - """打印单个全局变量: @ = [linkage] [unnamed_addr] [constant] """ - if gv is None: return - - _append_cstr(buf, size, "@") - if gv.Name is not None: - if _ll_name_needs_quote(gv.Name) != 0: - _append_cstr(buf, size, "\"") - _append_cstr(buf, size, gv.Name) - _append_cstr(buf, size, "\"") - else: - _append_cstr(buf, size, gv.Name) - _append_cstr(buf, size, " = ") - - # 链接类型(有初始值时不输出 external,因为 external + 初始值是非法 IR) - if gv.Linkage is not None: - if gv.Initializer is not None and string.strcmp(gv.Linkage, "external") == 0: - pass - else: - _append_cstr(buf, size, gv.Linkage) - _append_cstr(buf, size, " ") - - # unnamed_addr - if gv.UnnamedAddr == 1: - _append_cstr(buf, size, "unnamed_addr ") - elif gv.UnnamedAddr == 2: - _append_cstr(buf, size, "local_unnamed_addr ") - - # constant / global - if gv.IsConstant == 1: - _append_cstr(buf, size, "constant ") - else: - _append_cstr(buf, size, "global ") - - # 类型 - ty_buf: str = pool.alloc(512) - if ty_buf is not None: - ty_buf[0] = '\0' - TypePrint(ty_buf, 512, gv.Ty, pool) - _append_cstr(buf, size, ty_buf) - - # 初始化值 - if gv.Initializer is not None: - _append_cstr(buf, size, " ") - _append_cstr(buf, size, gv.Initializer) - - # 段名 - if gv.Section is not None: - _append_cstr(buf, size, ", section \"") - _append_cstr(buf, size, gv.Section) - _append_cstr(buf, size, "\"") - - # 对齐(@c.Attribute(t.attr.aligned(N)) 类的全局实例,如 fpu_context 16 对齐) - if gv.Align > 0: - align_buf: str = pool.alloc(32) - if align_buf is not None: - viperlib.snprintf(align_buf, 32, ", align %d", gv.Align) - _append_cstr(buf, size, align_buf) - - -# ============================================================ -# _print_global_stub - 打印全局变量的声明形式(external global,无初值) -# ============================================================ -def _print_global_stub(buf: str, size: t.CSizeT, - gv: rowptr[GlobalVariable], pool: rowptr[memhub.MemBuddy]): - """打印全局变量声明: @ = external global """ - if gv is None: return - - _append_cstr(buf, size, "@") - if gv.Name is not None: - if _ll_name_needs_quote(gv.Name) != 0: - _append_cstr(buf, size, "\"") - _append_cstr(buf, size, gv.Name) - _append_cstr(buf, size, "\"") - else: - _append_cstr(buf, size, gv.Name) - _append_cstr(buf, size, " = external global ") - - # 类型 - ty_buf: str = pool.alloc(512) - if ty_buf is not None: - ty_buf[0] = '\0' - TypePrint(ty_buf, 512, gv.Ty, pool) - _append_cstr(buf, size, ty_buf) - - -# ============================================================ -# 内部辅助 -# ============================================================ -# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, -# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 -# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 -_g_last_dst: t.CUInt64T = 0 -_g_last_pos: t.CSizeT = 0 - -def _append_cstr(dst: str, dst_size: t.CSizeT, src: str): - """将 C 字符串 src 追加到 dst 末尾""" - global _g_last_dst, _g_last_pos - if dst is None or src is None: return - dlen: t.CSizeT = 0 - if _g_last_dst == t.CUInt64T(dst): - dlen = _g_last_pos - # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 - 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))。 - if dlen == 0: - if dst[0] != '\0': - dlen = string.strlen(dst) - elif dst[dlen] != '\0' or dst[dlen - 1] == '\0': - dlen = string.strlen(dst) - else: - dlen = string.strlen(dst) - _g_last_dst = t.CUInt64T(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 - if dlen >= dst_size: - return - slen: t.CSizeT = string.strlen(src) - remain: t.CSizeT = dst_size - dlen - if remain <= 0: return - i: t.CSizeT = 0 - while i < slen and i + 1 < remain: - dst[dlen + i] = src[i] - i += 1 - dst[dlen + i] = '\0' +import t, c +from stdint import * +import string +import stdio +import stdlib +import viperlib +import memhub +from linkedlist import GSList, GSListNode +from .__types import LLVMType, TypePrint, Array, PrintStructDefinition, get_struct_name, Struct, ParamNode +from .__function import Function, FunctionPrint, _ll_name_needs_quote, Param, param_get_next, param_get_ty + + +# ============================================================ +# GlobalVariable: LLVM IR 全局变量 +# +# 持有名称 + 类型 + 初始化值 + 常量标志 + 链接类型。 +# 继承 GSListNode[GlobalVariable] 获取强类型 Next。 +# ============================================================ +@t.NoVTable +class GlobalVariable(GSListNode[GlobalVariable]): + Name: str # 全局变量名(如 ".str" / "my_global") + Ty: rawptr[LLVMType] # 变量类型 + Initializer: rawptr # 初始化值 Value*(None = 无初始化) + IsConstant: int # 1=constant, 0=variable + Linkage: str # 链接类型("private"/"internal"/"external"/...) + Section: str # 段名(None = 默认) + UnnamedAddr: int # 1=unnamed_addr, 2=local_unnamed_addr, 0=none + Align: int # 对齐字节数(0=默认对齐,如 @c.Attribute(t.attr.aligned(16))) + + +# ============================================================ +# NamedTypeNode: 命名结构体类型链表节点 +# +# 持有 LLVMType* 指针(必须是命名 Struct 类型)。 +# 继承 GSListNode[NamedTypeNode] 获取强类型 Next。 +# ============================================================ +@t.NoVTable +class NamedTypeNode(GSListNode[NamedTypeNode]): + Ty: rawptr[LLVMType] # 命名结构体类型指针 + IsPacked: int # 1=packed 结构体(输出 <{...}>,无字段填充) + + +# ============================================================ +# LLVMModule: LLVM IR 模块容器 +# +# 使用嵌入式 Head/Tail/Count 字段代替 GSList 指针, +# 避免泛型 GSList 的 __sizeof__ 返回 0 和 vtable 偏移不一致问题。 +# LLVMModulePrint 输出完整 .ll 文件内容。 +# 注意: 类名必须为 LLVMModule 而非 Module,避免与 ast.Module 命名冲突 +# ============================================================ +@t.NoVTable +class LLVMModule: + Name: str # 模块名 + FuncHead: rawptr[Function] # 函数链表头 + FuncTail: rawptr[Function] # 函数链表尾 + FuncCount: t.CSizeT # 函数计数 + GlobalHead: rawptr[GlobalVariable] # 全局变量链表头 + GlobalTail: rawptr[GlobalVariable] # 全局变量链表尾 + GlobalCount: t.CSizeT # 全局变量计数 + TargetTriple: str # 目标三元组(如 "x86_64-pc-linux-gnu") + DataLayout: str # 数据布局(如 "e-m:e-p270:32:32-...") + NamedTypeHead: rawptr[NamedTypeNode] # 命名结构体类型链表头 + NamedTypeTail: rawptr[NamedTypeNode] # 命名结构体类型链表尾 + NamedTypeCount: t.CSizeT # 命名结构体类型计数 + # 总计: 12 字段 * 8 = 96 字节(无 vtable) + + # ============================================================ + # 实例方法(对象化重构:原 module_* 顶层函数改写为方法, + # 原顶层函数保留为一行委托) + # ============================================================ + + # ---- 链表操作 ---- + def add_function(self, func: rawptr[Function]): + """将函数追加到模块函数链表(O(1))""" + if self is None or func is None: return + func.Next = None + if self.FuncHead is None: + self.FuncHead = func + else: + self.FuncTail.Next = func + self.FuncTail = func + self.FuncCount += 1 + + def add_global(self, gv: rawptr[GlobalVariable]): + """将全局变量追加到模块全局变量链表(O(1))""" + if self is None or gv is None: return + gv.Next = None + if self.GlobalHead is None: + self.GlobalHead = gv + else: + self.GlobalTail.Next = gv + self.GlobalTail = gv + self.GlobalCount += 1 + + # ---- 目标属性 ---- + def set_target(self, triple: str): + """设置目标三元组""" + if self is None: return + self.TargetTriple = triple + + def set_datalayout(self, layout: str): + """设置数据布局""" + if self is None: return + self.DataLayout = layout + + # ---- 命名结构体类型管理 ---- + def add_named_type(self, pool: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType]): + """将命名结构体类型添加到模块(用于输出 %"name" = type { ... } 定义行) + + 非命名结构体(Name 为 None 或非 Struct 类型)会被忽略。 + 按 Name 去重;同名时 opaque 声明可被完整定义替换(供跨模块值类型 alloca)。 + """ + 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 声明可被完整定义替换) + cur: rawptr[NamedTypeNode] = self.NamedTypeHead + cnt_dedup: int = 0 + while cur is not None: + # 步数守卫:NamedTypeHead 链表成环(内存损坏/插入 bug)会导致 + # 无限遍历挂起(G3 编译 LinkedNode.prepend 挂起根因)。 + # 正常链表节点数远小于此上限,超限即链表损坏 → 立即报错终止。 + cnt_dedup += 1 + if cnt_dedup > 100000: + stdio.printf("[ANT-LOOP] NamedTypeHead 链表疑似成环, name=%s, 遍历节点=%d, 强制终止\n", + name if name is not None else "(null)", cnt_dedup) + stdlib.exit(1) + if cur.Ty is not None: + existing_name: str = get_struct_name(cur.Ty) + if existing_name is not None: + if string.strcmp(existing_name, name) == 0: + # 已有完整定义则无需替换 + if _is_opaque_struct(cur.Ty) == 0: + return + # 已有 opaque,新类型也是 opaque 则不重复 + if _is_opaque_struct(ty) != 0: + return + # 用完整定义替换 opaque 声明(原地替换 Ty 指针) + cur.Ty = ty + return + cur = cur.Next + # 创建新节点 + 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"。 + # 完整定义追加到链表尾部,保持原有顺序。 + if _is_opaque_struct(ty) != 0: + node.Next = self.NamedTypeHead + if self.NamedTypeHead is None: + self.NamedTypeTail = node + self.NamedTypeHead = node + else: + node.Next = None + if self.NamedTypeHead is None: + self.NamedTypeHead = node + else: + self.NamedTypeTail.Next = node + self.NamedTypeTail = node + self.NamedTypeCount += 1 + + def _is_type_defined_in_module(self, name: str) -> int: + """检查模块中是否已定义指定名称的结构体类型,返回 1=已定义 / 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 链表成环无限遍历 + cnt_tdm += 1 + if cnt_tdm > 100000: + stdio.printf("[TDM-LOOP] NamedTypeHead 链表疑似成环, name=%s, 强制终止\n", + name if name is not None else "(null)") + stdlib.exit(1) + if cur.Ty is not None: + existing_name: str = get_struct_name(cur.Ty) + if existing_name is not None: + # 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。 + if existing_name[0] == name[0] and string.strcmp(existing_name, name) == 0: + return 1 + cur = cur.Next + return 0 + + def ensure_opaque_for_type(self, pool: rawptr[memhub.MemBuddy], + ty: rawptr[LLVMType]): + """为类型中的跨模块结构体引用添加 opaque 声明 + + 当类型引用了 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时, + 如果该名称未在模块中定义,则添加 opaque 声明。 + """ + _scan_type_for_cross_module_refs(pool, self, ty) + + # ---- 打印 ---- + def PrintModule(self, buf: str, size: t.CSizeT, + pool: rawptr[memhub.MemBuddy], mode: int): + """将模块序列化为 .ll 文本写入 buf""" + if self is None or buf is None or size == 0: return + + # 头部注释(stub 和 full 模式输出,text 模式不输出) + if mode == OUTPUT_FULL or mode == OUTPUT_STUB: + _append_cstr(buf, size, "; ModuleID = '") + if self.Name is not None: + _append_cstr(buf, size, self.Name) + _append_cstr(buf, size, "'\n") + + # 目标三元组 + if self.TargetTriple is not None: + _append_cstr(buf, size, "target triple = \"") + _append_cstr(buf, size, self.TargetTriple) + _append_cstr(buf, size, "\"\n") + + # 数据布局 + if self.DataLayout is not None: + _append_cstr(buf, size, "target datalayout = \"") + _append_cstr(buf, size, self.DataLayout) + _append_cstr(buf, size, "\"\n") + + # 空行 + _append_cstr(buf, size, "\n") + + # 命名结构体类型定义(stub 和 full 模式输出,text 模式不输出) + if mode == OUTPUT_FULL or mode == OUTPUT_STUB: + # 扫描所有函数签名,为跨模块结构体引用添加 opaque 定义 + # 确保 stub.ll 包含被 declare/define 引用的跨模块类型定义 + # 修复:alloc_buf 返回 viperio.Buf,但 Buf 类型未在 NamedTypeHead 中注册 + func_scan: rawptr[Function] = self.FuncHead + func_scan_steps: t.CSizeT = 0 + while func_scan is not None: + func_scan_steps += 1 + if func_scan_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module funcs(FuncCount=%zu,dump 终止)\n", + self.FuncCount) + stdlib.exit(1) + if func_scan.RetTy is not None: + self.ensure_opaque_for_type(pool, func_scan.RetTy) + if func_scan.Params is not None: + params_scan: rawptr[GSList[Param]] = func_scan.Params + param_scan: rawptr[Param] = params_scan.Head + param_scan_steps: t.CSizeT = 0 + while param_scan is not None: + param_scan_steps += 1 + if param_scan_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params.Count=%zu,param=%llu next=%llu,遍历超 %d 步,终止)\n", + func_scan.Name, params_scan.Count, + t.CUInt64T(param_scan), t.CUInt64T(param_scan.Next), IR_DUMP_CHAIN_LIMIT) + stdlib.exit(1) + # 用访问器(__function.vp 完整类型内)绕开 stub 字段访问 + # 陷阱:跨模块 struct 字段访问可能生成错误 GEP/静默跳过, + # 导致 param_scan 永不更新死循环。 + if param_get_ty(param_scan) is not None: + self.ensure_opaque_for_type(pool, param_get_ty(param_scan)) + param_scan = param_get_next(param_scan) + func_scan = func_scan.Next + nt: rawptr[NamedTypeNode] = self.NamedTypeHead + # 结构体定义缓冲区:48 字段 × ~50 字节/字段类型名 ≈ 2400 字节,4096 足够 + # 在循环外分配一次并复用,减少 pool 碎片 + ty_buf: str = pool.alloc(4096) + nt_steps: t.CSizeT = 0 + while nt is not None: + nt_steps += 1 + if nt_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module named types(NamedTypeCount=%zu,dump 终止)\n", + self.NamedTypeCount) + stdlib.exit(1) + if nt.Ty is not None: + if ty_buf is not None: + ty_buf[0] = '\0' + PrintStructDefinition(ty_buf, 4096, nt.Ty, pool, nt.IsPacked) + _append_cstr(buf, size, ty_buf) + _append_cstr(buf, size, "\n") + nt = nt.Next + + if self.NamedTypeHead is not None: + _append_cstr(buf, size, "\n") + + # 全局变量 + gv: rawptr[GlobalVariable] = self.GlobalHead + gv_steps: t.CSizeT = 0 + if mode == OUTPUT_STUB: + # stub 模式:输出 external global(无初值) + while gv is not None: + gv_steps += 1 + if gv_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module globals stub(GlobalCount=%zu,dump 终止)\n", + self.GlobalCount) + stdlib.exit(1) + _print_global_stub(buf, size, gv, pool) + _append_cstr(buf, size, "\n") + gv = gv.Next + else: + # full 和 text 模式:输出完整全局变量 + # text 模式跳过无初值的全局变量(纯声明) + gv_idx: int = 0 + while gv is not None: + gv_steps += 1 + if gv_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module globals(GlobalCount=%zu,dump 终止)\n", + self.GlobalCount) + stdlib.exit(1) + if mode == OUTPUT_TEXT and gv.Initializer is None: + gv = gv.Next + gv_idx += 1 + continue + _print_global(buf, size, gv, pool) + _append_cstr(buf, size, "\n") + gv = gv.Next + gv_idx += 1 + + if self.GlobalHead is not None and (mode == OUTPUT_FULL or mode == OUTPUT_STUB): + _append_cstr(buf, size, "\n") + elif self.GlobalHead is not None and mode == OUTPUT_TEXT: + # text 模式下如果输出了全局变量,也加空行 + had_global: int = 0 + gv2: rawptr[GlobalVariable] = self.GlobalHead + gv2_steps: t.CSizeT = 0 + while gv2 is not None: + gv2_steps += 1 + if gv2_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module globals text(GlobalCount=%zu,dump 终止)\n", + self.GlobalCount) + stdlib.exit(1) + if gv2.Initializer is not None: + had_global = 1 + break + gv2 = gv2.Next + if had_global != 0: + _append_cstr(buf, size, "\n") + + # 遍历函数 + func: rawptr[Function] = self.FuncHead + func_idx: int = 0 + func_steps: t.CSizeT = 0 + while func is not None: + func_steps += 1 + if func_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module funcs print(FuncCount=%zu,dump 终止)\n", + self.FuncCount) + stdlib.exit(1) + if mode == OUTPUT_STUB: + # stub 模式:仅输出 declare(IsDeclared==1) + # FunctionPrint 的 declare 分支已以 "\n" 收尾(L768),这里不再 + # 补换行,否则每个 declare 之间多出一个空行(美观/文件瘦身)。 + if func.IsDeclared == 1: + FunctionPrint(buf, size, func, pool) + elif mode == OUTPUT_TEXT: + # text 模式:仅输出 define(IsDeclared==0) + if func.IsDeclared == 0: + FunctionPrint(buf, size, func, pool) + _append_cstr(buf, size, "\n") + else: + # full 模式:输出所有函数 + FunctionPrint(buf, size, func, pool) + _append_cstr(buf, size, "\n") + func = func.Next + func_idx += 1 + + +# ============================================================ +# 工厂函数 +# ============================================================ +def new_module(pool: rawptr[memhub.MemBuddy], name: str) -> rawptr[LLVMModule]: + """创建一个模块""" + # 硬编码 96 字节:12 字段 * 8 字节(无 vtable,@t.NoVTable) + ptr: rawptr[LLVMModule] = pool.alloc(96) + if ptr is None: return None + string.memset(ptr, 0, 96) + ptr.Name = name + ptr.FuncHead = None + ptr.FuncTail = None + ptr.FuncCount = 0 + ptr.GlobalHead = None + ptr.GlobalTail = None + ptr.GlobalCount = 0 + ptr.TargetTriple = None + ptr.DataLayout = None + ptr.NamedTypeHead = None + ptr.NamedTypeTail = None + ptr.NamedTypeCount = 0 + return ptr + + +# GlobalVariable 硬编码大小: Next(8) + Name(8) + Ty(8) + Initializer(8) + IsConstant(4) + Linkage(8) + Section(8) + UnnamedAddr(4) + Align(4) = 72 字节 +GV_SIZE: t.CDefine = 72 + +# NamedTypeNode 硬编码大小: Next(8) + Ty(8) + IsPacked(8) = 24 字节 +NAMED_TYPE_NODE_SIZE: t.CDefine = 24 + +def new_global_variable(pool: rawptr[memhub.MemBuddy], + name: str, ty: rawptr[LLVMType]) -> rawptr[GlobalVariable]: + """创建一个全局变量""" + ptr: rawptr[GlobalVariable] = pool.alloc(GV_SIZE) + if ptr is None: return None + string.memset(ptr, 0, GV_SIZE) + ptr.Name = name + ptr.Ty = ty + ptr.Initializer = None + ptr.IsConstant = 0 + ptr.Linkage = "external" + ptr.Section = None + ptr.UnnamedAddr = 0 + ptr.Align = 0 + return ptr + + +# ============================================================ +# 链表操作(嵌入式 Head/Tail/Count,O(1) 追加) +# ============================================================ +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 字段访问陷阱)""" + if mod is None: + return None + return mod.FuncHead + + +def module_add_global(mod: rawptr[LLVMModule], gv: rawptr[GlobalVariable]): + mod.add_global(gv) + + +def module_set_target(mod: rawptr[LLVMModule], triple: str): + mod.set_target(triple) + + +def module_set_datalayout(mod: rawptr[LLVMModule], layout: str): + mod.set_datalayout(layout) + + +# ============================================================ +# 命名结构体类型管理 +# ============================================================ + +# ============================================================ +# _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=否 +# ============================================================ +def _is_opaque_struct(ty: rawptr[LLVMType]) -> int: + """检查类型是否是 opaque 结构体 + + opaque 是跨模块前向声明(仅名称无字段定义),由 + _scan_type_for_cross_module_refs 创建,fcount=-1 哨兵标记。 + 空结构体(fcount==0, fields==None,无字段类)是合法完整定义 + (LLVM {} 类型,可 alloca),必须与 opaque 区分——此前混淆导致 + 空类结构体被输出为 "= type opaque",alloca 报 Cannot allocate + unsized type。 + """ + if ty is None: + return 0 + match ty: + case LLVMType.Struct(fields, fcount, name): + # opaque: fcount == -1(哨兵) + if fcount < 0: + return 1 + return 0 + case _: + return 0 + + +def module_add_named_type(mod: rawptr[LLVMModule], pool: rawptr[memhub.MemBuddy], + ty: rawptr[LLVMType]): + mod.add_named_type(pool, ty) + + +def module_set_named_type_packed(mod: rawptr[LLVMModule], pool: rawptr[memhub.MemBuddy], + ty: rawptr[LLVMType], is_packed: int): + """按名称设置命名结构体的 packed 标记(输出 %"name" = type <{...}>)。 + + 编译器解析 @c.Attribute(t.attr.packed) 后调用,使 llc 按 packed 布局 + (无字段填充)计算字段偏移/GEP。ty 为命名 Struct 类型(按 get_struct_name + 定位已注册节点);未找到则静默跳过(opaque/跨模块 stub 场景无布局需求)。 + """ + if mod is None or ty is None: + return + name: str = get_struct_name(ty) + if name is None: + return + cur: rawptr[NamedTypeNode] = mod.NamedTypeHead + cnt_p: int = 0 + while cur is not None: + cnt_p += 1 + if cnt_p > 100000: + return + if cur.Ty is not None: + existing_name: str = get_struct_name(cur.Ty) + if existing_name is not None and string.strcmp(existing_name, name) == 0: + cur.IsPacked = is_packed + return + cur = cur.Next + + +# ============================================================ +# module_ensure_opaque_for_type - 为类型中的跨模块结构体引用添加 opaque 声明 +# +# 递归扫描 LLVMType 树,当遇到 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时, +# 如果该名称未在模块中定义,则添加 opaque 声明(%"name" = type opaque)。 +# 这解决了跨模块结构体引用导致的 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) + + +def _scan_type_for_cross_module_refs(pool: rawptr[memhub.MemBuddy], + mod: rawptr[LLVMModule], + ty: rawptr[LLVMType], + depth: int = 0): + """递归扫描类型树,为跨模块结构体引用添加 opaque 声明""" + if ty is None or mod is None or pool is None: + return + # 深度守卫:自引用结构体(链表/树节点,如 Node{...; Ptr(Node) next}) + # 会无限递归栈溢出,与其他递归(TypePrint/DeepCopyType)一致设上限 + if depth >= 100: + return + match ty: + case LLVMType.Ptr(pointee): + if pointee is not None: + match pointee: + case LLVMType.Struct(sfields, sfcount, sname): + if sname is not None: + # 检查是否为跨模块引用(名称包含 '.') + if string.strchr(sname, 46) is not None: + # 检查是否已在模块中定义 + if _is_type_defined_in_module(mod, sname) == 0: + # 创建 opaque 类型并添加到模块 + # fcount=-1 哨兵标记 opaque(区别于空结构体 {}, + # 后者 fcount==0 是合法完整定义,可 alloca) + 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 引用 + if sfields is not None: + fld: rawptr[ParamNode] = sfields + fld_steps: t.CSizeT = 0 + while fld is not None: + fld_steps += 1 + if fld_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: struct fields(sname=%s,dump 终止)\n", + sname) + stdlib.exit(1) + if fld.Ty is not None: + _scan_type_for_cross_module_refs(pool, mod, fld.Ty, depth + 1) + fld = fld.Next + case _: + _scan_type_for_cross_module_refs(pool, mod, pointee, depth + 1) + case LLVMType.Struct(sfields2, sfcount2, sname2): + # 顶层结构体(非指针):递归扫描字段 + if sfields2 is not None: + fld2: rawptr[ParamNode] = sfields2 + fld2_steps: t.CSizeT = 0 + while fld2 is not None: + fld2_steps += 1 + if fld2_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: struct fields2(sname=%s,dump 终止)\n", + sname2) + stdlib.exit(1) + if fld2.Ty is not None: + _scan_type_for_cross_module_refs(pool, mod, fld2.Ty, depth + 1) + fld2 = fld2.Next + case LLVMType.Array(elem_ty, acount): + if elem_ty is not None: + _scan_type_for_cross_module_refs(pool, mod, elem_ty, depth + 1) + case _: + pass + + +def module_ensure_opaque_for_type(mod: rawptr[LLVMModule], + pool: rawptr[memhub.MemBuddy], + ty: rawptr[LLVMType]): + mod.ensure_opaque_for_type(pool, ty) + + +# ============================================================ +# 全局变量属性设置 +# ============================================================ +def global_set_constant(gv: rawptr[GlobalVariable], is_const: int): + """设置全局变量是否为常量""" + if gv is None: return + gv.IsConstant = is_const + + +def global_set_linkage(gv: rawptr[GlobalVariable], linkage: str): + """设置链接类型 (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)""" + if gv is None: return + gv.UnnamedAddr = level + + +def global_set_section(gv: rawptr[GlobalVariable], section: str): + """设置段名""" + if gv is None: return + gv.Section = section + + +# ============================================================ +# 输出模式常量 +# ============================================================ +OUTPUT_FULL: t.CDefine = 0 # 完整 IR(stub + text) +OUTPUT_STUB: t.CDefine = 1 # 仅声明(header + 类型定义 + declare + external global) +OUTPUT_TEXT: t.CDefine = 2 # 仅代码(define + global 带初值) + +# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast)。 +# 任何一条链(函数/全局/命名类型/结构体字段)的 Next 被越界写破坏成环时, +# 遍历会无限循环导致 tpv.exe 挂起(builtins.vp dump 卡死根因排查)。 +# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位" +# (对齐 memhub.self_check 的 max_steps 哲学)。 +IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000 + + +# ============================================================ +# LLVMModulePrint: 将模块序列化为 .ll 文本 +# +# mode: OUTPUT_FULL=完整, OUTPUT_STUB=仅声明, OUTPUT_TEXT=仅代码 +# ============================================================ +def LLVMModulePrint(buf: str, size: t.CSizeT, + mod: rawptr[LLVMModule], pool: rawptr[memhub.MemBuddy], + mode: int): + mod.PrintModule(buf, size, pool, mode) + + +# ============================================================ +# 全局变量打印 +# ============================================================ +def _print_global(buf: str, size: t.CSizeT, + gv: rawptr[GlobalVariable], pool: rawptr[memhub.MemBuddy]): + """打印单个全局变量: @ = [linkage] [unnamed_addr] [constant] """ + if gv is None: return + + _append_cstr(buf, size, "@") + if gv.Name is not None: + if _ll_name_needs_quote(gv.Name) != 0: + _append_cstr(buf, size, "\"") + _append_cstr(buf, size, gv.Name) + _append_cstr(buf, size, "\"") + else: + _append_cstr(buf, size, gv.Name) + _append_cstr(buf, size, " = ") + + # 链接类型(有初始值时不输出 external,因为 external + 初始值是非法 IR) + if gv.Linkage is not None: + if gv.Initializer is not None and string.strcmp(gv.Linkage, "external") == 0: + pass + else: + _append_cstr(buf, size, gv.Linkage) + _append_cstr(buf, size, " ") + + # unnamed_addr + if gv.UnnamedAddr == 1: + _append_cstr(buf, size, "unnamed_addr ") + elif gv.UnnamedAddr == 2: + _append_cstr(buf, size, "local_unnamed_addr ") + + # constant / global + if gv.IsConstant == 1: + _append_cstr(buf, size, "constant ") + else: + _append_cstr(buf, size, "global ") + + # 类型 + ty_buf: str = pool.alloc(512) + if ty_buf is not None: + ty_buf[0] = '\0' + TypePrint(ty_buf, 512, gv.Ty, pool) + _append_cstr(buf, size, ty_buf) + + # 初始化值 + if gv.Initializer is not None: + _append_cstr(buf, size, " ") + _append_cstr(buf, size, gv.Initializer) + + # 段名 + if gv.Section is not None: + _append_cstr(buf, size, ", section \"") + _append_cstr(buf, size, gv.Section) + _append_cstr(buf, size, "\"") + + # 对齐(@c.Attribute(t.attr.aligned(N)) 类的全局实例,如 fpu_context 16 对齐) + if gv.Align > 0: + align_buf: str = pool.alloc(32) + if align_buf is not None: + viperlib.snprintf(align_buf, 32, ", align %d", gv.Align) + _append_cstr(buf, size, align_buf) + + +# ============================================================ +# _print_global_stub - 打印全局变量的声明形式(external global,无初值) +# ============================================================ +def _print_global_stub(buf: str, size: t.CSizeT, + gv: rawptr[GlobalVariable], pool: rawptr[memhub.MemBuddy]): + """打印全局变量声明: @ = external global """ + if gv is None: return + + _append_cstr(buf, size, "@") + if gv.Name is not None: + if _ll_name_needs_quote(gv.Name) != 0: + _append_cstr(buf, size, "\"") + _append_cstr(buf, size, gv.Name) + _append_cstr(buf, size, "\"") + else: + _append_cstr(buf, size, gv.Name) + _append_cstr(buf, size, " = external global ") + + # 类型 + ty_buf: str = pool.alloc(512) + if ty_buf is not None: + ty_buf[0] = '\0' + TypePrint(ty_buf, 512, gv.Ty, pool) + _append_cstr(buf, size, ty_buf) + + +# ============================================================ +# 内部辅助 +# ============================================================ +# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, +# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 +# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 +_g_last_dst: t.CUInt64T = 0 +_g_last_pos: t.CSizeT = 0 + +def _append_cstr(dst: str, dst_size: t.CSizeT, src: str): + """将 C 字符串 src 追加到 dst 末尾""" + global _g_last_dst, _g_last_pos + if dst is None or src is None: return + dlen: t.CSizeT = 0 + if _g_last_dst == t.CUInt64T(dst): + dlen = _g_last_pos + # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 + 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))。 + if dlen == 0: + if dst[0] != '\0': + dlen = string.strlen(dst) + elif dst[dlen] != '\0' or dst[dlen - 1] == '\0': + dlen = string.strlen(dst) + else: + dlen = string.strlen(dst) + _g_last_dst = t.CUInt64T(dst) + # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, + # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + if dlen >= dst_size: + return + slen: t.CSizeT = string.strlen(src) + remain: t.CSizeT = dst_size - dlen + if remain <= 0: return + i: t.CSizeT = 0 + while i < slen and i + 1 < remain: + dst[dlen + i] = src[i] + i += 1 + dst[dlen + i] = '\0' _g_last_pos = dlen + i \ No newline at end of file diff --git a/llvmlite/__types.vp b/llvmlite/__types.vp index 6bb8b9b..3160e4c 100644 --- a/llvmlite/__types.vp +++ b/llvmlite/__types.vp @@ -1,780 +1,780 @@ -import t, c -from stdint import * -import viperlib -import string -import memhub -import stdio -import stdlib -from linkedlist import GSListNode - - -# ============================================================ -# ParamNode: 函数参数类型链表节点 -# 继承 GSListNode[ParamNode] 获取强类型 Next: ParamNode|CPtr -# Ty 用 rowptr[t.CVoid] 前向引用,使用时转型为 (rowptr[LLVMType]) -# -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 -# ============================================================ -@t.NoVTable -class ParamNode(GSListNode[ParamNode]): - Ty: rowptr[t.CVoid] # LLVMType*(前向引用,使用时转型) - - -# ============================================================ -# LLVMType: LLVM IR 类型系统(t.REnum tagged union) -# -# 变体少、无通用字段、操作通过 match 分派 —— 契合 REnum 模型。 -# 布局: { i32 __tag, } -# ============================================================ -class LLVMType(t.REnum): - # 整数类型: i1/i8/i16/i32/i64 - class Int: - Bits: int - - # 指针类型: pointee* - class Ptr: - Pointee: rowptr[LLVMType] - - # 函数类型: ret (params) - class Func: - Ret: rowptr[LLVMType] - Params: rowptr[ParamNode] - PCount: int - - # 数组类型: [N x elem_ty] - class Array: - ElemTy: rowptr[LLVMType] - Count: int - - # 结构体类型: {ty1, ty2, ...} 或 %"name" = type {ty1, ty2, ...} - # Fields 复用 ParamNode 链表(ParamNode.Ty 即字段类型) - # Name 非 None 时为命名结构体,TypePrint 输出 %"name" 而非 {...} - class Struct: - Fields: rowptr[ParamNode] - FCount: int - Name: str - - # 浮点类型: half/float/double/fp128 - class Float: - Bits: int # 16=half, 32=float, 64=double, 128=fp128 - - # 无值类型 - class Void: - pass - - # 标签类型 - class Label: - pass - - # 元数据类型 - class Metadata: - pass - - -# ============================================================ -# 内部分配辅助 -# ============================================================ -def _new_type(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - """从 mpool 分配一个零初始化的 LLVMType""" - ptr: rowptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) - if ptr: - string.memset(ptr, 0, LLVMType.__sizeof__()) - return ptr - - -# ============================================================ -# 类型工厂函数 -# ============================================================ -def Int1(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Int(1)) - return ptr - - -def Int8(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Int(8)) - return ptr - - -def Int16(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Int(16)) - return ptr - - -def Int32(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Int(32)) - return ptr - - -def Int64(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Int(64)) - return ptr - - -def Ptr(pool: rowptr[memhub.MemBuddy], pointee: rowptr[LLVMType]) -> rowptr[LLVMType]: - """创建指针类型 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)。 - """ - # ============== 前置校验: 拦截 Void / None pointee(void* 非法)============== - safe_pointee: rowptr[LLVMType] = pointee - if safe_pointee is None: - safe_pointee = Int8(pool) - else: - match safe_pointee: - case LLVMType.Void(): - safe_pointee = Int8(pool) - case _: - pass - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Ptr(safe_pointee)) - return ptr - - -def Func(pool: rowptr[memhub.MemBuddy], ret: rowptr[LLVMType], - params: rowptr[ParamNode], count: int) -> rowptr[LLVMType]: - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Func(ret, params, count)) - return ptr - - -def Void(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Void()) - return ptr - - -def Array(pool: rowptr[memhub.MemBuddy], elem_ty: rowptr[LLVMType], - count: int) -> rowptr[LLVMType]: - """创建数组类型 [count x elem_ty]""" - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Array(elem_ty, count)) - return ptr - - -def Struct(pool: rowptr[memhub.MemBuddy], fields: rowptr[ParamNode], - fcount: int, name: str) -> rowptr[LLVMType]: - """创建结构体类型 {ty1, ty2, ...} - - fields 为 ParamNode 链表头,fcount 为字段数量。 - name 非 None 时为命名结构体(如 "sha1.ClassName"),TypePrint 输出 %"name"。 - name 为 None 时为匿名结构体,TypePrint 输出 {ty1, ty2, ...}。 - """ - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Struct(fields, fcount, name)) - return ptr - - -def Half(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - """创建半精度浮点类型 half (16-bit)""" - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Float(16)) - return ptr - - -def Float(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - """创建单精度浮点类型 float (32-bit)""" - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Float(32)) - return ptr - - -def Double(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - """创建双精度浮点类型 double (64-bit)""" - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Float(64)) - return ptr - - -def FP128(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - """创建四精度浮点类型 fp128 (128-bit)""" - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Float(128)) - return ptr - - -def Label(pool: rowptr[memhub.MemBuddy]) -> rowptr[LLVMType]: - """创建标签类型 label""" - ptr: rowptr[LLVMType] = _new_type(pool) - if ptr is None: return None - c.DerefAs(ptr, LLVMType.Label()) - return ptr - - -# ============================================================ -# ParamNode 辅助 -# ============================================================ -# ParamNode 硬编码大小: Next(8) + Ty(8) = 16 字节 -PNODE_SIZE: t.CDefine = 16 - -def new_param_node(pool: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType]) -> rowptr[ParamNode]: - """创建一个参数节点,Ty 设为给定类型指针""" - ptr: rowptr[ParamNode] = pool.alloc(PNODE_SIZE) - if ptr is None: return None - string.memset(ptr, 0, PNODE_SIZE) - ptr.Ty = ty - ptr.Next = None - return ptr - - -def param_list_append(head: rowptr[ParamNode], tail: rowptr[ParamNode], - node: rowptr[ParamNode]) -> rowptr[ParamNode]: - """将 node 追加到参数链表尾部,返回 head 和更新后的 tail""" - if node is None: return head - node.Next = None - if head is None: - return node - if tail is not None: - tail.Next = node - return node - - -# ============================================================ -# ParamNode 字段访问器(供其他模块绕过 stub 类型限制使用) -# -# 跨模块编译时,使用方模块只有 ParamNode 的 stub 类型, -# 导致 TransPyC 静默跳过字段访问。这些访问器在拥有完整类型定义的 -# 本模块内执行,能正确访问所有字段。 -# ============================================================ -def paramnode_get_ty(node: rowptr[ParamNode]) -> rowptr[LLVMType]: - """获取参数节点的类型""" - if node is None: - return None - return (rowptr[LLVMType])(node.Ty) - - -def paramnode_get_next(node: rowptr[ParamNode]) -> rowptr[ParamNode]: - """获取链表下一个参数节点""" - if node is None: - return None - return node.Next - - -def paramnode_set_ty(node: rowptr[ParamNode], ty: rowptr[LLVMType]): - """设置参数节点的类型""" - if node is None: - return - node.Ty = ty - - -def paramnode_set_next(node: rowptr[ParamNode], nxt: rowptr[ParamNode]): - """设置链表下一个参数节点""" - if node is None: - return - node.Next = nxt - - -# ============================================================ -# LLVMTypeCore: 类型创建核心,封装 pool 避免重复传参 -# -# 用法: -# core: rowptr[LLVMTypeCore] = LLVMTypeCore(pool) -# ty_i32: rowptr[LLVMType] = core.Int32() -# ty_ptr: rowptr[LLVMType] = core.Ptr(ty_i32) -# ty_void: rowptr[LLVMType] = core.Void() -# ============================================================ -@t.NoVTable -class LLVMTypeCore: - """类型创建核心,封装 pool 避免每次创建类型都传 pool""" - Pool: rowptr[memhub.MemBuddy] - - def __init__(self, pool: rowptr[memhub.MemBuddy]): - self.Pool = pool - - def Int1(self) -> rowptr[LLVMType]: - return Int1(self.Pool) - - def Int8(self) -> rowptr[LLVMType]: - return Int8(self.Pool) - - def Int16(self) -> rowptr[LLVMType]: - return Int16(self.Pool) - - def Int32(self) -> rowptr[LLVMType]: - return Int32(self.Pool) - - def Int64(self) -> rowptr[LLVMType]: - return Int64(self.Pool) - - def Ptr(self, pointee: rowptr[LLVMType]) -> rowptr[LLVMType]: - return Ptr(self.Pool, pointee) - - def Func(self, ret: rowptr[LLVMType], params: rowptr[ParamNode], - count: int) -> rowptr[LLVMType]: - return Func(self.Pool, ret, params, count) - - def Array(self, elem_ty: rowptr[LLVMType], count: int) -> rowptr[LLVMType]: - return Array(self.Pool, elem_ty, count) - - def Struct(self, fields: rowptr[ParamNode], fcount: int, - name: str = None) -> rowptr[LLVMType]: - return Struct(self.Pool, fields, fcount, name) - - def Void(self) -> rowptr[LLVMType]: - return Void(self.Pool) - - def Half(self) -> rowptr[LLVMType]: - return Half(self.Pool) - - def Float(self) -> rowptr[LLVMType]: - return Float(self.Pool) - - def Double(self) -> rowptr[LLVMType]: - return Double(self.Pool) - - def FP128(self) -> rowptr[LLVMType]: - return FP128(self.Pool) - - def Label(self) -> rowptr[LLVMType]: - return Label(self.Pool) - - def NewParamNode(self, ty: rowptr[LLVMType]) -> rowptr[ParamNode]: - return new_param_node(self.Pool, ty) - - # ---- 链表辅助 ---- - def ParamListAppend(self, head: rowptr[ParamNode], tail: rowptr[ParamNode], - node: rowptr[ParamNode]) -> rowptr[ParamNode]: - """将 node 追加到参数/字段链表,返回新的 tail""" - return param_list_append(head, tail, node) - - # ---- 打印 ---- - def PrintType(self, buf: str, size: t.CSizeT, ty: rowptr[LLVMType]): - TypePrint(buf, size, ty, self.Pool) - - -# ============================================================ -# TypePrint: 将 LLVMType 序列化为 IR 文本 -# -# 示例: -# Int(32) -> "i32" -# Int(8) -> "i8" -# Ptr(Int(32)) -> "i32*" -# Void -> "void" -# Array(Int(32),4) -> "[4 x i32]" -# Struct(...) -> "{i32, i8*}" -# Func(...) -> "i32 (i32, i8*)" -# -# pool 用于复杂类型(Array/Struct/Func)递归打印子类型时分配临时缓冲区。 -# ============================================================ -def TypePrint(buf: str, size: t.CSizeT, - ty: rowptr[LLVMType], pool: rowptr[memhub.MemBuddy]): - """将类型 ty 序列化为 IR 文本写入 buf(容量 size) - - pool 用于递归打印子类型时分配临时缓冲区。 - """ - _TypePrintDepth(buf, size, ty, pool, 0) - - -# 递归深度上限:悬空/自引用的 LLVMType 树(1b reset 后内存被复用)会导致 -# Ptr(pointee)/Struct(fields)/Func(params) 无限递归,每次递归分配临时缓冲 -# 造成 OOM(单文件暴涨 900MB 根因)。超过 100 层即停止打印。 -TYPEPRINT_MAX_DEPTH: t.CDefine = 100 - -def _TypePrintDepth(buf: str, size: t.CSizeT, - ty: rowptr[LLVMType], pool: rowptr[memhub.MemBuddy], - depth: int): - """带递归深度上限的 TypePrint 内部实现""" - if ty is None or buf is None or size == 0: - return - if depth >= TYPEPRINT_MAX_DEPTH: - viperlib.snprintf(buf, size, "...") - return - buf[0] = '\0' - match ty: - case LLVMType.Int(bits): - viperlib.snprintf(buf, size, "i%d", bits) - case LLVMType.Ptr(pointee): - # ================================================================ - # LLVM IR 不允许 "void*",必须是 "i8*"。 - # 兜底处理: - # pointee == None / Void() / Label() / Metadata() → 统一打印 i8* - # ================================================================ - # 先判断是否 Struct(name) 的快速路径(需先判断 pointee 非None) - if pointee is not None: - match pointee: - case LLVMType.Struct(sfields, sfcount, sname): - if sname is not None: - # 悬空防护:sname 可能指向被 reset 回收/部分复用的 pool 内存。 - # viperlib 的 %s 未实现 precision(%.40s 会全量读 sname), - # 手动 strncpy 限长拷贝后再打印,读侧限制在缓冲内。 - # 类型名含 sha1 前缀可超 40 字符(如 - # "bbdf3bbd4c3bc28c.CONSOLE_SCREEN_BUFFER_INFO"=42), - # 原 40 字节截断会产生未定义类型引用(llc: undefined type)。 - sn_cp: str = pool.alloc(256) - if sn_cp is not None: - string.strncpy(sn_cp, sname, 255) - sn_cp[255] = '\0' - viperlib.snprintf(buf, size, "%%\"%s\"*", sn_cp) - return - case LLVMType.Void(): - # Ptr(Void) → i8*(LLVM IR void* 非法) - viperlib.snprintf(buf, size, "%s", "i8*") - return - case LLVMType.Label(): - viperlib.snprintf(buf, size, "%s", "i8*") - return - case LLVMType.Metadata(): - viperlib.snprintf(buf, size, "%s", "i8*") - return - case _: - pass - # pointee == None 时也兜底 Ptr(Int8) 打印 i8* - if pointee is None: - viperlib.snprintf(buf, size, "%s", "i8*") - return - # 通用路径: 递归打印 pointee 后追加 '*' - _TypePrintDepth(buf, size, pointee, pool, depth + 1) - pos: t.CSizeT = string.strlen(buf) - if pos + 1 < size: - buf[pos] = '*' - buf[pos + 1] = '\0' - case LLVMType.Array(elem_ty, count): - # [N x elem_ty] - viperlib.snprintf(buf, size, "[%d x ", count) - # SHA1 限定类型名(如 %"21a7fcfc665f75ef.NamedTypeNode")可达 ~50 字节, - # 嵌套指针/数组更易超过 128,使用 512 字节避免截断 - tmp: str = pool.alloc(512) - if tmp is not None: - _TypePrintDepth(tmp, 512, elem_ty, pool, depth + 1) - _strappend(buf, size, tmp) - _strappend(buf, size, "]") - case LLVMType.Struct(fields, fcount, name): - if name is not None: - # 命名结构体: %"name" - viperlib.snprintf(buf, size, "%%\"%s\"", name) - else: - # 匿名结构体: {ty1, ty2, ...} - _print_struct_body(buf, size, fields, pool, depth) - case LLVMType.Func(ret, params, count): - # ret (ty1, ty2, ...) - _TypePrintDepth(buf, size, ret, pool, depth + 1) - _strappend(buf, size, " (") - cur2: rowptr[ParamNode] = params - first2: int = 1 - _pc2: int = 0 - # 函数参数类型可能为 SHA1 限定命名结构体指针,512 字节避免截断 - tmp3: str = pool.alloc(512) - while cur2 is not None: - # 悬空保护:参数链若被 reset 回收后内容复用,可能成环无限循环 - _pc2 += 1 - if _pc2 > 200: - _strappend(buf, size, "...)") - break - if first2 == 0: - _strappend(buf, size, ", ") - if tmp3 is not None: - _TypePrintDepth(tmp3, 512, cur2.Ty, pool, depth + 1) - _strappend(buf, size, tmp3) - cur2 = cur2.Next - first2 = 0 - _strappend(buf, size, ")") - case LLVMType.Void(): - viperlib.snprintf(buf, size, "%s", "void") - case LLVMType.Label(): - viperlib.snprintf(buf, size, "%s", "label") - case LLVMType.Metadata(): - viperlib.snprintf(buf, size, "%s", "metadata") - case LLVMType.Float(bits): - if bits == 16: - viperlib.snprintf(buf, size, "%s", "half") - elif bits == 32: - viperlib.snprintf(buf, size, "%s", "float") - elif bits == 64: - viperlib.snprintf(buf, size, "%s", "double") - elif bits == 128: - viperlib.snprintf(buf, size, "%s", "fp128") - else: - viperlib.snprintf(buf, size, "float%x", bits) - case _: - viperlib.snprintf(buf, size, "%s", "void") - - -# ============================================================ -# 内部辅助 -# ============================================================ -def _strappend(dst: str, dst_size: t.CSizeT, src: str): - """将 src 追加到 dst 末尾(不超过 dst_size)""" - if dst is None or src is None: return - dlen: t.CSizeT = string.strlen(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到 GB 外 - if dlen >= dst_size: - return - slen: t.CSizeT = string.strlen(src) - remain: t.CSizeT = dst_size - dlen - if remain <= 0: return - i: t.CSizeT = 0 - while i < slen and i + 1 < remain: - dst[dlen + i] = src[i] - i += 1 - dst[dlen + i] = '\0' - - -# ============================================================ -# _print_struct_body - 打印结构体字段部分 {ty1, ty2, ...} -# -# 供 TypePrint(匿名结构体)和 PrintStructDefinition(命名结构体定义行)复用 -# ============================================================ -def _print_struct_body(buf: str, size: t.CSizeT, - fields: rowptr[ParamNode], pool: rowptr[memhub.MemBuddy], - depth: int = 0, packed: int = 0): - """打印 {ty1, ty2, ...} 到 buf(追加模式,不覆盖已有内容) - - 直接在 buf 当前末尾写入字段类型,避免 pool.alloc 返回与 buf 重叠的内存 - 导致 TypePrint 写入 tmp 时覆盖 buf 已有内容。 - packed=1 时输出 <{ty1, ty2, ...}>(LLVM packed struct,无字段填充)。 - """ - if buf is None or size == 0: - return - if packed != 0: - _strappend(buf, size, "<{") - else: - _strappend(buf, size, "{") - cur: rowptr[ParamNode] = fields - first: int = 1 - fidx: int = 0 - while cur is not None: - # 悬空保护:ParamNode 链若被 reset 回收后内容复用,Next 指针可能成环或 - # 指向垃圾,无限循环。最多打印 200 个字段即停止,防止 OOM。 - if fidx >= 200: - if packed != 0: - _strappend(buf, size, "...}>") - else: - _strappend(buf, size, "...}") - return - if first == 0: - _strappend(buf, size, ", ") - # 直接在 buf 当前末尾位置写入字段类型 - # TypePrint 首字节置 '\0' 是无害的(pos 处本来就是 '\0') - pos: t.CSizeT = string.strlen(buf) - if pos + 2 < size: - _TypePrintDepth(buf + pos, size - pos, cur.Ty, pool, depth + 1) - cur = cur.Next - first = 0 - fidx += 1 - if packed != 0: - _strappend(buf, size, "}>") - else: - _strappend(buf, size, "}") - - -# ============================================================ -# PrintStructDefinition - 打印命名结构体类型定义行 -# -# 输出格式: %"name" = type { ty1, ty2, ... } -# 供 LLVMModulePrint 在全局变量/函数之前输出类型定义 -# ============================================================ -def PrintStructDefinition(buf: str, size: t.CSizeT, - ty: rowptr[LLVMType], pool: rowptr[memhub.MemBuddy], - packed: int = 0): - """打印命名结构体类型定义行 %"name" = type { ty1, ty2, ... } - - 当 fcount == -1(哨兵)时输出 opaque 声明: %"name" = type opaque。 - 空结构体(fcount==0, fields==None,无字段类)是完整定义,输出 "= type {}"。 - packed=1 时输出 %"name" = type <{ ty1, ty2, ... }>(LLVM packed struct)。 - """ - if ty is None or buf is None or size == 0: - return - match ty: - case LLVMType.Struct(fields, fcount, name): - if name is None: - return - # 使用 snprintf 构建 "%%"name" = type" 前缀,避免 _strappend 截断 - # opaque 类型: fcount == -1(哨兵,跨模块前向声明) - if fcount < 0: - viperlib.snprintf(buf, size, "%%\"%s\" = type opaque", name) - else: - viperlib.snprintf(buf, size, "%%\"%s\" = type ", name) - _print_struct_body(buf, size, fields, pool, 0, packed) - case _: - return - - -# ============================================================ -# get_struct_name - 获取命名结构体的名称 -# -# 返回 Name 字段(如 "sha1.ClassName"),非 Struct 类型或匿名 Struct 返回 None -# 供 module_add_named_type 检查是否为命名结构体及去重 -# ============================================================ -def get_struct_name(ty: rowptr[LLVMType]) -> str: - """获取命名结构体的名称,非命名结构体返回 None""" - if ty is None: - return None - match ty: - case LLVMType.Struct(fields, fcount, name): - return name - case _: - return None - - -def get_struct_fields(ty: rowptr[LLVMType]) -> rowptr[ParamNode]: - """获取结构体的字段链表(供跨模块重建 Struct 类型用)""" - if ty is None: - return None - match ty: - case LLVMType.Struct(fields, fcount, name): - return fields - case _: - return None - - -def get_struct_fcount(ty: rowptr[LLVMType]) -> int: - """获取结构体的字段数量(供跨模块重建 Struct 类型用)""" - if ty is None: - return 0 - match ty: - case LLVMType.Struct(fields, fcount, name): - return fcount - case _: - 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) 字段)会导致无限递归, -# 用深度上限防护(超过即返回浅拷贝副本,后续不再递归)。 -# ============================================================ -DEEPCOPY_MAX_DEPTH: t.CDefine = 64 - -def DeepCopyType(pool: rowptr[memhub.MemBuddy], - ty: rowptr[LLVMType]) -> rowptr[LLVMType]: - """深拷贝 LLVMType 到 stdlib 区(深度限制防自引用递归),失败返回 None""" - return _DeepCopyDepth(pool, ty, 0) - - -def _DeepCopyDepth(pool: rowptr[memhub.MemBuddy], - ty: rowptr[LLVMType], depth: int) -> rowptr[LLVMType]: - """带深度上限的深拷贝实现 - - 分配源用传入的 pool 参数(调用方决定):传主 pool 为瞬时拷贝 - (随 mark/reset 回收),传永久 pool(CompilerFactory.get_perm_pool()) - 为跨 mark 持久拷贝(代替 stdlib.malloc,buddy 分配更快)。 - """ - if ty is None: - return None - new_ty: rowptr[LLVMType] = (rowptr[LLVMType])(pool.alloc(LLVMType.__sizeof__())) - if new_ty is None: - return None - string.memset(new_ty, 0, LLVMType.__sizeof__()) - if depth >= DEEPCOPY_MAX_DEPTH: - # 深度超限:浅拷贝 tag 后停止递归(复制当前变体但不深拷子类型) - match ty: - case LLVMType.Int(bits): - c.DerefAs(new_ty, LLVMType.Int(bits)) - case LLVMType.Float(bits): - c.DerefAs(new_ty, LLVMType.Float(bits)) - case LLVMType.Void(): - c.DerefAs(new_ty, LLVMType.Void()) - case LLVMType.Label(): - c.DerefAs(new_ty, LLVMType.Label()) - case LLVMType.Metadata(): - c.DerefAs(new_ty, LLVMType.Metadata()) - case _: - c.DerefAs(new_ty, LLVMType.Void()) - return new_ty - match ty: - case LLVMType.Int(bits): - c.DerefAs(new_ty, LLVMType.Int(bits)) - case LLVMType.Float(bits): - c.DerefAs(new_ty, LLVMType.Float(bits)) - case LLVMType.Void(): - c.DerefAs(new_ty, LLVMType.Void()) - case LLVMType.Label(): - c.DerefAs(new_ty, LLVMType.Label()) - case LLVMType.Metadata(): - c.DerefAs(new_ty, LLVMType.Metadata()) - case LLVMType.Ptr(pointee): - c.DerefAs(new_ty, LLVMType.Ptr(_DeepCopyDepth(pool, pointee, depth + 1))) - case LLVMType.Array(elem_ty, count): - c.DerefAs(new_ty, LLVMType.Array(_DeepCopyDepth(pool, elem_ty, depth + 1), count)) - case LLVMType.Func(ret, params, pcount): - c.DerefAs(new_ty, LLVMType.Func( - _DeepCopyDepth(pool, ret, depth + 1), - _DeepCopyParamChain(pool, params, depth + 1), pcount)) - case LLVMType.Struct(fields, fcount, name): - c.DerefAs(new_ty, LLVMType.Struct( - _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)。 - pool.free(new_ty) - return None - return new_ty - - -def _DeepCopyParamChain(pool: rowptr[memhub.MemBuddy], - head: rowptr[ParamNode], depth: int) -> rowptr[ParamNode]: - """深拷贝 ParamNode 链表到 stdlib""" - if head is None: - return None - first: rowptr[ParamNode] = None - prev: rowptr[ParamNode] = None - cur: rowptr[ParamNode] = head - guard: int = 0 - while cur is not None: - guard += 1 - if guard > 512: - break - node: rowptr[ParamNode] = (rowptr[ParamNode])(pool.alloc(PNODE_SIZE)) - if node is None: - break - string.memset(node, 0, PNODE_SIZE) - node.Ty = _DeepCopyDepth(pool, paramnode_get_ty(cur), depth + 1) - node.Next = None - if first is None: - first = node - if prev is not None: - prev.Next = node - prev = node - cur = paramnode_get_next(cur) - return first - - -def _DeepCopyStr(pool: rowptr[memhub.MemBuddy], s: str) -> str: - """深拷贝字符串到 stdlib""" - if s is None: - return None - slen: t.CSizeT = string.strlen(s) - copy: str = pool.alloc(slen + 1) - if copy is None: - return None - string.strcpy(copy, s) - return copy - - - - +import t, c +from stdint import * +import viperlib +import string +import memhub +import stdio +import stdlib +from linkedlist import GSListNode + + +# ============================================================ +# ParamNode: 函数参数类型链表节点 +# 继承 GSListNode[ParamNode] 获取强类型 Next: ParamNode|CPtr +# Ty 用 rawptr[t\.CVoid] 前向引用,使用时转型为 (rawptr[LLVMType]) +# +# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 +# ============================================================ +@t.NoVTable +class ParamNode(GSListNode[ParamNode]): + Ty: rawptr # LLVMType*(前向引用,使用时转型) + + +# ============================================================ +# LLVMType: LLVM IR 类型系统(t.REnum tagged union) +# +# 变体少、无通用字段、操作通过 match 分派 —— 契合 REnum 模型。 +# 布局: { i32 __tag, } +# ============================================================ +class LLVMType(t.REnum): + # 整数类型: i1/i8/i16/i32/i64 + class Int: + Bits: int + + # 指针类型: pointee* + class Ptr: + Pointee: rawptr[LLVMType] + + # 函数类型: ret (params) + class Func: + Ret: rawptr[LLVMType] + Params: rawptr[ParamNode] + PCount: int + + # 数组类型: [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" 而非 {...} + class Struct: + Fields: rawptr[ParamNode] + FCount: int + Name: str + + # 浮点类型: half/float/double/fp128 + class Float: + Bits: int # 16=half, 32=float, 64=double, 128=fp128 + + # 无值类型 + class Void: + pass + + # 标签类型 + class Label: + pass + + # 元数据类型 + class Metadata: + pass + + +# ============================================================ +# 内部分配辅助 +# ============================================================ +def _new_type(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + """从 mpool 分配一个零初始化的 LLVMType""" + ptr: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__()) + if ptr: + string.memset(ptr, 0, LLVMType.__sizeof__()) + return ptr + + +# ============================================================ +# 类型工厂函数 +# ============================================================ +def Int1(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Int(1)) + return ptr + + +def Int8(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Int(8)) + return ptr + + +def Int16(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Int(16)) + return ptr + + +def Int32(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Int(32)) + return ptr + + +def Int64(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Int(64)) + return ptr + + +def Ptr(pool: rawptr[memhub.MemBuddy], pointee: rawptr[LLVMType]) -> rawptr[LLVMType]: + """创建指针类型 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)。 + """ + # ============== 前置校验: 拦截 Void / None pointee(void* 非法)============== + safe_pointee: rawptr[LLVMType] = pointee + if safe_pointee is None: + safe_pointee = Int8(pool) + else: + match safe_pointee: + case LLVMType.Void(): + safe_pointee = Int8(pool) + case _: + pass + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Ptr(safe_pointee)) + return ptr + + +def Func(pool: rawptr[memhub.MemBuddy], ret: rawptr[LLVMType], + params: rawptr[ParamNode], count: int) -> rawptr[LLVMType]: + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Func(ret, params, count)) + return ptr + + +def Void(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Void()) + return ptr + + +def Array(pool: rawptr[memhub.MemBuddy], elem_ty: rawptr[LLVMType], + count: int) -> rawptr[LLVMType]: + """创建数组类型 [count x elem_ty]""" + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Array(elem_ty, count)) + return ptr + + +def Struct(pool: rawptr[memhub.MemBuddy], fields: rawptr[ParamNode], + fcount: int, name: str) -> rawptr[LLVMType]: + """创建结构体类型 {ty1, ty2, ...} + + fields 为 ParamNode 链表头,fcount 为字段数量。 + name 非 None 时为命名结构体(如 "sha1.ClassName"),TypePrint 输出 %"name"。 + name 为 None 时为匿名结构体,TypePrint 输出 {ty1, ty2, ...}。 + """ + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Struct(fields, fcount, name)) + return ptr + + +def Half(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + """创建半精度浮点类型 half (16-bit)""" + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Float(16)) + return ptr + + +def Float(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + """创建单精度浮点类型 float (32-bit)""" + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Float(32)) + return ptr + + +def Double(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + """创建双精度浮点类型 double (64-bit)""" + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Float(64)) + return ptr + + +def FP128(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + """创建四精度浮点类型 fp128 (128-bit)""" + ptr: rawptr[LLVMType] = _new_type(pool) + if ptr is None: return None + c.DerefAs(ptr, LLVMType.Float(128)) + return ptr + + +def Label(pool: rawptr[memhub.MemBuddy]) -> rawptr[LLVMType]: + """创建标签类型 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 字节 +PNODE_SIZE: t.CDefine = 16 + +def new_param_node(pool: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType]) -> rawptr[ParamNode]: + """创建一个参数节点,Ty 设为给定类型指针""" + ptr: rawptr[ParamNode] = pool.alloc(PNODE_SIZE) + if ptr is None: return None + string.memset(ptr, 0, PNODE_SIZE) + ptr.Ty = ty + ptr.Next = None + return ptr + + +def param_list_append(head: rawptr[ParamNode], tail: rawptr[ParamNode], + node: rawptr[ParamNode]) -> rawptr[ParamNode]: + """将 node 追加到参数链表尾部,返回 head 和更新后的 tail""" + if node is None: return head + node.Next = None + if head is None: + return node + if tail is not None: + tail.Next = node + return node + + +# ============================================================ +# ParamNode 字段访问器(供其他模块绕过 stub 类型限制使用) +# +# 跨模块编译时,使用方模块只有 ParamNode 的 stub 类型, +# 导致 TransPyC 静默跳过字段访问。这些访问器在拥有完整类型定义的 +# 本模块内执行,能正确访问所有字段。 +# ============================================================ +def paramnode_get_ty(node: rawptr[ParamNode]) -> rawptr[LLVMType]: + """获取参数节点的类型""" + if node is None: + return None + return (rawptr[LLVMType])(node.Ty) + + +def paramnode_get_next(node: rawptr[ParamNode]) -> rawptr[ParamNode]: + """获取链表下一个参数节点""" + if node is None: + return None + return node.Next + + +def paramnode_set_ty(node: rawptr[ParamNode], ty: rawptr[LLVMType]): + """设置参数节点的类型""" + if node is None: + return + node.Ty = ty + + +def paramnode_set_next(node: rawptr[ParamNode], nxt: rawptr[ParamNode]): + """设置链表下一个参数节点""" + 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() +# ============================================================ +@t.NoVTable +class LLVMTypeCore: + """类型创建核心,封装 pool 避免每次创建类型都传 pool""" + Pool: rawptr[memhub.MemBuddy] + + def __init__(self, pool: rawptr[memhub.MemBuddy]): + self.Pool = pool + + def Int1(self) -> rawptr[LLVMType]: + return Int1(self.Pool) + + def Int8(self) -> rawptr[LLVMType]: + return Int8(self.Pool) + + def Int16(self) -> rawptr[LLVMType]: + return Int16(self.Pool) + + def Int32(self) -> rawptr[LLVMType]: + return Int32(self.Pool) + + def Int64(self) -> rawptr[LLVMType]: + return Int64(self.Pool) + + def Ptr(self, pointee: rawptr[LLVMType]) -> rawptr[LLVMType]: + return Ptr(self.Pool, pointee) + + def Func(self, ret: rawptr[LLVMType], params: rawptr[ParamNode], + count: int) -> rawptr[LLVMType]: + return Func(self.Pool, ret, params, count) + + def Array(self, elem_ty: rawptr[LLVMType], count: int) -> rawptr[LLVMType]: + return Array(self.Pool, elem_ty, count) + + def Struct(self, fields: rawptr[ParamNode], fcount: int, + name: str = None) -> rawptr[LLVMType]: + return Struct(self.Pool, fields, fcount, name) + + def Void(self) -> rawptr[LLVMType]: + return Void(self.Pool) + + def Half(self) -> rawptr[LLVMType]: + return Half(self.Pool) + + def Float(self) -> rawptr[LLVMType]: + return Float(self.Pool) + + def Double(self) -> rawptr[LLVMType]: + return Double(self.Pool) + + def FP128(self) -> rawptr[LLVMType]: + return FP128(self.Pool) + + def Label(self) -> rawptr[LLVMType]: + return Label(self.Pool) + + def NewParamNode(self, ty: rawptr[LLVMType]) -> rawptr[ParamNode]: + return new_param_node(self.Pool, ty) + + # ---- 链表辅助 ---- + def ParamListAppend(self, head: rawptr[ParamNode], tail: rawptr[ParamNode], + node: rawptr[ParamNode]) -> rawptr[ParamNode]: + """将 node 追加到参数/字段链表,返回新的 tail""" + return param_list_append(head, tail, node) + + # ---- 打印 ---- + def PrintType(self, buf: str, size: t.CSizeT, ty: rawptr[LLVMType]): + TypePrint(buf, size, ty, self.Pool) + + +# ============================================================ +# TypePrint: 将 LLVMType 序列化为 IR 文本 +# +# 示例: +# Int(32) -> "i32" +# Int(8) -> "i8" +# Ptr(Int(32)) -> "i32*" +# Void -> "void" +# Array(Int(32),4) -> "[4 x i32]" +# Struct(...) -> "{i32, i8*}" +# Func(...) -> "i32 (i32, i8*)" +# +# pool 用于复杂类型(Array/Struct/Func)递归打印子类型时分配临时缓冲区。 +# ============================================================ +def TypePrint(buf: str, size: t.CSizeT, + ty: rawptr[LLVMType], pool: rawptr[memhub.MemBuddy]): + """将类型 ty 序列化为 IR 文本写入 buf(容量 size) + + pool 用于递归打印子类型时分配临时缓冲区。 + """ + _TypePrintDepth(buf, size, ty, pool, 0) + + +# 递归深度上限:悬空/自引用的 LLVMType 树(1b reset 后内存被复用)会导致 +# Ptr(pointee)/Struct(fields)/Func(params) 无限递归,每次递归分配临时缓冲 +# 造成 OOM(单文件暴涨 900MB 根因)。超过 100 层即停止打印。 +TYPEPRINT_MAX_DEPTH: t.CDefine = 100 + +def _TypePrintDepth(buf: str, size: t.CSizeT, + ty: rawptr[LLVMType], pool: rawptr[memhub.MemBuddy], + depth: int): + """带递归深度上限的 TypePrint 内部实现""" + if ty is None or buf is None or size == 0: + return + if depth >= TYPEPRINT_MAX_DEPTH: + viperlib.snprintf(buf, size, "...") + return + buf[0] = '\0' + match ty: + case LLVMType.Int(bits): + viperlib.snprintf(buf, size, "i%d", bits) + case LLVMType.Ptr(pointee): + # ================================================================ + # LLVM IR 不允许 "void*",必须是 "i8*"。 + # 兜底处理: + # pointee == None / Void() / Label() / Metadata() → 统一打印 i8* + # ================================================================ + # 先判断是否 Struct(name) 的快速路径(需先判断 pointee 非None) + if pointee is not None: + match pointee: + case LLVMType.Struct(sfields, sfcount, sname): + if sname is not None: + # 悬空防护:sname 可能指向被 reset 回收/部分复用的 pool 内存。 + # viperlib 的 %s 未实现 precision(%.40s 会全量读 sname), + # 手动 strncpy 限长拷贝后再打印,读侧限制在缓冲内。 + # 类型名含 sha1 前缀可超 40 字符(如 + # "bbdf3bbd4c3bc28c.CONSOLE_SCREEN_BUFFER_INFO"=42), + # 原 40 字节截断会产生未定义类型引用(llc: undefined type)。 + sn_cp: str = pool.alloc(256) + if sn_cp is not None: + string.strncpy(sn_cp, sname, 255) + sn_cp[255] = '\0' + viperlib.snprintf(buf, size, "%%\"%s\"*", sn_cp) + return + case LLVMType.Void(): + # Ptr(Void) → i8*(LLVM IR void* 非法) + viperlib.snprintf(buf, size, "%s", "i8*") + return + case LLVMType.Label(): + viperlib.snprintf(buf, size, "%s", "i8*") + return + case LLVMType.Metadata(): + viperlib.snprintf(buf, size, "%s", "i8*") + return + case _: + pass + # pointee == None 时也兜底 Ptr(Int8) 打印 i8* + if pointee is None: + viperlib.snprintf(buf, size, "%s", "i8*") + return + # 通用路径: 递归打印 pointee 后追加 '*' + _TypePrintDepth(buf, size, pointee, pool, depth + 1) + pos: t.CSizeT = string.strlen(buf) + if pos + 1 < size: + buf[pos] = '*' + buf[pos + 1] = '\0' + case LLVMType.Array(elem_ty, count): + # [N x elem_ty] + viperlib.snprintf(buf, size, "[%d x ", count) + # SHA1 限定类型名(如 %"21a7fcfc665f75ef.NamedTypeNode")可达 ~50 字节, + # 嵌套指针/数组更易超过 128,使用 512 字节避免截断 + tmp: str = pool.alloc(512) + if tmp is not None: + _TypePrintDepth(tmp, 512, elem_ty, pool, depth + 1) + _strappend(buf, size, tmp) + _strappend(buf, size, "]") + case LLVMType.Struct(fields, fcount, name): + if name is not None: + # 命名结构体: %"name" + viperlib.snprintf(buf, size, "%%\"%s\"", name) + else: + # 匿名结构体: {ty1, ty2, ...} + _print_struct_body(buf, size, fields, pool, depth) + case LLVMType.Func(ret, params, count): + # ret (ty1, ty2, ...) + _TypePrintDepth(buf, size, ret, pool, depth + 1) + _strappend(buf, size, " (") + cur2: rawptr[ParamNode] = params + first2: int = 1 + _pc2: int = 0 + # 函数参数类型可能为 SHA1 限定命名结构体指针,512 字节避免截断 + tmp3: str = pool.alloc(512) + while cur2 is not None: + # 悬空保护:参数链若被 reset 回收后内容复用,可能成环无限循环 + _pc2 += 1 + if _pc2 > 200: + _strappend(buf, size, "...)") + break + if first2 == 0: + _strappend(buf, size, ", ") + if tmp3 is not None: + _TypePrintDepth(tmp3, 512, cur2.Ty, pool, depth + 1) + _strappend(buf, size, tmp3) + cur2 = cur2.Next + first2 = 0 + _strappend(buf, size, ")") + case LLVMType.Void(): + viperlib.snprintf(buf, size, "%s", "void") + case LLVMType.Label(): + viperlib.snprintf(buf, size, "%s", "label") + case LLVMType.Metadata(): + viperlib.snprintf(buf, size, "%s", "metadata") + case LLVMType.Float(bits): + if bits == 16: + viperlib.snprintf(buf, size, "%s", "half") + elif bits == 32: + viperlib.snprintf(buf, size, "%s", "float") + elif bits == 64: + viperlib.snprintf(buf, size, "%s", "double") + elif bits == 128: + viperlib.snprintf(buf, size, "%s", "fp128") + else: + viperlib.snprintf(buf, size, "float%x", bits) + case _: + viperlib.snprintf(buf, size, "%s", "void") + + +# ============================================================ +# 内部辅助 +# ============================================================ +def _strappend(dst: str, dst_size: t.CSizeT, src: str): + """将 src 追加到 dst 末尾(不超过 dst_size)""" + if dst is None or src is None: return + dlen: t.CSizeT = string.strlen(dst) + # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, + # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到 GB 外 + if dlen >= dst_size: + return + slen: t.CSizeT = string.strlen(src) + remain: t.CSizeT = dst_size - dlen + if remain <= 0: return + i: t.CSizeT = 0 + while i < slen and i + 1 < remain: + dst[dlen + i] = src[i] + i += 1 + dst[dlen + i] = '\0' + + +# ============================================================ +# _print_struct_body - 打印结构体字段部分 {ty1, ty2, ...} +# +# 供 TypePrint(匿名结构体)和 PrintStructDefinition(命名结构体定义行)复用 +# ============================================================ +def _print_struct_body(buf: str, size: t.CSizeT, + fields: rawptr[ParamNode], pool: rawptr[memhub.MemBuddy], + depth: int = 0, packed: int = 0): + """打印 {ty1, ty2, ...} 到 buf(追加模式,不覆盖已有内容) + + 直接在 buf 当前末尾写入字段类型,避免 pool.alloc 返回与 buf 重叠的内存 + 导致 TypePrint 写入 tmp 时覆盖 buf 已有内容。 + packed=1 时输出 <{ty1, ty2, ...}>(LLVM packed struct,无字段填充)。 + """ + if buf is None or size == 0: + return + if packed != 0: + _strappend(buf, size, "<{") + else: + _strappend(buf, size, "{") + cur: rawptr[ParamNode] = fields + first: int = 1 + fidx: int = 0 + while cur is not None: + # 悬空保护:ParamNode 链若被 reset 回收后内容复用,Next 指针可能成环或 + # 指向垃圾,无限循环。最多打印 200 个字段即停止,防止 OOM。 + if fidx >= 200: + if packed != 0: + _strappend(buf, size, "...}>") + else: + _strappend(buf, size, "...}") + return + if first == 0: + _strappend(buf, size, ", ") + # 直接在 buf 当前末尾位置写入字段类型 + # TypePrint 首字节置 '\0' 是无害的(pos 处本来就是 '\0') + pos: t.CSizeT = string.strlen(buf) + if pos + 2 < size: + _TypePrintDepth(buf + pos, size - pos, cur.Ty, pool, depth + 1) + cur = cur.Next + first = 0 + fidx += 1 + if packed != 0: + _strappend(buf, size, "}>") + else: + _strappend(buf, size, "}") + + +# ============================================================ +# PrintStructDefinition - 打印命名结构体类型定义行 +# +# 输出格式: %"name" = type { ty1, ty2, ... } +# 供 LLVMModulePrint 在全局变量/函数之前输出类型定义 +# ============================================================ +def PrintStructDefinition(buf: str, size: t.CSizeT, + ty: rawptr[LLVMType], pool: rawptr[memhub.MemBuddy], + packed: int = 0): + """打印命名结构体类型定义行 %"name" = type { ty1, ty2, ... } + + 当 fcount == -1(哨兵)时输出 opaque 声明: %"name" = type opaque。 + 空结构体(fcount==0, fields==None,无字段类)是完整定义,输出 "= type {}"。 + packed=1 时输出 %"name" = type <{ ty1, ty2, ... }>(LLVM packed struct)。 + """ + if ty is None or buf is None or size == 0: + return + match ty: + case LLVMType.Struct(fields, fcount, name): + if name is None: + return + # 使用 snprintf 构建 "%%"name" = type" 前缀,避免 _strappend 截断 + # opaque 类型: fcount == -1(哨兵,跨模块前向声明) + if fcount < 0: + viperlib.snprintf(buf, size, "%%\"%s\" = type opaque", name) + else: + viperlib.snprintf(buf, size, "%%\"%s\" = type ", name) + _print_struct_body(buf, size, fields, pool, 0, packed) + case _: + return + + +# ============================================================ +# get_struct_name - 获取命名结构体的名称 +# +# 返回 Name 字段(如 "sha1.ClassName"),非 Struct 类型或匿名 Struct 返回 None +# 供 module_add_named_type 检查是否为命名结构体及去重 +# ============================================================ +def get_struct_name(ty: rawptr[LLVMType]) -> str: + """获取命名结构体的名称,非命名结构体返回 None""" + if ty is None: + return None + match ty: + case LLVMType.Struct(fields, fcount, name): + return name + case _: + return None + + +def get_struct_fields(ty: rawptr[LLVMType]) -> rawptr[ParamNode]: + """获取结构体的字段链表(供跨模块重建 Struct 类型用)""" + if ty is None: + return None + match ty: + case LLVMType.Struct(fields, fcount, name): + return fields + case _: + return None + + +def get_struct_fcount(ty: rawptr[LLVMType]) -> int: + """获取结构体的字段数量(供跨模块重建 Struct 类型用)""" + if ty is None: + return 0 + match ty: + case LLVMType.Struct(fields, fcount, name): + return fcount + case _: + 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) 字段)会导致无限递归, +# 用深度上限防护(超过即返回浅拷贝副本,后续不再递归)。 +# ============================================================ +DEEPCOPY_MAX_DEPTH: t.CDefine = 64 + +def DeepCopyType(pool: rawptr[memhub.MemBuddy], + ty: rawptr[LLVMType]) -> rawptr[LLVMType]: + """深拷贝 LLVMType 到 stdlib 区(深度限制防自引用递归),失败返回 None""" + return _DeepCopyDepth(pool, ty, 0) + + +def _DeepCopyDepth(pool: rawptr[memhub.MemBuddy], + ty: rawptr[LLVMType], depth: int) -> rawptr[LLVMType]: + """带深度上限的深拷贝实现 + + 分配源用传入的 pool 参数(调用方决定):传主 pool 为瞬时拷贝 + (随 mark/reset 回收),传永久 pool(CompilerFactory.get_perm_pool()) + 为跨 mark 持久拷贝(代替 stdlib.malloc,buddy 分配更快)。 + """ + if ty is None: + return None + new_ty: rawptr[LLVMType] = (rawptr[LLVMType])(pool.alloc(LLVMType.__sizeof__())) + if new_ty is None: + return None + string.memset(new_ty, 0, LLVMType.__sizeof__()) + if depth >= DEEPCOPY_MAX_DEPTH: + # 深度超限:浅拷贝 tag 后停止递归(复制当前变体但不深拷子类型) + match ty: + case LLVMType.Int(bits): + c.DerefAs(new_ty, LLVMType.Int(bits)) + case LLVMType.Float(bits): + c.DerefAs(new_ty, LLVMType.Float(bits)) + case LLVMType.Void(): + c.DerefAs(new_ty, LLVMType.Void()) + case LLVMType.Label(): + c.DerefAs(new_ty, LLVMType.Label()) + case LLVMType.Metadata(): + c.DerefAs(new_ty, LLVMType.Metadata()) + case _: + c.DerefAs(new_ty, LLVMType.Void()) + return new_ty + match ty: + case LLVMType.Int(bits): + c.DerefAs(new_ty, LLVMType.Int(bits)) + case LLVMType.Float(bits): + c.DerefAs(new_ty, LLVMType.Float(bits)) + case LLVMType.Void(): + c.DerefAs(new_ty, LLVMType.Void()) + case LLVMType.Label(): + c.DerefAs(new_ty, LLVMType.Label()) + case LLVMType.Metadata(): + c.DerefAs(new_ty, LLVMType.Metadata()) + case LLVMType.Ptr(pointee): + c.DerefAs(new_ty, LLVMType.Ptr(_DeepCopyDepth(pool, pointee, depth + 1))) + case LLVMType.Array(elem_ty, count): + c.DerefAs(new_ty, LLVMType.Array(_DeepCopyDepth(pool, elem_ty, depth + 1), count)) + case LLVMType.Func(ret, params, pcount): + c.DerefAs(new_ty, LLVMType.Func( + _DeepCopyDepth(pool, ret, depth + 1), + _DeepCopyParamChain(pool, params, depth + 1), pcount)) + case LLVMType.Struct(fields, fcount, name): + c.DerefAs(new_ty, LLVMType.Struct( + _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)。 + pool.free(new_ty) + return None + return new_ty + + +def _DeepCopyParamChain(pool: rawptr[memhub.MemBuddy], + head: rawptr[ParamNode], depth: int) -> rawptr[ParamNode]: + """深拷贝 ParamNode 链表到 stdlib""" + if head is None: + return None + first: rawptr[ParamNode] = None + prev: rawptr[ParamNode] = None + cur: rawptr[ParamNode] = head + guard: int = 0 + while cur is not None: + guard += 1 + if guard > 512: + break + node: rawptr[ParamNode] = (rawptr[ParamNode])(pool.alloc(PNODE_SIZE)) + if node is None: + break + string.memset(node, 0, PNODE_SIZE) + node.Ty = _DeepCopyDepth(pool, paramnode_get_ty(cur), depth + 1) + node.Next = None + if first is None: + first = node + if prev is not None: + prev.Next = node + prev = node + cur = paramnode_get_next(cur) + return first + + +def _DeepCopyStr(pool: rawptr[memhub.MemBuddy], s: str) -> str: + """深拷贝字符串到 stdlib""" + if s is None: + return None + slen: t.CSizeT = string.strlen(s) + copy: str = pool.alloc(slen + 1) + if copy is None: + return None + string.strcpy(copy, s) + return copy + + + + diff --git a/llvmlite/__values.vp b/llvmlite/__values.vp index 0e40f0e..7413299 100644 --- a/llvmlite/__values.vp +++ b/llvmlite/__values.vp @@ -1,234 +1,234 @@ -import t, c -from stdint import * -import string -import viperlib -import stdio -import memhub -from linkedlist import GSListNode -from .__types import LLVMType, TypePrint - - -# ============================================================ -# Value: SSA 值表示 -# -# 表示一个 LLVM IR 值:SSA 临时值(%0/%result)或常量字面量(42/0)。 -# 每个值有类型指针、名字、是否常量标志。 -# 继承 GSListNode[Value] 获取强类型 Next: Value|CPtr(值池链表) -# -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 -# ============================================================ -@t.NoVTable -class Value(GSListNode[Value]): - Ty: rowptr[LLVMType] # 值的类型 - Name: str # SSA 名("%0"/"%result")或常量文本("42"/"1.5e+00") - IsConst: int # 1=常量字面量, 0=SSA 临时值 - IntVal: int64 # 整数常量值(当 IsConst=1 时使用) - FloatVal: t.CDouble # 浮点常量值(当 IsConst=1 且 Ty 为 Float 时使用) - - -# ============================================================ -# 工厂函数 -# ============================================================ -# Value 硬编码大小: Next(8) + Ty(8) + Name(8) + IsConst(4) + IntVal(8) + FloatVal(8) = 48 字节 -VALUE_SIZE: t.CDefine = 48 - -def new_value(pool: rowptr[memhub.MemBuddy]) -> rowptr[Value]: - """从 mpool 分配一个零初始化的 Value""" - ptr: rowptr[Value] = pool.alloc(VALUE_SIZE) - if ptr: - string.memset(ptr, 0, VALUE_SIZE) - return ptr - - -def ConstInt(pool: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType], - val: int64, name: str) -> rowptr[Value]: - """创建整数常量值 - - 参数: - ty: 整数类型(Int1/Int8/Int32/Int64) - val: 整数值 - name: 常量文本(如 "42"),由调用方提供 - """ - v: rowptr[Value] = new_value(pool) - if v is None: return None - v.Ty = ty - v.Name = name - v.IsConst = 1 - v.IntVal = val - return v - - -def ConstNull(pool: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType], - name: str) -> rowptr[Value]: - """创建 null 指针常量""" - v: rowptr[Value] = new_value(pool) - if v is None: return None - v.Ty = ty - v.Name = name - v.IsConst = 1 - v.IntVal = 0 - return v - - -def ConstZero(pool: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType]) -> rowptr[Value]: - """创建 zeroinitializer 常量(零初始化聚合类型:结构体/数组)""" - v: rowptr[Value] = new_value(pool) - if v is None: return None - v.Ty = ty - v.Name = "zeroinitializer" - v.IsConst = 1 - v.IntVal = 0 - return v - - -def ConstFloat(pool: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType], - val: t.CDouble) -> rowptr[Value]: - """创建浮点常量值 - - 参数: - ty: 浮点类型(Half/Float/Double/FP128) - val: 浮点数值 - 名字自动格式化为 LLVM IR 科学计数法文本(如 "1.500000e+00")。 - """ - v: rowptr[Value] = new_value(pool) - if v is None: return None - v.Ty = ty - v.IsConst = 1 - v.FloatVal = val - v.IntVal = 0 - # 格式化名字: LLVM IR 浮点常量文本用十六进制位模式(0x%016llx / 0x%08x) - # 原因: %.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。 - f_bits: int = 64 - match ty: - case LLVMType.Float(ftb): - f_bits = ftb - fv_bits: t.CUInt64T = 0 - c.Load(c.Addr(fv_bits), c.Addr(val)) - name_buf: str = pool.alloc(32) - if name_buf is not None: - if f_bits == 32: - stdio.snprintf(name_buf, 32, "0x%08x", t.CUInt32T(fv_bits)) - else: - stdio.snprintf(name_buf, 32, "0x%016llx", fv_bits) - v.Name = name_buf - return v - - -def SSAValue(pool: rowptr[memhub.MemBuddy], ty: rowptr[LLVMType], - name: str) -> rowptr[Value]: - """创建 SSA 临时值 - - 参数: - ty: 值类型 - name: SSA 名(如 "%0"/"%result"),由调用方提供 - """ - v: rowptr[Value] = new_value(pool) - if v is None: return None - v.Ty = ty - v.Name = name - v.IsConst = 0 - v.IntVal = 0 - return v - - -# ============================================================ -# ValuePrint: 将值序列化为 IR 文本 -# -# 示例: -# ConstInt(i32, 42, "42") -> "i32 42" -# SSAValue(i32, "%0") -> "i32 %0" -# ============================================================ -def ValuePrint(buf: str, size: t.CSizeT, - val: rowptr[Value], pool: rowptr[memhub.MemBuddy]): - """将值 val 序列化为 IR 文本写入 buf - - 格式: - pool 用于复杂类型(Array/Struct/Func)递归打印时分配临时缓冲区。 - """ - if val is None or buf is None or size == 0: - return - # 先写类型 - TypePrint(buf, size, val.Ty, pool) - # 追加空格 - pos: t.CSizeT = string.strlen(buf) - if pos + 1 < size: - buf[pos] = ' ' - buf[pos + 1] = '\0' - # 追加名字 - if val.Name is not None: - pos = string.strlen(buf) - _append_str(buf, size, val.Name) - - -# ============================================================ -# Value 字段访问器(供其他模块绕过 stub 类型限制使用) -# -# 跨模块编译时,使用方模块只有 Value 的 stub 类型(字段不足或偏移错误), -# 导致 TransPyC 静默跳过字段访问(不生成 GEP,不报错)。 -# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问所有字段。 -# ============================================================ -def value_get_ty(val: rowptr[Value]) -> rowptr[LLVMType]: - """获取值的类型""" - if val is None: - return None - return val.Ty - - -def value_get_next(val: rowptr[Value]) -> rowptr[Value]: - """获取链表下一个值""" - if val is None: - return None - return val.Next - - -def value_set_ty(val: rowptr[Value], ty: rowptr[LLVMType]): - """设置值的类型""" - if val is None: - return - val.Ty = ty - - -def value_set_next(val: rowptr[Value], nxt: rowptr[Value]): - """设置链表下一个值""" - if val is None: - return - val.Next = nxt - - -def value_set_name(val: rowptr[Value], name: str): - """设置值的名字""" - if val is None: - return - val.Name = name - - -def value_set_isconst(val: rowptr[Value], is_const: int): - """设置是否为常量""" - if val is None: - return - val.IsConst = is_const - - -# ============================================================ -# 内部辅助 -# ============================================================ -def _append_str(dst: str, dst_size: t.CSizeT, src: str): - """将 src 追加到 dst 末尾""" - if dst is None or src is None: return - dlen: t.CSizeT = string.strlen(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 - if dlen >= dst_size: - return - slen: t.CSizeT = string.strlen(src) - remain: t.CSizeT = dst_size - dlen - if remain <= 0: return - i: t.CSizeT = 0 - while i < slen and i + 1 < remain: - dst[dlen + i] = src[i] - i += 1 - dst[dlen + i] = '\0' +import t, c +from stdint import * +import string +import viperlib +import stdio +import memhub +from linkedlist import GSListNode +from .__types import LLVMType, TypePrint + + +# ============================================================ +# Value: SSA 值表示 +# +# 表示一个 LLVM IR 值:SSA 临时值(%0/%result)或常量字面量(42/0)。 +# 每个值有类型指针、名字、是否常量标志。 +# 继承 GSListNode[Value] 获取强类型 Next: Value|CPtr(值池链表) +# +# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 +# ============================================================ +@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: t.CDouble # 浮点常量值(当 IsConst=1 且 Ty 为 Float 时使用) + + +# ============================================================ +# 工厂函数 +# ============================================================ +# Value 硬编码大小: Next(8) + Ty(8) + Name(8) + IsConst(4) + IntVal(8) + FloatVal(8) = 48 字节 +VALUE_SIZE: t.CDefine = 48 + +def new_value(pool: rawptr[memhub.MemBuddy]) -> rawptr[Value]: + """从 mpool 分配一个零初始化的 Value""" + ptr: rawptr[Value] = pool.alloc(VALUE_SIZE) + if ptr: + string.memset(ptr, 0, VALUE_SIZE) + return ptr + + +def ConstInt(pool: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType], + val: int64, name: str) -> rawptr[Value]: + """创建整数常量值 + + 参数: + ty: 整数类型(Int1/Int8/Int32/Int64) + val: 整数值 + name: 常量文本(如 "42"),由调用方提供 + """ + v: rawptr[Value] = new_value(pool) + if v is None: return None + v.Ty = ty + v.Name = name + v.IsConst = 1 + v.IntVal = val + return v + + +def ConstNull(pool: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType], + name: str) -> rawptr[Value]: + """创建 null 指针常量""" + v: rawptr[Value] = new_value(pool) + if v is None: return None + v.Ty = ty + v.Name = name + v.IsConst = 1 + v.IntVal = 0 + return v + + +def ConstZero(pool: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType]) -> rawptr[Value]: + """创建 zeroinitializer 常量(零初始化聚合类型:结构体/数组)""" + v: rawptr[Value] = new_value(pool) + if v is None: return None + v.Ty = ty + v.Name = "zeroinitializer" + v.IsConst = 1 + v.IntVal = 0 + return v + + +def ConstFloat(pool: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType], + val: t.CDouble) -> rawptr[Value]: + """创建浮点常量值 + + 参数: + ty: 浮点类型(Half/Float/Double/FP128) + val: 浮点数值 + 名字自动格式化为 LLVM IR 科学计数法文本(如 "1.500000e+00")。 + """ + v: rawptr[Value] = new_value(pool) + if v is None: return None + v.Ty = ty + v.IsConst = 1 + v.FloatVal = val + v.IntVal = 0 + # 格式化名字: LLVM IR 浮点常量文本用十六进制位模式(0x%016llx / 0x%08x) + # 原因: %.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。 + f_bits: int = 64 + match ty: + case LLVMType.Float(ftb): + f_bits = ftb + fv_bits: t.CUInt64T = 0 + c.Load(c.Addr(fv_bits), c.Addr(val)) + name_buf: str = pool.alloc(32) + if name_buf is not None: + if f_bits == 32: + stdio.snprintf(name_buf, 32, "0x%08x", t.CUInt32T(fv_bits)) + else: + stdio.snprintf(name_buf, 32, "0x%016llx", fv_bits) + v.Name = name_buf + return v + + +def SSAValue(pool: rawptr[memhub.MemBuddy], ty: rawptr[LLVMType], + name: str) -> rawptr[Value]: + """创建 SSA 临时值 + + 参数: + ty: 值类型 + name: SSA 名(如 "%0"/"%result"),由调用方提供 + """ + v: rawptr[Value] = new_value(pool) + if v is None: return None + v.Ty = ty + v.Name = name + v.IsConst = 0 + v.IntVal = 0 + return v + + +# ============================================================ +# ValuePrint: 将值序列化为 IR 文本 +# +# 示例: +# ConstInt(i32, 42, "42") -> "i32 42" +# SSAValue(i32, "%0") -> "i32 %0" +# ============================================================ +def ValuePrint(buf: str, size: t.CSizeT, + val: rawptr[Value], pool: rawptr[memhub.MemBuddy]): + """将值 val 序列化为 IR 文本写入 buf + + 格式: + pool 用于复杂类型(Array/Struct/Func)递归打印时分配临时缓冲区。 + """ + if val is None or buf is None or size == 0: + return + # 先写类型 + TypePrint(buf, size, val.Ty, pool) + # 追加空格 + pos: t.CSizeT = string.strlen(buf) + if pos + 1 < size: + buf[pos] = ' ' + buf[pos + 1] = '\0' + # 追加名字 + if val.Name is not None: + pos = string.strlen(buf) + _append_str(buf, size, val.Name) + + +# ============================================================ +# Value 字段访问器(供其他模块绕过 stub 类型限制使用) +# +# 跨模块编译时,使用方模块只有 Value 的 stub 类型(字段不足或偏移错误), +# 导致 TransPyC 静默跳过字段访问(不生成 GEP,不报错)。 +# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问所有字段。 +# ============================================================ +def value_get_ty(val: rawptr[Value]) -> rawptr[LLVMType]: + """获取值的类型""" + if val is None: + return None + return val.Ty + + +def value_get_next(val: rawptr[Value]) -> rawptr[Value]: + """获取链表下一个值""" + if val is None: + return None + return val.Next + + +def value_set_ty(val: rawptr[Value], ty: rawptr[LLVMType]): + """设置值的类型""" + if val is None: + return + val.Ty = ty + + +def value_set_next(val: rawptr[Value], nxt: rawptr[Value]): + """设置链表下一个值""" + if val is None: + return + val.Next = nxt + + +def value_set_name(val: rawptr[Value], name: str): + """设置值的名字""" + if val is None: + return + val.Name = name + + +def value_set_isconst(val: rawptr[Value], is_const: int): + """设置是否为常量""" + if val is None: + return + val.IsConst = is_const + + +# ============================================================ +# 内部辅助 +# ============================================================ +def _append_str(dst: str, dst_size: t.CSizeT, src: str): + """将 src 追加到 dst 末尾""" + if dst is None or src is None: return + dlen: t.CSizeT = string.strlen(dst) + # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, + # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + if dlen >= dst_size: + return + slen: t.CSizeT = string.strlen(src) + remain: t.CSizeT = dst_size - dlen + if remain <= 0: return + i: t.CSizeT = 0 + while i < slen and i + 1 < remain: + dst[dlen + i] = src[i] + i += 1 + dst[dlen + i] = '\0' diff --git a/llvmlite/__verify.vp b/llvmlite/__verify.vp index bcaa937..66747e1 100644 --- a/llvmlite/__verify.vp +++ b/llvmlite/__verify.vp @@ -1,430 +1,430 @@ -import t, c -from stdint import * -import string -import viperlib -import memhub -from linkedlist import GSListNode, GSList -from .__function import Function, BasicBlock, Line, Param -from .__module import LLVMModule - - -# ============================================================ -# SSA 验证器 -# -# 检查项: -# 1. 块终止: 每个基本块必须以终止指令结尾 (ret/br/switch/unreachable) -# 2. SSA 唯一性: 每个 %name 只能被定义一次 -# 3. 未定义引用: 所有 %name 使用必须有对应定义 -# 4. Phi 位置: phi 指令只能出现在块首(非 phi 指令之前) -# ============================================================ - -# 错误码 -VERIFY_OK: t.CDefine = 0 -VERIFY_ERR_NO_TERMINATOR: t.CDefine = 1 -VERIFY_ERR_DUPLICATE_DEF: t.CDefine = 2 -VERIFY_ERR_UNDEFINED_USE: t.CDefine = 3 -VERIFY_ERR_PHI_NOT_FIRST: t.CDefine = 4 - - -# ============================================================ -# NameEntry: 已定义 SSA 名条目(链表节点) -# -# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 -# ============================================================ -@t.NoVTable -class NameEntry(GSListNode[NameEntry]): - Name: str # SSA 名(如 "%0"/"%result") - - -# ============================================================ -# VerifyResult: 验证结果 -# ============================================================ -class VerifyResult: - ErrorCount: int # 错误总数 - WarningCount: int # 警告总数 - ErrorCode: int # 第一个错误码 - ErrorBuf: str # 错误消息缓冲区 - ErrorBufSize: t.CSizeT # 缓冲区大小 - - -# ============================================================ -# 辅助函数 -# ============================================================ -def _new_verify_result(pool: rowptr[memhub.MemBuddy], - buf_size: t.CSizeT) -> rowptr[VerifyResult]: - """创建验证结果对象""" - ptr: rowptr[VerifyResult] = pool.alloc(VerifyResult.__sizeof__()) - if ptr is None: return None - string.memset(ptr, 0, VerifyResult.__sizeof__()) - ptr.ErrorCount = 0 - ptr.WarningCount = 0 - ptr.ErrorCode = VERIFY_OK - buf: str = pool.alloc(buf_size) - if buf is not None: - buf[0] = '\0' - ptr.ErrorBuf = buf - ptr.ErrorBufSize = buf_size - return ptr - - -def _report_error(result: rowptr[VerifyResult], code: int, msg: str): - """记录一个错误""" - if result is None: return - result.ErrorCount += 1 - if result.ErrorCode == VERIFY_OK: - result.ErrorCode = code - if result.ErrorBuf is not None and msg is not None: - _append_cstr(result.ErrorBuf, result.ErrorBufSize, msg) - _append_cstr(result.ErrorBuf, result.ErrorBufSize, "\n") - - -# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, -# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 -# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 -_g_last_dst: t.CUInt64T = 0 -_g_last_pos: t.CSizeT = 0 - -def _append_cstr(dst: str, dst_size: t.CSizeT, src: str): - """将 src 追加到 dst 末尾""" - global _g_last_dst, _g_last_pos - if dst is None or src is None: return - dlen: t.CSizeT = 0 - if _g_last_dst == t.CUInt64T(dst): - dlen = _g_last_pos - # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 - if dlen > 0 and dst[0] == '\0': - dlen = 0 - # 缓存一致性校验(同 __module/__function 版):缓存 pos 过期会追加到首个 NUL - # 之后使内容不可见,校验失败回退 strlen;正常热路径仍 O(1)。 - if dlen == 0: - if dst[0] != '\0': - dlen = string.strlen(dst) - elif dst[dlen] != '\0' or dst[dlen - 1] == '\0': - dlen = string.strlen(dst) - else: - dlen = string.strlen(dst) - _g_last_dst = t.CUInt64T(dst) - # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, - # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 - if dlen >= dst_size: - return - slen: t.CSizeT = string.strlen(src) - remain: t.CSizeT = dst_size - dlen - if remain <= 0: return - i: t.CSizeT = 0 - while i < slen and i + 1 < remain: - dst[dlen + i] = src[i] - i += 1 - dst[dlen + i] = '\0' - _g_last_pos = dlen + i - - -# ============================================================ -# SSA 名表操作 -# ============================================================ -def _new_name_table(pool: rowptr[memhub.MemBuddy]) -> rowptr[GSList[NameEntry]]: - """创建空名表""" - gsl: rowptr[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 字节 -NENTRY_SIZE: t.CDefine = 16 - -def _name_table_add(table: rowptr[GSList[NameEntry]], - pool: rowptr[memhub.MemBuddy], - name: str) -> int: - """添加名到表。返回 1=重复, 0=成功添加""" - if table is None or name is None: return 0 - # 检查重复 - cur: rowptr[NameEntry] = table.Head - while cur is not None: - if string.strcmp(cur.Name, name) == 0: - return 1 - cur = cur.Next - # 添加新条目 - entry: rowptr[NameEntry] = pool.alloc(NENTRY_SIZE) - if entry is None: return 0 - string.memset(entry, 0, NENTRY_SIZE) - entry.Name = name - table.append(entry) - return 0 - - -def _name_table_contains(table: rowptr[GSList[NameEntry]], - name: str) -> int: - """检查名是否在表中。返回 1=存在, 0=不存在""" - if table is None or name is None: return 0 - cur: rowptr[NameEntry] = table.Head - while cur is not None: - if string.strcmp(cur.Name, name) == 0: - return 1 - cur = cur.Next - return 0 - - -# ============================================================ -# 文本分析辅助 -# ============================================================ -def _is_char_name(ch: t.CChar) -> int: - """判断字符是否为 SSA 名字符 [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 - if ch == '.' or ch == '_': return 1 - return 0 - - -def _extract_def_name(pool: rowptr[memhub.MemBuddy], - text: str) -> str: - """从定义行提取被定义的 SSA 名。 - - 定义行格式: "% = " - 返回分配的 NUL 结尾名串(如 "%0"),或 None 若非定义行。 - """ - if text is None: return None - if text[0] != '%': return None - # 扫描 " = " 模式 - i: int = 1 - while text[i] != '\0': - if text[i] == ' ' and text[i + 1] == '=' and text[i + 2] == ' ': - # 找到 " = " 在位置 i - name_len: int = i - name: str = pool.alloc(name_len + 1) - if name is None: return None - j: int = 0 - while j < name_len: - name[j] = text[j] - j += 1 - name[name_len] = '\0' - return name - i += 1 - return None - - -def _is_terminator(text: str) -> int: - """判断文本是否为终止指令""" - if text is None: return 0 - if string.strncmp(text, "ret", 3) == 0: return 1 - if string.strncmp(text, "br ", 3) == 0: return 1 - if string.strncmp(text, "switch ", 7) == 0: return 1 - if string.strncmp(text, "unreachable", 11) == 0: return 1 - if string.strncmp(text, "indirectbr ", 11) == 0: return 1 - if string.strncmp(text, "resume ", 7) == 0: return 1 - return 0 - - -def _is_phi(text: str) -> int: - """判断文本是否为 phi 指令""" - if text is None: return 0 - if string.strstr(text, " = phi ") is not None: return 1 - return 0 - - -def _check_uses_in_line(table: rowptr[GSList[NameEntry]], - text: str, - def_name: str) -> int: - """检查行中所有 SSA 名使用是否已定义。 - - 跳过 def_name(定义位置的名字)。 - 返回未定义使用数量。 - """ - if text is None: return 0 - i: int = 0 - undef_count: int = 0 - while text[i] != '\0': - if text[i] == '%': - # 提取名 - 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 进行简化检查 - # 这里采用逐字符比较方式 - if _name_matches_at(text, name_start, name_len, table) == 0: - # 检查是否是 def_name(定义本身的位置) - if def_name is not None: - if _str_matches_at(text, name_start, name_len, def_name) == 0: - undef_count += 1 - else: - undef_count += 1 - else: - i += 1 - return undef_count - - -def _name_matches_at(text: str, start: int, length: int, - table: rowptr[GSList[NameEntry]]) -> int: - """检查 text[start:start+length] 是否匹配名表中的某个条目。 - 返回 1=匹配, 0=不匹配 - """ - if table is None: return 0 - cur: rowptr[NameEntry] = table.Head - while cur is not None: - if cur.Name is not None: - if _str_matches_at(text, start, length, cur.Name) == 1: - return 1 - cur = cur.Next - return 0 - - -def _str_matches_at(text: str, start: int, length: int, - target: str) -> int: - """检查 text[start:start+length] 是否等于 target 字符串。 - 返回 1=匹配, 0=不匹配 - """ - if text is None or target is None: return 0 - # 检查 target 长度 - target_len: t.CSizeT = string.strlen(target) - if target_len != length: return 0 - i: t.CSizeT = 0 - while i < length: - if text[start + i] != target[i]: return 0 - i += 1 - return 1 - - -# ============================================================ -# 主验证函数 -# ============================================================ -def verify_function(pool: rowptr[memhub.MemBuddy], - func: rowptr[Function], - result: rowptr[VerifyResult]) -> int: - """验证函数的 SSA 正确性。 - - 参数: - pool: 内存池 - func: 要验证的函数 - result: 验证结果(调用前需用 _new_verify_result 创建) - - 返回: 错误数量(0 = 验证通过) - """ - if func is None or result is None: return 1 - - # 创建名表 - name_table: rowptr[GSList[NameEntry]] = _new_name_table(pool) - if name_table is None: return 1 - - # 1. 将函数参数加入名表(参数是已定义的) - params: rowptr[GSList[Param]] = func.Params - param: rowptr[Param] = None - if params is not None: - param = params.Head - while param is not None: - if param.Name is not None: - _name_table_add(name_table, pool, param.Name) - param = param.Next - - # 1b. 将基本块名加入名表(块标签如 %then 是 br/phi 的合法引用目标) - blocks: rowptr[GSList[BasicBlock]] = func.Blocks - blk_iter: rowptr[BasicBlock] = None - if blocks is not None: - blk_iter = blocks.Head - while blk_iter is not None: - if blk_iter.Name is not None: - blk_name_len: t.CSizeT = string.strlen(blk_iter.Name) - blk_ref: str = pool.alloc(blk_name_len + 2) - if blk_ref is not None: - # 构造 "%" 字符串 - blk_ref[0] = '%' - k: t.CSizeT = 0 - while k < blk_name_len: - blk_ref[k + 1] = blk_iter.Name[k] - k += 1 - blk_ref[blk_name_len + 1] = '\0' - _name_table_add(name_table, pool, blk_ref) - blk_iter = blk_iter.Next - - # 2. 遍历所有块 - blk: rowptr[BasicBlock] = None - if blocks is not None: - blk = blocks.Head - while blk is not None: - # 检查终止指令 - if blk.IsTerminated == 0: - # 进一步检查最后一行是否为终止指令 - has_term: int = 0 - line_check: rowptr[Line] = None - if blk.Lines is not None: - lines_check: rowptr[GSList[Line]] = blk.Lines - line_check = lines_check.Head - while line_check is not None: - if line_check.Buf is not None: - if _is_terminator(line_check.Buf) == 1: - has_term = 1 - line_check = line_check.Next - if has_term == 0: - blk_name: str = "" - if blk.Name is not None: - blk_name = blk.Name - err_buf: str = pool.alloc(128) - if err_buf is not None: - viperlib.snprintf(err_buf, 128, "ERROR: block '%s' missing terminator", blk_name) - _report_error(result, VERIFY_ERR_NO_TERMINATOR, err_buf) - - # 遍历块内指令行 - seen_non_phi: int = 0 - line: rowptr[Line] = None - if blk.Lines is not None: - lines: rowptr[GSList[Line]] = blk.Lines - line = lines.Head - while line is not None: - if line.Buf is not None: - text: str = line.Buf - - # 检查 phi 位置 - if _is_phi(text) == 1: - if seen_non_phi == 1: - phi_err: str = pool.alloc(128) - if phi_err is not None: - viperlib.snprintf(phi_err, 128, "ERROR: phi not at block start") - _report_error(result, VERIFY_ERR_PHI_NOT_FIRST, phi_err) - else: - # 非空行且非 phi 标记为已见非 phi - if string.strlen(text) > 0: - seen_non_phi = 1 - - # 提取定义名 - def_name: str = _extract_def_name(pool, text) - if def_name is not None: - # 检查重复定义 - dup: int = _name_table_add(name_table, pool, def_name) - if dup == 1: - dup_err: str = pool.alloc(128) - if dup_err is not None: - viperlib.snprintf(dup_err, 128, "ERROR: duplicate definition '%s'", def_name) - _report_error(result, VERIFY_ERR_DUPLICATE_DEF, dup_err) - - # 检查未定义使用 - undef: int = _check_uses_in_line(name_table, text, def_name) - if undef > 0: - use_err: str = pool.alloc(128) - if use_err is not None: - viperlib.snprintf(use_err, 128, "ERROR: %d undefined use(s) in: %s", undef, text) - _report_error(result, VERIFY_ERR_UNDEFINED_USE, use_err) - - line = line.Next - - blk = blk.Next - - return result.ErrorCount - - -def verify_module(pool: rowptr[memhub.MemBuddy], mod: rowptr[LLVMModule], result: rowptr[VerifyResult]) -> int: - """验证模块中所有函数的 SSA 正确性。 - - 返回: 错误总数 - """ - if mod is None or result is None: return 1 - func: rowptr[Function] = mod.FuncHead - while func is not None: - if func.IsDeclared == 0: - # 只验证有函数体的定义,跳过声明 - verify_function(pool, func, result) - func = func.Next - return result.ErrorCount +import t, c +from stdint import * +import string +import viperlib +import memhub +from linkedlist import GSListNode, GSList +from .__function import Function, BasicBlock, Line, Param +from .__module import LLVMModule + + +# ============================================================ +# SSA 验证器 +# +# 检查项: +# 1. 块终止: 每个基本块必须以终止指令结尾 (ret/br/switch/unreachable) +# 2. SSA 唯一性: 每个 %name 只能被定义一次 +# 3. 未定义引用: 所有 %name 使用必须有对应定义 +# 4. Phi 位置: phi 指令只能出现在块首(非 phi 指令之前) +# ============================================================ + +# 错误码 +VERIFY_OK: t.CDefine = 0 +VERIFY_ERR_NO_TERMINATOR: t.CDefine = 1 +VERIFY_ERR_DUPLICATE_DEF: t.CDefine = 2 +VERIFY_ERR_UNDEFINED_USE: t.CDefine = 3 +VERIFY_ERR_PHI_NOT_FIRST: t.CDefine = 4 + + +# ============================================================ +# NameEntry: 已定义 SSA 名条目(链表节点) +# +# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。 +# ============================================================ +@t.NoVTable +class NameEntry(GSListNode[NameEntry]): + Name: str # SSA 名(如 "%0"/"%result") + + +# ============================================================ +# VerifyResult: 验证结果 +# ============================================================ +class VerifyResult: + ErrorCount: int # 错误总数 + WarningCount: int # 警告总数 + ErrorCode: int # 第一个错误码 + ErrorBuf: str # 错误消息缓冲区 + ErrorBufSize: t.CSizeT # 缓冲区大小 + + +# ============================================================ +# 辅助函数 +# ============================================================ +def _new_verify_result(pool: rawptr[memhub.MemBuddy], + buf_size: t.CSizeT) -> rawptr[VerifyResult]: + """创建验证结果对象""" + ptr: rawptr[VerifyResult] = pool.alloc(VerifyResult.__sizeof__()) + if ptr is None: return None + string.memset(ptr, 0, VerifyResult.__sizeof__()) + ptr.ErrorCount = 0 + ptr.WarningCount = 0 + ptr.ErrorCode = VERIFY_OK + buf: str = pool.alloc(buf_size) + if buf is not None: + buf[0] = '\0' + ptr.ErrorBuf = buf + ptr.ErrorBufSize = buf_size + return ptr + + +def _report_error(result: rawptr[VerifyResult], code: int, msg: str): + """记录一个错误""" + if result is None: return + result.ErrorCount += 1 + if result.ErrorCode == VERIFY_OK: + result.ErrorCode = code + if result.ErrorBuf is not None and msg is not None: + _append_cstr(result.ErrorBuf, result.ErrorBufSize, msg) + _append_cstr(result.ErrorBuf, result.ErrorBufSize, "\n") + + +# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位, +# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。 +# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。 +_g_last_dst: t.CUInt64T = 0 +_g_last_pos: t.CSizeT = 0 + +def _append_cstr(dst: str, dst_size: t.CSizeT, src: str): + """将 src 追加到 dst 末尾""" + global _g_last_dst, _g_last_pos + if dst is None or src is None: return + dlen: t.CSizeT = 0 + if _g_last_dst == t.CUInt64T(dst): + dlen = _g_last_pos + # 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部 + if dlen > 0 and dst[0] == '\0': + dlen = 0 + # 缓存一致性校验(同 __module/__function 版):缓存 pos 过期会追加到首个 NUL + # 之后使内容不可见,校验失败回退 strlen;正常热路径仍 O(1)。 + if dlen == 0: + if dst[0] != '\0': + dlen = string.strlen(dst) + elif dst[dlen] != '\0' or dst[dlen - 1] == '\0': + dlen = string.strlen(dst) + else: + dlen = string.strlen(dst) + _g_last_dst = t.CUInt64T(dst) + # 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回, + # 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外 + if dlen >= dst_size: + return + slen: t.CSizeT = string.strlen(src) + remain: t.CSizeT = dst_size - dlen + if remain <= 0: return + i: t.CSizeT = 0 + while i < slen and i + 1 < remain: + dst[dlen + i] = src[i] + i += 1 + dst[dlen + i] = '\0' + _g_last_pos = dlen + i + + +# ============================================================ +# SSA 名表操作 +# ============================================================ +def _new_name_table(pool: rawptr[memhub.MemBuddy]) -> rawptr[GSList[NameEntry]]: + """创建空名表""" + 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 字节 +NENTRY_SIZE: t.CDefine = 16 + +def _name_table_add(table: rawptr[GSList[NameEntry]], + pool: rawptr[memhub.MemBuddy], + name: str) -> int: + """添加名到表。返回 1=重复, 0=成功添加""" + if table is None or name is None: return 0 + # 检查重复 + cur: rawptr[NameEntry] = table.Head + while cur is not None: + if string.strcmp(cur.Name, name) == 0: + return 1 + cur = cur.Next + # 添加新条目 + entry: rawptr[NameEntry] = pool.alloc(NENTRY_SIZE) + if entry is None: return 0 + string.memset(entry, 0, NENTRY_SIZE) + entry.Name = name + table.append(entry) + return 0 + + +def _name_table_contains(table: rawptr[GSList[NameEntry]], + name: str) -> int: + """检查名是否在表中。返回 1=存在, 0=不存在""" + if table is None or name is None: return 0 + cur: rawptr[NameEntry] = table.Head + while cur is not None: + if string.strcmp(cur.Name, name) == 0: + return 1 + cur = cur.Next + return 0 + + +# ============================================================ +# 文本分析辅助 +# ============================================================ +def _is_char_name(ch: t.CChar) -> int: + """判断字符是否为 SSA 名字符 [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 + if ch == '.' or ch == '_': return 1 + return 0 + + +def _extract_def_name(pool: rawptr[memhub.MemBuddy], + text: str) -> str: + """从定义行提取被定义的 SSA 名。 + + 定义行格式: "% = " + 返回分配的 NUL 结尾名串(如 "%0"),或 None 若非定义行。 + """ + if text is None: return None + if text[0] != '%': return None + # 扫描 " = " 模式 + i: int = 1 + while text[i] != '\0': + if text[i] == ' ' and text[i + 1] == '=' and text[i + 2] == ' ': + # 找到 " = " 在位置 i + name_len: int = i + name: str = pool.alloc(name_len + 1) + if name is None: return None + j: int = 0 + while j < name_len: + name[j] = text[j] + j += 1 + name[name_len] = '\0' + return name + i += 1 + return None + + +def _is_terminator(text: str) -> int: + """判断文本是否为终止指令""" + if text is None: return 0 + if string.strncmp(text, "ret", 3) == 0: return 1 + if string.strncmp(text, "br ", 3) == 0: return 1 + if string.strncmp(text, "switch ", 7) == 0: return 1 + if string.strncmp(text, "unreachable", 11) == 0: return 1 + if string.strncmp(text, "indirectbr ", 11) == 0: return 1 + if string.strncmp(text, "resume ", 7) == 0: return 1 + return 0 + + +def _is_phi(text: str) -> int: + """判断文本是否为 phi 指令""" + if text is None: return 0 + if string.strstr(text, " = phi ") is not None: return 1 + return 0 + + +def _check_uses_in_line(table: rawptr[GSList[NameEntry]], + text: str, + def_name: str) -> int: + """检查行中所有 SSA 名使用是否已定义。 + + 跳过 def_name(定义位置的名字)。 + 返回未定义使用数量。 + """ + if text is None: return 0 + i: int = 0 + undef_count: int = 0 + while text[i] != '\0': + if text[i] == '%': + # 提取名 + 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 进行简化检查 + # 这里采用逐字符比较方式 + if _name_matches_at(text, name_start, name_len, table) == 0: + # 检查是否是 def_name(定义本身的位置) + if def_name is not None: + if _str_matches_at(text, name_start, name_len, def_name) == 0: + undef_count += 1 + else: + undef_count += 1 + else: + i += 1 + return undef_count + + +def _name_matches_at(text: str, start: int, length: int, + table: rawptr[GSList[NameEntry]]) -> int: + """检查 text[start:start+length] 是否匹配名表中的某个条目。 + 返回 1=匹配, 0=不匹配 + """ + if table is None: return 0 + cur: rawptr[NameEntry] = table.Head + while cur is not None: + if cur.Name is not None: + if _str_matches_at(text, start, length, cur.Name) == 1: + return 1 + cur = cur.Next + return 0 + + +def _str_matches_at(text: str, start: int, length: int, + target: str) -> int: + """检查 text[start:start+length] 是否等于 target 字符串。 + 返回 1=匹配, 0=不匹配 + """ + if text is None or target is None: return 0 + # 检查 target 长度 + target_len: t.CSizeT = string.strlen(target) + if target_len != length: return 0 + i: t.CSizeT = 0 + while i < length: + if text[start + i] != target[i]: return 0 + i += 1 + return 1 + + +# ============================================================ +# 主验证函数 +# ============================================================ +def verify_function(pool: rawptr[memhub.MemBuddy], + func: rawptr[Function], + result: rawptr[VerifyResult]) -> int: + """验证函数的 SSA 正确性。 + + 参数: + pool: 内存池 + func: 要验证的函数 + result: 验证结果(调用前需用 _new_verify_result 创建) + + 返回: 错误数量(0 = 验证通过) + """ + if func is None or result is None: return 1 + + # 创建名表 + name_table: rawptr[GSList[NameEntry]] = _new_name_table(pool) + if name_table is None: return 1 + + # 1. 将函数参数加入名表(参数是已定义的) + params: rawptr[GSList[Param]] = func.Params + param: rawptr[Param] = None + if params is not None: + param = params.Head + while param is not None: + if param.Name is not None: + _name_table_add(name_table, pool, param.Name) + param = param.Next + + # 1b. 将基本块名加入名表(块标签如 %then 是 br/phi 的合法引用目标) + blocks: rawptr[GSList[BasicBlock]] = func.Blocks + blk_iter: rawptr[BasicBlock] = None + if blocks is not None: + blk_iter = blocks.Head + while blk_iter is not None: + if blk_iter.Name is not None: + blk_name_len: t.CSizeT = string.strlen(blk_iter.Name) + blk_ref: str = pool.alloc(blk_name_len + 2) + if blk_ref is not None: + # 构造 "%" 字符串 + blk_ref[0] = '%' + k: t.CSizeT = 0 + while k < blk_name_len: + blk_ref[k + 1] = blk_iter.Name[k] + k += 1 + blk_ref[blk_name_len + 1] = '\0' + _name_table_add(name_table, pool, blk_ref) + blk_iter = blk_iter.Next + + # 2. 遍历所有块 + blk: rawptr[BasicBlock] = None + if blocks is not None: + blk = blocks.Head + while blk is not None: + # 检查终止指令 + if blk.IsTerminated == 0: + # 进一步检查最后一行是否为终止指令 + has_term: int = 0 + line_check: rawptr[Line] = None + if blk.Lines is not None: + lines_check: rawptr[GSList[Line]] = blk.Lines + line_check = lines_check.Head + while line_check is not None: + if line_check.Buf is not None: + if _is_terminator(line_check.Buf) == 1: + has_term = 1 + line_check = line_check.Next + if has_term == 0: + blk_name: str = "" + if blk.Name is not None: + blk_name = blk.Name + err_buf: str = pool.alloc(128) + if err_buf is not None: + viperlib.snprintf(err_buf, 128, "ERROR: block '%s' missing terminator", blk_name) + _report_error(result, VERIFY_ERR_NO_TERMINATOR, err_buf) + + # 遍历块内指令行 + seen_non_phi: int = 0 + line: rawptr[Line] = None + if blk.Lines is not None: + lines: rawptr[GSList[Line]] = blk.Lines + line = lines.Head + while line is not None: + if line.Buf is not None: + text: str = line.Buf + + # 检查 phi 位置 + if _is_phi(text) == 1: + if seen_non_phi == 1: + phi_err: str = pool.alloc(128) + if phi_err is not None: + viperlib.snprintf(phi_err, 128, "ERROR: phi not at block start") + _report_error(result, VERIFY_ERR_PHI_NOT_FIRST, phi_err) + else: + # 非空行且非 phi 标记为已见非 phi + if string.strlen(text) > 0: + seen_non_phi = 1 + + # 提取定义名 + def_name: str = _extract_def_name(pool, text) + if def_name is not None: + # 检查重复定义 + dup: int = _name_table_add(name_table, pool, def_name) + if dup == 1: + dup_err: str = pool.alloc(128) + if dup_err is not None: + viperlib.snprintf(dup_err, 128, "ERROR: duplicate definition '%s'", def_name) + _report_error(result, VERIFY_ERR_DUPLICATE_DEF, dup_err) + + # 检查未定义使用 + undef: int = _check_uses_in_line(name_table, text, def_name) + if undef > 0: + use_err: str = pool.alloc(128) + if use_err is not None: + viperlib.snprintf(use_err, 128, "ERROR: %d undefined use(s) in: %s", undef, text) + _report_error(result, VERIFY_ERR_UNDEFINED_USE, use_err) + + line = line.Next + + blk = blk.Next + + return result.ErrorCount + + +def verify_module(pool: rawptr[memhub.MemBuddy], mod: rawptr[LLVMModule], result: rawptr[VerifyResult]) -> int: + """验证模块中所有函数的 SSA 正确性。 + + 返回: 错误总数 + """ + 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_function(pool, func, result) + func = func.Next + return result.ErrorCount diff --git a/memhub.vp b/memhub.vp index 67a9e59..f21d55f 100644 --- a/memhub.vp +++ b/memhub.vp @@ -187,18 +187,18 @@ class MemManager: def calloc(self, count: t.CSizeT, size: t.CSizeT) -> t.CPtr: total: t.CSizeT = count * size - ptr: t.CVoid | t.CPtr = self.alloc(total) + ptr: t.CPtr = self.alloc(total) if ptr is not None: string.memset(ptr, 0, total) return ptr - def realloc(self, ptr: t.CVoid | t.CPtr, new_size: t.CSizeT) -> t.CVoid | t.CPtr: + def realloc(self, ptr: t.CPtr, new_size: t.CSizeT) -> t.CPtr: if ptr is None: return self.alloc(new_size) if new_size == 0: self.free(ptr) return None - new_ptr: t.CVoid | t.CPtr = self.alloc(new_size) + new_ptr: t.CPtr = self.alloc(new_size) if new_ptr is None: return ptr # Base class cannot track original allocation size; data will not be copied. Subclasses may override to preserve data @@ -215,7 +215,7 @@ class MemManager: buf: viperio.Buf | t.CPtr = self.alloc(viperio.Buf.__sizeof__()) if buf is None: return None - data_ptr: t.CVoid | t.CPtr = self.alloc(capacity) + data_ptr: t.CPtr = self.alloc(capacity) if data_ptr is None: return None buf.__before_init__() @@ -231,13 +231,13 @@ class MemPool(MemManager): offset: t.CSizeT # Bump pointer offset high_water: t.CSizeT # High-water mark - def __init__(self, base: t.CVoid | t.CPtr, size: t.CSizeT): + def __init__(self, base: t.CPtr, size: t.CSizeT): self.base = base self.size = size self.offset = 0 self.high_water = 0 - def alloc(self, size: t.CSizeT) -> t.CVoid | t.CPtr: + def alloc(self, size: t.CSizeT) -> t.CPtr: if size == 0: return None aligned: t.CSizeT = _align_up(size, MEMHUB_ALIGN) if self.offset + aligned > self.size: @@ -247,13 +247,13 @@ class MemPool(MemManager): self.size, self.offset) stdio.printf("[OOM] 说明: 内存池已耗尽。可能原因: (1) 池大小不足,需增大 POOL_SIZE; (2) 存在内存泄漏。\n") stdlib.exit(1) - ptr: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(self.base) + self.offset, t.CPtr) + ptr: t.CPtr = t.CPtr(t.CUInt64T(self.base) + self.offset) self.offset += aligned if self.offset > self.high_water: self.high_water = self.offset return ptr - def free(self, ptr: t.CVoid | t.CPtr) -> t.CInt: + def free(self, ptr: t.CPtr) -> t.CInt: # Bump allocator does not support individual free operations return 1 @@ -271,13 +271,13 @@ class MemSlab(MemManager): block_size: t.CSizeT # Block size (aligned) block_count: t.CSizeT # Total block count used_count: t.CSizeT # Used block count - free_list: t.CVoid | t.CPtr # Free list head + free_list: t.CPtr # Free list head alloc_map: t.CUInt8T | t.CPtr # Allocation bitmap (at start of arena) alloc_map_size: t.CSizeT # Bitmap byte count - usable: t.CVoid | t.CPtr # Usable block area start (after bitmap) + usable: t.CPtr # Usable block area start (after bitmap) usable_size: t.CSizeT # Usable block area size - def __init__(self, base: t.CVoid | t.CPtr, size: t.CSizeT, block_size: t.CSizeT): + def __init__(self, base: t.CPtr, size: t.CSizeT, block_size: t.CSizeT): self.base = base self.size = size self.block_size = 0 @@ -299,7 +299,7 @@ class MemSlab(MemManager): self.alloc_map = base self.alloc_map_size = map_bytes - self.usable = t.CVoid(t.CUInt64T(base) + map_bytes, t.CPtr) + self.usable = t.CPtr(t.CUInt64T(base) + map_bytes) self.usable_size = size - map_bytes self.block_size = bs @@ -316,12 +316,12 @@ class MemSlab(MemManager): self.free_list = None i: t.CSizeT = 0 while i < self.block_count: - block: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(self.usable) + i * bs, t.CPtr) + block: t.CPtr = t.CPtr(t.CUInt64T(self.usable) + i * bs) c.DerefAs(block, self.free_list) self.free_list = block i += 1 - def alloc(self, size: t.CSizeT) -> t.CVoid | t.CPtr: + def alloc(self, size: t.CSizeT) -> t.CPtr: # Slab mode: allocate one block only if requested size <= block_size if self.free_list is None: stdio.printf("\n[OOM] 内存不足 (Out of Memory)\n") @@ -336,14 +336,14 @@ class MemSlab(MemManager): stdio.printf("[WARN] MemSlab: 请求 %zu 字节超过 block_size=%zu,分配失败返回 NULL;应改用 MemBuddy/MemPool。\n", size, self.block_size) return None - block: t.CVoid | t.CPtr = self.free_list - self.free_list = t.CVoid(c.Deref(t.CUInt64T(block, t.CPtr)), t.CPtr) + block: t.CPtr = self.free_list + self.free_list = t.CPtr(c.Deref(t.CUInt64T(block))) self.used_count += 1 idx: t.CSizeT = (t.CUInt64T(block) - t.CUInt64T(self.usable)) / self.block_size self.alloc_map[idx / 8] = self.alloc_map[idx / 8] | t.CUInt8T(1 << (idx % 8)) return block - def free(self, ptr: t.CVoid | t.CPtr) -> t.CInt: + def free(self, ptr: t.CPtr) -> t.CInt: if ptr is None: return 0 p: t.CUInt64T = t.CUInt64T(ptr) if p < t.CUInt64T(self.usable): return 0 @@ -364,7 +364,7 @@ class MemSlab(MemManager): self.free_list = None i: t.CSizeT = 0 while i < self.block_count: - block: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(self.usable) + i * self.block_size, t.CPtr) + block: t.CPtr = t.CPtr(t.CUInt64T(self.usable) + i * self.block_size) c.DerefAs(block, self.free_list) self.free_list = block if i / 8 < self.alloc_map_size: @@ -393,12 +393,12 @@ class MemBuddy(MemManager): max_order: t.CInt # Maximum order free_lists: t.CUInt64T | t.CPtr # Free list head array (located at arena start) lock_val: t.CVolatile | t.CInt # Spinlock - usable: t.CVoid | t.CPtr # Start of usable memory region (skips free_lists) + usable: t.CPtr # Start of usable memory region (skips free_lists) usable_size: t.CSizeT # Size of usable memory (power of two) chk_stack: t.CUInt64T | t.CPtr # Checkpoint 分配记录栈(存 block 地址) chk_cap: t.CSizeT # 记录栈容量(条目数) chk_depth: t.CSizeT # 当前记录深度 - chk_base: t.CVoid | t.CPtr # 记录栈起始地址(由 stdlib.malloc 分配,独立于 buddy 区) + chk_base: t.CPtr # 记录栈起始地址(由 stdlib.malloc 分配,独立于 buddy 区) big_alloc_count: t.CSizeT # ≥8KB 大分配计数(OOM 诊断:定位大缓冲反复分配) # [DET] 分配检测(默认关,排查越界写/存活块覆盖时开启): # detection=1 → alloc 前后 2 字节 0xCDCD 标记 + 记录表 + 每次 alloc 后探测 @@ -409,12 +409,12 @@ class MemBuddy(MemManager): # 默认 0 = 与历史实现完全一致(范式同上方 detection 字段)。 deep_detect: t.CInt = 0 detection_size: t.CSizeT = MEMBUDDY_DET_RECORD_SIZE - _det_arena: t.CVoid | t.CPtr = None # 记录区基址(stdlib.malloc,供 realloc 扩容) + _det_arena: t.CPtr = None # 记录区基址(stdlib.malloc,供 realloc 扩容) _det_records: t.CUInt64T | t.CPtr = None # 记录表(每 2×u64 = 1 条:{ptr, size}) _det_count: t.CSizeT = 0 # 已记录条数 _det_cap: t.CSizeT = 0 # 记录容量(条) - def __init__(self, base: t.CVoid | t.CPtr, size: t.CSizeT, + def __init__(self, base: t.CPtr, size: t.CSizeT, detection: t.CInt = 0, deep_detect: t.CInt = 0): self.base = base self.size = size @@ -473,7 +473,7 @@ class MemBuddy(MemManager): self.max_order = 0 return - self.usable = t.CVoid(t.CUInt64T(base) + fl_bytes, t.CPtr) + self.usable = t.CPtr(t.CUInt64T(base) + fl_bytes) # 计算 max_order: log2(max_blk / MIN_BLOCK) self.max_order = 0 @@ -495,7 +495,7 @@ class MemBuddy(MemManager): while ob < blk: ob = ob << 1 o += 1 - self._fl_push(o, t.CVoid(t.CUInt64T(self.usable) + pos, t.CPtr)) + self._fl_push(o, t.CPtr(t.CUInt64T(self.usable) + pos)) pos += blk used_area += blk rem -= blk @@ -506,11 +506,10 @@ class MemBuddy(MemManager): # 2026-09-04:记录+重叠检测从 detection 解耦——非 detection 也分配 # 记录区并启用同地址重复分配检测(不做前后标记、不改 alloc 布局), # 定位 free-list/重复分配类损坏(memhub 翻译早期容器 Count 槽被写坏)。 - det_arena: t.CVoid | t.CPtr = t.CVoid( - stdlib.malloc(self.detection_size), t.CPtr) + det_arena: t.CPtr = t.CPtr(stdlib.malloc(self.detection_size)) if det_arena is not None: self._det_arena = det_arena - self._det_records = (t.CUInt64T | t.CPtr)(t.CVoid(det_arena, t.CPtr)) + self._det_records = (t.CUInt64T | t.CPtr)(det_arena) self._det_cap = self.detection_size // 16 self._det_count = 0 @@ -528,11 +527,11 @@ class MemBuddy(MemManager): """ if self.chk_stack is None: cap: t.CSizeT = 2 * 1024 * 1024 - base: t.CVoid | t.CPtr = t.CVoid(stdlib.malloc(cap * 8), t.CPtr) + base: t.CPtr = t.CPtr(stdlib.malloc(cap * 8)) if base is None: return 0 self.chk_base = base - self.chk_stack = (t.CUInt64T | t.CPtr)(t.CVoid(base, t.CPtr)) + self.chk_stack = (t.CUInt64T | t.CPtr)(base) self.chk_cap = cap self.chk_depth = 0 return self.chk_depth @@ -557,10 +556,10 @@ class MemBuddy(MemManager): self._lock() while self.chk_depth > mark: self.chk_depth -= 1 - block: t.CVoid | t.CPtr = t.CVoid(self.chk_stack[self.chk_depth], t.CPtr) + block: t.CPtr = t.CPtr(self.chk_stack[self.chk_depth]) if block is None: continue - stored: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(block, t.CPtr)), t.CPtr) + stored: t.CPtr = t.CPtr(c.Deref(t.CUInt64T(block, t.CPtr))) stored_val: t.CUInt64T = t.CUInt64T(stored) if (stored_val & 1) == 0: continue # 已被单独 free,跳过 @@ -587,50 +586,50 @@ class MemBuddy(MemManager): # === Free list operations === - def _fl_push(self, order: t.CInt, block: t.CVoid | t.CPtr): + def _fl_push(self, order: t.CInt, block: t.CPtr): old_head: t.CUInt64T = self.free_lists[order] - c.DerefAs(block, t.CVoid(old_head, t.CPtr)) + c.DerefAs(block, t.CPtr(old_head)) self.free_lists[order] = t.CUInt64T(block) - def _fl_pop(self, order: t.CInt) -> t.CVoid | t.CPtr: + def _fl_pop(self, order: t.CInt) -> t.CPtr: head_val: t.CUInt64T = self.free_lists[order] if head_val == 0: return None - block: t.CVoid | t.CPtr = t.CVoid(head_val, t.CPtr) - next_ptr: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(block, t.CPtr)), t.CPtr) + block: t.CPtr = t.CPtr(head_val) + next_ptr: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(block))) self.free_lists[order] = t.CUInt64T(next_ptr) return block - def _fl_find_and_remove(self, order: t.CInt, target: t.CVoid | t.CPtr) -> t.CInt: + def _fl_find_and_remove(self, order: t.CInt, target: t.CPtr) -> t.CInt: head_val: t.CUInt64T = self.free_lists[order] if head_val == 0: return 0 - head: t.CVoid | t.CPtr = t.CVoid(head_val, t.CPtr) + head: t.CPtr = t.CPtr(head_val) if t.CUInt64T(head) == t.CUInt64T(target): - next_ptr: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(head, t.CPtr)), t.CPtr) + next_ptr: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(head))) self.free_lists[order] = t.CUInt64T(next_ptr) return 1 - prev: t.CVoid | t.CPtr = head - cur: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(head, t.CPtr)), t.CPtr) + prev: t.CPtr = head + cur: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(head))) while t.CUInt64T(cur) != 0: if t.CUInt64T(cur) == t.CUInt64T(target): - next_ptr: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(cur, t.CPtr)), t.CPtr) + next_ptr: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(cur))) c.DerefAs(prev, next_ptr) return 1 prev = cur - cur = t.CVoid(c.Deref(t.CUInt64T(cur, t.CPtr)), t.CPtr) + cur = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(cur))) return 0 # === Buddy system core === - def _buddy_of(self, block: t.CVoid | t.CPtr, order: t.CInt) -> t.CVoid | t.CPtr: + def _buddy_of(self, block: t.CPtr, order: t.CInt) -> t.CPtr: offset: t.CSizeT = t.CUInt64T(block) - t.CUInt64T(self.usable) # order 恒 ≥ 0(调用链:_coalesce 的 order 源自合法 header (stored>>1) 且只递增, # 起点为已释放块的阶)。内联左移等价于 _block_size_at_order(order),省去每次 # 调用的 call/ret(_buddy_of 实测 3517 万次,为 _block_size_at_order 主要来源)。 bs: t.CSizeT = MEMBUDDY_MIN_BLOCK << t.CSizeT(order) buddy_offset: t.CSizeT = offset ^ bs - return t.CVoid(t.CUInt64T(self.usable) + buddy_offset, t.CPtr) + return t.CPtr(t.CUInt64T(self.usable) + buddy_offset) def _order_for_size(self, size: t.CSizeT) -> t.CInt: order: t.CInt = 0 @@ -640,7 +639,7 @@ class MemBuddy(MemManager): order += 1 return order - def _split_to_order(self, to_order: t.CInt) -> t.CVoid | t.CPtr: + def _split_to_order(self, to_order: t.CInt) -> t.CPtr: found_order: t.CInt = to_order while found_order <= self.max_order: if self.free_lists[found_order] != 0: @@ -648,17 +647,17 @@ class MemBuddy(MemManager): found_order += 1 if found_order > self.max_order: return None - block: t.CVoid | t.CPtr = self._fl_pop(found_order) + block: t.CPtr = self._fl_pop(found_order) while found_order > to_order: found_order -= 1 # 循环条件保证 found_order > to_order ≥ 0,故递减后 found_order ≥ 0; # 内联左移等价于 _block_size_at_order(found_order),省去每次拆分的 call/ret。 bs: t.CSizeT = MEMBUDDY_MIN_BLOCK << t.CSizeT(found_order) - buddy: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(block) + bs, t.CPtr) + buddy: t.CPtr = t.CPtr(t.CUInt64T(block) + bs) self._fl_push(found_order, buddy) return block - def _coalesce(self, block: t.CVoid | t.CPtr, order: t.CInt): + def _coalesce(self, block: t.CPtr, order: t.CInt): while order < self.max_order: # 提前退出(不改变语义,仅省去"注定找不到"的两次调用):本阶空闲链表 # 为空 ⇒ _fl_find_and_remove 入口即判 head==0 返回 0 ⇒ 本阶不可能合并。 @@ -667,7 +666,7 @@ class MemBuddy(MemManager): # 逐字相同,且两函数均无副作用,故对空闲链表语义与顺序完全等价。 if self.free_lists[order] == 0: break - buddy: t.CVoid | t.CPtr = self._buddy_of(block, order) + buddy: t.CPtr = self._buddy_of(block, order) found: t.CInt = self._fl_find_and_remove(order, buddy) if found == 0: break @@ -676,15 +675,15 @@ class MemBuddy(MemManager): order += 1 self._fl_push(order, block) - def _is_valid_ptr(self, ptr: t.CVoid | t.CPtr) -> t.CInt: + def _is_valid_ptr(self, ptr: t.CPtr) -> t.CInt: if ptr is None: return 0 if self.usable is None: return 0 - block: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(ptr) - MEMBUDDY_HEADER_SIZE, t.CPtr) + block: t.CPtr = t.CPtr(t.CUInt64T(ptr) - MEMBUDDY_HEADER_SIZE) if t.CUInt64T(block) < t.CUInt64T(self.usable): return 0 if t.CUInt64T(block) >= t.CUInt64T(self.usable) + self.usable_size: return 0 offset: t.CSizeT = t.CUInt64T(block) - t.CUInt64T(self.usable) if offset % MEMBUDDY_MIN_BLOCK != 0: return 0 - stored: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(block, t.CPtr)), t.CPtr) + stored: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(block))) stored_val: t.CUInt64T = t.CUInt64T(stored) if (stored_val & 1) == 0: return 0 order: t.CInt = t.CInt(stored_val >> 1) @@ -711,7 +710,6 @@ class MemBuddy(MemManager): return 1 # === Spinlock === - def _lock(self): # can use the spinlock library for decoupling # 单线程编译(MEMBUDDY_THREAD_SAFE=0)下本 if 被常量折叠,函数体为空; # 多线程(=1)下保留原有原子自旋加锁,行为与历史实现逐字节一致。 @@ -724,10 +722,9 @@ class MemBuddy(MemManager): atom.__atomic_clear(c.Addr(self.lock_val), atom.ATOMIC_RELEASE) # === Virtual function overrides === - - def alloc(self, size: t.CSizeT) -> t.CVoid | t.CPtr: + def alloc(self, size: t.CSizeT) -> t.CPtr: self._lock() - result: t.CVoid | t.CPtr = None + result: t.CPtr = None if size != 0 and self.usable is not None: # +MEMBUDDY_CANARY:预留块尾 8 字节哨兵(用户数据区之外),越界写定位用 needed: t.CSizeT = size + MEMBUDDY_HEADER_SIZE + MEMBUDDY_CANARY @@ -736,16 +733,16 @@ class MemBuddy(MemManager): needed = needed + 4 order: t.CInt = self._order_for_size(needed) if order <= self.max_order: - block: t.CVoid | t.CPtr = self._split_to_order(order) + block: t.CPtr = self._split_to_order(order) if block is not None: - c.DerefAs(block, t.CVoid(t.CUInt64T((order << 1) | 1), t.CPtr)) - result = t.CVoid(t.CUInt64T(block) + MEMBUDDY_HEADER_SIZE, t.CPtr) + c.DerefAs(block, t.CPtr(t.CUInt64T((order << 1) | 1))) + result = t.CPtr(t.CUInt64T(block) + MEMBUDDY_HEADER_SIZE) # 写块尾 canary(block + block_size - CANARY) # order 恒 ≥ 0(_order_for_size 返回值),内联左移等价于 # _block_size_at_order(order),省去每次 alloc 的 call/ret。 bs: t.CSizeT = MEMBUDDY_MIN_BLOCK << t.CSizeT(order) - c.DerefAs(t.CVoid(t.CUInt64T(block) + bs - MEMBUDDY_CANARY, t.CPtr), - t.CVoid(MEMBUDDY_CANARY_PATTERN, t.CPtr)) + c.DerefAs(t.CPtr(t.CUInt64T(block) + bs - MEMBUDDY_CANARY), + t.CPtr(MEMBUDDY_CANARY_PATTERN)) # 分配守卫:填充用户数据区 0xCD 哨兵(未初始化检测)。 # 调用方初始化(list.__init__ 等)覆盖前,字段保持 0xCDCD... # CTORDBG 打印 d0-d40 时:0xCDCD...=未初始化,其他=被覆盖。 @@ -759,11 +756,10 @@ class MemBuddy(MemManager): if self.chk_stack is not None: if self.chk_depth >= self.chk_cap: new_cap: t.CSizeT = self.chk_cap * 2 - new_stack: t.CVoid | t.CPtr = t.CVoid( - stdlib.realloc(self.chk_base, new_cap * 8), t.CPtr) + new_stack: t.CPtr = t.CPtr(stdlib.realloc(self.chk_base, new_cap * 8)) if new_stack is not None: self.chk_base = new_stack - self.chk_stack = (t.CUInt64T | t.CPtr)(t.CVoid(new_stack, t.CPtr)) + self.chk_stack = (t.CUInt64T | t.CPtr)(t.CPtr(new_stack)) self.chk_cap = new_cap else: # realloc 失败:块不再记录 → reset_to 无法回收该块 → @@ -782,10 +778,10 @@ class MemBuddy(MemManager): # [DET] 布局 [0xCDCD][请求大小内存][0xCDCD]:返回指针 = 用户区 + 2 # (前标记之后);记录 {返回 ptr, size} 到独立记录区;随后探测全部 # 存活块的后标记——被破坏 = 越界写,立即汇报并终止。 - m_f_d: t.CUInt16T | t.CPtr = (t.CUInt16T | t.CPtr)(t.CVoid(base_ret_d, t.CPtr)) + m_f_d: t.CUInt16T | t.CPtr = (t.CUInt16T | t.CPtr)(t.CPtr(base_ret_d)) m_f_d[0] = t.CUInt16T(MEMBUDDY_DET_MARKER) tail_d: t.CUInt64T = base_ret_d + 2 + t.CUInt64T(size) - m_t_d: t.CUInt16T | t.CPtr = (t.CUInt16T | t.CPtr)(t.CVoid(tail_d, t.CPtr)) + m_t_d: t.CUInt16T | t.CPtr = (t.CUInt16T | t.CPtr)(t.CPtr(tail_d)) m_t_d[0] = t.CUInt16T(MEMBUDDY_DET_MARKER) rec_ptr_d = base_ret_d + 2 # 采样:每 MEMBUDDY_DET_PROBE_PERIOD 次 alloc 做一次全表扫描 @@ -818,10 +814,10 @@ class MemBuddy(MemManager): # [MEMDBG] 新出块是否仍残留于同 order free list(此前的重复 put # 使链表含两条本块 → pop 后还有残条 → 本块将被二次分配,双向写穿)。 # 记录表重叠检测要等下次 pop 同地址才撞见,这里在出块当场提前拦截。 - dbg_block: t.CVoid | t.CPtr = t.CVoid(base_ret_d - MEMBUDDY_HEADER_SIZE, t.CPtr) - dbg_hdr: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(dbg_block, t.CPtr)), t.CPtr) + dbg_block: t.CPtr = t.CPtr(base_ret_d - MEMBUDDY_HEADER_SIZE) + dbg_hdr: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(dbg_block))) dbg_order: t.CInt = t.CInt(t.CUInt64T(dbg_hdr) >> 1) - dbg_cur: t.CVoid | t.CPtr = t.CVoid(self.free_lists[dbg_order], t.CPtr) + dbg_cur: t.CPtr = t.CPtr(self.free_lists[dbg_order]) dbg_n: t.CSizeT = 0 while t.CUInt64T(dbg_cur) != 0: if t.CUInt64T(dbg_cur) == t.CUInt64T(dbg_block): @@ -834,17 +830,16 @@ class MemBuddy(MemManager): stdio.printf("[MEMDBG] alloc 扫描 free list order=%d 成环/超长(自检异常,终止)\n", dbg_order) stdio.fflush(None) stdlib.exit(1) - dbg_cur = t.CVoid(c.Deref(t.CUInt64T(dbg_cur, t.CPtr)), t.CPtr) + dbg_cur = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(dbg_cur))) if self._det_count >= self._det_cap: # 记录表满:stdlib.realloc 翻倍扩容(防 is_alive/_det_probe # 因容量耗尽漏检——SlotsBugTest 编译 includes 超 2M 次 alloc, # 满后新块无记录 → is_alive 误报"读取悬空") new_cap: t.CSizeT = self._det_cap * 2 - new_rec: t.CVoid | t.CPtr = t.CVoid( - stdlib.realloc(self._det_arena, new_cap * 16), t.CPtr) + new_rec: t.CPtr = t.CPtr(stdlib.realloc(self._det_arena, new_cap * 16)) if new_rec is not None: self._det_arena = new_rec - self._det_records = (t.CUInt64T | t.CPtr)(t.CVoid(new_rec, t.CPtr)) + self._det_records = (t.CUInt64T | t.CPtr)(t.CPtr(new_rec)) self._det_cap = new_cap if self._det_count < self._det_cap: self._det_records[self._det_count * 2] = rec_ptr_d @@ -856,7 +851,7 @@ class MemBuddy(MemManager): if self.detection != 0 and ((self._det_count & (MEMBUDDY_DET_PROBE_PERIOD - 1)) == 0 or self.deep_detect != 0): self._det_probe() if self.detection != 0: - result = t.CVoid(base_ret_d + 2, t.CPtr) + result = t.CPtr(base_ret_d + 2) if result is None: # 分配失败诊断(定位"异常 size/池耗尽导致 None → 调用方解引用段错误") # size == 0 与"池耗尽"是两回事:alloc 入口的 `size != 0` 守卫会直接 @@ -896,13 +891,13 @@ class MemBuddy(MemManager): self._unlock() return result - def free(self, ptr: t.CVoid | t.CPtr) -> t.CInt: + def free(self, ptr: t.CPtr) -> t.CInt: self._lock() if ptr is not None: - p_eff: t.CVoid | t.CPtr = ptr + p_eff: t.CPtr = ptr if self.detection != 0: # [DET] 返回指针比实际用户区靠后 2 字节,回收时向前偏移 2 - p_eff = t.CVoid(t.CUInt64T(ptr) - 2, t.CPtr) + p_eff = t.CPtr(t.CUInt64T(ptr) - 2) # 记录表置 free 标志(解耦于 detection,2026-09-04):非 detection # 也需置位,否则 free 后同地址再 alloc(正常 reuse)被误报重叠。 if self._det_records is not None: @@ -920,8 +915,8 @@ class MemBuddy(MemManager): break ri += 1 if self._is_valid_ptr(p_eff) != 0: - block: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(p_eff) - MEMBUDDY_HEADER_SIZE, t.CPtr) - stored: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(block, t.CPtr)), t.CPtr) + block: t.CPtr = t.CPtr(t.CUInt64T(p_eff) - MEMBUDDY_HEADER_SIZE) + stored: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(block))) stored_val: t.CUInt64T = t.CUInt64T(stored) order: t.CInt = t.CInt(stored_val >> 1) if (stored_val & 1) == 0: @@ -938,7 +933,7 @@ class MemBuddy(MemManager): if _memdbg_enable != 0: # [MEMDBG] 重复归还检测:活跃块(header 奇数)绝不该已在本 order # free list;命中 = 同块被 put 两次(free list 重复 put 实锤)。 - dbg_f: t.CVoid | t.CPtr = t.CVoid(self.free_lists[order], t.CPtr) + dbg_f: t.CPtr = t.CPtr(self.free_lists[order]) dbg_fn: t.CSizeT = 0 while t.CUInt64T(dbg_f) != 0: if t.CUInt64T(dbg_f) == t.CUInt64T(block): @@ -951,8 +946,8 @@ class MemBuddy(MemManager): stdio.printf("[MEMDBG] free 扫描 free list order=%d 成环/超长(自检异常,终止)\n", order) stdio.fflush(None) stdlib.exit(1) - dbg_f = t.CVoid(c.Deref(t.CUInt64T(dbg_f, t.CPtr)), t.CPtr) - c.DerefAs(block, t.CVoid(0, t.CPtr)) + dbg_f = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(dbg_f))) + c.DerefAs(block, t.CPtr(0)) self._coalesce(block, order) elif _memdbg_enable != 0: # [MEMDBG] 无效/悬空 free(原实现静默忽略):块在本池范围内但 header @@ -962,7 +957,7 @@ class MemBuddy(MemManager): dbg_blk: t.CUInt64T = dbg_eff - MEMBUDDY_HEADER_SIZE if (dbg_eff >= MEMBUDDY_HEADER_SIZE and dbg_blk >= t.CUInt64T(self.usable) and dbg_blk < t.CUInt64T(self.usable) + self.usable_size): - dbg_hv: t.CUInt64T = t.CUInt64T(t.CVoid(c.Deref(t.CUInt64T(t.CVoid(dbg_blk, t.CPtr), t.CPtr)), t.CPtr)) + dbg_hv: t.CUInt64T = t.CUInt64T(t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(dbg_blk)))) if (dbg_hv & 1) == 0: stdio.printf("[MEMDBG] 悬空/重复 free: ptr=%llu block=%llu header=0x%llX(偶数=已释放/未初始化,终止)\n", t.CUInt64T(ptr), dbg_blk, dbg_hv) @@ -990,7 +985,7 @@ class MemBuddy(MemManager): while ob < blk: ob = ob << 1 o += 1 - self._fl_push(o, t.CVoid(t.CUInt64T(self.usable) + pos, t.CPtr)) + self._fl_push(o, t.CPtr(t.CUInt64T(self.usable) + pos)) pos += blk rem -= blk @@ -1006,20 +1001,20 @@ class MemBuddy(MemManager): return 1 # realloc override: buddy system reads original order from block header; old_size is not required - def realloc(self, ptr: t.CVoid | t.CPtr, new_size: t.CSizeT) -> t.CVoid | t.CPtr: + def realloc(self, ptr: t.CPtr, new_size: t.CSizeT) -> t.CPtr: if ptr is None: return self.alloc(new_size) if new_size == 0: self.free(ptr) return None - p_eff: t.CVoid | t.CPtr = ptr + p_eff: t.CPtr = ptr if self.detection != 0: # [DET] 与 free 一致:先向前偏移 2 字节再做块级计算 - p_eff = t.CVoid(t.CUInt64T(ptr) - 2, t.CPtr) + p_eff = t.CPtr(t.CUInt64T(ptr) - 2) if self._is_valid_ptr(p_eff) == 0: return None - block: t.CVoid | t.CPtr = t.CVoid(t.CUInt64T(p_eff) - MEMBUDDY_HEADER_SIZE, t.CPtr) - stored: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(block, t.CPtr)), t.CPtr) + block: t.CPtr = t.CPtr(t.CUInt64T(p_eff) - MEMBUDDY_HEADER_SIZE) + stored: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(block))) stored_val: t.CUInt64T = t.CUInt64T(stored) old_order: t.CInt = t.CInt(stored_val >> 1) needed: t.CSizeT = new_size + MEMBUDDY_HEADER_SIZE @@ -1028,7 +1023,7 @@ class MemBuddy(MemManager): new_order: t.CInt = self._order_for_size(needed) if new_order <= old_order: return ptr - new_ptr: t.CVoid | t.CPtr = self.alloc(new_size) + new_ptr: t.CPtr = self.alloc(new_size) if new_ptr is None: return ptr old_block_size: t.CSizeT = _block_size_at_order(old_order) @@ -1047,7 +1042,7 @@ class MemBuddy(MemManager): # 补 _det_probe 盲区:写入侧检测(标记/0xCD)拦不住 use-after-free # 读取(如 SlotsBugTest KwEntry cargs=cl.args 悬空指向复用块)。 # ============================================================ - def is_alive(self, ptr: t.CVoid | t.CPtr) -> t.CInt: + def is_alive(self, ptr: t.CPtr) -> t.CInt: if self.detection == 0: return 1 if self._det_records is None: @@ -1090,21 +1085,21 @@ class MemBuddy(MemManager): i += 1 continue # 无 size 记录(理论不发生,防御) # active 记录:前标记(下溢检测)+ 后标记(上溢检测) - m_fa: t.CUInt16T | t.CPtr = (t.CUInt16T | t.CPtr)(t.CVoid(p - 2, t.CPtr)) + m_fa: t.CUInt16T | t.CPtr = (t.CUInt16T | t.CPtr)(t.CPtr(p - 2)) mfv_a: t.CUInt16T = m_fa[0] if mfv_a != t.CUInt16T(MEMBUDDY_DET_MARKER): stdio.printf("[DET] 下溢写: ptr=%llu size=%llu 前标记=0x%04X(期望 0xCDCD)\n", p, s, t.CUInt32T(mfv_a)) stdio.fflush(None) stdlib.exit(1) - m16: t.CUInt16T | t.CPtr = (t.CUInt16T | t.CPtr)(t.CVoid(p + s, t.CPtr)) + m16: t.CUInt16T | t.CPtr = (t.CUInt16T | t.CPtr)(t.CPtr(p + s)) mv: t.CUInt16T = m16[0] if mv != t.CUInt16T(MEMBUDDY_DET_MARKER): stdio.printf("[DET] 越界写: ptr=%llu size=%llu 后标记=0x%04X(期望 0xCDCD)块内容前16B: ", p, s, t.CUInt32T(mv)) bi3: t.CInt = 0 while bi3 < 16: - bv3: t.CUInt8T = (t.CUInt8T | t.CPtr)(t.CVoid(p + t.CUInt64T(bi3), t.CPtr))[0] + bv3: t.CUInt8T = (t.CUInt8T | t.CPtr)(t.CPtr(p + t.CUInt64T(bi3)))[0] stdio.printf("%02X ", t.CUInt32T(bv3)) bi3 += 1 # 越界区(ptr+size 起 24 字节):0x00=被写穿 0xCD=未写 其它=写入值, @@ -1112,7 +1107,7 @@ class MemBuddy(MemManager): stdio.printf("越界区: ") bj3: t.CInt = 0 while bj3 < 24: - bvj: t.CUInt8T = (t.CUInt8T | t.CPtr)(t.CVoid(p + s + t.CUInt64T(bj3), t.CPtr))[0] + bvj: t.CUInt8T = (t.CUInt8T | t.CPtr)(t.CPtr(p + s + t.CUInt64T(bj3)))[0] stdio.printf("%02X ", t.CUInt32T(bvj)) bj3 += 1 # 记录表上下文:该条前 3 + 后 3 的 {ptr,size},推断越界块分配阶段 @@ -1157,10 +1152,10 @@ class MemBuddy(MemManager): i: t.CInt for i in range(MEMBUDDY_MAX_ORDERS + 1): head_val: t.CUInt64T = self.free_lists[i] - cur: t.CVoid | t.CPtr = t.CVoid(head_val, t.CPtr) + cur: t.CPtr = t.CPtr(head_val) while t.CUInt64T(cur) != 0: count += 1 - cur = t.CVoid(c.Deref(t.CUInt64T(cur, t.CPtr)), t.CPtr) + cur = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(cur))) return count def free_bytes(self) -> t.CSizeT: @@ -1169,10 +1164,10 @@ class MemBuddy(MemManager): i: t.CInt for i in range(MEMBUDDY_MAX_ORDERS + 1): head_val: t.CUInt64T = self.free_lists[i] - cur: t.CVoid | t.CPtr = t.CVoid(head_val, t.CPtr) + cur: t.CPtr = t.CPtr(head_val) while t.CUInt64T(cur) != 0: total += _block_size_at_order(i) - cur = t.CVoid(c.Deref(t.CUInt64T(cur, t.CPtr)), t.CPtr) + cur = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(cur))) return total def used_bytes(self) -> t.CSizeT: @@ -1198,7 +1193,7 @@ class MemBuddy(MemManager): i: t.CInt for i in range(MEMBUDDY_MAX_ORDERS + 1): head_val: t.CUInt64T = self.free_lists[i] - cur: t.CVoid | t.CPtr = t.CVoid(head_val, t.CPtr) + cur: t.CPtr = t.CPtr(head_val) steps: t.CSizeT = 0 while t.CUInt64T(cur) != 0: steps += 1 @@ -1214,7 +1209,7 @@ class MemBuddy(MemManager): bs: t.CSizeT = _block_size_at_order(i) if offset % bs != 0: return 3 - cur = t.CVoid(c.Deref(t.CUInt64T(cur, t.CPtr)), t.CPtr) + cur = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(cur))) return 0 def check_alive_blocks(self, from_mark: t.CSizeT) -> t.CInt: @@ -1237,7 +1232,7 @@ class MemBuddy(MemManager): return 0 ci: t.CSizeT = from_mark while ci < self.chk_depth: - block: t.CVoid | t.CPtr = t.CVoid(self.chk_stack[ci], t.CPtr) + block: t.CPtr = t.CPtr(self.chk_stack[ci]) if block is not None: if t.CUInt64T(block) < t.CUInt64T(self.usable): # [TEMP-DIAG] 现场:块地址低于池起点(%llu/%zu 格式先例见 L861/L1216) @@ -1249,7 +1244,7 @@ class MemBuddy(MemManager): stdio.printf("[MEM] 存活块损坏 rc=2(块地址超出池末尾): block=%llu usable=%llu ci=%zu from_mark=%zu\n", t.CUInt64T(block), t.CUInt64T(self.usable), ci, from_mark) return 2 - stored: t.CVoid | t.CPtr = t.CVoid(c.Deref(t.CUInt64T(block, t.CPtr)), t.CPtr) + stored: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(block))) stored_val: t.CUInt64T = t.CUInt64T(stored) if (stored_val & 1) != 0: # 活块:header 必须是 (order<<1)|1,order 合法且块对齐到该 order 大小 @@ -1274,9 +1269,7 @@ class MemBuddy(MemManager): # 块尾 canary 校验:越界写穿数据区会破坏块尾哨兵, # 立即定位到越界写发生的文件(崩溃点漂移的关键哨兵)。 # 与 alloc 写入点统一使用编译期常量 MEMBUDDY_CANARY_PATTERN(同值同宽)。 - can_stored: t.CVoid | t.CPtr = t.CVoid( - c.Deref(t.CUInt64T(t.CUInt64T(block) + bs - MEMBUDDY_CANARY, t.CPtr), t.CPtr), - t.CPtr) + can_stored: t.CPtr = t.CPtr(c.Deref((t.CUInt64T | t.CPtr)(block) + bs - MEMBUDDY_CANARY)) if t.CUInt64T(can_stored) != t.CUInt64T(MEMBUDDY_CANARY_PATTERN): # [TEMP-DIAG] 越界写现场:块尾 canary 被写穿(rc=6 的判据)。 # 原实现只回 rc(int),调用方拿不到块/阶/实际值 → 无法定位。 diff --git a/openssl/types.vp b/openssl/types.vp index 5675aa9..3084a0e 100644 --- a/openssl/types.vp +++ b/openssl/types.vp @@ -9,61 +9,61 @@ import t, c # 链接目标:ViperOpenSSLProject 的 project.vpj 链接 openssl_358_C # 自编译静态库(libcrypto.a / libssl.a)。 # -# 注意:typedef 用 `t.CVoid | t.CPtr` 表达(与 vqt6 一致),生成 i8* +# 注意:typedef 用 `t.CPtr` 表达(与 vqt6 一致),生成 i8* # 指针参数,可被 ASan 插桩;纯 `t.CPtr` 会生成 opaque struct 参数, # opt -passes=asan 会报 invalid type for function argument。 # ============================================================ # ---- 基础上下文 ---- -OsslLibCtxPtr: t.CTypedef = t.CVoid | t.CPtr # OSSL_LIB_CTX* -EnginePtr: t.CTypedef = t.CVoid | t.CPtr # ENGINE* -OpensslInitSettingsPtr: t.CTypedef = t.CVoid | t.CPtr # OPENSSL_INIT_SETTINGS* +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)---- -BioPtr: t.CTypedef = t.CVoid | t.CPtr # BIO* -BioMethodPtr: t.CTypedef = t.CVoid | t.CPtr # BIO_METHOD* -BioAddrPtr: t.CTypedef = t.CVoid | t.CPtr # BIO_ADDR* -BioAddrInfoPtr: t.CTypedef = t.CVoid | t.CPtr # BIO_ADDRINFO* +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(摘要/对称/非对称)---- -EvpMdPtr: t.CTypedef = t.CVoid | t.CPtr # EVP_MD* -EvpMdCtxPtr: t.CTypedef = t.CVoid | t.CPtr # EVP_MD_CTX* -EvpCipherPtr: t.CTypedef = t.CVoid | t.CPtr # EVP_CIPHER* -EvpCipherCtxPtr: t.CTypedef = t.CVoid | t.CPtr # EVP_CIPHER_CTX* -EvpPkeyPtr: t.CTypedef = t.CVoid | t.CPtr # EVP_PKEY* -EvpPkeyCtxPtr: t.CTypedef = t.CVoid | t.CPtr # EVP_PKEY_CTX* -EvpEncodeCtxPtr: t.CTypedef = t.CVoid | t.CPtr # EVP_ENCODE_CTX* -HmacCtxPtr: t.CTypedef = t.CVoid | t.CPtr # HMAC_CTX* +EvpMdPtr: t.CTypedef = t.CPtr # EVP_MD* +EvpMdCtxPtr: t.CTypedef = t.CPtr # EVP_MD_CTX* +EvpCipherPtr: t.CTypedef = t.CPtr # EVP_CIPHER* +EvpCipherCtxPtr: t.CTypedef = t.CPtr # EVP_CIPHER_CTX* +EvpPkeyPtr: t.CTypedef = t.CPtr # EVP_PKEY* +EvpPkeyCtxPtr: t.CTypedef = t.CPtr # EVP_PKEY_CTX* +EvpEncodeCtxPtr: t.CTypedef = t.CPtr # EVP_ENCODE_CTX* +HmacCtxPtr: t.CTypedef = t.CPtr # HMAC_CTX* # ---- X509 / 证书 ---- -X509Ptr: t.CTypedef = t.CVoid | t.CPtr # X509* -X509NamePtr: t.CTypedef = t.CVoid | t.CPtr # X509_NAME* -X509NameEntryPtr: t.CTypedef = t.CVoid | t.CPtr # X509_NAME_ENTRY* -X509CrlPtr: t.CTypedef = t.CVoid | t.CPtr # X509_CRL* -X509ReqPtr: t.CTypedef = t.CVoid | t.CPtr # X509_REQ* -X509ExtensionPtr: t.CTypedef = t.CVoid | t.CPtr # X509_EXTENSION* -X509StorePtr: t.CTypedef = t.CVoid | t.CPtr # X509_STORE* -X509StoreCtxPtr: t.CTypedef = t.CVoid | t.CPtr # X509_STORE_CTX* -X509InfoPtr: t.CTypedef = t.CVoid | t.CPtr # X509_INFO* -X509VerifyParamPtr: t.CTypedef = t.CVoid | t.CPtr # X509_VERIFY_PARAM* +X509Ptr: t.CTypedef = t.CPtr # X509* +X509NamePtr: t.CTypedef = t.CPtr # X509_NAME* +X509NameEntryPtr: t.CTypedef = t.CPtr # X509_NAME_ENTRY* +X509CrlPtr: t.CTypedef = t.CPtr # X509_CRL* +X509ReqPtr: t.CTypedef = t.CPtr # X509_REQ* +X509ExtensionPtr: t.CTypedef = t.CPtr # X509_EXTENSION* +X509StorePtr: t.CTypedef = t.CPtr # X509_STORE* +X509StoreCtxPtr: t.CTypedef = t.CPtr # X509_STORE_CTX* +X509InfoPtr: t.CTypedef = t.CPtr # X509_INFO* +X509VerifyParamPtr: t.CTypedef = t.CPtr # X509_VERIFY_PARAM* # ---- ASN.1 ---- -Asn1ObjectPtr: t.CTypedef = t.CVoid | t.CPtr # ASN1_OBJECT* -Asn1StringPtr: t.CTypedef = t.CVoid | t.CPtr # ASN1_STRING* -Asn1IntegerPtr: t.CTypedef = t.CVoid | t.CPtr # ASN1_INTEGER* -Asn1TimePtr: t.CTypedef = t.CVoid | t.CPtr # ASN1_TIME* -Asn1OctetStringPtr: t.CTypedef = t.CVoid | t.CPtr # ASN1_OCTET_STRING* -Asn1BitStringPtr: t.CTypedef = t.CVoid | t.CPtr # ASN1_BIT_STRING* -GeneralNamesPtr: t.CTypedef = t.CVoid | t.CPtr # GENERAL_NAMES* -GeneralNamePtr: t.CTypedef = t.CVoid | t.CPtr # GENERAL_NAME* +Asn1ObjectPtr: t.CTypedef = t.CPtr # ASN1_OBJECT* +Asn1StringPtr: t.CTypedef = t.CPtr # ASN1_STRING* +Asn1IntegerPtr: t.CTypedef = t.CPtr # ASN1_INTEGER* +Asn1TimePtr: t.CTypedef = t.CPtr # ASN1_TIME* +Asn1OctetStringPtr: t.CTypedef = t.CPtr # ASN1_OCTET_STRING* +Asn1BitStringPtr: t.CTypedef = t.CPtr # ASN1_BIT_STRING* +GeneralNamesPtr: t.CTypedef = t.CPtr # GENERAL_NAMES* +GeneralNamePtr: t.CTypedef = t.CPtr # GENERAL_NAME* # ---- SSL/TLS ---- -SslPtr: t.CTypedef = t.CVoid | t.CPtr # SSL* -SslCtxPtr: t.CTypedef = t.CVoid | t.CPtr # SSL_CTX* -SslMethodPtr: t.CTypedef = t.CVoid | t.CPtr # SSL_METHOD* -SslSessionPtr: t.CTypedef = t.CVoid | t.CPtr # SSL_SESSION* -SslCipherPtr: t.CTypedef = t.CVoid | t.CPtr # SSL_CIPHER* +SslPtr: t.CTypedef = t.CPtr # SSL* +SslCtxPtr: t.CTypedef = t.CPtr # SSL_CTX* +SslMethodPtr: t.CTypedef = t.CPtr # SSL_METHOD* +SslSessionPtr: t.CTypedef = t.CPtr # SSL_SESSION* +SslCipherPtr: t.CTypedef = t.CPtr # SSL_CIPHER* # ---- BIGNUM ---- -BnPtr: t.CTypedef = t.CVoid | t.CPtr # BIGNUM* -BnCtxPtr: t.CTypedef = t.CVoid | t.CPtr # BN_CTX* +BnPtr: t.CTypedef = t.CPtr # BIGNUM* +BnCtxPtr: t.CTypedef = t.CPtr # BN_CTX* diff --git a/posix.vp b/posix.vp index 7d588eb..12a5cda 100644 --- a/posix.vp +++ b/posix.vp @@ -6,7 +6,7 @@ from stdint import * # POSIX Type Alias # Note: ssize_t has already been defined as t.CPtrDiffT in stdint.py, so we won’t repeat it here. # ============================================================================= -DIR: t.CTypedef = t.CVoid | t.CPtr # Opaque directory stream handle +DIR: t.CTypedef = t.CPtr # Opaque directory stream handle mode_t: t.CTypedef = t.CUInt32T # File mode/permission bits pid_t: t.CTypedef = t.CInt32T # Process ID off_t: t.CTypedef = t.CInt64T # File offset (Linux x86_64) @@ -201,7 +201,7 @@ extern def execv(path: t.CConst | str, argv: CHARPTR | t.CPtr) -> INT extern def waitpid(pid: pid_t, status: INT | t.CPtr, options: INT) -> pid_t extern def pipe(fds: INT | t.CPtr) -> INT extern def dup2(oldfd: INT, newfd: INT) -> INT -extern def _exit(status: INT) -> t.CVoid +extern def _exit(status: INT) extern def getpid() -> pid_t diff --git a/requests/__init__.vp b/requests/__init__.vp index 40e605f..d8aa67a 100644 --- a/requests/__init__.vp +++ b/requests/__init__.vp @@ -693,7 +693,7 @@ def release(resp: Response | t.CPtr): _mbuddy.free(resp._body_owned) if resp._raw_buf is not None: _mbuddy.free(resp._raw_buf) - _mbuddy.free(t.CVoid(t.CUInt64T(resp), t.CPtr)) + _mbuddy.free(t.CPtr(t.CUInt64T(resp))) # ============================================================ diff --git a/requests/download.vp b/requests/download.vp index e554324..6b10797 100644 --- a/requests/download.vp +++ b/requests/download.vp @@ -359,7 +359,7 @@ def download(url: str, path: str, threads: INT = 1, if job.path is None or job.url is None: stdlib.free(job.path) stdlib.free(job.url) - stdlib.free(t.CVoid(t.CUInt64T(job), t.CPtr)) + stdlib.free(t.CPtr(t.CUInt64T(job))) break job.is_https = 1 if (string.strncmp(url, "https://", 8) == 0) else 0 job.verify = 1 if verify else 0 @@ -374,11 +374,11 @@ def download(url: str, path: str, threads: INT = 1, stdlib.free(job.url) if job.ca_file is not None: stdlib.free(job.ca_file) - stdlib.free(t.CVoid(t.CUInt64T(job), t.CPtr)) + stdlib.free(t.CPtr(t.CUInt64T(job))) break job.done = 0 job.status = 0 - ctxs[i] = t.CVoid(t.CUInt64T(job), t.CPtr) + ctxs[i] = t.CPtr(t.CUInt64T(job)) th: vt.Thread | t.CPtr = vt.Thread(_dl_worker, ctxs[i]) if th is None: @@ -387,7 +387,7 @@ def download(url: str, path: str, threads: INT = 1, stdlib.free(job.url) if job.ca_file is not None: stdlib.free(job.ca_file) - stdlib.free(t.CVoid(t.CUInt64T(job), t.CPtr)) + stdlib.free(t.CPtr(t.CUInt64T(job))) break th.start() handles[i] = th.handle @@ -425,7 +425,7 @@ def download(url: str, path: str, threads: INT = 1, if jobm.ca_file is not None: stdlib.free(jobm.ca_file) stdlib.free(jobm.buf) - stdlib.free(t.CVoid(t.CUInt64T(jobm), t.CPtr)) + stdlib.free(t.CPtr(t.CUInt64T(jobm))) m += 1 if failed_job != 0 or total_done != total: diff --git a/requests/https.vp b/requests/https.vp index 5dbd319..b486c72 100644 --- a/requests/https.vp +++ b/requests/https.vp @@ -451,7 +451,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, if resp_cap >= 268435456: break resp_cap = resp_cap * 2 - nb: t.CVoid | t.CPtr = requests._mbuddy.realloc(resp_buf, t.CSizeT(resp_cap)) + nb: t.CPtr = requests._mbuddy.realloc(resp_buf, t.CSizeT(resp_cap)) if nb is None: _RecordErrorText("realloc response buffer failed") requests._mbuddy.free(resp_buf) diff --git a/socket.vp b/socket.vp index 7a8e2d3..a39826a 100644 --- a/socket.vp +++ b/socket.vp @@ -84,12 +84,12 @@ class timeval: extern def socket(family: INT, type: INT, protocol: INT) -> u64 extern def connect(fd: u64, addr: SocketAddr | t.CPtr, addrlen: INT) -> INT -extern def send(fd: u64, buf: t.CVoid | t.CPtr, len: INT, flags: INT) -> INT -extern def recv(fd: u64, buf: t.CVoid | t.CPtr, len: INT, flags: INT) -> INT +extern def send(fd: u64, buf: t.CPtr, len: INT, flags: INT) -> INT +extern def recv(fd: u64, buf: t.CPtr, len: INT, flags: INT) -> INT extern def bind(fd: u64, addr: SocketAddr | t.CPtr, addrlen: INT) -> INT extern def listen(fd: u64, backlog: INT) -> INT extern def accept(fd: u64, addr: SocketAddr | t.CPtr, addrlen: INT | t.CPtr) -> u64 -extern def setsockopt(fd: u64, level: INT, optname: INT, optval: t.CVoid | t.CPtr, optlen: INT) -> INT +extern def setsockopt(fd: u64, level: INT, optname: INT, optval: t.CPtr, optlen: INT) -> INT extern def gethostbyname(name: t.CConst | str) -> HostEnt | t.CPtr extern def ntohs(netshort: u16) -> u16 extern def htons(hostshort: u16) -> u16 @@ -236,7 +236,7 @@ class Socket: result = connect(self.fd, c.Addr(addr), 16) return result - def send(self, data: t.CVoid | t.CPtr, length: INT) -> INT: + def send(self, data: t.CPtr, length: INT) -> INT: """Send data. Returns: @@ -249,7 +249,7 @@ class Socket: return w32.winsock2.send(u64(self.fd), data, length, 0) return send(self.fd, data, length, 0) - def recv(self, buf: t.CVoid | t.CPtr, bufsize: INT) -> INT: + def recv(self, buf: t.CPtr, bufsize: INT) -> INT: """Receive data. Returns: @@ -262,7 +262,7 @@ class Socket: return w32.winsock2.recv(u64(self.fd), buf, bufsize, 0) return recv(self.fd, buf, bufsize, 0) - def sendall(self, data: t.CVoid | t.CPtr, length: INT) -> INT: + def sendall(self, data: t.CPtr, length: INT) -> INT: """Send all data. Returns: @@ -273,7 +273,7 @@ class Socket: total: INT = 0 while total < length: - n: INT = self.send(t.CVoid(t.CUInt64T(data) + total, t.CPtr), length - total) + n: INT = self.send(t.CPtr(t.CUInt64T(data) + total), length - total) if n <= 0: return SOCKET_ERROR total += n return 0 @@ -419,11 +419,11 @@ def SocketConnect(sock: Socket | t.CPtr, host: str, port: INT) -> INT: return sock.connect(host, port) -def SocketSend(sock: Socket | t.CPtr, data: t.CVoid | t.CPtr, length: INT) -> INT: +def SocketSend(sock: Socket | t.CPtr, data: t.CPtr, length: INT) -> INT: return sock.send(data, length) -def SocketRecv(sock: Socket | t.CPtr, buf: t.CVoid | t.CPtr, bufsize: INT) -> INT: +def SocketRecv(sock: Socket | t.CPtr, buf: t.CPtr, bufsize: INT) -> INT: return sock.recv(buf, bufsize) diff --git a/stdint.vp b/stdint.vp index c672976..029f8c8 100644 --- a/stdint.vp +++ b/stdint.vp @@ -37,7 +37,7 @@ CHARPTR: t.CTypedef = t.CChar | t.CPtr FSIZE_t: t.CTypedef = DWORD LBA_t: t.CTypedef = DWORD -VOIDPTR: t.CTypedef = t.CVoid | t.CPtr +VOIDPTR: t.CTypedef = t.CPtr FLOAT: t.CTypedef = t.CFloat DOUBLE: t.CTypedef = t.CDouble diff --git a/string.vp b/string.vp index 3d55cae..1df884c 100644 --- a/string.vp +++ b/string.vp @@ -113,7 +113,7 @@ def strncmp(str1: str, str2: str, n: t.CSizeT) -> int: # Memory compare function -def memcmp(ptr1: t.CVoid | t.CPtr, ptr2: t.CVoid | t.CPtr, n: t.CSizeT) -> int: +def memcmp(ptr1: t.CPtr, ptr2: t.CPtr, n: t.CSizeT) -> int: p1: BYTEPTR = BYTEPTR(ptr1) p2: BYTEPTR = BYTEPTR(ptr2) i: t.CSizeT = 0 @@ -200,7 +200,7 @@ export def memset(ptr: t.CPtr, value: int, num: t.CSizeT) -> t.CPtr: return ptr -def memset32(ptr: t.CVoid | t.CPtr, value: t.CUInt32T, count: t.CSizeT) -> t.CVoid | t.CPtr: +def memset32(ptr: t.CPtr, value: t.CUInt32T, count: t.CSizeT) -> t.CPtr: # Original implementation: # p: UINT32PTR = UINT32PTR(ptr) # i: t.CSizeT @@ -217,7 +217,7 @@ def memset32(ptr: t.CVoid | t.CPtr, value: t.CUInt32T, count: t.CSizeT) -> t.CVo # Memory copy function -export def memcpy(dest: t.CVoid | t.CPtr, src: t.CVoid | t.CPtr, num: t.CSizeT) -> t.CPtr: +export def memcpy(dest: t.CPtr, src: t.CPtr, num: t.CSizeT) -> t.CPtr: # Original implementation: # d: BYTEPTR = BYTEPTR(dest) # s: BYTEPTR = BYTEPTR(src) @@ -235,7 +235,7 @@ export def memcpy(dest: t.CVoid | t.CPtr, src: t.CVoid | t.CPtr, num: t.CSizeT) # 内存移动函数 -def memmove(dest: t.CVoid | t.CPtr, src: t.CVoid | t.CPtr, num: t.CSizeT) -> t.CVoid | t.CPtr: +def memmove(dest: t.CPtr, src: t.CPtr, num: t.CSizeT) -> t.CPtr: # 原始实现: # d: BYTEPTR = BYTEPTR(dest) # s: BYTEPTR = BYTEPTR(src) diff --git a/sys.vp b/sys.vp index 8f29169..5ba4985 100644 --- a/sys.vp +++ b/sys.vp @@ -20,8 +20,8 @@ _mbuddy: memhub.MemBuddy | t.CPtr # C Standard Library IO Declarations # ============================================================================= -extern def fgets(buf: bytes, size: int, stream: t.CVoid | t.CPtr) -> str -extern def fflush(stream: t.CVoid | t.CPtr) -> int +extern def fgets(buf: bytes, size: int, stream: t.CPtr) -> str +extern def fflush(stream: t.CPtr) -> int # C standard library exit (terminates the process after flushing the buffer, cross-platform) extern def exit(status: int) diff --git a/torch/__init__.vp b/torch/__init__.vp index 0d92b22..ef9e296 100644 --- a/torch/__init__.vp +++ b/torch/__init__.vp @@ -1,925 +1,925 @@ -# -*- coding: utf-8 -*- -# ============================================================ -# torch-lite: 仿 PyTorch 的极简张量/神经网络库(Viper) -# -# 设计原则: -# * "足够用就行": 只覆盖 MNIST 两层 MLP 训练/推理所需的原语。 -# * API 命名贴近 PyTorch: Tensor / linear_fwd(bias 融合) / relu / -# relu_backward / softmax_crossentropy(前向+梯度一体) / sgd_step。 -# * 张量为 2D 稠密 double (rows x cols, 行主序), 权重形状同 PyTorch: -# Linear(in, out) -> weight (out, in) / bias (out,), forward = x @ W^T + b。 -# * 计算热点结合 vipersimd: -# - linear_fwd 内积按 k%4 用 simd_dot4d 累加(纯 AVX2 点积); -# - relu_fwd 用 simd_max4d(x, 0) 四路取大; -# - 向量乘标量用 simd_mul_scalar4d(归一化 /255 等)。 -# backward / softmax / sgd 等按标量写(数据量小, 可读性优先)。 -# * 内存: 所有 Tensor 及 data/grad 都从调用方传入的 memhub.MemBuddy -# 分配(与 includes 既有约定一致, pool.alloc 免手动 free)。 -# ============================================================ -from stdint import * -import t, c -import stdlib -import string -import stdio -import vipermath -import vipersimd -import memhub - - -# ------------------------------------------------------------ -# Tensor -# ------------------------------------------------------------ -@t.NoVTable -class Tensor: - Data: t.CPtr # rows*cols*8 字节连续 double - Grad: t.CPtr # 同尺寸梯度(可为 None) - Rows: sizet - Cols: sizet - -# 字段: Data/Grad/Rows/Cols = 32 字节(无 vtable) -TENSOR_SIZE: t.CDefine = 32 - - -def _zeros_block(pool: rowptr[memhub.MemBuddy], bytes_n: sizet) -> t.CPtr: - """分配并清零一块内存""" - p: t.CPtr = pool.alloc(bytes_n) - if p is not None: - string.memset(p, 0, bytes_n) - return p - - -def zeros(pool: rowptr[memhub.MemBuddy], rows: sizet, - cols: sizet, with_grad: int = 1) -> Tensor | t.CPtr: - """torch.zeros: 创建 rows x cols 的全零 Tensor, 可选分配 Grad 缓冲""" - if pool is None: - return None - t0: rowptr[Tensor] = pool.alloc(TENSOR_SIZE) - if t0 is None: - return None - string.memset(t0, 0, TENSOR_SIZE) - nb: sizet = rows * cols * sizet(8) - t0.Data = _zeros_block(pool, nb) - if t0.Data is None: - return None - if with_grad != 0: - t0.Grad = _zeros_block(pool, nb) - t0.Rows = rows - t0.Cols = cols - return t0 - - -def from_raw(pool: rowptr[memhub.MemBuddy], data: t.CPtr, - rows: sizet, cols: sizet, - with_grad: int = 0) -> Tensor | t.CPtr: - """torch.from_raw: 包装一块已有内存为 Tensor(不复制, 不拥有)""" - if pool is None: - return None - t0: rowptr[Tensor] = pool.alloc(TENSOR_SIZE) - if t0 is None: - return None - string.memset(t0, 0, TENSOR_SIZE) - t0.Data = data - t0.Grad = None - t0.Rows = rows - t0.Cols = cols - return t0 - - -def tensor_free(t0: Tensor | t.CPtr): - """释放 Tensor 外壳(数据由 MemBuddy 池统一回收, 无需手动 free)""" - pass - - -def tensor_data(t0: Tensor | t.CPtr) -> t.CPtr: - if t0 is None: - return None - return t0.Data - - -def tensor_grad(t0: Tensor | t.CPtr) -> t.CPtr: - if t0 is None: - return None - return t0.Grad - - -def tensor_rows(t0: Tensor | t.CPtr) -> sizet: - if t0 is None: - return 0 - return t0.Rows - - -def tensor_cols(t0: Tensor | t.CPtr) -> sizet: - if t0 is None: - return 0 - return t0.Cols - - -def fill(t0: Tensor | t.CPtr, val: double): - """torch.fill: 全部元素置为 val""" - if t0 is None or t0.Data is None: - return - d: double | t.CPtr = t0.Data - n: sizet = t0.Rows * t0.Cols - i: sizet = 0 - while i < n: - d[i] = val - i += 1 - - -def zero_grad(t0: Tensor | t.CPtr): - """torch.zero_grad: Grad 缓冲清零""" - if t0 is None or t0.Grad is None: - return - string.memset(t0.Grad, 0, t0.Rows * t0.Cols * sizet(8)) - - -# ------------------------------------------------------------ -# 确定性伪随机(LCG, minstd)与 Kaiming 均匀初始化 -# ------------------------------------------------------------ -_rng_state: double = 12345.0 -LCG_M: double = 2147483647.0 -LCG_A: double = 48271.0 - - -def manual_seed(seed: int64): - global _rng_state - _rng_state = double(seed % int64(2147483647)) - if _rng_state <= double(0.0): - _rng_state = double(12345.0) - - -def _next_rand() -> double: - """(0,1) 均匀随机""" - global _rng_state - _rng_state = vipermath.fmod(_rng_state * LCG_A, LCG_M) - return _rng_state / LCG_M - - -def uniform_fill(t0: Tensor | t.CPtr, lo: double, hi: double): - """U(lo, hi) 填充""" - if t0 is None or t0.Data is None: - return - d: double | t.CPtr = t0.Data - n: sizet = t0.Rows * t0.Cols - span: double = hi - lo - i: sizet = 0 - while i < n: - d[i] = lo + _next_rand() * span - i += 1 - - -def kaiming_uniform_fill(w: Tensor | t.CPtr, fan_in: sizet, - fan_out: sizet): - """PyTorch Linear 默认初始化: U(-sqrt(6/(fan_in+fan_out)), +...)""" - bound: double = vipermath.sqrt( - double(6.0) / double(fan_in + fan_out)) - uniform_fill(w, -bound, bound) - - -# ------------------------------------------------------------ -# 基础元素级运算(结合 vipersimd 4 宽指令) -# ------------------------------------------------------------ -def vector_mul_scalar4(src: t.CPtr, scalar_ptr: t.CPtr, dst: t.CPtr, - n: sizet): - """dst = src * scalar (逐 4 元素 simd_mul_scalar4d + 标量尾数)""" - if src is None or scalar_ptr is None or dst is None: - return - i: sizet = 0 - while i + 4 <= n: - vipersimd.simd_mul_scalar4d( - t.CPtr(uint64(src) + uint64(i) * 8), - scalar_ptr, - t.CPtr(uint64(dst) + uint64(i) * 8)) - i += 4 - sd: double | t.CPtr = src - dd: double | t.CPtr = dst - # 尾部标量 - sp: double | t.CPtr = scalar_ptr - sval: double = sp[0] - while i < n: - dd[i] = sd[i] * sval - i += 1 - - -def vector_add_inplace(a: t.CPtr, b: t.CPtr, n: sizet): - """a += b (逐 4 元素 simd_add_array 尾部标量)""" - if a is None or b is None: - return - tmp: t.CPtr = stdlib.malloc(8) - if tmp is None: - return - i: sizet = 0 - while i + 4 <= n: - vipersimd.simd_add_array( - t.CPtr(uint64(a) + uint64(i) * 8), - t.CPtr(uint64(b) + uint64(i) * 8), - t.CPtr(uint64(a) + uint64(i) * 8)) - i += 4 - da: double | t.CPtr = a - db: double | t.CPtr = b - while i < n: - da[i] = da[i] + db[i] - i += 1 - stdlib.free(tmp) - - -# ------------------------------------------------------------ -# 线性层: forward = x @ W^T + b (W: out x in, x: m x in, out: m x out) -# 内积按 k%4 用 vipersimd.simd_dot4d —— MNIST 主计算热点 -# ------------------------------------------------------------ -def linear_fwd(w: Tensor | t.CPtr, b: Tensor | t.CPtr, - x: Tensor | t.CPtr, out: Tensor | t.CPtr): - if w is None or b is None or x is None or out is None: - return - m: sizet = x.Rows - k: sizet = x.Cols - n: sizet = w.Rows - wd: double | t.CPtr = w.Data - bd: double | t.CPtr = b.Data - xd: double | t.CPtr = x.Data - od: double | t.CPtr = out.Data - acc_buf: t.CPtr = stdlib.malloc(8) - if acc_buf is None: - return - accp: double | t.CPtr = acc_buf - i: sizet = 0 - while i < m: - j: sizet = 0 - while j < n: - acc: double = bd[j] - kk: sizet = 0 - while kk + 4 <= k: - vipersimd.simd_dot4d( - t.CPtr(uint64(xd) + uint64(i * k + kk) * 8), - t.CPtr(uint64(wd) + uint64(j * k + kk) * 8), - acc_buf) - acc = acc + accp[0] - kk += 4 - while kk < k: - acc = acc + xd[i * k + kk] * wd[j * k + kk] - kk += 1 - od[i * n + j] = acc - j += 1 - i += 1 - stdlib.free(acc_buf) - - -# ------------------------------------------------------------ -# 线性层反向: 输入 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) -# ------------------------------------------------------------ -def linear_bwd(w: Tensor | t.CPtr, x: Tensor | t.CPtr, - g: Tensor | t.CPtr, dw: Tensor | t.CPtr, - db: Tensor | t.CPtr, dx: Tensor | t.CPtr, - accum: int = 0): - if w is None or x is None or g is None: - return - if dw is None or db is None or dx is None: - return - m: sizet = x.Rows - k: sizet = x.Cols - n: sizet = w.Rows - wd: double | t.CPtr = w.Data - xd: double | t.CPtr = x.Data - gd: double | t.CPtr = g.Data - dwd: double | t.CPtr = dw.Data - dbd: double | t.CPtr = db.Data - dxd: double | t.CPtr = dx.Data - - # dw/db/dx 清零(dx 恒清零: 它是本样本的输入梯度, 不跨样本累积) - # accum=1 时保留 dw/db 现值 —— 逐样本反向累积整批参数梯度的调用方用 - if accum == 0: - string.memset(dw.Data, 0, n * k * sizet(8)) - string.memset(db.Data, 0, n * sizet(8)) - string.memset(dx.Data, 0, m * k * sizet(8)) - - b: sizet = 0 - while b < m: - j: sizet = 0 - while j < n: - gj: double = gd[b * n + j] - if gj != double(0.0): - dbd[j] = dbd[j] + gj - i: sizet = 0 - while i < k: - dwd[j * k + i] = dwd[j * k + i] + gj * xd[b * k + i] - dxd[b * k + i] = dxd[b * k + i] + gj * wd[j * k + i] - i += 1 - j += 1 - b += 1 - - -# ------------------------------------------------------------ -# ReLU (simd_max4d 版本) 与反向 -# ------------------------------------------------------------ -def relu_fwd(x: Tensor | t.CPtr, out: Tensor | t.CPtr): - if x is None or out is None: - return - n: sizet = x.Rows * x.Cols - zero4: t.CPtr = stdlib.malloc(32) - if zero4 is None: - return - string.memset(zero4, 0, 32) - xd: double | t.CPtr = x.Data - od: double | t.CPtr = out.Data - i: sizet = 0 - while i + 4 <= n: - vipersimd.simd_max4d( - t.CPtr(uint64(xd) + uint64(i) * 8), - zero4, - t.CPtr(uint64(od) + uint64(i) * 8)) - i += 4 - while i < n: - if xd[i] > double(0.0): - od[i] = xd[i] - else: - od[i] = double(0.0) - i += 1 - stdlib.free(zero4) - - -def relu_backward(x: Tensor | t.CPtr, g: Tensor | t.CPtr, - gx: Tensor | t.CPtr): - """gx = g * (x > 0)""" - if x is None or g is None or gx is None: - return - n: sizet = x.Rows * x.Cols - xd: double | t.CPtr = x.Data - gd: double | t.CPtr = g.Data - od: double | t.CPtr = gx.Data - i: sizet = 0 - while i < n: - if xd[i] > double(0.0): - od[i] = gd[i] - else: - od[i] = double(0.0) - i += 1 - - -# ------------------------------------------------------------ -# softmax + CrossEntropy(融合, 数值稳定) —— MNIST 输出层 -# logits: (m, C), labels: m 个 int (0..C-1) -# loss 写入 *loss, 梯度(已按 1/m 归一)写入 grad (m, C) -# ------------------------------------------------------------ -def softmax_crossentropy(logits: Tensor | t.CPtr, - labels: int | t.CPtr, - n_labels: sizet, - loss: double | t.CPtr, - grad: Tensor | t.CPtr): - if logits is None or labels is None or grad is None: - return - if loss is None: - return - m: sizet = logits.Rows - c: sizet = logits.Cols - ld: double | t.CPtr = logits.Data - gd: double | t.CPtr = grad.Data - lb: int | t.CPtr = labels - total: double = double(0.0) - inv_m: double = double(1.0) / double(m) - b: sizet = 0 - while b < m: - # 数值稳定 softmax - mx: double = ld[b * c] - j: sizet = 1 - while j < c: - if ld[b * c + j] > mx: - mx = ld[b * c + j] - j += 1 - s: double = double(0.0) - j = 0 - while j < c: - s = s + vipermath.exp(ld[b * c + j] - mx) - j += 1 - y: int = lb[b] - # loss += -(logits[lab] - max - log(s)) - total = total - (ld[b * c + sizet(y)] - mx - vipermath.log(s)) - # 梯度: (softmax - onehot) / m - j = 0 - while j < c: - p: double = vipermath.exp(ld[b * c + j] - mx) / s - o: double = double(0.0) - if sizet(y) == j: - o = double(1.0) - gd[b * c + j] = (p - o) * inv_m - j += 1 - b += 1 - loss[0] = total - - -# ------------------------------------------------------------ -# 每行 argmax (分类预测) -# ------------------------------------------------------------ -def row_argmax(x: Tensor | t.CPtr, pred: int | t.CPtr): - if x is None or pred is None: - return - m: sizet = x.Rows - c: sizet = x.Cols - d: double | t.CPtr = x.Data - i: sizet = 0 - while i < m: - best: sizet = 0 - bv: double = d[i * c] - j: sizet = 1 - while j < c: - if d[i * c + j] > bv: - bv = d[i * c + j] - best = j - j += 1 - pred[i] = int(best) - i += 1 - - -# ------------------------------------------------------------ -# SGD: data -= lr * grad(全量梯度) / 重球法: v = m*v+g, data -= lr*v -# ------------------------------------------------------------ -def sgd_step(param: Tensor | t.CPtr, lr: double): - if param is None or param.Data is None or param.Grad is None: - return - d: double | t.CPtr = param.Data - g: double | t.CPtr = param.Grad - n: sizet = param.Rows * param.Cols - i: sizet = 0 - while i < n: - d[i] = d[i] - lr * g[i] - i += 1 - - -def sgd_step_momentum(param: Tensor | t.CPtr, 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 很慢且容易卡在低准确率。""" - if param is None or param.Data is None or param.Grad is None: - return - if vel is None: - sgd_step(param, lr) - return - d: double | t.CPtr = param.Data - g: double | t.CPtr = param.Grad - v: double | t.CPtr = vel - n: sizet = param.Rows * param.Cols - i: sizet = 0 - while i < n: - vv: double = momentum * v[i] + g[i] - v[i] = vv - d[i] = d[i] - lr * vv - i += 1 - - -# ------------------------------------------------------------ -# 逐元素乘加 (调试/扩展用, vipersimd fma_array) -# a = a*b + c -# ------------------------------------------------------------ -def fma_array_inplace(a: Tensor | t.CPtr, b: t.CPtr, c: t.CPtr): - if a is None or a.Data is None: - return - vipersimd.simd_fma_array(a.Data, b, c, a.Data, a.Rows * a.Cols) - - -# ------------------------------------------------------------ -# 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%。) -# ------------------------------------------------------------ -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) 同一写法。)""" - return t.CPtr(uint64(base) + uint64(elem) * 8) - - -def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr, - n: sizet, cin: sizet, hin: sizet, win: sizet, - 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 同理, 整除向下取整)""" - if x is None or w is None or out is None: - return - hout: sizet = (hin + pad * 2 - k) / stride + 1 - wout: sizet = (win + pad * 2 - k) / stride + 1 - xd: double | t.CPtr = x - wd: double | t.CPtr = w - od: double | t.CPtr = out - kk: sizet = cin * k * k - # SIMD 安全区间(见文件头说明) - 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 路累加器 - if simd_on != 0: - wb = stdlib.malloc(kk * 32) - t4 = stdlib.malloc(32) - if wb is None or t4 is None: - if wb is not None: - stdlib.free(wb) - if t4 is not None: - stdlib.free(t4) - wb = None - t4 = None - simd_on = 0 - wbd: double | t.CPtr = wb - t4d: double | t.CPtr = t4 - - bi: sizet = 0 - while bi < n: - co: sizet = 0 - while co < cout: - bias: double = double(0.0) - if b is not None: - bd: double | t.CPtr = b - bias = bd[co] - # 权重标量预广播: 每个 (ci,ky,kx) 一个 4 路副本, 本通道所有输出 - # 行/列共用。否则 vbroadcastsd(4 次 store) 会跟着 4 路循环重复, - # 把 SIMD 的收益吃掉一半。 - if simd_on != 0: - wb4: sizet = 0 - while wb4 < kk: - wq: double = wd[co * kk + wb4] - wbd[wb4 * 4] = wq - wbd[wb4 * 4 + 1] = wq - wbd[wb4 * 4 + 2] = wq - wbd[wb4 * 4 + 3] = wq - wb4 += 1 - oy: sizet = 0 - while oy < hout: - oy0: sizet = oy * stride - ox: sizet = 0 - while ox < wout: - if simd_on != 0 and ox >= pad and ox <= smax and ox + 4 <= wout: - # ---- 4 路: 一次算 4 个相邻输出列 ---- - t4d[0] = bias - t4d[1] = bias - t4d[2] = bias - t4d[3] = bias - ci: sizet = 0 - while ci < cin: - ky: sizet = 0 - while ky < k: - iy: sizet = oy0 + ky - if iy >= pad: - yy: sizet = iy - pad - if yy < hin: - xrow: sizet = ((bi * cin + ci) * hin + yy) * win - kx: sizet = 0 - while kx < k: - vipersimd.simd_fma4d( - _dptr(x, xrow + ox + kx - pad), - _dptr(wb, ((ci * k + ky) * k + kx) * 4), - t4, t4) - kx += 1 - ky += 1 - ci += 1 - ob: sizet = ((bi * cout + co) * hout + oy) * wout + ox - od[ob] = t4d[0] - od[ob + 1] = t4d[1] - od[ob + 2] = t4d[2] - od[ob + 3] = t4d[3] - ox += 4 - else: - # ---- 标量: 边界列 / stride != 1 ---- - ox0: sizet = ox * stride - acc: double = bias - ci2: sizet = 0 - while ci2 < cin: - ky2: sizet = 0 - while ky2 < k: - iy2: sizet = oy0 + ky2 - if iy2 >= pad: - yy2: sizet = iy2 - pad - if yy2 < hin: - kx2: sizet = 0 - while kx2 < k: - ix2: sizet = ox0 + kx2 - if ix2 >= pad: - xx2: sizet = ix2 - pad - if xx2 < win: - acc = acc + \ - wd[co * kk + (ci2 * k + ky2) * k + kx2] * \ - xd[((bi * cin + ci2) * hin + yy2) * win + xx2] - kx2 += 1 - ky2 += 1 - ci2 += 1 - od[((bi * cout + co) * hout + oy) * wout + ox] = acc - ox += 1 - oy += 1 - co += 1 - bi += 1 - if wb is not None: - stdlib.free(wb) - if t4 is not None: - stdlib.free(t4) - - -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 成标量。""" - if x is None or dout is None or dw is None: - return - hout: sizet = (hin + pad * 2 - k) / stride + 1 - wout: sizet = (win + pad * 2 - k) / stride + 1 - xd: double | t.CPtr = x - gd: double | t.CPtr = dout - dwd: double | t.CPtr = dw - dbd: double | t.CPtr = None - if db is not None: - dbd = db - kk: sizet = cin * k * k - simd_on: int = 0 - smax: sizet = 0 - if stride == 1 and win + pad >= k + 3: - simd_on = 1 - smax = win + pad - (k + 3) - acc: t.CPtr = None - if simd_on != 0: - acc = stdlib.malloc(32) - if acc is None: - simd_on = 0 - accd: double | t.CPtr = acc - - bi: sizet = 0 - while bi < n: - co: sizet = 0 - while co < cout: - gp: sizet = (bi * cout + co) * hout * wout - # ---- db[co] += sum(g): g 的整个通道平面是连续的 ---- - if dbd is not None: - tot0: double = double(0.0) - if simd_on != 0: - accd[0] = double(0.0) - accd[1] = double(0.0) - accd[2] = double(0.0) - accd[3] = double(0.0) - gcnt: sizet = hout * wout - gof: sizet = 0 - while gof + 4 <= gcnt: - if simd_on != 0: - vipersimd.simd_add4d(_dptr(dout, gp + gof), acc, acc) - else: - tot0 = tot0 + gd[gp + gof] - tot0 = tot0 + gd[gp + gof + 1] - tot0 = tot0 + gd[gp + gof + 2] - tot0 = tot0 + gd[gp + gof + 3] - gof += 4 - while gof < gcnt: - tot0 = tot0 + gd[gp + gof] - gof += 1 - if simd_on != 0: - vipersimd.simd_hsum4d(acc, acc) - tot0 = tot0 + accd[0] - dbd[co] = dbd[co] + tot0 - # ---- dw[co][ci][ky][kx] ---- - ci: sizet = 0 - while ci < cin: - ky: sizet = 0 - while ky < k: - kx: sizet = 0 - while kx < k: - tot: double = double(0.0) - if simd_on != 0: - accd[0] = double(0.0) - accd[1] = double(0.0) - accd[2] = double(0.0) - accd[3] = double(0.0) - oy: sizet = 0 - while oy < hout: - iy: sizet = oy * stride + ky - if iy >= pad: - yy: sizet = iy - pad - if yy < hin: - xrow: sizet = ((bi * cin + ci) * hin + yy) * win - goff: sizet = gp + oy * wout - ox: sizet = 0 - while ox < wout: - if simd_on != 0 and ox >= pad and ox <= smax and ox + 4 <= wout: - vipersimd.simd_fma4d( - _dptr(dout, goff + ox), - _dptr(x, xrow + ox + kx - pad), - acc, acc) - ox += 4 - else: - ix: sizet = ox * stride + kx - if ix >= pad: - xx: sizet = ix - pad - if xx < win: - tot = tot + gd[goff + ox] * \ - xd[xrow + xx] - ox += 1 - oy += 1 - if simd_on != 0: - vipersimd.simd_hsum4d(acc, acc) - tot = tot + accd[0] - wj: sizet = co * kk + (ci * k + ky) * k + kx - dwd[wj] = dwd[wj] + tot - kx += 1 - ky += 1 - ci += 1 - co += 1 - bi += 1 - if acc is not None: - stdlib.free(acc) - - -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)。""" - if w is None or dout is None or dx is None: - return - hout: sizet = (hin + pad * 2 - k) / stride + 1 - wout: sizet = (win + pad * 2 - k) / stride + 1 - wd: double | t.CPtr = w - gd: double | t.CPtr = dout - dxd: double | t.CPtr = dx - string.memset(dx, 0, n * cin * hin * win * sizet(8)) - kk: sizet = cin * k * k - 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 路广播副本(cout*kk 个标量) - if simd_on != 0: - wb = stdlib.malloc(cout * kk * 32) - if wb is None: - simd_on = 0 - wbd: double | t.CPtr = wb - if simd_on != 0: - # 权重在一次调用内不变 → 整体预广播一次(注意别在 4 路循环里重复 - # vbroadcastsd: 那是 4 次 store, 会把 SIMD 的收益吃掉一半) - wjn: sizet = cout * kk - wj4: sizet = 0 - while wj4 < wjn: - wq2: double = wd[wj4] - wbd[wj4 * 4] = wq2 - wbd[wj4 * 4 + 1] = wq2 - wbd[wj4 * 4 + 2] = wq2 - wbd[wj4 * 4 + 3] = wq2 - wj4 += 1 - - bi: sizet = 0 - while bi < n: - ci: sizet = 0 - while ci < cin: - ky: sizet = 0 - while ky < k: - oy: sizet = 0 - while oy < hout: - iy: sizet = oy * stride + ky - if iy >= pad: - yy: sizet = iy - pad - if yy < hin: - dxrow: sizet = ((bi * cin + ci) * hin + yy) * win - ox: sizet = 0 - while ox < wout: - if simd_on != 0 and ox >= pad and ox <= smax and ox + 4 <= wout: - co: sizet = 0 - while co < cout: - kx: sizet = 0 - while kx < k: - goff: sizet = ((bi * cout + co) * hout + oy) * wout + ox - doff: sizet = dxrow + ox + kx - pad - vipersimd.simd_fma4d( - _dptr(dout, goff), - _dptr(wb, (co * kk + (ci * k + ky) * k + kx) * 4), - _dptr(dx, doff), _dptr(dx, doff)) - kx += 1 - co += 1 - ox += 4 - else: - co2: sizet = 0 - while co2 < cout: - kx2: sizet = 0 - while kx2 < k: - ix: sizet = ox * stride + kx2 - if ix >= pad: - xx: sizet = ix - pad - if xx < win: - xi: sizet = dxrow + xx - gi: sizet = ((bi * cout + co2) * hout + oy) * wout + ox - dxd[xi] = dxd[xi] + \ - gd[gi] * wd[co2 * kk + (ci * k + ky) * k + kx2] - kx2 += 1 - co2 += 1 - ox += 1 - oy += 1 - ky += 1 - ci += 1 - bi += 1 - if wb is not None: - stdlib.free(wb) - - -# ------------------------------------------------------------ -# MaxPool2d (k x k, stride 通常 = 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)""" - if x is None or out is None or argmax is None: - return - hout: sizet = (hin - k) / stride + 1 - wout: sizet = (win - k) / stride + 1 - xd: double | t.CPtr = x - od: double | t.CPtr = out - am: int | t.CPtr = argmax - bi: sizet = 0 - while bi < n: - ch: sizet = 0 - while ch < c: - plano: sizet = (bi * c + ch) * hin * win - oy: sizet = 0 - while oy < hout: - oy0: sizet = oy * stride - ox: sizet = 0 - while ox < wout: - ox0: sizet = ox * stride - best: double = double(0.0) - bidx: int = 0 - first: int = 1 - ky: sizet = 0 - while ky < k: - yy: sizet = oy0 + ky - kx: sizet = 0 - while kx < k: - xx: sizet = ox0 + kx - v: double = xd[plano + yy * win + xx] - if first != 0 or v > best: - best = v - bidx = int(yy * win + xx) - first = 0 - kx += 1 - ky += 1 - oi: sizet = (bi * c + ch) * hout * wout + oy * wout + ox - od[oi] = best - am[oi] = bidx - ox += 1 - oy += 1 - ch += 1 - bi += 1 - - -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""" - if dout is None or argmax is None or dx is None: - return - hout: sizet = (hin - k) / stride + 1 - wout: sizet = (win - k) / stride + 1 - gd: double | t.CPtr = dout - am: int | t.CPtr = argmax - dxd: double | t.CPtr = dx - string.memset(dx, 0, n * c * hin * win * sizet(8)) - bi: sizet = 0 - while bi < n: - ch: sizet = 0 - while ch < c: - plano: sizet = (bi * c + ch) * hin * win - oy: sizet = 0 - while oy < hout: - ox: sizet = 0 - while ox < wout: - oi: sizet = (bi * c + ch) * hout * wout + oy * wout + ox - tgt: sizet = plano + sizet(am[oi]) - dxd[tgt] = dxd[tgt] + gd[oi] - ox += 1 - oy += 1 - ch += 1 - bi += 1 +# -*- coding: utf-8 -*- +# ============================================================ +# torch-lite: 仿 PyTorch 的极简张量/神经网络库(Viper) +# +# 设计原则: +# * "足够用就行": 只覆盖 MNIST 两层 MLP 训练/推理所需的原语。 +# * API 命名贴近 PyTorch: Tensor / linear_fwd(bias 融合) / relu / +# relu_backward / softmax_crossentropy(前向+梯度一体) / sgd_step。 +# * 张量为 2D 稠密 double (rows x cols, 行主序), 权重形状同 PyTorch: +# Linear(in, out) -> weight (out, in) / bias (out,), forward = x @ W^T + b。 +# * 计算热点结合 vipersimd: +# - linear_fwd 内积按 k%4 用 simd_dot4d 累加(纯 AVX2 点积); +# - relu_fwd 用 simd_max4d(x, 0) 四路取大; +# - 向量乘标量用 simd_mul_scalar4d(归一化 /255 等)。 +# backward / softmax / sgd 等按标量写(数据量小, 可读性优先)。 +# * 内存: 所有 Tensor 及 data/grad 都从调用方传入的 memhub.MemBuddy +# 分配(与 includes 既有约定一致, pool.alloc 免手动 free)。 +# ============================================================ +from stdint import * +import t, c +import stdlib +import string +import stdio +import vipermath +import vipersimd +import memhub + + +# ------------------------------------------------------------ +# Tensor +# ------------------------------------------------------------ +@t.NoVTable +class Tensor: + Data: t.CPtr # rows*cols*8 字节连续 double + Grad: t.CPtr # 同尺寸梯度(可为 None) + Rows: sizet + Cols: sizet + +# 字段: Data/Grad/Rows/Cols = 32 字节(无 vtable) +TENSOR_SIZE: t.CDefine = 32 + + +def _zeros_block(pool: rawptr[memhub.MemBuddy], bytes_n: sizet) -> t.CPtr: + """分配并清零一块内存""" + p: t.CPtr = pool.alloc(bytes_n) + if p is not None: + string.memset(p, 0, bytes_n) + return p + + +def zeros(pool: rawptr[memhub.MemBuddy], rows: sizet, + cols: sizet, with_grad: int = 1) -> Tensor | t.CPtr: + """torch.zeros: 创建 rows x cols 的全零 Tensor, 可选分配 Grad 缓冲""" + if pool is None: + return None + t0: rawptr[Tensor] = pool.alloc(TENSOR_SIZE) + if t0 is None: + return None + string.memset(t0, 0, TENSOR_SIZE) + nb: sizet = rows * cols * sizet(8) + t0.Data = _zeros_block(pool, nb) + if t0.Data is None: + return None + if with_grad != 0: + t0.Grad = _zeros_block(pool, nb) + t0.Rows = rows + t0.Cols = cols + return t0 + + +def from_raw(pool: rawptr[memhub.MemBuddy], data: t.CPtr, + rows: sizet, cols: sizet, + with_grad: int = 0) -> Tensor | t.CPtr: + """torch.from_raw: 包装一块已有内存为 Tensor(不复制, 不拥有)""" + if pool is None: + return None + t0: rawptr[Tensor] = pool.alloc(TENSOR_SIZE) + if t0 is None: + return None + string.memset(t0, 0, TENSOR_SIZE) + t0.Data = data + t0.Grad = None + t0.Rows = rows + t0.Cols = cols + return t0 + + +def tensor_free(t0: Tensor | t.CPtr): + """释放 Tensor 外壳(数据由 MemBuddy 池统一回收, 无需手动 free)""" + pass + + +def tensor_data(t0: Tensor | t.CPtr) -> t.CPtr: + if t0 is None: + return None + return t0.Data + + +def tensor_grad(t0: Tensor | t.CPtr) -> t.CPtr: + if t0 is None: + return None + return t0.Grad + + +def tensor_rows(t0: Tensor | t.CPtr) -> sizet: + if t0 is None: + return 0 + return t0.Rows + + +def tensor_cols(t0: Tensor | t.CPtr) -> sizet: + if t0 is None: + return 0 + return t0.Cols + + +def fill(t0: Tensor | t.CPtr, val: double): + """torch.fill: 全部元素置为 val""" + if t0 is None or t0.Data is None: + return + d: double | t.CPtr = t0.Data + n: sizet = t0.Rows * t0.Cols + i: sizet = 0 + while i < n: + d[i] = val + i += 1 + + +def zero_grad(t0: Tensor | t.CPtr): + """torch.zero_grad: Grad 缓冲清零""" + if t0 is None or t0.Grad is None: + return + string.memset(t0.Grad, 0, t0.Rows * t0.Cols * sizet(8)) + + +# ------------------------------------------------------------ +# 确定性伪随机(LCG, minstd)与 Kaiming 均匀初始化 +# ------------------------------------------------------------ +_rng_state: double = 12345.0 +LCG_M: double = 2147483647.0 +LCG_A: double = 48271.0 + + +def manual_seed(seed: int64): + global _rng_state + _rng_state = double(seed % int64(2147483647)) + if _rng_state <= double(0.0): + _rng_state = double(12345.0) + + +def _next_rand() -> double: + """(0,1) 均匀随机""" + global _rng_state + _rng_state = vipermath.fmod(_rng_state * LCG_A, LCG_M) + return _rng_state / LCG_M + + +def uniform_fill(t0: Tensor | t.CPtr, lo: double, hi: double): + """U(lo, hi) 填充""" + if t0 is None or t0.Data is None: + return + d: double | t.CPtr = t0.Data + n: sizet = t0.Rows * t0.Cols + span: double = hi - lo + i: sizet = 0 + while i < n: + d[i] = lo + _next_rand() * span + i += 1 + + +def kaiming_uniform_fill(w: Tensor | t.CPtr, fan_in: sizet, + fan_out: sizet): + """PyTorch Linear 默认初始化: U(-sqrt(6/(fan_in+fan_out)), +...)""" + bound: double = vipermath.sqrt( + double(6.0) / double(fan_in + fan_out)) + uniform_fill(w, -bound, bound) + + +# ------------------------------------------------------------ +# 基础元素级运算(结合 vipersimd 4 宽指令) +# ------------------------------------------------------------ +def vector_mul_scalar4(src: t.CPtr, scalar_ptr: t.CPtr, dst: t.CPtr, + n: sizet): + """dst = src * scalar (逐 4 元素 simd_mul_scalar4d + 标量尾数)""" + if src is None or scalar_ptr is None or dst is None: + return + i: sizet = 0 + while i + 4 <= n: + vipersimd.simd_mul_scalar4d( + t.CPtr(uint64(src) + uint64(i) * 8), + scalar_ptr, + t.CPtr(uint64(dst) + uint64(i) * 8)) + i += 4 + sd: double | t.CPtr = src + dd: double | t.CPtr = dst + # 尾部标量 + sp: double | t.CPtr = scalar_ptr + sval: double = sp[0] + while i < n: + dd[i] = sd[i] * sval + i += 1 + + +def vector_add_inplace(a: t.CPtr, b: t.CPtr, n: sizet): + """a += b (逐 4 元素 simd_add_array 尾部标量)""" + if a is None or b is None: + return + tmp: t.CPtr = stdlib.malloc(8) + if tmp is None: + return + i: sizet = 0 + while i + 4 <= n: + vipersimd.simd_add_array( + t.CPtr(uint64(a) + uint64(i) * 8), + t.CPtr(uint64(b) + uint64(i) * 8), + t.CPtr(uint64(a) + uint64(i) * 8)) + i += 4 + da: double | t.CPtr = a + db: double | t.CPtr = b + while i < n: + da[i] = da[i] + db[i] + i += 1 + stdlib.free(tmp) + + +# ------------------------------------------------------------ +# 线性层: forward = x @ W^T + b (W: out x in, x: m x in, out: m x out) +# 内积按 k%4 用 vipersimd.simd_dot4d —— MNIST 主计算热点 +# ------------------------------------------------------------ +def linear_fwd(w: Tensor | t.CPtr, b: Tensor | t.CPtr, + x: Tensor | t.CPtr, out: Tensor | t.CPtr): + if w is None or b is None or x is None or out is None: + return + m: sizet = x.Rows + k: sizet = x.Cols + n: sizet = w.Rows + wd: double | t.CPtr = w.Data + bd: double | t.CPtr = b.Data + xd: double | t.CPtr = x.Data + od: double | t.CPtr = out.Data + acc_buf: t.CPtr = stdlib.malloc(8) + if acc_buf is None: + return + accp: double | t.CPtr = acc_buf + i: sizet = 0 + while i < m: + j: sizet = 0 + while j < n: + acc: double = bd[j] + kk: sizet = 0 + while kk + 4 <= k: + vipersimd.simd_dot4d( + t.CPtr(uint64(xd) + uint64(i * k + kk) * 8), + t.CPtr(uint64(wd) + uint64(j * k + kk) * 8), + acc_buf) + acc = acc + accp[0] + kk += 4 + while kk < k: + acc = acc + xd[i * k + kk] * wd[j * k + kk] + kk += 1 + od[i * n + j] = acc + j += 1 + i += 1 + stdlib.free(acc_buf) + + +# ------------------------------------------------------------ +# 线性层反向: 输入 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) +# ------------------------------------------------------------ +def linear_bwd(w: Tensor | t.CPtr, x: Tensor | t.CPtr, + g: Tensor | t.CPtr, dw: Tensor | t.CPtr, + db: Tensor | t.CPtr, dx: Tensor | t.CPtr, + accum: int = 0): + if w is None or x is None or g is None: + return + if dw is None or db is None or dx is None: + return + m: sizet = x.Rows + k: sizet = x.Cols + n: sizet = w.Rows + wd: double | t.CPtr = w.Data + xd: double | t.CPtr = x.Data + gd: double | t.CPtr = g.Data + dwd: double | t.CPtr = dw.Data + dbd: double | t.CPtr = db.Data + dxd: double | t.CPtr = dx.Data + + # dw/db/dx 清零(dx 恒清零: 它是本样本的输入梯度, 不跨样本累积) + # accum=1 时保留 dw/db 现值 —— 逐样本反向累积整批参数梯度的调用方用 + if accum == 0: + string.memset(dw.Data, 0, n * k * sizet(8)) + string.memset(db.Data, 0, n * sizet(8)) + string.memset(dx.Data, 0, m * k * sizet(8)) + + b: sizet = 0 + while b < m: + j: sizet = 0 + while j < n: + gj: double = gd[b * n + j] + if gj != double(0.0): + dbd[j] = dbd[j] + gj + i: sizet = 0 + while i < k: + dwd[j * k + i] = dwd[j * k + i] + gj * xd[b * k + i] + dxd[b * k + i] = dxd[b * k + i] + gj * wd[j * k + i] + i += 1 + j += 1 + b += 1 + + +# ------------------------------------------------------------ +# ReLU (simd_max4d 版本) 与反向 +# ------------------------------------------------------------ +def relu_fwd(x: Tensor | t.CPtr, out: Tensor | t.CPtr): + if x is None or out is None: + return + n: sizet = x.Rows * x.Cols + zero4: t.CPtr = stdlib.malloc(32) + if zero4 is None: + return + string.memset(zero4, 0, 32) + xd: double | t.CPtr = x.Data + od: double | t.CPtr = out.Data + i: sizet = 0 + while i + 4 <= n: + vipersimd.simd_max4d( + t.CPtr(uint64(xd) + uint64(i) * 8), + zero4, + t.CPtr(uint64(od) + uint64(i) * 8)) + i += 4 + while i < n: + if xd[i] > double(0.0): + od[i] = xd[i] + else: + od[i] = double(0.0) + i += 1 + stdlib.free(zero4) + + +def relu_backward(x: Tensor | t.CPtr, g: Tensor | t.CPtr, + gx: Tensor | t.CPtr): + """gx = g * (x > 0)""" + if x is None or g is None or gx is None: + return + n: sizet = x.Rows * x.Cols + xd: double | t.CPtr = x.Data + gd: double | t.CPtr = g.Data + od: double | t.CPtr = gx.Data + i: sizet = 0 + while i < n: + if xd[i] > double(0.0): + od[i] = gd[i] + else: + od[i] = double(0.0) + i += 1 + + +# ------------------------------------------------------------ +# softmax + CrossEntropy(融合, 数值稳定) —— MNIST 输出层 +# logits: (m, C), labels: m 个 int (0..C-1) +# loss 写入 *loss, 梯度(已按 1/m 归一)写入 grad (m, C) +# ------------------------------------------------------------ +def softmax_crossentropy(logits: Tensor | t.CPtr, + labels: int | t.CPtr, + n_labels: sizet, + loss: double | t.CPtr, + grad: Tensor | t.CPtr): + if logits is None or labels is None or grad is None: + return + if loss is None: + return + m: sizet = logits.Rows + c: sizet = logits.Cols + ld: double | t.CPtr = logits.Data + gd: double | t.CPtr = grad.Data + lb: int | t.CPtr = labels + total: double = double(0.0) + inv_m: double = double(1.0) / double(m) + b: sizet = 0 + while b < m: + # 数值稳定 softmax + mx: double = ld[b * c] + j: sizet = 1 + while j < c: + if ld[b * c + j] > mx: + mx = ld[b * c + j] + j += 1 + s: double = double(0.0) + j = 0 + while j < c: + s = s + vipermath.exp(ld[b * c + j] - mx) + j += 1 + y: int = lb[b] + # loss += -(logits[lab] - max - log(s)) + total = total - (ld[b * c + sizet(y)] - mx - vipermath.log(s)) + # 梯度: (softmax - onehot) / m + j = 0 + while j < c: + p: double = vipermath.exp(ld[b * c + j] - mx) / s + o: double = double(0.0) + if sizet(y) == j: + o = double(1.0) + gd[b * c + j] = (p - o) * inv_m + j += 1 + b += 1 + loss[0] = total + + +# ------------------------------------------------------------ +# 每行 argmax (分类预测) +# ------------------------------------------------------------ +def row_argmax(x: Tensor | t.CPtr, pred: int | t.CPtr): + if x is None or pred is None: + return + m: sizet = x.Rows + c: sizet = x.Cols + d: double | t.CPtr = x.Data + i: sizet = 0 + while i < m: + best: sizet = 0 + bv: double = d[i * c] + j: sizet = 1 + while j < c: + if d[i * c + j] > bv: + bv = d[i * c + j] + best = j + j += 1 + pred[i] = int(best) + i += 1 + + +# ------------------------------------------------------------ +# SGD: data -= lr * grad(全量梯度) / 重球法: v = m*v+g, data -= lr*v +# ------------------------------------------------------------ +def sgd_step(param: Tensor | t.CPtr, lr: double): + if param is None or param.Data is None or param.Grad is None: + return + d: double | t.CPtr = param.Data + g: double | t.CPtr = param.Grad + n: sizet = param.Rows * param.Cols + i: sizet = 0 + while i < n: + d[i] = d[i] - lr * g[i] + i += 1 + + +def sgd_step_momentum(param: Tensor | t.CPtr, 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 很慢且容易卡在低准确率。""" + if param is None or param.Data is None or param.Grad is None: + return + if vel is None: + sgd_step(param, lr) + return + d: double | t.CPtr = param.Data + g: double | t.CPtr = param.Grad + v: double | t.CPtr = vel + n: sizet = param.Rows * param.Cols + i: sizet = 0 + while i < n: + vv: double = momentum * v[i] + g[i] + v[i] = vv + d[i] = d[i] - lr * vv + i += 1 + + +# ------------------------------------------------------------ +# 逐元素乘加 (调试/扩展用, vipersimd fma_array) +# a = a*b + c +# ------------------------------------------------------------ +def fma_array_inplace(a: Tensor | t.CPtr, b: t.CPtr, c: t.CPtr): + if a is None or a.Data is None: + return + vipersimd.simd_fma_array(a.Data, b, c, a.Data, a.Rows * a.Cols) + + +# ------------------------------------------------------------ +# 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%。) +# ------------------------------------------------------------ +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) 同一写法。)""" + return t.CPtr(uint64(base) + uint64(elem) * 8) + + +def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr, + n: sizet, cin: sizet, hin: sizet, win: sizet, + 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 同理, 整除向下取整)""" + if x is None or w is None or out is None: + return + hout: sizet = (hin + pad * 2 - k) / stride + 1 + wout: sizet = (win + pad * 2 - k) / stride + 1 + xd: double | t.CPtr = x + wd: double | t.CPtr = w + od: double | t.CPtr = out + kk: sizet = cin * k * k + # SIMD 安全区间(见文件头说明) + 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 路累加器 + if simd_on != 0: + wb = stdlib.malloc(kk * 32) + t4 = stdlib.malloc(32) + if wb is None or t4 is None: + if wb is not None: + stdlib.free(wb) + if t4 is not None: + stdlib.free(t4) + wb = None + t4 = None + simd_on = 0 + wbd: double | t.CPtr = wb + t4d: double | t.CPtr = t4 + + bi: sizet = 0 + while bi < n: + co: sizet = 0 + while co < cout: + bias: double = double(0.0) + if b is not None: + bd: double | t.CPtr = b + bias = bd[co] + # 权重标量预广播: 每个 (ci,ky,kx) 一个 4 路副本, 本通道所有输出 + # 行/列共用。否则 vbroadcastsd(4 次 store) 会跟着 4 路循环重复, + # 把 SIMD 的收益吃掉一半。 + if simd_on != 0: + wb4: sizet = 0 + while wb4 < kk: + wq: double = wd[co * kk + wb4] + wbd[wb4 * 4] = wq + wbd[wb4 * 4 + 1] = wq + wbd[wb4 * 4 + 2] = wq + wbd[wb4 * 4 + 3] = wq + wb4 += 1 + oy: sizet = 0 + while oy < hout: + oy0: sizet = oy * stride + ox: sizet = 0 + while ox < wout: + if simd_on != 0 and ox >= pad and ox <= smax and ox + 4 <= wout: + # ---- 4 路: 一次算 4 个相邻输出列 ---- + t4d[0] = bias + t4d[1] = bias + t4d[2] = bias + t4d[3] = bias + ci: sizet = 0 + while ci < cin: + ky: sizet = 0 + while ky < k: + iy: sizet = oy0 + ky + if iy >= pad: + yy: sizet = iy - pad + if yy < hin: + xrow: sizet = ((bi * cin + ci) * hin + yy) * win + kx: sizet = 0 + while kx < k: + vipersimd.simd_fma4d( + _dptr(x, xrow + ox + kx - pad), + _dptr(wb, ((ci * k + ky) * k + kx) * 4), + t4, t4) + kx += 1 + ky += 1 + ci += 1 + ob: sizet = ((bi * cout + co) * hout + oy) * wout + ox + od[ob] = t4d[0] + od[ob + 1] = t4d[1] + od[ob + 2] = t4d[2] + od[ob + 3] = t4d[3] + ox += 4 + else: + # ---- 标量: 边界列 / stride != 1 ---- + ox0: sizet = ox * stride + acc: double = bias + ci2: sizet = 0 + while ci2 < cin: + ky2: sizet = 0 + while ky2 < k: + iy2: sizet = oy0 + ky2 + if iy2 >= pad: + yy2: sizet = iy2 - pad + if yy2 < hin: + kx2: sizet = 0 + while kx2 < k: + ix2: sizet = ox0 + kx2 + if ix2 >= pad: + xx2: sizet = ix2 - pad + if xx2 < win: + acc = acc + \ + wd[co * kk + (ci2 * k + ky2) * k + kx2] * \ + xd[((bi * cin + ci2) * hin + yy2) * win + xx2] + kx2 += 1 + ky2 += 1 + ci2 += 1 + od[((bi * cout + co) * hout + oy) * wout + ox] = acc + ox += 1 + oy += 1 + co += 1 + bi += 1 + if wb is not None: + stdlib.free(wb) + if t4 is not None: + stdlib.free(t4) + + +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 成标量。""" + if x is None or dout is None or dw is None: + return + hout: sizet = (hin + pad * 2 - k) / stride + 1 + wout: sizet = (win + pad * 2 - k) / stride + 1 + xd: double | t.CPtr = x + gd: double | t.CPtr = dout + dwd: double | t.CPtr = dw + dbd: double | t.CPtr = None + if db is not None: + dbd = db + kk: sizet = cin * k * k + simd_on: int = 0 + smax: sizet = 0 + if stride == 1 and win + pad >= k + 3: + simd_on = 1 + smax = win + pad - (k + 3) + acc: t.CPtr = None + if simd_on != 0: + acc = stdlib.malloc(32) + if acc is None: + simd_on = 0 + accd: double | t.CPtr = acc + + bi: sizet = 0 + while bi < n: + co: sizet = 0 + while co < cout: + gp: sizet = (bi * cout + co) * hout * wout + # ---- db[co] += sum(g): g 的整个通道平面是连续的 ---- + if dbd is not None: + tot0: double = double(0.0) + if simd_on != 0: + accd[0] = double(0.0) + accd[1] = double(0.0) + accd[2] = double(0.0) + accd[3] = double(0.0) + gcnt: sizet = hout * wout + gof: sizet = 0 + while gof + 4 <= gcnt: + if simd_on != 0: + vipersimd.simd_add4d(_dptr(dout, gp + gof), acc, acc) + else: + tot0 = tot0 + gd[gp + gof] + tot0 = tot0 + gd[gp + gof + 1] + tot0 = tot0 + gd[gp + gof + 2] + tot0 = tot0 + gd[gp + gof + 3] + gof += 4 + while gof < gcnt: + tot0 = tot0 + gd[gp + gof] + gof += 1 + if simd_on != 0: + vipersimd.simd_hsum4d(acc, acc) + tot0 = tot0 + accd[0] + dbd[co] = dbd[co] + tot0 + # ---- dw[co][ci][ky][kx] ---- + ci: sizet = 0 + while ci < cin: + ky: sizet = 0 + while ky < k: + kx: sizet = 0 + while kx < k: + tot: double = double(0.0) + if simd_on != 0: + accd[0] = double(0.0) + accd[1] = double(0.0) + accd[2] = double(0.0) + accd[3] = double(0.0) + oy: sizet = 0 + while oy < hout: + iy: sizet = oy * stride + ky + if iy >= pad: + yy: sizet = iy - pad + if yy < hin: + xrow: sizet = ((bi * cin + ci) * hin + yy) * win + goff: sizet = gp + oy * wout + ox: sizet = 0 + while ox < wout: + if simd_on != 0 and ox >= pad and ox <= smax and ox + 4 <= wout: + vipersimd.simd_fma4d( + _dptr(dout, goff + ox), + _dptr(x, xrow + ox + kx - pad), + acc, acc) + ox += 4 + else: + ix: sizet = ox * stride + kx + if ix >= pad: + xx: sizet = ix - pad + if xx < win: + tot = tot + gd[goff + ox] * \ + xd[xrow + xx] + ox += 1 + oy += 1 + if simd_on != 0: + vipersimd.simd_hsum4d(acc, acc) + tot = tot + accd[0] + wj: sizet = co * kk + (ci * k + ky) * k + kx + dwd[wj] = dwd[wj] + tot + kx += 1 + ky += 1 + ci += 1 + co += 1 + bi += 1 + if acc is not None: + stdlib.free(acc) + + +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)。""" + if w is None or dout is None or dx is None: + return + hout: sizet = (hin + pad * 2 - k) / stride + 1 + wout: sizet = (win + pad * 2 - k) / stride + 1 + wd: double | t.CPtr = w + gd: double | t.CPtr = dout + dxd: double | t.CPtr = dx + string.memset(dx, 0, n * cin * hin * win * sizet(8)) + kk: sizet = cin * k * k + 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 路广播副本(cout*kk 个标量) + if simd_on != 0: + wb = stdlib.malloc(cout * kk * 32) + if wb is None: + simd_on = 0 + wbd: double | t.CPtr = wb + if simd_on != 0: + # 权重在一次调用内不变 → 整体预广播一次(注意别在 4 路循环里重复 + # vbroadcastsd: 那是 4 次 store, 会把 SIMD 的收益吃掉一半) + wjn: sizet = cout * kk + wj4: sizet = 0 + while wj4 < wjn: + wq2: double = wd[wj4] + wbd[wj4 * 4] = wq2 + wbd[wj4 * 4 + 1] = wq2 + wbd[wj4 * 4 + 2] = wq2 + wbd[wj4 * 4 + 3] = wq2 + wj4 += 1 + + bi: sizet = 0 + while bi < n: + ci: sizet = 0 + while ci < cin: + ky: sizet = 0 + while ky < k: + oy: sizet = 0 + while oy < hout: + iy: sizet = oy * stride + ky + if iy >= pad: + yy: sizet = iy - pad + if yy < hin: + dxrow: sizet = ((bi * cin + ci) * hin + yy) * win + ox: sizet = 0 + while ox < wout: + if simd_on != 0 and ox >= pad and ox <= smax and ox + 4 <= wout: + co: sizet = 0 + while co < cout: + kx: sizet = 0 + while kx < k: + goff: sizet = ((bi * cout + co) * hout + oy) * wout + ox + doff: sizet = dxrow + ox + kx - pad + vipersimd.simd_fma4d( + _dptr(dout, goff), + _dptr(wb, (co * kk + (ci * k + ky) * k + kx) * 4), + _dptr(dx, doff), _dptr(dx, doff)) + kx += 1 + co += 1 + ox += 4 + else: + co2: sizet = 0 + while co2 < cout: + kx2: sizet = 0 + while kx2 < k: + ix: sizet = ox * stride + kx2 + if ix >= pad: + xx: sizet = ix - pad + if xx < win: + xi: sizet = dxrow + xx + gi: sizet = ((bi * cout + co2) * hout + oy) * wout + ox + dxd[xi] = dxd[xi] + \ + gd[gi] * wd[co2 * kk + (ci * k + ky) * k + kx2] + kx2 += 1 + co2 += 1 + ox += 1 + oy += 1 + ky += 1 + ci += 1 + bi += 1 + if wb is not None: + stdlib.free(wb) + + +# ------------------------------------------------------------ +# MaxPool2d (k x k, stride 通常 = 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)""" + if x is None or out is None or argmax is None: + return + hout: sizet = (hin - k) / stride + 1 + wout: sizet = (win - k) / stride + 1 + xd: double | t.CPtr = x + od: double | t.CPtr = out + am: int | t.CPtr = argmax + bi: sizet = 0 + while bi < n: + ch: sizet = 0 + while ch < c: + plano: sizet = (bi * c + ch) * hin * win + oy: sizet = 0 + while oy < hout: + oy0: sizet = oy * stride + ox: sizet = 0 + while ox < wout: + ox0: sizet = ox * stride + best: double = double(0.0) + bidx: int = 0 + first: int = 1 + ky: sizet = 0 + while ky < k: + yy: sizet = oy0 + ky + kx: sizet = 0 + while kx < k: + xx: sizet = ox0 + kx + v: double = xd[plano + yy * win + xx] + if first != 0 or v > best: + best = v + bidx = int(yy * win + xx) + first = 0 + kx += 1 + ky += 1 + oi: sizet = (bi * c + ch) * hout * wout + oy * wout + ox + od[oi] = best + am[oi] = bidx + ox += 1 + oy += 1 + ch += 1 + bi += 1 + + +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""" + if dout is None or argmax is None or dx is None: + return + hout: sizet = (hin - k) / stride + 1 + wout: sizet = (win - k) / stride + 1 + gd: double | t.CPtr = dout + am: int | t.CPtr = argmax + dxd: double | t.CPtr = dx + string.memset(dx, 0, n * c * hin * win * sizet(8)) + bi: sizet = 0 + while bi < n: + ch: sizet = 0 + while ch < c: + plano: sizet = (bi * c + ch) * hin * win + oy: sizet = 0 + while oy < hout: + ox: sizet = 0 + while ox < wout: + oi: sizet = (bi * c + ch) * hout * wout + oy * wout + ox + tgt: sizet = plano + sizet(am[oi]) + dxd[tgt] = dxd[tgt] + gd[oi] + ox += 1 + oy += 1 + ch += 1 + bi += 1 diff --git a/vector.vp b/vector.vp index e32a74b..ef17003 100644 --- a/vector.vp +++ b/vector.vp @@ -4,7 +4,7 @@ import memhub class Vector[T](): - data: t.CVoid | t.CPtr + data: t.CPtr length: t.CSizeT capacity: t.CSizeT elem_size: t.CSizeT @@ -23,7 +23,7 @@ class Vector[T](): self._grow() if self.length >= self.capacity: return offset: t.CUInt64T = t.CUInt64T(self.data) + self.length * self.elem_size - dst: t.CVoid | t.CPtr = t.CVoid(offset, t.CPtr) + dst: t.CPtr = t.CPtr(offset) c.DerefAs(dst, value) self.length += 1 @@ -32,7 +32,7 @@ class Vector[T](): new_capacity: t.CSizeT = self.capacity * 2 if new_capacity == 0: new_capacity = 4 new_size: t.CSizeT = new_capacity * self.elem_size - new_data: t.CVoid | t.CPtr = self.pool.realloc(self.data, new_size) + new_data: t.CPtr = self.pool.realloc(self.data, new_size) if new_data == None: return self.data = new_data self.capacity = new_capacity @@ -47,7 +47,7 @@ class Vector[T](): if index >= self.length: return if self.data == None: return offset: t.CUInt64T = t.CUInt64T(self.data) + index * self.elem_size - dst: t.CVoid | t.CPtr = t.CVoid(offset, t.CPtr) + dst: t.CPtr = t.CPtr(offset) c.DerefAs(dst, value) def len(self) -> t.CSizeT: diff --git a/viperlib.vp b/viperlib.vp index 6a93ccf..a1689e4 100644 --- a/viperlib.vp +++ b/viperlib.vp @@ -13,8 +13,6 @@ def get_digit_count(num: t.CUnsignedInt, base: t.CInt) -> t.CStatic | t.CInt: return count # 返回类型必须与 CRT `sprintf` 以及 includes/stdio.vp 的 -# `extern def sprintf(...) -> int` 一致:此前写作 t.CVoid → 本模块产出 -# `define void @sprintf` 与别处 `declare i32 @sprintf` 同符号签名冲突(N13)。 # 语义上直接返回 vsnprintf 的写入长度(与 CRT 一致)。 export def sprintf(buf: t.CChar | t.CPtr, rule: str, *args) -> t.CInt: if buf is None or rule is None: return 0 diff --git a/vipersimd.vp b/vipersimd.vp index 842f933..ed9bcf4 100644 --- a/vipersimd.vp +++ b/vipersimd.vp @@ -5,7 +5,7 @@ import t, c # AVX2 double precision vector operations (4 x double, 256-bit YMM) # ============================================================ -def simd_add4d(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_add4d(a: t.CPtr, b: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = a[0:4] + b[0:4]""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vmovupd ymm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -14,7 +14,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_sub4d(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_sub4d(a: t.CPtr, b: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = a[0:4] - b[0:4]""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vmovupd ymm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -23,7 +23,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_mul4d(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_mul4d(a: t.CPtr, b: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = a[0:4] * b[0:4]""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vmovupd ymm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -32,7 +32,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_div4d(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_div4d(a: t.CPtr, b: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = a[0:4] / b[0:4]""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vmovupd ymm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -41,7 +41,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_sqrt4d(a: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_sqrt4d(a: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = sqrt(a[0:4])""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vsqrtpd ymm0, ymm0 @@ -49,7 +49,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0']) -def simd_neg4d(a: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_neg4d(a: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = -a[0:4] (flip sign bit via XOR)""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vpcmpeqd ymm1, ymm1, ymm1 @@ -59,7 +59,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_abs4d(a: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_abs4d(a: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = |a[0:4]| (clear sign bit via ANDN)""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vpcmpeqd ymm1, ymm1, ymm1 @@ -69,7 +69,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_max4d(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_max4d(a: t.CPtr, b: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = max(a[0:4], b[0:4])""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vmovupd ymm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -78,7 +78,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_min4d(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_min4d(a: t.CPtr, b: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = min(a[0:4], b[0:4])""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vmovupd ymm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -87,7 +87,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_mul_scalar4d(a: t.CPtr, scalar: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_mul_scalar4d(a: t.CPtr, scalar: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = a[0:4] * scalar (broadcast)""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vbroadcastsd ymm1, [{c.AsmInp(scalar, t.ASM_DESCR.REG_ANY)}] @@ -96,7 +96,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_add_scalar4d(a: t.CPtr, scalar: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_add_scalar4d(a: t.CPtr, scalar: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = a[0:4] + scalar (broadcast)""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vbroadcastsd ymm1, [{c.AsmInp(scalar, t.ASM_DESCR.REG_ANY)}] @@ -105,7 +105,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_hsum4d(a: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_hsum4d(a: t.CPtr, out: t.CPtr): """AVX2: out[0] = a[0]+a[1]+a[2]+a[3] (horizontal sum)""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vextractf128 xmm1, ymm0, 1 @@ -115,7 +115,7 @@ vmovsd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], xmm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_dot4d(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_dot4d(a: t.CPtr, b: t.CPtr, out: t.CPtr): """AVX2: out[0] = dot(a[0:4], b[0:4])""" c.Asm(f"""vmovupd ymm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] vmovupd ymm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -127,7 +127,7 @@ vmovsd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], xmm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1']) -def simd_fma4d(a: t.CPtr, b: t.CPtr, c_val: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_fma4d(a: t.CPtr, b: t.CPtr, c_val: t.CPtr, out: t.CPtr): """AVX2: out[0:4] = a[0:4]*b[0:4] + c[0:4] (fused multiply-add) Uses vfmadd213pd: ymm0 = ymm1 * ymm0 + ymm2 Load order: b->ymm0, a->ymm1, c->ymm2""" @@ -143,7 +143,7 @@ vmovupd [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], ymm0""", # SSE2 single precision vector operations (4 x float, 128-bit XMM) # ============================================================ -def simd_add4f(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_add4f(a: t.CPtr, b: t.CPtr, out: t.CPtr): """SSE2: out[0:4] = a[0:4] + b[0:4] (float)""" c.Asm(f"""movups xmm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] movups xmm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -152,7 +152,7 @@ movups [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], xmm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'xmm0', 'xmm1']) -def simd_sub4f(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_sub4f(a: t.CPtr, b: t.CPtr, out: t.CPtr): """SSE2: out[0:4] = a[0:4] - b[0:4] (float)""" c.Asm(f"""movups xmm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] movups xmm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -161,7 +161,7 @@ movups [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], xmm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'xmm0', 'xmm1']) -def simd_mul4f(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_mul4f(a: t.CPtr, b: t.CPtr, out: t.CPtr): """SSE2: out[0:4] = a[0:4] * b[0:4] (float)""" c.Asm(f"""movups xmm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] movups xmm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -170,7 +170,7 @@ movups [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], xmm0""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'xmm0', 'xmm1']) -def simd_div4f(a: t.CPtr, b: t.CPtr, out: t.CPtr) -> t.CVoid: +def simd_div4f(a: t.CPtr, b: t.CPtr, out: t.CPtr): """SSE2: out[0:4] = a[0:4] / b[0:4] (float)""" c.Asm(f"""movups xmm0, [{c.AsmInp(a, t.ASM_DESCR.REG_ANY)}] movups xmm1, [{c.AsmInp(b, t.ASM_DESCR.REG_ANY)}] @@ -183,7 +183,7 @@ movups [{c.AsmInp(out, t.ASM_DESCR.REG_ANY)}], xmm0""", # Batch array operations (AVX2 loop, process N double values) # ============================================================ -def simd_add_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def simd_add_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT): """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 链,最大化内存级并行、压榨访存口。 @@ -256,7 +256,7 @@ vzeroupper""", 'ymm12', 'ymm13', 'ymm14', 'ymm15', 'rcx', 'r8', 'r9', 'r10', 'r11']) -def simd_sub_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def simd_sub_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT): """AVX2 batch: out[i:i+4] = a[i:i+4] - b[i:i+4]""" c.Asm(f"""mov rcx, {c.AsmInp(n, t.ASM_DESCR.REG_ANY)} shr rcx, 2 @@ -280,7 +280,7 @@ vzeroupper""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1', 'rcx', 'r8', 'r9', 'r10']) -def simd_mul_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def simd_mul_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT): """AVX2 batch: out[i:i+4] = a[i:i+4] * b[i:i+4] ×8 双缓冲(同 simd_add_array)。""" c.Asm(f"""mov rcx, {c.AsmInp(n, t.ASM_DESCR.REG_ANY)} @@ -350,7 +350,7 @@ vzeroupper""", 'ymm12', 'ymm13', 'ymm14', 'ymm15', 'rcx', 'r8', 'r9', 'r10', 'r11']) -def simd_fma_array(a: t.CPtr, b: t.CPtr, c_val: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def simd_fma_array(a: t.CPtr, b: t.CPtr, c_val: t.CPtr, out: t.CPtr, n: t.CSizeT): """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, @@ -428,7 +428,7 @@ vzeroupper""", 'ymm12', 'ymm13', 'ymm14', 'ymm15', 'rcx', 'rdx', 'r8', 'r9', 'r10', 'r11']) -def scalar_fma_array(a: t.CPtr, b: t.CPtr, c_val: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def scalar_fma_array(a: t.CPtr, b: t.CPtr, c_val: t.CPtr, out: t.CPtr, n: t.CSizeT): """标量(逐元素)融合乘加: out[i] = a[i]*b[i] + c_val[i], 一次只算 1 个 double。 与 simd_fma_array 是同一数学内核的"未向量化"对照实现: @@ -461,7 +461,7 @@ jnz .Lsca_loop 'rcx', 'rdx', 'r8', 'r9', 'r10']) -def scalar_add_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def scalar_add_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT): """标量(逐元素)加法: out[i] = a[i] + b[i], 一次 1 个 double。 `simd_add_array` 的未向量化对照: 标量 `vaddsd` vs SIMD `vaddpd`, @@ -485,7 +485,7 @@ jnz .Lsca_add_loop op=[t.ASM_DESCR.CLOBBER_MEMORY, 'xmm0', 'rcx', 'r8', 'r9', 'r10']) -def scalar_mul_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def scalar_mul_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT): """标量(逐元素)乘法: out[i] = a[i] * b[i], 一次 1 个 double。 `simd_mul_array` 的未向量化对照: 标量 `vmulsd` vs SIMD `vmulpd`, @@ -509,7 +509,7 @@ jnz .Lsca_mul_loop op=[t.ASM_DESCR.CLOBBER_MEMORY, 'xmm0', 'rcx', 'r8', 'r9', 'r10']) -def scalar_sqrt_array(a: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def scalar_sqrt_array(a: t.CPtr, out: t.CPtr, n: t.CSizeT): """标量(逐元素)平方根: out[i] = sqrt(a[i]), 一次 1 个 double。 `simd_sqrt_array` 的未向量化对照: 标量 `vsqrtsd` vs SIMD `vsqrtpd`, @@ -531,7 +531,7 @@ jnz .Lsca_sqrt_loop op=[t.ASM_DESCR.CLOBBER_MEMORY, 'xmm0', 'rcx', 'r8', 'r10']) -def simd_div_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def simd_div_array(a: t.CPtr, b: t.CPtr, out: t.CPtr, n: t.CSizeT): """AVX2 batch: out[i:i+4] = a[i:i+4] / b[i:i+4]""" c.Asm(f"""mov rcx, {c.AsmInp(n, t.ASM_DESCR.REG_ANY)} shr rcx, 2 @@ -555,7 +555,7 @@ vzeroupper""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm1', 'rcx', 'r8', 'r9', 'r10']) -def simd_sqrt_array(a: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def simd_sqrt_array(a: t.CPtr, out: t.CPtr, n: t.CSizeT): """AVX2 batch: out[i:i+4] = sqrt(a[i:i+4]) ×4 展开:4 路独立 vsqrtpd 链隐藏平方根长延迟(吞吐受限场景显著提速)。""" c.Asm(f"""mov rcx, {c.AsmInp(n, t.ASM_DESCR.REG_ANY)} @@ -600,7 +600,7 @@ vzeroupper""", 'rcx', 'r8', 'r10', 'r11']) -def simd_abs_array(a: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def simd_abs_array(a: t.CPtr, out: t.CPtr, n: t.CSizeT): """AVX2 batch: out[i:i+4] = |a[i:i+4]|""" c.Asm(f"""mov rcx, {c.AsmInp(n, t.ASM_DESCR.REG_ANY)} shr rcx, 2 @@ -623,7 +623,7 @@ vzeroupper""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm2', 'rcx', 'r8', 'r10']) -def simd_neg_array(a: t.CPtr, out: t.CPtr, n: t.CSizeT) -> t.CVoid: +def simd_neg_array(a: t.CPtr, out: t.CPtr, n: t.CSizeT): """AVX2 batch: out[i:i+4] = -a[i:i+4] (flip sign bit via XOR)""" c.Asm(f"""mov rcx, {c.AsmInp(n, t.ASM_DESCR.REG_ANY)} shr rcx, 2 @@ -646,7 +646,7 @@ vzeroupper""", op=[t.ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm2', 'rcx', 'r8', 'r10']) -def simd_dot_array(a: t.CPtr, b: t.CPtr, n: t.CSizeT, out: t.CPtr) -> t.CVoid: +def simd_dot_array(a: t.CPtr, b: t.CPtr, n: t.CSizeT, out: t.CPtr): """AVX2 batch: out[0] = dot(a[0:n], b[0:n]) using FMA accumulation""" c.Asm(f"""mov rcx, {c.AsmInp(n, t.ASM_DESCR.REG_ANY)} shr rcx, 2 diff --git a/vqt6/_qtcore.vp b/vqt6/_qtcore.vp index f95816c..f9f44b6 100644 --- a/vqt6/_qtcore.vp +++ b/vqt6/_qtcore.vp @@ -39,8 +39,8 @@ def vqt6_asset_url_utf8(buf: t.CChar | t.CPtr, buf_size: t.CSizeT, name: t.CChar # ============================================================ # QByteArray — 字节数组 # ============================================================ -def vqt6_qbytearray_create(data: t.CVoid | t.CPtr, size: t.CSizeT) -> QtByteArrayPtr | t.State: pass -def vqt6_qbytearray_data(qba: QtByteArrayPtr, out_size: t.CSizeT | t.CPtr) -> t.CVoid | t.CPtr | t.State: pass +def vqt6_qbytearray_create(data: t.CPtr, size: t.CSizeT) -> QtByteArrayPtr | t.State: pass +def vqt6_qbytearray_data(qba: QtByteArrayPtr, out_size: t.CSizeT | t.CPtr) -> t.CPtr | t.State: pass def vqt6_qbytearray_size(qba: QtByteArrayPtr) -> t.CSizeT | t.State: pass def vqt6_qbytearray_free(qba: QtByteArrayPtr) -> t.State: pass @@ -155,7 +155,7 @@ def vqt6_qbytearraylist_free(list: QtByteArrayListPtr) -> t.State: pass # 用于从 C++ 端回调 Viper 代码 # Viper 注册一个 slot 函数(用整数 ID 标识),C++ 在触发信号时通过 ID 查找 # typedef void (*vqt6_slot_callback_t)(int slot_id, void* user_data); -def vqt6_register_slot(slot_id: t.CInt, user_data: t.CVoid | t.CPtr) -> t.State: pass +def vqt6_register_slot(slot_id: t.CInt, user_data: t.CPtr) -> t.State: pass def vqt6_unregister_slot(slot_id: t.CInt) -> t.State: pass def vqt6_trigger_slot(slot_id: t.CInt) -> t.State: pass # 测试用 diff --git a/vqt6/_qtgui.vp b/vqt6/_qtgui.vp index 80f0094..2f597a9 100644 --- a/vqt6/_qtgui.vp +++ b/vqt6/_qtgui.vp @@ -9,229 +9,231 @@ from vqt6._types import * # ============================================================ # QColor — 颜色 # ============================================================ -def vqt6_qcolor_create_rgb(r: t.CInt, g: t.CInt, b: t.CInt) -> QtColorPtr | t.State: pass -def vqt6_qcolor_create_rgba(r: t.CInt, g: t.CInt, b: t.CInt, a: t.CInt) -> QtColorPtr | t.State: pass -def vqt6_qcolor_create_name(name: QtStringPtr) -> QtColorPtr | t.State: pass -def vqt6_qcolor_create_hex(hex_str: QtStringPtr) -> QtColorPtr | t.State: pass -def vqt6_qcolor_red(c: QtColorPtr) -> t.CInt | t.State: pass -def vqt6_qcolor_green(c: QtColorPtr) -> t.CInt | t.State: pass -def vqt6_qcolor_blue(c: QtColorPtr) -> t.CInt | t.State: pass -def vqt6_qcolor_alpha(c: QtColorPtr) -> t.CInt | t.State: pass -def vqt6_qcolor_set_red(c: QtColorPtr, r: t.CInt) -> t.State: pass -def vqt6_qcolor_set_green(c: QtColorPtr, g: t.CInt) -> t.State: pass -def vqt6_qcolor_set_blue(c: QtColorPtr, b: t.CInt) -> t.State: pass -def vqt6_qcolor_set_alpha(c: QtColorPtr, a: t.CInt) -> t.State: pass -def vqt6_qcolor_set_rgb(c: QtColorPtr, r: t.CInt, g: t.CInt, b: t.CInt) -> t.State: pass -def vqt6_qcolor_set_rgba(c: QtColorPtr, r: t.CInt, g: t.CInt, b: t.CInt, a: t.CInt) -> t.State: pass -def vqt6_qcolor_name(c: QtColorPtr) -> QtStringPtr | t.State: pass -def vqt6_qcolor_is_valid(c: QtColorPtr) -> t.CInt | t.State: pass -def vqt6_qcolor_free(c: QtColorPtr) -> t.State: pass +extern def vqt6_qcolor_create_rgb(r: t.CInt, g: t.CInt, b: t.CInt) -> QtColorPtr +extern def vqt6_qcolor_create_rgba(r: t.CInt, g: t.CInt, b: t.CInt, a: t.CInt) -> QtColorPtr +extern def vqt6_qcolor_create_name(name: QtStringPtr) -> QtColorPtr +extern def vqt6_qcolor_create_hex(hex_str: QtStringPtr) -> QtColorPtr +extern def vqt6_qcolor_red(c: QtColorPtr) -> t.CInt +extern def vqt6_qcolor_green(c: QtColorPtr) -> t.CInt +extern def vqt6_qcolor_blue(c: QtColorPtr) -> t.CInt +extern def vqt6_qcolor_alpha(c: QtColorPtr) -> t.CInt +extern def vqt6_qcolor_set_red(c: QtColorPtr, r: t.CInt) +extern def vqt6_qcolor_set_green(c: QtColorPtr, g: t.CInt) +extern def vqt6_qcolor_set_blue(c: QtColorPtr, b: t.CInt) +extern def vqt6_qcolor_set_alpha(c: QtColorPtr, a: t.CInt) +extern def vqt6_qcolor_set_rgb(c: QtColorPtr, r: t.CInt, g: t.CInt, b: t.CInt) +extern def vqt6_qcolor_set_rgba(c: QtColorPtr, r: t.CInt, g: t.CInt, b: t.CInt, a: t.CInt) +extern def vqt6_qcolor_name(c: QtColorPtr) -> QtStringPtr +extern def vqt6_qcolor_is_valid(c: QtColorPtr) -> t.CInt +extern def vqt6_qcolor_free(c: QtColorPtr) # ============================================================ # QFont — 字体 # ============================================================ -def vqt6_qfont_create() -> QtFontPtr | t.State: pass -def vqt6_qfont_create_with_family(family: QtStringPtr) -> QtFontPtr | t.State: pass -def vqt6_qfont_create_with_family_size(family: QtStringPtr, point_size: t.CInt) -> QtFontPtr | t.State: pass -def vqt6_qfont_set_family(font: QtFontPtr, family: QtStringPtr) -> t.State: pass -def vqt6_qfont_family(font: QtFontPtr) -> QtStringPtr | t.State: pass -def vqt6_qfont_set_point_size(font: QtFontPtr, point_size: t.CInt) -> t.State: pass -def vqt6_qfont_point_size(font: QtFontPtr) -> t.CInt | t.State: pass -def vqt6_qfont_set_pixel_size(font: QtFontPtr, pixel_size: t.CInt) -> t.State: pass -def vqt6_qfont_pixel_size(font: QtFontPtr) -> t.CInt | t.State: pass -def vqt6_qfont_set_bold(font: QtFontPtr, bold: t.CInt) -> t.State: pass -def vqt6_qfont_bold(font: QtFontPtr) -> t.CInt | t.State: pass -def vqt6_qfont_set_italic(font: QtFontPtr, italic: t.CInt) -> t.State: pass -def vqt6_qfont_italic(font: QtFontPtr) -> t.CInt | t.State: pass -def vqt6_qfont_set_underline(font: QtFontPtr, underline: t.CInt) -> t.State: pass -def vqt6_qfont_underline(font: QtFontPtr) -> t.CInt | t.State: pass -def vqt6_qfont_set_strike_out(font: QtFontPtr, strike_out: t.CInt) -> t.State: pass -def vqt6_qfont_strike_out(font: QtFontPtr) -> t.CInt | t.State: pass -def vqt6_qfont_set_weight(font: QtFontPtr, weight: t.CInt) -> t.State: pass -def vqt6_qfont_weight(font: QtFontPtr) -> t.CInt | t.State: pass -def vqt6_qfont_free(font: QtFontPtr) -> t.State: pass +extern def vqt6_qfont_create() -> QtFontPtr +extern def vqt6_qfont_create_with_family(family: QtStringPtr) -> QtFontPtr +extern def vqt6_qfont_create_with_family_size(family: QtStringPtr, point_size: t.CInt) -> QtFontPtr +extern def vqt6_qfont_set_family(font: QtFontPtr, family: QtStringPtr) +extern def vqt6_qfont_family(font: QtFontPtr) -> QtStringPtr +extern def vqt6_qfont_set_point_size(font: QtFontPtr, point_size: t.CInt) +extern def vqt6_qfont_point_size(font: QtFontPtr) -> t.CInt +extern def vqt6_qfont_set_pixel_size(font: QtFontPtr, pixel_size: t.CInt) +extern def vqt6_qfont_pixel_size(font: QtFontPtr) -> t.CInt +extern def vqt6_qfont_set_bold(font: QtFontPtr, bold: t.CInt) +extern def vqt6_qfont_bold(font: QtFontPtr) -> t.CInt +extern def vqt6_qfont_set_italic(font: QtFontPtr, italic: t.CInt) +extern def vqt6_qfont_italic(font: QtFontPtr) -> t.CInt +extern def vqt6_qfont_set_underline(font: QtFontPtr, underline: t.CInt) +extern def vqt6_qfont_underline(font: QtFontPtr) -> t.CInt +extern def vqt6_qfont_set_strike_out(font: QtFontPtr, strike_out: t.CInt) +extern def vqt6_qfont_strike_out(font: QtFontPtr) -> t.CInt +extern def vqt6_qfont_set_weight(font: QtFontPtr, weight: t.CInt) +extern def vqt6_qfont_weight(font: QtFontPtr) -> t.CInt +extern def vqt6_qfont_free(font: QtFontPtr) # ============================================================ # QFontMetrics — 字体度量 # ============================================================ -def vqt6_qfontmetrics_create(font: QtFontPtr) -> QtFontMetricsPtr | t.State: pass -def vqt6_qfontmetrics_width(fm: QtFontMetricsPtr, text: QtStringPtr) -> t.CInt | t.State: pass -def vqt6_qfontmetrics_height(fm: QtFontMetricsPtr) -> t.CInt | t.State: pass -def vqt6_qfontmetrics_ascent(fm: QtFontMetricsPtr) -> t.CInt | t.State: pass -def vqt6_qfontmetrics_descent(fm: QtFontMetricsPtr) -> t.CInt | t.State: pass -def vqt6_qfontmetrics_free(fm: QtFontMetricsPtr) -> t.State: pass +extern def vqt6_qfontmetrics_create(font: QtFontPtr) -> QtFontMetricsPtr +extern def vqt6_qfontmetrics_width(fm: QtFontMetricsPtr, text: QtStringPtr) -> t.CInt +extern def vqt6_qfontmetrics_height(fm: QtFontMetricsPtr) -> t.CInt +extern def vqt6_qfontmetrics_ascent(fm: QtFontMetricsPtr) -> t.CInt +extern def vqt6_qfontmetrics_descent(fm: QtFontMetricsPtr) -> t.CInt +extern def vqt6_qfontmetrics_free(fm: QtFontMetricsPtr) # ============================================================ # QPixmap — 像素图 # ============================================================ -def vqt6_qpixmap_create() -> QtPixmapPtr | t.State: pass -def vqt6_qpixmap_create_with_size(w: t.CInt, h: t.CInt) -> QtPixmapPtr | t.State: pass -def vqt6_qpixmap_load(pm: QtPixmapPtr, file_name: QtStringPtr) -> t.CInt | t.State: pass -def vqt6_qpixmap_load_from_data(pm: QtPixmapPtr, data: t.CVoid | t.CPtr, size: t.CSizeT) -> t.CInt | t.State: pass -def vqt6_qpixmap_save(pm: QtPixmapPtr, file_name: QtStringPtr) -> t.CInt | t.State: pass -def vqt6_qpixmap_width(pm: QtPixmapPtr) -> t.CInt | t.State: pass -def vqt6_qpixmap_height(pm: QtPixmapPtr) -> t.CInt | t.State: pass -def vqt6_qpixmap_is_null(pm: QtPixmapPtr) -> t.CInt | t.State: pass -def vqt6_qpixmap_scaled(pm: QtPixmapPtr, w: t.CInt, h: t.CInt) -> QtPixmapPtr | t.State: pass -def vqt6_qpixmap_fill(pm: QtPixmapPtr, color: QtColorPtr) -> t.State: pass -def vqt6_qpixmap_free(pm: QtPixmapPtr) -> t.State: pass +extern def vqt6_qpixmap_create() -> QtPixmapPtr +extern def vqt6_qpixmap_create_with_size(w: t.CInt, h: t.CInt) -> QtPixmapPtr +extern def vqt6_qpixmap_load(pm: QtPixmapPtr, file_name: QtStringPtr) -> t.CInt +extern def vqt6_qpixmap_load_from_data(pm: QtPixmapPtr, data: t.CPtr, size: t.CSizeT) -> t.CInt +extern def vqt6_qpixmap_save(pm: QtPixmapPtr, file_name: QtStringPtr) -> t.CInt +extern def vqt6_qpixmap_width(pm: QtPixmapPtr) -> t.CInt +extern def vqt6_qpixmap_height(pm: QtPixmapPtr) -> t.CInt +extern def vqt6_qpixmap_is_null(pm: QtPixmapPtr) -> t.CInt +extern def vqt6_qpixmap_scaled(pm: QtPixmapPtr, w: t.CInt, h: t.CInt) -> QtPixmapPtr +extern def vqt6_qpixmap_fill(pm: QtPixmapPtr, color: QtColorPtr) +extern def vqt6_qpixmap_free(pm: QtPixmapPtr) # ============================================================ # QImage — 图像 # ============================================================ -def vqt6_qimage_create() -> QtImagePtr | t.State: pass -def vqt6_qimage_create_with_size(w: t.CInt, h: t.CInt, format: t.CInt) -> QtImagePtr | t.State: pass -def vqt6_qimage_load(img: QtImagePtr, file_name: QtStringPtr) -> t.CInt | t.State: pass -def vqt6_qimage_save(img: QtImagePtr, file_name: QtStringPtr) -> t.CInt | t.State: pass -def vqt6_qimage_width(img: QtImagePtr) -> t.CInt | t.State: pass -def vqt6_qimage_height(img: QtImagePtr) -> t.CInt | t.State: pass -def vqt6_qimage_format(img: QtImagePtr) -> t.CInt | t.State: pass -def vqt6_qimage_is_null(img: QtImagePtr) -> t.CInt | t.State: pass -def vqt6_qimage_fill(img: QtImagePtr, color: QtColorPtr) -> t.State: pass -def vqt6_qimage_scaled(img: QtImagePtr, w: t.CInt, h: t.CInt) -> QtImagePtr | t.State: pass -def vqt6_qimage_free(img: QtImagePtr) -> t.State: pass +extern def vqt6_qimage_create() -> QtImagePtr +extern def vqt6_qimage_create_with_size(w: t.CInt, h: t.CInt, format: t.CInt) -> QtImagePtr +extern def vqt6_qimage_load(img: QtImagePtr, file_name: QtStringPtr) -> t.CInt +extern def vqt6_qimage_save(img: QtImagePtr, file_name: QtStringPtr) -> t.CInt +extern def vqt6_qimage_width(img: QtImagePtr) -> t.CInt +extern def vqt6_qimage_height(img: QtImagePtr) -> t.CInt +extern def vqt6_qimage_format(img: QtImagePtr) -> t.CInt +extern def vqt6_qimage_is_null(img: QtImagePtr) -> t.CInt +extern def vqt6_qimage_fill(img: QtImagePtr, color: QtColorPtr) +extern def vqt6_qimage_scaled(img: QtImagePtr, w: t.CInt, h: t.CInt) -> QtImagePtr +extern def vqt6_qimage_free(img: QtImagePtr) # ============================================================ # QPainter — 绘画器 # ============================================================ -def vqt6_qpainter_create() -> QtPainterPtr | t.State: pass -def vqt6_qpainter_begin(p: QtPainterPtr, device: QtPixmapPtr) -> t.CInt | t.State: pass -def vqt6_qpainter_end(p: QtPainterPtr) -> t.CInt | t.State: pass -def vqt6_qpainter_is_active(p: QtPainterPtr) -> t.CInt | t.State: pass -def vqt6_qpainter_set_pen(p: QtPainterPtr, pen: QtPenPtr) -> t.State: pass -def vqt6_qpainter_set_brush(p: QtPainterPtr, brush: QtBrushPtr) -> t.State: pass -def vqt6_qpainter_set_font(p: QtPainterPtr, font: QtFontPtr) -> t.State: pass -def vqt6_qpainter_pen(p: QtPainterPtr) -> QtPenPtr | t.State: pass -def vqt6_qpainter_brush(p: QtPainterPtr) -> QtBrushPtr | t.State: pass -def vqt6_qpainter_font(p: QtPainterPtr) -> QtFontPtr | t.State: pass -def vqt6_qpainter_set_opacity(p: QtPainterPtr, opacity: t.CDouble) -> t.State: pass -def vqt6_qpainter_opacity(p: QtPainterPtr) -> t.CDouble | t.State: pass +extern def vqt6_qpainter_create() -> QtPainterPtr +extern def vqt6_qpainter_begin(p: QtPainterPtr, device: QtPixmapPtr) -> t.CInt +extern def vqt6_qpainter_end(p: QtPainterPtr) -> t.CInt +extern def vqt6_qpainter_is_active(p: QtPainterPtr) -> t.CInt +extern def vqt6_qpainter_set_pen(p: QtPainterPtr, pen: QtPenPtr) +extern def vqt6_qpainter_set_brush(p: QtPainterPtr, brush: QtBrushPtr) +extern def vqt6_qpainter_set_font(p: QtPainterPtr, font: QtFontPtr) +extern def vqt6_qpainter_pen(p: QtPainterPtr) -> QtPenPtr +extern def vqt6_qpainter_brush(p: QtPainterPtr) -> QtBrushPtr +extern def vqt6_qpainter_font(p: QtPainterPtr) -> QtFontPtr +extern def vqt6_qpainter_set_opacity(p: QtPainterPtr, opacity: t.CDouble) +extern def vqt6_qpainter_opacity(p: QtPainterPtr) -> t.CDouble # 绘图原语 -def vqt6_qpainter_draw_line(p: QtPainterPtr, x1: t.CInt, y1: t.CInt, x2: t.CInt, y2: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_rect(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt) -> t.State: pass -def vqt6_qpainter_fill_rect(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, color: QtColorPtr) -> t.State: pass -def vqt6_qpainter_draw_ellipse(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_arc(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, start_angle: t.CInt, span_angle: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_chord(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, start_angle: t.CInt, span_angle: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_pie(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, start_angle: t.CInt, span_angle: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_polygon(p: QtPainterPtr, points: t.CInt | t.CPtr, point_count: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_polyline(p: QtPainterPtr, points: t.CInt | t.CPtr, point_count: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_point(p: QtPainterPtr, x: t.CInt, y: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_points(p: QtPainterPtr, points: t.CInt | t.CPtr, point_count: t.CInt) -> t.State: pass -def vqt6_qpainter_draw_text(p: QtPainterPtr, x: t.CInt, y: t.CInt, text: QtStringPtr) -> t.State: pass -def vqt6_qpainter_draw_text_in_rect(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, flags: t.CInt, text: QtStringPtr) -> t.State: pass -def vqt6_qpainter_draw_pixmap(p: QtPainterPtr, x: t.CInt, y: t.CInt, pixmap: QtPixmapPtr) -> t.State: pass -def vqt6_qpainter_draw_image(p: QtPainterPtr, x: t.CInt, y: t.CInt, image: QtImagePtr) -> t.State: pass -def vqt6_qpainter_draw_tiled_pixmap(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, pixmap: QtPixmapPtr) -> t.State: pass +extern def vqt6_qpainter_draw_line(p: QtPainterPtr, x1: t.CInt, y1: t.CInt, x2: t.CInt, y2: t.CInt) +extern def vqt6_qpainter_draw_rect(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt) +extern def vqt6_qpainter_fill_rect(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, color: QtColorPtr) +extern def vqt6_qpainter_draw_ellipse(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt) +extern def vqt6_qpainter_draw_arc(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, start_angle: t.CInt, span_angle: t.CInt) +extern def vqt6_qpainter_draw_chord(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, start_angle: t.CInt, span_angle: t.CInt) +extern def vqt6_qpainter_draw_pie(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, start_angle: t.CInt, span_angle: t.CInt) +extern def vqt6_qpainter_draw_polygon(p: QtPainterPtr, points: t.CInt | t.CPtr, point_count: t.CInt) +extern def vqt6_qpainter_draw_polyline(p: QtPainterPtr, points: t.CInt | t.CPtr, point_count: t.CInt) +extern def vqt6_qpainter_draw_point(p: QtPainterPtr, x: t.CInt, y: t.CInt) +extern def vqt6_qpainter_draw_points(p: QtPainterPtr, points: t.CInt | t.CPtr, point_count: t.CInt) +extern def vqt6_qpainter_draw_text(p: QtPainterPtr, x: t.CInt, y: t.CInt, text: QtStringPtr) +extern def vqt6_qpainter_draw_text_in_rect(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, flags: t.CInt, text: QtStringPtr) +extern def vqt6_qpainter_draw_pixmap(p: QtPainterPtr, x: t.CInt, y: t.CInt, pixmap: QtPixmapPtr) +extern def vqt6_qpainter_draw_image(p: QtPainterPtr, x: t.CInt, y: t.CInt, image: QtImagePtr) +extern def vqt6_qpainter_draw_tiled_pixmap(p: QtPainterPtr, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, pixmap: QtPixmapPtr) # 变换 -def vqt6_qpainter_translate(p: QtPainterPtr, dx: t.CInt, dy: t.CInt) -> t.State: pass -def vqt6_qpainter_rotate(p: QtPainterPtr, angle: t.CDouble) -> t.State: pass -def vqt6_qpainter_scale(p: QtPainterPtr, sx: t.CDouble, sy: t.CDouble) -> t.State: pass -def vqt6_qpainter_reset_transform(p: QtPainterPtr) -> t.State: pass -def vqt6_qpainter_save(p: QtPainterPtr) -> t.State: pass -def vqt6_qpainter_restore(p: QtPainterPtr) -> t.State: pass +extern def vqt6_qpainter_translate(p: QtPainterPtr, dx: t.CInt, dy: t.CInt) +extern def vqt6_qpainter_rotate(p: QtPainterPtr, angle: t.CDouble) +extern def vqt6_qpainter_scale(p: QtPainterPtr, sx: t.CDouble, sy: t.CDouble) +extern def vqt6_qpainter_reset_transform(p: QtPainterPtr) +extern def vqt6_qpainter_save(p: QtPainterPtr) +extern def vqt6_qpainter_restore(p: QtPainterPtr) -def vqt6_qpainter_free(p: QtPainterPtr) -> t.State: pass +extern def vqt6_qpainter_free(p: QtPainterPtr) # ============================================================ # QPen — 画笔 # ============================================================ -def vqt6_qpen_create() -> QtPenPtr | t.State: pass -def vqt6_qpen_create_with_color(color: QtColorPtr) -> QtPenPtr | t.State: pass -def vqt6_qpen_create_with_color_width(color: QtColorPtr, width: t.CInt) -> QtPenPtr | t.State: pass -def vqt6_qpen_set_color(pen: QtPenPtr, color: QtColorPtr) -> t.State: pass -def vqt6_qpen_color(pen: QtPenPtr) -> QtColorPtr | t.State: pass -def vqt6_qpen_set_width(pen: QtPenPtr, width: t.CInt) -> t.State: pass -def vqt6_qpen_width(pen: QtPenPtr) -> t.CInt | t.State: pass -def vqt6_qpen_set_style(pen: QtPenPtr, style: t.CInt) -> t.State: pass -def vqt6_qpen_style(pen: QtPenPtr) -> t.CInt | t.State: pass -def vqt6_qpen_free(pen: QtPenPtr) -> t.State: pass +extern def vqt6_qpen_create() -> QtPenPtr +extern def vqt6_qpen_create_with_color(color: QtColorPtr) -> QtPenPtr +extern def vqt6_qpen_create_with_color_width(color: QtColorPtr, width: t.CInt) -> QtPenPtr +extern def vqt6_qpen_set_color(pen: QtPenPtr, color: QtColorPtr) +extern def vqt6_qpen_color(pen: QtPenPtr) -> QtColorPtr +extern def vqt6_qpen_set_width(pen: QtPenPtr, width: t.CInt) +extern def vqt6_qpen_width(pen: QtPenPtr) -> t.CInt +extern def vqt6_qpen_set_style(pen: QtPenPtr, style: t.CInt) +extern def vqt6_qpen_style(pen: QtPenPtr) -> t.CInt +extern def vqt6_qpen_free(pen: QtPenPtr) # ============================================================ # QBrush — 画刷 # ============================================================ -def vqt6_qbrush_create() -> QtBrushPtr | t.State: pass -def vqt6_qbrush_create_with_color(color: QtColorPtr) -> QtBrushPtr | t.State: pass -def vqt6_qbrush_create_with_style(style: t.CInt) -> QtBrushPtr | t.State: pass -def vqt6_qbrush_set_color(brush: QtBrushPtr, color: QtColorPtr) -> t.State: pass -def vqt6_qbrush_color(brush: QtBrushPtr) -> QtColorPtr | t.State: pass -def vqt6_qbrush_set_style(brush: QtBrushPtr, style: t.CInt) -> t.State: pass -def vqt6_qbrush_style(brush: QtBrushPtr) -> t.CInt | t.State: pass -def vqt6_qbrush_free(brush: QtBrushPtr) -> t.State: pass +extern def vqt6_qbrush_create() -> QtBrushPtr +extern def vqt6_qbrush_create_with_color(color: QtColorPtr) -> QtBrushPtr +extern def vqt6_qbrush_create_with_style(style: t.CInt) -> QtBrushPtr +extern def vqt6_qbrush_set_color(brush: QtBrushPtr, color: QtColorPtr) +extern def vqt6_qbrush_color(brush: QtBrushPtr) -> QtColorPtr +extern def vqt6_qbrush_set_style(brush: QtBrushPtr, style: t.CInt) +extern def vqt6_qbrush_style(brush: QtBrushPtr) -> t.CInt +extern def vqt6_qbrush_free(brush: QtBrushPtr) # ============================================================ # QIcon — 图标 # ============================================================ -def vqt6_qicon_create() -> QtIconPtr | t.State: pass -def vqt6_qicon_create_with_file(file_name: QtStringPtr) -> QtIconPtr | t.State: pass -def vqt6_qicon_add_file(icon: QtIconPtr, file_name: QtStringPtr) -> t.State: pass -def vqt6_qicon_add_pixmap(icon: QtIconPtr, pixmap: QtPixmapPtr) -> t.State: pass -def vqt6_qicon_is_null(icon: QtIconPtr) -> t.CInt | t.State: pass -def vqt6_qicon_pixmap(icon: QtIconPtr, w: t.CInt, h: t.CInt) -> QtPixmapPtr | t.State: pass -def vqt6_qicon_free(icon: QtIconPtr) -> t.State: pass +extern def vqt6_qicon_create() -> QtIconPtr +extern def vqt6_qicon_create_with_file(file_name: QtStringPtr) -> QtIconPtr +extern def vqt6_qicon_add_file(icon: QtIconPtr, file_name: QtStringPtr) +extern def vqt6_qicon_add_pixmap(icon: QtIconPtr, pixmap: QtPixmapPtr) +extern def vqt6_qicon_is_null(icon: QtIconPtr) -> t.CInt +extern def vqt6_qicon_pixmap(icon: QtIconPtr, w: t.CInt, h: t.CInt) -> QtPixmapPtr +extern def vqt6_qicon_free(icon: QtIconPtr) # ============================================================ # QCursor — 鼠标光标 # ============================================================ -def vqt6_qcursor_create() -> QtCursorPtr | t.State: pass -def vqt6_qcursor_create_with_shape(shape: t.CInt) -> QtCursorPtr | t.State: pass -def vqt6_qcursor_pos(x: t.CInt | t.CPtr, y: t.CInt | t.CPtr) -> t.State: pass -def vqt6_qcursor_set_pos(x: t.CInt, y: t.CInt) -> t.State: pass -def vqt6_qcursor_set_shape(cursor: QtCursorPtr, shape: t.CInt) -> t.State: pass -def vqt6_qcursor_shape(cursor: QtCursorPtr) -> t.CInt | t.State: pass -def vqt6_qcursor_free(cursor: QtCursorPtr) -> t.State: pass +extern def vqt6_qcursor_create() -> QtCursorPtr +extern def vqt6_qcursor_create_with_shape(shape: t.CInt) -> QtCursorPtr +extern def vqt6_qcursor_pos(x: t.CInt | t.CPtr, y: t.CInt | t.CPtr) + +extern def vqt6_qcursor_set_pos(x: t.CInt, y: t.CInt) +extern def vqt6_qcursor_set_shape(cursor: QtCursorPtr, shape: t.CInt) +extern def vqt6_qcursor_shape(cursor: QtCursorPtr) -> t.CInt +extern def vqt6_qcursor_free(cursor: QtCursorPtr) # ============================================================ # QKeyEvent — 键盘事件 # ============================================================ -def vqt6_qkeyevent_create(type: t.CInt, key: t.CInt, modifiers: t.CInt, text: QtStringPtr) -> QtKeyEventPtr | t.State: pass -def vqt6_qkeyevent_key(event: QtKeyEventPtr) -> t.CInt | t.State: pass -def vqt6_qkeyevent_text(event: QtKeyEventPtr) -> QtStringPtr | t.State: pass -def vqt6_qkeyevent_modifiers(event: QtKeyEventPtr) -> t.CInt | t.State: pass -def vqt6_qkeyevent_is_auto_repeat(event: QtKeyEventPtr) -> t.CInt | t.State: pass -def vqt6_qkeyevent_free(event: QtKeyEventPtr) -> t.State: pass +extern def vqt6_qkeyevent_create(type: t.CInt, key: t.CInt, modifiers: t.CInt, text: QtStringPtr) -> QtKeyEventPtr +extern def vqt6_qkeyevent_key(event: QtKeyEventPtr) -> t.CInt +extern def vqt6_qkeyevent_text(event: QtKeyEventPtr) -> QtStringPtr +extern def vqt6_qkeyevent_modifiers(event: QtKeyEventPtr) -> t.CInt +extern def vqt6_qkeyevent_is_auto_repeat(event: QtKeyEventPtr) -> t.CInt +extern def vqt6_qkeyevent_free(event: QtKeyEventPtr) # ============================================================ # QMouseEvent — 鼠标事件 # ============================================================ -def vqt6_qmouseevent_create(type: t.CInt, local_pos: QtObjectPtr, button: t.CInt, buttons: t.CInt, modifiers: t.CInt) -> QtMouseEventPtr | t.State: pass -def vqt6_qmouseevent_button(event: QtMouseEventPtr) -> t.CInt | t.State: pass -def vqt6_qmouseevent_buttons(event: QtMouseEventPtr) -> t.CInt | t.State: pass -def vqt6_qmouseevent_x(event: QtMouseEventPtr) -> t.CInt | t.State: pass -def vqt6_qmouseevent_y(event: QtMouseEventPtr) -> t.CInt | t.State: pass -def vqt6_qmouseevent_global_x(event: QtMouseEventPtr) -> t.CInt | t.State: pass -def vqt6_qmouseevent_global_y(event: QtMouseEventPtr) -> t.CInt | t.State: pass -def vqt6_qmouseevent_modifiers(event: QtMouseEventPtr) -> t.CInt | t.State: pass -def vqt6_qmouseevent_free(event: QtMouseEventPtr) -> t.State: pass +extern def vqt6_qmouseevent_create(type: t.CInt, local_pos: QtObjectPtr, button: t.CInt, buttons: t.CInt, modifiers: t.CInt) -> QtMouseEventPtr +extern def vqt6_qmouseevent_button(event: QtMouseEventPtr) -> t.CInt +extern def vqt6_qmouseevent_buttons(event: QtMouseEventPtr) -> t.CInt +extern def vqt6_qmouseevent_x(event: QtMouseEventPtr) -> t.CInt +extern def vqt6_qmouseevent_y(event: QtMouseEventPtr) -> t.CInt +extern def vqt6_qmouseevent_global_x(event: QtMouseEventPtr) -> t.CInt +extern def vqt6_qmouseevent_global_y(event: QtMouseEventPtr) -> t.CInt +extern def vqt6_qmouseevent_modifiers(event: QtMouseEventPtr) -> t.CInt +extern def vqt6_qmouseevent_free(event: QtMouseEventPtr) # ============================================================ # QResizeEvent / QPaintEvent / QCloseEvent # ============================================================ -def vqt6_qresizeevent_create(width: t.CInt, height: t.CInt, old_width: t.CInt, old_height: t.CInt) -> QtResizeEventPtr | t.State: pass -def vqt6_qresizeevent_size(event: QtResizeEventPtr, w: t.CInt | t.CPtr, h: t.CInt | t.CPtr) -> t.State: pass -def vqt6_qresizeevent_old_size(event: QtResizeEventPtr, w: t.CInt | t.CPtr, h: t.CInt | t.CPtr) -> t.State: pass -def vqt6_qresizeevent_free(event: QtResizeEventPtr) -> t.State: pass +extern def vqt6_qresizeevent_create(width: t.CInt, height: t.CInt, old_width: t.CInt, old_height: t.CInt) -> QtResizeEventPtr +extern def vqt6_qresizeevent_size(event: QtResizeEventPtr, w: t.CInt | t.CPtr, h: t.CInt | t.CPtr) +extern def vqt6_qresizeevent_old_size(event: QtResizeEventPtr, w: t.CInt | t.CPtr, h: t.CInt | t.CPtr) +extern def vqt6_qresizeevent_free(event: QtResizeEventPtr) -def vqt6_qpaintevent_create(rect_x: t.CInt, rect_y: t.CInt, rect_w: t.CInt, rect_h: t.CInt) -> QtPaintEventPtr | t.State: pass -def vqt6_qpaintevent_rect(event: QtPaintEventPtr, x: t.CInt | t.CPtr, y: t.CInt | t.CPtr, w: t.CInt | t.CPtr, h: t.CInt | t.CPtr) -> t.State: pass -def vqt6_qpaintevent_free(event: QtPaintEventPtr) -> t.State: pass -def vqt6_qcloseevent_create() -> QtCloseEventPtr | t.State: pass -def vqt6_qcloseevent_is_accepted(event: QtCloseEventPtr) -> t.CInt | t.State: pass -def vqt6_qcloseevent_accept(event: QtCloseEventPtr) -> t.State: pass -def vqt6_qcloseevent_ignore(event: QtCloseEventPtr) -> t.State: pass -def vqt6_qcloseevent_free(event: QtCloseEventPtr) -> t.State: pass +extern def vqt6_qpaintevent_create(rect_x: t.CInt, rect_y: t.CInt, rect_w: t.CInt, rect_h: t.CInt) -> QtPaintEventPtr +extern def vqt6_qpaintevent_rect(event: QtPaintEventPtr, x: t.CInt | t.CPtr, y: t.CInt | t.CPtr, w: t.CInt | t.CPtr, h: t.CInt | t.CPtr) -> t.State: pass +extern def vqt6_qpaintevent_free(event: QtPaintEventPtr) + +extern def vqt6_qcloseevent_create() -> QtCloseEventPtr +extern def vqt6_qcloseevent_is_accepted(event: QtCloseEventPtr) -> t.CInt +extern def vqt6_qcloseevent_accept(event: QtCloseEventPtr) +extern def vqt6_qcloseevent_ignore(event: QtCloseEventPtr) +extern def vqt6_qcloseevent_free(event: QtCloseEventPtr) diff --git a/vqt6/_qtnetwork.vp b/vqt6/_qtnetwork.vp index 6859d8b..c82b864 100644 --- a/vqt6/_qtnetwork.vp +++ b/vqt6/_qtnetwork.vp @@ -60,9 +60,9 @@ def vqt6_qabstractsocket_is_open(socket: QtAbstractSocketPtr) -> t.CInt | t.Stat def vqt6_qabstractsocket_flush(socket: QtAbstractSocketPtr) -> t.CInt | t.State: pass def vqt6_qabstractsocket_close(socket: QtAbstractSocketPtr) -> t.State: pass def vqt6_qabstractsocket_abort(socket: QtAbstractSocketPtr) -> t.State: pass -def vqt6_qabstractsocket_read(socket: QtAbstractSocketPtr, buf: t.CVoid | t.CPtr, max_size: t.CSizeT) -> t.CSizeT | t.State: pass -def vqt6_qabstractsocket_read_all(socket: QtAbstractSocketPtr, out_size: t.CSizeT | t.CPtr) -> t.CVoid | t.CPtr | t.State: pass -def vqt6_qabstractsocket_write(socket: QtAbstractSocketPtr, data: t.CVoid | t.CPtr, size: t.CSizeT) -> t.CSizeT | t.State: pass +def vqt6_qabstractsocket_read(socket: QtAbstractSocketPtr, buf: t.CPtr, max_size: t.CSizeT) -> t.CSizeT | t.State: pass +def vqt6_qabstractsocket_read_all(socket: QtAbstractSocketPtr, out_size: t.CSizeT | t.CPtr) -> t.CPtr | t.State: pass +def vqt6_qabstractsocket_write(socket: QtAbstractSocketPtr, data: t.CPtr, size: t.CSizeT) -> t.CSizeT | t.State: pass def vqt6_qabstractsocket_bytes_available(socket: QtAbstractSocketPtr) -> t.CSizeT | t.State: pass def vqt6_qabstractsocket_bytes_to_write(socket: QtAbstractSocketPtr) -> t.CSizeT | t.State: pass def vqt6_qabstractsocket_peer_address(socket: QtAbstractSocketPtr) -> QtHostAddressPtr | t.State: pass @@ -92,8 +92,8 @@ def vqt6_qudpsocket_create_with_parent(parent: QtObjectPtr) -> QtUdpSocketPtr | def vqt6_qudpsocket_bind(socket: QtUdpSocketPtr, port: t.CUInt16T) -> t.CInt | t.State: pass def vqt6_qudpsocket_bind_with_addr(socket: QtUdpSocketPtr, address: QtHostAddressPtr, port: t.CUInt16T) -> t.CInt | t.State: pass def vqt6_qudpsocket_close(socket: QtUdpSocketPtr) -> t.State: pass -def vqt6_qudpsocket_write_datagram(socket: QtUdpSocketPtr, data: t.CVoid | t.CPtr, size: t.CSizeT, host: QtHostAddressPtr, port: t.CUInt16T) -> t.CSizeT | t.State: pass -def vqt6_qudpsocket_read_datagram(socket: QtUdpSocketPtr, data: t.CVoid | t.CPtr, max_size: t.CSizeT, host: QtHostAddressPtr, port: t.CUInt16T | t.CPtr) -> t.CSizeT | t.State: pass +def vqt6_qudpsocket_write_datagram(socket: QtUdpSocketPtr, data: t.CPtr, size: t.CSizeT, host: QtHostAddressPtr, port: t.CUInt16T) -> t.CSizeT | t.State: pass +def vqt6_qudpsocket_read_datagram(socket: QtUdpSocketPtr, data: t.CPtr, max_size: t.CSizeT, host: QtHostAddressPtr, port: t.CUInt16T | t.CPtr) -> t.CSizeT | t.State: pass def vqt6_qudpsocket_has_pending_datagrams(socket: QtUdpSocketPtr) -> t.CInt | t.State: pass def vqt6_qudpsocket_pending_datagram_size(socket: QtUdpSocketPtr) -> t.CSizeT | t.State: pass def vqt6_qudpsocket_join_multicast_group(socket: QtUdpSocketPtr, group_address: QtHostAddressPtr) -> t.CInt | t.State: pass @@ -109,8 +109,8 @@ def vqt6_qnetworkrequest_set_url(req: QtNetworkRequestPtr, url: QtStringPtr) -> def vqt6_qnetworkrequest_url(req: QtNetworkRequestPtr) -> QtStringPtr | t.State: pass def vqt6_qnetworkrequest_set_header(req: QtNetworkRequestPtr, header_type: t.CInt, value: QtVariantPtr) -> t.State: pass def vqt6_qnetworkrequest_header(req: QtNetworkRequestPtr, header_type: t.CInt) -> QtVariantPtr | t.State: pass -def vqt6_qnetworkrequest_set_raw_header(req: QtNetworkRequestPtr, header_name: t.CChar | t.CPtr, value: t.CVoid | t.CPtr, value_size: t.CSizeT) -> t.State: pass -def vqt6_qnetworkrequest_raw_header(req: QtNetworkRequestPtr, header_name: t.CChar | t.CPtr, out_size: t.CSizeT | t.CPtr) -> t.CVoid | t.CPtr | t.State: pass +def vqt6_qnetworkrequest_set_raw_header(req: QtNetworkRequestPtr, header_name: t.CChar | t.CPtr, value: t.CPtr, value_size: t.CSizeT) -> t.State: pass +def vqt6_qnetworkrequest_raw_header(req: QtNetworkRequestPtr, header_name: t.CChar | t.CPtr, out_size: t.CSizeT | t.CPtr) -> t.CPtr | t.State: pass def vqt6_qnetworkrequest_set_attribute(req: QtNetworkRequestPtr, attr_type: t.CInt, value: QtVariantPtr) -> t.State: pass def vqt6_qnetworkrequest_attribute(req: QtNetworkRequestPtr, attr_type: t.CInt) -> QtVariantPtr | t.State: pass def vqt6_qnetworkrequest_free(req: QtNetworkRequestPtr) -> t.State: pass @@ -122,8 +122,8 @@ def vqt6_qnetworkrequest_free(req: QtNetworkRequestPtr) -> t.State: pass def vqt6_qnetworkaccessmanager_create() -> QtNetworkAccessManagerPtr | t.State: pass def vqt6_qnetworkaccessmanager_create_with_parent(parent: QtObjectPtr) -> QtNetworkAccessManagerPtr | t.State: pass def vqt6_qnetworkaccessmanager_get(nam: QtNetworkAccessManagerPtr, req: QtNetworkRequestPtr) -> QtNetworkReplyPtr | t.State: pass -def vqt6_qnetworkaccessmanager_post(nam: QtNetworkAccessManagerPtr, req: QtNetworkRequestPtr, data: t.CVoid | t.CPtr, size: t.CSizeT) -> QtNetworkReplyPtr | t.State: pass -def vqt6_qnetworkaccessmanager_put(nam: QtNetworkAccessManagerPtr, req: QtNetworkRequestPtr, data: t.CVoid | t.CPtr, size: t.CSizeT) -> QtNetworkReplyPtr | t.State: pass +def vqt6_qnetworkaccessmanager_post(nam: QtNetworkAccessManagerPtr, req: QtNetworkRequestPtr, data: t.CPtr, size: t.CSizeT) -> QtNetworkReplyPtr | t.State: pass +def vqt6_qnetworkaccessmanager_put(nam: QtNetworkAccessManagerPtr, req: QtNetworkRequestPtr, data: t.CPtr, size: t.CSizeT) -> QtNetworkReplyPtr | t.State: pass def vqt6_qnetworkaccessmanager_delete_resource(nam: QtNetworkAccessManagerPtr, req: QtNetworkRequestPtr) -> QtNetworkReplyPtr | t.State: pass def vqt6_qnetworkaccessmanager_head(nam: QtNetworkAccessManagerPtr, req: QtNetworkRequestPtr) -> QtNetworkReplyPtr | t.State: pass def vqt6_qnetworkaccessmanager_set_cookie_store(nam: QtNetworkAccessManagerPtr, store: QtObjectPtr) -> t.State: pass @@ -139,14 +139,14 @@ def vqt6_qnetworkreply_error_string(reply: QtNetworkReplyPtr) -> QtStringPtr | t def vqt6_qnetworkreply_is_finished(reply: QtNetworkReplyPtr) -> t.CInt | t.State: pass def vqt6_qnetworkreply_is_running(reply: QtNetworkReplyPtr) -> t.CInt | t.State: pass def vqt6_qnetworkreply_abort(reply: QtNetworkReplyPtr) -> t.State: pass -def vqt6_qnetworkreply_read(reply: QtNetworkReplyPtr, buf: t.CVoid | t.CPtr, max_size: t.CSizeT) -> t.CSizeT | t.State: pass -def vqt6_qnetworkreply_read_all(reply: QtNetworkReplyPtr, out_size: t.CSizeT | t.CPtr) -> t.CVoid | t.CPtr | t.State: pass +def vqt6_qnetworkreply_read(reply: QtNetworkReplyPtr, buf: t.CPtr, max_size: t.CSizeT) -> t.CSizeT | t.State: pass +def vqt6_qnetworkreply_read_all(reply: QtNetworkReplyPtr, out_size: t.CSizeT | t.CPtr) -> t.CPtr | t.State: pass def vqt6_qnetworkreply_bytes_available(reply: QtNetworkReplyPtr) -> t.CSizeT | t.State: pass def vqt6_qnetworkreply_header(reply: QtNetworkReplyPtr, header_type: t.CInt) -> QtVariantPtr | t.State: pass -def vqt6_qnetworkreply_raw_header(reply: QtNetworkReplyPtr, header_name: t.CChar | t.CPtr, out_size: t.CSizeT | t.CPtr) -> t.CVoid | t.CPtr | t.State: pass +def vqt6_qnetworkreply_raw_header(reply: QtNetworkReplyPtr, header_name: t.CChar | t.CPtr, out_size: t.CSizeT | t.CPtr) -> t.CPtr | t.State: pass def vqt6_qnetworkreply_attribute(reply: QtNetworkReplyPtr, attribute_code: t.CInt) -> QtVariantPtr | t.State: pass def vqt6_qnetworkreply_attribute_int(reply: QtNetworkReplyPtr, attribute_code: t.CInt, default_value: t.CInt) -> t.CInt | t.State: pass -def vqt6_qnetworkreply_set_raw_header(reply: QtNetworkReplyPtr, header_name: t.CChar | t.CPtr, value: t.CVoid | t.CPtr, value_size: t.CSizeT) -> t.State: pass +def vqt6_qnetworkreply_set_raw_header(reply: QtNetworkReplyPtr, header_name: t.CChar | t.CPtr, value: t.CPtr, value_size: t.CSizeT) -> t.State: pass # ============================================================ diff --git a/vqt6/_types.vp b/vqt6/_types.vp index 57e5177..cfce66d 100644 --- a/vqt6/_types.vp +++ b/vqt6/_types.vp @@ -9,91 +9,91 @@ from stdint import * # ============================================================ # 不透明指针 typedef(Qt6 C++ 类的 Viper 端表示) -QtObjectPtr: t.CTypedef = t.CVoid | t.CPtr # void* 通用不透明指针 -QtStringPtr: t.CTypedef = t.CVoid | t.CPtr # QString* -QtByteArrayPtr: t.CTypedef = t.CVoid | t.CPtr # QByteArray* -QtVariantPtr: t.CTypedef = t.CVoid | t.CPtr # QVariant* -QtObjectListPtr: t.CTypedef = t.CVoid | t.CPtr # QList* -QtStringListPtr: t.CTypedef = t.CVoid | t.CPtr # QStringList* -QtByteArrayListPtr: t.CTypedef = t.CVoid | t.CPtr # QList* -QtEventPtr: t.CTypedef = t.CVoid | t.CPtr # QEvent* +QtObjectPtr: t.CTypedef = t.CPtr # void* 通用不透明指针 +QtStringPtr: t.CTypedef = t.CPtr # QString* +QtByteArrayPtr: t.CTypedef = t.CPtr # QByteArray* +QtVariantPtr: t.CTypedef = t.CPtr # QVariant* +QtObjectListPtr: t.CTypedef = t.CPtr # QList* +QtStringListPtr: t.CTypedef = t.CPtr # QStringList* +QtByteArrayListPtr: t.CTypedef = t.CPtr # QList* +QtEventPtr: t.CTypedef = t.CPtr # QEvent* # QtCore -QtCoreAppPtr: t.CTypedef = t.CVoid | t.CPtr # QCoreApplication* -QtTimerPtr: t.CTypedef = t.CVoid | t.CPtr # QTimer* -QtObjectPtr: t.CTypedef = t.CVoid | t.CPtr # QObject* -QtThreadPtr: t.CTypedef = t.CVoid | t.CPtr # QThread* -QtMutexPtr: t.CTypedef = t.CVoid | t.CPtr # QMutex* -QtWaitConditionPtr: t.CTypedef = t.CVoid | t.CPtr # QWaitCondition* +QtCoreAppPtr: t.CTypedef = t.CPtr # QCoreApplication* +QtTimerPtr: t.CTypedef = t.CPtr # QTimer* +QtObjectPtr: t.CTypedef = t.CPtr # QObject* +QtThreadPtr: t.CTypedef = t.CPtr # QThread* +QtMutexPtr: t.CTypedef = t.CPtr # QMutex* +QtWaitConditionPtr: t.CTypedef = t.CPtr # QWaitCondition* # QtGui -QtColorPtr: t.CTypedef = t.CVoid | t.CPtr # QColor* -QtFontPtr: t.CTypedef = t.CVoid | t.CPtr # QFont* -QtFontMetricsPtr: t.CTypedef = t.CVoid | t.CPtr # QFontMetrics* -QtPixmapPtr: t.CTypedef = t.CVoid | t.CPtr # QPixmap* -QtImagePtr: t.CTypedef = t.CVoid | t.CPtr # QImage* -QtPainterPtr: t.CTypedef = t.CVoid | t.CPtr # QPainter* -QtPenPtr: t.CTypedef = t.CVoid | t.CPtr # QPen* -QtBrushPtr: t.CTypedef = t.CVoid | t.CPtr # QBrush* -QtIconPtr: t.CTypedef = t.CVoid | t.CPtr # QIcon* -QtKeyEventPtr: t.CTypedef = t.CVoid | t.CPtr # QKeyEvent* -QtMouseEventPtr: t.CTypedef = t.CVoid | t.CPtr # QMouseEvent* -QtResizeEventPtr: t.CTypedef = t.CVoid | t.CPtr # QResizeEvent* -QtPaintEventPtr: t.CTypedef = t.CVoid | t.CPtr # QPaintEvent* -QtCloseEventPtr: t.CTypedef = t.CVoid | t.CPtr # QCloseEvent* -QtCursorPtr: t.CTypedef = t.CVoid | t.CPtr # QCursor* +QtColorPtr: t.CTypedef = t.CPtr # QColor* +QtFontPtr: t.CTypedef = t.CPtr # QFont* +QtFontMetricsPtr: t.CTypedef = t.CPtr # QFontMetrics* +QtPixmapPtr: t.CTypedef = t.CPtr # QPixmap* +QtImagePtr: t.CTypedef = t.CPtr # QImage* +QtPainterPtr: t.CTypedef = t.CPtr # QPainter* +QtPenPtr: t.CTypedef = t.CPtr # QPen* +QtBrushPtr: t.CTypedef = t.CPtr # QBrush* +QtIconPtr: t.CTypedef = t.CPtr # QIcon* +QtKeyEventPtr: t.CTypedef = t.CPtr # QKeyEvent* +QtMouseEventPtr: t.CTypedef = t.CPtr # QMouseEvent* +QtResizeEventPtr: t.CTypedef = t.CPtr # QResizeEvent* +QtPaintEventPtr: t.CTypedef = t.CPtr # QPaintEvent* +QtCloseEventPtr: t.CTypedef = t.CPtr # QCloseEvent* +QtCursorPtr: t.CTypedef = t.CPtr # QCursor* # QtWidgets -QtAppPtr: t.CTypedef = t.CVoid | t.CPtr # QApplication* -QtWidgetPtr: t.CTypedef = t.CVoid | t.CPtr # QWidget* -QtDialogPtr: t.CTypedef = t.CVoid | t.CPtr # QDialog* -QtMainWindowPtr: t.CTypedef = t.CVoid | t.CPtr # QMainWindow* -QtLabelPtr: t.CTypedef = t.CVoid | t.CPtr # QLabel* -QtPushButtonPtr: t.CTypedef = t.CVoid | t.CPtr # QPushButton* -QtLineEditPtr: t.CTypedef = t.CVoid | t.CPtr # QLineEdit* -QtTextEditPtr: t.CTypedef = t.CVoid | t.CPtr # QTextEdit* -QtPlainTextEditPtr: t.CTypedef = t.CVoid | t.CPtr # QPlainTextEdit* -QtCheckBoxPtr: t.CTypedef = t.CVoid | t.CPtr # QCheckBox* -QtRadioButtonPtr: t.CTypedef = t.CVoid | t.CPtr # QRadioButton* -QtComboBoxPtr: t.CTypedef = t.CVoid | t.CPtr # QComboBox* -QtListWidgetPtr: t.CTypedef = t.CVoid | t.CPtr # QListWidget* -QtListWidgetItemPtr: t.CTypedef = t.CVoid | t.CPtr # QListWidgetItem* -QtTreeWidgetPtr: t.CTypedef = t.CVoid | t.CPtr # QTreeWidget* -QtTreeWidgetItemPtr: t.CTypedef = t.CVoid | t.CPtr # QTreeWidgetItem* -QtProgressBarPtr: t.CTypedef = t.CVoid | t.CPtr # QProgressBar* -QtSliderPtr: t.CTypedef = t.CVoid | t.CPtr # QSlider* -QtSpinBoxPtr: t.CTypedef = t.CVoid | t.CPtr # QSpinBox* -QtLayoutPtr: t.CTypedef = t.CVoid | t.CPtr # QLayout* -QtBoxLayoutPtr: t.CTypedef = t.CVoid | t.CPtr # QHBoxLayout / QVBoxLayout* -QtGridLayoutPtr: t.CTypedef = t.CVoid | t.CPtr # QGridLayout* -QtFormLayoutPtr: t.CTypedef = t.CVoid | t.CPtr # QFormLayout* -QtStackedWidgetPtr: t.CTypedef = t.CVoid | t.CPtr # QStackedWidget* -QtTabWidgetPtr: t.CTypedef = t.CVoid | t.CPtr # QTabWidget* -QtMenuBarPtr: t.CTypedef = t.CVoid | t.CPtr # QMenuBar* -QtMenuPtr: t.CTypedef = t.CVoid | t.CPtr # QMenu* -QtActionPtr: t.CTypedef = t.CVoid | t.CPtr # QAction* -QtStatusBarPtr: t.CTypedef = t.CVoid | t.CPtr # QStatusBar* -QtToolBarPtr: t.CTypedef = t.CVoid | t.CPtr # QToolBar* -QtMessageBoxPtr: t.CTypedef = t.CVoid | t.CPtr # QMessageBox* -QtFileDialogPtr: t.CTypedef = t.CVoid | t.CPtr # QFileDialog* -QtColorDialogPtr: t.CTypedef = t.CVoid | t.CPtr # QColorDialog* -QtFontDialogPtr: t.CTypedef = t.CVoid | t.CPtr # QFontDialog* -QtInputDialogPtr: t.CTypedef = t.CVoid | t.CPtr # QInputDialog* +QtAppPtr: t.CTypedef = t.CPtr # QApplication* +QtWidgetPtr: t.CTypedef = t.CPtr # QWidget* +QtDialogPtr: t.CTypedef = t.CPtr # QDialog* +QtMainWindowPtr: t.CTypedef = t.CPtr # QMainWindow* +QtLabelPtr: t.CTypedef = t.CPtr # QLabel* +QtPushButtonPtr: t.CTypedef = t.CPtr # QPushButton* +QtLineEditPtr: t.CTypedef = t.CPtr # QLineEdit* +QtTextEditPtr: t.CTypedef = t.CPtr # QTextEdit* +QtPlainTextEditPtr: t.CTypedef = t.CPtr # QPlainTextEdit* +QtCheckBoxPtr: t.CTypedef = t.CPtr # QCheckBox* +QtRadioButtonPtr: t.CTypedef = t.CPtr # QRadioButton* +QtComboBoxPtr: t.CTypedef = t.CPtr # QComboBox* +QtListWidgetPtr: t.CTypedef = t.CPtr # QListWidget* +QtListWidgetItemPtr: t.CTypedef = t.CPtr # QListWidgetItem* +QtTreeWidgetPtr: t.CTypedef = t.CPtr # QTreeWidget* +QtTreeWidgetItemPtr: t.CTypedef = t.CPtr # QTreeWidgetItem* +QtProgressBarPtr: t.CTypedef = t.CPtr # QProgressBar* +QtSliderPtr: t.CTypedef = t.CPtr # QSlider* +QtSpinBoxPtr: t.CTypedef = t.CPtr # QSpinBox* +QtLayoutPtr: t.CTypedef = t.CPtr # QLayout* +QtBoxLayoutPtr: t.CTypedef = t.CPtr # QHBoxLayout / QVBoxLayout* +QtGridLayoutPtr: t.CTypedef = t.CPtr # QGridLayout* +QtFormLayoutPtr: t.CTypedef = t.CPtr # QFormLayout* +QtStackedWidgetPtr: t.CTypedef = t.CPtr # QStackedWidget* +QtTabWidgetPtr: t.CTypedef = t.CPtr # QTabWidget* +QtMenuBarPtr: t.CTypedef = t.CPtr # QMenuBar* +QtMenuPtr: t.CTypedef = t.CPtr # QMenu* +QtActionPtr: t.CTypedef = t.CPtr # QAction* +QtStatusBarPtr: t.CTypedef = t.CPtr # QStatusBar* +QtToolBarPtr: t.CTypedef = t.CPtr # QToolBar* +QtMessageBoxPtr: t.CTypedef = t.CPtr # QMessageBox* +QtFileDialogPtr: t.CTypedef = t.CPtr # QFileDialog* +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.CVoid | t.CPtr # Vqt6Canvas*(QWidget* 兼容) -QtGraph3DPtr: t.CTypedef = t.CVoid | t.CPtr # Vqt6Graph3D*(QWidget* 兼容) -QtChartViewPtr: t.CTypedef = t.CVoid | t.CPtr # QChartView* -QtChartSeriesPtr: t.CTypedef = t.CVoid | t.CPtr # QLineSeries* +QtCanvasPtr: t.CTypedef = t.CPtr # Vqt6Canvas*(QWidget* 兼容) +QtGraph3DPtr: t.CTypedef = t.CPtr # Vqt6Graph3D*(QWidget* 兼容) +QtChartViewPtr: t.CTypedef = t.CPtr # QChartView* +QtChartSeriesPtr: t.CTypedef = t.CPtr # QLineSeries* # QtNetwork -QtTcpSocketPtr: t.CTypedef = t.CVoid | t.CPtr # QTcpSocket* -QtUdpSocketPtr: t.CTypedef = t.CVoid | t.CPtr # QUdpSocket* -QtHostAddressPtr: t.CTypedef = t.CVoid | t.CPtr # QHostAddress* -QtAbstractSocketPtr: t.CTypedef = t.CVoid | t.CPtr # QAbstractSocket* -QtNetworkAccessManagerPtr: t.CTypedef = t.CVoid | t.CPtr # QNetworkAccessManager* -QtNetworkReplyPtr: t.CTypedef = t.CVoid | t.CPtr # QNetworkReply* -QtNetworkRequestPtr: t.CTypedef = t.CVoid | t.CPtr # QNetworkRequest* +QtTcpSocketPtr: t.CTypedef = t.CPtr # QTcpSocket* +QtUdpSocketPtr: t.CTypedef = t.CPtr # QUdpSocket* +QtHostAddressPtr: t.CTypedef = t.CPtr # QHostAddress* +QtAbstractSocketPtr: t.CTypedef = t.CPtr # QAbstractSocket* +QtNetworkAccessManagerPtr: t.CTypedef = t.CPtr # QNetworkAccessManager* +QtNetworkReplyPtr: t.CTypedef = t.CPtr # QNetworkReply* +QtNetworkRequestPtr: t.CTypedef = t.CPtr # QNetworkRequest* # ============================================================ # Qt 通用常量(C++ 枚举值的 C 兼容定义) diff --git a/w32/winsock2.vp b/w32/winsock2.vp index 990b248..af2c5ea 100644 --- a/w32/winsock2.vp +++ b/w32/winsock2.vp @@ -91,12 +91,12 @@ extern def WSAGetLastError() -> INT extern def socket(family: INT, type: INT, protocol: INT) -> u64 extern def closesocket(s: u64) -> INT extern def connect(s: u64, name: WSASocketAddr | t.CPtr, namelen: INT) -> INT -extern def send(s: u64, buf: t.CVoid | t.CPtr, len: INT, flags: INT) -> INT -extern def recv(s: u64, buf: t.CVoid | t.CPtr, len: INT, flags: INT) -> INT +extern def send(s: u64, buf: t.CPtr, len: INT, flags: INT) -> INT +extern def recv(s: u64, buf: t.CPtr, len: INT, flags: INT) -> INT extern def bind(s: u64, name: WSASocketAddr | t.CPtr, namelen: INT) -> INT extern def listen(s: u64, backlog: INT) -> INT extern def accept(s: u64, addr: WSASocketAddr | t.CPtr, addrlen: INT | t.CPtr) -> u64 -extern def setsockopt(s: u64, level: INT, optname: INT, optval: t.CVoid | t.CPtr, optlen: INT) -> INT +extern def setsockopt(s: u64, level: INT, optname: INT, optval: t.CPtr, optlen: INT) -> INT extern def shutdown(s: u64, how: INT) -> INT extern def gethostbyname(name: t.CChar | t.CConst | t.CPtr) -> WSAHostEnt | t.CPtr extern def ntohs(netshort: u16) -> u16