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.
Files changed (78) hide show
  1. package/.mocharc.json +4 -0
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +199 -0
  4. package/dist/analyzer/index.d.ts +71 -0
  5. package/dist/analyzer/index.js +110 -0
  6. package/dist/analyzer/inspections/functional.d.ts +46 -0
  7. package/dist/analyzer/inspections/functional.js +123 -0
  8. package/dist/analyzer/inspections/generic.d.ts +151 -0
  9. package/dist/analyzer/inspections/generic.js +427 -0
  10. package/dist/analyzer/inspections/imperative.d.ts +37 -0
  11. package/dist/analyzer/inspections/imperative.js +105 -0
  12. package/dist/analyzer/inspections/logic.d.ts +49 -0
  13. package/dist/analyzer/inspections/logic.js +140 -0
  14. package/dist/analyzer/inspections/object.d.ts +83 -0
  15. package/dist/analyzer/inspections/object.js +235 -0
  16. package/dist/analyzer/utils.d.ts +4 -0
  17. package/dist/analyzer/utils.js +16 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +3 -0
  20. package/dist/interpreter/components/EnvBuilder.d.ts +16 -0
  21. package/dist/interpreter/components/EnvBuilder.js +78 -0
  22. package/dist/interpreter/components/FunctionRuntime.d.ts +8 -0
  23. package/dist/interpreter/components/FunctionRuntime.js +52 -0
  24. package/dist/interpreter/components/LazyRuntime.d.ts +7 -0
  25. package/dist/interpreter/components/LazyRuntime.js +75 -0
  26. package/dist/interpreter/components/LogicEngine.d.ts +21 -0
  27. package/dist/interpreter/components/LogicEngine.js +152 -0
  28. package/dist/interpreter/components/LogicResolver.d.ts +11 -0
  29. package/dist/interpreter/components/LogicResolver.js +87 -0
  30. package/dist/interpreter/components/Operations.d.ts +14 -0
  31. package/dist/interpreter/components/Operations.js +69 -0
  32. package/dist/interpreter/components/PatternMatcher.d.ts +41 -0
  33. package/dist/interpreter/components/PatternMatcher.js +206 -0
  34. package/dist/interpreter/components/Visitor.d.ts +64 -0
  35. package/dist/interpreter/components/Visitor.js +299 -0
  36. package/dist/interpreter/errors.d.ts +19 -0
  37. package/dist/interpreter/errors.js +36 -0
  38. package/dist/interpreter/index.d.ts +32 -0
  39. package/dist/interpreter/index.js +44 -0
  40. package/dist/interpreter/utils.d.ts +14 -0
  41. package/dist/interpreter/utils.js +57 -0
  42. package/dist/utils/helpers.d.ts +14 -0
  43. package/dist/utils/helpers.js +51 -0
  44. package/package.json +30 -0
  45. package/src/analyzer/index.ts +132 -0
  46. package/src/analyzer/inspections/functional.ts +159 -0
  47. package/src/analyzer/inspections/generic.ts +499 -0
  48. package/src/analyzer/inspections/imperative.ts +129 -0
  49. package/src/analyzer/inspections/logic.ts +166 -0
  50. package/src/analyzer/inspections/object.ts +282 -0
  51. package/src/analyzer/utils.ts +26 -0
  52. package/src/index.ts +3 -0
  53. package/src/interpreter/components/EnvBuilder.ts +97 -0
  54. package/src/interpreter/components/FunctionRuntime.ts +79 -0
  55. package/src/interpreter/components/LazyRuntime.ts +97 -0
  56. package/src/interpreter/components/LogicEngine.ts +227 -0
  57. package/src/interpreter/components/LogicResolver.ts +130 -0
  58. package/src/interpreter/components/Operations.ts +81 -0
  59. package/src/interpreter/components/PatternMatcher.ts +254 -0
  60. package/src/interpreter/components/Visitor.ts +493 -0
  61. package/src/interpreter/errors.ts +47 -0
  62. package/src/interpreter/index.ts +59 -0
  63. package/src/interpreter/utils.ts +79 -0
  64. package/src/utils/helpers.ts +73 -0
  65. package/tests/analyzer/functional.spec.ts +221 -0
  66. package/tests/analyzer/generic.spec.ts +100 -0
  67. package/tests/analyzer/helpers.spec.ts +83 -0
  68. package/tests/analyzer/logic.spec.ts +292 -0
  69. package/tests/analyzer/oop.spec.ts +338 -0
  70. package/tests/interpreter/EnvBuilder.spec.ts +178 -0
  71. package/tests/interpreter/FunctionRuntime.spec.ts +234 -0
  72. package/tests/interpreter/LazyRuntime.spec.ts +190 -0
  73. package/tests/interpreter/LogicEngine.spec.ts +194 -0
  74. package/tests/interpreter/Operations.spec.ts +220 -0
  75. package/tests/interpreter/PatternSystem.spec.ts +189 -0
  76. package/tests/interpreter/interpreter.spec.ts +937 -0
  77. package/tsconfig.build.json +7 -0
  78. package/tsconfig.json +17 -0
