yukigo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +4 -0
- package/CHANGELOG.md +6 -0
- package/README.md +199 -0
- package/dist/analyzer/index.d.ts +71 -0
- package/dist/analyzer/index.js +110 -0
- package/dist/analyzer/inspections/functional.d.ts +46 -0
- package/dist/analyzer/inspections/functional.js +123 -0
- package/dist/analyzer/inspections/generic.d.ts +151 -0
- package/dist/analyzer/inspections/generic.js +427 -0
- package/dist/analyzer/inspections/imperative.d.ts +37 -0
- package/dist/analyzer/inspections/imperative.js +105 -0
- package/dist/analyzer/inspections/logic.d.ts +49 -0
- package/dist/analyzer/inspections/logic.js +140 -0
- package/dist/analyzer/inspections/object.d.ts +83 -0
- package/dist/analyzer/inspections/object.js +235 -0
- package/dist/analyzer/utils.d.ts +4 -0
- package/dist/analyzer/utils.js +16 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/interpreter/components/EnvBuilder.d.ts +16 -0
- package/dist/interpreter/components/EnvBuilder.js +78 -0
- package/dist/interpreter/components/FunctionRuntime.d.ts +8 -0
- package/dist/interpreter/components/FunctionRuntime.js +52 -0
- package/dist/interpreter/components/LazyRuntime.d.ts +7 -0
- package/dist/interpreter/components/LazyRuntime.js +75 -0
- package/dist/interpreter/components/LogicEngine.d.ts +21 -0
- package/dist/interpreter/components/LogicEngine.js +152 -0
- package/dist/interpreter/components/LogicResolver.d.ts +11 -0
- package/dist/interpreter/components/LogicResolver.js +87 -0
- package/dist/interpreter/components/Operations.d.ts +14 -0
- package/dist/interpreter/components/Operations.js +69 -0
- package/dist/interpreter/components/PatternMatcher.d.ts +41 -0
- package/dist/interpreter/components/PatternMatcher.js +206 -0
- package/dist/interpreter/components/Visitor.d.ts +64 -0
- package/dist/interpreter/components/Visitor.js +299 -0
- package/dist/interpreter/errors.d.ts +19 -0
- package/dist/interpreter/errors.js +36 -0
- package/dist/interpreter/index.d.ts +32 -0
- package/dist/interpreter/index.js +44 -0
- package/dist/interpreter/utils.d.ts +14 -0
- package/dist/interpreter/utils.js +57 -0
- package/dist/utils/helpers.d.ts +14 -0
- package/dist/utils/helpers.js +51 -0
- package/package.json +30 -0
- package/src/analyzer/index.ts +132 -0
- package/src/analyzer/inspections/functional.ts +159 -0
- package/src/analyzer/inspections/generic.ts +499 -0
- package/src/analyzer/inspections/imperative.ts +129 -0
- package/src/analyzer/inspections/logic.ts +166 -0
- package/src/analyzer/inspections/object.ts +282 -0
- package/src/analyzer/utils.ts +26 -0
- package/src/index.ts +3 -0
- package/src/interpreter/components/EnvBuilder.ts +97 -0
- package/src/interpreter/components/FunctionRuntime.ts +79 -0
- package/src/interpreter/components/LazyRuntime.ts +97 -0
- package/src/interpreter/components/LogicEngine.ts +227 -0
- package/src/interpreter/components/LogicResolver.ts +130 -0
- package/src/interpreter/components/Operations.ts +81 -0
- package/src/interpreter/components/PatternMatcher.ts +254 -0
- package/src/interpreter/components/Visitor.ts +493 -0
- package/src/interpreter/errors.ts +47 -0
- package/src/interpreter/index.ts +59 -0
- package/src/interpreter/utils.ts +79 -0
- package/src/utils/helpers.ts +73 -0
- package/tests/analyzer/functional.spec.ts +221 -0
- package/tests/analyzer/generic.spec.ts +100 -0
- package/tests/analyzer/helpers.spec.ts +83 -0
- package/tests/analyzer/logic.spec.ts +292 -0
- package/tests/analyzer/oop.spec.ts +338 -0
- package/tests/interpreter/EnvBuilder.spec.ts +178 -0
- package/tests/interpreter/FunctionRuntime.spec.ts +234 -0
- package/tests/interpreter/LazyRuntime.spec.ts +190 -0
- package/tests/interpreter/LogicEngine.spec.ts +194 -0
- package/tests/interpreter/Operations.spec.ts +220 -0
- package/tests/interpreter/PatternSystem.spec.ts +189 -0
- package/tests/interpreter/interpreter.spec.ts +937 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import {
|
|
3
|
+
Fact,
|
|
4
|
+
Rule,
|
|
5
|
+
SymbolPrimitive,
|
|
6
|
+
Exist,
|
|
7
|
+
Not,
|
|
8
|
+
Forall,
|
|
9
|
+
Findall,
|
|
10
|
+
AssignOperation,
|
|
11
|
+
UnifyOperation,
|
|
12
|
+
NumberPrimitive,
|
|
13
|
+
ASTNode,
|
|
14
|
+
StopTraversalException,
|
|
15
|
+
ArithmeticBinaryOperation,
|
|
16
|
+
} from "yukigo-ast";
|
|
17
|
+
import {
|
|
18
|
+
DeclaresFact,
|
|
19
|
+
DeclaresRule,
|
|
20
|
+
DeclaresPredicate,
|
|
21
|
+
UsesFindall,
|
|
22
|
+
UsesForall,
|
|
23
|
+
UsesNot,
|
|
24
|
+
UsesUnificationOperator,
|
|
25
|
+
UsesCut,
|
|
26
|
+
UsesFail,
|
|
27
|
+
HasRedundantReduction,
|
|
28
|
+
} from "../../src/analyzer/inspections/logic.js";
|
|
29
|
+
|
|
30
|
+
function executeVisitor(node: ASTNode | ASTNode[], visitor: any) {
|
|
31
|
+
if (Array.isArray(node)) {
|
|
32
|
+
node.forEach((n) => n.accept(visitor));
|
|
33
|
+
} else {
|
|
34
|
+
node.accept(visitor);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("Logic Spec", () => {
|
|
39
|
+
const createSymbol = (name: string) => new SymbolPrimitive(name);
|
|
40
|
+
const createNumber = (val: number) => new NumberPrimitive(val);
|
|
41
|
+
|
|
42
|
+
const createFact = (name: string, args: any[] = []) => {
|
|
43
|
+
return new Fact(createSymbol(name), args);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const createRule = (
|
|
47
|
+
name: string,
|
|
48
|
+
patterns: any[] = [],
|
|
49
|
+
bodyExprs: any[] = []
|
|
50
|
+
) => {
|
|
51
|
+
return new Rule(createSymbol(name), patterns, bodyExprs);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const createCall = (name: string, args: any[] = []) => {
|
|
55
|
+
return new Exist(createSymbol(name), args);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
describe("DeclaresFact", () => {
|
|
59
|
+
it("detects when a specific fact is declared", () => {
|
|
60
|
+
const fact = createFact("baz", [createSymbol("a")]);
|
|
61
|
+
const visitor = new DeclaresFact("baz");
|
|
62
|
+
expect(() => executeVisitor(fact, visitor)).to.throw(
|
|
63
|
+
StopTraversalException
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("ignores facts with different names", () => {
|
|
68
|
+
const fact = createFact("baz");
|
|
69
|
+
const visitor = new DeclaresFact("foo");
|
|
70
|
+
expect(() => executeVisitor(fact, visitor)).to.not.throw();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("DeclaresRule", () => {
|
|
75
|
+
it("detects when a specific rule is declared", () => {
|
|
76
|
+
const rule = createRule("foo");
|
|
77
|
+
const visitor = new DeclaresRule("foo");
|
|
78
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
79
|
+
StopTraversalException
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("ignores rules with different names", () => {
|
|
84
|
+
const rule = createRule("foo");
|
|
85
|
+
const visitor = new DeclaresRule("baz");
|
|
86
|
+
expect(() => executeVisitor(rule, visitor)).to.not.throw();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("DeclaresPredicate", () => {
|
|
91
|
+
it("is True when rule is declared", () => {
|
|
92
|
+
const rule = createRule("foo");
|
|
93
|
+
const visitor = new DeclaresPredicate("foo");
|
|
94
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
95
|
+
StopTraversalException
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("is True when fact is declared", () => {
|
|
100
|
+
const fact = createFact("foo");
|
|
101
|
+
const visitor = new DeclaresPredicate("foo");
|
|
102
|
+
expect(() => executeVisitor(fact, visitor)).to.throw(
|
|
103
|
+
StopTraversalException
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("is False when predicate is not declared", () => {
|
|
108
|
+
const fact = createFact("bar");
|
|
109
|
+
const visitor = new DeclaresPredicate("foo");
|
|
110
|
+
expect(() => executeVisitor(fact, visitor)).to.not.throw();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("UsesForall", () => {
|
|
115
|
+
it("detects usage of forall/2", () => {
|
|
116
|
+
const forallNode = new Forall(createCall("cond"), createCall("action"));
|
|
117
|
+
const rule = createRule("foo", [], [forallNode]);
|
|
118
|
+
|
|
119
|
+
const visitor = new UsesForall();
|
|
120
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
121
|
+
StopTraversalException
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("is False when not used", () => {
|
|
126
|
+
const rule = createRule("foo", [], [createCall("bar")]);
|
|
127
|
+
const visitor = new UsesForall();
|
|
128
|
+
expect(() => executeVisitor(rule, visitor)).to.not.throw();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("UsesFindall", () => {
|
|
133
|
+
it("detects usage of findall/3", () => {
|
|
134
|
+
const findallNode = new Findall(
|
|
135
|
+
createSymbol("X"),
|
|
136
|
+
createCall("goal"),
|
|
137
|
+
createSymbol("L")
|
|
138
|
+
);
|
|
139
|
+
const rule = createRule("foo", [], [findallNode]);
|
|
140
|
+
|
|
141
|
+
const visitor = new UsesFindall();
|
|
142
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
143
|
+
StopTraversalException
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe("UsesNot", () => {
|
|
149
|
+
it("detects usage of Not node", () => {
|
|
150
|
+
const notNode = new Not([createCall("g")]);
|
|
151
|
+
const rule = createRule("foo", [], [notNode]);
|
|
152
|
+
|
|
153
|
+
const visitor = new UsesNot();
|
|
154
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
155
|
+
StopTraversalException
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("detects usage of 'not' as an Exist call (legacy/parsing variant)", () => {
|
|
160
|
+
const notCall = createCall("not", [createCall("g")]);
|
|
161
|
+
const rule = createRule("foo", [], [notCall]);
|
|
162
|
+
|
|
163
|
+
const visitor = new UsesNot();
|
|
164
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
165
|
+
StopTraversalException
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("is False when not used", () => {
|
|
170
|
+
const rule = createRule("foo", [], [createCall("bar")]);
|
|
171
|
+
const visitor = new UsesNot();
|
|
172
|
+
expect(() => executeVisitor(rule, visitor)).to.not.throw();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe("UsesUnificationOperator", () => {
|
|
177
|
+
it("detects usage of unification (=)", () => {
|
|
178
|
+
const unify = new UnifyOperation(
|
|
179
|
+
"Unify",
|
|
180
|
+
createSymbol("X"),
|
|
181
|
+
createNumber(4)
|
|
182
|
+
);
|
|
183
|
+
const rule = createRule("baz", [], [unify]);
|
|
184
|
+
|
|
185
|
+
const visitor = new UsesUnificationOperator("baz");
|
|
186
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
187
|
+
StopTraversalException
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("is False when no equal", () => {
|
|
192
|
+
const rule = createRule("baz", [], [createCall("other")]);
|
|
193
|
+
const visitor = new UsesUnificationOperator("baz");
|
|
194
|
+
expect(() => executeVisitor(rule, visitor)).to.not.throw();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe("UsesCut", () => {
|
|
199
|
+
it("detects usage of cut (!)", () => {
|
|
200
|
+
const cut = createCall("!");
|
|
201
|
+
const rule = createRule("baz", [], [cut]);
|
|
202
|
+
|
|
203
|
+
const visitor = new UsesCut("baz");
|
|
204
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
205
|
+
StopTraversalException
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("is False when not used", () => {
|
|
210
|
+
const rule = createRule("baz");
|
|
211
|
+
const visitor = new UsesCut("baz");
|
|
212
|
+
expect(() => executeVisitor(rule, visitor)).to.not.throw();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("UsesFail", () => {
|
|
217
|
+
it("detects usage of fail", () => {
|
|
218
|
+
const failNode = createCall("fail");
|
|
219
|
+
const rule = createRule("baz", [], [failNode]);
|
|
220
|
+
|
|
221
|
+
const visitor = new UsesFail("baz");
|
|
222
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
223
|
+
StopTraversalException
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("HasRedundantReduction", () => {
|
|
229
|
+
it("is True when there is a redundant reduction of parameters (Var is Var)", () => {
|
|
230
|
+
const assign = new AssignOperation(
|
|
231
|
+
"Assign",
|
|
232
|
+
createSymbol("X"),
|
|
233
|
+
createSymbol("Y")
|
|
234
|
+
);
|
|
235
|
+
const rule = createRule("baz", [], [assign]);
|
|
236
|
+
|
|
237
|
+
const visitor = new HasRedundantReduction("baz");
|
|
238
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
239
|
+
StopTraversalException
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("is True when there is a redundant reduction of literals (Var is 5)", () => {
|
|
244
|
+
const assign = new AssignOperation(
|
|
245
|
+
"Assign",
|
|
246
|
+
createSymbol("X"),
|
|
247
|
+
createNumber(5)
|
|
248
|
+
);
|
|
249
|
+
const rule = createRule("baz", [], [assign]);
|
|
250
|
+
|
|
251
|
+
const visitor = new HasRedundantReduction("baz");
|
|
252
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
253
|
+
StopTraversalException
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("is True when there is a redundant reduction of functors (Var is struct)", () => {
|
|
258
|
+
const functor = createCall("aFunctor", [createNumber(5)]);
|
|
259
|
+
const assign = new AssignOperation("Assign", createSymbol("Z"), functor);
|
|
260
|
+
const rule = createRule("baz", [], [assign]);
|
|
261
|
+
|
|
262
|
+
const visitor = new HasRedundantReduction("baz");
|
|
263
|
+
expect(() => executeVisitor(rule, visitor)).to.throw(
|
|
264
|
+
StopTraversalException
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("is False when there is a complex reduction (operators)", () => {
|
|
269
|
+
const assign = new AssignOperation(
|
|
270
|
+
"Assign",
|
|
271
|
+
createSymbol("X"),
|
|
272
|
+
new ArithmeticBinaryOperation("Plus", createNumber(3), createNumber(2))
|
|
273
|
+
);
|
|
274
|
+
const rule = createRule("baz", [], [assign]);
|
|
275
|
+
|
|
276
|
+
const visitor = new HasRedundantReduction("baz");
|
|
277
|
+
expect(() => executeVisitor(rule, visitor)).to.not.throw();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("respects bindings checks", () => {
|
|
281
|
+
const assign = new AssignOperation(
|
|
282
|
+
"Assign",
|
|
283
|
+
createSymbol("X"),
|
|
284
|
+
createNumber(5)
|
|
285
|
+
);
|
|
286
|
+
const rule = createRule("other", [], [assign]);
|
|
287
|
+
|
|
288
|
+
const visitor = new HasRedundantReduction("baz");
|
|
289
|
+
expect(() => executeVisitor(rule, visitor)).to.not.throw();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
});
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import {
|
|
3
|
+
Class,
|
|
4
|
+
Method,
|
|
5
|
+
Send,
|
|
6
|
+
Self,
|
|
7
|
+
SymbolPrimitive,
|
|
8
|
+
Attribute,
|
|
9
|
+
New,
|
|
10
|
+
Include,
|
|
11
|
+
Interface,
|
|
12
|
+
Object as AstObject,
|
|
13
|
+
PrimitiveMethod,
|
|
14
|
+
Implement,
|
|
15
|
+
ASTNode,
|
|
16
|
+
Equation,
|
|
17
|
+
UnguardedBody,
|
|
18
|
+
Sequence,
|
|
19
|
+
NilPrimitive,
|
|
20
|
+
Statement,
|
|
21
|
+
Print,
|
|
22
|
+
StringPrimitive,
|
|
23
|
+
} from "yukigo-ast";
|
|
24
|
+
import {
|
|
25
|
+
DeclaresAttribute,
|
|
26
|
+
DeclaresClass,
|
|
27
|
+
DeclaresInterface,
|
|
28
|
+
DeclaresMethod,
|
|
29
|
+
DeclaresObject,
|
|
30
|
+
DeclaresPrimitive,
|
|
31
|
+
DeclaresSuperclass,
|
|
32
|
+
Implements,
|
|
33
|
+
IncludeMixin,
|
|
34
|
+
Instantiates,
|
|
35
|
+
UsesDynamicPolymorphism,
|
|
36
|
+
UsesInheritance,
|
|
37
|
+
UsesMixins,
|
|
38
|
+
UsesObjectComposition,
|
|
39
|
+
UsesStaticMethodOverload,
|
|
40
|
+
UsesDynamicMethodOverload,
|
|
41
|
+
UsesTemplateMethod,
|
|
42
|
+
} from "../../src/analyzer/inspections/object.js";
|
|
43
|
+
import { executeVisitor } from "../../src/analyzer/utils.js";
|
|
44
|
+
|
|
45
|
+
describe("OOP Spec", () => {
|
|
46
|
+
const createSymbol = (name: string) => new SymbolPrimitive(name);
|
|
47
|
+
const createMethod = (
|
|
48
|
+
name: string,
|
|
49
|
+
equationsCount: number = 1,
|
|
50
|
+
isAbstract: boolean = false,
|
|
51
|
+
bodyNode?: Statement
|
|
52
|
+
): Method => {
|
|
53
|
+
const identifier = createSymbol(name);
|
|
54
|
+
const stmts = bodyNode ? [bodyNode] : [];
|
|
55
|
+
const equations = new Array(equationsCount).fill(
|
|
56
|
+
new Equation([], new UnguardedBody(new Sequence(stmts)))
|
|
57
|
+
);
|
|
58
|
+
const method = new Method(identifier, equations);
|
|
59
|
+
method.setMetadata("isAbstract", isAbstract);
|
|
60
|
+
return method;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const createAttribute = (
|
|
64
|
+
name: string,
|
|
65
|
+
expression: any = new NilPrimitive(null)
|
|
66
|
+
) => {
|
|
67
|
+
return new Attribute(createSymbol(name), expression);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const createNew = (className: string) => {
|
|
71
|
+
return new New(createSymbol(className), []);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const createClass = (
|
|
75
|
+
name: string,
|
|
76
|
+
stmts: Statement[] = [],
|
|
77
|
+
extendsName?: string,
|
|
78
|
+
implementsName?: string
|
|
79
|
+
): Class => {
|
|
80
|
+
const identifier = createSymbol(name);
|
|
81
|
+
const extendsSymbol = extendsName ? createSymbol(extendsName) : undefined;
|
|
82
|
+
const implementsNode = implementsName
|
|
83
|
+
? new Implement(createSymbol(implementsName))
|
|
84
|
+
: undefined;
|
|
85
|
+
|
|
86
|
+
const expression = new Sequence(stmts);
|
|
87
|
+
|
|
88
|
+
return new Class(identifier, extendsSymbol, implementsNode, expression);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const createObject = (name: string, children: ASTNode[] = []) => {
|
|
92
|
+
const identifier = createSymbol(name);
|
|
93
|
+
const expression = {
|
|
94
|
+
accept: (visitor: any) => {
|
|
95
|
+
children.forEach((child) => child.accept(visitor));
|
|
96
|
+
},
|
|
97
|
+
toJSON: () => ({}),
|
|
98
|
+
} as any;
|
|
99
|
+
return new AstObject(identifier, expression);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
describe("DeclaresAttribute", () => {
|
|
103
|
+
it("should detect if an attribute with the specific name is declared", () => {
|
|
104
|
+
const attr = createAttribute("energy");
|
|
105
|
+
const visitor = new DeclaresAttribute("energy");
|
|
106
|
+
expect(executeVisitor(attr, visitor)).to.eq(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should ignore attributes with different names", () => {
|
|
110
|
+
const attr = createAttribute("life");
|
|
111
|
+
const visitor = new DeclaresAttribute("energy");
|
|
112
|
+
expect(executeVisitor(attr, visitor)).to.eq(false);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("DeclaresClass", () => {
|
|
117
|
+
it("should detect specific class declaration", () => {
|
|
118
|
+
const node = createClass("Bird");
|
|
119
|
+
const visitor = new DeclaresClass("Bird");
|
|
120
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("DeclaresInterface", () => {
|
|
125
|
+
it("should detect specific interface declaration", () => {
|
|
126
|
+
const node = new Interface(createSymbol("Flyable"), [], {} as any);
|
|
127
|
+
const visitor = new DeclaresInterface("Flyable");
|
|
128
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("DeclaresMethod", () => {
|
|
133
|
+
it("should detect specific method declaration", () => {
|
|
134
|
+
const node = createMethod("fly");
|
|
135
|
+
const visitor = new DeclaresMethod("fly");
|
|
136
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("DeclaresObject", () => {
|
|
141
|
+
it("should detect specific object declaration", () => {
|
|
142
|
+
const node = createObject("pepita");
|
|
143
|
+
const visitor = new DeclaresObject("pepita");
|
|
144
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe("DeclaresPrimitive", () => {
|
|
149
|
+
it("should detect primitive operator override", () => {
|
|
150
|
+
const op: any = "==";
|
|
151
|
+
const node = new PrimitiveMethod(op, [], undefined as any);
|
|
152
|
+
const visitor = new DeclaresPrimitive("==");
|
|
153
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("DeclaresSuperclass", () => {
|
|
158
|
+
it("should detect if a class extends a specific superclass", () => {
|
|
159
|
+
const node = createClass("Sparrow", [], "Bird");
|
|
160
|
+
const visitor = new DeclaresSuperclass("Bird");
|
|
161
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("Implements", () => {
|
|
166
|
+
it("should detect if a class implements a specific interface", () => {
|
|
167
|
+
const node = createClass("Pigeon", [], undefined, "Messenger");
|
|
168
|
+
const visitor = new Implements("Messenger");
|
|
169
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("IncludeMixin", () => {
|
|
174
|
+
it("should detect usage of a specific mixin", () => {
|
|
175
|
+
const node = new Include(createSymbol("Walking"));
|
|
176
|
+
const visitor = new IncludeMixin("Walking");
|
|
177
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe("Instantiates", () => {
|
|
182
|
+
it("should detect instantiation of a specific class", () => {
|
|
183
|
+
const node = createNew("Engine");
|
|
184
|
+
const visitor = new Instantiates("Engine");
|
|
185
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("UsesDynamicPolymorphism", () => {
|
|
190
|
+
it("should detect when a method name is used in at least two different places (polymorphism)", () => {
|
|
191
|
+
const method1 = createMethod("fly");
|
|
192
|
+
const method2 = createMethod("fly");
|
|
193
|
+
|
|
194
|
+
const class1 = createClass("Bird", [method1]);
|
|
195
|
+
const class2 = createClass("Airplane", [method2]);
|
|
196
|
+
|
|
197
|
+
const visitor = new UsesDynamicPolymorphism("fly");
|
|
198
|
+
|
|
199
|
+
executeVisitor(class1, visitor);
|
|
200
|
+
|
|
201
|
+
expect(executeVisitor(class2, visitor)).to.eq(true);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should not throw if only one method is found", () => {
|
|
205
|
+
const method1 = createMethod("fly");
|
|
206
|
+
const class1 = createClass("Bird", [method1]);
|
|
207
|
+
const visitor = new UsesDynamicPolymorphism("fly");
|
|
208
|
+
|
|
209
|
+
expect(executeVisitor(class1, visitor)).to.eq(false);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("UsesInheritance", () => {
|
|
214
|
+
it("should detect inheritance in classes", () => {
|
|
215
|
+
const node = createClass("Child", [], "Parent");
|
|
216
|
+
const visitor = new UsesInheritance();
|
|
217
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("should detect inheritance in interfaces", () => {
|
|
221
|
+
const node = new Interface(
|
|
222
|
+
createSymbol("IChild"),
|
|
223
|
+
[createSymbol("IParent")],
|
|
224
|
+
{} as any
|
|
225
|
+
);
|
|
226
|
+
const visitor = new UsesInheritance();
|
|
227
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("should ignore classes without parent", () => {
|
|
231
|
+
const node = createClass("Orphan");
|
|
232
|
+
const visitor = new UsesInheritance();
|
|
233
|
+
expect(executeVisitor(node, visitor)).to.eq(false);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe("UsesMixins", () => {
|
|
238
|
+
it("should detect any mixin usage", () => {
|
|
239
|
+
const node = new Include(createSymbol("AnyMixin"));
|
|
240
|
+
const visitor = new UsesMixins();
|
|
241
|
+
expect(executeVisitor(node, visitor)).to.eq(true);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("UsesObjectComposition", () => {
|
|
246
|
+
it("should detect composition when an attribute is initialized with New", () => {
|
|
247
|
+
const instantiation = createNew("Engine");
|
|
248
|
+
const attr = createAttribute("myEngine", instantiation);
|
|
249
|
+
const visitor = new UsesObjectComposition();
|
|
250
|
+
expect(executeVisitor(attr, visitor)).to.eq(true);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("should ignore attributes initialized with literals", () => {
|
|
254
|
+
const attr = createAttribute(
|
|
255
|
+
"energy",
|
|
256
|
+
new StringPrimitive("Initialized with string!")
|
|
257
|
+
);
|
|
258
|
+
const visitor = new UsesObjectComposition();
|
|
259
|
+
expect(executeVisitor(attr, visitor)).to.eq(false);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe("UsesStaticMethodOverload", () => {
|
|
264
|
+
it("should detect two methods with the same name in the same class", () => {
|
|
265
|
+
const m1 = createMethod("calculate");
|
|
266
|
+
const m2 = createMethod("calculate");
|
|
267
|
+
const classNode = createClass("Calculator", [m1, m2]);
|
|
268
|
+
|
|
269
|
+
const visitor = new UsesStaticMethodOverload();
|
|
270
|
+
expect(executeVisitor(classNode, visitor)).to.eq(true);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should NOT detect overload if methods are in different classes", () => {
|
|
274
|
+
const m1 = createMethod("calculate");
|
|
275
|
+
const m2 = createMethod("calculate");
|
|
276
|
+
|
|
277
|
+
const classA = createClass("A", [m1]);
|
|
278
|
+
const classB = createClass("B", [m2]);
|
|
279
|
+
|
|
280
|
+
const visitor = new UsesStaticMethodOverload();
|
|
281
|
+
|
|
282
|
+
executeVisitor(classA, visitor);
|
|
283
|
+
|
|
284
|
+
expect(executeVisitor(classB, visitor)).to.eq(false);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe("UsesDynamicMethodOverload", () => {
|
|
289
|
+
it("should detect a method with multiple equations (pattern matching)", () => {
|
|
290
|
+
const method = createMethod("fibonacci", 2);
|
|
291
|
+
const visitor = new UsesDynamicMethodOverload();
|
|
292
|
+
expect(executeVisitor(method, visitor)).to.eq(true);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("should ignore methods with a single equation", () => {
|
|
296
|
+
const method = createMethod("fibonacci", 1);
|
|
297
|
+
const visitor = new UsesDynamicMethodOverload();
|
|
298
|
+
expect(executeVisitor(method, visitor)).to.eq(false);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe("UsesTemplateMethod", () => {
|
|
303
|
+
it("should detect template method usage (call to undeclared method on self)", () => {
|
|
304
|
+
const declaredMethod = createMethod("abstractMethod", 1, true);
|
|
305
|
+
const sendToSelf = new Send(
|
|
306
|
+
new Self(),
|
|
307
|
+
createSymbol("abstractMethod"),
|
|
308
|
+
[]
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
const templateMethod = createMethod("process", 1, false, sendToSelf);
|
|
312
|
+
const classNode = createClass("MyClass", [
|
|
313
|
+
declaredMethod,
|
|
314
|
+
templateMethod,
|
|
315
|
+
]);
|
|
316
|
+
|
|
317
|
+
const visitor = new UsesTemplateMethod();
|
|
318
|
+
expect(executeVisitor(classNode, visitor)).to.eq(true);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("should NOT detect if calling a declared method on self", () => {
|
|
322
|
+
const declaredMethod = createMethod(
|
|
323
|
+
"declared",
|
|
324
|
+
1,
|
|
325
|
+
false,
|
|
326
|
+
new Print(new StringPrimitive("This method is Declared"))
|
|
327
|
+
);
|
|
328
|
+
const sendToSelf = new Send(new Self(), createSymbol("declared"), []);
|
|
329
|
+
|
|
330
|
+
const method = createMethod("process", 1, false, sendToSelf);
|
|
331
|
+
|
|
332
|
+
const classNode = createClass("MyClass", [declaredMethod, method]);
|
|
333
|
+
|
|
334
|
+
const visitor = new UsesTemplateMethod();
|
|
335
|
+
expect(executeVisitor(classNode, visitor)).to.eq(false);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
});
|