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.
Files changed (144) hide show
  1. package/.mocharc.json +3 -3
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +193 -199
  4. package/dist/analyzer/GraphBuilder.d.ts +29 -0
  5. package/dist/analyzer/GraphBuilder.js +99 -0
  6. package/dist/analyzer/index.d.ts +11 -23
  7. package/dist/analyzer/index.js +100 -58
  8. package/dist/analyzer/inspections/functional/functional.d.ts +44 -0
  9. package/dist/analyzer/inspections/functional/functional.js +149 -0
  10. package/dist/analyzer/inspections/functional/smells.d.ts +16 -0
  11. package/dist/analyzer/inspections/functional/smells.js +98 -0
  12. package/dist/analyzer/inspections/{generic.d.ts → generic/generic.d.ts} +70 -43
  13. package/dist/analyzer/inspections/generic/generic.js +604 -0
  14. package/dist/analyzer/inspections/generic/smells.d.ts +61 -0
  15. package/dist/analyzer/inspections/generic/smells.js +349 -0
  16. package/dist/analyzer/inspections/imperative/imperative.d.ts +35 -0
  17. package/dist/analyzer/inspections/imperative/imperative.js +109 -0
  18. package/dist/analyzer/inspections/imperative/smells.d.ts +16 -0
  19. package/dist/analyzer/inspections/imperative/smells.js +58 -0
  20. package/dist/analyzer/inspections/logic/logic.d.ts +32 -0
  21. package/dist/analyzer/inspections/logic/logic.js +96 -0
  22. package/dist/analyzer/inspections/logic/smells.d.ts +15 -0
  23. package/dist/analyzer/inspections/logic/smells.js +60 -0
  24. package/dist/analyzer/inspections/object/object.d.ts +88 -0
  25. package/dist/analyzer/inspections/object/object.js +319 -0
  26. package/dist/analyzer/inspections/object/smells.d.ts +30 -0
  27. package/dist/analyzer/inspections/object/smells.js +135 -0
  28. package/dist/analyzer/utils.d.ts +26 -4
  29. package/dist/analyzer/utils.js +71 -13
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +1 -0
  32. package/dist/interpreter/components/EnvBuilder.d.ts +9 -5
  33. package/dist/interpreter/components/EnvBuilder.js +100 -30
  34. package/dist/interpreter/components/Operations.d.ts +4 -4
  35. package/dist/interpreter/components/Operations.js +17 -2
  36. package/dist/interpreter/components/PatternMatcher.d.ts +47 -17
  37. package/dist/interpreter/components/PatternMatcher.js +264 -119
  38. package/dist/interpreter/components/RuntimeContext.d.ts +35 -0
  39. package/dist/interpreter/components/RuntimeContext.js +93 -0
  40. package/dist/interpreter/components/TestRunner.d.ts +18 -0
  41. package/dist/interpreter/components/TestRunner.js +103 -0
  42. package/dist/interpreter/components/Visitor.d.ts +63 -57
  43. package/dist/interpreter/components/Visitor.js +508 -173
  44. package/dist/interpreter/components/logic/LogicEngine.d.ts +29 -0
  45. package/dist/interpreter/components/logic/LogicEngine.js +259 -0
  46. package/dist/interpreter/components/logic/LogicResolver.d.ts +53 -0
  47. package/dist/interpreter/components/logic/LogicResolver.js +471 -0
  48. package/dist/interpreter/components/logic/LogicTranslator.d.ts +14 -0
  49. package/dist/interpreter/components/logic/LogicTranslator.js +99 -0
  50. package/dist/interpreter/components/runtimes/FunctionRuntime.d.ts +12 -0
  51. package/dist/interpreter/components/runtimes/FunctionRuntime.js +147 -0
  52. package/dist/interpreter/components/runtimes/LazyRuntime.d.ts +19 -0
  53. package/dist/interpreter/components/runtimes/LazyRuntime.js +269 -0
  54. package/dist/interpreter/components/runtimes/ObjectRuntime.d.ts +35 -0
  55. package/dist/interpreter/components/runtimes/ObjectRuntime.js +126 -0
  56. package/dist/interpreter/entities.d.ts +105 -0
  57. package/dist/interpreter/entities.js +96 -0
  58. package/dist/interpreter/errors.d.ts +1 -1
  59. package/dist/interpreter/index.d.ts +4 -12
  60. package/dist/interpreter/index.js +10 -13
  61. package/dist/interpreter/trampoline.d.ts +17 -0
  62. package/dist/interpreter/trampoline.js +38 -0
  63. package/dist/interpreter/utils.d.ts +4 -7
  64. package/dist/interpreter/utils.js +25 -17
  65. package/dist/tester/index.d.ts +25 -0
  66. package/dist/tester/index.js +108 -0
  67. package/dist/utils/helpers.d.ts +0 -4
  68. package/dist/utils/helpers.js +20 -24
  69. package/package.json +2 -2
  70. package/src/analyzer/GraphBuilder.ts +142 -0
  71. package/src/analyzer/index.ts +185 -132
  72. package/src/analyzer/inspections/functional/functional.ts +121 -0
  73. package/src/analyzer/inspections/functional/smells.ts +102 -0
  74. package/src/analyzer/inspections/{generic.ts → generic/generic.ts} +581 -499
  75. package/src/analyzer/inspections/generic/smells.ts +365 -0
  76. package/src/analyzer/inspections/imperative/imperative.ts +101 -0
  77. package/src/analyzer/inspections/imperative/smells.ts +54 -0
  78. package/src/analyzer/inspections/logic/logic.ts +90 -0
  79. package/src/analyzer/inspections/logic/smells.ts +54 -0
  80. package/src/analyzer/inspections/{object.ts → object/object.ts} +264 -282
  81. package/src/analyzer/inspections/object/smells.ts +144 -0
  82. package/src/analyzer/utils.ts +109 -26
  83. package/src/index.ts +3 -2
  84. package/src/interpreter/components/EnvBuilder.ts +202 -97
  85. package/src/interpreter/components/Operations.ts +99 -81
  86. package/src/interpreter/components/PatternMatcher.ts +475 -254
  87. package/src/interpreter/components/RuntimeContext.ts +119 -0
  88. package/src/interpreter/components/TestRunner.ts +151 -0
  89. package/src/interpreter/components/Visitor.ts +1065 -493
  90. package/src/interpreter/components/logic/LogicEngine.ts +519 -0
  91. package/src/interpreter/components/logic/LogicResolver.ts +858 -0
  92. package/src/interpreter/components/logic/LogicTranslator.ts +149 -0
  93. package/src/interpreter/components/runtimes/FunctionRuntime.ts +227 -0
  94. package/src/interpreter/components/runtimes/LazyRuntime.ts +334 -0
  95. package/src/interpreter/components/runtimes/ObjectRuntime.ts +224 -0
  96. package/src/interpreter/errors.ts +47 -47
  97. package/src/interpreter/index.ts +52 -59
  98. package/src/interpreter/trampoline.ts +71 -0
  99. package/src/interpreter/utils.ts +84 -79
  100. package/src/tester/index.ts +128 -0
  101. package/src/utils/helpers.ts +67 -73
  102. package/tests/analyzer/functional.spec.ts +207 -221
  103. package/tests/analyzer/generic.spec.ts +178 -100
  104. package/tests/analyzer/helpers.spec.ts +83 -83
  105. package/tests/analyzer/logic.spec.ts +237 -292
  106. package/tests/analyzer/oop.spec.ts +323 -338
  107. package/tests/analyzer/transitive.spec.ts +166 -0
  108. package/tests/interpreter/EnvBuilder.spec.ts +183 -178
  109. package/tests/interpreter/FunctionRuntime.spec.ts +223 -234
  110. package/tests/interpreter/LazyRuntime.spec.ts +225 -190
  111. package/tests/interpreter/LogicEngine.spec.ts +327 -194
  112. package/tests/interpreter/LogicSubstitution.spec.ts +80 -0
  113. package/tests/interpreter/ObjectRuntime.spec.ts +606 -0
  114. package/tests/interpreter/Operations.spec.ts +220 -220
  115. package/tests/interpreter/PatternSystem.spec.ts +213 -189
  116. package/tests/interpreter/Tests.spec.ts +122 -0
  117. package/tests/interpreter/interpreter.spec.ts +991 -937
  118. package/tests/tester/Tester.spec.ts +153 -0
  119. package/tsconfig.build.json +15 -7
  120. package/tsconfig.json +25 -17
  121. package/dist/analyzer/inspections/functional.d.ts +0 -46
  122. package/dist/analyzer/inspections/functional.js +0 -123
  123. package/dist/analyzer/inspections/generic.js +0 -427
  124. package/dist/analyzer/inspections/imperative.d.ts +0 -37
  125. package/dist/analyzer/inspections/imperative.js +0 -105
  126. package/dist/analyzer/inspections/logic.d.ts +0 -49
  127. package/dist/analyzer/inspections/logic.js +0 -140
  128. package/dist/analyzer/inspections/object.d.ts +0 -83
  129. package/dist/analyzer/inspections/object.js +0 -235
  130. package/dist/interpreter/components/FunctionRuntime.d.ts +0 -8
  131. package/dist/interpreter/components/FunctionRuntime.js +0 -52
  132. package/dist/interpreter/components/LazyRuntime.d.ts +0 -7
  133. package/dist/interpreter/components/LazyRuntime.js +0 -75
  134. package/dist/interpreter/components/LogicEngine.d.ts +0 -21
  135. package/dist/interpreter/components/LogicEngine.js +0 -152
  136. package/dist/interpreter/components/LogicResolver.d.ts +0 -11
  137. package/dist/interpreter/components/LogicResolver.js +0 -87
  138. package/src/analyzer/inspections/functional.ts +0 -159
  139. package/src/analyzer/inspections/imperative.ts +0 -129
  140. package/src/analyzer/inspections/logic.ts +0 -166
  141. package/src/interpreter/components/FunctionRuntime.ts +0 -79
  142. package/src/interpreter/components/LazyRuntime.ts +0 -97
  143. package/src/interpreter/components/LogicEngine.ts +0 -227
  144. package/src/interpreter/components/LogicResolver.ts +0 -130
