A Golang runtime and compilation backend for Delta Interaction Nets.
1package deltanet
2
3import (
4 "fmt"
5 "testing"
6)
7
8// TestStringConcat tests concatenating two strings using native functions
9func TestStringConcat(t *testing.T) {
10 net := NewNetwork()
11
12 // Register concat native function
13 net.RegisterNative("concat", func(a interface{}) (interface{}, error) {
14 // This returns a function that captures 'a' (first string)
15 s1, ok := a.(string)
16 if !ok {
17 return nil, fmt.Errorf("concat: first arg must be string, got %T", a)
18 }
19
20 // Return a native function that takes the second argument
21 return func(b interface{}) (interface{}, error) {
22 s2, ok := b.(string)
23 if !ok {
24 return nil, fmt.Errorf("concat: second arg must be string, got %T", b)
25 }
26 return s1 + s2, nil
27 }, nil
28 })
29
30 // Build net structure for: concat "hello" "world"
31 // This is: (concat "hello") "world"
32 // Structure: Fan(App) where:
33 // Fan.0 -> connects to another Fan(App) for (concat "hello")
34 // Fan.2 -> Data("world")
35 // Fan.1 -> result
36
37 // Inner application: concat "hello"
38 innerFan := net.NewFan()
39 concatNode := net.NewNative("concat")
40 helloData := net.NewData("hello")
41
42 net.Link(innerFan, 0, concatNode, 0) // Function
43 net.Link(innerFan, 2, helloData, 0) // Argument
44 // innerFan.1 is the result of (concat "hello")
45
46 // Outer application: (concat "hello") "world"
47 outerFan := net.NewFan()
48 worldData := net.NewData("world")
49
50 net.Link(outerFan, 0, innerFan, 1) // Function (result of inner app)
51 net.Link(outerFan, 2, worldData, 0) // Argument
52 // outerFan.1 is the final result
53
54 // Connect result to output var
55 output := net.NewVar()
56 net.Link(outerFan, 1, output, 0)
57
58 // Reduce
59 net.ReduceAll()
60
61 // Check result
62 resultNode, resultPort := net.GetLink(output, 0)
63 if resultNode == nil {
64 t.Fatal("Result node is nil")
65 }
66 if resultNode.Type() != NodeTypeData {
67 t.Errorf("Expected result to be Data node, got %v", resultNode.Type())
68 }
69
70 result := resultNode.GetValue()
71 expected := "helloworld"
72 if result != expected {
73 t.Errorf("Expected %q, got %v", expected, result)
74 }
75
76 t.Logf("Result: %v (port %d)", result, resultPort)
77}
78
79// TestStringLength tests computing the length of a string
80func TestStringLength(t *testing.T) {
81 net := NewNetwork()
82
83 // Register length native function
84 net.RegisterNative("length", func(v interface{}) (interface{}, error) {
85 s, ok := v.(string)
86 if !ok {
87 return nil, fmt.Errorf("length: arg must be string, got %T", v)
88 }
89 return len(s), nil
90 })
91
92 // Build net structure for: length "hello"
93 // Structure: Fan(App) where:
94 // Fan.0 -> Native("length")
95 // Fan.2 -> Data("hello")
96 // Fan.1 -> result
97
98 fan := net.NewFan()
99 lengthNode := net.NewNative("length")
100 helloData := net.NewData("hello")
101
102 net.Link(fan, 0, lengthNode, 0) // Function
103 net.Link(fan, 2, helloData, 0) // Argument
104
105 // Connect result to output var
106 output := net.NewVar()
107 net.Link(fan, 1, output, 0)
108
109 // Reduce
110 net.ReduceAll()
111
112 // Check result
113 resultNode, resultPort := net.GetLink(output, 0)
114 if resultNode == nil {
115 t.Fatal("Result node is nil")
116 }
117 if resultNode.Type() != NodeTypeData {
118 t.Errorf("Expected result to be Data node, got %v", resultNode.Type())
119 }
120
121 result := resultNode.GetValue()
122 expected := 5
123 if result != expected {
124 t.Errorf("Expected %d, got %v", expected, result)
125 }
126
127 t.Logf("Result: %v (port %d)", result, resultPort)
128}
129
130// TestConcatLength tests composing concat and length
131// length (concat "hello" "world") should return 10
132func TestConcatLength(t *testing.T) {
133 net := NewNetwork()
134
135 // Register both natives
136 net.RegisterNative("concat", func(a interface{}) (interface{}, error) {
137 s1, ok := a.(string)
138 if !ok {
139 return nil, fmt.Errorf("concat: first arg must be string, got %T", a)
140 }
141 return func(b interface{}) (interface{}, error) {
142 s2, ok := b.(string)
143 if !ok {
144 return nil, fmt.Errorf("concat: second arg must be string, got %T", b)
145 }
146 return s1 + s2, nil
147 }, nil
148 })
149
150 net.RegisterNative("length", func(v interface{}) (interface{}, error) {
151 s, ok := v.(string)
152 if !ok {
153 return nil, fmt.Errorf("length: arg must be string, got %T", v)
154 }
155 return len(s), nil
156 })
157
158 // Build: length (concat "hello" "world")
159 // Structure:
160 // 1. Inner: concat "hello" -> partial
161 // 2. Middle: partial "world" -> "helloworld"
162 // 3. Outer: length "helloworld" -> 10
163
164 // Build concat application
165 concatInnerFan := net.NewFan()
166 concatNode := net.NewNative("concat")
167 helloData := net.NewData("hello")
168 net.Link(concatInnerFan, 0, concatNode, 0)
169 net.Link(concatInnerFan, 2, helloData, 0)
170
171 concatOuterFan := net.NewFan()
172 worldData := net.NewData("world")
173 net.Link(concatOuterFan, 0, concatInnerFan, 1)
174 net.Link(concatOuterFan, 2, worldData, 0)
175 // concatOuterFan.1 is "helloworld"
176
177 // Build length application
178 lengthFan := net.NewFan()
179 lengthNode := net.NewNative("length")
180 net.Link(lengthFan, 0, lengthNode, 0)
181 net.Link(lengthFan, 2, concatOuterFan, 1) // Apply to concat result
182
183 // Connect result
184 output := net.NewVar()
185 net.Link(lengthFan, 1, output, 0)
186
187 // Reduce
188 net.ReduceAll()
189
190 // Check result
191 resultNode, _ := net.GetLink(output, 0)
192 if resultNode == nil {
193 t.Fatal("Result node is nil")
194 }
195 if resultNode.Type() != NodeTypeData {
196 t.Errorf("Expected result to be Data node, got %v", resultNode.Type())
197 }
198
199 result := resultNode.GetValue()
200 expected := 10
201 if result != expected {
202 t.Errorf("Expected %d, got %v", expected, result)
203 }
204
205 t.Logf("length (concat \"hello\" \"world\") = %v", result)
206}