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