yukigo 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/.mocharc.json +3 -3
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +193 -199
  4. package/dist/analyzer/GraphBuilder.d.ts +29 -0
  5. package/dist/analyzer/GraphBuilder.js +99 -0
  6. package/dist/analyzer/index.d.ts +11 -23
  7. package/dist/analyzer/index.js +100 -58
  8. package/dist/analyzer/inspections/functional/functional.d.ts +44 -0
  9. package/dist/analyzer/inspections/functional/functional.js +149 -0
  10. package/dist/analyzer/inspections/functional/smells.d.ts +16 -0
  11. package/dist/analyzer/inspections/functional/smells.js +98 -0
  12. package/dist/analyzer/inspections/{generic.d.ts → generic/generic.d.ts} +70 -43
  13. package/dist/analyzer/inspections/generic/generic.js +604 -0
  14. package/dist/analyzer/inspections/generic/smells.d.ts +61 -0
  15. package/dist/analyzer/inspections/generic/smells.js +349 -0
  16. package/dist/analyzer/inspections/imperative/imperative.d.ts +35 -0
  17. package/dist/analyzer/inspections/imperative/imperative.js +109 -0
  18. package/dist/analyzer/inspections/imperative/smells.d.ts +16 -0
  19. package/dist/analyzer/inspections/imperative/smells.js +58 -0
  20. package/dist/analyzer/inspections/logic/logic.d.ts +32 -0
  21. package/dist/analyzer/inspections/logic/logic.js +96 -0
  22. package/dist/analyzer/inspections/logic/smells.d.ts +15 -0
  23. package/dist/analyzer/inspections/logic/smells.js +60 -0
  24. package/dist/analyzer/inspections/object/object.d.ts +88 -0
  25. package/dist/analyzer/inspections/object/object.js +319 -0
  26. package/dist/analyzer/inspections/object/smells.d.ts +30 -0
  27. package/dist/analyzer/inspections/object/smells.js +135 -0
  28. package/dist/analyzer/utils.d.ts +26 -4
  29. package/dist/analyzer/utils.js +71 -13
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +1 -0
  32. package/dist/interpreter/components/EnvBuilder.d.ts +9 -5
  33. package/dist/interpreter/components/EnvBuilder.js +100 -30
  34. package/dist/interpreter/components/Operations.d.ts +4 -4
  35. package/dist/interpreter/components/Operations.js +17 -2
  36. package/dist/interpreter/components/PatternMatcher.d.ts +47 -17
  37. package/dist/interpreter/components/PatternMatcher.js +264 -119
  38. package/dist/interpreter/components/RuntimeContext.d.ts +35 -0
  39. package/dist/interpreter/components/RuntimeContext.js +93 -0
  40. package/dist/interpreter/components/TestRunner.d.ts +18 -0
  41. package/dist/interpreter/components/TestRunner.js +103 -0
  42. package/dist/interpreter/components/Visitor.d.ts +63 -57
  43. package/dist/interpreter/components/Visitor.js +508 -173
  44. package/dist/interpreter/components/logic/LogicEngine.d.ts +29 -0
  45. package/dist/interpreter/components/logic/LogicEngine.js +259 -0
  46. package/dist/interpreter/components/logic/LogicResolver.d.ts +53 -0
  47. package/dist/interpreter/components/logic/LogicResolver.js +471 -0
  48. package/dist/interpreter/components/logic/LogicTranslator.d.ts +14 -0
  49. package/dist/interpreter/components/logic/LogicTranslator.js +99 -0
  50. package/dist/interpreter/components/runtimes/FunctionRuntime.d.ts +12 -0
  51. package/dist/interpreter/components/runtimes/FunctionRuntime.js +147 -0
  52. package/dist/interpreter/components/runtimes/LazyRuntime.d.ts +19 -0
  53. package/dist/interpreter/components/runtimes/LazyRuntime.js +269 -0
  54. package/dist/interpreter/components/runtimes/ObjectRuntime.d.ts +35 -0
  55. package/dist/interpreter/components/runtimes/ObjectRuntime.js +126 -0
  56. package/dist/interpreter/entities.d.ts +105 -0
  57. package/dist/interpreter/entities.js +96 -0
  58. package/dist/interpreter/errors.d.ts +1 -1
  59. package/dist/interpreter/index.d.ts +4 -12
  60. package/dist/interpreter/index.js +10 -13
  61. package/dist/interpreter/trampoline.d.ts +17 -0
  62. package/dist/interpreter/trampoline.js +38 -0
  63. package/dist/interpreter/utils.d.ts +4 -7
  64. package/dist/interpreter/utils.js +25 -17
  65. package/dist/tester/index.d.ts +25 -0
  66. package/dist/tester/index.js +108 -0
  67. package/dist/utils/helpers.d.ts +0 -4
  68. package/dist/utils/helpers.js +20 -24
  69. package/package.json +2 -2
  70. package/src/analyzer/GraphBuilder.ts +142 -0
  71. package/src/analyzer/index.ts +185 -132
  72. package/src/analyzer/inspections/functional/functional.ts +121 -0
  73. package/src/analyzer/inspections/functional/smells.ts +102 -0
  74. package/src/analyzer/inspections/{generic.ts → generic/generic.ts} +581 -499
  75. package/src/analyzer/inspections/generic/smells.ts +365 -0
  76. package/src/analyzer/inspections/imperative/imperative.ts +101 -0
  77. package/src/analyzer/inspections/imperative/smells.ts +54 -0
  78. package/src/analyzer/inspections/logic/logic.ts +90 -0
  79. package/src/analyzer/inspections/logic/smells.ts +54 -0
  80. package/src/analyzer/inspections/{object.ts → object/object.ts} +264 -282
  81. package/src/analyzer/inspections/object/smells.ts +144 -0
  82. package/src/analyzer/utils.ts +109 -26
  83. package/src/index.ts +3 -2
  84. package/src/interpreter/components/EnvBuilder.ts +202 -97
  85. package/src/interpreter/components/Operations.ts +99 -81
  86. package/src/interpreter/components/PatternMatcher.ts +475 -254
  87. package/src/interpreter/components/RuntimeContext.ts +119 -0
  88. package/src/interpreter/components/TestRunner.ts +151 -0
  89. package/src/interpreter/components/Visitor.ts +1065 -493
  90. package/src/interpreter/components/logic/LogicEngine.ts +519 -0
  91. package/src/interpreter/components/logic/LogicResolver.ts +858 -0
  92. package/src/interpreter/components/logic/LogicTranslator.ts +149 -0
  93. package/src/interpreter/components/runtimes/FunctionRuntime.ts +227 -0
  94. package/src/interpreter/components/runtimes/LazyRuntime.ts +334 -0
  95. package/src/interpreter/components/runtimes/ObjectRuntime.ts +224 -0
  96. package/src/interpreter/errors.ts +47 -47
  97. package/src/interpreter/index.ts +52 -59
  98. package/src/interpreter/trampoline.ts +71 -0
  99. package/src/interpreter/utils.ts +84 -79
  100. package/src/tester/index.ts +128 -0
  101. package/src/utils/helpers.ts +67 -73
  102. package/tests/analyzer/functional.spec.ts +207 -221
  103. package/tests/analyzer/generic.spec.ts +178 -100
  104. package/tests/analyzer/helpers.spec.ts +83 -83
  105. package/tests/analyzer/logic.spec.ts +237 -292
  106. package/tests/analyzer/oop.spec.ts +323 -338
  107. package/tests/analyzer/transitive.spec.ts +166 -0
  108. package/tests/interpreter/EnvBuilder.spec.ts +183 -178
  109. package/tests/interpreter/FunctionRuntime.spec.ts +223 -234
  110. package/tests/interpreter/LazyRuntime.spec.ts +225 -190
  111. package/tests/interpreter/LogicEngine.spec.ts +327 -194
  112. package/tests/interpreter/LogicSubstitution.spec.ts +80 -0
  113. package/tests/interpreter/ObjectRuntime.spec.ts +606 -0
  114. package/tests/interpreter/Operations.spec.ts +220 -220
  115. package/tests/interpreter/PatternSystem.spec.ts +213 -189
  116. package/tests/interpreter/Tests.spec.ts +122 -0
  117. package/tests/interpreter/interpreter.spec.ts +991 -937
  118. package/tests/tester/Tester.spec.ts +153 -0
  119. package/tsconfig.build.json +15 -7
  120. package/tsconfig.json +25 -17
  121. package/dist/analyzer/inspections/functional.d.ts +0 -46
  122. package/dist/analyzer/inspections/functional.js +0 -123
  123. package/dist/analyzer/inspections/generic.js +0 -427
  124. package/dist/analyzer/inspections/imperative.d.ts +0 -37
  125. package/dist/analyzer/inspections/imperative.js +0 -105
  126. package/dist/analyzer/inspections/logic.d.ts +0 -49
  127. package/dist/analyzer/inspections/logic.js +0 -140
  128. package/dist/analyzer/inspections/object.d.ts +0 -83
  129. package/dist/analyzer/inspections/object.js +0 -235
  130. package/dist/interpreter/components/FunctionRuntime.d.ts +0 -8
  131. package/dist/interpreter/components/FunctionRuntime.js +0 -52
  132. package/dist/interpreter/components/LazyRuntime.d.ts +0 -7
  133. package/dist/interpreter/components/LazyRuntime.js +0 -75
  134. package/dist/interpreter/components/LogicEngine.d.ts +0 -21
  135. package/dist/interpreter/components/LogicEngine.js +0 -152
  136. package/dist/interpreter/components/LogicResolver.d.ts +0 -11
  137. package/dist/interpreter/components/LogicResolver.js +0 -87
  138. package/src/analyzer/inspections/functional.ts +0 -159
  139. package/src/analyzer/inspections/imperative.ts +0 -129
  140. package/src/analyzer/inspections/logic.ts +0 -166
  141. package/src/interpreter/components/FunctionRuntime.ts +0 -79
  142. package/src/interpreter/components/LazyRuntime.ts +0 -97
  143. package/src/interpreter/components/LogicEngine.ts +0 -227
  144. package/src/interpreter/components/LogicResolver.ts +0 -130