@@ -0,0 +1,166 @@
1
+ import {
2
+ Assignment,
3
+ AssignOperation,
4
+ Exist,
5
+ Fact,
6
+ Findall,
7
+ Forall,
8
+ Not,
9
+ Rule,
10
+ StopTraversalException,
11
+ SymbolPrimitive,
12
+ TraverseVisitor,
13
+ UnifyOperation,
14
+ } from "yukigo-ast";
15
+ import { InspectionMap, executeVisitor } from "../utils.js";
16
+ import { isYukigoPrimitive } from "yukigo-ast";
17
+
18
+ export abstract class DeclaresBinding extends TraverseVisitor {
19
+ protected readonly targetBinding: string;
20
+
21
+ constructor(binding: string) {
22
+ super();
23
+ this.targetBinding = binding;
24
+ }
25
+ }
26
+ export class DeclaresFact extends DeclaresBinding {
27
+ visitFact(node: Fact): void {
28
+ const bindingName = node.identifier.value;
29
+ if (!this.targetBinding || bindingName === this.targetBinding)
30
+ throw new StopTraversalException();
31
+ }
32
+ }
33
+ export class DeclaresRule extends DeclaresBinding {
34
+ visitRule(node: Rule): void {
35
+ const bindingName = node.identifier.value;
36
+ if (!this.targetBinding || bindingName === this.targetBinding)
37
+ throw new StopTraversalException();
38
+ }
39
+ }
40
+ export class DeclaresPredicate extends DeclaresBinding {
41
+ visitFact(node: Fact): void {
42
+ new DeclaresFact(this.targetBinding).visitFact(node);
43
+ }
44
+ visitRule(node: Rule): void {
45
+ new DeclaresRule(this.targetBinding).visitRule(node);
46
+ }
47
+ }
48
+
49
+ export class PredicateVisitor extends TraverseVisitor {
50
+ private readonly targetBinding: string;
51
+ protected isInsideTargetScope: boolean = false; // this flag helps to check nested functions inside the targetBinding scope
52
+ constructor(binding: string) {
53
+ super();
54
+ this.targetBinding = binding;
55
+ }
56
+ visitFact(node: Fact): void {
57
+ const nodeName = node.identifier.value;
58
+ if (!this.isInsideTargetScope) {
59
+ if (nodeName === this.targetBinding) {
60
+ this.isInsideTargetScope = true;
61
+ this.traverseCollection(node.patterns);
62
+ this.isInsideTargetScope = false;
63
+ }
64
+ return;
65
+ }
66
+ this.traverseCollection(node.patterns);
67
+ }
68
+ visitRule(node: Rule): void {
69
+ const nodeName = node.identifier.value;
70
+ if (!this.isInsideTargetScope) {
71
+ if (nodeName === this.targetBinding) {
72
+ this.isInsideTargetScope = true;
73
+ this.traverseCollection(node.expressions);
74
+ this.isInsideTargetScope = false;
75
+ }
76
+ return;
77
+ }
78
+ this.traverseCollection(node.expressions);
79
+ }
80
+ }
81
+
82
+ export class UsesFindall extends TraverseVisitor {
83
+ visitFindall(node: Findall): void {
84
+ throw new StopTraversalException();
85
+ }
86
+ }
87
+ export class UsesForall extends TraverseVisitor {
88
+ visitForall(node: Forall): void {
89
+ throw new StopTraversalException();
90
+ }
91
+ }
92
+ export class UsesNot extends TraverseVisitor {
93
+ visitNot(node: Not): void {
94
+ throw new StopTraversalException();
95
+ }
96
+ visitExist(node: Exist): void {
97
+ if (node.identifier.value === "not") throw new StopTraversalException();
98
+ }
99
+ }
100
+ export class UsesUnificationOperator extends PredicateVisitor {
101
+ visitUnifyOperation(node: UnifyOperation): void {
102
+ throw new StopTraversalException();
103
+ }
104
+ }
105
+ export class UsesCut extends PredicateVisitor {
106
+ visitExist(node: Exist): void {
107
+ if (node.identifier.value === "!") throw new StopTraversalException();
108
+ }
109
+ }
110
+ export class UsesFail extends PredicateVisitor {
111
+ visitExist(node: Exist): void {
112
+ if (node.identifier.value === "fail") throw new StopTraversalException();
113
+ }
114
+ }
115
+ export class HasRedundantReduction extends TraverseVisitor {
116
+ private readonly targetBinding: string;
117
+ protected isInsideTargetScope: boolean = false;
118
+
119
+ constructor(binding: string) {
120
+ super();
121
+ this.targetBinding = binding;
122
+ }
123
+ visitRule(node: Rule): void {
124
+ // Only inspect the specified rule if a binding is provided
125
+ if (this.targetBinding && node.identifier.value !== this.targetBinding)
126
+ return;
127
+
128
+ // Check each unification in the rule's body
129
+ for (const bodyElement of node.expressions) {
130
+ if (bodyElement instanceof AssignOperation) {
131
+ const left = bodyElement.left;
132
+ const right = bodyElement.right;
133
+
134
+ if (!(left instanceof SymbolPrimitive)) continue;
135
+
136
+ const redundantReductionParameters = isYukigoPrimitive(right);
137
+ const redundantReductionFunctors = right instanceof Exist;
138
+
139
+ // Check if reduction is redundant
140
+ const isRedundant =
141
+ redundantReductionParameters || redundantReductionFunctors;
142
+
143
+ if (isRedundant) throw new StopTraversalException();
144
+ }
145
+ }
146
+ }
147
+ }
148
+
149
+ export const logicInspections: InspectionMap = {
150
+ DeclaresFact: (node, args, binding) =>
151
+ executeVisitor(node, new DeclaresFact(binding)),
152
+ DeclaresRule: (node, args, binding) =>
153
+ executeVisitor(node, new DeclaresRule(binding)),
154
+ DeclaresPredicate: (node, args, binding) =>
155
+ executeVisitor(node, new DeclaresPredicate(binding)),
156
+ UsesFindall: (node, args, binding) => executeVisitor(node, new UsesFindall()),
157
+ UsesForall: (node, args, binding) => executeVisitor(node, new UsesForall()),
158
+ UsesNot: (node, args, binding) => executeVisitor(node, new UsesNot()),
159
+ UsesUnificationOperator: (node, args, binding) =>
160
+ executeVisitor(node, new UsesUnificationOperator(binding)),
161
+ UsesCut: (node, args, binding) => executeVisitor(node, new UsesCut(binding)),
162
+ UsesFail: (node, args, binding) =>
163
+ executeVisitor(node, new UsesFail(binding)),
164
+ HasRedundantReduction: (node, args, binding) =>
165
+ executeVisitor(node, new HasRedundantReduction(binding)),
166
+ };
@@ -0,0 +1,282 @@
1
+ import {
2
+ Attribute,
3
+ Class,
4
+ Include,
5
+ Interface,
6
+ Method,
7
+ New,
8
+ Object,
9
+ PrimitiveMethod,
10
+ Self,
11
+ Send,
12
+ StopTraversalException,
13
+ SymbolPrimitive,
14
+ TraverseVisitor,
15
+ } from "yukigo-ast";
16
+ import { executeVisitor, InspectionMap } from "../utils.js";
17
+
18
+ export class DeclaresAttribute extends TraverseVisitor {
19
+ constructor(private attributeName: string) {
20
+ super();
21
+ }
22
+ visitAttribute(node: Attribute): void {
23
+ if (node.identifier.value === this.attributeName)
24
+ throw new StopTraversalException();
25
+ }
26
+ }
27
+
28
+ export class DeclaresClass extends TraverseVisitor {
29
+ constructor(private className: string) {
30
+ super();
31
+ }
32
+ visitClass(node: Class): void {
33
+ if (node.identifier.value === this.className)
34
+ throw new StopTraversalException();
35
+ }
36
+ }
37
+
38
+ export class DeclaresInterface extends TraverseVisitor {
39
+ constructor(private interfaceName: string) {
40
+ super();
41
+ }
42
+ visitInterface(node: Interface): void {
43
+ if (node.identifier.value === this.interfaceName)
44
+ throw new StopTraversalException();
45
+ }
46
+ }
47
+
48
+ export class DeclaresMethod extends TraverseVisitor {
49
+ constructor(private methodName: string) {
50
+ super();
51
+ }
52
+ visitMethod(node: Method): void {
53
+ if (node.identifier.value === this.methodName)
54
+ throw new StopTraversalException();
55
+ }
56
+ }
57
+
58
+ export class DeclaresObject extends TraverseVisitor {
59
+ constructor(private objectName: string) {
60
+ super();
61
+ }
62
+ visitObject(node: Object): void {
63
+ if (node.identifier.value === this.objectName)
64
+ throw new StopTraversalException();
65
+ }
66
+ }
67
+
68
+ export class DeclaresPrimitive extends TraverseVisitor {
69
+ constructor(private operatorName: string) {
70
+ super();
71
+ }
72
+ visitPrimitiveMethod(node: PrimitiveMethod): void {
73
+ if (node.operator === this.operatorName) throw new StopTraversalException();
74
+ }
75
+ }
76
+
77
+ export class DeclaresSuperclass extends TraverseVisitor {
78
+ constructor(private superclassName: string) {
79
+ super();
80
+ }
81
+ visitClass(node: Class): void {
82
+ if (node.extendsSymbol && node.extendsSymbol.value === this.superclassName)
83
+ throw new StopTraversalException();
84
+ }
85
+ }
86
+
87
+ export class Implements extends TraverseVisitor {
88
+ constructor(private interfaceName: string) {
89
+ super();
90
+ }
91
+ visitClass(node: Class): void {
92
+ if (
93
+ node.implementsNode &&
94
+ node.implementsNode.identifier.value === this.interfaceName
95
+ )
96
+ throw new StopTraversalException();
97
+ }
98
+ }
99
+
100
+ export class IncludeMixin extends TraverseVisitor {
101
+ constructor(private mixinsName: string) {
102
+ super();
103
+ }
104
+ visitInclude(node: Include): void {
105
+ if (node.identifier.value === this.mixinsName)
106
+ throw new StopTraversalException();
107
+ }
108
+ }
109
+
110
+ export class Instantiates extends TraverseVisitor {
111
+ constructor(private className: string) {
112
+ super();
113
+ }
114
+ visitNew(node: New): void {
115
+ if (node.identifier.value === this.className)
116
+ throw new StopTraversalException();
117
+ }
118
+ }
119
+
120
+ export class UsesDynamicPolymorphism extends TraverseVisitor {
121
+ private count = 0;
122
+
123
+ constructor(private selectorName: string) {
124
+ super();
125
+ }
126
+
127
+ visitMethod(node: Method): void {
128
+ if (node.identifier.value === this.selectorName) {
129
+ this.count++;
130
+ if (this.count >= 2) throw new StopTraversalException();
131
+ }
132
+ }
133
+ }
134
+
135
+ export class UsesInheritance extends TraverseVisitor {
136
+ visitClass(node: Class): void {
137
+ if (node.extendsSymbol) throw new StopTraversalException();
138
+ }
139
+ visitInterface(node: Interface): void {
140
+ if (node.extendsSymbol && node.extendsSymbol.length > 0)
141
+ throw new StopTraversalException();
142
+ }
143
+ }
144
+
145
+ export class UsesMixins extends TraverseVisitor {
146
+ visitInclude(node: Include): void {
147
+ throw new StopTraversalException();
148
+ }
149
+ }
150
+
151
+ export class UsesObjectComposition extends TraverseVisitor {
152
+ visitAttribute(node: Attribute): void {
153
+ if (node.expression instanceof New) throw new StopTraversalException();
154
+ }
155
+ }
156
+
157
+ export class UsesStaticMethodOverload extends TraverseVisitor {
158
+ private scopes: Set<string>[] = [];
159
+
160
+ visitClass(node: Class): void {
161
+ this.scopes.push(new Set());
162
+ node.expression.accept(this);
163
+ this.scopes.pop();
164
+ }
165
+
166
+ visitObject(node: Object): void {
167
+ this.scopes.push(new Set());
168
+ node.expression.accept(this);
169
+ this.scopes.pop();
170
+ }
171
+
172
+ visitMethod(node: Method): void {
173
+ const currentScope = this.scopes[0];
174
+ const methodName = node.identifier.value;
175
+
176
+ if (currentScope.has(methodName)) throw new StopTraversalException();
177
+ currentScope.add(methodName);
178
+ }
179
+ }
180
+
181
+ export class UsesDynamicMethodOverload extends TraverseVisitor {
182
+ visitMethod(node: Method): void {
183
+ if (node.equations.length > 1) throw new StopTraversalException();
184
+ }
185
+ }
186
+
187
+ class AbstractMethodCollector extends TraverseVisitor {
188
+ public abstractMethods: Set<string> = new Set();
189
+
190
+ visitMethod(node: Method): void {
191
+ if (node.getMetadata<boolean>("isAbstract") === true)
192
+ this.abstractMethods.add(node.identifier.value);
193
+ }
194
+ // stop propagation to not mix scopes
195
+ visitClass(node: Class) {
196
+ return;
197
+ }
198
+ visitObject(node: Object) {
199
+ return;
200
+ }
201
+ }
202
+
203
+ export class UsesTemplateMethod extends TraverseVisitor {
204
+ private abstractMethodsStack: Set<string>[] = [];
205
+
206
+ visitClass(node: Class): void {
207
+ const collector = new AbstractMethodCollector();
208
+ if (node.expression) executeVisitor(node.expression, collector);
209
+ this.abstractMethodsStack.push(collector.abstractMethods);
210
+ node.expression.accept(this);
211
+ this.abstractMethodsStack.pop();
212
+ }
213
+
214
+ visitSend(node: Send): void {
215
+ if (node.receiver instanceof Self) {
216
+ if (this.abstractMethodsStack.length === 0) return;
217
+ const currentAbstractMethods = this.abstractMethodsStack[0];
218
+
219
+ // This doesnt match if message is complex expression
220
+ if (!(node.selector instanceof SymbolPrimitive)) return;
221
+ const selectorName = node.selector.value;
222
+
223
+ const isMessageAbstract = currentAbstractMethods.has(selectorName);
224
+
225
+ if (isMessageAbstract) throw new StopTraversalException();
226
+ }
227
+ }
228
+ }
229
+
230
+ export const objectInspections: InspectionMap = {
231
+ DeclaresAttribute: (node, args) =>
232
+ executeVisitor(node, new DeclaresAttribute(args[0])),
233
+
234
+ DeclaresClass: (node, args) =>
235
+ executeVisitor(node, new DeclaresClass(args[0])),
236
+
237
+ DeclaresInterface: (node, args) =>
238
+ executeVisitor(node, new DeclaresInterface(args[0])),
239
+
240
+ DeclaresMethod: (node, args) =>
241
+ executeVisitor(node, new DeclaresMethod(args[0])),
242
+
243
+ DeclaresObject: (node, args) =>
244
+ executeVisitor(node, new DeclaresObject(args[0])),
245
+
246
+ DeclaresPrimitive: (node, args) =>
247
+ executeVisitor(node, new DeclaresPrimitive(args[0])),
248
+
249
+ DeclaresSuperclass: (node, args) =>
250
+ executeVisitor(node, new DeclaresSuperclass(args[0])),
251
+
252
+ Implements: (node, args) => executeVisitor(node, new Implements(args[0])),
253
+
254
+ Include: (node, args) => executeVisitor(node, new IncludeMixin(args[0])),
255
+
256
+ Inherits: (node, args) =>
257
+ executeVisitor(node, new DeclaresSuperclass(args[0])),
258
+
259
+ Instantiates: (node, args) => executeVisitor(node, new Instantiates(args[0])),
260
+
261
+ UsesDynamicPolymorphism: (node, args) =>
262
+ executeVisitor(node, new UsesDynamicPolymorphism(args[0])),
263
+
264
+ UsesInheritance: (node, args) => executeVisitor(node, new UsesInheritance()),
265
+
266
+ UsesMixins: (node, args) => executeVisitor(node, new UsesMixins()),
267
+
268
+ UsesObjectComposition: (node, args) =>
269
+ executeVisitor(node, new UsesObjectComposition()),
270
+
271
+ UsesStaticMethodOverload: (node, args) =>
272
+ executeVisitor(node, new UsesStaticMethodOverload()),
273
+
274
+ UsesDynamicMethodOverload: (node, args) =>
275
+ executeVisitor(node, new UsesDynamicMethodOverload()),
276
+
277
+ UsesTemplateMethod: (node, args) =>
278
+ executeVisitor(node, new UsesTemplateMethod()),
279
+
280
+ UsesStaticPolymorphism: (node, args) =>
281
+ executeVisitor(node, new UsesStaticMethodOverload()),
282
+ };
@@ -0,0 +1,26 @@
1
+ import { ASTNode, StopTraversalException, TraverseVisitor } from "yukigo-ast";
2
+
3
+ export type InspectionHandler = (
4
+ node: ASTNode,
5
+ args: string[],
6
+ binding?: string
7
+ ) => boolean;
8
+
9
+ export type InspectionMap = Record<string, InspectionHandler>;
10
+
11
+ export function executeVisitor(
12
+ node: ASTNode,
13
+ visitor: TraverseVisitor
14
+ ): boolean {
15
+ let passes = false;
16
+ try {
17
+ node.accept(visitor);
18
+ } catch (e) {
19
+ if (e instanceof StopTraversalException) {
20
+ passes = true;
21
+ } else {
22
+ throw e;
23
+ }
24
+ }
25
+ return passes;
26
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./interpreter/index.js";
2
+ export * from "./analyzer/index.js";
3
+ export * from "./utils/helpers.js";
@@ -0,0 +1,97 @@
1
+ import {
2
+ AST,
3
+ ASTNode,
4
+ EquationRuntime,
5
+ Fact,
6
+ Function,
7
+ isRuntimePredicate,
8
+ Rule,
9
+ RuntimeFunction,
10
+ TraverseVisitor,
11
+ } from "yukigo-ast";
12
+ import { EnvStack } from "../index.js";
13
+ import { define } from "../utils.js";
14
+
15
+ /**
16
+ * Builds the initial environment by collecting all top-level function declarations.
17
+ * Each function captures a closure of the environment at its definition time,
18
+ * allowing recursion by including itself in the closure.
19
+ */
20
+ export class EnvBuilderVisitor extends TraverseVisitor {
21
+ private env: EnvStack;
22
+
23
+ constructor(baseEnv?: EnvStack) {
24
+ super();
25
+ this.env = baseEnv ?? [new Map()];
26
+ }
27
+
28
+ public build(ast: AST): EnvStack {
29
+ for (const node of ast) node.accept(this);
30
+ return this.env;
31
+ }
32
+ visitFunction(node: Function): void {
33
+ const name = node.identifier.value;
34
+
35
+ if (node.equations.length === 0)
36
+ throw new Error(`Function ${name} has no equations`);
37
+
38
+ const arity = node.equations[0].patterns.length;
39
+ for (const eq of node.equations) {
40
+ if (eq.patterns.length !== arity)
41
+ throw new Error(`All equations of ${name} must have the same arity`);
42
+ }
43
+
44
+ let placeholder: RuntimeFunction;
45
+ define(this.env, name, placeholder);
46
+
47
+ const equations: EquationRuntime[] = node.equations.map((eq) => ({
48
+ patterns: eq.patterns,
49
+ body: eq.body,
50
+ }));
51
+
52
+ const runtimeFunc: RuntimeFunction = {
53
+ identifier: name,
54
+ arity,
55
+ equations,
56
+ };
57
+ define(this.env, name, runtimeFunc);
58
+ }
59
+ visitFact(node: Fact): void {
60
+ const identifier = node.identifier.value;
61
+ const runtimeValue = this.env[0].get(identifier);
62
+
63
+ if (isRuntimePredicate(runtimeValue) && runtimeValue.kind === "Fact") {
64
+ this.env[0].set(identifier, {
65
+ ...runtimeValue,
66
+ equations: [...runtimeValue.equations, node],
67
+ });
68
+ } else {
69
+ this.env[0].set(identifier, {
70
+ kind: "Fact",
71
+ identifier,
72
+ equations: [node],
73
+ });
74
+ }
75
+ }
76
+
77
+ visitRule(node: Rule): void {
78
+ const identifier = node.identifier.value;
79
+ const runtimeValue = this.env[0].get(identifier);
80
+
81
+ if (isRuntimePredicate(runtimeValue) && runtimeValue.kind === "Rule") {
82
+ this.env[0].set(identifier, {
83
+ ...runtimeValue,
84
+ equations: [...runtimeValue.equations, node],
85
+ });
86
+ } else {
87
+ this.env[0].set(identifier, {
88
+ kind: "Rule",
89
+ identifier,
90
+ equations: [node],
91
+ });
92
+ }
93
+ }
94
+ visit(node: ASTNode): void {
95
+ return node.accept(this);
96
+ }
97
+ }
@@ -0,0 +1,79 @@
1
+ import {
2
+ EquationRuntime,
3
+ PrimitiveValue,
4
+ UnguardedBody,
5
+ Sequence,
6
+ Return,
7
+ Expression,
8
+ } from "yukigo-ast";
9
+ import { Bindings, EnvStack } from "../index.js";
10
+ import { PatternMatcher } from "./PatternMatcher.js";
11
+ import { ExpressionEvaluator } from "../utils.js";
12
+ import { InterpreterError } from "../errors.js";
13
+
14
+ class NonExhaustivePatterns extends InterpreterError {
15
+ constructor(funcName: string) {
16
+ super("PatternMatch", `Non-exhaustive patterns in '${funcName}'`);
17
+ }
18
+ }
19
+
20
+ export class FunctionRuntime {
21
+ static apply(
22
+ funcName: string,
23
+ equations: EquationRuntime[],
24
+ args: PrimitiveValue[],
25
+ currentEnv: EnvStack,
26
+ evaluatorFactory: (env: EnvStack) => ExpressionEvaluator
27
+ ): PrimitiveValue {
28
+ for (const eq of equations) {
29
+ if (eq.patterns.length !== args.length) continue;
30
+
31
+ const bindings: Bindings = [];
32
+
33
+ if (!FunctionRuntime.patternsMatch(eq, args, bindings)) continue;
34
+ const localEnv = new Map<string, PrimitiveValue>(bindings);
35
+ const newStack: EnvStack = [localEnv, ...currentEnv];
36
+
37
+ const scopeEvaluator = evaluatorFactory(newStack);
38
+
39
+ // UnguardedBody
40
+ if (eq.body instanceof UnguardedBody)
41
+ return this.evaluateSequence(eq.body.sequence, scopeEvaluator);
42
+
43
+ // GuardedBody
44
+ for (const guard of eq.body) {
45
+ const cond = scopeEvaluator.evaluate(guard.condition);
46
+ if (cond === true) return scopeEvaluator.evaluate(guard.body);
47
+ }
48
+ }
49
+ throw new NonExhaustivePatterns(funcName);
50
+ }
51
+
52
+ private static patternsMatch(
53
+ eq: EquationRuntime,
54
+ args: PrimitiveValue[],
55
+ bindings: Bindings
56
+ ): boolean {
57
+ for (let i = 0; i < args.length; i++) {
58
+ const matcher = new PatternMatcher(args[i], bindings);
59
+ if (!eq.patterns[i].accept(matcher)) return false;
60
+ }
61
+ return true;
62
+ }
63
+
64
+ private static evaluateSequence(
65
+ seq: Sequence,
66
+ evaluator: ExpressionEvaluator
67
+ ): PrimitiveValue {
68
+ let result: PrimitiveValue = undefined;
69
+
70
+ for (const stmt of seq.statements) {
71
+ if (stmt instanceof Return) {
72
+ return evaluator.evaluate(stmt.body);
73
+ } else {
74
+ result = evaluator.evaluate(stmt);
75
+ }
76
+ }
77
+ return result;
78
+ }
79
+ }