480 lines
17 KiB
Plaintext
480 lines
17 KiB
Plaintext
import t, c
|
|
from stdint import *
|
|
|
|
|
|
# ============================================================
|
|
# LinkedNode: Non-polymorphic base class (@t.NoVTable)
|
|
#
|
|
# Subclasses inheriting from this class gain standard linked list and tree capabilities:
|
|
# - Bidirectional sibling links (next/prev) → O(1) removal
|
|
# - Parent-child tree structure (child/last_child/parent) → O(1) child appending
|
|
# - Child counter (child_count)
|
|
#
|
|
# Fields are flattened and embedded into subclasses with no vtable overhead
|
|
# (equivalent to non-polymorphic inheritance in C++).
|
|
# OOP methods are automatically wrapped for subclasses via _generate_inherited_method_wrappers
|
|
# (self pointer cast to base type before invocation). Inherited methods can be called directly by subclasses.
|
|
#
|
|
# Usage:
|
|
# class MyNode(LinkedNode):
|
|
# value: t.CInt
|
|
# root: MyNode = MyNode()
|
|
# child: MyNode = MyNode()
|
|
# root.append(child) # OOP invocation, self = root
|
|
# child.detach() # Remove self from sibling chain
|
|
# ============================================================
|
|
@t.NoVTable
|
|
class LinkedNode:
|
|
next: "LinkedNode" | t.CPtr # Next sibling node
|
|
prev: "LinkedNode" | t.CPtr # Previous sibling node (doubly linked list, O(1) removal)
|
|
child: "LinkedNode" | t.CPtr # First child node
|
|
last_child: "LinkedNode" | t.CPtr # Last child node (O(1) append)
|
|
parent: "LinkedNode" | t.CPtr # Parent node
|
|
child_count: t.CSizeT # Number of child nodes
|
|
|
|
def append(self, node: "LinkedNode" | t.CPtr):
|
|
"""Append node as the last child of self (O(1)).
|
|
|
|
If node is already attached to any list, the caller shall invoke node.detach() prior to append.
|
|
"""
|
|
if self is None or node is None: return
|
|
node.parent = self
|
|
node.next = None
|
|
node.prev = self.last_child
|
|
if self.child is None:
|
|
self.child = node
|
|
else:
|
|
self.last_child.next = node
|
|
self.last_child = node
|
|
self.child_count += 1
|
|
|
|
def prepend(self, node: "LinkedNode" | t.CPtr):
|
|
"""Insert node as the first child of self (O(1))."""
|
|
if self is None or node is None: return
|
|
node.parent = self
|
|
node.prev = None
|
|
node.next = self.child
|
|
if self.child is not None:
|
|
self.child.prev = node
|
|
else:
|
|
self.last_child = node
|
|
self.child = node
|
|
self.child_count += 1
|
|
|
|
def insert_before(self, node: "LinkedNode" | t.CPtr,
|
|
new_node: "LinkedNode" | t.CPtr):
|
|
"""Insert new_node before node (O(1)). node must be a direct child of self."""
|
|
if self is None or node is None or new_node is None: return
|
|
new_node.parent = self
|
|
new_node.prev = node.prev
|
|
new_node.next = node
|
|
if node.prev is not None:
|
|
node.prev.next = new_node
|
|
else:
|
|
self.child = new_node
|
|
node.prev = new_node
|
|
self.child_count += 1
|
|
|
|
def insert_after(self, node: "LinkedNode" | t.CPtr,
|
|
new_node: "LinkedNode" | t.CPtr):
|
|
"""Insert new_node after node (O(1)). node must be a direct child of self."""
|
|
if self is None or node is None or new_node is None: return
|
|
new_node.parent = self
|
|
new_node.prev = node
|
|
new_node.next = node.next
|
|
if node.next is not None:
|
|
node.next.prev = new_node
|
|
else:
|
|
self.last_child = new_node
|
|
node.next = new_node
|
|
self.child_count += 1
|
|
|
|
def remove_child(self, node: "LinkedNode" | t.CPtr):
|
|
"""Remove node from self's child linked list (O(1)). The node must be a direct child of self."""
|
|
if self is None or node is None:
|
|
return
|
|
# Patch prev.next
|
|
if node.prev is not None:
|
|
node.prev.next = node.next
|
|
else:
|
|
self.child = node.next
|
|
# Patch next.prev
|
|
if node.next is not None:
|
|
node.next.prev = node.prev
|
|
else:
|
|
self.last_child = node.prev
|
|
# Clear sibling and parent links of node
|
|
node.next = None
|
|
node.prev = None
|
|
node.parent = None
|
|
if self.child_count > 0:
|
|
self.child_count -= 1
|
|
|
|
def detach(self):
|
|
"""Detach self from its sibling linked list (O(1)).
|
|
|
|
After invocation, self.next/self.prev/self.parent are cleared. The child chain remains untouched.
|
|
"""
|
|
if self is None:
|
|
return
|
|
# Patch prev.next
|
|
if self.prev is not None:
|
|
self.prev.next = self.next
|
|
elif self.parent is not None:
|
|
self.parent.child = self.next
|
|
# Patch next.prev
|
|
if self.next is not None:
|
|
self.next.prev = self.prev
|
|
elif self.parent is not None:
|
|
self.parent.last_child = self.prev
|
|
# Decrement parent child counter
|
|
if self.parent is not None:
|
|
if self.parent.child_count > 0:
|
|
self.parent.child_count -= 1
|
|
# Clear links of self
|
|
self.next = None
|
|
self.prev = None
|
|
self.parent = None
|
|
|
|
def unlink(self):
|
|
"""Sever all links of self (siblings and parent). Child nodes are not processed recursively.
|
|
|
|
After invocation, self becomes an isolated node, while its child chain still references original children
|
|
(the parent field of child nodes still points to self). For full isolation, the caller shall
|
|
invoke self.remove_child on each child beforehand.
|
|
"""
|
|
if self is None:
|
|
return
|
|
# First detach from sibling list
|
|
self.detach()
|
|
# Then break parent-child links
|
|
self.child = None
|
|
self.last_child = None
|
|
self.child_count = 0
|
|
|
|
def has_children(self) -> t.CInt:
|
|
"""Return 1 if child nodes exist, otherwise return 0."""
|
|
if self is None:
|
|
return 0
|
|
if self.child is not None:
|
|
return 1
|
|
return 0
|
|
|
|
def child_at(self, index: t.CSizeT) -> "LinkedNode" | t.CPtr:
|
|
"""Return the child node at specified zero-based index (O(n)). Return None if index out of bounds."""
|
|
if self is None:
|
|
return None
|
|
cur: "LinkedNode" | t.CPtr = self.child
|
|
i: t.CSizeT = 0
|
|
while cur is not None:
|
|
if i == index:
|
|
return cur
|
|
i += 1
|
|
cur = cur.next
|
|
return None
|
|
|
|
def count_children(self) -> t.CSizeT:
|
|
"""Count children by traversal (O(n)), used to validate child_count."""
|
|
if self is None: return 0
|
|
n: t.CSizeT = 0
|
|
cur: "LinkedNode" | t.CPtr = self.child
|
|
while cur is not None:
|
|
n += 1
|
|
cur = cur.next
|
|
return n
|
|
|
|
def first_sibling(self) -> "LinkedNode" | t.CPtr:
|
|
"""Return the head node of sibling linked list (traverse backward via prev pointers)."""
|
|
if self is None: return None
|
|
cur: "LinkedNode" | t.CPtr = self
|
|
while cur.prev is not None:
|
|
cur = cur.prev
|
|
return cur
|
|
|
|
def last_sibling(self) -> "LinkedNode" | t.CPtr:
|
|
"""Return the tail node of sibling linked list (traverse forward via next pointers)."""
|
|
if self is None: return None
|
|
cur: "LinkedNode" | t.CPtr = self
|
|
while cur.next is not None:
|
|
cur = cur.next
|
|
return cur
|
|
|
|
|
|
# ============================================================
|
|
# SListNode: Singly linked list node (@t.NoVTable)
|
|
#
|
|
# Lightweight singly linked list with only Next pointer. No prev/parent/child.
|
|
# Suitable for queues, stacks and simple linked structures
|
|
# (such as Param/BasicBlock/Function/Line/Value in llvmlite).
|
|
# O(1) append requires caller to maintain tail pointer (append_after).
|
|
# O(n) append works with head pointer only (append).
|
|
# ============================================================
|
|
@t.NoVTable
|
|
class SListNode:
|
|
Next: "SListNode" | t.CPtr # Next node
|
|
|
|
def append(self, node: "SListNode" | t.CPtr) -> "SListNode" | t.CPtr:
|
|
"""Append node to the end of the linked list (O(n)). Return self (head).
|
|
|
|
If self is None, the caller shall adopt node directly as the new head.
|
|
"""
|
|
if self is None:
|
|
return node
|
|
if node is None:
|
|
return self
|
|
node.Next = None
|
|
cur: "SListNode" | t.CPtr = self
|
|
while cur.Next is not None:
|
|
cur = cur.Next
|
|
cur.Next = node
|
|
return self
|
|
|
|
def append_after(self, node: "SListNode" | t.CPtr) -> "SListNode" | t.CPtr:
|
|
"""Insert node immediately after self (O(1)). Return new tail (node).
|
|
|
|
Caller must maintain the head pointer manually. Return node as head if self is None.
|
|
"""
|
|
if node is None:
|
|
return self
|
|
node.Next = None
|
|
if self is not None:
|
|
self.Next = node
|
|
return node
|
|
|
|
def count(self) -> t.CSizeT:
|
|
"""Calculate list length via full traversal (O(n))."""
|
|
if self is None:
|
|
return 0
|
|
n: t.CSizeT = 0
|
|
cur: "SListNode" | t.CPtr = self
|
|
while cur is not None:
|
|
n += 1
|
|
cur = cur.Next
|
|
return n
|
|
|
|
def at(self, index: t.CSizeT) -> "SListNode" | t.CPtr:
|
|
"""Return node at zero-based index (O(n)). Return None if index out of bounds."""
|
|
if self is None:
|
|
return None
|
|
cur: "SListNode" | t.CPtr = self
|
|
i: t.CSizeT = 0
|
|
while cur is not None:
|
|
if i == index:
|
|
return cur
|
|
i += 1
|
|
cur = cur.Next
|
|
return None
|
|
|
|
def remove(self, node: "SListNode" | t.CPtr) -> "SListNode" | t.CPtr:
|
|
"""Remove node from list (O(n)). Return updated head.
|
|
|
|
The Next pointer of node will be cleared. If node is the original head, return head.Next.
|
|
"""
|
|
if self is None or node is None:
|
|
return self
|
|
if self is node:
|
|
new_head: "SListNode" | t.CPtr = self.Next
|
|
self.Next = None
|
|
return new_head
|
|
cur: "SListNode" | t.CPtr = self
|
|
while cur.Next is not None:
|
|
if cur.Next is node:
|
|
cur.Next = node.Next
|
|
node.Next = None
|
|
break
|
|
cur = cur.Next
|
|
return self
|
|
|
|
|
|
# ============================================================
|
|
# GSListNode[T]: Generic singly linked list node (@t.NoVTable + PEP 695 generics)
|
|
#
|
|
# Unlike SListNode (non-generic, Next: SListNode|t.CPtr), the Next field of GSListNode[T]
|
|
# is typed as T|t.CPtr. After specialization it becomes a concrete node type directly,
|
|
# eliminating downcasts.
|
|
#
|
|
# Recursive generic usage pattern (subclass inherits from self-specialized variant):
|
|
# class GNode(GSListNode[GNode]):
|
|
# value: t.CInt
|
|
# # At this point GNode.Next: GNode|t.CPtr (strongly typed, no casting required)
|
|
#
|
|
# Technical validation targets:
|
|
# 1. Recursive generics: class GNode(GSListNode[GNode])
|
|
# 2. Combination of @t.NoVTable attribute with generics
|
|
# 3. Generic class used as field type: list: GSList[GNode]
|
|
# 4. Inheritance of generic methods (GSList[T].append available after specialization)
|
|
# ============================================================
|
|
@t.NoVTable
|
|
class GSListNode[T]:
|
|
Next: T | t.CPtr
|
|
|
|
|
|
# ============================================================
|
|
# GSList[T]: Generic singly linked list container (@t.NoVTable + PEP 695 generics)
|
|
#
|
|
# Holds Head, Tail and Count. Provides O(1) append and O(n) indexed access.
|
|
# Node type T must inherit from GSListNode[T] (to acquire the Next field).
|
|
#
|
|
# Usage:
|
|
# list: GSList[GNode] | t.CPtr = GSList[GNode]()
|
|
# list.append(node) # Method invocation, not free function
|
|
# ============================================================
|
|
@t.NoVTable
|
|
class GSList[T]:
|
|
Head: T | t.CPtr
|
|
Tail: T | t.CPtr
|
|
Count: t.CSizeT
|
|
|
|
def __init__(self):
|
|
self.Head = None
|
|
self.Tail = None
|
|
self.Count = 0
|
|
|
|
def append(self, node: T):
|
|
"""Append node to the end of the list (O(1))."""
|
|
if node is None:
|
|
return
|
|
node.Next = None
|
|
if self.Head is None:
|
|
self.Head = node
|
|
else:
|
|
self.Tail.Next = node
|
|
self.Tail = node
|
|
self.Count += 1
|
|
|
|
def count(self) -> t.CSizeT:
|
|
"""Return node count (O(1))."""
|
|
return self.Count
|
|
|
|
def at(self, index: t.CSizeT) -> T | t.CPtr:
|
|
"""Return node at zero-based index (O(n)). Return None if index out of bounds."""
|
|
if self.Head is None:
|
|
return None
|
|
cur: T | t.CPtr = self.Head
|
|
i: t.CSizeT = 0
|
|
while cur is not None:
|
|
if i == index:
|
|
return cur
|
|
i += 1
|
|
cur = cur.Next
|
|
return None
|
|
|
|
|
|
# ============================================================
|
|
# GTreeNode[T]: Generic tree node (@t.NoVTable + PEP 695 generics)
|
|
#
|
|
# Type-safe variant of LinkedNode. All pointer fields are typed T|t.CPtr.
|
|
# Capabilities provided:
|
|
# - Bidirectional sibling linked list (Next/Prev) → O(1) removal
|
|
# - Parent-child tree structure (Child/LastChild/Parent) → O(1) child appending
|
|
# - Child counter (Count)
|
|
#
|
|
# Recursive generic usage pattern (subclass inherits self-specialized variant):
|
|
# @t.CVTable
|
|
# class AST(GTreeNode[AST]):
|
|
# def kind(self) -> t.CInt:
|
|
# return 0
|
|
# # At this point AST.Next/Prev/Child/LastChild/Parent are all AST|t.CPtr (strongly typed)
|
|
#
|
|
# Fields are flattened and embedded into subclasses with no vtable overhead
|
|
# (equivalent to non-polymorphic inheritance in C++).
|
|
# If a subclass is marked @t.CVTable, it receives its own vtable (at offset 0),
|
|
# and GTreeNode fields are laid out after the vtable.
|
|
#
|
|
# OOP methods are automatically wrapped for subclasses via _generate_inherited_method_wrappers
|
|
# (self pointer cast to base type before invocation). Inherited methods can be called directly by subclasses.
|
|
# ============================================================
|
|
@t.NoVTable
|
|
class GTreeNode[T]:
|
|
Next: T | t.CPtr # Next sibling node
|
|
Prev: T | t.CPtr # Previous sibling node (doubly linked list, O(1) removal)
|
|
Child: T | t.CPtr # First child node
|
|
LastChild: T | t.CPtr # Last child node (O(1) append)
|
|
Parent: T | t.CPtr # Parent node
|
|
Count: t.CSizeT # Number of child nodes
|
|
|
|
def append(self, node: T | t.CPtr):
|
|
"""Append node as the last child of self (O(1)).
|
|
|
|
If node is already attached to any list, the caller shall invoke node.detach() prior to append.
|
|
"""
|
|
if self is None or node is None:
|
|
return
|
|
node.Parent = self
|
|
node.Next = None
|
|
node.Prev = self.LastChild
|
|
if self.Child is None:
|
|
self.Child = node
|
|
else:
|
|
self.LastChild.Next = node
|
|
self.LastChild = node
|
|
self.Count += 1
|
|
|
|
def prepend(self, node: T | t.CPtr):
|
|
"""Insert node as the first child of self (O(1))."""
|
|
if self is None or node is None:
|
|
return
|
|
node.Parent = self
|
|
node.Prev = None
|
|
node.Next = self.Child
|
|
if self.Child is not None:
|
|
self.Child.Prev = node
|
|
else:
|
|
self.LastChild = node
|
|
self.Child = node
|
|
self.Count += 1
|
|
|
|
def detach(self):
|
|
"""Detach self from its sibling linked list (O(1)).
|
|
|
|
After invocation, self.Next/self.Prev/self.Parent are cleared. The child chain remains untouched.
|
|
"""
|
|
if self is None:
|
|
return
|
|
if self.Prev is not None:
|
|
self.Prev.Next = self.Next
|
|
elif self.Parent is not None:
|
|
self.Parent.Child = self.Next
|
|
if self.Next is not None:
|
|
self.Next.Prev = self.Prev
|
|
elif self.Parent is not None:
|
|
self.Parent.LastChild = self.Prev
|
|
if self.Parent is not None:
|
|
if self.Parent.Count > 0:
|
|
self.Parent.Count -= 1
|
|
self.Next = None
|
|
self.Prev = None
|
|
self.Parent = None
|
|
|
|
def has_children(self) -> t.CInt:
|
|
"""Return 1 if child nodes exist, otherwise return 0."""
|
|
if self is None:
|
|
return 0
|
|
if self.Child is not None:
|
|
return 1
|
|
return 0
|
|
|
|
def child_at(self, index: t.CSizeT) -> T | t.CPtr:
|
|
"""Return the child node at specified zero-based index (O(n)). Return None if index out of bounds."""
|
|
if self is None:
|
|
return None
|
|
cur: T | t.CPtr = self.Child
|
|
i: t.CSizeT = 0
|
|
while cur is not None:
|
|
if i == index:
|
|
return cur
|
|
i += 1
|
|
cur = cur.Next
|
|
return None
|
|
|
|
def count_children(self) -> t.CSizeT:
|
|
"""Count children by traversal (O(n)), used to validate Count field."""
|
|
if self is None:
|
|
return 0
|
|
n: t.CSizeT = 0
|
|
cur: T | t.CPtr = self.Child
|
|
while cur is not None:
|
|
n += 1
|
|
cur = cur.Next
|
|
return n
|