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,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
+ }
@@ -0,0 +1,126 @@
1
+ import { isRuntimeObject, isRuntimeClass, } from "yukigo-ast";
2
+ import { InterpreterError } from "../../errors.js";
3
+ export class ObjectRuntime {
4
+ context;
5
+ constructor(context) {
6
+ this.context = context;
7
+ }
8
+ /**
9
+ * Creates a new instance of an Object.
10
+ * Typically called by visitNew()
11
+ */
12
+ instantiate(className, identifier, fieldDefinitions, methodDefinitions) {
13
+ return {
14
+ type: "Object",
15
+ className,
16
+ identifier,
17
+ fields: new Map(fieldDefinitions),
18
+ methods: methodDefinitions,
19
+ };
20
+ }
21
+ /**
22
+ * Handles Method Calls (Message Passing).
23
+ * Reuses FunctionRuntime to execute the method body.
24
+ */
25
+ dispatch(receiver, methodName, args, env, k) {
26
+ if (!isRuntimeObject(receiver))
27
+ throw new Error(`${receiver} is not an object`);
28
+ const chain = this.getResolutionChain(receiver, env);
29
+ const match = this.findMethodInChain(chain, methodName);
30
+ if (!match)
31
+ throw new InterpreterError("MethodDispatch", `${receiver.className} does not understand '${methodName}'.`);
32
+ const objectScope = this.createDispatchScope(receiver, match, methodName);
33
+ this.context.pushEnv(objectScope);
34
+ return this.context.funcRuntime.apply(match.method, args, k);
35
+ }
36
+ /**
37
+ * Handles calls to super() or super.method()
38
+ */
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__");
43
+ const targetMethodName = methodName || currentMethodName;
44
+ if (!self || !currentHolder)
45
+ throw new InterpreterError("SuperError", "'super' used outside of a method context");
46
+ const chain = this.getResolutionChain(self, currentEnv);
47
+ const currentIndex = chain.findIndex((c) => c === currentHolder);
48
+ if (currentIndex === -1)
49
+ throw new Error("Fatal: Execution context not found in hierarchy chain");
50
+ const remainingChain = chain.slice(currentIndex + 1);
51
+ const match = this.findMethodInChain(remainingChain, methodName);
52
+ if (!match)
53
+ throw new InterpreterError("Super", `Super method '${methodName}' not found`);
54
+ const objectScope = this.createDispatchScope(self, match, targetMethodName);
55
+ this.context.pushEnv(objectScope);
56
+ return this.context.funcRuntime.apply(match.method, args, k);
57
+ }
58
+ createDispatchScope(self, match, targetName) {
59
+ const objectScope = new Map();
60
+ objectScope.set("self", self);
61
+ objectScope.set("__CONTEXT_CLASS__", match.holder);
62
+ objectScope.set("__METHOD_NAME__", targetName);
63
+ for (const [key, val] of self.fields)
64
+ objectScope.set(key, val);
65
+ return objectScope;
66
+ }
67
+ getResolutionChain(receiver, env) {
68
+ const chain = [];
69
+ chain.push(receiver);
70
+ if (receiver.className) {
71
+ this.expandClassHierarchy(receiver.className, env, chain);
72
+ }
73
+ return chain;
74
+ }
75
+ expandClassHierarchy(className, env, chain) {
76
+ const classDef = this.context.lookup(className);
77
+ if (!isRuntimeClass(classDef))
78
+ throw new InterpreterError("expandClassHierarchy", "classDef was expected to be a RuntimeClass");
79
+ chain.push(classDef);
80
+ const classDefCopy = [...classDef.mixins];
81
+ if (classDef.mixins)
82
+ classDefCopy.reverse().forEach((mixinName) => {
83
+ this.expandClassHierarchy(mixinName, env, chain);
84
+ });
85
+ if (classDef.superclass)
86
+ this.expandClassHierarchy(classDef.superclass, env, chain);
87
+ }
88
+ findMethodInChain(chain, methodName) {
89
+ for (const link of chain) {
90
+ if (link.methods.has(methodName))
91
+ return {
92
+ method: link.methods.get(methodName),
93
+ holder: link,
94
+ };
95
+ }
96
+ return undefined;
97
+ }
98
+ /**
99
+ * Field Access (Get)
100
+ * e.g. self.myField
101
+ */
102
+ getField(receiver, fieldName) {
103
+ if (!isRuntimeObject(receiver))
104
+ throw new InterpreterError("FieldAccess", "Target is not an object");
105
+ if (!receiver.fields.has(fieldName)) {
106
+ if (receiver.methods.has(fieldName))
107
+ return receiver.methods.get(fieldName);
108
+ throw new InterpreterError("FieldAccess", `Field '${fieldName}' not found in ${receiver.className}`);
109
+ }
110
+ return receiver.fields.get(fieldName);
111
+ }
112
+ /**
113
+ * Field Mutation (Set)
114
+ * e.g. self.myField = 10
115
+ */
116
+ setField(receiver, fieldName, value) {
117
+ if (!this.context.config.mutability)
118
+ throw new InterpreterError("FieldAssignment", `Cannot mutate field '${fieldName}': mutability is disabled`);
119
+ if (!isRuntimeObject(receiver))
120
+ throw new InterpreterError("FieldAssignment", "Target is not an object");
121
+ if (!receiver.fields.has(fieldName))
122
+ throw new InterpreterError("FieldAssignment", `Cannot set unknown field '${fieldName}'`);
123
+ receiver.fields.set(fieldName, value);
124
+ return value;
125
+ }
126
+ }
@@ -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
+ }