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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import {
|
|
3
|
+
Function,
|
|
4
|
+
SymbolPrimitive,
|
|
5
|
+
Application,
|
|
6
|
+
Equation,
|
|
7
|
+
UnguardedBody,
|
|
8
|
+
Sequence,
|
|
9
|
+
AST,
|
|
10
|
+
Rule,
|
|
11
|
+
Exist,
|
|
12
|
+
} from "yukigo-ast";
|
|
13
|
+
import { Analyzer, InspectionRule } from "../../src/analyzer/index.js";
|
|
14
|
+
import { AutoScoped, ScopedVisitor } from "../../src/analyzer/utils.js";
|
|
15
|
+
|
|
16
|
+
// records which functions it visits
|
|
17
|
+
@AutoScoped
|
|
18
|
+
class SpyVisitor extends ScopedVisitor {
|
|
19
|
+
public static visited: string[] = [];
|
|
20
|
+
|
|
21
|
+
constructor(scope?: string) {
|
|
22
|
+
super(scope);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
visitFunction(node: Function): void {
|
|
26
|
+
SpyVisitor.visited.push(node.identifier.value);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
visitRule(node: Rule): void {
|
|
30
|
+
SpyVisitor.visited.push(node.identifier.value);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("Analyzer Transitive Behavior", () => {
|
|
35
|
+
let analyzer: Analyzer;
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
SpyVisitor.visited = [];
|
|
39
|
+
analyzer = new Analyzer();
|
|
40
|
+
analyzer.registerInspection("Spy", SpyVisitor);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const createFunction = (name: string, calls: string[] = []) => {
|
|
44
|
+
const identifier = new SymbolPrimitive(name);
|
|
45
|
+
const body = new Sequence(
|
|
46
|
+
calls.map(
|
|
47
|
+
(callee) =>
|
|
48
|
+
new Application(
|
|
49
|
+
new SymbolPrimitive(callee),
|
|
50
|
+
new SymbolPrimitive("arg")
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
const equation = new Equation([], new UnguardedBody(body));
|
|
55
|
+
return new Function(identifier, [equation]);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const createRule = (name: string, calls: string[] = []) => {
|
|
59
|
+
const identifier = new SymbolPrimitive(name);
|
|
60
|
+
const body = new Sequence(
|
|
61
|
+
calls.map((callee) => new Exist(new SymbolPrimitive(callee), []))
|
|
62
|
+
);
|
|
63
|
+
const equation = new Equation([], new UnguardedBody(body));
|
|
64
|
+
return new Rule(identifier, [equation]);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const runInspection = (
|
|
68
|
+
ast: AST,
|
|
69
|
+
binding: string,
|
|
70
|
+
inspectionName: string = "Spy"
|
|
71
|
+
) => {
|
|
72
|
+
const rule: InspectionRule = {
|
|
73
|
+
inspection: inspectionName,
|
|
74
|
+
binding,
|
|
75
|
+
args: [],
|
|
76
|
+
expected: true,
|
|
77
|
+
};
|
|
78
|
+
analyzer.analyze(ast, [rule]);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
it("should traverse dependencies transitively by default", () => {
|
|
82
|
+
// A -> B
|
|
83
|
+
const funcA = createFunction("A", ["B"]);
|
|
84
|
+
const funcB = createFunction("B", []);
|
|
85
|
+
const ast = [funcA, funcB];
|
|
86
|
+
|
|
87
|
+
runInspection(ast, "A", "Spy");
|
|
88
|
+
|
|
89
|
+
expect(SpyVisitor.visited).to.include.members(["A", "B"]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should traverse deep dependencies", () => {
|
|
93
|
+
// A -> B -> C
|
|
94
|
+
const funcA = createFunction("A", ["B"]);
|
|
95
|
+
const funcB = createFunction("B", ["C"]);
|
|
96
|
+
const funcC = createFunction("C", []);
|
|
97
|
+
const ast = [funcA, funcB, funcC];
|
|
98
|
+
|
|
99
|
+
runInspection(ast, "A", "Spy");
|
|
100
|
+
|
|
101
|
+
expect(SpyVisitor.visited).to.include.members(["A", "B", "C"]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should handle cycles gracefully", () => {
|
|
105
|
+
// A -> B -> A
|
|
106
|
+
const funcA = createFunction("A", ["B"]);
|
|
107
|
+
const funcB = createFunction("B", ["A"]);
|
|
108
|
+
const ast = [funcA, funcB];
|
|
109
|
+
|
|
110
|
+
runInspection(ast, "A", "Spy");
|
|
111
|
+
|
|
112
|
+
expect(SpyVisitor.visited).to.include.members(["A", "B"]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should NOT traverse dependencies if Intransitive prefix is used", () => {
|
|
116
|
+
// A -> B
|
|
117
|
+
const funcA = createFunction("A", ["B"]);
|
|
118
|
+
const funcB = createFunction("B", []);
|
|
119
|
+
const ast = [funcA, funcB];
|
|
120
|
+
|
|
121
|
+
runInspection(ast, "A", "Intransitive:Spy");
|
|
122
|
+
|
|
123
|
+
expect(SpyVisitor.visited).to.include.members(["A"]);
|
|
124
|
+
expect(SpyVisitor.visited).to.not.include("B");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should not visit unreachable nodes", () => {
|
|
128
|
+
// A -> B; C (isolated)
|
|
129
|
+
const funcA = createFunction("A", ["B"]);
|
|
130
|
+
const funcB = createFunction("B", []);
|
|
131
|
+
const funcC = createFunction("C", []);
|
|
132
|
+
const ast = [funcA, funcB, funcC];
|
|
133
|
+
|
|
134
|
+
runInspection(ast, "A", "Spy");
|
|
135
|
+
|
|
136
|
+
expect(SpyVisitor.visited).to.include.members(["A", "B"]);
|
|
137
|
+
expect(SpyVisitor.visited).to.not.include("C");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should handle diamond dependencies correctly and visit each node once", () => {
|
|
141
|
+
// A -> B, A -> C, B -> D, C -> D
|
|
142
|
+
const funcA = createFunction("A", ["B", "C"]);
|
|
143
|
+
const funcB = createFunction("B", ["D"]);
|
|
144
|
+
const funcC = createFunction("C", ["D"]);
|
|
145
|
+
const funcD = createFunction("D", []);
|
|
146
|
+
const ast = [funcA, funcB, funcC, funcD];
|
|
147
|
+
|
|
148
|
+
runInspection(ast, "A", "Spy");
|
|
149
|
+
|
|
150
|
+
expect(SpyVisitor.visited).to.include.members(["A", "B", "C", "D"]);
|
|
151
|
+
|
|
152
|
+
// Ensure no duplicates
|
|
153
|
+
expect(SpyVisitor.visited.length).to.equal(4);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should traverse dependencies in Logic Rules (Exist nodes)", () => {
|
|
157
|
+
// ruleA :- ruleB.
|
|
158
|
+
const ruleA = createRule("ruleA", ["ruleB"]);
|
|
159
|
+
const ruleB = createRule("ruleB", []);
|
|
160
|
+
const ast = [ruleA, ruleB];
|
|
161
|
+
|
|
162
|
+
runInspection(ast, "ruleA", "Spy");
|
|
163
|
+
|
|
164
|
+
expect(SpyVisitor.visited).to.include.members(["ruleA", "ruleB"]);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -1,178 +1,183 @@
|
|
|
1
|
-
import { expect } from
|
|
2
|
-
import {
|
|
3
|
-
Function as AstFunction,
|
|
4
|
-
Fact,
|
|
5
|
-
Rule,
|
|
6
|
-
SymbolPrimitive,
|
|
7
|
-
Equation,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} as any
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
accept
|
|
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
|
-
it(
|
|
65
|
-
const funcNode = makeFunc(
|
|
66
|
-
|
|
67
|
-
visitor.visitFunction(funcNode);
|
|
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
|
-
expect(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
expect(
|
|
111
|
-
|
|
112
|
-
expect(entry
|
|
113
|
-
expect(entry.equations
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
expect(entry.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
expect(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import {
|
|
3
|
+
Function as AstFunction,
|
|
4
|
+
Fact,
|
|
5
|
+
Rule,
|
|
6
|
+
SymbolPrimitive,
|
|
7
|
+
Equation,
|
|
8
|
+
EnvStack,
|
|
9
|
+
RuntimeFunction,
|
|
10
|
+
RuntimePredicate,
|
|
11
|
+
Function,
|
|
12
|
+
AST,
|
|
13
|
+
} from "yukigo-ast";
|
|
14
|
+
import { EnvBuilderVisitor } from "../../src/interpreter/components/EnvBuilder.js";
|
|
15
|
+
import { RuntimeContext } from "../../src/interpreter/components/RuntimeContext.js";
|
|
16
|
+
|
|
17
|
+
const id = (val: string) => ({ value: val }) as SymbolPrimitive;
|
|
18
|
+
|
|
19
|
+
const makeEq = (arity: number): Equation =>
|
|
20
|
+
({
|
|
21
|
+
patterns: new Array(arity).fill({ type: "MockPattern" }),
|
|
22
|
+
body: { type: "MockBody" } as any,
|
|
23
|
+
}) as any;
|
|
24
|
+
|
|
25
|
+
const makeFunc = (
|
|
26
|
+
name: string,
|
|
27
|
+
arity: number,
|
|
28
|
+
eqCount: number = 1,
|
|
29
|
+
): AstFunction => new Function(id(name), Array(eqCount).fill(makeEq(arity)));
|
|
30
|
+
|
|
31
|
+
const makeFact = (name: string): Fact => {
|
|
32
|
+
const node = {
|
|
33
|
+
type: "Fact",
|
|
34
|
+
identifier: id(name),
|
|
35
|
+
patterns: [],
|
|
36
|
+
} as any;
|
|
37
|
+
node.accept = (v: any) => v.visitFact(node);
|
|
38
|
+
return node;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const makeRule = (name: string): Rule => {
|
|
42
|
+
const node = {
|
|
43
|
+
type: "Rule",
|
|
44
|
+
identifier: id(name),
|
|
45
|
+
patterns: [],
|
|
46
|
+
expressions: [],
|
|
47
|
+
} as any;
|
|
48
|
+
node.accept = (v: any) => v.visitRule(node);
|
|
49
|
+
return node;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
describe("EnvBuilderVisitor", () => {
|
|
53
|
+
let ctx: RuntimeContext;
|
|
54
|
+
let visitor: EnvBuilderVisitor;
|
|
55
|
+
let env: EnvStack;
|
|
56
|
+
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
ctx = new RuntimeContext();
|
|
59
|
+
visitor = new EnvBuilderVisitor(ctx);
|
|
60
|
+
env = ctx.env;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("Function Declarations", () => {
|
|
64
|
+
it("should register a valid function", () => {
|
|
65
|
+
const funcNode = makeFunc("myFunc", 1);
|
|
66
|
+
|
|
67
|
+
visitor.visitFunction(funcNode);
|
|
68
|
+
|
|
69
|
+
expect(ctx.isDefined("myFunc")).to.be.true;
|
|
70
|
+
const entry = ctx.lookup("myFunc") as RuntimeFunction;
|
|
71
|
+
|
|
72
|
+
expect(entry).to.have.property("identifier", "myFunc");
|
|
73
|
+
expect(entry).to.have.property("arity", 1);
|
|
74
|
+
expect(entry.equations).to.have.lengthOf(1);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should register a function with multiple equations", () => {
|
|
78
|
+
const funcNode = makeFunc("fib", 1, 2);
|
|
79
|
+
|
|
80
|
+
visitor.visitFunction(funcNode);
|
|
81
|
+
|
|
82
|
+
const entry = ctx.lookup("fib") as RuntimeFunction;
|
|
83
|
+
expect(entry.equations).to.have.lengthOf(2);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should throw error if function has no equations", () => {
|
|
87
|
+
const funcNode = makeFunc("empty", 0, 0);
|
|
88
|
+
|
|
89
|
+
expect(() => visitor.visitFunction(funcNode)).to.throw(
|
|
90
|
+
/has no equations/,
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should throw error if equations have different arity", () => {
|
|
95
|
+
const funcNode = makeFunc("badFunc", 1);
|
|
96
|
+
funcNode.equations.push(makeEq(2));
|
|
97
|
+
|
|
98
|
+
expect(() => visitor.visitFunction(funcNode)).to.throw(
|
|
99
|
+
/must have the same arity/,
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("Logic Programming (Facts)", () => {
|
|
105
|
+
it("should register a new Fact", () => {
|
|
106
|
+
const factNode = makeFact("parent");
|
|
107
|
+
|
|
108
|
+
visitor.visitFact(factNode);
|
|
109
|
+
|
|
110
|
+
expect(ctx.isDefined("parent")).to.be.true;
|
|
111
|
+
const entry = ctx.lookup("parent") as RuntimePredicate;
|
|
112
|
+
expect(entry).to.have.property("kind", "Predicate");
|
|
113
|
+
expect(entry.equations).to.have.lengthOf(1);
|
|
114
|
+
expect(entry.equations[0]).to.equal(factNode);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should append to existing Fact if identifier exists", () => {
|
|
118
|
+
const f1 = makeFact("parent");
|
|
119
|
+
const f2 = makeFact("parent");
|
|
120
|
+
|
|
121
|
+
visitor.visitFact(f1);
|
|
122
|
+
visitor.visitFact(f2);
|
|
123
|
+
|
|
124
|
+
const entry = ctx.lookup("parent") as RuntimePredicate;
|
|
125
|
+
expect(entry.kind).to.equal("Predicate");
|
|
126
|
+
expect(entry.equations).to.have.lengthOf(2);
|
|
127
|
+
expect(entry.equations[0]).to.equal(f1);
|
|
128
|
+
expect(entry.equations[1]).to.equal(f2);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should overwrite existing entry if it is not a Fact", () => {
|
|
132
|
+
ctx.define("test", { type: "SomethingElse", equations: [] } as any);
|
|
133
|
+
const factNode = makeFact("test");
|
|
134
|
+
visitor.visitFact(factNode);
|
|
135
|
+
|
|
136
|
+
const entry = ctx.lookup("test") as RuntimePredicate;
|
|
137
|
+
expect(entry.kind).to.equal("Predicate");
|
|
138
|
+
expect(entry.equations).to.have.lengthOf(1);
|
|
139
|
+
expect(entry.equations[0]).to.equal(factNode);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("Logic Programming (Rules)", () => {
|
|
144
|
+
it("should register a new Rule", () => {
|
|
145
|
+
const ruleNode = makeRule("grandparent");
|
|
146
|
+
|
|
147
|
+
visitor.visitRule(ruleNode);
|
|
148
|
+
|
|
149
|
+
expect(ctx.isDefined("grandparent")).to.be.true;
|
|
150
|
+
const entry = ctx.lookup("grandparent") as RuntimePredicate;
|
|
151
|
+
expect(entry).to.have.property("kind", "Predicate");
|
|
152
|
+
expect(entry.equations).to.have.lengthOf(1);
|
|
153
|
+
expect(entry.equations[0]).to.equal(ruleNode);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should append to existing Rule group", () => {
|
|
157
|
+
const r1 = makeRule("ancestor");
|
|
158
|
+
const r2 = makeRule("ancestor");
|
|
159
|
+
|
|
160
|
+
visitor.visitRule(r1);
|
|
161
|
+
visitor.visitRule(r2);
|
|
162
|
+
|
|
163
|
+
const entry = ctx.lookup("ancestor") as RuntimePredicate;
|
|
164
|
+
expect(entry.kind).to.equal("Predicate");
|
|
165
|
+
expect(entry.equations).to.have.lengthOf(2);
|
|
166
|
+
expect(entry.equations[0]).to.equal(r1);
|
|
167
|
+
expect(entry.equations[1]).to.equal(r2);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("should traverse the AST and build the complete environment", () => {
|
|
172
|
+
const nodes: AST = [
|
|
173
|
+
makeFunc("add", 2),
|
|
174
|
+
makeFact("is_human"),
|
|
175
|
+
makeRule("is_mortal"),
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
visitor.build(nodes);
|
|
179
|
+
expect(ctx.isDefined("add")).to.be.true;
|
|
180
|
+
expect(ctx.isDefined("is_human")).to.be.true;
|
|
181
|
+
expect(ctx.isDefined("is_mortal")).to.be.true;
|
|
182
|
+
});
|
|
183
|
+
});
|