symbols_test.mx raw
1 package iskra
2
3 import (
4 "fmt"
5 "testing"
6 )
7
8 func TestExtractSymbols(t *testing.T) {
9 dump := "FuncDecl RuneBytes\n Params\n r rune\n Results\n []byte\n Block\n Assign rs := [UTFMax]\n Return [rs,EncodeRune,r]\n"
10
11 st := ExtractSymbols(dump)
12 if st == nil {
13 t.Fatal("nil symbol table")
14 }
15
16 if st.Counts["RuneBytes"] != 1 {
17 t.Errorf("RuneBytes count: got %d, want 1", st.Counts["RuneBytes"])
18 }
19 if st.Counts["UTFMax"] != 1 {
20 t.Errorf("UTFMax count: got %d, want 1", st.Counts["UTFMax"])
21 }
22 if st.Counts["EncodeRune"] != 1 {
23 t.Errorf("EncodeRune count: got %d, want 1", st.Counts["EncodeRune"])
24 }
25 if st.Counts["rs"] != 2 {
26 t.Errorf("rs count: got %d, want 2 (decl+ref)", st.Counts["rs"])
27 }
28 if st.Counts["r"] != 2 {
29 t.Errorf("r count: got %d, want 2 (param+ref)", st.Counts["r"])
30 }
31 }
32
33 func TestCompareSymbols(t *testing.T) {
34 dumpA := "FuncDecl Foo\n Block\n Assign x := [Bar]\n Return [x]\n"
35 dumpB := "FuncDecl Foo\n Block\n Assign x := [Bar]\n Return [x]\n"
36
37 stA := ExtractSymbols(dumpA)
38 stB := ExtractSymbols(dumpB)
39 m := CompareSymbols(stA, stB)
40
41 if !m.IsEquivalent() {
42 t.Errorf("identical dumps should be equivalent, onlyA=%v onlyB=%v", m.OnlyInA, m.OnlyInB)
43 }
44 if m.Score() != 100 {
45 t.Errorf("score: got %d, want 100", m.Score())
46 }
47
48 dumpC := "FuncDecl Foo\n Block\n Assign y := [Baz]\n Return [y]\n"
49 stC := ExtractSymbols(dumpC)
50 m2 := CompareSymbols(stA, stC)
51 if m2.IsEquivalent() {
52 t.Error("different dumps should not be equivalent")
53 }
54 }
55
56 func TestIsomorphicIdentical(t *testing.T) {
57 // Same structure, same names → isomorphic.
58 dump := "FuncDecl Foo\n Params\n x int\n Block\n Assign y := [x]\n Return [y]\n"
59
60 st := ExtractSymbols(dump)
61 bg := ExtractBindings(st)
62 sig := bg.Signature()
63
64 if !sig.IsIsomorphic(sig) {
65 t.Error("a signature should be isomorphic to itself")
66 }
67 }
68
69 func TestIsomorphicRenamed(t *testing.T) {
70 // Same structure, different names → should be isomorphic.
71 dumpA := "FuncDecl Foo\n Params\n x int\n Block\n Assign y := [x]\n Return [y]\n"
72 dumpB := "FuncDecl Bar\n Params\n a int\n Block\n Assign b := [a]\n Return [b]\n"
73
74 stA := ExtractSymbols(dumpA)
75 stB := ExtractSymbols(dumpB)
76 bgA := ExtractBindings(stA)
77 bgB := ExtractBindings(stB)
78 sigA := bgA.Signature()
79 sigB := bgB.Signature()
80
81 if !sigA.IsIsomorphic(sigB) {
82 t.Error("renamed variables should still be isomorphic")
83 t.Logf("sigA locals: %v externs: %v", sigA.Locals, sigA.Externs)
84 t.Logf("sigB locals: %v externs: %v", sigB.Locals, sigB.Externs)
85 }
86 if GeometricDistance(sigA, sigB) != 0 {
87 t.Errorf("distance should be 0 for isomorphic sigs, got %d", GeometricDistance(sigA, sigB))
88 }
89 }
90
91 func TestNonIsomorphicExtraBinding(t *testing.T) {
92 // Different structure: extra variable.
93 dumpA := "FuncDecl F\n Params\n x int\n Block\n Return [x]\n"
94 dumpB := "FuncDecl G\n Params\n x int\n Block\n Assign y := [x]\n Return [y]\n"
95
96 stA := ExtractSymbols(dumpA)
97 stB := ExtractSymbols(dumpB)
98 sigA := ExtractBindings(stA).Signature()
99 sigB := ExtractBindings(stB).Signature()
100
101 if sigA.IsIsomorphic(sigB) {
102 t.Error("different binding counts should not be isomorphic")
103 }
104 dist := GeometricDistance(sigA, sigB)
105 if dist == 0 {
106 t.Error("distance should be > 0 for non-isomorphic sigs")
107 }
108 }
109
110 func TestIsomorphicNestedScope(t *testing.T) {
111 // Same nesting structure, different names.
112 dumpA := "FuncDecl F\n Block\n Assign x := [ExtA]\n If [x]\n Block\n Assign y := [x]\n Return [y]\n"
113 dumpB := "FuncDecl G\n Block\n Assign a := [ExtB]\n If [a]\n Block\n Assign b := [a]\n Return [b]\n"
114
115 stA := ExtractSymbols(dumpA)
116 stB := ExtractSymbols(dumpB)
117 sigA := ExtractBindings(stA).Signature()
118 sigB := ExtractBindings(stB).Signature()
119
120 if !sigA.IsIsomorphic(sigB) {
121 t.Error("same nesting with renamed vars should be isomorphic")
122 t.Logf("sigA locals: %v externs: %v", sigA.Locals, sigA.Externs)
123 t.Logf("sigB locals: %v externs: %v", sigB.Locals, sigB.Externs)
124 }
125 }
126
127 func TestIsomorphicDifferentDepth(t *testing.T) {
128 // Same names, but variable declared at different depth → not isomorphic.
129 dumpA := "FuncDecl F\n Block\n Assign x :=\n Return [x]\n"
130 dumpB := "FuncDecl F\n Block\n If\n Block\n Assign x :=\n Return [x]\n"
131
132 stA := ExtractSymbols(dumpA)
133 stB := ExtractSymbols(dumpB)
134 sigA := ExtractBindings(stA).Signature()
135 sigB := ExtractBindings(stB).Signature()
136
137 if sigA.IsIsomorphic(sigB) {
138 t.Error("different declaration depths should not be isomorphic")
139 }
140 }
141
142 func TestExternalRefsGeometry(t *testing.T) {
143 // External refs (package-level constants) should be tracked
144 // by count and depth, not by name.
145 dumpA := "FuncDecl F\n Block\n Return [RuneError,RuneError]\n"
146 dumpB := "FuncDecl G\n Block\n Return [MaxRune,MaxRune]\n"
147
148 stA := ExtractSymbols(dumpA)
149 stB := ExtractSymbols(dumpB)
150 sigA := ExtractBindings(stA).Signature()
151 sigB := ExtractBindings(stB).Signature()
152
153 if !sigA.IsIsomorphic(sigB) {
154 t.Error("same external ref pattern should be isomorphic regardless of name")
155 }
156 }
157
158 func TestRealDecodeRuneGeometry(t *testing.T) {
159 dump := "FuncDecl DecodeRune\n Params\n p []byte\n Results\n r rune\n size int\n Block\n Assign n := [p]\n If [n]\n Block\n Return [RuneError]\n Assign p0 := [p]\n Assign x := [first,p0]\n If [x,as]\n Block\n Assign mask := [x]\n Return [p,mask,RuneError]\n Assign sz := [x]\n Assign accept := [acceptRanges,x]\n If [n,sz]\n Block\n Return [RuneError]\n Assign b1 := [p]\n If [b1,accept,lo,hi]\n Block\n Return [RuneError]\n If [sz]\n Block\n Return [p0,mask2,b1,maskx]\n Assign b2 := [p]\n If [b2,locb,hicb]\n Block\n Return [RuneError]\n If [sz]\n Block\n Return [p0,mask3,b1,maskx,b2]\n Assign b3 := [p]\n If [b3,locb,hicb]\n Block\n Return [RuneError]\n Return [p0,mask4,b1,maskx,b2,b3]\n"
160
161 st := ExtractSymbols(dump)
162 bg := ExtractBindings(st)
163 sig := bg.Signature()
164
165 // Sanity: should have local bindings for DecodeRune, p, r, size,
166 // n, p0, x, mask, sz, accept, b1, b2, b3.
167 if len(bg.Bindings) < 10 {
168 t.Errorf("expected at least 10 local bindings, got %d", len(bg.Bindings))
169 }
170
171 // Should have external refs for RuneError, first, acceptRanges,
172 // as, lo, hi, mask2, mask3, mask4, maskx, locb, hicb.
173 if len(bg.Externals) < 5 {
174 t.Errorf("expected at least 5 external refs, got %d", len(bg.Externals))
175 }
176
177 // Self-isomorphism.
178 if !sig.IsIsomorphic(sig) {
179 t.Error("DecodeRune should be isomorphic to itself")
180 }
181
182 fmt.Printf("DecodeRune: %d locals, %d externals\n", len(bg.Bindings), len(bg.Externals))
183 for i, b := range bg.Bindings {
184 fmt.Printf(" local[%d]: decl@%d refs@%v\n", i, b.DeclDepth, b.RefDepths)
185 }
186 for i, e := range bg.Externals {
187 fmt.Printf(" extern[%d]: count=%d depths=%v\n", i, e.RefCount, e.Depths)
188 }
189 }
190