yukigo 0.1.1 → 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 (104) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/analyzer/index.d.ts +8 -1
  3. package/dist/analyzer/index.js +2 -1
  4. package/dist/interpreter/components/PatternMatcher.d.ts +2 -1
  5. package/dist/interpreter/components/RuntimeContext.d.ts +2 -1
  6. package/dist/interpreter/components/RuntimeContext.js +10 -3
  7. package/dist/interpreter/components/TestRunner.js +1 -42
  8. package/dist/interpreter/components/Visitor.d.ts +2 -2
  9. package/dist/interpreter/components/Visitor.js +16 -28
  10. package/dist/interpreter/components/runtimes/FunctionRuntime.d.ts +2 -1
  11. package/dist/interpreter/components/runtimes/FunctionRuntime.js +29 -2
  12. package/dist/interpreter/components/runtimes/LazyRuntime.d.ts +5 -0
  13. package/dist/interpreter/components/runtimes/LazyRuntime.js +70 -39
  14. package/dist/interpreter/entities.d.ts +105 -0
  15. package/dist/interpreter/entities.js +96 -0
  16. package/dist/src/analyzer/GraphBuilder.d.ts +30 -0
  17. package/dist/src/analyzer/GraphBuilder.js +100 -0
  18. package/dist/src/analyzer/index.d.ts +59 -0
  19. package/dist/src/analyzer/index.js +152 -0
  20. package/dist/src/analyzer/inspections/functional/functional.d.ts +44 -0
  21. package/dist/src/analyzer/inspections/functional/functional.js +149 -0
  22. package/dist/src/analyzer/inspections/functional/smells.d.ts +16 -0
  23. package/dist/src/analyzer/inspections/functional/smells.js +98 -0
  24. package/dist/src/analyzer/inspections/generic/generic.d.ts +178 -0
  25. package/dist/src/analyzer/inspections/generic/generic.js +604 -0
  26. package/dist/src/analyzer/inspections/generic/smells.d.ts +61 -0
  27. package/dist/src/analyzer/inspections/generic/smells.js +349 -0
  28. package/dist/src/analyzer/inspections/imperative/imperative.d.ts +35 -0
  29. package/dist/src/analyzer/inspections/imperative/imperative.js +109 -0
  30. package/dist/src/analyzer/inspections/imperative/smells.d.ts +16 -0
  31. package/dist/src/analyzer/inspections/imperative/smells.js +58 -0
  32. package/dist/src/analyzer/inspections/logic/logic.d.ts +32 -0
  33. package/dist/src/analyzer/inspections/logic/logic.js +96 -0
  34. package/dist/src/analyzer/inspections/logic/smells.d.ts +15 -0
  35. package/dist/src/analyzer/inspections/logic/smells.js +60 -0
  36. package/dist/src/analyzer/inspections/object/object.d.ts +90 -0
  37. package/dist/src/analyzer/inspections/object/object.js +321 -0
  38. package/dist/src/analyzer/inspections/object/smells.d.ts +30 -0
  39. package/dist/src/analyzer/inspections/object/smells.js +135 -0
  40. package/dist/src/analyzer/utils.d.ts +30 -0
  41. package/dist/src/analyzer/utils.js +78 -0
  42. package/dist/src/index.d.ts +4 -0
  43. package/dist/src/index.js +4 -0
  44. package/dist/src/interpreter/components/EnvBuilder.d.ts +21 -0
  45. package/dist/src/interpreter/components/EnvBuilder.js +155 -0
  46. package/dist/src/interpreter/components/Operations.d.ts +14 -0
  47. package/dist/src/interpreter/components/Operations.js +84 -0
  48. package/dist/src/interpreter/components/PatternMatcher.d.ts +73 -0
  49. package/dist/src/interpreter/components/PatternMatcher.js +358 -0
  50. package/dist/src/interpreter/components/RuntimeContext.d.ts +35 -0
  51. package/dist/src/interpreter/components/RuntimeContext.js +93 -0
  52. package/dist/src/interpreter/components/TestRunner.d.ts +19 -0
  53. package/dist/src/interpreter/components/TestRunner.js +110 -0
  54. package/dist/src/interpreter/components/Visitor.d.ts +71 -0
  55. package/dist/src/interpreter/components/Visitor.js +638 -0
  56. package/dist/src/interpreter/components/logic/LogicEngine.d.ts +29 -0
  57. package/dist/src/interpreter/components/logic/LogicEngine.js +259 -0
  58. package/dist/src/interpreter/components/logic/LogicResolver.d.ts +53 -0
  59. package/dist/src/interpreter/components/logic/LogicResolver.js +484 -0
  60. package/dist/src/interpreter/components/logic/LogicTranslator.d.ts +14 -0
  61. package/dist/src/interpreter/components/logic/LogicTranslator.js +99 -0
  62. package/dist/src/interpreter/components/runtimes/FunctionRuntime.d.ts +12 -0
  63. package/dist/src/interpreter/components/runtimes/FunctionRuntime.js +149 -0
  64. package/dist/src/interpreter/components/runtimes/LazyRuntime.d.ts +19 -0
  65. package/dist/src/interpreter/components/runtimes/LazyRuntime.js +269 -0
  66. package/dist/src/interpreter/components/runtimes/ObjectRuntime.d.ts +35 -0
  67. package/dist/{interpreter/components → src/interpreter/components/runtimes}/ObjectRuntime.js +23 -20
  68. package/dist/src/interpreter/errors.d.ts +19 -0
  69. package/dist/src/interpreter/errors.js +36 -0
  70. package/dist/src/interpreter/index.d.ts +24 -0
  71. package/dist/src/interpreter/index.js +41 -0
  72. package/dist/src/interpreter/trampoline.d.ts +17 -0
  73. package/dist/src/interpreter/trampoline.js +38 -0
  74. package/dist/src/interpreter/utils.d.ts +11 -0
  75. package/dist/src/interpreter/utils.js +65 -0
  76. package/dist/src/tester/index.d.ts +25 -0
  77. package/dist/src/tester/index.js +113 -0
  78. package/dist/src/utils/helpers.d.ts +13 -0
  79. package/dist/src/utils/helpers.js +52 -0
  80. package/dist/utils/helpers.d.ts +5 -1
  81. package/dist/utils/helpers.js +79 -6
  82. package/package.json +5 -3
  83. package/src/analyzer/index.ts +12 -2
  84. package/src/interpreter/components/PatternMatcher.ts +2 -0
  85. package/src/interpreter/components/RuntimeContext.ts +10 -4
  86. package/src/interpreter/components/TestRunner.ts +4 -40
  87. package/src/interpreter/components/Visitor.ts +34 -47
  88. package/src/interpreter/components/runtimes/FunctionRuntime.ts +42 -3
  89. package/src/interpreter/components/runtimes/LazyRuntime.ts +82 -49
  90. package/src/utils/helpers.ts +111 -10
  91. package/tests/analyzer/generic.spec.ts +14 -0
  92. package/tests/analyzer/helpers.spec.ts +14 -8
  93. package/tests/interpreter/interpreter.spec.ts +10 -0
  94. package/tests/tester/Tester.spec.ts +0 -1
  95. package/dist/interpreter/components/FunctionRuntime.d.ts +0 -8
  96. package/dist/interpreter/components/FunctionRuntime.js +0 -81
  97. package/dist/interpreter/components/LazyRuntime.d.ts +0 -7
  98. package/dist/interpreter/components/LazyRuntime.js +0 -94
  99. package/dist/interpreter/components/LogicEngine.d.ts +0 -24
  100. package/dist/interpreter/components/LogicEngine.js +0 -173
  101. package/dist/interpreter/components/LogicResolver.d.ts +0 -10
  102. package/dist/interpreter/components/LogicResolver.js +0 -97
  103. package/dist/interpreter/components/ObjectRuntime.d.ts +0 -33
  104. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,149 @@
