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,46 +1,119 @@
1
- import { SymbolPrimitive, VariablePattern, Application, Lambda, UnguardedBody, Sequence, Return, } from "@yukigo/ast";
1
+ import { SymbolPrimitive, VariablePattern, Application, UnguardedBody, Sequence, Return, Generator as YuGenerator, isRuntimeObject, isRuntimeClass, Super, isRuntimeFunction, isLazyList, } from "yukigo-ast";
2
2
  import { ArithmeticBinaryTable, ArithmeticUnaryTable, BitwiseBinaryTable, BitwiseUnaryTable, ComparisonOperationTable, ListBinaryTable, ListUnaryTable, LogicalBinaryTable, LogicalUnaryTable, StringOperationTable, } from "./Operations.js";
3
- import { define, lookup } from "../utils.js";
4
- import { LogicEngine } from "./LogicEngine.js";
3
+ import { LogicEngine } from "./logic/LogicEngine.js";
5
4
  import { InterpreterError, UnexpectedValue } from "../errors.js";
6
- import { LazyRuntime } from "./LazyRuntime.js";
7
- import { FunctionRuntime } from "./FunctionRuntime.js";
5
+ import { EnvBuilderVisitor } from "./EnvBuilder.js";
6
+ import { FailedAssert, TestRunner } from "./TestRunner.js";
7
+ import { idContinuation, trampoline, valueToCPS, } from "../trampoline.js";
8
+ import { RuntimeContext } from "./RuntimeContext.js";
8
9
  export class InterpreterVisitor {
10
+ context;
9
11
  frames;
10
- env;
11
- config;
12
- constructor(env, config, frames = []) {
12
+ constructor(context, frames = []) {
13
+ this.context = context;
13
14
  this.frames = frames;
14
- this.env = env;
15
- this.config = config;
16
15
  }
17
- evaluate(node) {
18
- return node.accept(this);
16
+ evaluate(node, cont) {
17
+ return () => {
18
+ try {
19
+ const cpsThunk = node.accept(this);
20
+ return cpsThunk(cont);
21
+ }
22
+ catch (err) {
23
+ if (err instanceof InterpreterError || err instanceof FailedAssert) {
24
+ if (err instanceof InterpreterError) {
25
+ err.pushFrame({ nodeType: node.constructor.name, loc: node.loc });
26
+ }
27
+ throw err;
28
+ }
29
+ throw new InterpreterError(node.constructor.name, err.message, [...this.frames, { nodeType: node.constructor.name, loc: node.loc }]);
30
+ }
31
+ };
19
32
  }
20
- getLogicEngine() {
21
- return new LogicEngine(this.env, this.config, this);
33
+ visitSequence(node) {
34
+ return (k) => {
35
+ if (node.statements.length === 0)
36
+ return k(undefined);
37
+ if (this.context.config.debug)
38
+ console.log(`[Interpreter] Entering sequence with ${node.statements.length} statements`);
39
+ const evaluateNext = (index, lastResult) => {
40
+ if (index >= node.statements.length)
41
+ return k(lastResult);
42
+ const stmt = node.statements[index];
43
+ return this.evaluate(stmt, (result) => {
44
+ if (stmt instanceof Return)
45
+ return k(result);
46
+ return () => evaluateNext(index + 1, result);
47
+ });
48
+ };
49
+ return evaluateNext(0, undefined);
50
+ };
51
+ }
52
+ visitAssert(node) {
53
+ if (this.context.config.debug) {
54
+ console.log(`[Interpreter] Visiting Assert`);
55
+ }
56
+ return (k) => new TestRunner(this, this.context.lazyRuntime).visitAssert(node)((val) => k(val));
57
+ }
58
+ visitTest(node) {
59
+ if (this.context.config.debug) {
60
+ console.log(`[Interpreter] Visiting Test`);
61
+ }
62
+ return (k) => new TestRunner(this, this.context.lazyRuntime).visitTest(node)((val) => k(val));
63
+ }
64
+ visitTestGroup(node) {
65
+ if (this.context.config.debug) {
66
+ console.log(`[Interpreter] Visiting TestGroup`);
67
+ }
68
+ return (k) => new TestRunner(this, this.context.lazyRuntime).visitTestGroup(node)((val) => k(val));
22
69
  }
23
70
  visitNumberPrimitive(node) {
24
- return node.value;
71
+ return valueToCPS(node.value);
25
72
  }
26
73
  visitBooleanPrimitive(node) {
27
- return node.value;
74
+ return valueToCPS(node.value);
28
75
  }
29
76
  visitStringPrimitive(node) {
30
- return node.value;
77
+ return valueToCPS(node.value);
31
78
  }
32
79
  visitListPrimitive(node) {
33
- return node.elements.map((elem) => elem.accept(this));
80
+ return (k) => {
81
+ if (node.value.length === 0)
82
+ return k([]);
83
+ const results = [];
84
+ const evaluateNext = (index) => {
85
+ if (index >= node.value.length)
86
+ return k(results);
87
+ return this.evaluate(node.value[index], (val) => {
88
+ results.push(val);
89
+ return () => evaluateNext(index + 1);
90
+ });
91
+ };
92
+ return evaluateNext(0);
93
+ };
34
94
  }
35
95
  visitNilPrimitive(node) {
36
- return node.value;
96
+ return valueToCPS(node.value);
37
97
  }
38
98
  visitCharPrimitive(node) {
39
- return node.value;
99
+ return valueToCPS(node.value);
40
100
  }
41
101
  visitSymbolPrimitive(node) {
102
+ if (this.context.config.debug) {
103
+ console.log(`[Interpreter] Looking for \`${node.value}\` in the environment`);
104
+ }
42
105
  try {
43
- return lookup(this.env, node.value);
106
+ const val = this.context.lookup(node.value);
107
+ if (isRuntimeFunction(val) && val.arity === 0) {
108
+ if (this.context.config.debug) {
109
+ console.log(`[Interpreter] Resolved symbol \`${node.value}\` as arity-0 function, applying...`);
110
+ }
111
+ return (k) => () => this.context.funcRuntime.apply(val, [], k);
112
+ }
113
+ if (this.context.config.debug) {
114
+ console.log(`[Interpreter] Found \`${node.value}\`: ${typeof val === "object" && "type" in val ? val.type : val}`);
115
+ }
116
+ return valueToCPS(val);
44
117
  }
45
118
  catch (error) {
46
119
  throw new InterpreterError("Symbol Lookup", error.message, this.frames);
@@ -48,9 +121,34 @@ export class InterpreterVisitor {
48
121
  }
49
122
  visitVariable(node) {
50
123
  const name = node.identifier.value;
51
- const value = node.expression.accept(this);
52
- this.env.at(-1).set(name, value);
53
- return true;
124
+ if (this.context.config.debug) {
125
+ console.log(`[Interpreter] Defining variable: ${name}`);
126
+ }
127
+ return (k) => this.evaluate(node.expression, (value) => {
128
+ this.context.define(name, value);
129
+ return k(true);
130
+ });
131
+ }
132
+ visitAssignment(node) {
133
+ if (!this.context.config.mutability) {
134
+ throw new InterpreterError("Assignment", `Cannot reassign variable '${node.identifier.value}': mutability is disabled`, this.frames);
135
+ }
136
+ const name = node.identifier.value;
137
+ if (this.context.config.debug) {
138
+ console.log(`[Interpreter] Assigning variable: ${name}`);
139
+ }
140
+ return (k) => this.evaluate(node.expression, (value) => {
141
+ const onReplace = (scope) => {
142
+ if (scope.has("self")) {
143
+ const self = scope.get("self");
144
+ if (isRuntimeObject(self) && self.fields.has(name))
145
+ self.fields.set(name, value);
146
+ }
147
+ };
148
+ if (!this.context.replace(name, value, onReplace))
149
+ throw new InterpreterError("Assignment", `Cannot assign to undefined variable: ${name}`, this.frames);
150
+ return k(value);
151
+ });
54
152
  }
55
153
  visitArithmeticUnaryOperation(node) {
56
154
  return this.processUnary(node, ArithmeticUnaryTable, (a) => !Number.isNaN(a), "ArithmeticUnaryOperation");
@@ -59,42 +157,58 @@ export class InterpreterVisitor {
59
157
  return this.processBinary(node, ArithmeticBinaryTable, (a, b) => typeof a === "number" && typeof b === "number", "ArithmeticBinaryOperation");
60
158
  }
61
159
  visitListUnaryOperation(node) {
62
- const operand = node.operand.accept(this);
63
- if (!Array.isArray(operand))
64
- throw new UnexpectedValue("ListUnaryOperation", "Array", typeof operand);
65
- const arr = this.realizeList(operand);
66
- const fn = ListUnaryTable[node.operator];
67
- if (!fn)
68
- throw new InterpreterError("ListUnaryOperation", `Unknown operator: ${node.operator}`);
69
- return fn(arr);
160
+ return (k) => this.evaluate(node.operand, (operand) => {
161
+ if (typeof operand !== "string" &&
162
+ !Array.isArray(operand) &&
163
+ !isLazyList(operand))
164
+ throw new UnexpectedValue("ListUnaryOperation", "Array, String or LazyList", typeof operand);
165
+ return this.context.lazyRuntime.realizeList(operand, (arr) => {
166
+ const fn = ListUnaryTable[node.operator];
167
+ if (!fn)
168
+ throw new InterpreterError("ListUnaryOperation", `Unknown operator: ${node.operator}`);
169
+ return k(fn(arr));
170
+ });
171
+ });
70
172
  }
71
173
  visitListBinaryOperation(node) {
72
- return this.processBinary(node, ListBinaryTable, (a, b) => Array.isArray(a) && Array.isArray(b), "ListBinaryOperation");
174
+ if (node.operator === "Concat") {
175
+ if (this.context.config.lazyLoading) {
176
+ return (k) => this.context.lazyRuntime.evaluateConcatLazy(node, this, k);
177
+ }
178
+ return (k) => this.evaluate(node.left, (left) => {
179
+ return () => this.evaluate(node.right, (right) => {
180
+ return this.context.lazyRuntime.evaluateConcat(left, right, k);
181
+ });
182
+ });
183
+ }
184
+ return this.processBinary(node, ListBinaryTable, (a, b) => (Array.isArray(a) || typeof a === "string" || isLazyList(a)) &&
185
+ (Array.isArray(b) || typeof b === "string" || isLazyList(b)), "ListBinaryOperation");
73
186
  }
74
187
  visitComparisonOperation(node) {
188
+ if (node.operator === "Equal" || node.operator === "NotEqual") {
189
+ return (k) => this.evaluate(node.left, (left) => () => this.evaluate(node.right, (right) => this.context.lazyRuntime.deepEqual(left, right, (eq) => k(node.operator === "Equal" ? eq : !eq))));
190
+ }
75
191
  return this.processBinary(node, ComparisonOperationTable, () => true, "ComparisonOperation");
76
192
  }
77
193
  visitLogicalBinaryOperation(node) {
78
- const left = node.left.accept(this);
79
- if (typeof left !== "boolean")
80
- throw new InterpreterError("LogicalBinaryOperation", `Expected left side to be boolean and got: ${left}`);
81
- const fn = LogicalBinaryTable[node.operator];
82
- if (!fn)
83
- throw new InterpreterError("LogicalBinaryOperation", `Unknown operator '${node.operator}'`);
84
- const rightThunk = () => {
85
- const right = node.right.accept(this);
86
- if (typeof right !== "boolean")
87
- throw new InterpreterError("LogicalBinaryOperation", `Expected right side to be boolean and got: ${right}`);
88
- return right;
89
- };
90
- // short circuit if lazy loading is enabled
91
- if (this.config.lazyLoading) {
92
- if (node.operator === "And" && left === false)
93
- return false;
94
- if (node.operator === "Or" && left === true)
95
- return true;
96
- }
97
- return fn(left, rightThunk);
194
+ return (k) => this.evaluate(node.left, (left) => {
195
+ if (typeof left !== "boolean")
196
+ throw new InterpreterError("LogicalBinaryOperation", `Expected left side to be boolean and got: ${left}`);
197
+ const fn = LogicalBinaryTable[node.operator];
198
+ if (!fn)
199
+ throw new InterpreterError("LogicalBinaryOperation", `Unknown operator '${node.operator}'`);
200
+ if (this.context.config.lazyLoading) {
201
+ if (node.operator === "And" && left === false)
202
+ return k(false);
203
+ if (node.operator === "Or" && left === true)
204
+ return k(true);
205
+ }
206
+ return this.evaluate(node.right, (right) => {
207
+ if (typeof right !== "boolean")
208
+ throw new InterpreterError("LogicalBinaryOperation", `Expected right side to be boolean and got: ${right}`);
209
+ return k(fn(left, () => right));
210
+ });
211
+ });
98
212
  }
99
213
  visitLogicalUnaryOperation(node) {
100
214
  return this.processUnary(node, LogicalUnaryTable, (a) => typeof a === "boolean", "LogicalUnaryOperation");
@@ -109,56 +223,147 @@ export class InterpreterVisitor {
109
223
  return this.processBinary(node, StringOperationTable, (a, b) => typeof a === "string" || typeof b === "string", "StringOperation");
110
224
  }
111
225
  visitUnifyOperation(node) {
112
- throw new Error("Method not implemented.");
226
+ return (k) => this.getLogicEngine().unifyExpr(node.left, node.right, k);
113
227
  }
114
228
  visitAssignOperation(node) {
115
- throw new Error("Method not implemented.");
229
+ if (!this.context.config.mutability) {
230
+ throw new InterpreterError("AssignOperation", `Cannot perform assignment operation: mutability is disabled`, this.frames);
231
+ }
232
+ if (!(node.left instanceof SymbolPrimitive))
233
+ throw new InterpreterError("AssignOperation", "Left side must be a SymbolPrimitive");
234
+ const name = node.left.value;
235
+ return (k) => this.evaluate(node.right, (value) => {
236
+ const onReplace = (scope) => {
237
+ if (scope.has("self")) {
238
+ const self = scope.get("self");
239
+ if (isRuntimeObject(self) && self.fields.has(name)) {
240
+ self.fields.set(name, value);
241
+ }
242
+ }
243
+ };
244
+ if (!this.context.replace(name, value, onReplace)) {
245
+ this.context.define(name, value);
246
+ }
247
+ return k(true);
248
+ });
116
249
  }
117
250
  visitTupleExpr(node) {
118
- throw new Error("Method not implemented.");
251
+ return (k) => {
252
+ const results = [];
253
+ const evaluateNext = (index) => {
254
+ if (index >= node.elements.length)
255
+ return k(results);
256
+ return this.evaluate(node.elements[index], (val) => {
257
+ results.push(val);
258
+ return () => evaluateNext(index + 1);
259
+ });
260
+ };
261
+ return evaluateNext(0);
262
+ };
119
263
  }
120
- visitFieldExpr(node) {
121
- throw new Error("Method not implemented.");
264
+ visitFieldExpression(node) {
265
+ return (k) => this.evaluate(node.name, (obj) => {
266
+ return k(this.context.objRuntime.getField(obj, node.name.value));
267
+ });
122
268
  }
123
269
  visitDataExpr(node) {
124
- throw new Error("Method not implemented.");
270
+ return (k) => {
271
+ const fieldValues = new Map();
272
+ const evaluateFields = (index) => {
273
+ if (index >= node.contents.length) {
274
+ return k(this.context.objRuntime.instantiate(node.name.value, node.name.value, fieldValues, new Map()));
275
+ }
276
+ const field = node.contents[index];
277
+ return this.evaluate(field.expression, (value) => {
278
+ fieldValues.set(field.name.value, value);
279
+ return () => evaluateFields(index + 1);
280
+ });
281
+ };
282
+ return evaluateFields(0);
283
+ };
125
284
  }
126
285
  visitConsExpr(node) {
127
- try {
128
- return LazyRuntime.evaluateCons(node, this, this.config.lazyLoading);
129
- }
130
- catch (e) {
131
- throw new InterpreterError("Cons", e.message, this.frames);
132
- }
286
+ return (k) => this.context.lazyRuntime.evaluateCons(node, this, k);
133
287
  }
134
288
  visitLetInExpr(node) {
135
- throw new Error("Method not implemented.");
289
+ return (k) => {
290
+ const oldEnv = this.context.env;
291
+ this.context.pushEnv();
292
+ const envBuilder = new EnvBuilderVisitor(this.context);
293
+ node.declarations.accept(envBuilder);
294
+ return this.evaluate(node.expression, (result) => {
295
+ this.context.env = oldEnv;
296
+ return k(result);
297
+ });
298
+ };
299
+ }
300
+ visitIf(node) {
301
+ return (k) => this.evaluate(node.condition, (condition) => {
302
+ if (typeof condition !== "boolean")
303
+ throw new InterpreterError("If", `Expected boolean in condition and got ${typeof condition}`, this.frames);
304
+ if (this.context.config.debug) {
305
+ console.log(`[Interpreter] If condition: ${condition}`);
306
+ }
307
+ return condition
308
+ ? node.then.accept(this)(k)
309
+ : node.elseExpr.accept(this)(k);
310
+ });
136
311
  }
137
312
  visitCall(node) {
138
- throw new Error("Method not implemented.");
313
+ return (k) => this.evaluate(node.callee, (callee) => {
314
+ const args = [];
315
+ const evaluateArgs = (index) => {
316
+ if (index < node.args.length)
317
+ return this.evaluate(node.args[index], (val) => {
318
+ args.push(val);
319
+ return () => evaluateArgs(index + 1);
320
+ });
321
+ if (!isRuntimeFunction(callee))
322
+ throw new InterpreterError("Call", "Target is not a function");
323
+ if (this.context.config.debug)
324
+ console.log(`[Interpreter] Calling function: ${callee.identifier} with ${args.length} args`);
325
+ return this.context.funcRuntime.apply(callee, args, k);
326
+ };
327
+ return evaluateArgs(0);
328
+ });
139
329
  }
140
330
  visitOtherwise(node) {
141
- return true;
331
+ return valueToCPS(true);
142
332
  }
143
333
  visitCompositionExpression(node) {
144
- const f = node.left.accept(this);
145
- const g = node.right.accept(this);
146
- if (!this.isRuntimeFunction(f) || !this.isRuntimeFunction(g))
147
- throw new InterpreterError("CompositionExpression", "Both operands of (.) must be functions");
148
- const fName = `__comp_f_${Date.now()}_${Math.random()
149
- .toString(36)
150
- .substring(2, 5)}`;
151
- const gName = `__comp_g_${Date.now()}_${Math.random()
152
- .toString(36)
153
- .substring(2, 5)}`;
154
- define(this.env, fName, f);
155
- define(this.env, gName, g);
156
- const arity = g.arity;
157
- const placeholders = Array.from({ length: arity }, (_, i) => new VariablePattern(new SymbolPrimitive(`_p${i}`)));
158
- const gCall = placeholders.reduce((acc, p) => new Application(acc, new SymbolPrimitive(p.name.value)), new SymbolPrimitive(gName));
159
- const composedBody = new Application(new SymbolPrimitive(fName), gCall);
160
- const lambda = new Lambda(placeholders, composedBody);
161
- return lambda.accept(this);
334
+ return (k) => this.evaluate(node.left, (f) => {
335
+ return this.evaluate(node.right, (g) => {
336
+ if (!isRuntimeFunction(f) || !isRuntimeFunction(g)) {
337
+ throw new InterpreterError("Composition", "Both operands of (.) must be functions");
338
+ }
339
+ const F_REF = "__internal_f";
340
+ const G_REF = "__internal_g";
341
+ const PARAM_NAME = "__x";
342
+ const compositionBody = new Application(new SymbolPrimitive(F_REF), new Application(new SymbolPrimitive(G_REF), new SymbolPrimitive(PARAM_NAME)));
343
+ const patterns = [
344
+ new VariablePattern(new SymbolPrimitive(PARAM_NAME)),
345
+ ];
346
+ const equation = {
347
+ patterns,
348
+ body: new UnguardedBody(new Sequence([new Return(compositionBody)])),
349
+ };
350
+ const privateScope = new Map();
351
+ privateScope.set(F_REF, f);
352
+ privateScope.set(G_REF, g);
353
+ const capturedEnv = {
354
+ head: privateScope,
355
+ tail: this.context.env,
356
+ };
357
+ return k({
358
+ type: "Function",
359
+ arity: 1,
360
+ identifier: `<(${f.identifier} . ${g.identifier})>`,
361
+ equations: [equation],
362
+ pendingArgs: [],
363
+ closure: capturedEnv,
364
+ });
365
+ });
366
+ });
162
367
  }
163
368
  visitLambda(node) {
164
369
  const patterns = node.parameters;
@@ -166,134 +371,264 @@ export class InterpreterVisitor {
166
371
  patterns,
167
372
  body: new UnguardedBody(new Sequence([new Return(node.body)])),
168
373
  };
169
- return {
374
+ return valueToCPS({
375
+ type: "Function",
170
376
  arity: patterns.length,
171
377
  equations: [equation],
172
378
  pendingArgs: [],
173
379
  identifier: "<lambda>",
174
- closure: Array.from(this.env),
175
- };
380
+ closure: this.context.env,
381
+ });
176
382
  }
177
383
  visitApplication(node) {
178
- const func = node.functionExpr.accept(this);
179
- if (!this.isRuntimeFunction(func))
180
- throw new InterpreterError("Application", "Cannot apply non-function");
181
- const argThunk = () => node.parameter.accept(this);
182
- const pending = func.pendingArgs
183
- ? [...func.pendingArgs, argThunk]
184
- : [argThunk];
185
- // partially applied
186
- if (pending.length < func.arity)
187
- return {
188
- ...func,
189
- pendingArgs: pending,
190
- };
191
- // fully applied
192
- if (pending.length === func.arity) {
193
- const evaluatedArgs = pending.map((arg) => typeof arg === "function" ? arg() : arg);
194
- const executionEnv = func.closure ?? this.env;
195
- return FunctionRuntime.apply(func.identifier ?? "<anonymous>", func.equations, evaluatedArgs, executionEnv, (newEnv) => new InterpreterVisitor(newEnv, this.config, this.frames));
384
+ const { funcRuntime } = this.context;
385
+ if (this.context.config.debug) {
386
+ console.log(`[Interpreter] Visiting Application`);
196
387
  }
197
- throw new InterpreterError("Application", "Too many arguments provided");
388
+ return (k) => this.evaluate(node.functionExpr, (func) => {
389
+ if (!isRuntimeFunction(func))
390
+ throw new InterpreterError("Application", "Cannot apply non-function");
391
+ const applyFuncToNode = (func) => (k) => this.evaluate(node.parameter, (arg) => {
392
+ const argThunk = () => arg;
393
+ const allPendingArgs = func.pendingArgs
394
+ ? [...func.pendingArgs, argThunk]
395
+ : [argThunk];
396
+ return funcRuntime.applyArguments(func, allPendingArgs)(k);
397
+ });
398
+ if (func.arity === 0) {
399
+ return funcRuntime.applyArguments(func, [])(resultOfFunc => {
400
+ if (!isRuntimeFunction(resultOfFunc))
401
+ throw new InterpreterError("Application", `Cannot apply non-function result of arity-0 function`);
402
+ return applyFuncToNode(resultOfFunc)(k);
403
+ });
404
+ }
405
+ return applyFuncToNode(func)(k);
406
+ });
198
407
  }
199
408
  visitQuery(node) {
200
- return this.getLogicEngine().solveQuery(node);
409
+ if (this.context.config.debug) {
410
+ console.log(`[Interpreter] Visiting Query.`);
411
+ }
412
+ return (k) => this.getLogicEngine().solveQuery(node, (res) => {
413
+ this.bindLogicResults(res);
414
+ return k(res);
415
+ });
201
416
  }
202
417
  visitExist(node) {
203
- return this.getLogicEngine().solveExist(node);
418
+ return (k) => this.getLogicEngine().solveExist(node, (res) => {
419
+ this.bindLogicResults(res);
420
+ return k(res);
421
+ });
204
422
  }
205
423
  visitNot(node) {
206
- throw new Error("Method not implemented.");
424
+ return (k) => this.getLogicEngine().solveNot(node, k);
207
425
  }
208
426
  visitFindall(node) {
209
- return this.getLogicEngine().solveFindall(node);
427
+ return (k) => this.getLogicEngine().solveFindall(node, k);
210
428
  }
211
429
  visitForall(node) {
212
- throw new Error("Method not implemented.");
430
+ return (k) => this.getLogicEngine().solveForall(node, k);
213
431
  }
214
432
  visitGoal(node) {
215
- return this.getLogicEngine().solveGoal(node);
433
+ return (k) => this.getLogicEngine().solveGoal(node, (res) => {
434
+ this.bindLogicResults(res);
435
+ return k(res);
436
+ });
437
+ }
438
+ bindLogicResults(res) {
439
+ if (!res)
440
+ return;
441
+ if (Array.isArray(res)) {
442
+ if (res.length > 0)
443
+ this.bindLogicResults(res[0]);
444
+ return;
445
+ }
446
+ if (typeof res === "object" &&
447
+ res !== null &&
448
+ "success" in res &&
449
+ res.success &&
450
+ "solutions" in res) {
451
+ for (const [name, val] of res.solutions) {
452
+ this.context.define(name, val);
453
+ }
454
+ }
216
455
  }
217
- visitSend(node) {
218
- throw new Error("Method not implemented.");
456
+ visitLogicConstraint(node) {
457
+ return (k) => this.evaluate(node.expression, (val) => {
458
+ if (Array.isArray(val))
459
+ return k(val.length > 0);
460
+ return k(!!val);
461
+ });
219
462
  }
220
- visitNew(node) {
221
- throw new Error("Method not implemented.");
463
+ visitSuper(node) {
464
+ let methodName;
465
+ try {
466
+ methodName = this.context.lookup("__METHOD_NAME__");
467
+ }
468
+ catch (e) {
469
+ throw new InterpreterError("Super", "'super' keyword used outside of a method context", this.frames);
470
+ }
471
+ return (k) => {
472
+ const args = [];
473
+ const evaluateNextArg = (index) => {
474
+ if (index >= node.args.length) {
475
+ if (this.context.config.debug) {
476
+ console.log(`[Interpreter] Dispatching super call: ${methodName}`);
477
+ }
478
+ return this.context.objRuntime.dispatchSuper(this.context.env, methodName, args, k);
479
+ }
480
+ return this.evaluate(node.args[index], (val) => {
481
+ args.push(val);
482
+ return () => evaluateNextArg(index + 1);
483
+ });
484
+ };
485
+ return evaluateNextArg(0);
486
+ };
222
487
  }
223
- visitImplement(node) {
224
- throw new Error("Method not implemented.");
488
+ visitSend(node) {
489
+ return (k) => {
490
+ if (node.receiver instanceof Super) {
491
+ const methodName = node.selector.value;
492
+ const args = [];
493
+ const evaluateNextArg = (index) => {
494
+ if (index >= node.args.length) {
495
+ if (this.context.config.debug) {
496
+ console.log(`[Interpreter] Dispatching super call: ${methodName}`);
497
+ }
498
+ return this.context.objRuntime.dispatchSuper(this.context.env, methodName, args, k);
499
+ }
500
+ return this.evaluate(node.args[index], (val) => {
501
+ args.push(val);
502
+ return () => evaluateNextArg(index + 1);
503
+ });
504
+ };
505
+ return evaluateNextArg(0);
506
+ }
507
+ return this.evaluate(node.receiver, (receiver) => {
508
+ const methodName = node.selector.value;
509
+ const args = [];
510
+ const evaluateNextArg = (index) => {
511
+ if (index >= node.args.length) {
512
+ if (this.context.config.debug) {
513
+ console.log(`[Interpreter] Sending method call: ${methodName} to object`);
514
+ }
515
+ return this.context.objRuntime.dispatch(receiver, methodName, args, this.context.env, k);
516
+ }
517
+ return this.evaluate(node.args[index], (val) => {
518
+ args.push(val);
519
+ return () => evaluateNextArg(index + 1);
520
+ });
521
+ };
522
+ return evaluateNextArg(0);
523
+ });
524
+ };
225
525
  }
226
- visitInclude(node) {
227
- throw new Error("Method not implemented.");
526
+ visitNew(node) {
527
+ const className = node.identifier.value;
528
+ const classDef = this.context.lookup(className);
529
+ if (!isRuntimeClass(classDef))
530
+ throw new InterpreterError("New", `${className} is not a class.`, this.frames);
531
+ if (this.context.config.debug) {
532
+ console.log(`[Interpreter] Instantiating class: ${className}`);
533
+ }
534
+ return valueToCPS(this.context.objRuntime.instantiate(className, node.identifier.value, classDef.fields, classDef.methods));
228
535
  }
229
536
  visitSelf(node) {
230
- throw new Error("Method not implemented.");
537
+ try {
538
+ return valueToCPS(this.context.lookup("self"));
539
+ }
540
+ catch {
541
+ throw new InterpreterError("Self", "'self' is not defined in this context");
542
+ }
231
543
  }
232
544
  visitListComprehension(node) {
233
- throw new Error("Method not implemented.");
545
+ return (k) => {
546
+ const results = [];
547
+ const process = (index) => {
548
+ if (index >= node.generators.length) {
549
+ return this.evaluate(node.projection, (proj) => {
550
+ results.push(proj);
551
+ return () => k(results);
552
+ });
553
+ }
554
+ const current = node.generators[index];
555
+ if (current instanceof YuGenerator) {
556
+ return this.evaluate(current.expression, (exprResult) => {
557
+ return this.context.lazyRuntime.realizeList(exprResult, (sourceList) => {
558
+ const iterateSource = (sourceIndex) => {
559
+ if (sourceIndex >= sourceList.length)
560
+ return () => k(results);
561
+ const item = sourceList[sourceIndex];
562
+ const varName = current.variable.value;
563
+ this.context.define(varName, item);
564
+ return () => process(index + 1);
565
+ };
566
+ return iterateSource(0);
567
+ });
568
+ });
569
+ }
570
+ else {
571
+ return this.evaluate(current, (condition) => {
572
+ return condition === true
573
+ ? () => process(index + 1)
574
+ : () => k(results);
575
+ });
576
+ }
577
+ };
578
+ this.context.pushEnv();
579
+ return process(0);
580
+ };
581
+ }
582
+ visitTypeCast(node) {
583
+ return node.expression.accept(this);
234
584
  }
235
585
  visitGenerator(node) {
236
- throw new Error("Method not implemented.");
586
+ return (k) => this.evaluate(node.expression, k);
237
587
  }
238
588
  visitRaise(node) {
239
- const msg = node.body.accept(this);
240
- if (typeof msg !== "string")
241
- throw new UnexpectedValue("Raise", "string", typeof msg);
242
- throw new InterpreterError("Raise", msg);
589
+ return (k) => this.evaluate(node.body, (msg) => {
590
+ if (typeof msg !== "string")
591
+ throw new UnexpectedValue("Raise", "string", typeof msg);
592
+ throw new InterpreterError("Raise", msg);
593
+ });
243
594
  }
244
595
  visitRangeExpression(node) {
245
- try {
246
- return LazyRuntime.evaluateRange(node, this, this.config);
247
- }
248
- catch (e) {
249
- throw new InterpreterError("Range", e.message, this.frames);
250
- }
596
+ return (k) => this.context.lazyRuntime.evaluateRange(node, this, k);
251
597
  }
252
598
  visit(node) {
253
- return this.safelyVisit(node, () => node.accept(this));
254
- }
255
- safelyVisit(node, fn) {
256
- try {
257
- return fn();
258
- }
259
- catch (err) {
260
- if (err instanceof InterpreterError) {
261
- err.pushFrame({ nodeType: node.constructor.name, loc: node.loc });
262
- throw err;
263
- }
264
- const wrapped = new InterpreterError(node.constructor.name, err.message, [...this.frames, { nodeType: node.constructor.name, loc: node.loc }]);
265
- throw wrapped;
266
- }
267
- }
268
- isRuntimeFunction(val) {
269
- return (typeof val === "object" &&
270
- val !== null &&
271
- Array.isArray(val.equations) &&
272
- typeof val.arity === "number");
599
+ return node.accept(this);
273
600
  }
274
- realizeList(val) {
275
- return LazyRuntime.realizeList(val);
601
+ realizeList(val, k) {
602
+ return this.context.lazyRuntime.realizeList(val, k);
276
603
  }
277
604
  processBinary(node, table, typeGuard, contextName) {
278
- const left = node.left.accept(this);
279
- const right = node.right.accept(this);
280
- if (!typeGuard(left, right))
281
- throw new InterpreterError(contextName, `Type mismatch: ${left}, ${right}`, this.frames);
282
- const fn = table[node.operator];
283
- if (!fn)
284
- throw new InterpreterError(contextName, `Unknown op: ${node.operator}`);
285
- return fn(left, right);
605
+ return (k) => this.evaluate(node.left, (left) => {
606
+ return this.evaluate(node.right, (right) => {
607
+ if (!typeGuard(left, right))
608
+ throw new InterpreterError(contextName, `Type mismatch: ${left}, ${right}`, this.frames);
609
+ const fn = table[node.operator];
610
+ if (!fn)
611
+ throw new InterpreterError(contextName, `Unknown op: ${node.operator}`);
612
+ return k(fn(left, right));
613
+ });
614
+ });
286
615
  }
287
616
  processUnary(node, table, typeGuard, contextName) {
288
- const operand = node.operand.accept(this);
289
- if (!typeGuard(operand))
290
- throw new InterpreterError(contextName, `Type mismatch: ${operand}`, this.frames);
291
- const fn = table[node.operator];
292
- if (!fn)
293
- throw new InterpreterError(contextName, `Unknown op: ${node.operator}`);
294
- return fn(operand);
617
+ return (k) => this.evaluate(node.operand, (operand) => {
618
+ if (!typeGuard(operand))
619
+ throw new InterpreterError(contextName, `Type mismatch: ${operand}`, this.frames);
620
+ const fn = table[node.operator];
621
+ if (!fn)
622
+ throw new InterpreterError(contextName, `Unknown op: ${node.operator}`);
623
+ return k(fn(operand));
624
+ });
625
+ }
626
+ getLogicEngine() {
627
+ return new LogicEngine(this, this.context);
295
628
  }
296
629
  static evaluateLiteral(node) {
297
- return node.accept(new InterpreterVisitor([new Map()], { lazyLoading: false }));
630
+ const ctx = new RuntimeContext();
631
+ const visitor = new InterpreterVisitor(ctx);
632
+ return trampoline(visitor.evaluate(node, idContinuation));
298
633
  }
299
634
  }