yukigo 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +3 -3
- package/CHANGELOG.md +26 -0
- package/README.md +193 -199
- package/dist/analyzer/GraphBuilder.d.ts +29 -0
- package/dist/analyzer/GraphBuilder.js +99 -0
- package/dist/analyzer/index.d.ts +11 -23
- package/dist/analyzer/index.js +100 -58
- package/dist/analyzer/inspections/functional/functional.d.ts +44 -0
- package/dist/analyzer/inspections/functional/functional.js +149 -0
- package/dist/analyzer/inspections/functional/smells.d.ts +16 -0
- package/dist/analyzer/inspections/functional/smells.js +98 -0
- package/dist/analyzer/inspections/{generic.d.ts → generic/generic.d.ts} +70 -43
- package/dist/analyzer/inspections/generic/generic.js +604 -0
- package/dist/analyzer/inspections/generic/smells.d.ts +61 -0
- package/dist/analyzer/inspections/generic/smells.js +349 -0
- package/dist/analyzer/inspections/imperative/imperative.d.ts +35 -0
- package/dist/analyzer/inspections/imperative/imperative.js +109 -0
- package/dist/analyzer/inspections/imperative/smells.d.ts +16 -0
- package/dist/analyzer/inspections/imperative/smells.js +58 -0
- package/dist/analyzer/inspections/logic/logic.d.ts +32 -0
- package/dist/analyzer/inspections/logic/logic.js +96 -0
- package/dist/analyzer/inspections/logic/smells.d.ts +15 -0
- package/dist/analyzer/inspections/logic/smells.js +60 -0
- package/dist/analyzer/inspections/object/object.d.ts +88 -0
- package/dist/analyzer/inspections/object/object.js +319 -0
- package/dist/analyzer/inspections/object/smells.d.ts +30 -0
- package/dist/analyzer/inspections/object/smells.js +135 -0
- package/dist/analyzer/utils.d.ts +26 -4
- package/dist/analyzer/utils.js +71 -13
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interpreter/components/EnvBuilder.d.ts +9 -5
- package/dist/interpreter/components/EnvBuilder.js +100 -30
- package/dist/interpreter/components/Operations.d.ts +4 -4
- package/dist/interpreter/components/Operations.js +17 -2
- package/dist/interpreter/components/PatternMatcher.d.ts +47 -17
- package/dist/interpreter/components/PatternMatcher.js +264 -119
- package/dist/interpreter/components/RuntimeContext.d.ts +35 -0
- package/dist/interpreter/components/RuntimeContext.js +93 -0
- package/dist/interpreter/components/TestRunner.d.ts +18 -0
- package/dist/interpreter/components/TestRunner.js +103 -0
- package/dist/interpreter/components/Visitor.d.ts +63 -57
- package/dist/interpreter/components/Visitor.js +508 -173
- package/dist/interpreter/components/logic/LogicEngine.d.ts +29 -0
- package/dist/interpreter/components/logic/LogicEngine.js +259 -0
- package/dist/interpreter/components/logic/LogicResolver.d.ts +53 -0
- package/dist/interpreter/components/logic/LogicResolver.js +471 -0
- package/dist/interpreter/components/logic/LogicTranslator.d.ts +14 -0
- package/dist/interpreter/components/logic/LogicTranslator.js +99 -0
- package/dist/interpreter/components/runtimes/FunctionRuntime.d.ts +12 -0
- package/dist/interpreter/components/runtimes/FunctionRuntime.js +147 -0
- package/dist/interpreter/components/runtimes/LazyRuntime.d.ts +19 -0
- package/dist/interpreter/components/runtimes/LazyRuntime.js +269 -0
- package/dist/interpreter/components/runtimes/ObjectRuntime.d.ts +35 -0
- package/dist/interpreter/components/runtimes/ObjectRuntime.js +126 -0
- package/dist/interpreter/entities.d.ts +105 -0
- package/dist/interpreter/entities.js +96 -0
- package/dist/interpreter/errors.d.ts +1 -1
- package/dist/interpreter/index.d.ts +4 -12
- package/dist/interpreter/index.js +10 -13
- package/dist/interpreter/trampoline.d.ts +17 -0
- package/dist/interpreter/trampoline.js +38 -0
- package/dist/interpreter/utils.d.ts +4 -7
- package/dist/interpreter/utils.js +25 -17
- package/dist/tester/index.d.ts +25 -0
- package/dist/tester/index.js +108 -0
- package/dist/utils/helpers.d.ts +0 -4
- package/dist/utils/helpers.js +20 -24
- package/package.json +2 -2
- package/src/analyzer/GraphBuilder.ts +142 -0
- package/src/analyzer/index.ts +185 -132
- package/src/analyzer/inspections/functional/functional.ts +121 -0
- package/src/analyzer/inspections/functional/smells.ts +102 -0
- package/src/analyzer/inspections/{generic.ts → generic/generic.ts} +581 -499
- package/src/analyzer/inspections/generic/smells.ts +365 -0
- package/src/analyzer/inspections/imperative/imperative.ts +101 -0
- package/src/analyzer/inspections/imperative/smells.ts +54 -0
- package/src/analyzer/inspections/logic/logic.ts +90 -0
- package/src/analyzer/inspections/logic/smells.ts +54 -0
- package/src/analyzer/inspections/{object.ts → object/object.ts} +264 -282
- package/src/analyzer/inspections/object/smells.ts +144 -0
- package/src/analyzer/utils.ts +109 -26
- package/src/index.ts +3 -2
- package/src/interpreter/components/EnvBuilder.ts +202 -97
- package/src/interpreter/components/Operations.ts +99 -81
- package/src/interpreter/components/PatternMatcher.ts +475 -254
- package/src/interpreter/components/RuntimeContext.ts +119 -0
- package/src/interpreter/components/TestRunner.ts +151 -0
- package/src/interpreter/components/Visitor.ts +1065 -493
- package/src/interpreter/components/logic/LogicEngine.ts +519 -0
- package/src/interpreter/components/logic/LogicResolver.ts +858 -0
- package/src/interpreter/components/logic/LogicTranslator.ts +149 -0
- package/src/interpreter/components/runtimes/FunctionRuntime.ts +227 -0
- package/src/interpreter/components/runtimes/LazyRuntime.ts +334 -0
- package/src/interpreter/components/runtimes/ObjectRuntime.ts +224 -0
- package/src/interpreter/errors.ts +47 -47
- package/src/interpreter/index.ts +52 -59
- package/src/interpreter/trampoline.ts +71 -0
- package/src/interpreter/utils.ts +84 -79
- package/src/tester/index.ts +128 -0
- package/src/utils/helpers.ts +67 -73
- package/tests/analyzer/functional.spec.ts +207 -221
- package/tests/analyzer/generic.spec.ts +178 -100
- package/tests/analyzer/helpers.spec.ts +83 -83
- package/tests/analyzer/logic.spec.ts +237 -292
- package/tests/analyzer/oop.spec.ts +323 -338
- package/tests/analyzer/transitive.spec.ts +166 -0
- package/tests/interpreter/EnvBuilder.spec.ts +183 -178
- package/tests/interpreter/FunctionRuntime.spec.ts +223 -234
- package/tests/interpreter/LazyRuntime.spec.ts +225 -190
- package/tests/interpreter/LogicEngine.spec.ts +327 -194
- package/tests/interpreter/LogicSubstitution.spec.ts +80 -0
- package/tests/interpreter/ObjectRuntime.spec.ts +606 -0
- package/tests/interpreter/Operations.spec.ts +220 -220
- package/tests/interpreter/PatternSystem.spec.ts +213 -189
- package/tests/interpreter/Tests.spec.ts +122 -0
- package/tests/interpreter/interpreter.spec.ts +991 -937
- package/tests/tester/Tester.spec.ts +153 -0
- package/tsconfig.build.json +15 -7
- package/tsconfig.json +25 -17
- package/dist/analyzer/inspections/functional.d.ts +0 -46
- package/dist/analyzer/inspections/functional.js +0 -123
- package/dist/analyzer/inspections/generic.js +0 -427
- package/dist/analyzer/inspections/imperative.d.ts +0 -37
- package/dist/analyzer/inspections/imperative.js +0 -105
- package/dist/analyzer/inspections/logic.d.ts +0 -49
- package/dist/analyzer/inspections/logic.js +0 -140
- package/dist/analyzer/inspections/object.d.ts +0 -83
- package/dist/analyzer/inspections/object.js +0 -235
- package/dist/interpreter/components/FunctionRuntime.d.ts +0 -8
- package/dist/interpreter/components/FunctionRuntime.js +0 -52
- package/dist/interpreter/components/LazyRuntime.d.ts +0 -7
- package/dist/interpreter/components/LazyRuntime.js +0 -75
- package/dist/interpreter/components/LogicEngine.d.ts +0 -21
- package/dist/interpreter/components/LogicEngine.js +0 -152
- package/dist/interpreter/components/LogicResolver.d.ts +0 -11
- package/dist/interpreter/components/LogicResolver.js +0 -87
- package/src/analyzer/inspections/functional.ts +0 -159
- package/src/analyzer/inspections/imperative.ts +0 -129
- package/src/analyzer/inspections/logic.ts +0 -166
- package/src/interpreter/components/FunctionRuntime.ts +0 -79
- package/src/interpreter/components/LazyRuntime.ts +0 -97
- package/src/interpreter/components/LogicEngine.ts +0 -227
- package/src/interpreter/components/LogicResolver.ts +0 -130
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Environment, EnvStack, PrimitiveValue } from "yukigo-ast";
|
|
2
|
+
import { FunctionRuntime } from "./runtimes/FunctionRuntime.js";
|
|
3
|
+
import { LazyRuntime } from "./runtimes/LazyRuntime.js";
|
|
4
|
+
import { ObjectRuntime } from "./runtimes/ObjectRuntime.js";
|
|
5
|
+
import { createGlobalEnv } from "../utils.js";
|
|
6
|
+
import { UnboundVariable } from "../errors.js";
|
|
7
|
+
|
|
8
|
+
export const DefaultConfiguration: Required<InterpreterConfig> = {
|
|
9
|
+
lazyLoading: false,
|
|
10
|
+
debug: false,
|
|
11
|
+
outputMode: "first",
|
|
12
|
+
mutability: true,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type LogicSearchMode = "first" | "all" | "stream";
|
|
16
|
+
export interface InterpreterConfig {
|
|
17
|
+
lazyLoading?: boolean;
|
|
18
|
+
debug?: boolean;
|
|
19
|
+
outputMode?: LogicSearchMode;
|
|
20
|
+
mutability?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class UninitializedConfig extends Error {
|
|
24
|
+
constructor() {
|
|
25
|
+
super(
|
|
26
|
+
"GlobalConfig was not initialized. You must call initialize() first.",
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class ReinitializedConfig extends Error {
|
|
31
|
+
constructor() {
|
|
32
|
+
super(
|
|
33
|
+
"GlobalConfig is already initialized. You cannot change it at execution.",
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class RuntimeContext {
|
|
39
|
+
public readonly config: InterpreterConfig | null = null;
|
|
40
|
+
public env: EnvStack;
|
|
41
|
+
public lazyRuntime: LazyRuntime;
|
|
42
|
+
public funcRuntime: FunctionRuntime;
|
|
43
|
+
public objRuntime: ObjectRuntime;
|
|
44
|
+
constructor(config?: InterpreterConfig) {
|
|
45
|
+
this.config = Object.freeze({ ...DefaultConfiguration, ...config });
|
|
46
|
+
this.lazyRuntime = new LazyRuntime(this);
|
|
47
|
+
this.funcRuntime = new FunctionRuntime(this);
|
|
48
|
+
this.objRuntime = new ObjectRuntime(this);
|
|
49
|
+
this.env = createGlobalEnv();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public setEnv(env: EnvStack) {
|
|
53
|
+
this.env = env;
|
|
54
|
+
}
|
|
55
|
+
public isDefined(name: string): boolean {
|
|
56
|
+
let current: EnvStack | null = this.env;
|
|
57
|
+
|
|
58
|
+
while (current !== null) {
|
|
59
|
+
if (current.head.has(name)) return true;
|
|
60
|
+
current = current.tail;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
public pushEnv(frame: Environment = new Map()) {
|
|
66
|
+
this.env = {
|
|
67
|
+
head: frame,
|
|
68
|
+
tail: this.env,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
public replace(
|
|
72
|
+
name: string,
|
|
73
|
+
value: PrimitiveValue,
|
|
74
|
+
onReplace?: (env: Environment) => void,
|
|
75
|
+
): boolean {
|
|
76
|
+
let current: EnvStack | null = this.env;
|
|
77
|
+
|
|
78
|
+
while (current !== null) {
|
|
79
|
+
if (current.head.has(name)) {
|
|
80
|
+
current.head.set(name, value);
|
|
81
|
+
if (onReplace) onReplace(current.head);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
current = current.tail;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
public popEnv() {
|
|
90
|
+
if (!this.env.tail)
|
|
91
|
+
throw new Error(
|
|
92
|
+
"Runtime Error: Cannot pop the global environment scope.",
|
|
93
|
+
);
|
|
94
|
+
this.env = this.env.tail;
|
|
95
|
+
}
|
|
96
|
+
public lookup(name: string): PrimitiveValue {
|
|
97
|
+
let current: EnvStack | null = this.env;
|
|
98
|
+
|
|
99
|
+
while (current !== null) {
|
|
100
|
+
if (current.head.has(name)) return current.head.get(name);
|
|
101
|
+
current = current.tail;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
throw new UnboundVariable(name);
|
|
105
|
+
}
|
|
106
|
+
public remove(name: string): void {
|
|
107
|
+
this.env.head.delete(name);
|
|
108
|
+
}
|
|
109
|
+
public define(name: string, value: PrimitiveValue): void {
|
|
110
|
+
this.env.head.set(name, value);
|
|
111
|
+
}
|
|
112
|
+
public clone(env?: EnvStack): EnvStack {
|
|
113
|
+
const target = env ?? this.env;
|
|
114
|
+
return {
|
|
115
|
+
head: new Map(target.head),
|
|
116
|
+
tail: this.env.tail,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Assert,
|
|
3
|
+
Equality,
|
|
4
|
+
Failure,
|
|
5
|
+
isLazyList,
|
|
6
|
+
PrimitiveValue,
|
|
7
|
+
Test,
|
|
8
|
+
TestGroup,
|
|
9
|
+
TraverseVisitor,
|
|
10
|
+
Truth,
|
|
11
|
+
} from "yukigo-ast";
|
|
12
|
+
import { InterpreterVisitor } from "./Visitor.js";
|
|
13
|
+
import {
|
|
14
|
+
CPSThunk,
|
|
15
|
+
idContinuation,
|
|
16
|
+
trampoline,
|
|
17
|
+
Continuation,
|
|
18
|
+
Thunk,
|
|
19
|
+
} from "../trampoline.js";
|
|
20
|
+
import { LazyRuntime } from "./runtimes/LazyRuntime.js";
|
|
21
|
+
|
|
22
|
+
export class FailedAssert extends Error {
|
|
23
|
+
constructor(
|
|
24
|
+
public actual?: PrimitiveValue,
|
|
25
|
+
public expected?: PrimitiveValue,
|
|
26
|
+
message?: string,
|
|
27
|
+
) {
|
|
28
|
+
super(message || `Assertion failed: expected ${expected}, got ${actual}`);
|
|
29
|
+
this.name = "FailedAssert";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class AssertionVisitor extends TraverseVisitor {
|
|
34
|
+
constructor(
|
|
35
|
+
private interpreter: InterpreterVisitor,
|
|
36
|
+
private negated: boolean,
|
|
37
|
+
private lazyRuntime: LazyRuntime,
|
|
38
|
+
) {
|
|
39
|
+
super();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
visitFailure(node: Failure): CPSThunk<void> {
|
|
43
|
+
return (k) => () => {
|
|
44
|
+
let threw = false;
|
|
45
|
+
let actualError: string | undefined;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// We use trampoline here to execute the function under test.
|
|
49
|
+
// While this is a nested trampoline, it's necessary to capture the error
|
|
50
|
+
// and isolate the test execution from the test runner's CPS flow.
|
|
51
|
+
trampoline(this.interpreter.evaluate(node.func, idContinuation));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
threw = true;
|
|
54
|
+
actualError = (error as Error).message;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this.interpreter.evaluate(node.message, (expectedError) => {
|
|
58
|
+
const passed =
|
|
59
|
+
threw &&
|
|
60
|
+
(expectedError === undefined ||
|
|
61
|
+
actualError?.includes(expectedError as string));
|
|
62
|
+
|
|
63
|
+
if (this.negated === passed) {
|
|
64
|
+
if (!threw) {
|
|
65
|
+
throw new FailedAssert(
|
|
66
|
+
undefined,
|
|
67
|
+
expectedError,
|
|
68
|
+
"Expected code to fail, but it succeeded",
|
|
69
|
+
);
|
|
70
|
+
} else {
|
|
71
|
+
throw new FailedAssert(
|
|
72
|
+
actualError,
|
|
73
|
+
expectedError,
|
|
74
|
+
`Expected error message to contain "${expectedError}", but got "${actualError}"`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return k(undefined);
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
visitEquality(node: Equality): CPSThunk<void> {
|
|
84
|
+
return (k) =>
|
|
85
|
+
this.interpreter.evaluate(node.value, (value) => {
|
|
86
|
+
return () =>
|
|
87
|
+
this.interpreter.evaluate(node.expected, (expected) => {
|
|
88
|
+
this.lazyRuntime.deepEqual(value, expected, (passed) => {
|
|
89
|
+
if (this.negated === passed) {
|
|
90
|
+
throw new FailedAssert(
|
|
91
|
+
value, expected,
|
|
92
|
+
this.negated
|
|
93
|
+
? `Expected ${JSON.stringify(value)} NOT to be equal to ${JSON.stringify(expected)}`
|
|
94
|
+
: `Expected ${JSON.stringify(expected)}, but got ${JSON.stringify(value)}`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return k(undefined);
|
|
98
|
+
})
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
visitTruth(node: Truth): CPSThunk<void> {
|
|
104
|
+
return (k) =>
|
|
105
|
+
this.interpreter.evaluate(node.body, (value) => {
|
|
106
|
+
const isTruthy = Boolean(value);
|
|
107
|
+
if (this.negated === isTruthy) {
|
|
108
|
+
throw new FailedAssert(
|
|
109
|
+
value,
|
|
110
|
+
!this.negated,
|
|
111
|
+
this.negated
|
|
112
|
+
? `Expected value to be falsy, but got ${JSON.stringify(value)}`
|
|
113
|
+
: `Expected value to be truthy, but got ${JSON.stringify(value)}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return k(undefined);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export class TestRunner extends TraverseVisitor {
|
|
122
|
+
constructor(
|
|
123
|
+
public interpreter: InterpreterVisitor,
|
|
124
|
+
private lazyRuntime: LazyRuntime,
|
|
125
|
+
) {
|
|
126
|
+
super();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public run(node: TestGroup | Test | Assert): CPSThunk<void> {
|
|
130
|
+
return node.accept(this) as any;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
visitTestGroup(node: TestGroup): CPSThunk<void> {
|
|
134
|
+
return (k) => this.interpreter.evaluate(node.group, () => k(undefined));
|
|
135
|
+
}
|
|
136
|
+
visitTest(node: Test): CPSThunk<void> {
|
|
137
|
+
return (k) => this.interpreter.evaluate(node.body, () => k(undefined));
|
|
138
|
+
}
|
|
139
|
+
visitAssert(node: Assert): CPSThunk<void> {
|
|
140
|
+
return (k) =>
|
|
141
|
+
this.interpreter.evaluate(node.negated, (negatedVal) => {
|
|
142
|
+
const isNegated = Boolean(negatedVal);
|
|
143
|
+
const visitor = new AssertionVisitor(
|
|
144
|
+
this.interpreter,
|
|
145
|
+
isNegated,
|
|
146
|
+
this.lazyRuntime,
|
|
147
|
+
);
|
|
148
|
+
return (node.body.accept(visitor) as any)(k);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|