import string import testcheck import linkedlist import viperlib import stdlib # Subclass of linkedlist.LinkedNode: flattened fields, no vtable; OOP wrappers are auto-generated. # Cast LinkedNode* back to Node* explicitly: (Node | t.CPtr)(ptr) class Node(linkedlist.LinkedNode): value: int # --- Test 1: append + child_count --- def test_append(): testcheck.section("Test 1: append") root: Node = Node() a: Node = Node() b: Node = Node() c: Node = Node() a.value = 10 b.value = 20 c.value = 30 root.append(a) root.append(b) root.append(c) testcheck.check(root.child_count == 3, "append count=3", "append count fail") # verify order (explicit downcast LinkedNode* -> Node*) c1: Node* = (Node*)(root.child) c2: Node* = (Node*)(c1.next) c3: Node* = (Node*)(c2.next) testcheck.check(c1.value == 10 and c2.value == 20 and c3.value == 30, "order 10,20,30", "order fail") # --- Test 2: doubly linked prev --- def test_prev_link(): testcheck.section("Test 2: prev pointer (doubly linked)") root: Node = Node() a: Node = Node() b: Node = Node() a.value = 1 b.value = 2 root.append(a) root.append(b) # b.prev should point to a bp: Node* = (Node*)(b.prev) testcheck.check(bp is not None and bp.value == 1, "b.prev.value=1", "prev link fail") # a.prev should be None (first child) testcheck.check(a.prev is None, "a.prev=None", "first prev fail") # --- Test 3: last_child pointer --- def test_last_child(): testcheck.section("Test 3: last_child pointer") root: Node = Node() a: Node = Node() b: Node = Node() a.value = 100 b.value = 200 root.append(a) lc: Node* = (Node*)(root.last_child) testcheck.check(lc is not None and lc.value == 100, "last_child=100 after 1 append", "last_child fail 1") root.append(b) lc = (Node*)(root.last_child) testcheck.check(lc is not None and lc.value == 200, "last_child=200 after 2 append", "last_child fail 2") # --- Test 4: detach (O(1) unlink) --- def test_remove(): testcheck.section("Test 4: detach (O(1) unlink)") root: Node = Node() a: Node = Node() b: Node = Node() c: Node = Node() a.value = 1 b.value = 2 c.value = 3 root.append(a) root.append(b) root.append(c) # detach middle node b b.detach() testcheck.check(root.child_count == 2, "count=2 after detach", "detach count fail") # a.next should point directly to c an: Node* = (Node*)(a.next) testcheck.check(an is not None and an.value == 3, "a.next.value=3", "detach link fail") # c.prev should point to a cp: Node* = (Node*)(c.prev) testcheck.check(cp is not None and cp.value == 1, "c.prev.value=1", "detach prev fail") # b links should be cleared testcheck.check(b.parent is None and b.next is None and b.prev is None, "b cleared", "b not cleared") # --- Test 5: remove_child --- def test_remove_child(): testcheck.section("Test 5: remove_child") root: Node = Node() a: Node = Node() b: Node = Node() a.value = 10 b.value = 20 root.append(a) root.append(b) root.remove_child(a) testcheck.check(root.child_count == 1, "count=1 after remove_child", "remove_child count fail") fc: Node* = (Node*)(root.child) testcheck.check(fc is not None and fc.value == 20, "first child=20", "remove_child first fail") # --- Test 6: prepend/insert_before/insert_after --- def test_insert_ops(): testcheck.section("Test 6: prepend/insert_before/insert_after") root: Node = Node() a: Node = Node() b: Node = Node() c: Node = Node() d: Node = Node() a.value = 1 b.value = 2 c.value = 3 d.value = 4 root.append(a) # [1] root.append(c) # [1, 3] root.insert_before(c, b) # [1, 2, 3] insert b before c root.insert_after(a, d) # [1, 4, 2, 3] insert d after a # traverse to verify order cur: Node* = (Node*)(root.child) v0: int = cur.value cur = (Node*)(cur.next) v1: int = cur.value cur = (Node*)(cur.next) v2: int = cur.value cur = (Node*)(cur.next) v3: int = cur.value testcheck.check(v0 == 1 and v1 == 4 and v2 == 2 and v3 == 3, "order 1,4,2,3", "insert order fail") testcheck.check(root.child_count == 4, "count=4", "insert count fail") # --- Test 7: child_at + count_children --- # Node() is stack-allocated; use distinct variables so each node has a unique address. def test_index_access(): testcheck.section("Test 7: child_at + count_children") root: Node = Node() n0: Node = Node() n1: Node = Node() n2: Node = Node() n3: Node = Node() n4: Node = Node() n0.value = 0 n1.value = 10 n2.value = 20 n3.value = 30 n4.value = 40 root.append(n0) root.append(n1) root.append(n2) root.append(n3) root.append(n4) # child_at r0: Node* = (Node*)(root.child_at(0)) r2: Node* = (Node*)(root.child_at(2)) r4: Node* = (Node*)(root.child_at(4)) r5: Node* = (Node*)(root.child_at(5)) testcheck.check(r0 is not None and r0.value == 0, "child_at(0)=0", "child_at 0 fail") testcheck.check(r2 is not None and r2.value == 20, "child_at(2)=20", "child_at 2 fail") testcheck.check(r4 is not None and r4.value == 40, "child_at(4)=40", "child_at 4 fail") testcheck.check(r5 is None, "child_at(5)=None", "child_at 5 fail") # count_children cc: sizet = root.count_children() testcheck.check(cc == 5, "count_children=5", "count_children fail") # --- Test 8: has_children + sibling traverse --- def test_sibling_traverse(): testcheck.section("Test 8: has_children + sibling traverse") root: Node = Node() testcheck.check(root.has_children() == 0, "empty has_children=0", "has_children empty fail") a: Node = Node() b: Node = Node() c: Node = Node() a.value = 1 b.value = 2 c.value = 3 root.append(a) root.append(b) root.append(c) testcheck.check(root.has_children() == 1, "has_children=1", "has_children fail") # find first/last sibling starting from b fs: Node* = (Node*)(b.first_sibling()) ls: Node* = (Node*)(b.last_sibling()) testcheck.check(fs is not None and fs.value == 1, "first_sibling=1", "first_sibling fail") testcheck.check(ls is not None and ls.value == 3, "last_sibling=3", "last_sibling fail") # --- Test 9: unlink --- def test_unlink(): testcheck.section("Test 9: unlink") root: Node = Node() a: Node = Node() b: Node = Node() a.value = 1 b.value = 2 root.append(a) root.append(b) # unlink a: remove from sibling chain and clear parent/child links a.unlink() testcheck.check(a.next is None and a.prev is None and a.parent is None, "a links cleared", "a links not cleared") testcheck.check(a.child is None and a.last_child is None and a.child_count == 0, "a child cleared", "a child not cleared") # root should keep only b testcheck.check(root.child_count == 1, "root count=1 after unlink", "root count fail") # --- Test 10: SListNode (singly linked, OOP) --- class SNode(linkedlist.SListNode): value: int def test_slist(): testcheck.section("Test 10: SListNode (singly linked, OOP)") n0: SNode = SNode() n1: SNode = SNode() n2: SNode = SNode() n0.value = 100 n1.value = 200 n2.value = 300 # append (O(n)); use n0 as head head: linkedlist.SListNode* = n0 head = head.append(n1) head = head.append(n2) # count testcheck.check(head.count() == 3, "count=3", "count fail") # at a0: SNode* = (SNode*)(head.at(0)) a1: SNode* = (SNode*)(head.at(1)) a2: SNode* = (SNode*)(head.at(2)) a3: SNode* = (SNode*)(head.at(3)) testcheck.check(a0 is not None and a0.value == 100, "at(0)=100", "at 0 fail") testcheck.check(a1 is not None and a1.value == 200, "at(1)=200", "at 1 fail") testcheck.check(a2 is not None and a2.value == 300, "at(2)=300", "at 2 fail") testcheck.check(a3 is None, "at(3)=None", "at 3 fail") # remove (drop middle node n1) head = head.remove(n1) testcheck.check(head.count() == 2, "after remove count=2", "remove count fail") testcheck.check(n1.Next is None, "n1.Next cleared", "n1.Next not cleared") # after removal a0.Next should point to n2 a0b: SNode* = (SNode*)(a0.Next) testcheck.check(a0b is not None and a0b.value == 300, "after remove a0.Next=300", "a0.Next fail") # append_after (O(1)) n3: SNode = SNode() n3.value = 400 tail: linkedlist.SListNode* = n2.append_after(n3) testcheck.check(tail is n3, "append_after returns n3", "append_after fail") testcheck.check(head.count() == 3, "after append count=3", "append count fail") # --- Test 11: viperlib.sprintf *args forwarding --- def test_sprintf_vararg(): testcheck.section("Test 11: viperlib.sprintf *args forwarding") # single %d argument buf: char* = stdlib.malloc(32) if buf is None: return buf[0] = 0 viperlib.sprintf(buf, "key%d", 42) testcheck.check(string.strcmp(buf, "key42") == 0, "sprintf key42", "sprintf key%d fail") # no argument buf2: char* = stdlib.malloc(32) if buf2 is None: return buf2[0] = 0 viperlib.sprintf(buf2, "hello") testcheck.check(string.strcmp(buf2, "hello") == 0, "sprintf hello", "sprintf no-arg fail") # multiple arguments buf3: char* = stdlib.malloc(32) if buf3 is None: return buf3[0] = 0 viperlib.sprintf(buf3, "%d+%d=%d", 1, 2, 3) testcheck.check(string.strcmp(buf3, "1+2=3") == 0, "sprintf 1+2=3", "sprintf multi-arg fail") # --- Test 12: generic GSListNode[T] + GSList[T] --- # Recursive generic, novirtual flattened fields, generic as field type; local def (no cross-module generic support yet). novirtual class GSListNode[T]: Next: T* novirtual class GSList[T]: Head: T* Tail: T* Count: sizet def __init__(self): self.Head = None self.Tail = None self.Count = 0 def append(self, node: T): 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) -> sizet: return self.Count def at(self, index: sizet) -> T*: if self.Head is None: return None cur: T* = self.Head i: sizet = 0 while cur is not None: if i == index: return cur i += 1 cur = cur.Next return None class GNode(GSListNode[GNode]): Value: int def test_generic_slist(): testcheck.section("Test 12: Generic GSListNode[T] + GSList[T]") # generic class as field type + construction lst: GSList[GNode]* = GSList[GNode]() # recursive generic + NoVTable combo (GNode defined above) n0: GNode = GNode() n1: GNode = GNode() n2: GNode = GNode() n0.Value = 10 n1.Value = 20 n2.Value = 30 # generic method inheritance lst.append(n0) lst.append(n1) lst.append(n2) # count (O(1)) testcheck.check(lst.count() == 3, "GSList.count()=3", "GSList count fail") # at (O(n)) returns GNode|CPtr, no downcast needed a0: GNode* = lst.at(0) a1: GNode* = lst.at(1) a2: GNode* = lst.at(2) a3: GNode* = lst.at(3) testcheck.check(a0 is not None and a0.Value == 10, "GSList.at(0)=10", "GSList at 0 fail") testcheck.check(a1 is not None and a1.Value == 20, "GSList.at(1)=20", "GSList at 1 fail") testcheck.check(a2 is not None and a2.Value == 30, "GSList.at(2)=30", "GSList at 2 fail") testcheck.check(a3 is None, "GSList.at(3)=None", "GSList at 3 fail") # Next is strongly typed (a0.Next is GNode|CPtr, no cast) testcheck.check(a0.Next is a1, "a0.Next is a1 (strong type)", "a0.Next fail") testcheck.check(a1.Next is a2, "a1.Next is a2 (strong type)", "a1.Next fail") testcheck.check(a2.Next is None, "a2.Next is None", "a2.Next fail") def main() -> int: testcheck.begin("NoVTableTest: linkedlist 库(@t.NoVTable OOP 方法 + 字段展平继承)") test_append() test_prev_link() test_last_child() test_remove() test_remove_child() test_insert_ops() test_index_access() test_sibling_traverse() test_unlink() test_slist() test_sprintf_vararg() test_generic_slist() return testcheck.end()