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,105 @@
|
|
|
1
|
+
import { Fact, Rule, Pattern, GuardedBody, UnguardedBody } from "yukigo-ast";
|
|
2
|
+
export type SuccessLogicResult = {
|
|
3
|
+
success: true;
|
|
4
|
+
solutions: Map<string, PrimitiveValue>;
|
|
5
|
+
};
|
|
6
|
+
export type FailedLogicResult = {
|
|
7
|
+
success: false;
|
|
8
|
+
};
|
|
9
|
+
export type LogicResult = SuccessLogicResult | FailedLogicResult;
|
|
10
|
+
export type PrimitiveValue = number | boolean | string | RuntimeFunction | RuntimePredicate | LogicResult | LazyList | null | void | PrimitiveValue[] | RuntimeObject | RuntimeClass | undefined;
|
|
11
|
+
export declare function isLogicResult(value: PrimitiveValue): value is LogicResult;
|
|
12
|
+
export type PrimitiveThunk = () => PrimitiveValue;
|
|
13
|
+
export type Environment = Map<string, PrimitiveValue>;
|
|
14
|
+
export type EnvStack = {
|
|
15
|
+
head: Environment;
|
|
16
|
+
tail: EnvStack | null;
|
|
17
|
+
};
|
|
18
|
+
type RuntimePredicateKind = "Fact" | "Rule" | "Predicate";
|
|
19
|
+
type PredicateEquation = Fact | Rule;
|
|
20
|
+
export interface IRuntimePredicate {
|
|
21
|
+
kind: RuntimePredicateKind;
|
|
22
|
+
identifier: string;
|
|
23
|
+
equations: PredicateEquation[];
|
|
24
|
+
}
|
|
25
|
+
export declare class RuntimePredicate implements IRuntimePredicate {
|
|
26
|
+
kind: RuntimePredicateKind;
|
|
27
|
+
identifier: string;
|
|
28
|
+
equations: PredicateEquation[];
|
|
29
|
+
constructor(kind: RuntimePredicateKind, identifier: string, equations: PredicateEquation[]);
|
|
30
|
+
pushEquation<T extends Fact | Rule>(eq: T): void;
|
|
31
|
+
}
|
|
32
|
+
export declare const isRuntimePredicate: (prim: PrimitiveValue) => prim is RuntimePredicate;
|
|
33
|
+
type Body = GuardedBody[] | UnguardedBody;
|
|
34
|
+
export interface IRuntimeEquation {
|
|
35
|
+
patterns: Pattern[];
|
|
36
|
+
body: Body;
|
|
37
|
+
}
|
|
38
|
+
export declare class RuntimeEquation implements IRuntimeEquation {
|
|
39
|
+
patterns: Pattern[];
|
|
40
|
+
body: Body;
|
|
41
|
+
constructor(patterns: Pattern[], body: Body);
|
|
42
|
+
}
|
|
43
|
+
type PendingArg = PrimitiveValue | PrimitiveThunk;
|
|
44
|
+
/**
|
|
45
|
+
* Runtime Function used in the Interpreter
|
|
46
|
+
*/
|
|
47
|
+
export interface IRuntimeFunction {
|
|
48
|
+
arity: number;
|
|
49
|
+
identifier: string;
|
|
50
|
+
equations: RuntimeEquation[];
|
|
51
|
+
pendingArgs: PendingArg[];
|
|
52
|
+
closure?: EnvStack;
|
|
53
|
+
}
|
|
54
|
+
export declare class RuntimeFunction implements IRuntimeFunction {
|
|
55
|
+
arity: number;
|
|
56
|
+
identifier: string;
|
|
57
|
+
equations: RuntimeEquation[];
|
|
58
|
+
pendingArgs: PendingArg[];
|
|
59
|
+
closure?: EnvStack;
|
|
60
|
+
constructor(arity: number, identifier: string, equations: RuntimeEquation[], pendingArgs: PendingArg[], closure?: EnvStack);
|
|
61
|
+
setPendingArgs(args: PendingArg[]): void;
|
|
62
|
+
}
|
|
63
|
+
export declare function isRuntimeFunction(val: PrimitiveValue): val is RuntimeFunction;
|
|
64
|
+
type Fields = Map<string, PrimitiveValue>;
|
|
65
|
+
type Methods = Map<string, RuntimeFunction>;
|
|
66
|
+
export interface IRuntimeClass {
|
|
67
|
+
identifier: string;
|
|
68
|
+
fields: Fields;
|
|
69
|
+
methods: Methods;
|
|
70
|
+
mixins: string[];
|
|
71
|
+
superclass?: string;
|
|
72
|
+
}
|
|
73
|
+
export declare class RuntimeClass implements IRuntimeClass {
|
|
74
|
+
identifier: string;
|
|
75
|
+
fields: Fields;
|
|
76
|
+
methods: Methods;
|
|
77
|
+
mixins: string[];
|
|
78
|
+
superclass?: string;
|
|
79
|
+
constructor(identifier: string, fields: Fields, methods: Methods, mixins: string[], superclass?: string);
|
|
80
|
+
}
|
|
81
|
+
export declare function isRuntimeClass(val: PrimitiveValue): val is RuntimeClass;
|
|
82
|
+
export interface IRuntimeObject {
|
|
83
|
+
identifier: string;
|
|
84
|
+
className: string;
|
|
85
|
+
fields: Map<string, PrimitiveValue>;
|
|
86
|
+
methods: Map<string, RuntimeFunction>;
|
|
87
|
+
}
|
|
88
|
+
export declare class RuntimeObject implements IRuntimeObject {
|
|
89
|
+
identifier: string;
|
|
90
|
+
className: string;
|
|
91
|
+
fields: Fields;
|
|
92
|
+
methods: Methods;
|
|
93
|
+
constructor(identifier: string, className: string, fields: Fields, methods: Methods);
|
|
94
|
+
}
|
|
95
|
+
export declare function isRuntimeObject(val: PrimitiveValue): val is RuntimeObject;
|
|
96
|
+
export type LazyGenerator = () => Generator<PrimitiveValue, void, unknown>;
|
|
97
|
+
export interface ILazyList {
|
|
98
|
+
readonly generator: LazyGenerator;
|
|
99
|
+
}
|
|
100
|
+
export declare class LazyList implements ILazyList {
|
|
101
|
+
readonly generator: LazyGenerator;
|
|
102
|
+
constructor(generator: LazyGenerator);
|
|
103
|
+
}
|
|
104
|
+
export declare function isLazyList(prim: unknown): prim is LazyList;
|
|
105
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export function isLogicResult(value) {
|
|
2
|
+
return (value &&
|
|
3
|
+
typeof value === "object" &&
|
|
4
|
+
"success" in value &&
|
|
5
|
+
typeof value.success === "boolean" &&
|
|
6
|
+
"solutions" in value &&
|
|
7
|
+
value.solutions instanceof Map);
|
|
8
|
+
}
|
|
9
|
+
export class RuntimePredicate {
|
|
10
|
+
kind;
|
|
11
|
+
identifier;
|
|
12
|
+
equations;
|
|
13
|
+
constructor(kind, identifier, equations) {
|
|
14
|
+
this.kind = kind;
|
|
15
|
+
this.identifier = identifier;
|
|
16
|
+
this.equations = equations;
|
|
17
|
+
}
|
|
18
|
+
pushEquation(eq) {
|
|
19
|
+
this.equations.push(eq);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// TODO: Replace with correct polymorphism
|
|
23
|
+
export const isRuntimePredicate = (prim) => {
|
|
24
|
+
return prim instanceof RuntimePredicate;
|
|
25
|
+
};
|
|
26
|
+
export class RuntimeEquation {
|
|
27
|
+
patterns;
|
|
28
|
+
body;
|
|
29
|
+
constructor(patterns, body) {
|
|
30
|
+
this.patterns = patterns;
|
|
31
|
+
this.body = body;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class RuntimeFunction {
|
|
35
|
+
arity;
|
|
36
|
+
identifier;
|
|
37
|
+
equations;
|
|
38
|
+
pendingArgs;
|
|
39
|
+
closure;
|
|
40
|
+
constructor(arity, identifier, equations, pendingArgs, closure) {
|
|
41
|
+
this.arity = arity;
|
|
42
|
+
this.identifier = identifier;
|
|
43
|
+
this.equations = equations;
|
|
44
|
+
this.pendingArgs = pendingArgs;
|
|
45
|
+
this.closure = closure;
|
|
46
|
+
}
|
|
47
|
+
setPendingArgs(args) {
|
|
48
|
+
this.pendingArgs = args;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// TODO: Replace with correct polymorphism
|
|
52
|
+
export function isRuntimeFunction(val) {
|
|
53
|
+
return val instanceof RuntimeFunction;
|
|
54
|
+
}
|
|
55
|
+
export class RuntimeClass {
|
|
56
|
+
identifier;
|
|
57
|
+
fields;
|
|
58
|
+
methods;
|
|
59
|
+
mixins;
|
|
60
|
+
superclass;
|
|
61
|
+
constructor(identifier, fields, methods, mixins, superclass) {
|
|
62
|
+
this.identifier = identifier;
|
|
63
|
+
this.fields = fields;
|
|
64
|
+
this.methods = methods;
|
|
65
|
+
this.mixins = mixins;
|
|
66
|
+
this.superclass = superclass;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// TODO: prev as before
|
|
70
|
+
export function isRuntimeClass(val) {
|
|
71
|
+
return val instanceof RuntimeClass;
|
|
72
|
+
}
|
|
73
|
+
export class RuntimeObject {
|
|
74
|
+
identifier;
|
|
75
|
+
className;
|
|
76
|
+
fields;
|
|
77
|
+
methods;
|
|
78
|
+
constructor(identifier, className, fields, methods) {
|
|
79
|
+
this.identifier = identifier;
|
|
80
|
+
this.className = className;
|
|
81
|
+
this.fields = fields;
|
|
82
|
+
this.methods = methods;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function isRuntimeObject(val) {
|
|
86
|
+
return val instanceof RuntimeObject;
|
|
87
|
+
}
|
|
88
|
+
export class LazyList {
|
|
89
|
+
generator;
|
|
90
|
+
constructor(generator) {
|
|
91
|
+
this.generator = generator;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function isLazyList(prim) {
|
|
95
|
+
return prim instanceof LazyList;
|
|
96
|
+
}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import { PrimitiveValue,
|
|
1
|
+
import { PrimitiveValue, AST, ASTNode } from "yukigo-ast";
|
|
2
|
+
import { InterpreterConfig } from "./components/RuntimeContext.js";
|
|
2
3
|
export type Bindings = [string, PrimitiveValue][];
|
|
3
|
-
export type LogicSearchMode = "first" | "all" | "stream";
|
|
4
|
-
export type InterpreterConfig = {
|
|
5
|
-
lazyLoading?: boolean;
|
|
6
|
-
debug?: boolean;
|
|
7
|
-
outputMode?: LogicSearchMode;
|
|
8
|
-
};
|
|
9
|
-
export type Environment = Map<string, PrimitiveValue>;
|
|
10
|
-
export type EnvStack = Environment[];
|
|
11
4
|
/**
|
|
12
5
|
* The Interpreter class is responsible for evaluating the Abstract Syntax Tree (AST)
|
|
13
6
|
* generated by the parsers.
|
|
@@ -16,8 +9,7 @@ export type EnvStack = Environment[];
|
|
|
16
9
|
* of Expression nodes to a dedicated visitor.
|
|
17
10
|
*/
|
|
18
11
|
export declare class Interpreter {
|
|
19
|
-
private
|
|
20
|
-
private readonly config;
|
|
12
|
+
private context;
|
|
21
13
|
/**
|
|
22
14
|
* @param ast The Abstract Syntax Tree (AST) of the program to be interpreted.
|
|
23
15
|
*/
|
|
@@ -28,5 +20,5 @@ export declare class Interpreter {
|
|
|
28
20
|
* @param expr The root Expression node to be evaluated.
|
|
29
21
|
* @returns The resulting primitive value (number, string, boolean, etc.) after evaluation.
|
|
30
22
|
*/
|
|
31
|
-
evaluate(expr:
|
|
23
|
+
evaluate(expr: ASTNode): PrimitiveValue;
|
|
32
24
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { InterpreterVisitor } from "./components/Visitor.js";
|
|
2
2
|
import { EnvBuilderVisitor } from "./components/EnvBuilder.js";
|
|
3
3
|
import { InterpreterError } from "./errors.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
debug: false,
|
|
7
|
-
outputMode: "first",
|
|
8
|
-
};
|
|
4
|
+
import { idContinuation, trampoline } from "./trampoline.js";
|
|
5
|
+
import { RuntimeContext, } from "./components/RuntimeContext.js";
|
|
9
6
|
/**
|
|
10
7
|
* The Interpreter class is responsible for evaluating the Abstract Syntax Tree (AST)
|
|
11
8
|
* generated by the parsers.
|
|
@@ -14,14 +11,14 @@ const DefaultConfiguration = {
|
|
|
14
11
|
* of Expression nodes to a dedicated visitor.
|
|
15
12
|
*/
|
|
16
13
|
export class Interpreter {
|
|
17
|
-
|
|
18
|
-
config;
|
|
14
|
+
context;
|
|
19
15
|
/**
|
|
20
16
|
* @param ast The Abstract Syntax Tree (AST) of the program to be interpreted.
|
|
21
17
|
*/
|
|
22
|
-
constructor(ast, config) {
|
|
23
|
-
this.
|
|
24
|
-
|
|
18
|
+
constructor(ast, config = {}) {
|
|
19
|
+
this.context = new RuntimeContext(config);
|
|
20
|
+
const builder = new EnvBuilderVisitor(this.context);
|
|
21
|
+
builder.build(ast);
|
|
25
22
|
}
|
|
26
23
|
/**
|
|
27
24
|
* Evaluates a single Expression node within the context of the global environment.
|
|
@@ -31,9 +28,9 @@ export class Interpreter {
|
|
|
31
28
|
*/
|
|
32
29
|
evaluate(expr) {
|
|
33
30
|
try {
|
|
34
|
-
const visitor = new InterpreterVisitor(this.
|
|
35
|
-
const
|
|
36
|
-
return
|
|
31
|
+
const visitor = new InterpreterVisitor(this.context);
|
|
32
|
+
const evaluatedCPS = expr.accept(visitor);
|
|
33
|
+
return trampoline(evaluatedCPS(idContinuation));
|
|
37
34
|
}
|
|
38
35
|
catch (error) {
|
|
39
36
|
if (error instanceof InterpreterError)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PrimitiveValue } from "yukigo-ast";
|
|
2
|
+
export type CallableThunk<T> = () => Thunk<T>;
|
|
3
|
+
export type Thunk<T> = T | CallableThunk<T>;
|
|
4
|
+
export type Continuation<T, R = any> = (value: T) => Thunk<R>;
|
|
5
|
+
export declare const idContinuation: Continuation<PrimitiveValue>;
|
|
6
|
+
export declare function trampoline<T>(thunk: Thunk<T>): T;
|
|
7
|
+
/**
|
|
8
|
+
* A specialized Thunk type for our CPS visitor.
|
|
9
|
+
* This is a function that, when called, takes the continuation for the current node's evaluation
|
|
10
|
+
* and returns the actual Thunk chain for that node.
|
|
11
|
+
* This effectively injects the continuation *after* the `visit` method has returned.
|
|
12
|
+
*/
|
|
13
|
+
export type CPSThunk<T, R = any> = (k: Continuation<T, R>) => Thunk<R>;
|
|
14
|
+
export declare function makeCPSThunk<T>(fn: (k: Continuation<T>) => Thunk<T>): CPSThunk<T>;
|
|
15
|
+
export declare function bindCPS<A, B, R>(cpsA: CPSThunk<A, R>, f: (valueA: A) => CPSThunk<B, R>): CPSThunk<B, R>;
|
|
16
|
+
export declare function valueToCPS<T, R>(value: T): CPSThunk<T, R>;
|
|
17
|
+
export declare function thunkToCPS<T>(thunk: Thunk<T>): CPSThunk<T>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const isCallableThunk = (thunk) => typeof thunk === "function";
|
|
2
|
+
// The identity continuation: simply returns the value as a resolved Thunk.
|
|
3
|
+
// This is used for the very last step of a computation or when a value is final.
|
|
4
|
+
export const idContinuation = (value) => value;
|
|
5
|
+
// The trampoline function executes a chain of Thunks until a final non-function value is reached.
|
|
6
|
+
export function trampoline(thunk) {
|
|
7
|
+
let result = thunk;
|
|
8
|
+
while (isCallableThunk(result))
|
|
9
|
+
result = result();
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
// Helper to create a CPSThunk for visitor methods.
|
|
13
|
+
// Visitor methods will call this, providing a function that knows how to use 'k'.
|
|
14
|
+
export function makeCPSThunk(fn) {
|
|
15
|
+
return fn;
|
|
16
|
+
}
|
|
17
|
+
// Helper to compose asynchronous/CPS operations.
|
|
18
|
+
// `cpsA` is a CPSThunk that eventually produces a value A.
|
|
19
|
+
// `f` is a function that takes A and returns a CPSThunk that eventually produces B.
|
|
20
|
+
export function bindCPS(cpsA, f) {
|
|
21
|
+
return (k) => {
|
|
22
|
+
return () => cpsA((valueA) => {
|
|
23
|
+
// f(valueA) returns a CPSThunk<B, R>
|
|
24
|
+
// calling that with 'k' returns Thunk<R>
|
|
25
|
+
return f(valueA)(k);
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Helper to lift a direct value into a CPSThunk
|
|
30
|
+
export function valueToCPS(value) {
|
|
31
|
+
return (k) => () => k(value);
|
|
32
|
+
}
|
|
33
|
+
// Helper to lift a regular Thunk into a CPSThunk (if it resolves a simple Thunk)
|
|
34
|
+
export function thunkToCPS(thunk) {
|
|
35
|
+
return (k) => {
|
|
36
|
+
return () => k(trampoline(thunk)); // Trampoline the simple thunk and pass its result to k
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { LazyList, PrimitiveValue, Environment, EnvStack, ASTNode } from "yukigo-ast";
|
|
2
|
+
import { Continuation, Thunk } from "./trampoline.js";
|
|
3
3
|
export interface ExpressionEvaluator {
|
|
4
|
-
evaluate(node:
|
|
4
|
+
evaluate<R = PrimitiveValue>(node: ASTNode, cont: Continuation<PrimitiveValue, R>): Thunk<R>;
|
|
5
5
|
}
|
|
6
6
|
export declare function createStream(generator: () => Generator<PrimitiveValue, void, unknown>): LazyList;
|
|
7
7
|
export declare function isArrayOfNumbers(arr: PrimitiveValue[]): arr is number[];
|
|
8
8
|
export declare function generateRange(start: number, end: number, step: number): number[];
|
|
9
9
|
export declare function createEnv(bindings: [string, PrimitiveValue][]): Environment;
|
|
10
10
|
export declare function createGlobalEnv(): EnvStack;
|
|
11
|
-
export declare function
|
|
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;
|
|
11
|
+
export declare function getYukigoType(val: PrimitiveValue): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isRuntimeFunction, isRuntimeObject, isLazyList, isRuntimeClass, isRuntimePredicate, } from "yukigo-ast";
|
|
2
2
|
export function createStream(generator) {
|
|
3
3
|
return {
|
|
4
4
|
type: "LazyList",
|
|
@@ -37,21 +37,29 @@ export function createEnv(bindings) {
|
|
|
37
37
|
return env;
|
|
38
38
|
}
|
|
39
39
|
export function createGlobalEnv() {
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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);
|
|
40
|
+
return {
|
|
41
|
+
head: new Map(),
|
|
42
|
+
tail: null,
|
|
43
|
+
};
|
|
54
44
|
}
|
|
55
|
-
export function
|
|
56
|
-
|
|
45
|
+
export function getYukigoType(val) {
|
|
46
|
+
if (val === null || val === undefined)
|
|
47
|
+
return "YuNil";
|
|
48
|
+
if (typeof val === "number")
|
|
49
|
+
return "YuNumber";
|
|
50
|
+
if (typeof val === "boolean")
|
|
51
|
+
return "YuBoolean";
|
|
52
|
+
if (typeof val === "string")
|
|
53
|
+
return val.length === 1 ? "YuChar" : "YuString";
|
|
54
|
+
if (Array.isArray(val) || isLazyList(val))
|
|
55
|
+
return "YuList";
|
|
56
|
+
if (isRuntimeFunction(val))
|
|
57
|
+
return "YuFunction";
|
|
58
|
+
if (isRuntimeObject(val))
|
|
59
|
+
return "YuObject";
|
|
60
|
+
if (isRuntimeClass(val))
|
|
61
|
+
return "YuClass";
|
|
62
|
+
if (isRuntimePredicate(val))
|
|
63
|
+
return "YuPredicate";
|
|
64
|
+
return "YuUnknown";
|
|
57
65
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AST } from "yukigo-ast";
|
|
2
|
+
import { InterpreterConfig } from "../interpreter/components/RuntimeContext.js";
|
|
3
|
+
export type TestStatus = "passed" | "failed" | "error";
|
|
4
|
+
export interface TestReport {
|
|
5
|
+
name: string;
|
|
6
|
+
status: TestStatus;
|
|
7
|
+
message?: string;
|
|
8
|
+
duration?: number;
|
|
9
|
+
children?: TestReport[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The Tester class provides a high-level API for executing test nodes
|
|
13
|
+
* (Tests and TestGroups) within an AST and generating a detailed report.
|
|
14
|
+
*/
|
|
15
|
+
export declare class Tester {
|
|
16
|
+
private ast;
|
|
17
|
+
private config?;
|
|
18
|
+
constructor(ast: AST, config?: InterpreterConfig);
|
|
19
|
+
/**
|
|
20
|
+
* Executes all Test and TestGroup nodes found at the top level of the provided AST.
|
|
21
|
+
* @param nodes The AST (or subset of nodes) to scan for tests.
|
|
22
|
+
* @returns An array of TestReport objects for each top-level test/group.
|
|
23
|
+
*/
|
|
24
|
+
test(nodes: AST): TestReport[];
|
|
25
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Test, TestGroup, TraverseVisitor, } from "yukigo-ast";
|
|
2
|
+
import { Interpreter } from "../interpreter/index.js";
|
|
3
|
+
import { FailedAssert } from "../interpreter/components/TestRunner.js";
|
|
4
|
+
class TestExecutor extends TraverseVisitor {
|
|
5
|
+
interpreter;
|
|
6
|
+
report = null;
|
|
7
|
+
constructor(interpreter) {
|
|
8
|
+
super();
|
|
9
|
+
this.interpreter = interpreter;
|
|
10
|
+
}
|
|
11
|
+
visitTest(node) {
|
|
12
|
+
const name = this.evaluateName(node.name);
|
|
13
|
+
const start = Date.now();
|
|
14
|
+
try {
|
|
15
|
+
//this.bindParameters(node);
|
|
16
|
+
this.interpreter.evaluate(node);
|
|
17
|
+
this.report = {
|
|
18
|
+
name,
|
|
19
|
+
status: "passed",
|
|
20
|
+
duration: Date.now() - start,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
this.report = this.handleError(name, start, error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/* private bindParameters(node: Test): void {
|
|
28
|
+
if (!node.args || node.args.length === 0) return;
|
|
29
|
+
|
|
30
|
+
for (const pattern of node.args) {
|
|
31
|
+
if (pattern instanceof VariablePattern) {
|
|
32
|
+
this.interpreter.define(pattern.name.value, null);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} */
|
|
36
|
+
visitTestGroup(node) {
|
|
37
|
+
const name = this.evaluateName(node.name);
|
|
38
|
+
const start = Date.now();
|
|
39
|
+
try {
|
|
40
|
+
const children = [];
|
|
41
|
+
for (const stmt of node.group.statements) {
|
|
42
|
+
if (stmt instanceof Test || stmt instanceof TestGroup) {
|
|
43
|
+
const visitor = new TestExecutor(this.interpreter);
|
|
44
|
+
stmt.accept(visitor);
|
|
45
|
+
if (visitor.report)
|
|
46
|
+
children.push(visitor.report);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.interpreter.evaluate(stmt);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const anyFailed = children.some((c) => c.status !== "passed");
|
|
53
|
+
this.report = {
|
|
54
|
+
name,
|
|
55
|
+
status: anyFailed ? "failed" : "passed",
|
|
56
|
+
duration: Date.now() - start,
|
|
57
|
+
children,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
this.report = this.handleError(name, start, error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
evaluateName(nameExpr) {
|
|
65
|
+
try {
|
|
66
|
+
return String(this.interpreter.evaluate(nameExpr));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return "Unknown Test";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
handleError(name, start, error) {
|
|
73
|
+
const duration = Date.now() - start;
|
|
74
|
+
if (error instanceof FailedAssert)
|
|
75
|
+
return { name, status: "failed", message: error.message, duration };
|
|
76
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
77
|
+
return { name, status: "error", message, duration };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The Tester class provides a high-level API for executing test nodes
|
|
82
|
+
* (Tests and TestGroups) within an AST and generating a detailed report.
|
|
83
|
+
*/
|
|
84
|
+
export class Tester {
|
|
85
|
+
ast;
|
|
86
|
+
config;
|
|
87
|
+
constructor(ast, config) {
|
|
88
|
+
this.ast = ast;
|
|
89
|
+
this.config = config;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Executes all Test and TestGroup nodes found at the top level of the provided AST.
|
|
93
|
+
* @param nodes The AST (or subset of nodes) to scan for tests.
|
|
94
|
+
* @returns An array of TestReport objects for each top-level test/group.
|
|
95
|
+
*/
|
|
96
|
+
test(nodes) {
|
|
97
|
+
const reports = [];
|
|
98
|
+
for (const node of nodes) {
|
|
99
|
+
if (node instanceof Test || node instanceof TestGroup) {
|
|
100
|
+
const interpreter = new Interpreter(this.ast, this.config);
|
|
101
|
+
const visitor = new TestExecutor(interpreter);
|
|
102
|
+
node.accept(visitor);
|
|
103
|
+
reports.push(visitor.report);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return reports;
|
|
107
|
+
}
|
|
108
|
+
}
|
package/dist/utils/helpers.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { InspectionRule } from "../analyzer/index.js";
|
|
2
|
-
import { YukigoPrimitive } from "@yukigo/ast";
|
|
3
2
|
/**
|
|
4
3
|
* Translates Mulang inspections (YAML format) to an array of `InspectionRule` objects.
|
|
5
4
|
* @param mulangYamlString The Mulang inspection syntax as a YAML string.
|
|
@@ -9,6 +8,3 @@ export declare class MulangAdapter {
|
|
|
9
8
|
translateMulangInspection(mulangInspection: any): InspectionRule;
|
|
10
9
|
translateMulangExpectations(mulangYamlString: string): InspectionRule[];
|
|
11
10
|
}
|
|
12
|
-
export declare const yukigoTsMappings: {
|
|
13
|
-
[key in YukigoPrimitive]: string;
|
|
14
|
-
};
|
package/dist/utils/helpers.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { parseDocument } from "yaml";
|
|
2
|
+
const isValidFormat = (inspection) => typeof inspection === "object" &&
|
|
3
|
+
"inspection" in inspection &&
|
|
4
|
+
"binding" in inspection;
|
|
2
5
|
/**
|
|
3
6
|
* Translates Mulang inspections (YAML format) to an array of `InspectionRule` objects.
|
|
4
7
|
* @param mulangYamlString The Mulang inspection syntax as a YAML string.
|
|
@@ -6,13 +9,10 @@ import { parseDocument } from "yaml";
|
|
|
6
9
|
*/
|
|
7
10
|
export class MulangAdapter {
|
|
8
11
|
translateMulangInspection(mulangInspection) {
|
|
9
|
-
if (!mulangInspection
|
|
10
|
-
|
|
11
|
-
typeof mulangInspection.binding !== "string") {
|
|
12
|
-
throw Error(`Skipping malformed Mulang inspection entry: ${mulangInspection}`);
|
|
13
|
-
}
|
|
12
|
+
if (!isValidFormat(mulangInspection))
|
|
13
|
+
throw new Error(`Skipping malformed Mulang inspection entry: ${mulangInspection}`);
|
|
14
14
|
const inspection = mulangInspection.inspection.split(":");
|
|
15
|
-
const expected = inspection[0] !== "Not";
|
|
15
|
+
const expected = inspection[0] !== "Not" && inspection[0] !== "Except";
|
|
16
16
|
const args = inspection.slice(expected ? 1 : 2);
|
|
17
17
|
return {
|
|
18
18
|
inspection: expected ? inspection[0] : inspection[1],
|
|
@@ -22,30 +22,26 @@ export class MulangAdapter {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
translateMulangExpectations(mulangYamlString) {
|
|
25
|
+
if (!mulangYamlString)
|
|
26
|
+
return [];
|
|
25
27
|
const parsedYaml = parseDocument(mulangYamlString).toJS();
|
|
26
|
-
if (!parsedYaml
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
if (!parsedYaml)
|
|
29
|
+
return [];
|
|
30
|
+
let expectations = [];
|
|
31
|
+
if (Array.isArray(parsedYaml)) {
|
|
32
|
+
expectations = parsedYaml;
|
|
33
|
+
}
|
|
34
|
+
else if (Array.isArray(parsedYaml.expectations)) {
|
|
35
|
+
expectations = parsedYaml.expectations;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
throw new Error("Invalid Mulang YAML structure. Expected 'expectations' to be an array.");
|
|
30
39
|
}
|
|
31
40
|
const inspectionRules = [];
|
|
32
|
-
for (const mulangInspection of
|
|
41
|
+
for (const mulangInspection of expectations) {
|
|
33
42
|
const inspection = this.translateMulangInspection(mulangInspection);
|
|
34
43
|
inspectionRules.push(inspection);
|
|
35
44
|
}
|
|
36
45
|
return inspectionRules;
|
|
37
46
|
}
|
|
38
47
|
}
|
|
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yukigo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"typescript": "^5.9.2",
|
|
17
17
|
"yaml": "^2.8.0",
|
|
18
|
-
"yukigo-ast": "0.
|
|
18
|
+
"yukigo-ast": "^0.2.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/chai": "^5.2.2",
|