yukigo 0.1.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 (78) hide show
  1. package/.mocharc.json +4 -0
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +199 -0
  4. package/dist/analyzer/index.d.ts +71 -0
  5. package/dist/analyzer/index.js +110 -0
  6. package/dist/analyzer/inspections/functional.d.ts +46 -0
  7. package/dist/analyzer/inspections/functional.js +123 -0
  8. package/dist/analyzer/inspections/generic.d.ts +151 -0
  9. package/dist/analyzer/inspections/generic.js +427 -0
  10. package/dist/analyzer/inspections/imperative.d.ts +37 -0
  11. package/dist/analyzer/inspections/imperative.js +105 -0
  12. package/dist/analyzer/inspections/logic.d.ts +49 -0
  13. package/dist/analyzer/inspections/logic.js +140 -0
  14. package/dist/analyzer/inspections/object.d.ts +83 -0
  15. package/dist/analyzer/inspections/object.js +235 -0
  16. package/dist/analyzer/utils.d.ts +4 -0
  17. package/dist/analyzer/utils.js +16 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +3 -0
  20. package/dist/interpreter/components/EnvBuilder.d.ts +16 -0
  21. package/dist/interpreter/components/EnvBuilder.js +78 -0
  22. package/dist/interpreter/components/FunctionRuntime.d.ts +8 -0
  23. package/dist/interpreter/components/FunctionRuntime.js +52 -0
  24. package/dist/interpreter/components/LazyRuntime.d.ts +7 -0
  25. package/dist/interpreter/components/LazyRuntime.js +75 -0
  26. package/dist/interpreter/components/LogicEngine.d.ts +21 -0
  27. package/dist/interpreter/components/LogicEngine.js +152 -0
  28. package/dist/interpreter/components/LogicResolver.d.ts +11 -0
  29. package/dist/interpreter/components/LogicResolver.js +87 -0
  30. package/dist/interpreter/components/Operations.d.ts +14 -0
  31. package/dist/interpreter/components/Operations.js +69 -0
  32. package/dist/interpreter/components/PatternMatcher.d.ts +41 -0
  33. package/dist/interpreter/components/PatternMatcher.js +206 -0
  34. package/dist/interpreter/components/Visitor.d.ts +64 -0
  35. package/dist/interpreter/components/Visitor.js +299 -0
  36. package/dist/interpreter/errors.d.ts +19 -0
  37. package/dist/interpreter/errors.js +36 -0
  38. package/dist/interpreter/index.d.ts +32 -0
  39. package/dist/interpreter/index.js +44 -0
  40. package/dist/interpreter/utils.d.ts +14 -0
  41. package/dist/interpreter/utils.js +57 -0
  42. package/dist/utils/helpers.d.ts +14 -0
  43. package/dist/utils/helpers.js +51 -0
  44. package/package.json +30 -0
  45. package/src/analyzer/index.ts +132 -0
  46. package/src/analyzer/inspections/functional.ts +159 -0
  47. package/src/analyzer/inspections/generic.ts +499 -0
  48. package/src/analyzer/inspections/imperative.ts +129 -0
  49. package/src/analyzer/inspections/logic.ts +166 -0
  50. package/src/analyzer/inspections/object.ts +282 -0
  51. package/src/analyzer/utils.ts +26 -0
  52. package/src/index.ts +3 -0
  53. package/src/interpreter/components/EnvBuilder.ts +97 -0
  54. package/src/interpreter/components/FunctionRuntime.ts +79 -0
  55. package/src/interpreter/components/LazyRuntime.ts +97 -0
  56. package/src/interpreter/components/LogicEngine.ts +227 -0
  57. package/src/interpreter/components/LogicResolver.ts +130 -0
  58. package/src/interpreter/components/Operations.ts +81 -0
  59. package/src/interpreter/components/PatternMatcher.ts +254 -0
  60. package/src/interpreter/components/Visitor.ts +493 -0
  61. package/src/interpreter/errors.ts +47 -0
  62. package/src/interpreter/index.ts +59 -0
  63. package/src/interpreter/utils.ts +79 -0
  64. package/src/utils/helpers.ts +73 -0
  65. package/tests/analyzer/functional.spec.ts +221 -0
  66. package/tests/analyzer/generic.spec.ts +100 -0
  67. package/tests/analyzer/helpers.spec.ts +83 -0
  68. package/tests/analyzer/logic.spec.ts +292 -0
  69. package/tests/analyzer/oop.spec.ts +338 -0
  70. package/tests/interpreter/EnvBuilder.spec.ts +178 -0
  71. package/tests/interpreter/FunctionRuntime.spec.ts +234 -0
  72. package/tests/interpreter/LazyRuntime.spec.ts +190 -0
  73. package/tests/interpreter/LogicEngine.spec.ts +194 -0
  74. package/tests/interpreter/Operations.spec.ts +220 -0
  75. package/tests/interpreter/PatternSystem.spec.ts +189 -0
  76. package/tests/interpreter/interpreter.spec.ts +937 -0
  77. package/tsconfig.build.json +7 -0
  78. package/tsconfig.json +17 -0
