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.
- package/CHANGELOG.md +26 -0
- package/dist/analyzer/index.d.ts +1 -1
- package/dist/analyzer/index.js +2 -1
- package/dist/interpreter/components/PatternMatcher.d.ts +2 -1
- package/dist/interpreter/components/RuntimeContext.d.ts +2 -1
- package/dist/interpreter/components/RuntimeContext.js +10 -3
- package/dist/interpreter/components/TestRunner.js +1 -42
- package/dist/interpreter/components/Visitor.d.ts +2 -2
- package/dist/interpreter/components/Visitor.js +16 -28
- package/dist/interpreter/components/runtimes/FunctionRuntime.d.ts +2 -1
- package/dist/interpreter/components/runtimes/FunctionRuntime.js +29 -2
- package/dist/interpreter/components/runtimes/LazyRuntime.d.ts +5 -0
- package/dist/interpreter/components/runtimes/LazyRuntime.js +70 -39
- package/dist/interpreter/entities.d.ts +105 -0
- package/dist/interpreter/entities.js +96 -0
- package/package.json +2 -2
- package/src/analyzer/index.ts +3 -2
- package/src/interpreter/components/PatternMatcher.ts +2 -0
- package/src/interpreter/components/RuntimeContext.ts +10 -4
- package/src/interpreter/components/TestRunner.ts +4 -40
- package/src/interpreter/components/Visitor.ts +34 -47
- package/src/interpreter/components/runtimes/FunctionRuntime.ts +42 -3
- package/src/interpreter/components/runtimes/LazyRuntime.ts +82 -49
- package/tests/analyzer/generic.spec.ts +14 -0
- package/tests/interpreter/interpreter.spec.ts +10 -0
- package/dist/interpreter/components/FunctionRuntime.d.ts +0 -8
- package/dist/interpreter/components/FunctionRuntime.js +0 -81
- package/dist/interpreter/components/LazyRuntime.d.ts +0 -7
- package/dist/interpreter/components/LazyRuntime.js +0 -94
- package/dist/interpreter/components/LogicEngine.d.ts +0 -24
- package/dist/interpreter/components/LogicEngine.js +0 -173
- package/dist/interpreter/components/LogicResolver.d.ts +0 -10
- package/dist/interpreter/components/LogicResolver.js +0 -97
- package/dist/interpreter/components/ObjectRuntime.d.ts +0 -33
- package/dist/interpreter/components/ObjectRuntime.js +0 -123
- package/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## 0.2.0 (2026-04-11)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **yukigo:** agrego `clone` a `RuntimeContext` para clonar el entorno ([ee8e0af](https://github.com/miyukiproject/yukigo/commit/ee8e0af))
|
|
6
|
+
|
|
7
|
+
### 🩹 Fixes
|
|
8
|
+
|
|
9
|
+
- **yukigo:** permitir a las inspections tener args undefined ([4f36ebe](https://github.com/miyukiproject/yukigo/commit/4f36ebe))
|
|
10
|
+
- **yukigo:** Evaluar `Equal` y `NotEqual` fuera de `processBinary` ([31eca15](https://github.com/miyukiproject/yukigo/commit/31eca15))
|
|
11
|
+
- **yukigo:** delegue logica de deepEqual a LazyRuntime ([8496113](https://github.com/miyukiproject/yukigo/commit/8496113))
|
|
12
|
+
- **yukigo:** evaluateConcatLazy permite eager y lazy evaluation ([3faf646](https://github.com/miyukiproject/yukigo/commit/3faf646))
|
|
13
|
+
- **yukigo:** corrijo `isEqual` para que compare strings con listas ([d6225c9](https://github.com/miyukiproject/yukigo/commit/d6225c9))
|
|
14
|
+
- **yukigo:** corrijo manejo de entorno en `applyArguments` y `visitApplication` ([5c7ab1f](https://github.com/miyukiproject/yukigo/commit/5c7ab1f))
|
|
15
|
+
- **yukigo:** capturar correctamente el entorno en `evaluateCons` ([a1bc7a4](https://github.com/miyukiproject/yukigo/commit/a1bc7a4))
|
|
16
|
+
- **yukigo:** `popEnv` no tiene que esperar el `env` por parametro ([575bacf](https://github.com/miyukiproject/yukigo/commit/575bacf))
|
|
17
|
+
- **yukigo:** agrego metodo visitTypeCast faltante ([61c9984](https://github.com/miyukiproject/yukigo/commit/61c9984))
|
|
18
|
+
|
|
19
|
+
### 🧱 Updated Dependencies
|
|
20
|
+
|
|
21
|
+
- Updated yukigo-ast to 0.2.0
|
|
22
|
+
|
|
23
|
+
### ❤️ Thank You
|
|
24
|
+
|
|
25
|
+
- noiseArch
|
|
26
|
+
|
|
1
27
|
## 0.1.0 (2025-12-09)
|
|
2
28
|
|
|
3
29
|
### 🧱 Updated Dependencies
|
package/dist/analyzer/index.d.ts
CHANGED
package/dist/analyzer/index.js
CHANGED
|
@@ -121,7 +121,8 @@ export class Analyzer {
|
|
|
121
121
|
// Execution Loop
|
|
122
122
|
const isGlobalVisitor = !rule.binding || rule.binding === "*";
|
|
123
123
|
const normalizedBinding = isGlobalVisitor ? undefined : rule.binding;
|
|
124
|
-
const
|
|
124
|
+
const args = rule.args ?? [];
|
|
125
|
+
const visitor = new InspectionClass(...args, normalizedBinding);
|
|
125
126
|
for (const { node, binding } of targets) {
|
|
126
127
|
try {
|
|
127
128
|
if (!isGlobalVisitor && visitor.setBinding) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApplicationPattern, AsPattern, ASTNode, ConsPattern, ConstructorPattern, Expression, FunctorPattern, LazyList, ListPattern, LiteralPattern, PrimitiveValue, TuplePattern, UnionPattern, VariablePattern, Visitor, WildcardPattern, TypePattern } from "yukigo-ast";
|
|
1
|
+
import { ApplicationPattern, AsPattern, ASTNode, ConsPattern, ConstructorPattern, Expression, FunctorPattern, LazyList, ListPattern, LiteralPattern, PrimitiveValue, TuplePattern, UnionPattern, VariablePattern, Visitor, WildcardPattern, TypePattern, EnvStack } from "yukigo-ast";
|
|
2
2
|
import { Bindings } from "../index.js";
|
|
3
3
|
import { CPSThunk } from "../trampoline.js";
|
|
4
4
|
import { RuntimeContext } from "./RuntimeContext.js";
|
|
@@ -17,6 +17,7 @@ export interface InternalConsState {
|
|
|
17
17
|
readonly head: PrimitiveValue;
|
|
18
18
|
readonly tailExpr: Expression;
|
|
19
19
|
readonly evaluator: ExpressionEvaluator;
|
|
20
|
+
readonly capturedEnv: EnvStack;
|
|
20
21
|
realizedTail?: PrimitiveValue;
|
|
21
22
|
}
|
|
22
23
|
export interface MemoizedLazyList extends LazyList {
|
|
@@ -27,8 +27,9 @@ export declare class RuntimeContext {
|
|
|
27
27
|
isDefined(name: string): boolean;
|
|
28
28
|
pushEnv(frame?: Environment): void;
|
|
29
29
|
replace(name: string, value: PrimitiveValue, onReplace?: (env: Environment) => void): boolean;
|
|
30
|
-
popEnv(
|
|
30
|
+
popEnv(): void;
|
|
31
31
|
lookup(name: string): PrimitiveValue;
|
|
32
32
|
remove(name: string): void;
|
|
33
33
|
define(name: string, value: PrimitiveValue): void;
|
|
34
|
+
clone(env?: EnvStack): EnvStack;
|
|
34
35
|
}
|
|
@@ -63,10 +63,10 @@ export class RuntimeContext {
|
|
|
63
63
|
}
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
|
-
popEnv(
|
|
67
|
-
if (!env.tail)
|
|
66
|
+
popEnv() {
|
|
67
|
+
if (!this.env.tail)
|
|
68
68
|
throw new Error("Runtime Error: Cannot pop the global environment scope.");
|
|
69
|
-
this.env = env.tail;
|
|
69
|
+
this.env = this.env.tail;
|
|
70
70
|
}
|
|
71
71
|
lookup(name) {
|
|
72
72
|
let current = this.env;
|
|
@@ -83,4 +83,11 @@ export class RuntimeContext {
|
|
|
83
83
|
define(name, value) {
|
|
84
84
|
this.env.head.set(name, value);
|
|
85
85
|
}
|
|
86
|
+
clone(env) {
|
|
87
|
+
const target = env ?? this.env;
|
|
88
|
+
return {
|
|
89
|
+
head: new Map(target.head),
|
|
90
|
+
tail: this.env.tail,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
86
93
|
}
|
|
@@ -53,7 +53,7 @@ class AssertionVisitor extends TraverseVisitor {
|
|
|
53
53
|
visitEquality(node) {
|
|
54
54
|
return (k) => this.interpreter.evaluate(node.value, (value) => {
|
|
55
55
|
return () => this.interpreter.evaluate(node.expected, (expected) => {
|
|
56
|
-
|
|
56
|
+
this.lazyRuntime.deepEqual(value, expected, (passed) => {
|
|
57
57
|
if (this.negated === passed) {
|
|
58
58
|
throw new FailedAssert(value, expected, this.negated
|
|
59
59
|
? `Expected ${JSON.stringify(value)} NOT to be equal to ${JSON.stringify(expected)}`
|
|
@@ -75,47 +75,6 @@ class AssertionVisitor extends TraverseVisitor {
|
|
|
75
75
|
return k(undefined);
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
|
-
isEqual(a, b, k) {
|
|
79
|
-
if (a === b)
|
|
80
|
-
return k(true);
|
|
81
|
-
if (a && b && typeof a === "object" && typeof b === "object") {
|
|
82
|
-
return this.lazyRuntime.realizeList(a, (valA) => {
|
|
83
|
-
return () => this.lazyRuntime.realizeList(b, (valB) => {
|
|
84
|
-
if (Array.isArray(valA) && Array.isArray(valB)) {
|
|
85
|
-
if (valA.length !== valB.length)
|
|
86
|
-
return k(false);
|
|
87
|
-
const checkNext = (index) => {
|
|
88
|
-
if (index >= valA.length)
|
|
89
|
-
return k(true);
|
|
90
|
-
return this.isEqual(valA[index], valB[index], (res) => {
|
|
91
|
-
if (!res)
|
|
92
|
-
return k(false);
|
|
93
|
-
return () => checkNext(index + 1);
|
|
94
|
-
});
|
|
95
|
-
};
|
|
96
|
-
return checkNext(0);
|
|
97
|
-
}
|
|
98
|
-
if (Array.isArray(valA) !== Array.isArray(valB))
|
|
99
|
-
return k(false);
|
|
100
|
-
const keys = Object.keys(valA);
|
|
101
|
-
if (keys.length !== Object.keys(valB).length)
|
|
102
|
-
return k(false);
|
|
103
|
-
const checkNextKey = (index) => {
|
|
104
|
-
if (index >= keys.length)
|
|
105
|
-
return k(true);
|
|
106
|
-
const key = keys[index];
|
|
107
|
-
return this.isEqual(valA[key], valB[key], (res) => {
|
|
108
|
-
if (!res)
|
|
109
|
-
return k(false);
|
|
110
|
-
return () => checkNextKey(index + 1);
|
|
111
|
-
});
|
|
112
|
-
};
|
|
113
|
-
return checkNextKey(0);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
return k(false);
|
|
118
|
-
}
|
|
119
78
|
}
|
|
120
79
|
export class TestRunner extends TraverseVisitor {
|
|
121
80
|
interpreter;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Visitor, PrimitiveValue, NumberPrimitive, BooleanPrimitive, StringPrimitive, ListPrimitive, NilPrimitive, SymbolPrimitive, Variable, CharPrimitive, ArithmeticUnaryOperation, ArithmeticBinaryOperation, ListUnaryOperation, ListBinaryOperation, ComparisonOperation, LogicalBinaryOperation, LogicalUnaryOperation, BitwiseBinaryOperation, BitwiseUnaryOperation, StringOperation, UnifyOperation, AssignOperation, Assignment, TupleExpression, FieldExpression, DataExpression, ConsExpression, LetInExpression, Call, Otherwise, CompositionExpression, Expression, Application, Lambda, Sequence, Exist, Not, Findall, Forall, Goal, Send, New, Self, ListComprehension, RangeExpression, Generator as YuGenerator, ASTNode, Raise, Query, Super, If, Assert, Test, TestGroup, LogicConstraint } from "yukigo-ast";
|
|
1
|
+
import { Visitor, PrimitiveValue, NumberPrimitive, BooleanPrimitive, StringPrimitive, ListPrimitive, NilPrimitive, SymbolPrimitive, Variable, CharPrimitive, ArithmeticUnaryOperation, ArithmeticBinaryOperation, ListUnaryOperation, ListBinaryOperation, ComparisonOperation, LogicalBinaryOperation, LogicalUnaryOperation, BitwiseBinaryOperation, BitwiseUnaryOperation, StringOperation, UnifyOperation, AssignOperation, Assignment, TupleExpression, FieldExpression, DataExpression, ConsExpression, LetInExpression, Call, Otherwise, CompositionExpression, Expression, Application, Lambda, Sequence, Exist, Not, Findall, Forall, Goal, Send, New, Self, ListComprehension, RangeExpression, Generator as YuGenerator, ASTNode, Raise, Query, TypeCast, Super, If, Assert, Test, TestGroup, LogicConstraint } from "yukigo-ast";
|
|
2
2
|
import { ExpressionEvaluator } from "../utils.js";
|
|
3
3
|
import { ErrorFrame } from "../errors.js";
|
|
4
4
|
import { Continuation, CPSThunk, Thunk } from "../trampoline.js";
|
|
@@ -44,7 +44,6 @@ export declare class InterpreterVisitor implements Visitor<CPSThunk<PrimitiveVal
|
|
|
44
44
|
visitCompositionExpression(node: CompositionExpression): CPSThunk<PrimitiveValue>;
|
|
45
45
|
visitLambda(node: Lambda): CPSThunk<PrimitiveValue>;
|
|
46
46
|
visitApplication(node: Application): CPSThunk<PrimitiveValue>;
|
|
47
|
-
private applyArguments;
|
|
48
47
|
visitQuery(node: Query): CPSThunk<PrimitiveValue>;
|
|
49
48
|
visitExist(node: Exist): CPSThunk<PrimitiveValue>;
|
|
50
49
|
visitNot(node: Not): CPSThunk<PrimitiveValue>;
|
|
@@ -58,6 +57,7 @@ export declare class InterpreterVisitor implements Visitor<CPSThunk<PrimitiveVal
|
|
|
58
57
|
visitNew(node: New): CPSThunk<PrimitiveValue>;
|
|
59
58
|
visitSelf(node: Self): CPSThunk<PrimitiveValue>;
|
|
60
59
|
visitListComprehension(node: ListComprehension): CPSThunk<PrimitiveValue>;
|
|
60
|
+
visitTypeCast(node: TypeCast): CPSThunk<PrimitiveValue>;
|
|
61
61
|
visitGenerator(node: YuGenerator): CPSThunk<PrimitiveValue>;
|
|
62
62
|
visitRaise(node: Raise): CPSThunk<PrimitiveValue>;
|
|
63
63
|
visitRangeExpression(node: RangeExpression): CPSThunk<PrimitiveValue>;
|
|
@@ -185,6 +185,9 @@ export class InterpreterVisitor {
|
|
|
185
185
|
(Array.isArray(b) || typeof b === "string" || isLazyList(b)), "ListBinaryOperation");
|
|
186
186
|
}
|
|
187
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
|
+
}
|
|
188
191
|
return this.processBinary(node, ComparisonOperationTable, () => true, "ComparisonOperation");
|
|
189
192
|
}
|
|
190
193
|
visitLogicalBinaryOperation(node) {
|
|
@@ -378,46 +381,28 @@ export class InterpreterVisitor {
|
|
|
378
381
|
});
|
|
379
382
|
}
|
|
380
383
|
visitApplication(node) {
|
|
384
|
+
const { funcRuntime } = this.context;
|
|
381
385
|
if (this.context.config.debug) {
|
|
382
386
|
console.log(`[Interpreter] Visiting Application`);
|
|
383
387
|
}
|
|
384
388
|
return (k) => this.evaluate(node.functionExpr, (func) => {
|
|
385
389
|
if (!isRuntimeFunction(func))
|
|
386
390
|
throw new InterpreterError("Application", "Cannot apply non-function");
|
|
387
|
-
|
|
391
|
+
const applyFuncToNode = (func) => (k) => this.evaluate(node.parameter, (arg) => {
|
|
388
392
|
const argThunk = () => arg;
|
|
389
393
|
const allPendingArgs = func.pendingArgs
|
|
390
394
|
? [...func.pendingArgs, argThunk]
|
|
391
395
|
: [argThunk];
|
|
392
|
-
return
|
|
396
|
+
return funcRuntime.applyArguments(func, allPendingArgs)(k);
|
|
393
397
|
});
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
pendingArgs: args,
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
const argsToConsume = args.slice(0, func.arity);
|
|
404
|
-
const remainingArgs = args.slice(func.arity);
|
|
405
|
-
const evaluatedArgs = argsToConsume.map((arg) => typeof arg === "function" ? arg() : arg);
|
|
406
|
-
if (func.closure)
|
|
407
|
-
this.context.pushEnv(func.closure.head);
|
|
408
|
-
return (cont) => () => this.context.funcRuntime.apply(func, evaluatedArgs, (result) => {
|
|
409
|
-
if (remainingArgs.length > 0) {
|
|
410
|
-
if (isRuntimeFunction(result)) {
|
|
411
|
-
const nextArgs = result.pendingArgs
|
|
412
|
-
? [...result.pendingArgs, ...remainingArgs]
|
|
413
|
-
: remainingArgs;
|
|
414
|
-
return this.applyArguments(result, nextArgs)(cont);
|
|
415
|
-
}
|
|
416
|
-
else {
|
|
417
|
-
throw new InterpreterError("Application", `Too many arguments provided. Result was '${result}' (not a function), but had ${remainingArgs.length} args left.`);
|
|
418
|
-
}
|
|
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
|
+
});
|
|
419
404
|
}
|
|
420
|
-
return
|
|
405
|
+
return applyFuncToNode(func)(k);
|
|
421
406
|
});
|
|
422
407
|
}
|
|
423
408
|
visitQuery(node) {
|
|
@@ -594,6 +579,9 @@ export class InterpreterVisitor {
|
|
|
594
579
|
return process(0);
|
|
595
580
|
};
|
|
596
581
|
}
|
|
582
|
+
visitTypeCast(node) {
|
|
583
|
+
return node.expression.accept(this);
|
|
584
|
+
}
|
|
597
585
|
visitGenerator(node) {
|
|
598
586
|
return (k) => this.evaluate(node.expression, k);
|
|
599
587
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { PrimitiveValue, RuntimeFunction } from "yukigo-ast";
|
|
2
|
-
import { Continuation, Thunk } from "../../trampoline.js";
|
|
2
|
+
import { Continuation, CPSThunk, Thunk } from "../../trampoline.js";
|
|
3
3
|
import { RuntimeContext } from "../RuntimeContext.js";
|
|
4
4
|
export declare class FunctionRuntime {
|
|
5
5
|
private context;
|
|
6
6
|
constructor(context: RuntimeContext);
|
|
7
7
|
apply(func: RuntimeFunction, args: PrimitiveValue[], k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
|
|
8
|
+
applyArguments(func: RuntimeFunction, args: (PrimitiveValue | (() => PrimitiveValue))[]): CPSThunk<PrimitiveValue>;
|
|
8
9
|
private preloadDefinitions;
|
|
9
10
|
private patternsMatch;
|
|
10
11
|
private evaluateSequence;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { UnguardedBody, Sequence, Return, Function, } from "yukigo-ast";
|
|
1
|
+
import { UnguardedBody, Sequence, Return, Function, isRuntimeFunction, } from "yukigo-ast";
|
|
2
2
|
import { PatternMatcher } from "../PatternMatcher.js";
|
|
3
3
|
import { InterpreterError } from "../../errors.js";
|
|
4
4
|
import { EnvBuilderVisitor } from "../EnvBuilder.js";
|
|
5
|
+
import { valueToCPS } from "../../trampoline.js";
|
|
5
6
|
import { RuntimeContext } from "../RuntimeContext.js";
|
|
6
7
|
import { InterpreterVisitor } from "../Visitor.js";
|
|
7
8
|
class NonExhaustivePatterns extends InterpreterError {
|
|
@@ -17,10 +18,12 @@ export class FunctionRuntime {
|
|
|
17
18
|
apply(func, args, k) {
|
|
18
19
|
const funcName = func.identifier;
|
|
19
20
|
const equations = func.equations;
|
|
21
|
+
const oldEnv = this.context.env;
|
|
20
22
|
if (this.context.config.debug)
|
|
21
23
|
console.log(`[FunctionRuntime] Applying function: ${funcName} with args:`, args);
|
|
22
24
|
const tryNextEquation = (eqIndex) => {
|
|
23
25
|
if (eqIndex >= equations.length) {
|
|
26
|
+
this.context.setEnv(oldEnv);
|
|
24
27
|
throw new NonExhaustivePatterns(funcName);
|
|
25
28
|
}
|
|
26
29
|
const eq = equations[eqIndex];
|
|
@@ -33,7 +36,6 @@ export class FunctionRuntime {
|
|
|
33
36
|
if (this.context.config.debug)
|
|
34
37
|
console.log(`[FunctionRuntime] Match successful for ${funcName} equation ${eqIndex}`);
|
|
35
38
|
const localEnv = new Map(bindings);
|
|
36
|
-
const oldEnv = this.context.env;
|
|
37
39
|
if (func.closure)
|
|
38
40
|
this.context.setEnv(func.closure);
|
|
39
41
|
this.context.pushEnv(localEnv);
|
|
@@ -74,6 +76,31 @@ export class FunctionRuntime {
|
|
|
74
76
|
};
|
|
75
77
|
return tryNextEquation(0);
|
|
76
78
|
}
|
|
79
|
+
applyArguments(func, args) {
|
|
80
|
+
if (args.length < func.arity) {
|
|
81
|
+
return valueToCPS({
|
|
82
|
+
...func,
|
|
83
|
+
pendingArgs: args,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const argsToConsume = args.slice(0, func.arity);
|
|
87
|
+
const remainingArgs = args.slice(func.arity);
|
|
88
|
+
const evaluatedArgs = argsToConsume.map((arg) => typeof arg === "function" ? arg() : arg);
|
|
89
|
+
return (cont) => () => this.apply(func, evaluatedArgs, (result) => {
|
|
90
|
+
if (remainingArgs.length > 0) {
|
|
91
|
+
if (isRuntimeFunction(result)) {
|
|
92
|
+
const nextArgs = result.pendingArgs
|
|
93
|
+
? [...result.pendingArgs, ...remainingArgs]
|
|
94
|
+
: remainingArgs;
|
|
95
|
+
return this.applyArguments(result, nextArgs)(cont);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw new InterpreterError("Application", `Too many arguments provided. Result was '${result}' (not a function), but had ${remainingArgs.length} args left.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return cont(result);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
77
104
|
preloadDefinitions(seq, evaluatorFactory) {
|
|
78
105
|
const ctx = new RuntimeContext();
|
|
79
106
|
new EnvBuilderVisitor(ctx).build(seq.statements);
|
|
@@ -11,4 +11,9 @@ export declare class LazyRuntime {
|
|
|
11
11
|
evaluateConcat(left: PrimitiveValue, right: PrimitiveValue, k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
|
|
12
12
|
evaluateConcatLazy(node: ListBinaryOperation, evaluator: ExpressionEvaluator, k: Continuation<PrimitiveValue>): Thunk<PrimitiveValue>;
|
|
13
13
|
deepEqual<R = boolean>(a: PrimitiveValue, b: PrimitiveValue, k: Continuation<boolean, R>): Thunk<R>;
|
|
14
|
+
private isPlainObject;
|
|
15
|
+
private isCollection;
|
|
16
|
+
private isListLike;
|
|
17
|
+
private deepEqualCollection;
|
|
18
|
+
private deepEqualObject;
|
|
14
19
|
}
|
|
@@ -78,14 +78,15 @@ export class LazyRuntime {
|
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
evaluateCons(node, evaluator, k) {
|
|
81
|
-
const capturedEnv = this.context.env;
|
|
82
81
|
const ctx = this.context;
|
|
82
|
+
const capturedEnv = ctx.clone();
|
|
83
83
|
return evaluator.evaluate(node.head, (head) => {
|
|
84
|
-
if (
|
|
84
|
+
if (ctx.config.lazyLoading) {
|
|
85
85
|
const consState = {
|
|
86
86
|
head,
|
|
87
87
|
tailExpr: node.tail,
|
|
88
88
|
evaluator,
|
|
89
|
+
capturedEnv,
|
|
89
90
|
realizedTail: undefined,
|
|
90
91
|
};
|
|
91
92
|
const consList = {
|
|
@@ -97,7 +98,7 @@ export class LazyRuntime {
|
|
|
97
98
|
yield current.head;
|
|
98
99
|
if (current.realizedTail === undefined) {
|
|
99
100
|
const prevEnv = ctx.env;
|
|
100
|
-
ctx.setEnv(capturedEnv);
|
|
101
|
+
ctx.setEnv(current.capturedEnv);
|
|
101
102
|
try {
|
|
102
103
|
current.realizedTail = trampoline(current.evaluator.evaluate(current.tailExpr, idContinuation));
|
|
103
104
|
}
|
|
@@ -178,61 +179,91 @@ export class LazyRuntime {
|
|
|
178
179
|
});
|
|
179
180
|
}
|
|
180
181
|
evaluateConcatLazy(node, evaluator, k) {
|
|
181
|
-
const capturedEnv = this.context.env;
|
|
182
182
|
const ctx = this.context;
|
|
183
|
-
return
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
const left = trampoline(evaluator.evaluate(node.left, idContinuation));
|
|
183
|
+
return evaluator.evaluate(node.left, (left) => {
|
|
184
|
+
const capturedEnv = ctx.clone();
|
|
185
|
+
return k(createMemoizedStream(function* () {
|
|
188
186
|
if (Array.isArray(left))
|
|
189
187
|
yield* left;
|
|
190
188
|
else if (typeof left === "string")
|
|
191
189
|
yield* left.split("");
|
|
192
190
|
else if (isLazyList(left))
|
|
193
191
|
yield* left.generator();
|
|
194
|
-
|
|
192
|
+
else
|
|
193
|
+
throw new Error("Invalid left operand for lazy Concat");
|
|
194
|
+
// right evaluates lazily on demand
|
|
195
|
+
const prevEnv = ctx.env;
|
|
196
|
+
ctx.setEnv(capturedEnv);
|
|
197
|
+
let right;
|
|
198
|
+
try {
|
|
199
|
+
right = trampoline(evaluator.evaluate(node.right, idContinuation));
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
ctx.setEnv(prevEnv);
|
|
203
|
+
}
|
|
195
204
|
if (Array.isArray(right))
|
|
196
205
|
yield* right;
|
|
197
206
|
else if (typeof right === "string")
|
|
198
207
|
yield* right.split("");
|
|
199
208
|
else if (isLazyList(right))
|
|
200
209
|
yield* right.generator();
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}));
|
|
210
|
+
else
|
|
211
|
+
throw new Error("Invalid right operand for lazy Concat");
|
|
212
|
+
}));
|
|
213
|
+
});
|
|
206
214
|
}
|
|
207
215
|
deepEqual(a, b, k) {
|
|
208
216
|
if (a === b)
|
|
209
217
|
return k(true);
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
218
|
+
const aIsListLike = this.isListLike(a);
|
|
219
|
+
const bIsListLike = this.isListLike(b);
|
|
220
|
+
const eitherIsCollection = this.isCollection(a) || this.isCollection(b);
|
|
221
|
+
if (eitherIsCollection && aIsListLike && bIsListLike) {
|
|
222
|
+
return this.realizeList(a, (valA) => () => this.realizeList(b, (valB) => {
|
|
214
223
|
if (valA.length !== valB.length)
|
|
215
224
|
return k(false);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return k(true);
|
|
219
|
-
return () => this.deepEqual(valA[index], valB[index], (isEqual) => {
|
|
220
|
-
if (!isEqual)
|
|
221
|
-
return k(false);
|
|
222
|
-
return () => compareNext(index + 1);
|
|
223
|
-
});
|
|
224
|
-
};
|
|
225
|
-
return compareNext(0);
|
|
226
|
-
}
|
|
227
|
-
return k(valA == valB);
|
|
228
|
-
};
|
|
229
|
-
if (isLazyList(a) || isLazyList(b)) {
|
|
230
|
-
return this.realizeList(a, (valA) => {
|
|
231
|
-
return () => this.realizeList(b, (valB) => {
|
|
232
|
-
return () => compareValues(valA, valB);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
225
|
+
return this.deepEqualCollection(valA, valB, 0, k);
|
|
226
|
+
}));
|
|
235
227
|
}
|
|
236
|
-
|
|
228
|
+
if (eitherIsCollection)
|
|
229
|
+
return k(false); // colección vs número → false
|
|
230
|
+
if (this.isPlainObject(a) && this.isPlainObject(b))
|
|
231
|
+
return this.deepEqualObject(a, b, k);
|
|
232
|
+
return k(a == b); // primitivos: number, boolean, string==number
|
|
233
|
+
}
|
|
234
|
+
isPlainObject(val) {
|
|
235
|
+
return val !== null && typeof val === "object"
|
|
236
|
+
&& !isLazyList(val) && !Array.isArray(val);
|
|
237
|
+
}
|
|
238
|
+
isCollection(val) {
|
|
239
|
+
return Array.isArray(val) || isLazyList(val);
|
|
240
|
+
}
|
|
241
|
+
isListLike(val) {
|
|
242
|
+
return Array.isArray(val) || isLazyList(val) || typeof val === "string";
|
|
243
|
+
}
|
|
244
|
+
deepEqualCollection(a, b, index, k) {
|
|
245
|
+
if (index >= a.length)
|
|
246
|
+
return k(true);
|
|
247
|
+
return this.deepEqual(a[index], b[index], (eq) => {
|
|
248
|
+
if (!eq)
|
|
249
|
+
return k(false);
|
|
250
|
+
return () => this.deepEqualCollection(a, b, index + 1, k);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
deepEqualObject(a, b, k) {
|
|
254
|
+
const keys = Object.keys(a);
|
|
255
|
+
if (keys.length !== Object.keys(b).length)
|
|
256
|
+
return k(false);
|
|
257
|
+
const checkNext = (index) => {
|
|
258
|
+
if (index >= keys.length)
|
|
259
|
+
return k(true);
|
|
260
|
+
const key = keys[index];
|
|
261
|
+
return this.deepEqual(a[key], b[key], (eq) => {
|
|
262
|
+
if (!eq)
|
|
263
|
+
return k(false);
|
|
264
|
+
return () => checkNext(index + 1);
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
return checkNext(0);
|
|
237
268
|
}
|
|
238
269
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Fact, Rule, Pattern, GuardedBody, UnguardedBody } from "yukigo-ast";
|
|
2
|
+
export type SuccessLogicResult = {
|
|
3
|
+
success: true;
|
|
4
|
+
solutions: Map<string, PrimitiveValue>;
|
|
5
|
+
};
|
|
6
|
+
export type FailedLogicResult = {
|
|
7
|
+
success: false;
|
|
8
|
+
};
|
|
9
|
+
export type LogicResult = SuccessLogicResult | FailedLogicResult;
|
|
10
|
+
export type PrimitiveValue = number | boolean | string | RuntimeFunction | RuntimePredicate | LogicResult | LazyList | null | void | PrimitiveValue[] | RuntimeObject | RuntimeClass | undefined;
|
|
11
|
+
export declare function isLogicResult(value: PrimitiveValue): value is LogicResult;
|
|
12
|
+
export type PrimitiveThunk = () => PrimitiveValue;
|
|
13
|
+
export type Environment = Map<string, PrimitiveValue>;
|
|
14
|
+
export type EnvStack = {
|
|
15
|
+
head: Environment;
|
|
16
|
+
tail: EnvStack | null;
|
|
17
|
+
};
|
|
18
|
+
type RuntimePredicateKind = "Fact" | "Rule" | "Predicate";
|
|
19
|
+
type PredicateEquation = Fact | Rule;
|
|
20
|
+
export interface IRuntimePredicate {
|
|
21
|
+
kind: RuntimePredicateKind;
|
|
22
|
+
identifier: string;
|
|
23
|
+
equations: PredicateEquation[];
|
|
24
|
+
}
|
|
25
|
+
export declare class RuntimePredicate implements IRuntimePredicate {
|
|
26
|
+
kind: RuntimePredicateKind;
|
|
27
|
+
identifier: string;
|
|
28
|
+
equations: PredicateEquation[];
|
|
29
|
+
constructor(kind: RuntimePredicateKind, identifier: string, equations: PredicateEquation[]);
|
|
30
|
+
pushEquation<T extends Fact | Rule>(eq: T): void;
|
|
31
|
+
}
|
|
32
|
+
export declare const isRuntimePredicate: (prim: PrimitiveValue) => prim is RuntimePredicate;
|
|
33
|
+
type Body = GuardedBody[] | UnguardedBody;
|
|
34
|
+
export interface IRuntimeEquation {
|
|
35
|
+
patterns: Pattern[];
|
|
36
|
+
body: Body;
|
|
37
|
+
}
|
|
38
|
+
export declare class RuntimeEquation implements IRuntimeEquation {
|
|
39
|
+
patterns: Pattern[];
|
|
40
|
+
body: Body;
|
|
41
|
+
constructor(patterns: Pattern[], body: Body);
|
|
42
|
+
}
|
|
43
|
+
type PendingArg = PrimitiveValue | PrimitiveThunk;
|
|
44
|
+
/**
|
|
45
|
+
* Runtime Function used in the Interpreter
|
|
46
|
+
*/
|
|
47
|
+
export interface IRuntimeFunction {
|
|
48
|
+
arity: number;
|
|
49
|
+
identifier: string;
|
|
50
|
+
equations: RuntimeEquation[];
|
|
51
|
+
pendingArgs: PendingArg[];
|
|
52
|
+
closure?: EnvStack;
|
|
53
|
+
}
|
|
54
|
+
export declare class RuntimeFunction implements IRuntimeFunction {
|
|
55
|
+
arity: number;
|
|
56
|
+
identifier: string;
|
|
57
|
+
equations: RuntimeEquation[];
|
|
58
|
+
pendingArgs: PendingArg[];
|
|
59
|
+
closure?: EnvStack;
|
|
60
|
+
constructor(arity: number, identifier: string, equations: RuntimeEquation[], pendingArgs: PendingArg[], closure?: EnvStack);
|
|
61
|
+
setPendingArgs(args: PendingArg[]): void;
|
|
62
|
+
}
|
|
63
|
+
export declare function isRuntimeFunction(val: PrimitiveValue): val is RuntimeFunction;
|
|
64
|
+
type Fields = Map<string, PrimitiveValue>;
|
|
65
|
+
type Methods = Map<string, RuntimeFunction>;
|
|
66
|
+
export interface IRuntimeClass {
|
|
67
|
+
identifier: string;
|
|
68
|
+
fields: Fields;
|
|
69
|
+
methods: Methods;
|
|
70
|
+
mixins: string[];
|
|
71
|
+
superclass?: string;
|
|
72
|
+
}
|
|
73
|
+
export declare class RuntimeClass implements IRuntimeClass {
|
|
74
|
+
identifier: string;
|
|
75
|
+
fields: Fields;
|
|
76
|
+
methods: Methods;
|
|
77
|
+
mixins: string[];
|
|
78
|
+
superclass?: string;
|
|
79
|
+
constructor(identifier: string, fields: Fields, methods: Methods, mixins: string[], superclass?: string);
|
|
80
|
+
}
|
|
81
|
+
export declare function isRuntimeClass(val: PrimitiveValue): val is RuntimeClass;
|
|
82
|
+
export interface IRuntimeObject {
|
|
83
|
+
identifier: string;
|
|
84
|
+
className: string;
|
|
85
|
+
fields: Map<string, PrimitiveValue>;
|
|
86
|
+
methods: Map<string, RuntimeFunction>;
|
|
87
|
+
}
|
|
88
|
+
export declare class RuntimeObject implements IRuntimeObject {
|
|
89
|
+
identifier: string;
|
|
90
|
+
className: string;
|
|
91
|
+
fields: Fields;
|
|
92
|
+
methods: Methods;
|
|
93
|
+
constructor(identifier: string, className: string, fields: Fields, methods: Methods);
|
|
94
|
+
}
|
|
95
|
+
export declare function isRuntimeObject(val: PrimitiveValue): val is RuntimeObject;
|
|
96
|
+
export type LazyGenerator = () => Generator<PrimitiveValue, void, unknown>;
|
|
97
|
+
export interface ILazyList {
|
|
98
|
+
readonly generator: LazyGenerator;
|
|
99
|
+
}
|
|
100
|
+
export declare class LazyList implements ILazyList {
|
|
101
|
+
readonly generator: LazyGenerator;
|
|
102
|
+
constructor(generator: LazyGenerator);
|
|
103
|
+
}
|
|
104
|
+
export declare function isLazyList(prim: unknown): prim is LazyList;
|
|
105
|
+
export {};
|