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
package/.mocharc.json
ADDED
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# ❄️ Yukigo (WIP)
|
|
2
|
+
A universal, multi-language, multi-paradigm code analyzer highly inspired in [mulang](https://github.com/mumuki/mulang)
|
|
3
|
+
|
|
4
|
+
> [!WARNING]
|
|
5
|
+
> This project is still in a "work in progress" state. Everything is subject to change :)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Components
|
|
9
|
+
|
|
10
|
+
### **Abstract Semantic Tree:**
|
|
11
|
+
|
|
12
|
+
This is the intermediate representation of any language. Allows us to analyse the semantics of the code independently of the paradigm or the language.
|
|
13
|
+
|
|
14
|
+
### **Inspector:**
|
|
15
|
+
|
|
16
|
+
We provide a set of built-in expectations for analysing code. Also allows to define custom expectations at runtime.
|
|
17
|
+
|
|
18
|
+
### **Translator:**
|
|
19
|
+
|
|
20
|
+
Translation AST-to-Typescript, this allows us to have an equivalent code to run input-output tests everywhere.
|
|
21
|
+
|
|
22
|
+
### **Tester:**
|
|
23
|
+
|
|
24
|
+
Runs tests for the Typescript translated code on an isolated enviroment.
|
|
25
|
+
|
|
26
|
+
# Usage
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
We will be using Haskell as the target language in this example.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
npm install yukigo yukigo-haskell-parser
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
or
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
yarn add yukigo yukigo-haskell-parser
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Example
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { ASTAnalyzer } from "yukigo";
|
|
46
|
+
import { YukigoHaskellParser } from "yukigo-haskell-parser";
|
|
47
|
+
|
|
48
|
+
const code = "doble num = num * 2";
|
|
49
|
+
const expectations = [
|
|
50
|
+
{
|
|
51
|
+
inspection: "HasBinding",
|
|
52
|
+
args: { name: "minimoEntre" },
|
|
53
|
+
expected: false,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
inspection: "HasBinding",
|
|
57
|
+
args: { name: "doble" },
|
|
58
|
+
expected: true,
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const parser = new YukigoHaskellParser();
|
|
63
|
+
const ast = parser.parse(code);
|
|
64
|
+
|
|
65
|
+
const analyser = new ASTAnalyzer(ast);
|
|
66
|
+
const result = analyser.analyse(expectations);
|
|
67
|
+
|
|
68
|
+
console.log(results);
|
|
69
|
+
// [
|
|
70
|
+
// {
|
|
71
|
+
// rule: {
|
|
72
|
+
// inspection: "HasBinding",
|
|
73
|
+
// args: { name: "minimoEntre" },
|
|
74
|
+
// expected: false,
|
|
75
|
+
// },
|
|
76
|
+
// passed: true,
|
|
77
|
+
// actual: false,
|
|
78
|
+
// },
|
|
79
|
+
// {
|
|
80
|
+
// rule: {
|
|
81
|
+
// inspection: "HasBinding",
|
|
82
|
+
// args: { name: "doble" },
|
|
83
|
+
// expected: true,
|
|
84
|
+
// },
|
|
85
|
+
// passed: true,
|
|
86
|
+
// actual: true,
|
|
87
|
+
// },
|
|
88
|
+
// ];
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Example with Mulang's Inspections (in a YAML file)
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { ASTAnalyzer, translateMulangToInspectionRules } from "yukigo";
|
|
95
|
+
import { YukigoHaskellParser } from "yukigo-haskell-parser";
|
|
96
|
+
|
|
97
|
+
const code = `
|
|
98
|
+
squareList :: [Int] -> [Int]
|
|
99
|
+
squareList xs = map (\n -> n * n) xs
|
|
100
|
+
|
|
101
|
+
square :: Int -> Int
|
|
102
|
+
square n = n * n
|
|
103
|
+
|
|
104
|
+
squareList2 :: [Int] -> [Int]
|
|
105
|
+
squareList2 = map square
|
|
106
|
+
`;
|
|
107
|
+
|
|
108
|
+
// Assuming the expectations are in a yaml file. Implement a way to load the actual file.
|
|
109
|
+
const mulangInspections = `
|
|
110
|
+
expectations:
|
|
111
|
+
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
|
112
|
+
binding: squareList
|
|
113
|
+
inspection: HasBinding
|
|
114
|
+
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
|
115
|
+
binding: squareList
|
|
116
|
+
inspection: HasLambdaExpression
|
|
117
|
+
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
|
118
|
+
binding: square
|
|
119
|
+
inspection: HasArithmetic
|
|
120
|
+
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
|
121
|
+
binding: doble
|
|
122
|
+
inspection: Not:HasBinding
|
|
123
|
+
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
|
124
|
+
binding: square
|
|
125
|
+
inspection: Uses:n
|
|
126
|
+
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
|
127
|
+
binding: squareList2
|
|
128
|
+
inspection: Uses:map
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
const expectations = translateMulangToInspectionRules(mulangInspections);
|
|
132
|
+
|
|
133
|
+
const parser = new YukigoHaskellParser();
|
|
134
|
+
const ast = parser.parse(code);
|
|
135
|
+
|
|
136
|
+
const analyser = new ASTAnalyzer(ast);
|
|
137
|
+
const result = analyser.analyse(expectations);
|
|
138
|
+
|
|
139
|
+
console.log(results);
|
|
140
|
+
// [
|
|
141
|
+
// {
|
|
142
|
+
// rule: { inspection: "HasBinding", args: [Object], expected: true },
|
|
143
|
+
// passed: true,
|
|
144
|
+
// actual: true,
|
|
145
|
+
// },
|
|
146
|
+
// {
|
|
147
|
+
// rule: {
|
|
148
|
+
// inspection: "HasLambdaExpression",
|
|
149
|
+
// args: [Object],
|
|
150
|
+
// expected: true,
|
|
151
|
+
// },
|
|
152
|
+
// passed: true,
|
|
153
|
+
// actual: true,
|
|
154
|
+
// },
|
|
155
|
+
// {
|
|
156
|
+
// rule: { inspection: "HasArithmetic", args: [Object], expected: true },
|
|
157
|
+
// passed: true,
|
|
158
|
+
// actual: true,
|
|
159
|
+
// },
|
|
160
|
+
// {
|
|
161
|
+
// rule: { inspection: "HasBinding", args: [Object], expected: false },
|
|
162
|
+
// passed: true,
|
|
163
|
+
// actual: false,
|
|
164
|
+
// },
|
|
165
|
+
// {
|
|
166
|
+
// rule: { inspection: "Uses", args: [Object], expected: true },
|
|
167
|
+
// passed: true,
|
|
168
|
+
// actual: true,
|
|
169
|
+
// },
|
|
170
|
+
// {
|
|
171
|
+
// rule: { inspection: "Uses", args: [Object], expected: true },
|
|
172
|
+
// passed: true,
|
|
173
|
+
// actual: true,
|
|
174
|
+
// },
|
|
175
|
+
// ];
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
# Relevant tools
|
|
179
|
+
- yukigo-ast: A library of AST's node definitions
|
|
180
|
+
|
|
181
|
+
## Tools
|
|
182
|
+
- [CLI](https://github.com/noiseArch/yukigo-cli)
|
|
183
|
+
|
|
184
|
+
## Parsers
|
|
185
|
+
- Haskell
|
|
186
|
+
- Prolog
|
|
187
|
+
|
|
188
|
+
# How to make a parser
|
|
189
|
+
|
|
190
|
+
A yukigo's parser is a class that implements the interface `YukigoParser` which exposes a public method called `parse` and an `errors` array like this:
|
|
191
|
+
```ts
|
|
192
|
+
errors: string[];
|
|
193
|
+
parse: (code: string) => AST;
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The package `yukigo-ast` has all the current supported AST nodes.
|
|
197
|
+
For the grammar, you can use a tool like Jison or Nearley.
|
|
198
|
+
|
|
199
|
+
Here's a tutorial for implementing a small custom language.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AST } from "@yukigo/ast";
|
|
2
|
+
import { InspectionHandler, InspectionMap } from "./utils.js";
|
|
3
|
+
export type AnalysisResult = {
|
|
4
|
+
rule: InspectionRule;
|
|
5
|
+
passed: boolean;
|
|
6
|
+
actual: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
};
|
|
9
|
+
export type InspectionRule = {
|
|
10
|
+
inspection: string;
|
|
11
|
+
binding?: string;
|
|
12
|
+
args: string[];
|
|
13
|
+
expected: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare const defaultInspectionSet: InspectionMap;
|
|
16
|
+
/**
|
|
17
|
+
* The Analyzer class.
|
|
18
|
+
* @remarks
|
|
19
|
+
* The Analyzer is the part of Yukigo which runs the inspections on the AST.
|
|
20
|
+
*/
|
|
21
|
+
export declare class Analyzer {
|
|
22
|
+
/**
|
|
23
|
+
* The set of inspections that are available for the Analyzer.
|
|
24
|
+
* You can load your set of inspections or leave the default one.
|
|
25
|
+
* @defaultValue a default set of inspections for each supported paradigm
|
|
26
|
+
*/
|
|
27
|
+
private inspectionHandlers;
|
|
28
|
+
constructor(inspectionSet?: InspectionMap);
|
|
29
|
+
/**
|
|
30
|
+
* Registers a new custom inspection handler after Analyzer was instantiated.
|
|
31
|
+
* @param name The name of the inspection (e.g., "HasArithmetic").
|
|
32
|
+
* @param handler The function that implements the inspection logic.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Implementation of HasArithmetic inspection
|
|
36
|
+
* export class UsesArithmetic extends TraverseVisitor {
|
|
37
|
+
* visitArithmeticBinaryOperation(node: ArithmeticBinaryOperation): void {
|
|
38
|
+
* throw new StopTraversalException();
|
|
39
|
+
* }
|
|
40
|
+
* visitArithmeticUnaryOperation(node: ArithmeticUnaryOperation): void {
|
|
41
|
+
* throw new StopTraversalException();
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* const analyzer = new ASTAnalyzer(ast);
|
|
45
|
+
* analyzer.registerInspection("HasArithmetic", (node, args) => executeVisitor(node, new UsesArithmetic()));
|
|
46
|
+
*/
|
|
47
|
+
registerInspection(name: string, handler: InspectionHandler): void;
|
|
48
|
+
/**
|
|
49
|
+
* Runs a list of inspection rules against the AST.
|
|
50
|
+
* @param ast The parsed AST.
|
|
51
|
+
* @param rules The array of inspection rules to run.
|
|
52
|
+
* @returns An array of analysis results.
|
|
53
|
+
* @example
|
|
54
|
+
* const rules: InspectionRule[] = [
|
|
55
|
+
* {
|
|
56
|
+
* inspection: "HasBinding",
|
|
57
|
+
* args: { name: "minimoEntre" },
|
|
58
|
+
* expected: false,
|
|
59
|
+
* },
|
|
60
|
+
* {
|
|
61
|
+
* inspection: "HasBinding",
|
|
62
|
+
* args: { name: "squareList" },
|
|
63
|
+
* expected: true,
|
|
64
|
+
* }
|
|
65
|
+
* ]
|
|
66
|
+
* const analyzer = new ASTAnalyzer(ast);
|
|
67
|
+
* const analysisResults = analyzer.analyze(expectations);
|
|
68
|
+
*/
|
|
69
|
+
analyze(ast: AST, rules: InspectionRule[]): AnalysisResult[];
|
|
70
|
+
private runRule;
|
|
71
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { genericInspections } from "./inspections/generic.js";
|
|
2
|
+
import { functionalInspections } from "./inspections/functional.js";
|
|
3
|
+
import { logicInspections } from "./inspections/logic.js";
|
|
4
|
+
import { objectInspections } from "./inspections/object.js";
|
|
5
|
+
import { imperativeInspections } from "./inspections/imperative.js";
|
|
6
|
+
export const defaultInspectionSet = {
|
|
7
|
+
...genericInspections,
|
|
8
|
+
...functionalInspections,
|
|
9
|
+
...logicInspections,
|
|
10
|
+
...objectInspections,
|
|
11
|
+
...imperativeInspections,
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The Analyzer class.
|
|
15
|
+
* @remarks
|
|
16
|
+
* The Analyzer is the part of Yukigo which runs the inspections on the AST.
|
|
17
|
+
*/
|
|
18
|
+
export class Analyzer {
|
|
19
|
+
/**
|
|
20
|
+
* The set of inspections that are available for the Analyzer.
|
|
21
|
+
* You can load your set of inspections or leave the default one.
|
|
22
|
+
* @defaultValue a default set of inspections for each supported paradigm
|
|
23
|
+
*/
|
|
24
|
+
inspectionHandlers = {};
|
|
25
|
+
constructor(inspectionSet) {
|
|
26
|
+
this.inspectionHandlers = inspectionSet ?? defaultInspectionSet;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Registers a new custom inspection handler after Analyzer was instantiated.
|
|
30
|
+
* @param name The name of the inspection (e.g., "HasArithmetic").
|
|
31
|
+
* @param handler The function that implements the inspection logic.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Implementation of HasArithmetic inspection
|
|
35
|
+
* export class UsesArithmetic extends TraverseVisitor {
|
|
36
|
+
* visitArithmeticBinaryOperation(node: ArithmeticBinaryOperation): void {
|
|
37
|
+
* throw new StopTraversalException();
|
|
38
|
+
* }
|
|
39
|
+
* visitArithmeticUnaryOperation(node: ArithmeticUnaryOperation): void {
|
|
40
|
+
* throw new StopTraversalException();
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* const analyzer = new ASTAnalyzer(ast);
|
|
44
|
+
* analyzer.registerInspection("HasArithmetic", (node, args) => executeVisitor(node, new UsesArithmetic()));
|
|
45
|
+
*/
|
|
46
|
+
registerInspection(name, handler) {
|
|
47
|
+
this.inspectionHandlers[name] = handler;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Runs a list of inspection rules against the AST.
|
|
51
|
+
* @param ast The parsed AST.
|
|
52
|
+
* @param rules The array of inspection rules to run.
|
|
53
|
+
* @returns An array of analysis results.
|
|
54
|
+
* @example
|
|
55
|
+
* const rules: InspectionRule[] = [
|
|
56
|
+
* {
|
|
57
|
+
* inspection: "HasBinding",
|
|
58
|
+
* args: { name: "minimoEntre" },
|
|
59
|
+
* expected: false,
|
|
60
|
+
* },
|
|
61
|
+
* {
|
|
62
|
+
* inspection: "HasBinding",
|
|
63
|
+
* args: { name: "squareList" },
|
|
64
|
+
* expected: true,
|
|
65
|
+
* }
|
|
66
|
+
* ]
|
|
67
|
+
* const analyzer = new ASTAnalyzer(ast);
|
|
68
|
+
* const analysisResults = analyzer.analyze(expectations);
|
|
69
|
+
*/
|
|
70
|
+
analyze(ast, rules) {
|
|
71
|
+
const results = [];
|
|
72
|
+
for (const rule of rules) {
|
|
73
|
+
results.push(this.runRule(rule, ast));
|
|
74
|
+
}
|
|
75
|
+
return results;
|
|
76
|
+
}
|
|
77
|
+
runRule(rule, ast) {
|
|
78
|
+
const inspection = this.inspectionHandlers[rule.inspection];
|
|
79
|
+
if (!inspection)
|
|
80
|
+
return {
|
|
81
|
+
rule,
|
|
82
|
+
passed: false,
|
|
83
|
+
actual: false,
|
|
84
|
+
error: "Unknown inspection",
|
|
85
|
+
};
|
|
86
|
+
try {
|
|
87
|
+
let result = false;
|
|
88
|
+
for (const node of ast) {
|
|
89
|
+
if (inspection(node, rule.args, rule.binding)) {
|
|
90
|
+
result = true;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const passed = result === rule.expected;
|
|
95
|
+
return {
|
|
96
|
+
rule,
|
|
97
|
+
passed,
|
|
98
|
+
actual: result,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
rule,
|
|
104
|
+
passed: false,
|
|
105
|
+
actual: false,
|
|
106
|
+
error,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ApplicationPattern, AsPattern, CompositionExpression, ConsPattern, ConstructorPattern, Fact, Function, FunctorPattern, GuardedBody, Lambda, ListComprehension, ListPattern, LiteralPattern, Rule, TraverseVisitor, TuplePattern, UnionPattern, WildcardPattern, Yield } from "@yukigo/ast";
|
|
2
|
+
import { InspectionMap } from "../utils.js";
|
|
3
|
+
export declare class FunctionVisitor extends TraverseVisitor {
|
|
4
|
+
private readonly targetBinding;
|
|
5
|
+
protected isInsideTargetScope: boolean;
|
|
6
|
+
constructor(binding: string);
|
|
7
|
+
visitFunction(node: Function): void;
|
|
8
|
+
}
|
|
9
|
+
export declare class UsesComposition extends FunctionVisitor {
|
|
10
|
+
visitCompositionExpression(node: CompositionExpression): void;
|
|
11
|
+
}
|
|
12
|
+
export declare class UsesAnonymousVariable extends TraverseVisitor {
|
|
13
|
+
private readonly targetBinding;
|
|
14
|
+
protected isInsideTargetScope: boolean;
|
|
15
|
+
constructor(binding: string);
|
|
16
|
+
visitWildcardPattern(node: WildcardPattern): void;
|
|
17
|
+
visitFunction(node: Function): void;
|
|
18
|
+
visitFact(node: Fact): void;
|
|
19
|
+
visitRule(node: Rule): void;
|
|
20
|
+
private enterScope;
|
|
21
|
+
}
|
|
22
|
+
export declare class UsesComprehension extends FunctionVisitor {
|
|
23
|
+
visitListComprehension(node: ListComprehension): void;
|
|
24
|
+
}
|
|
25
|
+
export declare class UsesGuards extends FunctionVisitor {
|
|
26
|
+
visitGuardedBody(node: GuardedBody): void;
|
|
27
|
+
}
|
|
28
|
+
export declare class UsesLambda extends FunctionVisitor {
|
|
29
|
+
visitLambda(node: Lambda): void;
|
|
30
|
+
}
|
|
31
|
+
export declare class UsesYield extends FunctionVisitor {
|
|
32
|
+
visitYield(node: Yield): void;
|
|
33
|
+
}
|
|
34
|
+
export declare class UsesPatternMatching extends FunctionVisitor {
|
|
35
|
+
visitConsPattern(node: ConsPattern): void;
|
|
36
|
+
visitAsPattern(node: AsPattern): void;
|
|
37
|
+
visitListPattern(node: ListPattern): void;
|
|
38
|
+
visitTuplePattern(node: TuplePattern): void;
|
|
39
|
+
visitLiteralPattern(node: LiteralPattern): void;
|
|
40
|
+
visitApplicationPattern(node: ApplicationPattern): void;
|
|
41
|
+
visitConstructorPattern(node: ConstructorPattern): void;
|
|
42
|
+
visitUnionPattern(node: UnionPattern): void;
|
|
43
|
+
visitFunctorPattern(node: FunctorPattern): void;
|
|
44
|
+
visitWildcardPattern(node: WildcardPattern): void;
|
|
45
|
+
}
|
|
46
|
+
export declare const functionalInspections: InspectionMap;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { StopTraversalException, TraverseVisitor, } from "@yukigo/ast";
|
|
2
|
+
import { executeVisitor } from "../utils.js";
|
|
3
|
+
export class FunctionVisitor extends TraverseVisitor {
|
|
4
|
+
targetBinding;
|
|
5
|
+
isInsideTargetScope = false; // this flag helps to check nested functions inside the targetBinding scope
|
|
6
|
+
constructor(binding) {
|
|
7
|
+
super();
|
|
8
|
+
this.targetBinding = binding;
|
|
9
|
+
}
|
|
10
|
+
visitFunction(node) {
|
|
11
|
+
const currentFunctionName = node.identifier.value;
|
|
12
|
+
// if not inside scope then is top-level
|
|
13
|
+
if (!this.isInsideTargetScope) {
|
|
14
|
+
if (!this.targetBinding || currentFunctionName === this.targetBinding) {
|
|
15
|
+
this.isInsideTargetScope = true;
|
|
16
|
+
this.traverseCollection(node.equations);
|
|
17
|
+
this.isInsideTargetScope = false;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// if inside the scope of targetBinding then traverse
|
|
23
|
+
this.traverseCollection(node.equations);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class UsesComposition extends FunctionVisitor {
|
|
27
|
+
visitCompositionExpression(node) {
|
|
28
|
+
throw new StopTraversalException();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class UsesAnonymousVariable extends TraverseVisitor {
|
|
32
|
+
targetBinding;
|
|
33
|
+
isInsideTargetScope = false;
|
|
34
|
+
constructor(binding) {
|
|
35
|
+
super();
|
|
36
|
+
this.targetBinding = binding;
|
|
37
|
+
}
|
|
38
|
+
visitWildcardPattern(node) {
|
|
39
|
+
throw new StopTraversalException();
|
|
40
|
+
}
|
|
41
|
+
visitFunction(node) {
|
|
42
|
+
this.enterScope(node, node.equations);
|
|
43
|
+
}
|
|
44
|
+
visitFact(node) {
|
|
45
|
+
this.enterScope(node, node.patterns);
|
|
46
|
+
}
|
|
47
|
+
visitRule(node) {
|
|
48
|
+
this.enterScope(node, node.patterns);
|
|
49
|
+
}
|
|
50
|
+
enterScope(node, children) {
|
|
51
|
+
const nodeName = node.identifier.value;
|
|
52
|
+
if (!this.isInsideTargetScope) {
|
|
53
|
+
if (!this.targetBinding || nodeName === this.targetBinding) {
|
|
54
|
+
this.isInsideTargetScope = true;
|
|
55
|
+
this.traverseCollection(children);
|
|
56
|
+
this.isInsideTargetScope = false;
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.traverseCollection(children);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export class UsesComprehension extends FunctionVisitor {
|
|
64
|
+
visitListComprehension(node) {
|
|
65
|
+
throw new StopTraversalException();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export class UsesGuards extends FunctionVisitor {
|
|
69
|
+
visitGuardedBody(node) {
|
|
70
|
+
throw new StopTraversalException();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export class UsesLambda extends FunctionVisitor {
|
|
74
|
+
visitLambda(node) {
|
|
75
|
+
throw new StopTraversalException();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export class UsesYield extends FunctionVisitor {
|
|
79
|
+
visitYield(node) {
|
|
80
|
+
throw new StopTraversalException();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export class UsesPatternMatching extends FunctionVisitor {
|
|
84
|
+
visitConsPattern(node) {
|
|
85
|
+
throw new StopTraversalException();
|
|
86
|
+
}
|
|
87
|
+
visitAsPattern(node) {
|
|
88
|
+
throw new StopTraversalException();
|
|
89
|
+
}
|
|
90
|
+
visitListPattern(node) {
|
|
91
|
+
throw new StopTraversalException();
|
|
92
|
+
}
|
|
93
|
+
visitTuplePattern(node) {
|
|
94
|
+
throw new StopTraversalException();
|
|
95
|
+
}
|
|
96
|
+
visitLiteralPattern(node) {
|
|
97
|
+
throw new StopTraversalException();
|
|
98
|
+
}
|
|
99
|
+
visitApplicationPattern(node) {
|
|
100
|
+
throw new StopTraversalException();
|
|
101
|
+
}
|
|
102
|
+
visitConstructorPattern(node) {
|
|
103
|
+
throw new StopTraversalException();
|
|
104
|
+
}
|
|
105
|
+
visitUnionPattern(node) {
|
|
106
|
+
throw new StopTraversalException();
|
|
107
|
+
}
|
|
108
|
+
visitFunctorPattern(node) {
|
|
109
|
+
throw new StopTraversalException();
|
|
110
|
+
}
|
|
111
|
+
visitWildcardPattern(node) {
|
|
112
|
+
throw new StopTraversalException();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export const functionalInspections = {
|
|
116
|
+
UsesComposition: (node, args, binding) => executeVisitor(node, new UsesComposition(binding)),
|
|
117
|
+
UsesAnonymousVariable: (node, args, binding) => executeVisitor(node, new UsesAnonymousVariable(binding)),
|
|
118
|
+
UsesComprehension: (node, args, binding) => executeVisitor(node, new UsesComprehension(binding)),
|
|
119
|
+
UsesGuards: (node, args, binding) => executeVisitor(node, new UsesGuards(binding)),
|
|
120
|
+
UsesLambda: (node, args, binding) => executeVisitor(node, new UsesLambda(binding)),
|
|
121
|
+
UsesYield: (node, args, binding) => executeVisitor(node, new UsesYield(binding)),
|
|
122
|
+
UsesPatternMatching: (node, args, binding) => executeVisitor(node, new UsesPatternMatching(binding)),
|
|
123
|
+
};
|