yukigo 0.2.0 → 0.2.2

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