@@ -0,0 +1,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,4 +1,4 @@
1
- import { SourceLocation } from "@yukigo/ast";
1
+ import { SourceLocation } from "yukigo-ast";
2
2
  export interface ErrorFrame {
3
3
  nodeType: string;
4
4
  loc?: SourceLocation;
@@ -1,13 +1,6 @@
1
- import { PrimitiveValue, Expression, AST } from "@yukigo/ast";
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 globalEnv;
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: Expression): PrimitiveValue;
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
- const DefaultConfiguration = {
5
- lazyLoading: false,
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
- globalEnv;
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.globalEnv = new EnvBuilderVisitor().build(ast);
24
- this.config = config ?? DefaultConfiguration;
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.globalEnv, this.config);
35
- const evaluatedExpr = expr.accept(visitor);
36
- return evaluatedExpr;
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 { Expression, LazyList, PrimitiveValue } from "@yukigo/ast";
2
- import { Environment, EnvStack } from "./index.js";
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: Expression): PrimitiveValue;
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 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;
11
+ export declare function getYukigoType(val: PrimitiveValue): string;
@@ -1,4 +1,4 @@
1
- import { UnboundVariable } from "./errors.js";
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 [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);
40
+ return {
41
+ head: new Map(),
42
+ tail: null,
43
+ };
54
44
  }
55
- export function define(env, name, value) {
56
- env[0].set(name, value);
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
+ }
@@ -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
- };
@@ -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
- typeof mulangInspection.inspection !== "string" ||
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
- !parsedYaml.expectations ||
28
- !Array.isArray(parsedYaml.expectations)) {
29
- throw Error("Invalid Mulang YAML structure. Expected 'expectations' to be an array.");
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 parsedYaml.expectations) {
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.1.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.1.0"
18
+ "yukigo-ast": "^0.2.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/chai": "^5.2.2",