yukigo 0.1.1 → 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 (36) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/analyzer/index.d.ts +1 -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/package.json +2 -2
  17. package/src/analyzer/index.ts +3 -2
  18. package/src/interpreter/components/PatternMatcher.ts +2 -0
  19. package/src/interpreter/components/RuntimeContext.ts +10 -4
  20. package/src/interpreter/components/TestRunner.ts +4 -40
  21. package/src/interpreter/components/Visitor.ts +34 -47
  22. package/src/interpreter/components/runtimes/FunctionRuntime.ts +42 -3
  23. package/src/interpreter/components/runtimes/LazyRuntime.ts +82 -49
  24. package/tests/analyzer/generic.spec.ts +14 -0
  25. package/tests/interpreter/interpreter.spec.ts +10 -0
  26. package/dist/interpreter/components/FunctionRuntime.d.ts +0 -8
  27. package/dist/interpreter/components/FunctionRuntime.js +0 -81
  28. package/dist/interpreter/components/LazyRuntime.d.ts +0 -7
  29. package/dist/interpreter/components/LazyRuntime.js +0 -94
  30. package/dist/interpreter/components/LogicEngine.d.ts +0 -24
  31. package/dist/interpreter/components/LogicEngine.js +0 -173
  32. package/dist/interpreter/components/LogicResolver.d.ts +0 -10
  33. package/dist/interpreter/components/LogicResolver.js +0 -97
  34. package/dist/interpreter/components/ObjectRuntime.d.ts +0 -33
  35. package/dist/interpreter/components/ObjectRuntime.js +0 -123
  36. package/tsconfig.tsbuildinfo +0 -1
