yukigo 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/.mocharc.json +3 -3
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +193 -199
  4. package/dist/analyzer/GraphBuilder.d.ts +29 -0
  5. package/dist/analyzer/GraphBuilder.js +99 -0
  6. package/dist/analyzer/index.d.ts +11 -23
  7. package/dist/analyzer/index.js +100 -58
  8. package/dist/analyzer/inspections/functional/functional.d.ts +44 -0
  9. package/dist/analyzer/inspections/functional/functional.js +149 -0
  10. package/dist/analyzer/inspections/functional/smells.d.ts +16 -0
  11. package/dist/analyzer/inspections/functional/smells.js +98 -0
  12. package/dist/analyzer/inspections/{generic.d.ts → generic/generic.d.ts} +70 -43
  13. package/dist/analyzer/inspections/generic/generic.js +604 -0
  14. package/dist/analyzer/inspections/generic/smells.d.ts +61 -0
  15. package/dist/analyzer/inspections/generic/smells.js +349 -0
  16. package/dist/analyzer/inspections/imperative/imperative.d.ts +35 -0
  17. package/dist/analyzer/inspections/imperative/imperative.js +109 -0
  18. package/dist/analyzer/inspections/imperative/smells.d.ts +16 -0
  19. package/dist/analyzer/inspections/imperative/smells.js +58 -0
  20. package/dist/analyzer/inspections/logic/logic.d.ts +32 -0
  21. package/dist/analyzer/inspections/logic/logic.js +96 -0
  22. package/dist/analyzer/inspections/logic/smells.d.ts +15 -0
  23. package/dist/analyzer/inspections/logic/smells.js +60 -0
  24. package/dist/analyzer/inspections/object/object.d.ts +88 -0
  25. package/dist/analyzer/inspections/object/object.js +319 -0
  26. package/dist/analyzer/inspections/object/smells.d.ts +30 -0
  27. package/dist/analyzer/inspections/object/smells.js +135 -0
  28. package/dist/analyzer/utils.d.ts +26 -4
  29. package/dist/analyzer/utils.js +71 -13
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +1 -0
  32. package/dist/interpreter/components/EnvBuilder.d.ts +9 -5
  33. package/dist/interpreter/components/EnvBuilder.js +100 -30
  34. package/dist/interpreter/components/Operations.d.ts +4 -4
  35. package/dist/interpreter/components/Operations.js +17 -2
  36. package/dist/interpreter/components/PatternMatcher.d.ts +47 -17
  37. package/dist/interpreter/components/PatternMatcher.js +264 -119
  38. package/dist/interpreter/components/RuntimeContext.d.ts +35 -0
  39. package/dist/interpreter/components/RuntimeContext.js +93 -0
  40. package/dist/interpreter/components/TestRunner.d.ts +18 -0
  41. package/dist/interpreter/components/TestRunner.js +103 -0
  42. package/dist/interpreter/components/Visitor.d.ts +63 -57
  43. package/dist/interpreter/components/Visitor.js +508 -173
  44. package/dist/interpreter/components/logic/LogicEngine.d.ts +29 -0
  45. package/dist/interpreter/components/logic/LogicEngine.js +259 -0
  46. package/dist/interpreter/components/logic/LogicResolver.d.ts +53 -0
  47. package/dist/interpreter/components/logic/LogicResolver.js +471 -0
  48. package/dist/interpreter/components/logic/LogicTranslator.d.ts +14 -0
  49. package/dist/interpreter/components/logic/LogicTranslator.js +99 -0
  50. package/dist/interpreter/components/runtimes/FunctionRuntime.d.ts +12 -0
  51. package/dist/interpreter/components/runtimes/FunctionRuntime.js +147 -0
  52. package/dist/interpreter/components/runtimes/LazyRuntime.d.ts +19 -0
  53. package/dist/interpreter/components/runtimes/LazyRuntime.js +269 -0
  54. package/dist/interpreter/components/runtimes/ObjectRuntime.d.ts +35 -0
  55. package/dist/interpreter/components/runtimes/ObjectRuntime.js +126 -0
  56. package/dist/interpreter/entities.d.ts +105 -0
  57. package/dist/interpreter/entities.js +96 -0
  58. package/dist/interpreter/errors.d.ts +1 -1
  59. package/dist/interpreter/index.d.ts +4 -12
  60. package/dist/interpreter/index.js +10 -13
  61. package/dist/interpreter/trampoline.d.ts +17 -0
  62. package/dist/interpreter/trampoline.js +38 -0
  63. package/dist/interpreter/utils.d.ts +4 -7
  64. package/dist/interpreter/utils.js +25 -17
  65. package/dist/tester/index.d.ts +25 -0
  66. package/dist/tester/index.js +108 -0
  67. package/dist/utils/helpers.d.ts +0 -4
  68. package/dist/utils/helpers.js +20 -24
  69. package/package.json +2 -2
  70. package/src/analyzer/GraphBuilder.ts +142 -0
  71. package/src/analyzer/index.ts +185 -132
  72. package/src/analyzer/inspections/functional/functional.ts +121 -0
  73. package/src/analyzer/inspections/functional/smells.ts +102 -0
  74. package/src/analyzer/inspections/{generic.ts → generic/generic.ts} +581 -499
  75. package/src/analyzer/inspections/generic/smells.ts +365 -0
  76. package/src/analyzer/inspections/imperative/imperative.ts +101 -0
  77. package/src/analyzer/inspections/imperative/smells.ts +54 -0
  78. package/src/analyzer/inspections/logic/logic.ts +90 -0
  79. package/src/analyzer/inspections/logic/smells.ts +54 -0
  80. package/src/analyzer/inspections/{object.ts → object/object.ts} +264 -282
  81. package/src/analyzer/inspections/object/smells.ts +144 -0
  82. package/src/analyzer/utils.ts +109 -26
  83. package/src/index.ts +3 -2
  84. package/src/interpreter/components/EnvBuilder.ts +202 -97
  85. package/src/interpreter/components/Operations.ts +99 -81
  86. package/src/interpreter/components/PatternMatcher.ts +475 -254
  87. package/src/interpreter/components/RuntimeContext.ts +119 -0
  88. package/src/interpreter/components/TestRunner.ts +151 -0
  89. package/src/interpreter/components/Visitor.ts +1065 -493
  90. package/src/interpreter/components/logic/LogicEngine.ts +519 -0
  91. package/src/interpreter/components/logic/LogicResolver.ts +858 -0
  92. package/src/interpreter/components/logic/LogicTranslator.ts +149 -0
  93. package/src/interpreter/components/runtimes/FunctionRuntime.ts +227 -0
  94. package/src/interpreter/components/runtimes/LazyRuntime.ts +334 -0
  95. package/src/interpreter/components/runtimes/ObjectRuntime.ts +224 -0
  96. package/src/interpreter/errors.ts +47 -47
  97. package/src/interpreter/index.ts +52 -59
  98. package/src/interpreter/trampoline.ts +71 -0
  99. package/src/interpreter/utils.ts +84 -79
  100. package/src/tester/index.ts +128 -0
  101. package/src/utils/helpers.ts +67 -73
  102. package/tests/analyzer/functional.spec.ts +207 -221
  103. package/tests/analyzer/generic.spec.ts +178 -100
  104. package/tests/analyzer/helpers.spec.ts +83 -83
  105. package/tests/analyzer/logic.spec.ts +237 -292
  106. package/tests/analyzer/oop.spec.ts +323 -338
  107. package/tests/analyzer/transitive.spec.ts +166 -0
  108. package/tests/interpreter/EnvBuilder.spec.ts +183 -178
  109. package/tests/interpreter/FunctionRuntime.spec.ts +223 -234
  110. package/tests/interpreter/LazyRuntime.spec.ts +225 -190
  111. package/tests/interpreter/LogicEngine.spec.ts +327 -194
  112. package/tests/interpreter/LogicSubstitution.spec.ts +80 -0
  113. package/tests/interpreter/ObjectRuntime.spec.ts +606 -0
  114. package/tests/interpreter/Operations.spec.ts +220 -220
  115. package/tests/interpreter/PatternSystem.spec.ts +213 -189
  116. package/tests/interpreter/Tests.spec.ts +122 -0
  117. package/tests/interpreter/interpreter.spec.ts +991 -937
  118. package/tests/tester/Tester.spec.ts +153 -0
  119. package/tsconfig.build.json +15 -7
  120. package/tsconfig.json +25 -17
  121. package/dist/analyzer/inspections/functional.d.ts +0 -46
  122. package/dist/analyzer/inspections/functional.js +0 -123
  123. package/dist/analyzer/inspections/generic.js +0 -427
  124. package/dist/analyzer/inspections/imperative.d.ts +0 -37
  125. package/dist/analyzer/inspections/imperative.js +0 -105
  126. package/dist/analyzer/inspections/logic.d.ts +0 -49
  127. package/dist/analyzer/inspections/logic.js +0 -140
  128. package/dist/analyzer/inspections/object.d.ts +0 -83
  129. package/dist/analyzer/inspections/object.js +0 -235
  130. package/dist/interpreter/components/FunctionRuntime.d.ts +0 -8
  131. package/dist/interpreter/components/FunctionRuntime.js +0 -52
  132. package/dist/interpreter/components/LazyRuntime.d.ts +0 -7
  133. package/dist/interpreter/components/LazyRuntime.js +0 -75
  134. package/dist/interpreter/components/LogicEngine.d.ts +0 -21
  135. package/dist/interpreter/components/LogicEngine.js +0 -152
  136. package/dist/interpreter/components/LogicResolver.d.ts +0 -11
  137. package/dist/interpreter/components/LogicResolver.js +0 -87
  138. package/src/analyzer/inspections/functional.ts +0 -159
  139. package/src/analyzer/inspections/imperative.ts +0 -129
  140. package/src/analyzer/inspections/logic.ts +0 -166
  141. package/src/interpreter/components/FunctionRuntime.ts +0 -79
  142. package/src/interpreter/components/LazyRuntime.ts +0 -97
  143. package/src/interpreter/components/LogicEngine.ts +0 -227
  144. package/src/interpreter/components/LogicResolver.ts +0 -130
