把 81 个 .vp 文件的中文 # 注释与三引号文档串改写为简洁英文,每个连续注释块 <=2 行;运行时中文字符串字面量(testcheck.begin/print 等用户可见消息)保持原样。骨架比对 HEAD 与工作区 112 文件漂移 0,全量 77/77 顺序回归通过。
470 lines
14 KiB
Plaintext
470 lines
14 KiB
Plaintext
import t
|
|
import stdio
|
|
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()
|