yukigo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +4 -0
- package/CHANGELOG.md +6 -0
- package/README.md +199 -0
- package/dist/analyzer/index.d.ts +71 -0
- package/dist/analyzer/index.js +110 -0
- package/dist/analyzer/inspections/functional.d.ts +46 -0
- package/dist/analyzer/inspections/functional.js +123 -0
- package/dist/analyzer/inspections/generic.d.ts +151 -0
- package/dist/analyzer/inspections/generic.js +427 -0
- package/dist/analyzer/inspections/imperative.d.ts +37 -0
- package/dist/analyzer/inspections/imperative.js +105 -0
- package/dist/analyzer/inspections/logic.d.ts +49 -0
- package/dist/analyzer/inspections/logic.js +140 -0
- package/dist/analyzer/inspections/object.d.ts +83 -0
- package/dist/analyzer/inspections/object.js +235 -0
- package/dist/analyzer/utils.d.ts +4 -0
- package/dist/analyzer/utils.js +16 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/interpreter/components/EnvBuilder.d.ts +16 -0
- package/dist/interpreter/components/EnvBuilder.js +78 -0
- package/dist/interpreter/components/FunctionRuntime.d.ts +8 -0
- package/dist/interpreter/components/FunctionRuntime.js +52 -0
- package/dist/interpreter/components/LazyRuntime.d.ts +7 -0
- package/dist/interpreter/components/LazyRuntime.js +75 -0
- package/dist/interpreter/components/LogicEngine.d.ts +21 -0
- package/dist/interpreter/components/LogicEngine.js +152 -0
- package/dist/interpreter/components/LogicResolver.d.ts +11 -0
- package/dist/interpreter/components/LogicResolver.js +87 -0
- package/dist/interpreter/components/Operations.d.ts +14 -0
- package/dist/interpreter/components/Operations.js +69 -0
- package/dist/interpreter/components/PatternMatcher.d.ts +41 -0
- package/dist/interpreter/components/PatternMatcher.js +206 -0
- package/dist/interpreter/components/Visitor.d.ts +64 -0
- package/dist/interpreter/components/Visitor.js +299 -0
- package/dist/interpreter/errors.d.ts +19 -0
- package/dist/interpreter/errors.js +36 -0
- package/dist/interpreter/index.d.ts +32 -0
- package/dist/interpreter/index.js +44 -0
- package/dist/interpreter/utils.d.ts +14 -0
- package/dist/interpreter/utils.js +57 -0
- package/dist/utils/helpers.d.ts +14 -0
- package/dist/utils/helpers.js +51 -0
- package/package.json +30 -0
- package/src/analyzer/index.ts +132 -0
- package/src/analyzer/inspections/functional.ts +159 -0
- package/src/analyzer/inspections/generic.ts +499 -0
- package/src/analyzer/inspections/imperative.ts +129 -0
- package/src/analyzer/inspections/logic.ts +166 -0
- package/src/analyzer/inspections/object.ts +282 -0
- package/src/analyzer/utils.ts +26 -0
- package/src/index.ts +3 -0
- package/src/interpreter/components/EnvBuilder.ts +97 -0
- package/src/interpreter/components/FunctionRuntime.ts +79 -0
- package/src/interpreter/components/LazyRuntime.ts +97 -0
- package/src/interpreter/components/LogicEngine.ts +227 -0
- package/src/interpreter/components/LogicResolver.ts +130 -0
- package/src/interpreter/components/Operations.ts +81 -0
- package/src/interpreter/components/PatternMatcher.ts +254 -0
- package/src/interpreter/components/Visitor.ts +493 -0
- package/src/interpreter/errors.ts +47 -0
- package/src/interpreter/index.ts +59 -0
- package/src/interpreter/utils.ts +79 -0
- package/src/utils/helpers.ts +73 -0
- package/tests/analyzer/functional.spec.ts +221 -0
- package/tests/analyzer/generic.spec.ts +100 -0
- package/tests/analyzer/helpers.spec.ts +83 -0
- package/tests/analyzer/logic.spec.ts +292 -0
- package/tests/analyzer/oop.spec.ts +338 -0
- package/tests/interpreter/EnvBuilder.spec.ts +178 -0
- package/tests/interpreter/FunctionRuntime.spec.ts +234 -0
- package/tests/interpreter/LazyRuntime.spec.ts +190 -0
- package/tests/interpreter/LogicEngine.spec.ts +194 -0
- package/tests/interpreter/Operations.spec.ts +220 -0
- package/tests/interpreter/PatternSystem.spec.ts +189 -0
- package/tests/interpreter/interpreter.spec.ts +937 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ApplicationPattern,
|
|
3
|
+
AsPattern,
|
|
4
|
+
ASTNode,
|
|
5
|
+
ConsPattern,
|
|
6
|
+
ConstructorPattern,
|
|
7
|
+
FunctorPattern,
|
|
8
|
+
isLazyList,
|
|
9
|
+
LazyList,
|
|
10
|
+
ListPattern,
|
|
11
|
+
ListPrimitive,
|
|
12
|
+
LiteralPattern,
|
|
13
|
+
Pattern,
|
|
14
|
+
PrimitiveValue,
|
|
15
|
+
SymbolPrimitive,
|
|
16
|
+
TuplePattern,
|
|
17
|
+
UnionPattern,
|
|
18
|
+
VariablePattern,
|
|
19
|
+
Visitor,
|
|
20
|
+
WildcardPattern,
|
|
21
|
+
} from "yukigo-ast";
|
|
22
|
+
import { Bindings } from "../index.js";
|
|
23
|
+
import { InterpreterVisitor } from "./Visitor.js";
|
|
24
|
+
import { createStream } from "../utils.js";
|
|
25
|
+
|
|
26
|
+
export class PatternResolver implements Visitor<string> {
|
|
27
|
+
visitVariablePattern(node: VariablePattern): string {
|
|
28
|
+
return node.name.value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
visitWildcardPattern(node: WildcardPattern): string {
|
|
32
|
+
return "_";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
visitLiteralPattern(node: LiteralPattern): string {
|
|
36
|
+
const { name } = node;
|
|
37
|
+
if (name instanceof ListPrimitive)
|
|
38
|
+
return String(name.elements.map((elem) => elem.accept(this)));
|
|
39
|
+
return String(name.value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
visitTuplePattern(node: TuplePattern): string {
|
|
43
|
+
return String(node.elements.map((elem) => elem.accept(this)));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
visitListPattern(node: ListPattern): string {
|
|
47
|
+
const { elements } = node;
|
|
48
|
+
return elements.length === 0
|
|
49
|
+
? "[]"
|
|
50
|
+
: String(elements.map((elem) => elem.accept(this)));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
visitConsPattern(node: ConsPattern): string {
|
|
54
|
+
const head = node.head.accept(this);
|
|
55
|
+
const tail = node.tail.accept(this);
|
|
56
|
+
return `(${head}:${tail})`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
visitConstructorPattern(node: ConstructorPattern): string {
|
|
60
|
+
const constr = node.constr;
|
|
61
|
+
const args = node.patterns.map((pat) => pat.accept(this)).join(" ");
|
|
62
|
+
return `${constr} ${args}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
visitFunctorPattern(node: FunctorPattern): string {
|
|
66
|
+
// Same as ConstructorPattern (alias)
|
|
67
|
+
return this.visitConstructorPattern(
|
|
68
|
+
new ConstructorPattern(node.identifier.value, node.args)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
visitApplicationPattern(node: ApplicationPattern): string {
|
|
73
|
+
// Same as FunctorPattern
|
|
74
|
+
return this.visitConstructorPattern(
|
|
75
|
+
new ConstructorPattern(node.symbol.value, node.args)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
visitAsPattern(node: AsPattern): string {
|
|
80
|
+
const pattern = node.pattern.accept(this);
|
|
81
|
+
const alias = node.alias.accept(this);
|
|
82
|
+
return `${alias}@${pattern}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
visit(node: ASTNode): string {
|
|
86
|
+
return node.accept(this);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Recursively matches a value against a pattern node.
|
|
91
|
+
* Updates `bindings` when variables are bound successfully.
|
|
92
|
+
* Returns true if the pattern matches, false otherwise.
|
|
93
|
+
*/
|
|
94
|
+
export class PatternMatcher implements Visitor<boolean> {
|
|
95
|
+
private value: PrimitiveValue;
|
|
96
|
+
private bindings: Bindings;
|
|
97
|
+
|
|
98
|
+
constructor(value: PrimitiveValue, bindings: Bindings) {
|
|
99
|
+
this.value = value;
|
|
100
|
+
this.bindings = bindings;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
visitVariablePattern(node: VariablePattern): boolean {
|
|
104
|
+
this.bindings.push([node.name.value, this.value]);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
visitWildcardPattern(node: WildcardPattern): boolean {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
visitLiteralPattern(node: LiteralPattern): boolean {
|
|
113
|
+
const literalValue = InterpreterVisitor.evaluateLiteral(node.name);
|
|
114
|
+
return this.deepEqual(this.value, literalValue);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
visitTuplePattern(node: TuplePattern): boolean {
|
|
118
|
+
if (!Array.isArray(this.value)) return false;
|
|
119
|
+
if (this.value.length !== node.elements.length) return false;
|
|
120
|
+
|
|
121
|
+
for (let i = 0; i < node.elements.length; i++) {
|
|
122
|
+
const matcher = new PatternMatcher(this.value[i], this.bindings);
|
|
123
|
+
if (!node.elements[i].accept(matcher)) return false;
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
visitListPattern(node: ListPattern): boolean {
|
|
129
|
+
// empty list case
|
|
130
|
+
if (node.elements.length === 0) {
|
|
131
|
+
if (Array.isArray(this.value)) return this.value.length === 0;
|
|
132
|
+
|
|
133
|
+
if (isLazyList(this.value)) {
|
|
134
|
+
const iter = this.value.generator();
|
|
135
|
+
return iter.next().done;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// finite list case
|
|
141
|
+
if (Array.isArray(this.value))
|
|
142
|
+
return this.matchList(node.elements, this.value);
|
|
143
|
+
|
|
144
|
+
// lazy list case
|
|
145
|
+
if (isLazyList(this.value)) {
|
|
146
|
+
const realized = this.realize(this.value);
|
|
147
|
+
return this.matchList(node.elements, realized);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
private matchList(elements: Pattern[], value: PrimitiveValue[]) {
|
|
153
|
+
if (value.length !== elements.length) return false;
|
|
154
|
+
for (let i = 0; i < elements.length; i++) {
|
|
155
|
+
const matcher = new PatternMatcher(value[i], this.bindings);
|
|
156
|
+
const isMatch = elements[i].accept(matcher);
|
|
157
|
+
if (!isMatch) return false;
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
visitConsPattern(node: ConsPattern): boolean {
|
|
163
|
+
const [head, tail] = this.resolveCons(this.value);
|
|
164
|
+
if(!head || !tail) return false
|
|
165
|
+
const headMatcher = new PatternMatcher(head, this.bindings);
|
|
166
|
+
const headMatches = node.head.accept(headMatcher);
|
|
167
|
+
if (!headMatches) return false;
|
|
168
|
+
|
|
169
|
+
const tailMatcher = new PatternMatcher(tail, this.bindings);
|
|
170
|
+
return node.tail.accept(tailMatcher);
|
|
171
|
+
}
|
|
172
|
+
private resolveCons(list: PrimitiveValue): [PrimitiveValue, PrimitiveValue] {
|
|
173
|
+
if (Array.isArray(list))
|
|
174
|
+
return list.length === 0 ? [null, null] : [list[0], list.slice(1)];
|
|
175
|
+
if (isLazyList(list)) {
|
|
176
|
+
const headIter = list.generator();
|
|
177
|
+
const next = headIter.next();
|
|
178
|
+
if (next.done) return [null, null];
|
|
179
|
+
const parentGeneratorFactory = list.generator;
|
|
180
|
+
const tailGenerator = function* (): Generator<PrimitiveValue> {
|
|
181
|
+
const freshIter = parentGeneratorFactory();
|
|
182
|
+
freshIter.next();
|
|
183
|
+
let next: IteratorResult<PrimitiveValue, void>;
|
|
184
|
+
while (!(next = freshIter.next()).done) yield next.value;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return [next.value, createStream(tailGenerator)];
|
|
188
|
+
}
|
|
189
|
+
return [null, null]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
visitConstructorPattern(node: ConstructorPattern): boolean {
|
|
193
|
+
if (!Array.isArray(this.value) || this.value.length === 0) return false;
|
|
194
|
+
if (this.value[0] !== node.constr) return false;
|
|
195
|
+
|
|
196
|
+
const args = this.value.slice(1);
|
|
197
|
+
return this.matchList(node.patterns, args);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
visitFunctorPattern(node: FunctorPattern): boolean {
|
|
201
|
+
return this.visitConstructorPattern(
|
|
202
|
+
new ConstructorPattern(node.identifier.value, node.args)
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
visitApplicationPattern(node: ApplicationPattern): boolean {
|
|
207
|
+
return this.visitConstructorPattern(
|
|
208
|
+
new ConstructorPattern(node.symbol.value, node.args)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
visitAsPattern(node: AsPattern): boolean {
|
|
213
|
+
const innerMatcher = new PatternMatcher(this.value, this.bindings);
|
|
214
|
+
const innerMatches = node.pattern.accept(innerMatcher);
|
|
215
|
+
if (!innerMatches) return false;
|
|
216
|
+
|
|
217
|
+
const aliasMatcher = new PatternMatcher(this.value, this.bindings);
|
|
218
|
+
return node.alias.accept(aliasMatcher);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
visitUnionPattern(node: UnionPattern): boolean {
|
|
222
|
+
for (const pattern of node.patterns) {
|
|
223
|
+
const trialBindings: Bindings = [];
|
|
224
|
+
const matcher = new PatternMatcher(this.value, trialBindings);
|
|
225
|
+
if (pattern.accept(matcher)) {
|
|
226
|
+
this.bindings.push(...trialBindings);
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
visit(node: ASTNode): boolean {
|
|
234
|
+
return node.accept(this);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private deepEqual(a: PrimitiveValue, b: PrimitiveValue): boolean {
|
|
238
|
+
if (a === b) return true;
|
|
239
|
+
if (typeof a !== typeof b) return false;
|
|
240
|
+
|
|
241
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
242
|
+
if (a.length !== b.length) return false;
|
|
243
|
+
for (let i = 0; i < a.length; i++) {
|
|
244
|
+
if (!this.deepEqual(a[i], b[i])) return false;
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
private realize(value: PrimitiveValue): PrimitiveValue[] {
|
|
252
|
+
return new InterpreterVisitor([new Map()], {}).realizeList(value);
|
|
253
|
+
}
|
|
254
|
+
}
|