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 # 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 # ============================================================ # ASTFlag - Node flags enum # ============================================================ class ASTFlag(t.CEnum): IsAsync: t.State Simple: t.State HasStar: t.State # ============================================================ # Helper Functions # ============================================================ def _copy_str(pool: memhub.MemManager | t.CPtr, 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: AST | t.CPtr, lineno: t.CInt, col_offset: t.CInt, end_lineno: t.CInt, end_col_offset: t.CInt): 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: AST | t.CPtr, ref: AST | t.CPtr): 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: AST | t.CPtr, pool: memhub.MemManager | t.CPtr, lineno: t.CInt, col: t.CInt): """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[AST | t.CPtr] | t.CPtr, parent: AST | t.CPtr): """Set list children parent pointers.""" if lst is None: return n: t.CSizeT = lst.__len__() i: t.CSizeT = 0 while i < n: child: AST | t.CPtr = lst.get(i) if child is not None: child.parent = parent i += 1 def _append_child(lst: list[AST | t.CPtr] | t.CPtr, node: AST | t.CPtr, pool: memhub.MemManager | t.CPtr): """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: t.CInt) -> t.CInt: 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: t.CInt) -> t.CInt: 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: t.CInt) -> t.CInt: 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: t.CChar | t.CPtr, 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: t.CChar | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(buf) + pos) rem: t.CSizeT = size - pos n: t.CInt = viperlib.snprintf(cur, rem, "%s", text) if n < 0: return pos return pos + t.CSizeT(n) def _emit_str(buf: t.CChar | t.CPtr, 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: t.CChar | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(buf) + pos) rem: t.CSizeT = size - pos n: t.CInt = viperlib.snprintf(cur, rem, "%s", text) if n < 0: return pos return pos + t.CSizeT(n) def _emit_int(buf: t.CChar | t.CPtr, size: t.CSizeT, pos: t.CSizeT, val: t.CInt64T) -> t.CSizeT: """Append an integer value to buf pos (using %lld format).""" if buf is None or pos >= size: return pos cur: t.CChar | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(buf) + pos) rem: t.CSizeT = size - pos n: t.CInt = viperlib.snprintf(cur, rem, "%lld", val) if n < 0: return pos return pos + t.CSizeT(n) def _op_name(op: t.CInt) -> 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" return "NoneOp" def _dump_list(lst: list[AST | t.CPtr] | t.CPtr, buf: t.CChar | t.CPtr, 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: AST | t.CPtr = 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: t.CChar | t.CPtr, 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: list[t.CInt] | t.CPtr = (list[t.CInt] | t.CPtr)(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: t.CInt = 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: AST | t.CPtr pool: memhub.MemManager | t.CPtr lineno: t.CInt col_offset: t.CInt end_lineno: t.CInt end_col_offset: t.CInt children: list[AST | t.CPtr] | t.CPtr def kind(self) -> t.CInt: """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: t.CChar | t.CPtr, 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: AST | t.CPtr): """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[AST | t.CPtr](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: AST | t.CPtr, buf: t.CChar | t.CPtr, 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'