@@ -1,234 +1,223 @@
1
- import { expect } from "chai";
2
- import {
3
- EquationRuntime,
4
- UnguardedBody,
5
- Sequence,
6
- Return,
7
- SymbolPrimitive,
8
- NumberPrimitive,
9
- LiteralPattern,
10
- VariablePattern,
11
- GuardedBody,
12
- } from "yukigo-ast";
13
- import { FunctionRuntime } from "../../src/interpreter/components/FunctionRuntime.js";
14
-
15
- const s = (val: string) => new SymbolPrimitive(val);
16
- const n = (val: number) => new NumberPrimitive(val);
17
- const litPat = (val: number | string) =>
18
- new LiteralPattern(typeof val === "number" ? n(val) : s(val));
19
- const varPat = (name: string) => new VariablePattern(s(name));
20
- const valExpr = (val: any) => ({ type: "Value", value: val } as any);
21
- const varExpr = (name: string) => ({ type: "Variable", value: name } as any);
22
- const seq = (stmts: any[]) => new Sequence(stmts);
23
- const unguarded = (stmts: any[]) => new UnguardedBody(seq(stmts));
24
- const guarded = (guards: { cond: any; body: any }[]): GuardedBody[] => {
25
- return guards.map((g) => new GuardedBody(g.cond, valExpr(g.body)));
26
- };
27
-
28
- class MockEvaluator {
29
- constructor(public env: any[]) {}
30
-
31
- evaluate(node: any): any {
32
- if (node.type === "Value") return node.value;
33
- if (node.type === "Variable") {
34
- const val = this.env[0].get(node.value);
35
- return val !== undefined ? val : `Error: ${node.value} not found`;
36
- }
37
- return node;
38
- }
39
- }
40
-
41
- describe("FunctionRuntime", () => {
42
- let globalEnv: any[];
43
- let evaluatorFactory: any;
44
-
45
- beforeEach(() => {
46
- globalEnv = [new Map()];
47
- evaluatorFactory = (env: any[]) => new MockEvaluator(env);
48
- });
49
-
50
- describe("Pattern Matching & Dispatch", () => {
51
- it("should match arguments to literal patterns", () => {
52
- const eq1: EquationRuntime = {
53
- patterns: [litPat(10)],
54
- body: unguarded([valExpr("ten")]),
55
- };
56
- const eq2: EquationRuntime = {
57
- patterns: [litPat(20)],
58
- body: unguarded([valExpr("twenty")]),
59
- };
60
-
61
- const result = FunctionRuntime.apply(
62
- "f",
63
- [eq1, eq2],
64
- [20],
65
- globalEnv,
66
- evaluatorFactory
67
- );
68
-
69
- expect(result).to.equal("twenty");
70
- });
71
-
72
- it("should throw error if no pattern matches (Non-exhaustive)", () => {
73
- const eq1: EquationRuntime = {
74
- patterns: [litPat(10)],
75
- body: unguarded([valExpr("ten")]),
76
- };
77
-
78
- expect(() => {
79
- FunctionRuntime.apply("f", [eq1], [99], globalEnv, evaluatorFactory);
80
- }).to.throw(/Non-exhaustive patterns/);
81
- });
82
-
83
- it("should skip equations with wrong arity (argument count)", () => {
84
- const eq1: EquationRuntime = {
85
- patterns: [varPat("X")],
86
- body: unguarded([valExpr("one arg")]),
87
- };
88
-
89
- expect(() => {
90
- FunctionRuntime.apply("f", [eq1], [1, 2], globalEnv, evaluatorFactory);
91
- }).to.throw(/Non-exhaustive patterns/);
92
- });
93
- });
94
-
95
- describe("Scope & Bindings", () => {
96
- it("should bind variables to a new local scope", () => {
97
- const eq1: EquationRuntime = {
98
- patterns: [varPat("X")],
99
- body: unguarded([varExpr("X")]),
100
- };
101
-
102
- const result = FunctionRuntime.apply(
103
- "identity",
104
- [eq1],
105
- [500],
106
- globalEnv,
107
- evaluatorFactory
108
- );
109
-
110
- expect(result).to.equal(500);
111
- });
112
-
113
- it("should prioritize local scope over global scope", () => {
114
- globalEnv[0].set("X", 1);
115
-
116
- const eq1: EquationRuntime = {
117
- patterns: [varPat("X")],
118
- body: unguarded([varExpr("X")]),
119
- };
120
-
121
- const result = FunctionRuntime.apply(
122
- "shadow",
123
- [eq1],
124
- [999],
125
- globalEnv,
126
- evaluatorFactory
127
- );
128
- expect(result).to.equal(999);
129
- });
130
- });
131
-
132
- describe("Guarded Bodies", () => {
133
- const guardedEvaluatorFactory = (env: any[]) => ({
134
- evaluate: (node: any) => {
135
- if (node.type === "Condition") return node.value;
136
- if (node.type === "Value") return node.value;
137
- return null;
138
- },
139
- });
140
-
141
- it("should execute the body of the first true guard", () => {
142
- const guards = [
143
- { cond: { type: "Condition", value: false }, body: 1 },
144
- { cond: { type: "Condition", value: true }, body: 2 },
145
- ];
146
-
147
- const eq: EquationRuntime = {
148
- patterns: [varPat("_")],
149
- body: guarded(guards),
150
- };
151
-
152
- const result = FunctionRuntime.apply(
153
- "guards",
154
- [eq],
155
- [0],
156
- globalEnv,
157
- guardedEvaluatorFactory as any
158
- );
159
- expect(result).to.equal(2);
160
- });
161
-
162
- it("should fall through to next equation if no guard matches", () => {
163
- const eq1: EquationRuntime = {
164
- patterns: [varPat("_")],
165
- body: guarded([{ cond: { type: "Condition", value: false }, body: 1 }]),
166
- };
167
-
168
- const eq2: EquationRuntime = {
169
- patterns: [varPat("_")],
170
- body: unguarded([valExpr(2)]),
171
- };
172
-
173
- const result = FunctionRuntime.apply(
174
- "fallback",
175
- [eq1, eq2],
176
- [0],
177
- globalEnv,
178
- guardedEvaluatorFactory as any
179
- );
180
- expect(result).to.equal(2);
181
- });
182
- });
183
-
184
- describe("Imperative Sequences & Returns", () => {
185
- it("should return the value of the last statement implicitly", () => {
186
- const eq: EquationRuntime = {
187
- patterns: [],
188
- body: unguarded([valExpr(10), valExpr(20), valExpr(30)]),
189
- };
190
-
191
- const result = FunctionRuntime.apply(
192
- "seq",
193
- [eq],
194
- [],
195
- globalEnv,
196
- evaluatorFactory
197
- );
198
- expect(result).to.equal(30);
199
- });
200
-
201
- it("should return early with Return statement", () => {
202
- const retStmt = new Return(valExpr(99));
203
-
204
- const eq: EquationRuntime = {
205
- patterns: [],
206
- body: unguarded([valExpr(10), retStmt, valExpr(30)]),
207
- };
208
-
209
- const result = FunctionRuntime.apply(
210
- "earlyRet",
211
- [eq],
212
- [],
213
- globalEnv,
214
- evaluatorFactory
215
- );
216
- expect(result).to.equal(99);
217
- });
218
-
219
- it("should return undefined for empty sequence", () => {
220
- const eq: EquationRuntime = {
221
- patterns: [],
222
- body: unguarded([]),
223
- };
224
- const result = FunctionRuntime.apply(
225
- "empty",
226
- [eq],
227
- [],
228
- globalEnv,
229
- evaluatorFactory
230
- );
231
- expect(result).to.be.undefined;
232
- });
233
- });
234
- });
1
+ import { expect } from "chai";
2
+ import {
3
+ EquationRuntime,
4
+ UnguardedBody,
5
+ Sequence,
6
+ Return,
7
+ SymbolPrimitive,
8
+ NumberPrimitive,
9
+ LiteralPattern,
10
+ VariablePattern,
11
+ GuardedBody,
12
+ EnvStack,
13
+ RuntimeFunction,
14
+ StringPrimitive,
15
+ Variable,
16
+ Expression,
17
+ BooleanPrimitive,
18
+ Equation,
19
+ } from "yukigo-ast";
20
+ import { FunctionRuntime } from "../../src/interpreter/components/runtimes/FunctionRuntime.js";
21
+ import { createGlobalEnv } from "../../src/interpreter/utils.js";
22
+ import {
23
+ idContinuation,
24
+ trampoline,
25
+ } from "../../src/interpreter/trampoline.js";
26
+ import { RuntimeContext } from "../../src/interpreter/components/RuntimeContext.js";
27
+
28
+ const symbol = (val: string) => new SymbolPrimitive(val);
29
+ const num = (val: number) => new NumberPrimitive(val);
30
+ const str = (val: string) => new StringPrimitive(val);
31
+ const litPat = (val: number | string) =>
32
+ new LiteralPattern(typeof val === "number" ? num(val) : symbol(val));
33
+ const varPat = (name: string) => new VariablePattern(symbol(name));
34
+ const varExpr = (name: string, expr: Expression) =>
35
+ new Variable(symbol(name), expr);
36
+ const seq = (stmts: any[]) => new Sequence(stmts);
37
+ const unguarded = (stmts: any[]) => new UnguardedBody(seq(stmts));
38
+ const guarded = (guards: { cond: any; body: Expression }[]): GuardedBody[] => {
39
+ return guards.map((g) => new GuardedBody(g.cond, g.body));
40
+ };
41
+
42
+ const makeRunFunc = (
43
+ identifier: string,
44
+ arity: number,
45
+ equations: EquationRuntime[],
46
+ ): RuntimeFunction => ({ type: "Function", identifier, arity, equations });
47
+
48
+ describe("FunctionRuntime", () => {
49
+ let globalEnv: EnvStack;
50
+
51
+ let funcRuntime: FunctionRuntime;
52
+ beforeEach(() => {
53
+ globalEnv = createGlobalEnv();
54
+ const context = new RuntimeContext();
55
+ context.setEnv(globalEnv);
56
+ funcRuntime = new FunctionRuntime(context);
57
+ });
58
+
59
+ describe("Pattern Matching & Dispatch", () => {
60
+ it("should match arguments to literal patterns", () => {
61
+ const eq1: EquationRuntime = {
62
+ patterns: [litPat(10)],
63
+ body: unguarded([str("ten")]),
64
+ };
65
+ const eq2: EquationRuntime = {
66
+ patterns: [litPat(20)],
67
+ body: unguarded([str("twenty")]),
68
+ };
69
+
70
+ const resultThunk = funcRuntime.apply(
71
+ makeRunFunc("f", 1, [eq1, eq2]),
72
+ [20],
73
+ idContinuation,
74
+ );
75
+ const result = trampoline(resultThunk);
76
+
77
+ expect(result).to.equal("twenty");
78
+ });
79
+
80
+ it("should throw error if no pattern matches (Non-exhaustive)", () => {
81
+ const eq1: EquationRuntime = {
82
+ patterns: [litPat(10)],
83
+ body: unguarded([str("ten")]),
84
+ };
85
+
86
+ expect(() => {
87
+ trampoline(
88
+ funcRuntime.apply(makeRunFunc("f", 1, [eq1]), [99], idContinuation),
89
+ );
90
+ }).to.throw(/Non-exhaustive patterns/);
91
+ });
92
+
93
+ it("should skip equations with wrong arity (argument count)", () => {
94
+ const eq1: EquationRuntime = {
95
+ patterns: [varPat("X")],
96
+ body: unguarded([str("one arg")]),
97
+ };
98
+
99
+ expect(() => {
100
+ trampoline(
101
+ funcRuntime.apply(makeRunFunc("f", 2, [eq1]), [1, 2], idContinuation),
102
+ );
103
+ }).to.throw(/Non-exhaustive patterns/);
104
+ });
105
+ });
106
+
107
+ describe("Scope & Bindings", () => {
108
+ it("should bind variables to a new local scope", () => {
109
+ const eq1: EquationRuntime = {
110
+ patterns: [varPat("X")],
111
+ body: unguarded([num(500)]),
112
+ };
113
+
114
+ const result = trampoline(
115
+ funcRuntime.apply(
116
+ makeRunFunc("identity", 1, [eq1]),
117
+ [500],
118
+ idContinuation,
119
+ ),
120
+ );
121
+
122
+ expect(result).to.equal(500);
123
+ });
124
+
125
+ it("should prioritize local scope over global scope", () => {
126
+ globalEnv.head.set("X", 1);
127
+
128
+ const eq1: EquationRuntime = new Equation(
129
+ [new VariablePattern(new SymbolPrimitive("X"))],
130
+ new UnguardedBody(new Sequence([new Return(new SymbolPrimitive("X"))])),
131
+ new Return(new SymbolPrimitive("X")),
132
+ );
133
+
134
+ const result = trampoline(
135
+ funcRuntime.apply(
136
+ makeRunFunc("shadow", 1, [eq1]),
137
+ [999],
138
+ idContinuation,
139
+ ),
140
+ );
141
+ expect(result).to.equal(999);
142
+ });
143
+ });
144
+
145
+ describe("Guarded Bodies", () => {
146
+ it("should execute the body of the first true guard", () => {
147
+ const guards = [
148
+ new GuardedBody(new BooleanPrimitive(false), num(1)),
149
+ new GuardedBody(new BooleanPrimitive(true), num(2)),
150
+ ];
151
+
152
+ const eq: EquationRuntime = {
153
+ patterns: [varPat("_")],
154
+ body: guards,
155
+ };
156
+
157
+ const result = trampoline(
158
+ funcRuntime.apply(makeRunFunc("guards", 1, [eq]), [0], idContinuation),
159
+ );
160
+ expect(result).to.equal(2);
161
+ });
162
+
163
+ it("should fall through to next equation if no guard matches", () => {
164
+ const eq1: EquationRuntime = {
165
+ patterns: [varPat("_")],
166
+ body: [new GuardedBody(new BooleanPrimitive(false), num(1))],
167
+ };
168
+
169
+ const eq2: EquationRuntime = {
170
+ patterns: [varPat("_")],
171
+ body: unguarded([num(2)]),
172
+ };
173
+
174
+ const result = trampoline(
175
+ funcRuntime.apply(
176
+ makeRunFunc("fallback", 1, [eq1, eq2]),
177
+ [0],
178
+ idContinuation,
179
+ ),
180
+ );
181
+ expect(result).to.equal(2);
182
+ });
183
+ });
184
+
185
+ describe("Imperative Sequences & Returns", () => {
186
+ it("should return the value of the last statement implicitly", () => {
187
+ const eq: EquationRuntime = {
188
+ patterns: [],
189
+ body: unguarded([num(10), num(20), num(30)]),
190
+ };
191
+
192
+ const result = trampoline(
193
+ funcRuntime.apply(makeRunFunc("seq", 1, [eq]), [], idContinuation),
194
+ );
195
+ expect(result).to.equal(30);
196
+ });
197
+
198
+ it("should return early with Return statement", () => {
199
+ const retStmt = new Return(num(99));
200
+
201
+ const eq: EquationRuntime = {
202
+ patterns: [],
203
+ body: unguarded([num(10), retStmt, num(30)]),
204
+ };
205
+
206
+ const result = trampoline(
207
+ funcRuntime.apply(makeRunFunc("earlyRet", 1, [eq]), [], idContinuation),
208
+ );
209
+ expect(result).to.equal(99);
210
+ });
211
+
212
+ it("should return undefined for empty sequence", () => {
213
+ const eq: EquationRuntime = {
214
+ patterns: [],
215
+ body: unguarded([]),
216
+ };
217
+ const result = trampoline(
218
+ funcRuntime.apply(makeRunFunc("empty", 1, [eq]), [], idContinuation),
219
+ );
220
+ expect(result).to.be.undefined;
221
+ });
222
+ });
223
+ });