@@ -0,0 +1,365 @@
1
+ import {
2
+ Application,
3
+ ASTNode,
4
+ BooleanPrimitive,
5
+ Call,
6
+ Catch,
7
+ Equation,
8
+ Expression,
9
+ Function,
10
+ If,
11
+ LogicalBinaryOperation,
12
+ NilPrimitive,
13
+ Print,
14
+ Return,
15
+ Sequence,
16
+ StopTraversalException,
17
+ SymbolPrimitive,
18
+ Variable,
19
+ VariablePattern,
20
+ } from "yukigo-ast";
21
+ import { AutoScoped, ScopedVisitor, VisitorConstructor } from "../../utils.js";
22
+
23
+ function isSequenceEmpty(node: Expression): boolean {
24
+ return node instanceof Sequence && node.statements.length === 0;
25
+ }
26
+
27
+ /**
28
+ * Basic Levenshtein distance for typo detection
29
+ */
30
+ function getLevenshteinDistance(a: string, b: string): number {
31
+ const matrix: number[][] = [];
32
+ for (let i = 0; i <= b.length; i++) matrix[i] = [i];
33
+ for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
34
+
35
+ for (let i = 1; i <= b.length; i++) {
36
+ for (let j = 1; j <= a.length; j++) {
37
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
38
+ matrix[i][j] = matrix[i - 1][j - 1];
39
+ } else {
40
+ matrix[i][j] = Math.min(
41
+ matrix[i - 1][j - 1] + 1,
42
+ matrix[i][j - 1] + 1,
43
+ matrix[i - 1][j] + 1
44
+ );
45
+ }
46
+ }
47
+ }
48
+ return matrix[b.length][a.length];
49
+ }
50
+
51
+ @AutoScoped
52
+ export class DiscardsExceptions extends ScopedVisitor {
53
+ visitCatch(node: Catch): void {
54
+ if (
55
+ !node.body ||
56
+ node.body instanceof NilPrimitive ||
57
+ isSequenceEmpty(node.body)
58
+ ) {
59
+ throw new StopTraversalException();
60
+ }
61
+ }
62
+ }
63
+
64
+ @AutoScoped
65
+ export class DoesConsolePrint extends ScopedVisitor {
66
+ visitPrint(node: Print): void {
67
+ throw new StopTraversalException();
68
+ }
69
+
70
+ visitCall(node: Call): void {
71
+ const name = node.callee.value;
72
+ if (this.isPrintFunc(name)) throw new StopTraversalException();
73
+ }
74
+
75
+ visitApplication(node: Application): void {
76
+ const func = node.functionExpr;
77
+ if (!(func instanceof SymbolPrimitive)) return func.accept(this);
78
+ const name = func.value;
79
+ if (this.isPrintFunc(name)) throw new StopTraversalException();
80
+ }
81
+ private isPrintFunc(name: string): boolean {
82
+ const printFuncs = [
83
+ "print",
84
+ "println",
85
+ "puts",
86
+ "log",
87
+ "console.log",
88
+ "System.out.println",
89
+ "fmt.Println",
90
+ ];
91
+ if (printFuncs.includes(name)) return true;
92
+ return false;
93
+ }
94
+ }
95
+
96
+ @AutoScoped
97
+ export class HasDeclarationTypos extends ScopedVisitor {
98
+ /*
99
+ * Checks if two variables declared in the same scope are suspiciously similar
100
+ * (Levenshtein distance <= 2), indicating a possible typo (e.g., 'count' vs 'conut').
101
+ */
102
+ visitSequence(node: Sequence): void {
103
+ const declaredNames: string[] = [];
104
+
105
+ for (const stmt of node.statements) {
106
+ if (stmt instanceof Variable) declaredNames.push(stmt.identifier.value);
107
+ }
108
+
109
+ for (let i = 0; i < declaredNames.length; i++) {
110
+ for (let j = i + 1; j < declaredNames.length; j++) {
111
+ const a = declaredNames[i];
112
+ const b = declaredNames[j];
113
+ if (a.length > 3 && b.length > 3) {
114
+ if (getLevenshteinDistance(a, b) <= 2) {
115
+ throw new StopTraversalException();
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ @AutoScoped
124
+ export class HasEmptyIfBranches extends ScopedVisitor {
125
+ visitIf(node: If): void {
126
+ const isThenEmpty = !node.then || isSequenceEmpty(node.then);
127
+ const isElseEmpty = node.elseExpr && isSequenceEmpty(node.elseExpr);
128
+ if (isThenEmpty || isElseEmpty) throw new StopTraversalException();
129
+ }
130
+ }
131
+
132
+ @AutoScoped
133
+ export class HasLongParameterList extends ScopedVisitor {
134
+ private readonly maxParams: number;
135
+
136
+ constructor(maxParams: number = 5, scope?: string) {
137
+ super(scope);
138
+ this.maxParams = Number(maxParams);
139
+ }
140
+
141
+ visitEquation(node: Equation): void {
142
+ const params = node.patterns;
143
+ if (params.length > this.maxParams) throw new StopTraversalException();
144
+ }
145
+ }
146
+
147
+ @AutoScoped
148
+ export class HasMisspelledIdentifiers extends ScopedVisitor {
149
+ private readonly dictionary: Set<string>;
150
+
151
+ constructor(dictionaryWords: string[] = [], scope?: string) {
152
+ super(scope);
153
+ // In a real app, load a standard dictionary + jargon here
154
+ this.dictionary = new Set(dictionaryWords.map((w) => w.toLowerCase()));
155
+ }
156
+
157
+ visitVariable(node: Variable): void {
158
+ this.checkSpelling(node.identifier.value);
159
+ }
160
+
161
+ visitFunction(node: Function): void {
162
+ this.checkSpelling(node.identifier.value);
163
+ }
164
+
165
+ private checkSpelling(word: string): void {
166
+ // Skip if no dictionary provided or word is very short
167
+ if (this.dictionary.size === 0 || word.length < 3) return;
168
+
169
+ // Naive camelCase splitter
170
+ const parts = word.split(/(?=[A-Z])|[-_]/);
171
+
172
+ for (const part of parts) {
173
+ if (!this.dictionary.has(part.toLowerCase()))
174
+ throw new StopTraversalException();
175
+ }
176
+ }
177
+ }
178
+
179
+ @AutoScoped
180
+ export class HasRedundantBooleanComparison extends ScopedVisitor {
181
+ visitLogicalBinaryOperation(node: LogicalBinaryOperation): void {
182
+ const isLeftBool = node.left instanceof BooleanPrimitive;
183
+ const isRightBool = node.right instanceof BooleanPrimitive;
184
+ if (isLeftBool || isRightBool) throw new StopTraversalException();
185
+ }
186
+ }
187
+
188
+ @AutoScoped
189
+ export class HasRedundantIf extends ScopedVisitor {
190
+ visitIf(node: If): void {
191
+ if (node.elseExpr) {
192
+ const thenStmt = node.then;
193
+ const elseStmt = node.elseExpr;
194
+
195
+ if (thenStmt instanceof Sequence && elseStmt instanceof Sequence) {
196
+ // Check if returning booleans
197
+ if (
198
+ thenStmt.statements.some(
199
+ (stmt) => stmt instanceof BooleanPrimitive
200
+ ) &&
201
+ elseStmt.statements.some((stmt) => stmt instanceof BooleanPrimitive)
202
+ ) {
203
+ throw new StopTraversalException();
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+
210
+ @AutoScoped
211
+ export class HasRedundantLocalVariableReturn extends ScopedVisitor {
212
+ /*
213
+ * Detects:
214
+ * var x = something;
215
+ * return x;
216
+ */
217
+ visitSequence(node: Sequence): void {
218
+ const stmts = node.statements;
219
+ for (let i = 0; i < stmts.length - 1; i++) {
220
+ const stmt = stmts[i];
221
+ const nextStmt = stmts[i + 1];
222
+
223
+ if (stmt instanceof Variable && nextStmt instanceof Return) {
224
+ if (
225
+ nextStmt.body instanceof SymbolPrimitive &&
226
+ nextStmt.body.value === stmt.identifier.value
227
+ ) {
228
+ throw new StopTraversalException();
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ @AutoScoped
236
+ export class HasTooShortIdentifiers extends ScopedVisitor {
237
+ private readonly minLength: number;
238
+
239
+ constructor(minLength: number = 3, scope?: string) {
240
+ super(scope);
241
+ this.minLength = Number(minLength);
242
+ }
243
+
244
+ visitVariable(node: Variable): void {
245
+ // exclude common counters
246
+ const allowed = ["i", "j", "k", "x", "y", "z", "id"];
247
+ const name = node.identifier.value;
248
+
249
+ if (name.length < this.minLength && !allowed.includes(name)) {
250
+ throw new StopTraversalException();
251
+ }
252
+ }
253
+ }
254
+
255
+ @AutoScoped
256
+ export class HasUsageTypos extends ScopedVisitor {
257
+ visitSequence(node: Sequence): void {
258
+ const declaredNames = new Set<string>();
259
+ const usedNames = new Set<string>();
260
+
261
+ for (const stmt of node.statements) {
262
+ if (stmt instanceof Function || stmt instanceof Variable) {
263
+ declaredNames.add(stmt.identifier.value);
264
+ }
265
+ }
266
+
267
+ for (const stmt of node.statements) {
268
+ if (stmt instanceof Call) usedNames.add(stmt.callee.value);
269
+ }
270
+
271
+ for (const usage of usedNames) {
272
+ if (!declaredNames.has(usage)) {
273
+ for (const decl of declaredNames) {
274
+ if (usage.length > 3 && getLevenshteinDistance(usage, decl) <= 1) {
275
+ throw new StopTraversalException();
276
+ }
277
+ }
278
+ }
279
+ }
280
+ }
281
+ }
282
+
283
+ @AutoScoped
284
+ export class UsesWrongCaseBindings extends ScopedVisitor {
285
+ private readonly caseType: "camel" | "snake" | "pascal";
286
+
287
+ constructor(caseType: string = "camel", scopeName?: string) {
288
+ super(scopeName);
289
+ this.caseType = caseType as any;
290
+ }
291
+
292
+ visitSymbolPrimitive(node: SymbolPrimitive): void {
293
+ this.checkCase(node.value);
294
+ }
295
+
296
+ private checkCase(name: string): void {
297
+ // Ignore operators (names not starting with a letter or underscore)
298
+ if (!/^[a-zA-Z_]/.test(name)) return;
299
+
300
+ let regex: RegExp;
301
+
302
+ switch (this.caseType) {
303
+ case "snake":
304
+ regex = /^[a-z][a-z0-9_]*$/;
305
+ break;
306
+ case "pascal":
307
+ regex = /^[A-Z][a-zA-Z0-9]*$/;
308
+ break;
309
+ case "camel":
310
+ default:
311
+ regex = /^[a-z][a-zA-Z0-9]*$/;
312
+ break;
313
+ }
314
+
315
+ if (!regex.test(name)) {
316
+ throw new StopTraversalException();
317
+ }
318
+ }
319
+ }
320
+
321
+ @AutoScoped
322
+ export class IsLongCode extends ScopedVisitor {
323
+ private readonly maxStatements: number;
324
+
325
+ constructor(maxStatements: number = 50, scopeName?: string) {
326
+ super(scopeName);
327
+ this.maxStatements = Number(maxStatements);
328
+ }
329
+
330
+ visitSequence(node: Sequence): void {
331
+ if (node.statements.length > this.maxStatements) {
332
+ throw new StopTraversalException();
333
+ }
334
+ }
335
+ }
336
+
337
+ @AutoScoped
338
+ export class ShouldInvertIfCondition extends ScopedVisitor {
339
+ visitIf(node: If): void {
340
+ const thenEmpty = !node.then || isSequenceEmpty(node.then);
341
+ const elseNonEmpty = node.elseExpr && !isSequenceEmpty(node.elseExpr);
342
+
343
+ if (thenEmpty && elseNonEmpty) {
344
+ throw new StopTraversalException();
345
+ }
346
+ }
347
+ }
348
+
349
+ export const genericSmells: Record<string, VisitorConstructor> = {
350
+ DiscardsExceptions: DiscardsExceptions,
351
+ DoesConsolePrint: DoesConsolePrint,
352
+ HasDeclarationTypos: HasDeclarationTypos,
353
+ HasEmptyIfBranches: HasEmptyIfBranches,
354
+ HasLongParameterList: HasLongParameterList,
355
+ HasMisspelledIdentifiers: HasMisspelledIdentifiers,
356
+ HasRedundantBooleanComparison: HasRedundantBooleanComparison,
357
+ HasRedundantIf: HasRedundantIf,
358
+ HasRedundantLocalVariableReturn: HasRedundantLocalVariableReturn,
359
+ HasTooShortIdentifiers: HasTooShortIdentifiers,
360
+ HasUsageTypos: HasUsageTypos,
361
+ HasWrongCaseIdentifiers: UsesWrongCaseBindings,
362
+ UsesWrongCaseBindings: UsesWrongCaseBindings,
363
+ IsLongCode: IsLongCode,
364
+ ShouldInvertIfCondition: ShouldInvertIfCondition,
365
+ };
@@ -0,0 +1,101 @@
1
+ import {
2
+ Enumeration,
3
+ ForLoop,
4
+ Function,
5
+ Procedure,
6
+ Repeat,
7
+ StopTraversalException,
8
+ Switch,
9
+ TraverseVisitor,
10
+ While,
11
+ } from "yukigo-ast";
12
+ import { AutoScoped, ScopedVisitor, VisitorConstructor } from "../../utils.js";
13
+
14
+ export class DeclaresEnumeration extends TraverseVisitor {
15
+ private readonly enumName: string;
16
+ constructor(enumName: string) {
17
+ super();
18
+ this.enumName = enumName;
19
+ }
20
+ visitEnumeration(node: Enumeration): void {
21
+ if (node.identifier.value === this.enumName)
22
+ throw new StopTraversalException();
23
+ }
24
+ }
25
+ @AutoScoped
26
+ export class DeclaresProcedure extends ScopedVisitor {
27
+ private readonly procedureName: string;
28
+ constructor(procedureName: string, scope?: string) {
29
+ super(scope);
30
+ this.procedureName = procedureName;
31
+ }
32
+ visitProcedure(node: Procedure): void {
33
+ if (node.identifier.value === this.procedureName)
34
+ throw new StopTraversalException();
35
+ }
36
+ }
37
+
38
+ @AutoScoped
39
+ export class UsesForLoop extends ScopedVisitor {
40
+ constructor(scope?: string) {
41
+ super(scope);
42
+ }
43
+ visitForLoop(node: ForLoop): void {
44
+ throw new StopTraversalException();
45
+ }
46
+ }
47
+ @AutoScoped
48
+ export class UsesWhile extends ScopedVisitor {
49
+ constructor(scope?: string) {
50
+ super(scope);
51
+ }
52
+ visitWhile(node: While): void {
53
+ throw new StopTraversalException();
54
+ }
55
+ }
56
+ @AutoScoped
57
+ export class UsesRepeat extends ScopedVisitor {
58
+ constructor(scope?: string) {
59
+ super(scope);
60
+ }
61
+ visitRepeat(node: Repeat): void {
62
+ throw new StopTraversalException();
63
+ }
64
+ }
65
+ @AutoScoped
66
+ export class UsesLoop extends ScopedVisitor {
67
+ constructor(scope?: string) {
68
+ super(scope);
69
+ }
70
+ visitForLoop(node: ForLoop): void {
71
+ throw new StopTraversalException();
72
+ }
73
+ visitWhile(node: While): void {
74
+ throw new StopTraversalException();
75
+ }
76
+ visitRepeat(node: Repeat): void {
77
+ throw new StopTraversalException();
78
+ }
79
+ // visitForEach(node: ForEach): void {
80
+ // throw StopTraversalException;
81
+ // }
82
+ }
83
+ @AutoScoped
84
+ export class UsesSwitch extends ScopedVisitor {
85
+ constructor(scope?: string) {
86
+ super(scope);
87
+ }
88
+ visitSwitch(node: Switch): void {
89
+ throw new StopTraversalException();
90
+ }
91
+ }
92
+
93
+ export const imperativeInspections: Record<string, VisitorConstructor> = {
94
+ DeclaresEnumeration: DeclaresEnumeration,
95
+ DeclaresProcedure: DeclaresProcedure,
96
+ UsesForLoop: UsesForLoop,
97
+ UsesRepeat: UsesRepeat,
98
+ UsesWhile: UsesWhile,
99
+ UsesLoop: UsesLoop,
100
+ UsesSwitch: UsesSwitch,
101
+ };
@@ -0,0 +1,54 @@
1
+ import {
2
+ Assignment,
3
+ If,
4
+ NumberPrimitive,
5
+ Repeat,
6
+ Return,
7
+ Sequence,
8
+ StopTraversalException,
9
+ While,
10
+ } from "yukigo-ast";
11
+ import { AutoScoped, ScopedVisitor, VisitorConstructor } from "../../utils.js";
12
+
13
+ @AutoScoped
14
+ export class HasAssignmentCondition extends ScopedVisitor {
15
+ visitIf(node: If): void {
16
+ if (node.condition instanceof Assignment)
17
+ throw new StopTraversalException();
18
+ }
19
+ visitWhile(node: While): void {
20
+ if (node.condition instanceof Assignment)
21
+ throw new StopTraversalException();
22
+ }
23
+ }
24
+
25
+ @AutoScoped
26
+ export class HasAssignmentReturn extends ScopedVisitor {
27
+ visitReturn(node: Return): void {
28
+ if (node.body && node.body instanceof Assignment)
29
+ throw new StopTraversalException();
30
+ }
31
+ }
32
+
33
+ @AutoScoped
34
+ export class HasEmptyRepeat extends ScopedVisitor {
35
+ visitRepeat(node: Repeat): void {
36
+ if (node.body instanceof Sequence && node.body.statements.length === 0)
37
+ throw new StopTraversalException();
38
+ }
39
+ }
40
+
41
+ @AutoScoped
42
+ export class HasRedundantRepeat extends ScopedVisitor {
43
+ visitRepeat(node: Repeat): void {
44
+ if (node.count instanceof NumberPrimitive && node.count.value === 1)
45
+ throw new StopTraversalException();
46
+ }
47
+ }
48
+
49
+ export const imperativeSmells: Record<string, VisitorConstructor> = {
50
+ HasAssignmentCondition: HasAssignmentCondition,
51
+ HasAssignmentReturn: HasAssignmentReturn,
52
+ HasEmptyRepeat: HasEmptyRepeat,
53
+ HasRedundantRepeat: HasRedundantRepeat,
54
+ };
@@ -0,0 +1,90 @@
1
+ import {
2
+ Assignment,
3
+ AssignOperation,
4
+ Exist,
5
+ Fact,
6
+ Findall,
7
+ Forall,
8
+ Not,
9
+ Rule,
10
+ StopTraversalException,
11
+ SymbolPrimitive,
12
+ TraverseVisitor,
13
+ UnifyOperation,
14
+ } from "yukigo-ast";
15
+ import { AutoScoped, ScopedVisitor, VisitorConstructor } from "../../utils.js";
16
+ import { isYukigoPrimitive } from "yukigo-ast";
17
+
18
+ export class DeclaresFact extends TraverseVisitor {
19
+ constructor(private readonly target?: string) {
20
+ super();
21
+ }
22
+ visitFact(node: Fact): void {
23
+ const bindingName = node.identifier.value;
24
+ if (!this.target || bindingName === this.target)
25
+ throw new StopTraversalException();
26
+ }
27
+ }
28
+ export class DeclaresRule extends TraverseVisitor {
29
+ constructor(private readonly target?: string) {
30
+ super();
31
+ }
32
+ visitRule(node: Rule): void {
33
+ const bindingName = node.identifier.value;
34
+ if (!this.target || bindingName === this.target)
35
+ throw new StopTraversalException();
36
+ }
37
+ }
38
+ export class DeclaresPredicate extends TraverseVisitor {
39
+ constructor(private readonly target?: string) {
40
+ super();
41
+ }
42
+ visitFact(node: Fact): void {
43
+ new DeclaresFact(this.target).visitFact(node);
44
+ }
45
+ visitRule(node: Rule): void {
46
+ new DeclaresRule(this.target).visitRule(node);
47
+ }
48
+ }
49
+ @AutoScoped
50
+ export class UsesFindall extends ScopedVisitor {
51
+ constructor(scope?: string) {
52
+ super(scope);
53
+ }
54
+ visitFindall(node: Findall): void {
55
+ throw new StopTraversalException();
56
+ }
57
+ }
58
+ @AutoScoped
59
+ export class UsesForall extends ScopedVisitor {
60
+ constructor(scope?: string) {
61
+ super(scope);
62
+ }
63
+ visitForall(node: Forall): void {
64
+ throw new StopTraversalException();
65
+ }
66
+ }
67
+ @AutoScoped
68
+ export class UsesNot extends ScopedVisitor {
69
+ constructor(scope?: string) {
70
+ super(scope);
71
+ }
72
+ visitNot(node: Not): void {
73
+ throw new StopTraversalException();
74
+ }
75
+ visitExist(node: Exist): void {
76
+ if (node.identifier.value === "not") throw new StopTraversalException();
77
+ }
78
+ }
79
+
80
+ export const logicInspections: Record<string, VisitorConstructor> = {
81
+ DeclaresFact: DeclaresFact,
82
+ DeclaresRule: DeclaresRule,
83
+ DeclaresPredicate: DeclaresPredicate,
84
+ UsesFindall: UsesFindall,
85
+ HasFindall: UsesFindall,
86
+ UsesForall: UsesForall,
87
+ HasForall: UsesForall,
88
+ UsesNot: UsesNot,
89
+ HasNot: UsesNot,
90
+ };
@@ -0,0 +1,54 @@
1
+ import {
2
+ AssignOperation,
3
+ Exist,
4
+ isYukigoPrimitive,
5
+ Rule,
6
+ StopTraversalException,
7
+ SymbolPrimitive,
8
+ UnifyOperation,
9
+ } from "yukigo-ast";
10
+ import { AutoScoped, ScopedVisitor, VisitorConstructor } from "../../utils.js";
11
+
12
+ @AutoScoped
13
+ export class HasRedundantReduction extends ScopedVisitor {
14
+ visitAssignOperation(node: AssignOperation): void {
15
+ const left = node.left;
16
+ const right = node.right;
17
+
18
+ if (!(left instanceof SymbolPrimitive)) return;
19
+
20
+ const redundantReductionParameters = isYukigoPrimitive(right);
21
+ const redundantReductionFunctors = right instanceof Exist;
22
+
23
+ const isRedundant =
24
+ redundantReductionParameters || redundantReductionFunctors;
25
+
26
+ if (isRedundant) throw new StopTraversalException();
27
+ }
28
+ }
29
+
30
+ @AutoScoped
31
+ export class UsesUnificationOperator extends ScopedVisitor {
32
+ visitUnifyOperation(node: UnifyOperation): void {
33
+ throw new StopTraversalException();
34
+ }
35
+ }
36
+ @AutoScoped
37
+ export class UsesCut extends ScopedVisitor {
38
+ visitExist(node: Exist): void {
39
+ if (node.identifier.value === "!") throw new StopTraversalException();
40
+ }
41
+ }
42
+ @AutoScoped
43
+ export class UsesFail extends ScopedVisitor {
44
+ visitExist(node: Exist): void {
45
+ if (node.identifier.value === "fail") throw new StopTraversalException();
46
+ }
47
+ }
48
+
49
+ export const logicSmells: Record<string, VisitorConstructor> = {
50
+ HasRedundantReduction: HasRedundantReduction,
51
+ UsesCut: UsesCut,
52
+ UsesFail: UsesFail,
53
+ UsesUnificationOperator: UsesUnificationOperator,
54
+ };