1
+ import { UnguardedBody, Sequence, Return, Function, isRuntimeFunction, } from "yukigo-ast";
2
+ import { PatternMatcher } from "../PatternMatcher.js";
3
+ import { InterpreterError } from "../../errors.js";
4
+ import { EnvBuilderVisitor } from "../EnvBuilder.js";
5
+ import { valueToCPS } from "../../trampoline.js";
6
+ import { RuntimeContext } from "../RuntimeContext.js";
7
+ import { InterpreterVisitor } from "../Visitor.js";
8
+ class NonExhaustivePatterns extends InterpreterError {
9
+ constructor(funcName) {
10
+ super("PatternMatch", `Non-exhaustive patterns in '${funcName}'`);
11
+ }
12
+ }
13
+ export class FunctionRuntime {
14
+ context;
15
+ constructor(context) {
16
+ this.context = context;
17
+ }
18
+ apply(func, args, k) {
19
+ const funcName = func.identifier;
20
+ const equations = func.equations;
21
+ const oldEnv = this.context.env;
22
+ if (this.context.config.debug)
23
+ console.log(`[FunctionRuntime] Applying function: ${funcName} with args:`, args);
24
+ const tryNextEquation = (eqIndex) => {
25
+ if (eqIndex >= equations.length) {
26
+ this.context.setEnv(oldEnv);
27
+ throw new NonExhaustivePatterns(funcName ?? "<anonymous>");
28
+ }
29
+ const eq = equations[eqIndex];
30
+ if (eq.patterns.length !== args.length)
31
+ return () => tryNextEquation(eqIndex + 1);
32
+ const bindings = [];
33
+ return this.patternsMatch(eq, args, bindings, (isMatch) => {
34
+ if (!isMatch)
35
+ return () => tryNextEquation(eqIndex + 1);
36
+ if (this.context.config.debug)
37
+ console.log(`[FunctionRuntime] Match successful for ${funcName} equation ${eqIndex}`);
38
+ const localEnv = new Map(bindings);
39
+ if (func.closure)
40
+ this.context.setEnv(func.closure);
41
+ this.context.pushEnv(localEnv);
42
+ const wrappedK = (res) => {
43
+ this.context.setEnv(oldEnv);
44
+ return k(res);
45
+ };
46
+ const evaluatorFactory = (ctx) => new InterpreterVisitor(ctx);
47
+ const body = eq.body;
48
+ // UnguardedBody
49
+ if (body instanceof UnguardedBody)
50
+ return this.evaluateSequence(body.sequence, this.context, evaluatorFactory, wrappedK);
51
+ // GuardedBody
52
+ return () => {
53
+ if (Array.isArray(body) && body.length > 0) {
54
+ const prototypeBody = body[0].body;
55
+ if (prototypeBody instanceof Sequence)
56
+ this.preloadDefinitions(prototypeBody, evaluatorFactory);
57
+ }
58
+ const tryNextGuard = (guardIndex) => {
59
+ if (guardIndex >= body.length) {
60
+ this.context.setEnv(oldEnv);
61
+ return () => tryNextEquation(eqIndex + 1);
62
+ }
63
+ const evaluator = evaluatorFactory(this.context);
64
+ const guard = body[guardIndex];
65
+ return evaluator.evaluate(guard.condition, (cond) => {
66
+ if (cond !== true)
67
+ return () => tryNextGuard(guardIndex + 1);
68
+ if (!(guard.body instanceof Sequence))
69
+ return evaluator.evaluate(guard.body, wrappedK);
70
+ return this.evaluateSequence(guard.body, this.context, evaluatorFactory, wrappedK);
71
+ });
72
+ };
73
+ return tryNextGuard(0);
74
+ };
75
+ });
76
+ };
77
+ return tryNextEquation(0);
78
+ }
79
+ applyArguments(func, args) {
80
+ if (args.length < func.arity) {
81
+ return valueToCPS({
82
+ ...func,
83
+ pendingArgs: args,
84
+ });
85
+ }
86
+ const argsToConsume = args.slice(0, func.arity);
87
+ const remainingArgs = args.slice(func.arity);
88
+ const evaluatedArgs = argsToConsume.map((arg) => typeof arg === "function" ? arg() : arg);
89
+ return (cont) => () => this.apply(func, evaluatedArgs, (result) => {
90
+ if (remainingArgs.length > 0) {
91
+ if (isRuntimeFunction(result)) {
92
+ const nextArgs = result.pendingArgs
93
+ ? [...result.pendingArgs, ...remainingArgs]
94
+ : remainingArgs;
95
+ return this.applyArguments(result, nextArgs)(cont);
96
+ }
97
+ else {
98
+ throw new InterpreterError("Application", `Too many arguments provided. Result was '${result}' (not a function), but had ${remainingArgs.length} args left.`);
99
+ }
100
+ }
101
+ return cont(result);
102
+ });
103
+ }
104
+ preloadDefinitions(seq, evaluatorFactory) {
105
+ const ctx = new RuntimeContext();
106
+ new EnvBuilderVisitor(ctx).build(seq.statements);
107
+ const evaluator = evaluatorFactory(ctx);
108
+ for (const stmt of seq.statements) {
109
+ if (stmt instanceof Function || stmt instanceof Return)
110
+ continue;
111
+ evaluator.evaluate(stmt, (val) => val);
112
+ }
113
+ }
114
+ patternsMatch(eq, args, bindings, k) {
115
+ const matchNext = (index) => {
116
+ if (index >= args.length)
117
+ return k(true);
118
+ const matcher = new PatternMatcher(args[index], bindings, this.context);
119
+ return eq.patterns[index].accept(matcher)((isMatch) => {
120
+ if (!isMatch)
121
+ return k(false);
122
+ return () => matchNext(index + 1);
123
+ });
124
+ };
125
+ return matchNext(0);
126
+ }
127
+ evaluateSequence(seq, ctx, evaluatorFactory, k) {
128
+ new EnvBuilderVisitor(ctx).build(seq.statements);
129
+ const evaluator = evaluatorFactory(ctx);
130
+ const evaluateNext = (index, lastResult) => {
131
+ if (index >= seq.statements.length)
132
+ return k(lastResult);
133
+ const stmt = seq.statements[index];
134
+ if (stmt instanceof Function)
135
+ return () => evaluateNext(index + 1, lastResult);
136
+ if (stmt instanceof Return) {
137
+ if (!stmt.body)
138
+ throw new Error("[FunctionRuntime]: Return \`body\` was undefined");
139
+ return evaluator.evaluate(stmt.body, k);
140
+ }
141
+ else {
142
+ return evaluator.evaluate(stmt, (result) => {
143
+ return () => evaluateNext(index + 1, result);
144
+ });
145
+ }
146
+ };
147
+ return evaluateNext(0, undefined);
148
+ }
149
+ }
@@ -0,0 +1,19 @@
1
+ import { PrimitiveValue, RangeExpression, ConsExpression, ListBinaryOperation } from "yukigo-ast";
2
+ import { ExpressionEvaluator } from "../../utils.js";
3
+ import { Continuation, Thunk } from "../../trampoline.js";
4
+ import { RuntimeContext } from "../RuntimeContext.js";
5
+ export declare class LazyRuntime {
6
+ private context;
7
+ constructor(context: RuntimeContext);
8
+ realizeList<R = PrimitiveValue[]>(val: PrimitiveValue, k: Continuation<PrimitiveValue[], R>): Thunk<R>;
9
+ evaluateRange(node: RangeExpression, evaluator: ExpressionEvaluator, k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
10
+ evaluateCons(node: ConsExpression, evaluator: ExpressionEvaluator, k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
11
+ evaluateConcat(left: PrimitiveValue, right: PrimitiveValue, k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
12
+ evaluateConcatLazy(node: ListBinaryOperation, evaluator: ExpressionEvaluator, k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
13
+ deepEqual<R = boolean>(a: PrimitiveValue, b: PrimitiveValue, k: Continuation<boolean, R>): Thunk<R>;
14
+ private isPlainObject;
15
+ private isCollection;
16
+ private isListLike;
17
+ private deepEqualCollection;
18
+ private deepEqualObject;
19
+ }
@@ -0,0 +1,269 @@
1
+ import { isLazyList, } from "yukigo-ast";
2
+ import { createMemoizedStream, isMemoizedList, } from "../PatternMatcher.js";
3
+ import { idContinuation, trampoline, } from "../../trampoline.js";
4
+ export class LazyRuntime {
5
+ context;
6
+ constructor(context) {
7
+ this.context = context;
8
+ }
9
+ realizeList(val, k) {
10
+ if (Array.isArray(val))
11
+ return k(val);
12
+ if (typeof val === "string")
13
+ return k(val.split(""));
14
+ if (isLazyList(val)) {
15
+ const result = [];
16
+ const iter = val.generator();
17
+ const next = () => {
18
+ const step = iter.next();
19
+ if (step.done)
20
+ return k(result);
21
+ if (step.value === undefined)
22
+ throw new Error("LazyList yielded undefined");
23
+ result.push(step.value);
24
+ return () => next();
25
+ };
26
+ return next();
27
+ }
28
+ throw new Error(`Expected List or LazyList, got ${typeof val}`);
29
+ }
30
+ evaluateRange(node, evaluator, k) {
31
+ return evaluator.evaluate(node.start, (startVal) => {
32
+ if (typeof startVal !== "number")
33
+ throw new Error("Range start must be a number");
34
+ const hasEnd = node.end != null;
35
+ const finishWithStep = (step) => {
36
+ if (!hasEnd) {
37
+ return k(createMemoizedStream(function* () {
38
+ let current = startVal;
39
+ while (true) {
40
+ yield current;
41
+ current += step;
42
+ }
43
+ }));
44
+ }
45
+ return evaluator.evaluate(node.end, (endVal) => {
46
+ if (typeof endVal !== "number")
47
+ throw new Error("Range end must be a number");
48
+ const cond = step > 0 ? (c) => c <= endVal : (c) => c >= endVal;
49
+ if (this.context.config.lazyLoading) {
50
+ return k(createMemoizedStream(function* () {
51
+ let current = startVal;
52
+ while (cond(current)) {
53
+ yield current;
54
+ current += step;
55
+ }
56
+ }));
57
+ }
58
+ const result = [];
59
+ let current = startVal;
60
+ while (cond(current)) {
61
+ result.push(current);
62
+ current += step;
63
+ }
64
+ return k(result);
65
+ });
66
+ };
67
+ if (node.step) {
68
+ return evaluator.evaluate(node.step, (secondVal) => {
69
+ if (typeof secondVal !== "number")
70
+ throw new Error("Range step must be a number");
71
+ const step = secondVal - startVal;
72
+ if (step === 0)
73
+ throw new Error("Range step cannot be zero");
74
+ return finishWithStep(step);
75
+ });
76
+ }
77
+ return finishWithStep(1);
78
+ });
79
+ }
80
+ evaluateCons(node, evaluator, k) {
81
+ const ctx = this.context;
82
+ const capturedEnv = ctx.clone();
83
+ return evaluator.evaluate(node.head, (head) => {
84
+ if (ctx.config.lazyLoading) {
85
+ const consState = {
86
+ head,
87
+ tailExpr: node.tail,
88
+ evaluator,
89
+ capturedEnv,
90
+ realizedTail: undefined,
91
+ };
92
+ const consList = {
93
+ type: "LazyList",
94
+ generator: function* () {
95
+ let current = consState;
96
+ while (current !== undefined && current !== null) {
97
+ if (current.tailExpr !== undefined) {
98
+ yield current.head;
99
+ if (current.realizedTail === undefined) {
100
+ const prevEnv = ctx.env;
101
+ ctx.setEnv(current.capturedEnv);
102
+ try {
103
+ current.realizedTail = trampoline(current.evaluator.evaluate(current.tailExpr, idContinuation));
104
+ }
105
+ finally {
106
+ ctx.setEnv(prevEnv);
107
+ }
108
+ }
109
+ current = current.realizedTail;
110
+ }
111
+ else if (isLazyList(current)) {
112
+ const memoized = current;
113
+ if (isMemoizedList(memoized) && memoized._consState) {
114
+ current = memoized._consState;
115
+ }
116
+ else {
117
+ const iter = current.generator();
118
+ let step = iter.next();
119
+ while (!step.done) {
120
+ yield step.value;
121
+ step = iter.next();
122
+ }
123
+ break;
124
+ }
125
+ }
126
+ else if (Array.isArray(current) ||
127
+ typeof current === "string") {
128
+ for (const x of current)
129
+ yield x;
130
+ break;
131
+ }
132
+ else {
133
+ throw new Error(`Invalid tail type for Cons: ${typeof current}`);
134
+ }
135
+ }
136
+ },
137
+ };
138
+ const memoized = createMemoizedStream(() => consList.generator());
139
+ memoized._consState = consState;
140
+ return k(memoized);
141
+ }
142
+ // Eager behavior
143
+ return evaluator.evaluate(node.tail, (tail) => {
144
+ if (typeof tail === "string")
145
+ return k(head + tail);
146
+ if (isLazyList(tail) || !Array.isArray(tail))
147
+ throw new Error("Expected Array in eager Cons");
148
+ return k([head, ...tail]);
149
+ });
150
+ });
151
+ }
152
+ evaluateConcat(left, right, k) {
153
+ if (this.context.config.lazyLoading) {
154
+ return k(createMemoizedStream(function* () {
155
+ if (Array.isArray(left))
156
+ yield* left;
157
+ else if (typeof left === "string")
158
+ yield* left.split("");
159
+ else if (isLazyList(left))
160
+ yield* left.generator();
161
+ else
162
+ throw new Error("Invalid left operand for lazy Concat");
163
+ if (Array.isArray(right))
164
+ yield* right;
165
+ else if (typeof right === "string")
166
+ yield* right.split("");
167
+ else if (isLazyList(right))
168
+ yield* right.generator();
169
+ else
170
+ throw new Error("Invalid right operand for lazy Concat");
171
+ }));
172
+ }
173
+ if (typeof left === "string" && typeof right === "string")
174
+ return k(left + right);
175
+ return this.realizeList(left, (lArr) => {
176
+ return () => this.realizeList(right, (rArr) => {
177
+ return k(lArr.concat(rArr));
178
+ });
179
+ });
180
+ }
181
+ evaluateConcatLazy(node, evaluator, k) {
182
+ const ctx = this.context;
183
+ return evaluator.evaluate(node.left, (left) => {
184
+ const capturedEnv = ctx.clone();
185
+ return k(createMemoizedStream(function* () {
186
+ if (Array.isArray(left))
187
+ yield* left;
188
+ else if (typeof left === "string")
189
+ yield* left.split("");
190
+ else if (isLazyList(left))
191
+ yield* left.generator();
192
+ else
193
+ throw new Error("Invalid left operand for lazy Concat");
194
+ // right evaluates lazily on demand
195
+ const prevEnv = ctx.env;
196
+ ctx.setEnv(capturedEnv);
197
+ let right;
198
+ try {
199
+ right = trampoline(evaluator.evaluate(node.right, idContinuation));
200
+ }
201
+ finally {
202
+ ctx.setEnv(prevEnv);
203
+ }
204
+ if (Array.isArray(right))
205
+ yield* right;
206
+ else if (typeof right === "string")
207
+ yield* right.split("");
208
+ else if (isLazyList(right))
209
+ yield* right.generator();
210
+ else
211
+ throw new Error("Invalid right operand for lazy Concat");
212
+ }));
213
+ });
214
+ }
215
+ deepEqual(a, b, k) {
216
+ if (a === b)
217
+ return k(true);
218
+ const aIsListLike = this.isListLike(a);
219
+ const bIsListLike = this.isListLike(b);
220
+ const eitherIsCollection = this.isCollection(a) || this.isCollection(b);
221
+ if (eitherIsCollection && aIsListLike && bIsListLike) {
222
+ return this.realizeList(a, (valA) => () => this.realizeList(b, (valB) => {
223
+ if (valA.length !== valB.length)
224
+ return k(false);
225
+ return this.deepEqualCollection(valA, valB, 0, k);
226
+ }));
227
+ }
228
+ if (eitherIsCollection)
229
+ return k(false); // colección vs número → false
230
+ if (this.isPlainObject(a) && this.isPlainObject(b))
231
+ return this.deepEqualObject(a, b, k);
232
+ return k(a == b); // primitivos: number, boolean, string==number
233
+ }
234
+ isPlainObject(val) {
235
+ return val !== null && typeof val === "object"
236
+ && !isLazyList(val) && !Array.isArray(val);
237
+ }
238
+ isCollection(val) {
239
+ return Array.isArray(val) || isLazyList(val);
240
+ }
241
+ isListLike(val) {
242
+ return Array.isArray(val) || isLazyList(val) || typeof val === "string";
243
+ }
244
+ deepEqualCollection(a, b, index, k) {
245
+ if (index >= a.length)
246
+ return k(true);
247
+ return this.deepEqual(a[index], b[index], (eq) => {
248
+ if (!eq)
249
+ return k(false);
250
+ return () => this.deepEqualCollection(a, b, index + 1, k);
251
+ });
252
+ }
253
+ deepEqualObject(a, b, k) {
254
+ const keys = Object.keys(a);
255
+ if (keys.length !== Object.keys(b).length)
256
+ return k(false);
257
+ const checkNext = (index) => {
258
+ if (index >= keys.length)
259
+ return k(true);
260
+ const key = keys[index];
261
+ return this.deepEqual(a[key], b[key], (eq) => {
262
+ if (!eq)
263
+ return k(false);
264
+ return () => checkNext(index + 1);
265
+ });
266
+ };
267
+ return checkNext(0);
268
+ }
269
+ }
@@ -0,0 +1,35 @@
1
+ import { PrimitiveValue, RuntimeFunction, RuntimeObject, EnvStack } from "yukigo-ast";
2
+ import { Continuation, Thunk } from "../../trampoline.js";
3
+ import { RuntimeContext } from "../RuntimeContext.js";
4
+ export declare class ObjectRuntime {
5
+ private context;
6
+ constructor(context: RuntimeContext);
7
+ /**
8
+ * Creates a new instance of an Object.
9
+ * Typically called by visitNew()
10
+ */
11
+ instantiate(className: string, identifier: string, fieldDefinitions: Map<string, PrimitiveValue>, methodDefinitions: Map<string, RuntimeFunction>): RuntimeObject;
12
+ /**
13
+ * Handles Method Calls (Message Passing).
14
+ * Reuses FunctionRuntime to execute the method body.
15
+ */
16
+ dispatch(receiver: PrimitiveValue, methodName: string, args: PrimitiveValue[], env: EnvStack, k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
17
+ /**
18
+ * Handles calls to super() or super.method()
19
+ */
20
+ dispatchSuper(currentEnv: EnvStack, methodName: string, args: PrimitiveValue[], k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
21
+ private createDispatchScope;
22
+ private getResolutionChain;
23
+ private expandClassHierarchy;
24
+ private findMethodInChain;
25
+ /**
26
+ * Field Access (Get)
27
+ * e.g. self.myField
28
+ */
29
+ getField(receiver: PrimitiveValue, fieldName: string): PrimitiveValue;
30
+ /**
31
+ * Field Mutation (Set)
32
+ * e.g. self.myField = 10
33
+ */
34
+ setField(receiver: PrimitiveValue, fieldName: string, value: PrimitiveValue): PrimitiveValue;
35
+ }
@@ -1,13 +1,15 @@
1
1
  import { isRuntimeObject, isRuntimeClass, } from "yukigo-ast";
2
- import { FunctionRuntime } from "./FunctionRuntime.js";
3
- import { lookup, pushEnv } from "../utils.js";
4
- import { InterpreterError } from "../errors.js";
2
+ import { InterpreterError } from "../../errors.js";
5
3
  export class ObjectRuntime {
4
+ context;
5
+ constructor(context) {
6
+ this.context = context;
7
+ }
6
8
  /**
7
9
  * Creates a new instance of an Object.
8
10
  * Typically called by visitNew()
9
11
  */
10
- static instantiate(className, identifier, fieldDefinitions, methodDefinitions) {
12
+ instantiate(className, identifier, fieldDefinitions, methodDefinitions) {
11
13
  return {
12
14
  type: "Object",
13
15
  className,
@@ -20,7 +22,7 @@ export class ObjectRuntime {
20
22
  * Handles Method Calls (Message Passing).
21
23
  * Reuses FunctionRuntime to execute the method body.
22
24
  */
23
- static dispatch(receiver, methodName, args, env, evaluatorFactory) {
25
+ dispatch(receiver, methodName, args, env, k) {
24
26
  if (!isRuntimeObject(receiver))
25
27
  throw new Error(`${receiver} is not an object`);
26
28
  const chain = this.getResolutionChain(receiver, env);
@@ -28,15 +30,16 @@ export class ObjectRuntime {
28
30
  if (!match)
29
31
  throw new InterpreterError("MethodDispatch", `${receiver.className} does not understand '${methodName}'.`);
30
32
  const objectScope = this.createDispatchScope(receiver, match, methodName);
31
- return FunctionRuntime.apply(methodName, match.method.equations, args, pushEnv(env, objectScope), evaluatorFactory);
33
+ this.context.pushEnv(objectScope);
34
+ return this.context.funcRuntime.apply(match.method, args, k);
32
35
  }
33
36
  /**
34
37
  * Handles calls to super() or super.method()
35
38
  */
36
- static dispatchSuper(currentEnv, methodName, args, evaluatorFactory) {
37
- const self = lookup(currentEnv, "self");
38
- const currentHolder = lookup(currentEnv, "__CONTEXT_CLASS__");
39
- const currentMethodName = lookup(currentEnv, "__METHOD_NAME__");
39
+ dispatchSuper(currentEnv, methodName, args, k) {
40
+ const self = this.context.lookup("self");
41
+ const currentHolder = this.context.lookup("__CONTEXT_CLASS__");
42
+ const currentMethodName = this.context.lookup("__METHOD_NAME__");
40
43
  const targetMethodName = methodName || currentMethodName;
41
44
  if (!self || !currentHolder)
42
45
  throw new InterpreterError("SuperError", "'super' used outside of a method context");
@@ -49,9 +52,10 @@ export class ObjectRuntime {
49
52
  if (!match)
50
53
  throw new InterpreterError("Super", `Super method '${methodName}' not found`);
51
54
  const objectScope = this.createDispatchScope(self, match, targetMethodName);
52
- return FunctionRuntime.apply(methodName, match.method.equations, args, pushEnv(currentEnv, objectScope), evaluatorFactory);
55
+ this.context.pushEnv(objectScope);
56
+ return this.context.funcRuntime.apply(match.method, args, k);
53
57
  }
54
- static createDispatchScope(self, match, targetName) {
58
+ createDispatchScope(self, match, targetName) {
55
59
  const objectScope = new Map();
56
60
  objectScope.set("self", self);
57
61
  objectScope.set("__CONTEXT_CLASS__", match.holder);
@@ -60,7 +64,7 @@ export class ObjectRuntime {
60
64
  objectScope.set(key, val);
61
65
  return objectScope;
62
66
  }
63
- static getResolutionChain(receiver, env) {
67
+ getResolutionChain(receiver, env) {
64
68
  const chain = [];
65
69
  chain.push(receiver);
66
70
  if (receiver.className) {
@@ -68,8 +72,8 @@ export class ObjectRuntime {
68
72
  }
69
73
  return chain;
70
74
  }
71
- static expandClassHierarchy(className, env, chain) {
72
- const classDef = lookup(env, className);
75
+ expandClassHierarchy(className, env, chain) {
76
+ const classDef = this.context.lookup(className);
73
77
  if (!isRuntimeClass(classDef))
74
78
  throw new InterpreterError("expandClassHierarchy", "classDef was expected to be a RuntimeClass");
75
79
  chain.push(classDef);
@@ -81,7 +85,7 @@ export class ObjectRuntime {
81
85
  if (classDef.superclass)
82
86
  this.expandClassHierarchy(classDef.superclass, env, chain);
83
87
  }
84
- static findMethodInChain(chain, methodName) {
88
+ findMethodInChain(chain, methodName) {
85
89
  for (const link of chain) {
86
90
  if (link.methods.has(methodName))
87
91
  return {
@@ -95,7 +99,7 @@ export class ObjectRuntime {
95
99
  * Field Access (Get)
96
100
  * e.g. self.myField
97
101
  */
98
- static getField(receiver, fieldName) {
102
+ getField(receiver, fieldName) {
99
103
  if (!isRuntimeObject(receiver))
100
104
  throw new InterpreterError("FieldAccess", "Target is not an object");
101
105
  if (!receiver.fields.has(fieldName)) {
@@ -109,10 +113,9 @@ export class ObjectRuntime {
109
113
  * Field Mutation (Set)
110
114
  * e.g. self.myField = 10
111
115
  */
112
- static setField(receiver, fieldName, value, config) {
113
- if (!config.mutability) {
116
+ setField(receiver, fieldName, value) {
117
+ if (!this.context.config.mutability)
114
118
  throw new InterpreterError("FieldAssignment", `Cannot mutate field '${fieldName}': mutability is disabled`);
115
- }
116
119
  if (!isRuntimeObject(receiver))
117
120
  throw new InterpreterError("FieldAssignment", "Target is not an object");
118
121
  if (!receiver.fields.has(fieldName))
@@ -0,0 +1,19 @@
1
+ import { SourceLocation } from "yukigo-ast";
2
+ export interface ErrorFrame {
3
+ nodeType: string;
4
+ loc?: SourceLocation;
5
+ }
6
+ export declare class InterpreterError extends Error {
7
+ context: string;
8
+ frames: ErrorFrame[];
9
+ constructor(context: string, message: string, frames?: ErrorFrame[]);
10
+ pushFrame(frame: ErrorFrame): void;
11
+ formatStack(): string;
12
+ toString(): string;
13
+ }
14
+ export declare class UnexpectedValue extends InterpreterError {
15
+ constructor(ctx: string, expected: string, got: string);
16
+ }
17
+ export declare class UnboundVariable extends Error {
18
+ constructor(name: string);
19
+ }
@@ -0,0 +1,36 @@
1
+ export class InterpreterError extends Error {
2
+ context;
3
+ frames;
4
+ constructor(context, message, frames = []) {
5
+ super(`[${context}] ${message}`);
6
+ this.context = context;
7
+ this.frames = frames;
8
+ }
9
+ pushFrame(frame) {
10
+ this.frames.push(frame);
11
+ }
12
+ formatStack() {
13
+ if (!this.frames.length)
14
+ return "";
15
+ const formatted = this.frames
16
+ .map((f) => {
17
+ const loc = f.loc ? ` (line ${f.loc.line}, col ${f.loc.column})` : "";
18
+ return ` • ${f.nodeType}${loc}`;
19
+ })
20
+ .join("\n");
21
+ return `\nTrace:\n${formatted}`;
22
+ }
23
+ toString() {
24
+ return `${this.message}${this.formatStack()}`;
25
+ }
26
+ }
27
+ export class UnexpectedValue extends InterpreterError {
28
+ constructor(ctx, expected, got) {
29
+ super(ctx, `Expected ${expected} but got ${got}`);
30
+ }
31
+ }
32
+ export class UnboundVariable extends Error {
33
+ constructor(name) {
34
+ super(`Unbound variable: ${name}`);
35
+ }
36
+ }
@@ -0,0 +1,24 @@
1
+ import { PrimitiveValue, AST, ASTNode } from "yukigo-ast";
2
+ import { InterpreterConfig } from "./components/RuntimeContext.js";
3
+ export type Bindings = [string, PrimitiveValue][];
4
+ /**
5
+ * The Interpreter class is responsible for evaluating the Abstract Syntax Tree (AST)
6
+ * generated by the parsers.
7
+ *
8
+ * It manages the global execution environment and delegates the actual evaluation
9
+ * of Expression nodes to a dedicated visitor.
10
+ */
11
+ export declare class Interpreter {
12
+ private context;
13
+ /**
14
+ * @param ast The Abstract Syntax Tree (AST) of the program to be interpreted.
15
+ */
16
+ constructor(ast: AST, config?: InterpreterConfig);
17
+ /**
18
+ * Evaluates a single Expression node within the context of the global environment.
19
+ *
20
+ * @param expr The root Expression node to be evaluated.
21
+ * @returns The resulting primitive value (number, string, boolean, etc.) after evaluation.
22
+ */
23
+ evaluate(expr: ASTNode): PrimitiveValue;
24
+ }