@@ -0,0 +1,206 @@
1
+ import { ConstructorPattern, isLazyList, ListPrimitive, } from "@yukigo/ast";
2
+ import { InterpreterVisitor } from "./Visitor.js";
3
+ import { createStream } from "../utils.js";
4
+ export class PatternResolver {
5
+ visitVariablePattern(node) {
6
+ return node.name.value;
7
+ }
8
+ visitWildcardPattern(node) {
9
+ return "_";
10
+ }
11
+ visitLiteralPattern(node) {
12
+ const { name } = node;
13
+ if (name instanceof ListPrimitive)
14
+ return String(name.elements.map((elem) => elem.accept(this)));
15
+ return String(name.value);
16
+ }
17
+ visitTuplePattern(node) {
18
+ return String(node.elements.map((elem) => elem.accept(this)));
19
+ }
20
+ visitListPattern(node) {
21
+ const { elements } = node;
22
+ return elements.length === 0
23
+ ? "[]"
24
+ : String(elements.map((elem) => elem.accept(this)));
25
+ }
26
+ visitConsPattern(node) {
27
+ const head = node.head.accept(this);
28
+ const tail = node.tail.accept(this);
29
+ return `(${head}:${tail})`;
30
+ }
31
+ visitConstructorPattern(node) {
32
+ const constr = node.constr;
33
+ const args = node.patterns.map((pat) => pat.accept(this)).join(" ");
34
+ return `${constr} ${args}`;
35
+ }
36
+ visitFunctorPattern(node) {
37
+ // Same as ConstructorPattern (alias)
38
+ return this.visitConstructorPattern(new ConstructorPattern(node.identifier.value, node.args));
39
+ }
40
+ visitApplicationPattern(node) {
41
+ // Same as FunctorPattern
42
+ return this.visitConstructorPattern(new ConstructorPattern(node.symbol.value, node.args));
43
+ }
44
+ visitAsPattern(node) {
45
+ const pattern = node.pattern.accept(this);
46
+ const alias = node.alias.accept(this);
47
+ return `${alias}@${pattern}`;
48
+ }
49
+ visit(node) {
50
+ return node.accept(this);
51
+ }
52
+ }
53
+ /**
54
+ * Recursively matches a value against a pattern node.
55
+ * Updates `bindings` when variables are bound successfully.
56
+ * Returns true if the pattern matches, false otherwise.
57
+ */
58
+ export class PatternMatcher {
59
+ value;
60
+ bindings;
61
+ constructor(value, bindings) {
62
+ this.value = value;
63
+ this.bindings = bindings;
64
+ }
65
+ visitVariablePattern(node) {
66
+ this.bindings.push([node.name.value, this.value]);
67
+ return true;
68
+ }
69
+ visitWildcardPattern(node) {
70
+ return true;
71
+ }
72
+ visitLiteralPattern(node) {
73
+ const literalValue = InterpreterVisitor.evaluateLiteral(node.name);
74
+ return this.deepEqual(this.value, literalValue);
75
+ }
76
+ visitTuplePattern(node) {
77
+ if (!Array.isArray(this.value))
78
+ return false;
79
+ if (this.value.length !== node.elements.length)
80
+ return false;
81
+ for (let i = 0; i < node.elements.length; i++) {
82
+ const matcher = new PatternMatcher(this.value[i], this.bindings);
83
+ if (!node.elements[i].accept(matcher))
84
+ return false;
85
+ }
86
+ return true;
87
+ }
88
+ visitListPattern(node) {
89
+ // empty list case
90
+ if (node.elements.length === 0) {
91
+ if (Array.isArray(this.value))
92
+ return this.value.length === 0;
93
+ if (isLazyList(this.value)) {
94
+ const iter = this.value.generator();
95
+ return iter.next().done;
96
+ }
97
+ return false;
98
+ }
99
+ // finite list case
100
+ if (Array.isArray(this.value))
101
+ return this.matchList(node.elements, this.value);
102
+ // lazy list case
103
+ if (isLazyList(this.value)) {
104
+ const realized = this.realize(this.value);
105
+ return this.matchList(node.elements, realized);
106
+ }
107
+ return false;
108
+ }
109
+ matchList(elements, value) {
110
+ if (value.length !== elements.length)
111
+ return false;
112
+ for (let i = 0; i < elements.length; i++) {
113
+ const matcher = new PatternMatcher(value[i], this.bindings);
114
+ const isMatch = elements[i].accept(matcher);
115
+ if (!isMatch)
116
+ return false;
117
+ }
118
+ return true;
119
+ }
120
+ visitConsPattern(node) {
121
+ const [head, tail] = this.resolveCons(this.value);
122
+ if (!head || !tail)
123
+ return false;
124
+ const headMatcher = new PatternMatcher(head, this.bindings);
125
+ const headMatches = node.head.accept(headMatcher);
126
+ if (!headMatches)
127
+ return false;
128
+ const tailMatcher = new PatternMatcher(tail, this.bindings);
129
+ return node.tail.accept(tailMatcher);
130
+ }
131
+ resolveCons(list) {
132
+ if (Array.isArray(list))
133
+ return list.length === 0 ? [null, null] : [list[0], list.slice(1)];
134
+ if (isLazyList(list)) {
135
+ const headIter = list.generator();
136
+ const next = headIter.next();
137
+ if (next.done)
138
+ return [null, null];
139
+ const parentGeneratorFactory = list.generator;
140
+ const tailGenerator = function* () {
141
+ const freshIter = parentGeneratorFactory();
142
+ freshIter.next();
143
+ let next;
144
+ while (!(next = freshIter.next()).done)
145
+ yield next.value;
146
+ };
147
+ return [next.value, createStream(tailGenerator)];
148
+ }
149
+ return [null, null];
150
+ }
151
+ visitConstructorPattern(node) {
152
+ if (!Array.isArray(this.value) || this.value.length === 0)
153
+ return false;
154
+ if (this.value[0] !== node.constr)
155
+ return false;
156
+ const args = this.value.slice(1);
157
+ return this.matchList(node.patterns, args);
158
+ }
159
+ visitFunctorPattern(node) {
160
+ return this.visitConstructorPattern(new ConstructorPattern(node.identifier.value, node.args));
161
+ }
162
+ visitApplicationPattern(node) {
163
+ return this.visitConstructorPattern(new ConstructorPattern(node.symbol.value, node.args));
164
+ }
165
+ visitAsPattern(node) {
166
+ const innerMatcher = new PatternMatcher(this.value, this.bindings);
167
+ const innerMatches = node.pattern.accept(innerMatcher);
168
+ if (!innerMatches)
169
+ return false;
170
+ const aliasMatcher = new PatternMatcher(this.value, this.bindings);
171
+ return node.alias.accept(aliasMatcher);
172
+ }
173
+ visitUnionPattern(node) {
174
+ for (const pattern of node.patterns) {
175
+ const trialBindings = [];
176
+ const matcher = new PatternMatcher(this.value, trialBindings);
177
+ if (pattern.accept(matcher)) {
178
+ this.bindings.push(...trialBindings);
179
+ return true;
180
+ }
181
+ }
182
+ return false;
183
+ }
184
+ visit(node) {
185
+ return node.accept(this);
186
+ }
187
+ deepEqual(a, b) {
188
+ if (a === b)
189
+ return true;
190
+ if (typeof a !== typeof b)
191
+ return false;
192
+ if (Array.isArray(a) && Array.isArray(b)) {
193
+ if (a.length !== b.length)
194
+ return false;
195
+ for (let i = 0; i < a.length; i++) {
196
+ if (!this.deepEqual(a[i], b[i]))
197
+ return false;
198
+ }
199
+ return true;
200
+ }
201
+ return false;
202
+ }
203
+ realize(value) {
204
+ return new InterpreterVisitor([new Map()], {}).realizeList(value);
205
+ }
206
+ }
@@ -0,0 +1,64 @@
1
+ import { Visitor, PrimitiveValue, NumberPrimitive, BooleanPrimitive, StringPrimitive, ListPrimitive, NilPrimitive, SymbolPrimitive, Variable, CharPrimitive, ArithmeticUnaryOperation, ArithmeticBinaryOperation, ListUnaryOperation, ListBinaryOperation, ComparisonOperation, LogicalBinaryOperation, LogicalUnaryOperation, BitwiseBinaryOperation, BitwiseUnaryOperation, StringOperation, UnifyOperation, AssignOperation, TupleExpression, FieldExpression, DataExpression, ConsExpression, LetInExpression, Call, Otherwise, CompositionExpression, Expression, Application, Lambda, Exist, Not, Findall, Forall, Goal, Send, New, Implement, Include, Self, ListComprehension, RangeExpression, Generator as YuGenerator, ASTNode, Raise, Query } from "@yukigo/ast";
2
+ import { EnvStack, InterpreterConfig } from "../index.js";
3
+ import { ExpressionEvaluator } from "../utils.js";
4
+ import { ErrorFrame } from "../errors.js";
5
+ export declare class InterpreterVisitor implements Visitor<PrimitiveValue>, ExpressionEvaluator {
6
+ private frames;
7
+ private env;
8
+ private readonly config;
9
+ constructor(env: EnvStack, config: InterpreterConfig, frames?: ErrorFrame[]);
10
+ evaluate(node: Expression): PrimitiveValue;
11
+ private getLogicEngine;
12
+ visitNumberPrimitive(node: NumberPrimitive): PrimitiveValue;
13
+ visitBooleanPrimitive(node: BooleanPrimitive): PrimitiveValue;
14
+ visitStringPrimitive(node: StringPrimitive): PrimitiveValue;
15
+ visitListPrimitive(node: ListPrimitive): PrimitiveValue;
16
+ visitNilPrimitive(node: NilPrimitive): PrimitiveValue;
17
+ visitCharPrimitive(node: CharPrimitive): PrimitiveValue;
18
+ visitSymbolPrimitive(node: SymbolPrimitive): PrimitiveValue;
19
+ visitVariable(node: Variable): PrimitiveValue;
20
+ visitArithmeticUnaryOperation(node: ArithmeticUnaryOperation): PrimitiveValue;
21
+ visitArithmeticBinaryOperation(node: ArithmeticBinaryOperation): PrimitiveValue;
22
+ visitListUnaryOperation(node: ListUnaryOperation): PrimitiveValue;
23
+ visitListBinaryOperation(node: ListBinaryOperation): PrimitiveValue;
24
+ visitComparisonOperation(node: ComparisonOperation): PrimitiveValue;
25
+ visitLogicalBinaryOperation(node: LogicalBinaryOperation): PrimitiveValue;
26
+ visitLogicalUnaryOperation(node: LogicalUnaryOperation): PrimitiveValue;
27
+ visitBitwiseBinaryOperation(node: BitwiseBinaryOperation): PrimitiveValue;
28
+ visitBitwiseUnaryOperation(node: BitwiseUnaryOperation): PrimitiveValue;
29
+ visitStringOperation(node: StringOperation): PrimitiveValue;
30
+ visitUnifyOperation(node: UnifyOperation): PrimitiveValue;
31
+ visitAssignOperation(node: AssignOperation): PrimitiveValue;
32
+ visitTupleExpr(node: TupleExpression): PrimitiveValue;
33
+ visitFieldExpr(node: FieldExpression): PrimitiveValue;
34
+ visitDataExpr(node: DataExpression): PrimitiveValue;
35
+ visitConsExpr(node: ConsExpression): PrimitiveValue;
36
+ visitLetInExpr(node: LetInExpression): PrimitiveValue;
37
+ visitCall(node: Call): PrimitiveValue;
38
+ visitOtherwise(node: Otherwise): PrimitiveValue;
39
+ visitCompositionExpression(node: CompositionExpression): PrimitiveValue;
40
+ visitLambda(node: Lambda): PrimitiveValue;
41
+ visitApplication(node: Application): PrimitiveValue;
42
+ visitQuery(node: Query): PrimitiveValue;
43
+ visitExist(node: Exist): PrimitiveValue;
44
+ visitNot(node: Not): PrimitiveValue;
45
+ visitFindall(node: Findall): PrimitiveValue;
46
+ visitForall(node: Forall): PrimitiveValue;
47
+ visitGoal(node: Goal): PrimitiveValue;
48
+ visitSend(node: Send): PrimitiveValue;
49
+ visitNew(node: New): PrimitiveValue;
50
+ visitImplement(node: Implement): PrimitiveValue;
51
+ visitInclude(node: Include): PrimitiveValue;
52
+ visitSelf(node: Self): PrimitiveValue;
53
+ visitListComprehension(node: ListComprehension): PrimitiveValue;
54
+ visitGenerator(node: YuGenerator): PrimitiveValue;
55
+ visitRaise(node: Raise): PrimitiveValue;
56
+ visitRangeExpression(node: RangeExpression): PrimitiveValue;
57
+ visit(node: Expression): PrimitiveValue;
58
+ private safelyVisit;
59
+ private isRuntimeFunction;
60
+ realizeList(val: PrimitiveValue): PrimitiveValue[];
61
+ private processBinary;
62
+ private processUnary;
63
+ static evaluateLiteral(node: ASTNode): PrimitiveValue;
64
+ }
@@ -0,0 +1,299 @@
1
+ import { SymbolPrimitive, VariablePattern, Application, Lambda, UnguardedBody, Sequence, Return, } from "@yukigo/ast";
2
+ import { ArithmeticBinaryTable, ArithmeticUnaryTable, BitwiseBinaryTable, BitwiseUnaryTable, ComparisonOperationTable, ListBinaryTable, ListUnaryTable, LogicalBinaryTable, LogicalUnaryTable, StringOperationTable, } from "./Operations.js";
3
+ import { define, lookup } from "../utils.js";
4
+ import { LogicEngine } from "./LogicEngine.js";
5
+ import { InterpreterError, UnexpectedValue } from "../errors.js";
6
+ import { LazyRuntime } from "./LazyRuntime.js";
7
+ import { FunctionRuntime } from "./FunctionRuntime.js";
8
+ export class InterpreterVisitor {
9
+ frames;
10
+ env;
11
+ config;
12
+ constructor(env, config, frames = []) {
13
+ this.frames = frames;
14
+ this.env = env;
15
+ this.config = config;
16
+ }
17
+ evaluate(node) {
18
+ return node.accept(this);
19
+ }
20
+ getLogicEngine() {
21
+ return new LogicEngine(this.env, this.config, this);
22
+ }
23
+ visitNumberPrimitive(node) {
24
+ return node.value;
25
+ }
26
+ visitBooleanPrimitive(node) {
27
+ return node.value;
28
+ }
29
+ visitStringPrimitive(node) {
30
+ return node.value;
31
+ }
32
+ visitListPrimitive(node) {
33
+ return node.elements.map((elem) => elem.accept(this));
34
+ }
35
+ visitNilPrimitive(node) {
36
+ return node.value;
37
+ }
38
+ visitCharPrimitive(node) {
39
+ return node.value;
40
+ }
41
+ visitSymbolPrimitive(node) {
42
+ try {
43
+ return lookup(this.env, node.value);
44
+ }
45
+ catch (error) {
46
+ throw new InterpreterError("Symbol Lookup", error.message, this.frames);
47
+ }
48
+ }
49
+ visitVariable(node) {
50
+ const name = node.identifier.value;
51
+ const value = node.expression.accept(this);
52
+ this.env.at(-1).set(name, value);
53
+ return true;
54
+ }
55
+ visitArithmeticUnaryOperation(node) {
56
+ return this.processUnary(node, ArithmeticUnaryTable, (a) => !Number.isNaN(a), "ArithmeticUnaryOperation");
57
+ }
58
+ visitArithmeticBinaryOperation(node) {
59
+ return this.processBinary(node, ArithmeticBinaryTable, (a, b) => typeof a === "number" && typeof b === "number", "ArithmeticBinaryOperation");
60
+ }
61
+ visitListUnaryOperation(node) {
62
+ const operand = node.operand.accept(this);
63
+ if (!Array.isArray(operand))
64
+ throw new UnexpectedValue("ListUnaryOperation", "Array", typeof operand);
65
+ const arr = this.realizeList(operand);
66
+ const fn = ListUnaryTable[node.operator];
67
+ if (!fn)
68
+ throw new InterpreterError("ListUnaryOperation", `Unknown operator: ${node.operator}`);
69
+ return fn(arr);
70
+ }
71
+ visitListBinaryOperation(node) {
72
+ return this.processBinary(node, ListBinaryTable, (a, b) => Array.isArray(a) && Array.isArray(b), "ListBinaryOperation");
73
+ }
74
+ visitComparisonOperation(node) {
75
+ return this.processBinary(node, ComparisonOperationTable, () => true, "ComparisonOperation");
76
+ }
77
+ visitLogicalBinaryOperation(node) {
78
+ const left = node.left.accept(this);
79
+ if (typeof left !== "boolean")
80
+ throw new InterpreterError("LogicalBinaryOperation", `Expected left side to be boolean and got: ${left}`);
81
+ const fn = LogicalBinaryTable[node.operator];
82
+ if (!fn)
83
+ throw new InterpreterError("LogicalBinaryOperation", `Unknown operator '${node.operator}'`);
84
+ const rightThunk = () => {
85
+ const right = node.right.accept(this);
86
+ if (typeof right !== "boolean")
87
+ throw new InterpreterError("LogicalBinaryOperation", `Expected right side to be boolean and got: ${right}`);
88
+ return right;
89
+ };
90
+ // short circuit if lazy loading is enabled
91
+ if (this.config.lazyLoading) {
92
+ if (node.operator === "And" && left === false)
93
+ return false;
94
+ if (node.operator === "Or" && left === true)
95
+ return true;
96
+ }
97
+ return fn(left, rightThunk);
98
+ }
99
+ visitLogicalUnaryOperation(node) {
100
+ return this.processUnary(node, LogicalUnaryTable, (a) => typeof a === "boolean", "LogicalUnaryOperation");
101
+ }
102
+ visitBitwiseBinaryOperation(node) {
103
+ return this.processBinary(node, BitwiseBinaryTable, (a, b) => !Number.isNaN(a) && !Number.isNaN(b), "BitwiseBinaryOperation");
104
+ }
105
+ visitBitwiseUnaryOperation(node) {
106
+ return this.processUnary(node, BitwiseUnaryTable, (a) => !Number.isNaN(a), "BitwiseUnaryOperation");
107
+ }
108
+ visitStringOperation(node) {
109
+ return this.processBinary(node, StringOperationTable, (a, b) => typeof a === "string" || typeof b === "string", "StringOperation");
110
+ }
111
+ visitUnifyOperation(node) {
112
+ throw new Error("Method not implemented.");
113
+ }
114
+ visitAssignOperation(node) {
115
+ throw new Error("Method not implemented.");
116
+ }
117
+ visitTupleExpr(node) {
118
+ throw new Error("Method not implemented.");
119
+ }
120
+ visitFieldExpr(node) {
121
+ throw new Error("Method not implemented.");
122
+ }
123
+ visitDataExpr(node) {
124
+ throw new Error("Method not implemented.");
125
+ }
126
+ visitConsExpr(node) {
127
+ try {
128
+ return LazyRuntime.evaluateCons(node, this, this.config.lazyLoading);
129
+ }
130
+ catch (e) {
131
+ throw new InterpreterError("Cons", e.message, this.frames);
132
+ }
133
+ }
134
+ visitLetInExpr(node) {
135
+ throw new Error("Method not implemented.");
136
+ }
137
+ visitCall(node) {
138
+ throw new Error("Method not implemented.");
139
+ }
140
+ visitOtherwise(node) {
141
+ return true;
142
+ }
143
+ visitCompositionExpression(node) {
144
+ const f = node.left.accept(this);
145
+ const g = node.right.accept(this);
146
+ if (!this.isRuntimeFunction(f) || !this.isRuntimeFunction(g))
147
+ throw new InterpreterError("CompositionExpression", "Both operands of (.) must be functions");
148
+ const fName = `__comp_f_${Date.now()}_${Math.random()
149
+ .toString(36)
150
+ .substring(2, 5)}`;
151
+ const gName = `__comp_g_${Date.now()}_${Math.random()
152
+ .toString(36)
153
+ .substring(2, 5)}`;
154
+ define(this.env, fName, f);
155
+ define(this.env, gName, g);
156
+ const arity = g.arity;
157
+ const placeholders = Array.from({ length: arity }, (_, i) => new VariablePattern(new SymbolPrimitive(`_p${i}`)));
158
+ const gCall = placeholders.reduce((acc, p) => new Application(acc, new SymbolPrimitive(p.name.value)), new SymbolPrimitive(gName));
159
+ const composedBody = new Application(new SymbolPrimitive(fName), gCall);
160
+ const lambda = new Lambda(placeholders, composedBody);
161
+ return lambda.accept(this);
162
+ }
163
+ visitLambda(node) {
164
+ const patterns = node.parameters;
165
+ const equation = {
166
+ patterns,
167
+ body: new UnguardedBody(new Sequence([new Return(node.body)])),
168
+ };
169
+ return {
170
+ arity: patterns.length,
171
+ equations: [equation],
172
+ pendingArgs: [],
173
+ identifier: "<lambda>",
174
+ closure: Array.from(this.env),
175
+ };
176
+ }
177
+ visitApplication(node) {
178
+ const func = node.functionExpr.accept(this);
179
+ if (!this.isRuntimeFunction(func))
180
+ throw new InterpreterError("Application", "Cannot apply non-function");
181
+ const argThunk = () => node.parameter.accept(this);
182
+ const pending = func.pendingArgs
183
+ ? [...func.pendingArgs, argThunk]
184
+ : [argThunk];
185
+ // partially applied
186
+ if (pending.length < func.arity)
187
+ return {
188
+ ...func,
189
+ pendingArgs: pending,
190
+ };
191
+ // fully applied
192
+ if (pending.length === func.arity) {
193
+ const evaluatedArgs = pending.map((arg) => typeof arg === "function" ? arg() : arg);
194
+ const executionEnv = func.closure ?? this.env;
195
+ return FunctionRuntime.apply(func.identifier ?? "<anonymous>", func.equations, evaluatedArgs, executionEnv, (newEnv) => new InterpreterVisitor(newEnv, this.config, this.frames));
196
+ }
197
+ throw new InterpreterError("Application", "Too many arguments provided");
198
+ }
199
+ visitQuery(node) {
200
+ return this.getLogicEngine().solveQuery(node);
201
+ }
202
+ visitExist(node) {
203
+ return this.getLogicEngine().solveExist(node);
204
+ }
205
+ visitNot(node) {
206
+ throw new Error("Method not implemented.");
207
+ }
208
+ visitFindall(node) {
209
+ return this.getLogicEngine().solveFindall(node);
210
+ }
211
+ visitForall(node) {
212
+ throw new Error("Method not implemented.");
213
+ }
214
+ visitGoal(node) {
215
+ return this.getLogicEngine().solveGoal(node);
216
+ }
217
+ visitSend(node) {
218
+ throw new Error("Method not implemented.");
219
+ }
220
+ visitNew(node) {
221
+ throw new Error("Method not implemented.");
222
+ }
223
+ visitImplement(node) {
224
+ throw new Error("Method not implemented.");
225
+ }
226
+ visitInclude(node) {
227
+ throw new Error("Method not implemented.");
228
+ }
229
+ visitSelf(node) {
230
+ throw new Error("Method not implemented.");
231
+ }
232
+ visitListComprehension(node) {
233
+ throw new Error("Method not implemented.");
234
+ }
235
+ visitGenerator(node) {
236
+ throw new Error("Method not implemented.");
237
+ }
238
+ visitRaise(node) {
239
+ const msg = node.body.accept(this);
240
+ if (typeof msg !== "string")
241
+ throw new UnexpectedValue("Raise", "string", typeof msg);
242
+ throw new InterpreterError("Raise", msg);
243
+ }
244
+ visitRangeExpression(node) {
245
+ try {
246
+ return LazyRuntime.evaluateRange(node, this, this.config);
247
+ }
248
+ catch (e) {
249
+ throw new InterpreterError("Range", e.message, this.frames);
250
+ }
251
+ }
252
+ visit(node) {
253
+ return this.safelyVisit(node, () => node.accept(this));
254
+ }
255
+ safelyVisit(node, fn) {
256
+ try {
257
+ return fn();
258
+ }
259
+ catch (err) {
260
+ if (err instanceof InterpreterError) {
261
+ err.pushFrame({ nodeType: node.constructor.name, loc: node.loc });
262
+ throw err;
263
+ }
264
+ const wrapped = new InterpreterError(node.constructor.name, err.message, [...this.frames, { nodeType: node.constructor.name, loc: node.loc }]);
265
+ throw wrapped;
266
+ }
267
+ }
268
+ isRuntimeFunction(val) {
269
+ return (typeof val === "object" &&
270
+ val !== null &&
271
+ Array.isArray(val.equations) &&
272
+ typeof val.arity === "number");
273
+ }
274
+ realizeList(val) {
275
+ return LazyRuntime.realizeList(val);
276
+ }
277
+ processBinary(node, table, typeGuard, contextName) {
278
+ const left = node.left.accept(this);
279
+ const right = node.right.accept(this);
280
+ if (!typeGuard(left, right))
281
+ throw new InterpreterError(contextName, `Type mismatch: ${left}, ${right}`, this.frames);
282
+ const fn = table[node.operator];
283
+ if (!fn)
284
+ throw new InterpreterError(contextName, `Unknown op: ${node.operator}`);
285
+ return fn(left, right);
286
+ }
287
+ processUnary(node, table, typeGuard, contextName) {
288
+ const operand = node.operand.accept(this);
289
+ if (!typeGuard(operand))
290
+ throw new InterpreterError(contextName, `Type mismatch: ${operand}`, this.frames);
291
+ const fn = table[node.operator];
292
+ if (!fn)
293
+ throw new InterpreterError(contextName, `Unknown op: ${node.operator}`);
294
+ return fn(operand);
295
+ }
296
+ static evaluateLiteral(node) {
297
+ return node.accept(new InterpreterVisitor([new Map()], { lazyLoading: false }));
298
+ }
299
+ }
@@ -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,32 @@
1
+ import { PrimitiveValue, Expression, AST } from "@yukigo/ast";
2
+ export type Bindings = [string, PrimitiveValue][];
3
+ export type LogicSearchMode = "first" | "all" | "stream";
4
+ export type InterpreterConfig = {
5
+ lazyLoading?: boolean;
6
+ debug?: boolean;
7
+ outputMode?: LogicSearchMode;
8
+ };
9
+ export type Environment = Map<string, PrimitiveValue>;
10
+ export type EnvStack = Environment[];
11
+ /**
12
+ * The Interpreter class is responsible for evaluating the Abstract Syntax Tree (AST)
13
+ * generated by the parsers.
14
+ *
15
+ * It manages the global execution environment and delegates the actual evaluation
16
+ * of Expression nodes to a dedicated visitor.
17
+ */
18
+ export declare class Interpreter {
19
+ private globalEnv;
20
+ private readonly config;
21
+ /**
22
+ * @param ast The Abstract Syntax Tree (AST) of the program to be interpreted.
23
+ */
24
+ constructor(ast: AST, config?: InterpreterConfig);
25
+ /**
26
+ * Evaluates a single Expression node within the context of the global environment.
27
+ *
28
+ * @param expr The root Expression node to be evaluated.
29
+ * @returns The resulting primitive value (number, string, boolean, etc.) after evaluation.
30
+ */
31
+ evaluate(expr: Expression): PrimitiveValue;
32
+ }