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,194 +1,327 @@
|
|
|
1
|
-
import { expect } from "chai";
|
|
2
|
-
import {
|
|
3
|
-
SymbolPrimitive,
|
|
4
|
-
NumberPrimitive,
|
|
5
|
-
LiteralPattern,
|
|
6
|
-
VariablePattern,
|
|
7
|
-
Fact,
|
|
8
|
-
Findall,
|
|
9
|
-
Goal,
|
|
10
|
-
Pattern,
|
|
11
|
-
Rule,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
it(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import {
|
|
3
|
+
SymbolPrimitive,
|
|
4
|
+
NumberPrimitive,
|
|
5
|
+
LiteralPattern,
|
|
6
|
+
VariablePattern,
|
|
7
|
+
Fact,
|
|
8
|
+
Findall,
|
|
9
|
+
Goal,
|
|
10
|
+
Pattern,
|
|
11
|
+
Rule,
|
|
12
|
+
LogicResult,
|
|
13
|
+
EnvStack,
|
|
14
|
+
Equation,
|
|
15
|
+
Sequence,
|
|
16
|
+
UnguardedBody,
|
|
17
|
+
Statement,
|
|
18
|
+
Variable,
|
|
19
|
+
ConsPattern,
|
|
20
|
+
ListPattern,
|
|
21
|
+
NilPrimitive,
|
|
22
|
+
LazyList,
|
|
23
|
+
LogicConstraint,
|
|
24
|
+
Expression,
|
|
25
|
+
SuccessLogicResult,
|
|
26
|
+
RuntimePredicate,
|
|
27
|
+
} from "yukigo-ast";
|
|
28
|
+
import {
|
|
29
|
+
createGlobalEnv,
|
|
30
|
+
ExpressionEvaluator,
|
|
31
|
+
} from "../../src/interpreter/utils.js";
|
|
32
|
+
import { LogicEngine } from "../../src/interpreter/components/logic/LogicEngine.js";
|
|
33
|
+
import { unify } from "../../src/interpreter/components/logic/LogicResolver.js";
|
|
34
|
+
import { PatternResolver } from "../../src/interpreter/components/PatternMatcher.js";
|
|
35
|
+
import { InterpreterVisitor } from "../../src/interpreter/components/Visitor.js";
|
|
36
|
+
import {
|
|
37
|
+
idContinuation,
|
|
38
|
+
trampoline,
|
|
39
|
+
} from "../../src/interpreter/trampoline.js";
|
|
40
|
+
import {
|
|
41
|
+
InterpreterConfig,
|
|
42
|
+
RuntimeContext,
|
|
43
|
+
} from "../../src/interpreter/components/RuntimeContext.js";
|
|
44
|
+
|
|
45
|
+
const s = (val: string) => new SymbolPrimitive(val);
|
|
46
|
+
const n = (val: number) => new NumberPrimitive(val);
|
|
47
|
+
const lit = (val: string | number) =>
|
|
48
|
+
new LiteralPattern(typeof val === "string" ? s(val) : n(val));
|
|
49
|
+
const varPat = (name: string) => new VariablePattern(s(name));
|
|
50
|
+
|
|
51
|
+
const makeEq = (args: Pattern[], stmts: Statement[]) =>
|
|
52
|
+
new Equation(args, new UnguardedBody(new Sequence(stmts)));
|
|
53
|
+
const makeFact = (id: string, args: Pattern[]) => new Fact(s(id), args);
|
|
54
|
+
const makeRule = (id: string, body: Equation[]) => new Rule(s(id), body);
|
|
55
|
+
const makeGoal = (id: string, args: Pattern[]) => new Goal(s(id), args);
|
|
56
|
+
const makeConstraint = (expr: Expression) => new LogicConstraint(expr);
|
|
57
|
+
|
|
58
|
+
const factsParent: RuntimePredicate = {
|
|
59
|
+
kind: "Fact",
|
|
60
|
+
identifier: "parent",
|
|
61
|
+
equations: [
|
|
62
|
+
makeFact("parent", [lit("zeus"), lit("ares")]),
|
|
63
|
+
makeFact("parent", [lit("zeus"), lit("athena")]),
|
|
64
|
+
makeFact("parent", [lit("hera"), lit("ares")]),
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
const rulesSibling: RuntimePredicate = {
|
|
68
|
+
kind: "Rule",
|
|
69
|
+
identifier: "sibling",
|
|
70
|
+
equations: [
|
|
71
|
+
makeRule("sibling", [
|
|
72
|
+
makeEq(
|
|
73
|
+
[varPat("X"), varPat("Y")],
|
|
74
|
+
[
|
|
75
|
+
makeConstraint(makeGoal("parent", [varPat("Z"), varPat("X")])),
|
|
76
|
+
makeConstraint(makeGoal("parent", [varPat("Z"), varPat("Y")])),
|
|
77
|
+
],
|
|
78
|
+
),
|
|
79
|
+
]),
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const env = createGlobalEnv();
|
|
84
|
+
const context = new RuntimeContext({
|
|
85
|
+
debug: false,
|
|
86
|
+
outputMode: "all",
|
|
87
|
+
});
|
|
88
|
+
context.setEnv(env);
|
|
89
|
+
context.define("sibling", rulesSibling);
|
|
90
|
+
context.define("parent", factsParent);
|
|
91
|
+
|
|
92
|
+
describe("Logic Engine & Unification", () => {
|
|
93
|
+
let engine: LogicEngine;
|
|
94
|
+
let evaluator: ExpressionEvaluator;
|
|
95
|
+
|
|
96
|
+
beforeEach(() => {
|
|
97
|
+
evaluator = new InterpreterVisitor(context);
|
|
98
|
+
engine = new LogicEngine(evaluator, context);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("Unification Algorithm", () => {
|
|
102
|
+
it("should unify two identical literals", () => {
|
|
103
|
+
const p1 = lit("cat");
|
|
104
|
+
const p2 = lit("cat");
|
|
105
|
+
const result = unify(p1, p2);
|
|
106
|
+
expect(result).to.not.be.null;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should not unify different literals", () => {
|
|
110
|
+
const p1 = lit("cat");
|
|
111
|
+
const p2 = lit("dog");
|
|
112
|
+
const result = unify(p1, p2);
|
|
113
|
+
expect(result).to.be.null;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should unify a variable with a literal", () => {
|
|
117
|
+
const v1 = varPat("X");
|
|
118
|
+
const p2 = lit("cat");
|
|
119
|
+
const result = unify(v1, p2);
|
|
120
|
+
|
|
121
|
+
expect(result).to.not.be.null;
|
|
122
|
+
const resolved = result!.get("X");
|
|
123
|
+
expect(resolved).to.be.instanceOf(LiteralPattern);
|
|
124
|
+
const resolver = new PatternResolver();
|
|
125
|
+
expect(resolved?.accept(resolver)).to.equal("cat");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should unify two variables (aliasing)", () => {
|
|
129
|
+
const v1 = varPat("X");
|
|
130
|
+
const v2 = varPat("Y");
|
|
131
|
+
const result = unify(v1, v2);
|
|
132
|
+
expect(result).to.not.be.null;
|
|
133
|
+
expect(result!.has("X")).to.be.true;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe("ConsPattern Unification", () => {
|
|
137
|
+
it("should unify two identical ConsPatterns", () => {
|
|
138
|
+
const p1 = new ConsPattern(lit(1), new ListPattern([]));
|
|
139
|
+
const p2 = new ConsPattern(lit(1), new ListPattern([]));
|
|
140
|
+
const result = unify(p1, p2);
|
|
141
|
+
expect(result).to.not.be.null;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should unify ConsPattern with equivalent ListPattern", () => {
|
|
145
|
+
const cons = new ConsPattern(lit(1), new ListPattern([lit(2)]));
|
|
146
|
+
const list = new ListPattern([lit(1), lit(2)]);
|
|
147
|
+
const result = unify(cons, list);
|
|
148
|
+
expect(result).to.not.be.null;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("should unify ListPattern with equivalent ConsPattern", () => {
|
|
152
|
+
const list = new ListPattern([lit(1), lit(2)]);
|
|
153
|
+
const cons = new ConsPattern(lit(1), new ListPattern([lit(2)]));
|
|
154
|
+
const result = unify(list, cons);
|
|
155
|
+
expect(result).to.not.be.null;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should fail if heads do not match", () => {
|
|
159
|
+
const cons = new ConsPattern(lit(1), new ListPattern([lit(2)]));
|
|
160
|
+
const list = new ListPattern([lit(3), lit(2)]);
|
|
161
|
+
const result = unify(cons, list);
|
|
162
|
+
expect(result).to.be.null;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should fail if ListPattern is empty and ConsPattern expects head", () => {
|
|
166
|
+
const list = new ListPattern([]);
|
|
167
|
+
const cons = new ConsPattern(varPat("H"), varPat("T"));
|
|
168
|
+
const result = unify(list, cons);
|
|
169
|
+
expect(result).to.be.null;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should bind variables in ConsPattern", () => {
|
|
173
|
+
const cons = new ConsPattern(varPat("H"), varPat("T"));
|
|
174
|
+
const list = new ListPattern([lit(1), lit(2)]);
|
|
175
|
+
const result = unify(cons, list);
|
|
176
|
+
expect(result).to.not.be.null;
|
|
177
|
+
|
|
178
|
+
const h = result!.get("H");
|
|
179
|
+
expect(h).to.be.instanceOf(LiteralPattern);
|
|
180
|
+
|
|
181
|
+
const t = result!.get("T");
|
|
182
|
+
expect(t).to.be.instanceOf(ListPattern);
|
|
183
|
+
expect((t as ListPattern).elements).to.have.lengthOf(1);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("should unify infinite LazyList with Variable lazily", () => {
|
|
187
|
+
// Create infinite generator
|
|
188
|
+
const gen = function*() {
|
|
189
|
+
let i = 1;
|
|
190
|
+
while(true) yield i++;
|
|
191
|
+
};
|
|
192
|
+
const lazyList: LazyList = {
|
|
193
|
+
type: "LazyList",
|
|
194
|
+
generator: gen
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Mock evaluator to return this lazy list for variable "Infinite"
|
|
198
|
+
evaluator.evaluate = (node: any) => {
|
|
199
|
+
if (node instanceof Variable && node.identifier.value === "Infinite") {
|
|
200
|
+
return lazyList;
|
|
201
|
+
}
|
|
202
|
+
if (node instanceof SymbolPrimitive) return node.value;
|
|
203
|
+
return null;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Unify X = Infinite
|
|
207
|
+
const infiniteVar = new Variable(s("Infinite"), new NilPrimitive(null));
|
|
208
|
+
const xVar = new Variable(s("X"), new NilPrimitive(null));
|
|
209
|
+
|
|
210
|
+
const result = trampoline(engine.unifyExpr(xVar, infiniteVar, idContinuation)) as boolean;
|
|
211
|
+
expect(result).to.be.true;
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("LogicEngine Execution", () => {
|
|
217
|
+
it("should solve a simple ground goal (Fact exists)", () => {
|
|
218
|
+
const query = makeGoal("parent", [lit("zeus"), lit("ares")]);
|
|
219
|
+
const results = trampoline(
|
|
220
|
+
engine.solveGoal(query, idContinuation),
|
|
221
|
+
) as LogicResult[];
|
|
222
|
+
expect(results).to.not.be.false;
|
|
223
|
+
results.forEach((res) => expect(res.success).to.be.true);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("should fail a ground goal that does not exist", () => {
|
|
227
|
+
const query = makeGoal("parent", [lit("zeus"), lit("thor")]);
|
|
228
|
+
const results = trampoline(
|
|
229
|
+
engine.solveGoal(query, idContinuation),
|
|
230
|
+
) as LogicResult[];
|
|
231
|
+
results.forEach((res) => expect(res.success).to.be.false);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should solve a goal with a variable", () => {
|
|
235
|
+
const query = makeGoal("parent", [lit("zeus"), varPat("Child")]);
|
|
236
|
+
const results = trampoline(
|
|
237
|
+
engine.solveGoal(query, idContinuation),
|
|
238
|
+
) as LogicResult[];
|
|
239
|
+
|
|
240
|
+
results.forEach((res) => {
|
|
241
|
+
if (!res.success) expect.fail("Result should be successful");
|
|
242
|
+
expect(res.solutions.has("Child")).to.be.true;
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const names = results.map((r) => {
|
|
246
|
+
if (!r.success) throw new Error("Unexpected failure");
|
|
247
|
+
return r.solutions.get("Child");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
expect(names).to.include("ares");
|
|
251
|
+
expect(names).to.include("athena");
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("should solve a rule using backtracking", () => {
|
|
255
|
+
const query = makeGoal("sibling", [lit("ares"), lit("athena")]);
|
|
256
|
+
const results = trampoline(
|
|
257
|
+
engine.solveGoal(query, idContinuation),
|
|
258
|
+
) as LogicResult[];
|
|
259
|
+
expect(results.length).to.be.eq(1);
|
|
260
|
+
const solution = results[0] as SuccessLogicResult;
|
|
261
|
+
expect(solution.success).to.be.true;
|
|
262
|
+
expect(solution.solutions.get("X")).to.eq("ares");
|
|
263
|
+
expect(solution.solutions.get("Y")).to.eq("athena");
|
|
264
|
+
expect(solution.solutions.get("Z")).to.eq("zeus");
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
it("should solve a rule using backtracking with variable", () => {
|
|
268
|
+
const query = makeGoal("sibling", [lit("ares"), varPat("Child")]);
|
|
269
|
+
const results = trampoline(
|
|
270
|
+
engine.solveGoal(query, idContinuation),
|
|
271
|
+
) as LogicResult[];
|
|
272
|
+
results.forEach((res) => {
|
|
273
|
+
if (!res.success) expect.fail("Result should be successful");
|
|
274
|
+
expect(res.solutions.has("Child")).to.be.true;
|
|
275
|
+
});
|
|
276
|
+
const names = results.map((r) => {
|
|
277
|
+
if (!r.success) throw new Error("Unexpected failure");
|
|
278
|
+
return r.solutions.get("Child");
|
|
279
|
+
});
|
|
280
|
+
expect(names).to.include("ares");
|
|
281
|
+
expect(names).to.include("athena");
|
|
282
|
+
});
|
|
283
|
+
describe("Output Modes", () => {
|
|
284
|
+
it('should return all results when outputMode is "all"', () => {
|
|
285
|
+
const query = makeGoal("parent", [lit("zeus"), varPat("X")]);
|
|
286
|
+
|
|
287
|
+
const results = trampoline(
|
|
288
|
+
engine.solveGoal(query, idContinuation),
|
|
289
|
+
) as LogicResult[];
|
|
290
|
+
expect(results).to.be.an("array");
|
|
291
|
+
expect(results).to.have.lengthOf(2);
|
|
292
|
+
const names = results.map((r) => {
|
|
293
|
+
if (!r.success) throw new Error("Unexpected failure");
|
|
294
|
+
return r.solutions.get("X");
|
|
295
|
+
});
|
|
296
|
+
expect(names).to.include("ares");
|
|
297
|
+
expect(names).to.include("athena");
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
describe("Findall", () => {
|
|
302
|
+
it("should collect all solutions into a list", () => {
|
|
303
|
+
const findallNode = new Findall(
|
|
304
|
+
varPat("X"), // Template
|
|
305
|
+
makeGoal("parent", [lit("zeus"), varPat("X")]), // Goal
|
|
306
|
+
varPat("List"), // Bag variable
|
|
307
|
+
);
|
|
308
|
+
const result = trampoline(
|
|
309
|
+
engine.solveFindall(findallNode, idContinuation),
|
|
310
|
+
) as any;
|
|
311
|
+
expect(Array.isArray(result)).to.be.true;
|
|
312
|
+
expect(result).to.have.lengthOf(2);
|
|
313
|
+
expect(result).to.include("ares");
|
|
314
|
+
expect(result).to.include("athena");
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe("Expression Unification", () => {
|
|
318
|
+
it("should successfully unify a variable with an array expression", () => {
|
|
319
|
+
env.head.set("myList", "dummy");
|
|
320
|
+
const X = new Variable(s("X"), new NilPrimitive(null));
|
|
321
|
+
const myList = new Variable(s("myList"), new NilPrimitive(null));
|
|
322
|
+
const result = trampoline(engine.unifyExpr(X, myList, idContinuation));
|
|
323
|
+
expect(result).to.be.true;
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import {
|
|
3
|
+
SymbolPrimitive,
|
|
4
|
+
NumberPrimitive,
|
|
5
|
+
LiteralPattern,
|
|
6
|
+
VariablePattern,
|
|
7
|
+
ListPattern,
|
|
8
|
+
ConsPattern,
|
|
9
|
+
FunctorPattern,
|
|
10
|
+
Pattern,
|
|
11
|
+
} from "yukigo-ast";
|
|
12
|
+
import {
|
|
13
|
+
instantiate,
|
|
14
|
+
Substitution,
|
|
15
|
+
} from "../../src/interpreter/components/logic/LogicResolver.js";
|
|
16
|
+
|
|
17
|
+
const s = (val: string) => new SymbolPrimitive(val);
|
|
18
|
+
const n = (val: number) => new NumberPrimitive(val);
|
|
19
|
+
const lit = (val: string | number) =>
|
|
20
|
+
new LiteralPattern(typeof val === "string" ? s(val) : n(val));
|
|
21
|
+
const varPat = (name: string) => new VariablePattern(s(name));
|
|
22
|
+
|
|
23
|
+
describe("Logic Substitution (instantiate)", () => {
|
|
24
|
+
it("should recursively substitute in ListPattern", () => {
|
|
25
|
+
const pattern = new ListPattern([varPat("X"), lit(1), varPat("Y")]);
|
|
26
|
+
const substs: Substitution = new Map([
|
|
27
|
+
["X", lit("cat")],
|
|
28
|
+
["Y", lit("dog")],
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
const result = instantiate(pattern, substs) as ListPattern;
|
|
32
|
+
expect(result).to.be.instanceOf(ListPattern);
|
|
33
|
+
expect(result.elements[0]).to.deep.equal(lit("cat"));
|
|
34
|
+
expect(result.elements[1]).to.deep.equal(lit(1));
|
|
35
|
+
expect(result.elements[2]).to.deep.equal(lit("dog"));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should recursively substitute in ConsPattern", () => {
|
|
39
|
+
const pattern = new ConsPattern(varPat("X"), varPat("Y"));
|
|
40
|
+
const substs: Substitution = new Map<string, Pattern>([
|
|
41
|
+
["X", lit(1)],
|
|
42
|
+
["Y", new ListPattern([lit(2), lit(3)])],
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const result = instantiate(pattern, substs) as ConsPattern;
|
|
46
|
+
expect(result).to.be.instanceOf(ConsPattern);
|
|
47
|
+
expect(result.left).to.deep.equal(lit(1));
|
|
48
|
+
expect(result.right).to.be.instanceOf(ListPattern);
|
|
49
|
+
expect((result.right as ListPattern).elements).to.have.lengthOf(2);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should recursively substitute in FunctorPattern", () => {
|
|
53
|
+
const pattern = new FunctorPattern(s("person"), [
|
|
54
|
+
varPat("Name"),
|
|
55
|
+
varPat("Age"),
|
|
56
|
+
]);
|
|
57
|
+
const substs: Substitution = new Map([
|
|
58
|
+
["Name", lit("Alice")],
|
|
59
|
+
["Age", lit(30)],
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
const result = instantiate(pattern, substs) as FunctorPattern;
|
|
63
|
+
expect(result).to.be.instanceOf(FunctorPattern);
|
|
64
|
+
expect(result.identifier.value).to.equal("person");
|
|
65
|
+
expect(result.args[0]).to.deep.equal(lit("Alice"));
|
|
66
|
+
expect(result.args[1]).to.deep.equal(lit(30));
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should handle nested substitutions", () => {
|
|
70
|
+
const pattern = varPat("X");
|
|
71
|
+
const substs: Substitution = new Map<string, Pattern>([
|
|
72
|
+
["X", new ListPattern([varPat("Y")])],
|
|
73
|
+
["Y", lit("hello")],
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
const result = instantiate(pattern, substs) as ListPattern;
|
|
77
|
+
expect(result).to.be.instanceOf(ListPattern);
|
|
78
|
+
expect(result.elements[0]).to.deep.equal(lit("hello"));
|
|
79
|
+
});
|
|
80
|
+
});
|