@@ -1,8 +0,0 @@
1
- import { EquationRuntime, PrimitiveValue, EnvStack } from "yukigo-ast";
2
- import { ExpressionEvaluator } from "../utils.js";
3
- export declare class FunctionRuntime {
4
- static apply(funcName: string, equations: EquationRuntime[], args: PrimitiveValue[], currentEnv: EnvStack, evaluatorFactory: (env: EnvStack) => ExpressionEvaluator): PrimitiveValue;
5
- private static preloadDefinitions;
6
- private static patternsMatch;
7
- private static evaluateSequence;
8
- }
@@ -1,81 +0,0 @@
1
- import { UnguardedBody, Sequence, Return, Function, } from "yukigo-ast";
2
- import { PatternMatcher } from "./PatternMatcher.js";
3
- import { pushEnv } from "../utils.js";
4
- import { InterpreterError } from "../errors.js";
5
- import { EnvBuilderVisitor } from "./EnvBuilder.js";
6
- class NonExhaustivePatterns extends InterpreterError {
7
- constructor(funcName) {
8
- super("PatternMatch", `Non-exhaustive patterns in '${funcName}'`);
9
- }
10
- }
11
- export class FunctionRuntime {
12
- static apply(funcName, equations, args, currentEnv, evaluatorFactory) {
13
- for (const eq of equations) {
14
- if (eq.patterns.length !== args.length)
15
- continue;
16
- const bindings = [];
17
- if (!FunctionRuntime.patternsMatch(eq, args, bindings))
18
- continue;
19
- const localEnv = new Map(bindings);
20
- const newStack = pushEnv(currentEnv, localEnv);
21
- const scopeEvaluator = evaluatorFactory(newStack);
22
- // UnguardedBody
23
- if (eq.body instanceof UnguardedBody)
24
- return this.evaluateSequence(eq.body.sequence, scopeEvaluator, newStack);
25
- // GuardedBody
26
- if (Array.isArray(eq.body) && eq.body.length > 0) {
27
- const prototypeBody = eq.body[0].body;
28
- if (prototypeBody instanceof Sequence)
29
- this.preloadDefinitions(prototypeBody, scopeEvaluator, newStack);
30
- }
31
- for (const guard of eq.body) {
32
- const cond = scopeEvaluator.evaluate(guard.condition);
33
- if (cond === true) {
34
- if (guard.body instanceof Sequence)
35
- return this.evaluateSequence(guard.body, scopeEvaluator, newStack);
36
- return scopeEvaluator.evaluate(guard.body);
37
- }
38
- }
39
- }
40
- throw new NonExhaustivePatterns(funcName);
41
- }
42
- static preloadDefinitions(seq, evaluator, env) {
43
- const builder = new EnvBuilderVisitor(env);
44
- for (const stmt of seq.statements) {
45
- if (stmt instanceof Function)
46
- stmt.accept(builder);
47
- }
48
- for (const stmt of seq.statements) {
49
- if (stmt instanceof Function || stmt instanceof Return)
50
- continue;
51
- evaluator.evaluate(stmt);
52
- }
53
- }
54
- static patternsMatch(eq, args, bindings) {
55
- for (let i = 0; i < args.length; i++) {
56
- const matcher = new PatternMatcher(args[i], bindings);
57
- if (!eq.patterns[i].accept(matcher))
58
- return false;
59
- }
60
- return true;
61
- }
62
- static evaluateSequence(seq, evaluator, env) {
63
- let result = undefined;
64
- const builder = new EnvBuilderVisitor(env);
65
- for (const stmt of seq.statements) {
66
- if (stmt instanceof Function)
67
- stmt.accept(builder);
68
- }
69
- for (const stmt of seq.statements) {
70
- if (stmt instanceof Function)
71
- continue;
72
- if (stmt instanceof Return) {
73
- return evaluator.evaluate(stmt.body);
74
- }
75
- else {
76
- result = evaluator.evaluate(stmt);
77
- }
78
- }
79
- return result;
80
- }
81
- }
@@ -1,7 +0,0 @@
1
- import { PrimitiveValue, RangeExpression, ConsExpression } from "yukigo-ast";
2
- import { ExpressionEvaluator } from "../utils.js";
3
- export declare class LazyRuntime {
4
- static realizeList(val: PrimitiveValue): PrimitiveValue[];
5
- static evaluateRange(node: RangeExpression, evaluator: ExpressionEvaluator): PrimitiveValue;
6
- static evaluateCons(node: ConsExpression, evaluator: ExpressionEvaluator, lazy: boolean): PrimitiveValue;
7
- }
@@ -1,94 +0,0 @@
1
- import { isLazyList, } from "yukigo-ast";
2
- import { createMemoizedStream, isMemoizedList } from "./PatternMatcher.js";
3
- export class LazyRuntime {
4
- static realizeList(val) {
5
- if (Array.isArray(val))
6
- return val;
7
- if (isLazyList(val)) {
8
- const result = [];
9
- const iter = val.generator();
10
- let next = iter.next();
11
- while (!next.done) {
12
- if (next.value === undefined)
13
- throw new Error("LazyList yielded undefined");
14
- result.push(next.value);
15
- next = iter.next();
16
- }
17
- return result;
18
- }
19
- throw new Error(`Expected List or LazyList, got ${typeof val}`);
20
- }
21
- static evaluateRange(node, evaluator) {
22
- const startVal = evaluator.evaluate(node.start);
23
- if (typeof startVal !== "number")
24
- throw new Error("Range start must be a number");
25
- const hasEnd = node.end != null;
26
- let step = 1;
27
- if (node.step) {
28
- const secondVal = evaluator.evaluate(node.step);
29
- if (typeof secondVal !== "number")
30
- throw new Error("Range step must be a number");
31
- step = secondVal - startVal;
32
- if (step === 0)
33
- throw new Error("Range step cannot be zero");
34
- }
35
- if (!hasEnd) {
36
- return createMemoizedStream(function* () {
37
- let current = startVal;
38
- while (true) {
39
- yield current;
40
- current += step;
41
- }
42
- });
43
- }
44
- // eager eval
45
- const endVal = evaluator.evaluate(node.end);
46
- if (typeof endVal !== "number")
47
- throw new Error("Range end must be a number");
48
- const result = [];
49
- let current = startVal;
50
- const cond = step > 0 ? () => current <= endVal : () => current >= endVal;
51
- while (cond()) {
52
- result.push(current);
53
- current += step;
54
- }
55
- return result;
56
- }
57
- static evaluateCons(node, evaluator, lazy) {
58
- const head = evaluator.evaluate(node.head);
59
- if (lazy) {
60
- return createMemoizedStream(function* () {
61
- yield head;
62
- const tail = evaluator.evaluate(node.tail);
63
- if (Array.isArray(tail)) {
64
- for (const item of tail)
65
- yield item;
66
- return;
67
- }
68
- if (isLazyList(tail)) {
69
- if (isMemoizedList(tail)) {
70
- const seq = tail._sequence;
71
- let currentIdx = tail._offset;
72
- while (true) {
73
- const res = seq.get(currentIdx);
74
- if (res.done)
75
- break;
76
- yield res.value;
77
- currentIdx++;
78
- }
79
- }
80
- else {
81
- yield* tail.generator();
82
- }
83
- return;
84
- }
85
- throw new Error(`Invalid tail type for Cons: ${typeof tail}`);
86
- });
87
- }
88
- // Eager behavior
89
- const tail = evaluator.evaluate(node.tail);
90
- if (isLazyList(tail) || !Array.isArray(tail))
91
- throw new Error("Expected Array in eager Cons");
92
- return [head, ...tail];
93
- }
94
- }
@@ -1,24 +0,0 @@
1
- import { Exist, Expression, Findall, Forall, Goal, Not, PrimitiveValue, Query, EnvStack } from "yukigo-ast";
2
- import { InterpreterConfig } from "../index.js";
3
- import { ExpressionEvaluator } from "../utils.js";
4
- export declare class LogicEngine {
5
- private env;
6
- private config;
7
- private evaluator;
8
- constructor(env: EnvStack, config: InterpreterConfig, evaluator: ExpressionEvaluator);
9
- unifyExpr(left: Expression, right: Expression): PrimitiveValue;
10
- solveQuery(node: Query): PrimitiveValue;
11
- solveGoal(node: Goal): PrimitiveValue;
12
- solveNot(node: Not): PrimitiveValue;
13
- solveFindall(node: Findall): PrimitiveValue;
14
- solveForall(node: Forall): PrimitiveValue;
15
- solveExist(node: Exist): PrimitiveValue;
16
- private solveConjunction;
17
- private expressionToPattern;
18
- private primitiveToPattern;
19
- private instantiateExpressionAsPattern;
20
- private substitutePattern;
21
- private instantiateTemplate;
22
- private handleOutputMode;
23
- private formatLogicResult;
24
- }
@@ -1,173 +0,0 @@
1
- import { Exist, Goal, LiteralPattern, SymbolPrimitive, Variable, VariablePattern, } from "yukigo-ast";
2
- import { solveGoal, success, unify, } from "./LogicResolver.js";
3
- import { PatternResolver } from "./PatternMatcher.js";
4
- import { createStream, isDefined } from "../utils.js";
5
- import { InterpreterError } from "../errors.js";
6
- export class LogicEngine {
7
- env;
8
- config;
9
- evaluator;
10
- constructor(env, config, evaluator) {
11
- this.env = env;
12
- this.config = config;
13
- this.evaluator = evaluator;
14
- }
15
- unifyExpr(left, right) {
16
- const p1 = this.instantiateExpressionAsPattern(left, new Map());
17
- const p2 = this.instantiateExpressionAsPattern(right, new Map());
18
- const resultSubsts = unify(p1, p2, new Map());
19
- return resultSubsts !== null;
20
- }
21
- solveQuery(node) {
22
- const generator = this.solveConjunction(node.expressions, new Map());
23
- return this.handleOutputMode(generator);
24
- }
25
- solveGoal(node) {
26
- const patterns = node.args;
27
- const generator = solveGoal(this.env, node.identifier.value, patterns, (body, substs) => this.solveConjunction(body, substs));
28
- return this.handleOutputMode(generator);
29
- }
30
- solveNot(node) {
31
- const generator = this.solveConjunction([node.expression], new Map());
32
- const result = generator.next();
33
- return result.done === true;
34
- }
35
- solveFindall(node) {
36
- if (!(node.goal instanceof Goal))
37
- throw new InterpreterError("solveFindall", "Findall expects a Goal");
38
- const goalPatterns = node.goal.args;
39
- const generator = solveGoal(this.env, node.goal.identifier.value, goalPatterns, (b, s) => this.solveConjunction(b, s));
40
- const results = [];
41
- for (const res of generator)
42
- results.push(this.instantiateTemplate(node.template, res.substs));
43
- return results;
44
- }
45
- solveForall(node) {
46
- const conditionGenerator = this.solveConjunction([node.condition], new Map());
47
- for (const condResult of conditionGenerator) {
48
- const actionGenerator = this.solveConjunction([node.action], condResult.substs);
49
- const actionResult = actionGenerator.next();
50
- if (actionResult.done)
51
- return false;
52
- }
53
- return true;
54
- }
55
- solveExist(node) {
56
- const generator = solveGoal(this.env, node.identifier.value, node.patterns, (body, substs) => this.solveConjunction(body, substs));
57
- return this.handleOutputMode(generator);
58
- }
59
- *solveConjunction(expressions, substs) {
60
- if (expressions.length === 0) {
61
- yield success(substs);
62
- return;
63
- }
64
- const [head, ...tail] = expressions;
65
- let headGen = null;
66
- if (head instanceof Goal) {
67
- const args = head.args.map((arg) => this.instantiateExpressionAsPattern(arg, substs));
68
- headGen = solveGoal(this.env, head.identifier.value, args, (b, s) => this.solveConjunction(b, s));
69
- }
70
- else if (head instanceof Exist) {
71
- const patterns = head.patterns.map((pat) => this.substitutePattern(pat, substs));
72
- headGen = solveGoal(this.env, head.identifier.value, patterns, (b, s) => this.solveConjunction(b, s));
73
- }
74
- if (headGen) {
75
- // backtracking
76
- for (const headResult of headGen) {
77
- const newSubsts = new Map([...substs, ...headResult.substs]);
78
- yield* this.solveConjunction(tail, newSubsts);
79
- }
80
- }
81
- else {
82
- // imperative
83
- const result = this.evaluator.evaluate(head);
84
- if (result)
85
- yield* this.solveConjunction(tail, substs);
86
- }
87
- }
88
- expressionToPattern(expr) {
89
- // 1. Si ya es explícitamente un patrón (ej. sintaxis especial si tuvieras), devolverlo.
90
- if (expr instanceof VariablePattern)
91
- return expr; // 2. Si es una variable, tenemos un dilema: ¿Es valor o incógnita?
92
- if (expr instanceof Variable) {
93
- const name = expr.identifier.value;
94
- // A. ¿Existe definida en el entorno actual?
95
- // Usamos una función auxiliar para no lanzar excepción
96
- if (isDefined(this.env, name)) {
97
- const val = this.evaluator.evaluate(expr);
98
- return this.primitiveToPattern(val); // Es un VALOR (ej: 42)
99
- }
100
- // B. No existe en el entorno -> Es una INCÓGNITA LÓGICA nueva
101
- return new VariablePattern(expr.identifier);
102
- }
103
- // 3. Cualquier otra expresión (números, strings, operaciones 1+1) se evalúa
104
- const val = this.evaluator.evaluate(expr);
105
- return this.primitiveToPattern(val);
106
- }
107
- primitiveToPattern(val) {
108
- if (typeof val === "number" ||
109
- typeof val === "string" ||
110
- typeof val === "boolean")
111
- return new LiteralPattern(new SymbolPrimitive(String(val)));
112
- // TODO: Manejar arrays/listas complejas si es necesario
113
- throw new InterpreterError("primitiveToPattern", `Cannot convert value ${val} to Logic Pattern`);
114
- }
115
- instantiateExpressionAsPattern(expr, substs) {
116
- const patternBase = this.expressionToPattern(expr);
117
- return this.substitutePattern(patternBase, substs);
118
- }
119
- substitutePattern(pat, substs, visited = new Set()) {
120
- if (pat instanceof VariablePattern) {
121
- const name = pat.name.value;
122
- if (visited.has(name))
123
- return pat;
124
- const val = substs.get(name);
125
- if (val) {
126
- const newVisited = new Set(visited);
127
- newVisited.add(name);
128
- return this.substitutePattern(val, substs, newVisited);
129
- }
130
- }
131
- return pat;
132
- }
133
- instantiateTemplate(template, substs) {
134
- const pat = this.instantiateExpressionAsPattern(template, substs);
135
- if (pat instanceof LiteralPattern)
136
- return this.evaluator.evaluate(pat.name);
137
- throw new Error("Complex template instantiation not fully implemented");
138
- }
139
- handleOutputMode(gen) {
140
- const mode = this.config.outputMode || "first";
141
- switch (mode) {
142
- case "stream": {
143
- const self = this;
144
- return createStream(function* () {
145
- for (const res of gen)
146
- yield self.formatLogicResult(res.substs);
147
- });
148
- }
149
- case "all": {
150
- const res = [];
151
- for (const r of gen)
152
- res.push(this.formatLogicResult(r.substs));
153
- return res;
154
- }
155
- case "first": {
156
- const next = gen.next();
157
- if (next.done)
158
- return false;
159
- return this.formatLogicResult(next.value.substs);
160
- }
161
- default:
162
- throw new InterpreterError("handleOutputMode", `Unsupported mode: ${mode}. Supported modes: all | stream | first`);
163
- }
164
- }
165
- formatLogicResult(substs) {
166
- const solutions = new Map();
167
- const resolver = new PatternResolver();
168
- substs.forEach((pattern, key) => {
169
- solutions.set(key, pattern.accept(resolver));
170
- });
171
- return { success: true, solutions };
172
- }
173
- }
@@ -1,10 +0,0 @@
1
- import { Expression, Pattern, EnvStack } from "yukigo-ast";
2
- export type Substitution = Map<string, Pattern>;
3
- export type InternalLogicResult = {
4
- success: true;
5
- substs: Substitution;
6
- };
7
- export type BodySolver = (expressions: Expression[], env: Substitution) => Generator<InternalLogicResult>;
8
- export declare function success(substs: Substitution): InternalLogicResult;
9
- export declare function solveGoal(envs: EnvStack, predicateName: string, args: Pattern[], solveBody: BodySolver): Generator<InternalLogicResult>;
10
- export declare function unify(t1: Pattern, t2: Pattern, argEnv?: Substitution): Substitution | null;
@@ -1,97 +0,0 @@
1
- import { Fact, FunctorPattern, ListPattern, LiteralPattern, Rule, VariablePattern, WildcardPattern, isRuntimePredicate, UnguardedBody, } from "yukigo-ast";
2
- import { lookup } from "../utils.js";
3
- import { InterpreterError } from "../errors.js";
4
- export function success(substs) {
5
- return { success: true, substs };
6
- }
7
- function unifyParameters(patterns, args) {
8
- const subst = new Map();
9
- for (let i = 0; i < patterns.length; i++) {
10
- const match = unify(patterns[i], args[i], subst);
11
- if (!match)
12
- return [, false];
13
- match.forEach((v, k) => subst.set(k, v));
14
- }
15
- return [subst, true];
16
- }
17
- export function* solveGoal(envs, predicateName, args, solveBody) {
18
- const pred = lookup(envs, predicateName);
19
- if (!pred || !isRuntimePredicate(pred))
20
- return;
21
- for (const clause of pred.equations) {
22
- const arity = clause instanceof Fact
23
- ? clause.patterns.length
24
- : clause.equations[0].patterns.length;
25
- if (arity !== args.length)
26
- continue;
27
- if (clause instanceof Fact) {
28
- const [substs, matches] = unifyParameters(clause.patterns, args);
29
- if (!matches)
30
- continue;
31
- yield success(substs);
32
- continue;
33
- }
34
- if (clause instanceof Rule) {
35
- for (const eq of clause.equations) {
36
- const [substs, matches] = unifyParameters(eq.patterns, args);
37
- if (!matches)
38
- continue;
39
- if (eq.body instanceof UnguardedBody) {
40
- const bodyGenerator = solveBody(eq.body.sequence.statements, substs);
41
- for (const finalResult of bodyGenerator)
42
- yield success(finalResult.substs);
43
- }
44
- }
45
- continue;
46
- }
47
- throw new InterpreterError("*solveGoal", `Unexpected node: ${JSON.stringify(clause)}`);
48
- }
49
- }
50
- function resolve(node, env) {
51
- if (node instanceof VariablePattern) {
52
- const name = node.name.toString();
53
- if (env.has(name))
54
- return resolve(env.get(name), env);
55
- }
56
- return node;
57
- }
58
- export function unify(t1, t2, argEnv) {
59
- const env = argEnv ?? new Map();
60
- const r1 = resolve(t1, env);
61
- const r2 = resolve(t2, env);
62
- if (r1 === r2)
63
- return env;
64
- if (r1 instanceof WildcardPattern || r2 instanceof WildcardPattern)
65
- return env;
66
- if (r1 instanceof VariablePattern) {
67
- env.set(r1.name.value, r2);
68
- return env;
69
- }
70
- if (r2 instanceof VariablePattern) {
71
- env.set(r2.name.value, r1);
72
- return env;
73
- }
74
- if (r1 instanceof LiteralPattern && r2 instanceof LiteralPattern)
75
- return r1.name.equals(r2.name) ? env : null;
76
- if (r1 instanceof FunctorPattern && r2 instanceof FunctorPattern) {
77
- if (r1.identifier.value !== r2.identifier.value)
78
- return null;
79
- if (r1.args.length !== r2.args.length)
80
- return null;
81
- for (let i = 0; i < r1.args.length; i++) {
82
- if (!unify(r1.args[i], r2.args[i], env))
83
- return null;
84
- }
85
- return env;
86
- }
87
- if (r1 instanceof ListPattern && r2 instanceof ListPattern) {
88
- if (r1.elements.length !== r2.elements.length)
89
- return null;
90
- for (let i = 0; i < r1.elements.length; i++) {
91
- if (!unify(r1.elements[i], r2.elements[i], env))
92
- return null;
93
- }
94
- return env;
95
- }
96
- return null;
97
- }
@@ -1,33 +0,0 @@
1
- import { PrimitiveValue, RuntimeFunction, RuntimeObject, EnvStack } from "yukigo-ast";
2
- import { ExpressionEvaluator } from "../utils.js";
3
- import { InterpreterConfig } from "../index.js";
4
- export declare class ObjectRuntime {
5
- /**
6
- * Creates a new instance of an Object.
7
- * Typically called by visitNew()
8
- */
9
- static instantiate(className: string, identifier: string, fieldDefinitions: Map<string, PrimitiveValue>, methodDefinitions: Map<string, RuntimeFunction>): RuntimeObject;
10
- /**
11
- * Handles Method Calls (Message Passing).
12
- * Reuses FunctionRuntime to execute the method body.
13
- */
14
- static dispatch(receiver: PrimitiveValue, methodName: string, args: PrimitiveValue[], env: EnvStack, evaluatorFactory: (env: EnvStack) => ExpressionEvaluator): PrimitiveValue;
15
- /**
16
- * Handles calls to super() or super.method()
17
- */
18
- static dispatchSuper(currentEnv: EnvStack, methodName: string, args: PrimitiveValue[], evaluatorFactory: (env: EnvStack) => ExpressionEvaluator): PrimitiveValue;
19
- private static createDispatchScope;
20
- private static getResolutionChain;
21
- private static expandClassHierarchy;
22
- private static findMethodInChain;
23
- /**
24
- * Field Access (Get)
25
- * e.g. self.myField
26
- */
27
- static getField(receiver: PrimitiveValue, fieldName: string): PrimitiveValue;
28
- /**
29
- * Field Mutation (Set)
30
- * e.g. self.myField = 10
31
- */
32
- static setField(receiver: PrimitiveValue, fieldName: string, value: PrimitiveValue, config: InterpreterConfig): PrimitiveValue;
33
- }
@@ -1,123 +0,0 @@
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";
5
- export class ObjectRuntime {
6
- /**
7
- * Creates a new instance of an Object.
8
- * Typically called by visitNew()
9
- */
10
- static instantiate(className, identifier, fieldDefinitions, methodDefinitions) {
11
- return {
12
- type: "Object",
13
- className,
14
- identifier,
15
- fields: new Map(fieldDefinitions),
16
- methods: methodDefinitions,
17
- };
18
- }
19
- /**
20
- * Handles Method Calls (Message Passing).
21
- * Reuses FunctionRuntime to execute the method body.
22
- */
23
- static dispatch(receiver, methodName, args, env, evaluatorFactory) {
24
- if (!isRuntimeObject(receiver))
25
- throw new Error(`${receiver} is not an object`);
26
- const chain = this.getResolutionChain(receiver, env);
27
- const match = this.findMethodInChain(chain, methodName);
28
- if (!match)
29
- throw new InterpreterError("MethodDispatch", `${receiver.className} does not understand '${methodName}'.`);
30
- const objectScope = this.createDispatchScope(receiver, match, methodName);
31
- return FunctionRuntime.apply(methodName, match.method.equations, args, pushEnv(env, objectScope), evaluatorFactory);
32
- }
33
- /**
34
- * Handles calls to super() or super.method()
35
- */
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__");
40
- const targetMethodName = methodName || currentMethodName;
41
- if (!self || !currentHolder)
42
- throw new InterpreterError("SuperError", "'super' used outside of a method context");
43
- const chain = this.getResolutionChain(self, currentEnv);
44
- const currentIndex = chain.findIndex((c) => c === currentHolder);
45
- if (currentIndex === -1)
46
- throw new Error("Fatal: Execution context not found in hierarchy chain");
47
- const remainingChain = chain.slice(currentIndex + 1);
48
- const match = this.findMethodInChain(remainingChain, methodName);
49
- if (!match)
50
- throw new InterpreterError("Super", `Super method '${methodName}' not found`);
51
- const objectScope = this.createDispatchScope(self, match, targetMethodName);
52
- return FunctionRuntime.apply(methodName, match.method.equations, args, pushEnv(currentEnv, objectScope), evaluatorFactory);
53
- }
54
- static createDispatchScope(self, match, targetName) {
55
- const objectScope = new Map();
56
- objectScope.set("self", self);
57
- objectScope.set("__CONTEXT_CLASS__", match.holder);
58
- objectScope.set("__METHOD_NAME__", targetName);
59
- for (const [key, val] of self.fields)
60
- objectScope.set(key, val);
61
- return objectScope;
62
- }
63
- static getResolutionChain(receiver, env) {
64
- const chain = [];
65
- chain.push(receiver);
66
- if (receiver.className) {
67
- this.expandClassHierarchy(receiver.className, env, chain);
68
- }
69
- return chain;
70
- }
71
- static expandClassHierarchy(className, env, chain) {
72
- const classDef = lookup(env, className);
73
- if (!isRuntimeClass(classDef))
74
- throw new InterpreterError("expandClassHierarchy", "classDef was expected to be a RuntimeClass");
75
- chain.push(classDef);
76
- const classDefCopy = [...classDef.mixins];
77
- if (classDef.mixins)
78
- classDefCopy.reverse().forEach((mixinName) => {
79
- this.expandClassHierarchy(mixinName, env, chain);
80
- });
81
- if (classDef.superclass)
82
- this.expandClassHierarchy(classDef.superclass, env, chain);
83
- }
84
- static findMethodInChain(chain, methodName) {
85
- for (const link of chain) {
86
- if (link.methods.has(methodName))
87
- return {
88
- method: link.methods.get(methodName),
89
- holder: link,
90
- };
91
- }
92
- return undefined;
93
- }
94
- /**
95
- * Field Access (Get)
96
- * e.g. self.myField
97
- */
98
- static getField(receiver, fieldName) {
99
- if (!isRuntimeObject(receiver))
100
- throw new InterpreterError("FieldAccess", "Target is not an object");
101
- if (!receiver.fields.has(fieldName)) {
102
- if (receiver.methods.has(fieldName))
103
- return receiver.methods.get(fieldName);
104
- throw new InterpreterError("FieldAccess", `Field '${fieldName}' not found in ${receiver.className}`);
105
- }
106
- return receiver.fields.get(fieldName);
107
- }
108
- /**
109
- * Field Mutation (Set)
110
- * e.g. self.myField = 10
111
- */
112
- static setField(receiver, fieldName, value, config) {
113
- if (!config.mutability) {
114
- throw new InterpreterError("FieldAssignment", `Cannot mutate field '${fieldName}': mutability is disabled`);
115
- }
116
- if (!isRuntimeObject(receiver))
117
- throw new InterpreterError("FieldAssignment", "Target is not an object");
118
- if (!receiver.fields.has(fieldName))
119
- throw new InterpreterError("FieldAssignment", `Cannot set unknown field '${fieldName}'`);
120
- receiver.fields.set(fieldName, value);
121
- return value;
122
- }
123
- }