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,44 @@
|
|
|
1
|
+
import { InterpreterVisitor } from "./components/Visitor.js";
|
|
2
|
+
import { EnvBuilderVisitor } from "./components/EnvBuilder.js";
|
|
3
|
+
import { InterpreterError } from "./errors.js";
|
|
4
|
+
const DefaultConfiguration = {
|
|
5
|
+
lazyLoading: false,
|
|
6
|
+
debug: false,
|
|
7
|
+
outputMode: "first",
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The Interpreter class is responsible for evaluating the Abstract Syntax Tree (AST)
|
|
11
|
+
* generated by the parsers.
|
|
12
|
+
*
|
|
13
|
+
* It manages the global execution environment and delegates the actual evaluation
|
|
14
|
+
* of Expression nodes to a dedicated visitor.
|
|
15
|
+
*/
|
|
16
|
+
export class Interpreter {
|
|
17
|
+
globalEnv;
|
|
18
|
+
config;
|
|
19
|
+
/**
|
|
20
|
+
* @param ast The Abstract Syntax Tree (AST) of the program to be interpreted.
|
|
21
|
+
*/
|
|
22
|
+
constructor(ast, config) {
|
|
23
|
+
this.globalEnv = new EnvBuilderVisitor().build(ast);
|
|
24
|
+
this.config = config ?? DefaultConfiguration;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Evaluates a single Expression node within the context of the global environment.
|
|
28
|
+
*
|
|
29
|
+
* @param expr The root Expression node to be evaluated.
|
|
30
|
+
* @returns The resulting primitive value (number, string, boolean, etc.) after evaluation.
|
|
31
|
+
*/
|
|
32
|
+
evaluate(expr) {
|
|
33
|
+
try {
|
|
34
|
+
const visitor = new InterpreterVisitor(this.globalEnv, this.config);
|
|
35
|
+
const evaluatedExpr = expr.accept(visitor);
|
|
36
|
+
return evaluatedExpr;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error instanceof InterpreterError)
|
|
40
|
+
console.log(error.formatStack());
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Expression, LazyList, PrimitiveValue } from "@yukigo/ast";
|
|
2
|
+
import { Environment, EnvStack } from "./index.js";
|
|
3
|
+
export interface ExpressionEvaluator {
|
|
4
|
+
evaluate(node: Expression): PrimitiveValue;
|
|
5
|
+
}
|
|
6
|
+
export declare function createStream(generator: () => Generator<PrimitiveValue, void, unknown>): LazyList;
|
|
7
|
+
export declare function isArrayOfNumbers(arr: PrimitiveValue[]): arr is number[];
|
|
8
|
+
export declare function generateRange(start: number, end: number, step: number): number[];
|
|
9
|
+
export declare function createEnv(bindings: [string, PrimitiveValue][]): Environment;
|
|
10
|
+
export declare function createGlobalEnv(): EnvStack;
|
|
11
|
+
export declare function pushEnv(env: EnvStack, frame?: Environment): EnvStack;
|
|
12
|
+
export declare function popEnv(env: EnvStack): EnvStack;
|
|
13
|
+
export declare function lookup(env: EnvStack, name: string): PrimitiveValue;
|
|
14
|
+
export declare function define(env: EnvStack, name: string, value: PrimitiveValue): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { UnboundVariable } from "./errors.js";
|
|
2
|
+
export function createStream(generator) {
|
|
3
|
+
return {
|
|
4
|
+
type: "LazyList",
|
|
5
|
+
generator,
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function isArrayOfNumbers(arr) {
|
|
9
|
+
for (const item of arr)
|
|
10
|
+
if (typeof item !== "number")
|
|
11
|
+
return false;
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
export function generateRange(start, end, step) {
|
|
15
|
+
if (step === 0)
|
|
16
|
+
throw new Error("Step cannot be zero in range expression");
|
|
17
|
+
const result = [];
|
|
18
|
+
let current = start;
|
|
19
|
+
if (step > 0) {
|
|
20
|
+
while (current <= end) {
|
|
21
|
+
result.push(current);
|
|
22
|
+
current += step;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
while (current >= end) {
|
|
27
|
+
result.push(current);
|
|
28
|
+
current += step;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
export function createEnv(bindings) {
|
|
34
|
+
const env = new Map();
|
|
35
|
+
for (const [name, value] of bindings)
|
|
36
|
+
env.set(name, value);
|
|
37
|
+
return env;
|
|
38
|
+
}
|
|
39
|
+
export function createGlobalEnv() {
|
|
40
|
+
return [new Map()];
|
|
41
|
+
}
|
|
42
|
+
export function pushEnv(env, frame) {
|
|
43
|
+
return [frame ?? new Map(), ...env];
|
|
44
|
+
}
|
|
45
|
+
export function popEnv(env) {
|
|
46
|
+
return env.slice(1);
|
|
47
|
+
}
|
|
48
|
+
export function lookup(env, name) {
|
|
49
|
+
for (const frame of env) {
|
|
50
|
+
if (frame.has(name))
|
|
51
|
+
return frame.get(name);
|
|
52
|
+
}
|
|
53
|
+
throw new UnboundVariable(name);
|
|
54
|
+
}
|
|
55
|
+
export function define(env, name, value) {
|
|
56
|
+
env[0].set(name, value);
|
|
57
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { InspectionRule } from "../analyzer/index.js";
|
|
2
|
+
import { YukigoPrimitive } from "@yukigo/ast";
|
|
3
|
+
/**
|
|
4
|
+
* Translates Mulang inspections (YAML format) to an array of `InspectionRule` objects.
|
|
5
|
+
* @param mulangYamlString The Mulang inspection syntax as a YAML string.
|
|
6
|
+
* @returns An array of InspectionRule objects.
|
|
7
|
+
*/
|
|
8
|
+
export declare class MulangAdapter {
|
|
9
|
+
translateMulangInspection(mulangInspection: any): InspectionRule;
|
|
10
|
+
translateMulangExpectations(mulangYamlString: string): InspectionRule[];
|
|
11
|
+
}
|
|
12
|
+
export declare const yukigoTsMappings: {
|
|
13
|
+
[key in YukigoPrimitive]: string;
|
|
14
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { parseDocument } from "yaml";
|
|
2
|
+
/**
|
|
3
|
+
* Translates Mulang inspections (YAML format) to an array of `InspectionRule` objects.
|
|
4
|
+
* @param mulangYamlString The Mulang inspection syntax as a YAML string.
|
|
5
|
+
* @returns An array of InspectionRule objects.
|
|
6
|
+
*/
|
|
7
|
+
export class MulangAdapter {
|
|
8
|
+
translateMulangInspection(mulangInspection) {
|
|
9
|
+
if (!mulangInspection ||
|
|
10
|
+
typeof mulangInspection.inspection !== "string" ||
|
|
11
|
+
typeof mulangInspection.binding !== "string") {
|
|
12
|
+
throw Error(`Skipping malformed Mulang inspection entry: ${mulangInspection}`);
|
|
13
|
+
}
|
|
14
|
+
const inspection = mulangInspection.inspection.split(":");
|
|
15
|
+
const expected = inspection[0] !== "Not";
|
|
16
|
+
const args = inspection.slice(expected ? 1 : 2);
|
|
17
|
+
return {
|
|
18
|
+
inspection: expected ? inspection[0] : inspection[1],
|
|
19
|
+
expected,
|
|
20
|
+
args,
|
|
21
|
+
binding: mulangInspection.binding,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
translateMulangExpectations(mulangYamlString) {
|
|
25
|
+
const parsedYaml = parseDocument(mulangYamlString).toJS();
|
|
26
|
+
if (!parsedYaml ||
|
|
27
|
+
!parsedYaml.expectations ||
|
|
28
|
+
!Array.isArray(parsedYaml.expectations)) {
|
|
29
|
+
throw Error("Invalid Mulang YAML structure. Expected 'expectations' to be an array.");
|
|
30
|
+
}
|
|
31
|
+
const inspectionRules = [];
|
|
32
|
+
for (const mulangInspection of parsedYaml.expectations) {
|
|
33
|
+
const inspection = this.translateMulangInspection(mulangInspection);
|
|
34
|
+
inspectionRules.push(inspection);
|
|
35
|
+
}
|
|
36
|
+
return inspectionRules;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export const yukigoTsMappings = {
|
|
40
|
+
YuNumber: "number",
|
|
41
|
+
YuString: "string",
|
|
42
|
+
YuChar: "char",
|
|
43
|
+
YuBoolean: "boolean",
|
|
44
|
+
YuNil: "null",
|
|
45
|
+
// Below some missing mappigs
|
|
46
|
+
YuList: "YuList",
|
|
47
|
+
YuObject: "YuObject",
|
|
48
|
+
YuDict: "YuDict",
|
|
49
|
+
YuTuple: "YuTuple",
|
|
50
|
+
YuSymbol: "YuSymbol",
|
|
51
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "yukigo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc -p tsconfig.build.json",
|
|
13
|
+
"test": "mocha --import=tsx"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"typescript": "^5.9.2",
|
|
17
|
+
"yaml": "^2.8.0",
|
|
18
|
+
"yukigo-ast": "0.1.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/chai": "^5.2.2",
|
|
22
|
+
"@types/mocha": "^10.0.10",
|
|
23
|
+
"@types/node": "^24.2.0",
|
|
24
|
+
"chai": "^6.2.0",
|
|
25
|
+
"mocha": "^11.7.1",
|
|
26
|
+
"ts-node": "^10.9.2",
|
|
27
|
+
"tsx": "^4.21.0",
|
|
28
|
+
"typescript-eslint": "^8.33.1"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { AST } from "yukigo-ast";
|
|
2
|
+
import { genericInspections } from "./inspections/generic.js";
|
|
3
|
+
import { functionalInspections } from "./inspections/functional.js";
|
|
4
|
+
import { logicInspections } from "./inspections/logic.js";
|
|
5
|
+
import { objectInspections } from "./inspections/object.js";
|
|
6
|
+
import { imperativeInspections } from "./inspections/imperative.js";
|
|
7
|
+
import { InspectionHandler, InspectionMap } from "./utils.js";
|
|
8
|
+
|
|
9
|
+
export type AnalysisResult = {
|
|
10
|
+
rule: InspectionRule;
|
|
11
|
+
passed: boolean;
|
|
12
|
+
actual: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type InspectionRule = {
|
|
17
|
+
inspection: string;
|
|
18
|
+
binding?: string;
|
|
19
|
+
args: string[];
|
|
20
|
+
expected: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const defaultInspectionSet: InspectionMap = {
|
|
24
|
+
...genericInspections,
|
|
25
|
+
...functionalInspections,
|
|
26
|
+
...logicInspections,
|
|
27
|
+
...objectInspections,
|
|
28
|
+
...imperativeInspections,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The Analyzer class.
|
|
33
|
+
* @remarks
|
|
34
|
+
* The Analyzer is the part of Yukigo which runs the inspections on the AST.
|
|
35
|
+
*/
|
|
36
|
+
export class Analyzer {
|
|
37
|
+
/**
|
|
38
|
+
* The set of inspections that are available for the Analyzer.
|
|
39
|
+
* You can load your set of inspections or leave the default one.
|
|
40
|
+
* @defaultValue a default set of inspections for each supported paradigm
|
|
41
|
+
*/
|
|
42
|
+
private inspectionHandlers: InspectionMap = {};
|
|
43
|
+
constructor(inspectionSet?: InspectionMap) {
|
|
44
|
+
this.inspectionHandlers = inspectionSet ?? defaultInspectionSet;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Registers a new custom inspection handler after Analyzer was instantiated.
|
|
49
|
+
* @param name The name of the inspection (e.g., "HasArithmetic").
|
|
50
|
+
* @param handler The function that implements the inspection logic.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // Implementation of HasArithmetic inspection
|
|
54
|
+
* export class UsesArithmetic extends TraverseVisitor {
|
|
55
|
+
* visitArithmeticBinaryOperation(node: ArithmeticBinaryOperation): void {
|
|
56
|
+
* throw new StopTraversalException();
|
|
57
|
+
* }
|
|
58
|
+
* visitArithmeticUnaryOperation(node: ArithmeticUnaryOperation): void {
|
|
59
|
+
* throw new StopTraversalException();
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
* const analyzer = new ASTAnalyzer(ast);
|
|
63
|
+
* analyzer.registerInspection("HasArithmetic", (node, args) => executeVisitor(node, new UsesArithmetic()));
|
|
64
|
+
*/
|
|
65
|
+
public registerInspection(name: string, handler: InspectionHandler) {
|
|
66
|
+
this.inspectionHandlers[name] = handler;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Runs a list of inspection rules against the AST.
|
|
71
|
+
* @param ast The parsed AST.
|
|
72
|
+
* @param rules The array of inspection rules to run.
|
|
73
|
+
* @returns An array of analysis results.
|
|
74
|
+
* @example
|
|
75
|
+
* const rules: InspectionRule[] = [
|
|
76
|
+
* {
|
|
77
|
+
* inspection: "HasBinding",
|
|
78
|
+
* args: { name: "minimoEntre" },
|
|
79
|
+
* expected: false,
|
|
80
|
+
* },
|
|
81
|
+
* {
|
|
82
|
+
* inspection: "HasBinding",
|
|
83
|
+
* args: { name: "squareList" },
|
|
84
|
+
* expected: true,
|
|
85
|
+
* }
|
|
86
|
+
* ]
|
|
87
|
+
* const analyzer = new ASTAnalyzer(ast);
|
|
88
|
+
* const analysisResults = analyzer.analyze(expectations);
|
|
89
|
+
*/
|
|
90
|
+
public analyze(ast: AST, rules: InspectionRule[]): AnalysisResult[] {
|
|
91
|
+
const results: AnalysisResult[] = [];
|
|
92
|
+
for (const rule of rules) {
|
|
93
|
+
results.push(this.runRule(rule, ast));
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private runRule(rule: InspectionRule, ast: AST): AnalysisResult {
|
|
99
|
+
const inspection = this.inspectionHandlers[rule.inspection];
|
|
100
|
+
if (!inspection)
|
|
101
|
+
return {
|
|
102
|
+
rule,
|
|
103
|
+
passed: false,
|
|
104
|
+
actual: false,
|
|
105
|
+
error: "Unknown inspection",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
let result = false;
|
|
110
|
+
for (const node of ast) {
|
|
111
|
+
if (inspection(node, rule.args, rule.binding)) {
|
|
112
|
+
result = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const passed = result === rule.expected;
|
|
118
|
+
return {
|
|
119
|
+
rule,
|
|
120
|
+
passed,
|
|
121
|
+
actual: result,
|
|
122
|
+
};
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return {
|
|
125
|
+
rule,
|
|
126
|
+
passed: false,
|
|
127
|
+
actual: false,
|
|
128
|
+
error,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ApplicationPattern,
|
|
3
|
+
AsPattern,
|
|
4
|
+
ASTNode,
|
|
5
|
+
CompositionExpression,
|
|
6
|
+
ConsPattern,
|
|
7
|
+
ConstructorPattern,
|
|
8
|
+
Fact,
|
|
9
|
+
For,
|
|
10
|
+
Function,
|
|
11
|
+
FunctorPattern,
|
|
12
|
+
GuardedBody,
|
|
13
|
+
Lambda,
|
|
14
|
+
ListComprehension,
|
|
15
|
+
ListPattern,
|
|
16
|
+
LiteralPattern,
|
|
17
|
+
Rule,
|
|
18
|
+
StopTraversalException,
|
|
19
|
+
TraverseVisitor,
|
|
20
|
+
TuplePattern,
|
|
21
|
+
UnionPattern,
|
|
22
|
+
WildcardPattern,
|
|
23
|
+
Yield,
|
|
24
|
+
} from "yukigo-ast";
|
|
25
|
+
import { InspectionMap, executeVisitor } from "../utils.js";
|
|
26
|
+
|
|
27
|
+
export class FunctionVisitor extends TraverseVisitor {
|
|
28
|
+
private readonly targetBinding: string;
|
|
29
|
+
protected isInsideTargetScope: boolean = false; // this flag helps to check nested functions inside the targetBinding scope
|
|
30
|
+
constructor(binding: string) {
|
|
31
|
+
super();
|
|
32
|
+
this.targetBinding = binding;
|
|
33
|
+
}
|
|
34
|
+
visitFunction(node: Function): void {
|
|
35
|
+
const currentFunctionName = node.identifier.value;
|
|
36
|
+
|
|
37
|
+
// if not inside scope then is top-level
|
|
38
|
+
if (!this.isInsideTargetScope) {
|
|
39
|
+
if (!this.targetBinding || currentFunctionName === this.targetBinding) {
|
|
40
|
+
this.isInsideTargetScope = true;
|
|
41
|
+
this.traverseCollection(node.equations);
|
|
42
|
+
this.isInsideTargetScope = false;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// if inside the scope of targetBinding then traverse
|
|
48
|
+
this.traverseCollection(node.equations);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class UsesComposition extends FunctionVisitor {
|
|
53
|
+
visitCompositionExpression(node: CompositionExpression): void {
|
|
54
|
+
throw new StopTraversalException();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export class UsesAnonymousVariable extends TraverseVisitor {
|
|
58
|
+
private readonly targetBinding: string;
|
|
59
|
+
protected isInsideTargetScope: boolean = false;
|
|
60
|
+
|
|
61
|
+
constructor(binding: string) {
|
|
62
|
+
super();
|
|
63
|
+
this.targetBinding = binding;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
visitWildcardPattern(node: WildcardPattern): void {
|
|
67
|
+
throw new StopTraversalException();
|
|
68
|
+
}
|
|
69
|
+
visitFunction(node: Function): void {
|
|
70
|
+
this.enterScope(node, node.equations);
|
|
71
|
+
}
|
|
72
|
+
visitFact(node: Fact): void {
|
|
73
|
+
this.enterScope(node, node.patterns);
|
|
74
|
+
}
|
|
75
|
+
visitRule(node: Rule): void {
|
|
76
|
+
this.enterScope(node, node.patterns);
|
|
77
|
+
}
|
|
78
|
+
private enterScope(node: Function | Fact | Rule, children: ASTNode[]): void {
|
|
79
|
+
const nodeName = node.identifier.value;
|
|
80
|
+
if (!this.isInsideTargetScope) {
|
|
81
|
+
if (!this.targetBinding || nodeName === this.targetBinding) {
|
|
82
|
+
this.isInsideTargetScope = true;
|
|
83
|
+
this.traverseCollection(children);
|
|
84
|
+
this.isInsideTargetScope = false;
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.traverseCollection(children);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export class UsesComprehension extends FunctionVisitor {
|
|
92
|
+
visitListComprehension(node: ListComprehension): void {
|
|
93
|
+
throw new StopTraversalException();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export class UsesGuards extends FunctionVisitor {
|
|
97
|
+
visitGuardedBody(node: GuardedBody): void {
|
|
98
|
+
throw new StopTraversalException();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export class UsesLambda extends FunctionVisitor {
|
|
102
|
+
visitLambda(node: Lambda): void {
|
|
103
|
+
throw new StopTraversalException();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export class UsesYield extends FunctionVisitor {
|
|
107
|
+
visitYield(node: Yield): void {
|
|
108
|
+
throw new StopTraversalException();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export class UsesPatternMatching extends FunctionVisitor {
|
|
112
|
+
visitConsPattern(node: ConsPattern): void {
|
|
113
|
+
throw new StopTraversalException();
|
|
114
|
+
}
|
|
115
|
+
visitAsPattern(node: AsPattern): void {
|
|
116
|
+
throw new StopTraversalException();
|
|
117
|
+
}
|
|
118
|
+
visitListPattern(node: ListPattern): void {
|
|
119
|
+
throw new StopTraversalException();
|
|
120
|
+
}
|
|
121
|
+
visitTuplePattern(node: TuplePattern): void {
|
|
122
|
+
throw new StopTraversalException();
|
|
123
|
+
}
|
|
124
|
+
visitLiteralPattern(node: LiteralPattern): void {
|
|
125
|
+
throw new StopTraversalException();
|
|
126
|
+
}
|
|
127
|
+
visitApplicationPattern(node: ApplicationPattern): void {
|
|
128
|
+
throw new StopTraversalException();
|
|
129
|
+
}
|
|
130
|
+
visitConstructorPattern(node: ConstructorPattern): void {
|
|
131
|
+
throw new StopTraversalException();
|
|
132
|
+
}
|
|
133
|
+
visitUnionPattern(node: UnionPattern): void {
|
|
134
|
+
throw new StopTraversalException();
|
|
135
|
+
}
|
|
136
|
+
visitFunctorPattern(node: FunctorPattern): void {
|
|
137
|
+
throw new StopTraversalException();
|
|
138
|
+
}
|
|
139
|
+
visitWildcardPattern(node: WildcardPattern): void {
|
|
140
|
+
throw new StopTraversalException();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const functionalInspections: InspectionMap = {
|
|
145
|
+
UsesComposition: (node, args, binding) =>
|
|
146
|
+
executeVisitor(node, new UsesComposition(binding)),
|
|
147
|
+
UsesAnonymousVariable: (node, args, binding) =>
|
|
148
|
+
executeVisitor(node, new UsesAnonymousVariable(binding)),
|
|
149
|
+
UsesComprehension: (node, args, binding) =>
|
|
150
|
+
executeVisitor(node, new UsesComprehension(binding)),
|
|
151
|
+
UsesGuards: (node, args, binding) =>
|
|
152
|
+
executeVisitor(node, new UsesGuards(binding)),
|
|
153
|
+
UsesLambda: (node, args, binding) =>
|
|
154
|
+
executeVisitor(node, new UsesLambda(binding)),
|
|
155
|
+
UsesYield: (node, args, binding) =>
|
|
156
|
+
executeVisitor(node, new UsesYield(binding)),
|
|
157
|
+
UsesPatternMatching: (node, args, binding) =>
|
|
158
|
+
executeVisitor(node, new UsesPatternMatching(binding)),
|
|
159
|
+
};
|