yukigo-haskell-parser 0.1.3 → 0.2.2
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 +51 -9
- package/README.md +5 -5
- package/dist/index.js +21 -13
- package/dist/index.js.map +1 -1
- package/dist/parser/grammar.cjs +397 -0
- package/dist/parser/grammar.cjs.map +1 -0
- package/dist/parser/grammar.d.cts +38 -0
- package/dist/parser/lexer.d.ts +2 -1
- package/dist/parser/lexer.js +6 -1
- package/dist/parser/lexer.js.map +1 -1
- package/dist/prelude.js +279 -279
- package/dist/typechecker/DeclarationCollector.d.ts +2 -0
- package/dist/typechecker/DeclarationCollector.js +6 -5
- package/dist/typechecker/DeclarationCollector.js.map +1 -1
- package/dist/typechecker/TypeBuilder.js +9 -7
- package/dist/typechecker/TypeBuilder.js.map +1 -1
- package/dist/typechecker/checker.d.ts +9 -4
- package/dist/typechecker/checker.js +66 -94
- package/dist/typechecker/checker.js.map +1 -1
- package/dist/typechecker/core.js +8 -7
- package/dist/typechecker/core.js.map +1 -1
- package/dist/typechecker/inference.d.ts +4 -1
- package/dist/typechecker/inference.js +39 -19
- package/dist/typechecker/inference.js.map +1 -1
- package/dist/utils/helpers.d.ts +3 -0
- package/dist/utils/helpers.js +5 -0
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/types.d.ts +12 -3
- package/dist/utils/types.js +34 -22
- package/dist/utils/types.js.map +1 -1
- package/package.json +6 -9
- package/src/index.ts +223 -192
- package/src/parser/{grammar.ts → grammar.cjs} +191 -223
- package/src/parser/grammar.d.ts +3 -0
- package/src/parser/grammar.ne +521 -518
- package/src/parser/lexer.ts +357 -353
- package/src/prelude.ts +281 -281
- package/src/typechecker/DeclarationCollector.ts +160 -157
- package/src/typechecker/TypeBuilder.ts +153 -150
- package/src/typechecker/checker.ts +487 -501
- package/src/typechecker/core.ts +195 -192
- package/src/typechecker/inference.ts +1442 -1421
- package/src/utils/helpers.ts +34 -29
- package/src/utils/types.ts +75 -63
- package/tests/hspec.spec.ts +95 -92
- package/tests/lexer.spec.ts +175 -175
- package/tests/parser.spec.ts +838 -829
- package/tests/prelude.spec.ts +17 -17
- package/tests/typechecker.spec.ts +326 -327
- package/tsconfig.build.json +16 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +29 -26
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/parser/grammar.d.ts +0 -28
- package/dist/parser/grammar.js +0 -393
- package/dist/parser/grammar.js.map +0 -1
|
@@ -1,501 +1,487 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AST,
|
|
3
|
-
Function,
|
|
4
|
-
Visitor,
|
|
5
|
-
Return,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
| { success:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
private
|
|
84
|
-
private
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
private
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
node.
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
node.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
this.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
this.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.environments.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
this.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
this.signatureMap,
|
|
361
|
-
this.coreHM,
|
|
362
|
-
|
|
363
|
-
);
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
t
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
export function
|
|
475
|
-
return
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
}
|
|
481
|
-
export function
|
|
482
|
-
return
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
export function functionType(params: Type, returnType: Type): FunctionType {
|
|
489
|
-
return {
|
|
490
|
-
type: "TypeConstructor",
|
|
491
|
-
name: "->",
|
|
492
|
-
args: [params, returnType],
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
export function listType(elementsType: Type): ListType {
|
|
496
|
-
return {
|
|
497
|
-
type: "TypeConstructor",
|
|
498
|
-
name: "List",
|
|
499
|
-
args: [elementsType],
|
|
500
|
-
};
|
|
501
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AST,
|
|
3
|
+
Function,
|
|
4
|
+
Visitor,
|
|
5
|
+
Return,
|
|
6
|
+
Sequence,
|
|
7
|
+
TestGroup,
|
|
8
|
+
Test,
|
|
9
|
+
Assert,
|
|
10
|
+
ASTNode,
|
|
11
|
+
UnguardedBody,
|
|
12
|
+
NativeBody,
|
|
13
|
+
} from "yukigo-ast";
|
|
14
|
+
import { InferenceEngine, PatternVisitor } from "./inference.js";
|
|
15
|
+
import { CoreHM } from "./core.js";
|
|
16
|
+
import { DeclarationCollectorVisitor } from "./DeclarationCollector.js";
|
|
17
|
+
import { typeClasses as staticTypeClasses, YUTYPES } from "../utils/types.js";
|
|
18
|
+
|
|
19
|
+
export interface TypeVar {
|
|
20
|
+
type: "TypeVar";
|
|
21
|
+
id: number;
|
|
22
|
+
name?: string;
|
|
23
|
+
constraints: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface TypeConstructor {
|
|
27
|
+
type: "TypeConstructor";
|
|
28
|
+
name: string;
|
|
29
|
+
args: Type[];
|
|
30
|
+
}
|
|
31
|
+
export interface FunctionType {
|
|
32
|
+
type: "TypeConstructor";
|
|
33
|
+
name: YUTYPES.Arrow;
|
|
34
|
+
args: [Type, Type];
|
|
35
|
+
}
|
|
36
|
+
export interface ListType {
|
|
37
|
+
type: "TypeConstructor";
|
|
38
|
+
name: YUTYPES.List;
|
|
39
|
+
args: [Type];
|
|
40
|
+
}
|
|
41
|
+
export interface TupleType {
|
|
42
|
+
type: "TypeConstructor";
|
|
43
|
+
name: YUTYPES.Tuple;
|
|
44
|
+
args: Type[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface TypeScheme {
|
|
48
|
+
type: "TypeScheme";
|
|
49
|
+
quantifiers: number[];
|
|
50
|
+
body: Type;
|
|
51
|
+
constraints: Map<number, string[]>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type Type = TypeVar | TypeConstructor;
|
|
55
|
+
|
|
56
|
+
export type Environment = Map<string, TypeScheme>;
|
|
57
|
+
|
|
58
|
+
export type Substitution = Map<number, Type>;
|
|
59
|
+
|
|
60
|
+
export type Result<T> =
|
|
61
|
+
| { success: true; value: T }
|
|
62
|
+
| { success: false; error: string };
|
|
63
|
+
|
|
64
|
+
export const booleanType: TypeConstructor = {
|
|
65
|
+
type: "TypeConstructor",
|
|
66
|
+
name: YUTYPES.YuBoolean,
|
|
67
|
+
args: [],
|
|
68
|
+
};
|
|
69
|
+
export const numberType: TypeConstructor = {
|
|
70
|
+
type: "TypeConstructor",
|
|
71
|
+
name: YUTYPES.YuNumber,
|
|
72
|
+
args: [],
|
|
73
|
+
};
|
|
74
|
+
export const charType: TypeConstructor = {
|
|
75
|
+
type: "TypeConstructor",
|
|
76
|
+
name: YUTYPES.YuChar,
|
|
77
|
+
args: [],
|
|
78
|
+
};
|
|
79
|
+
export const stringType: Type = listType(charType);
|
|
80
|
+
|
|
81
|
+
export class FunctionRegistrarVisitor implements Visitor<void> {
|
|
82
|
+
constructor(
|
|
83
|
+
private env: Environment,
|
|
84
|
+
private signatureMap: Map<string, TypeScheme>,
|
|
85
|
+
private coreHM: CoreHM,
|
|
86
|
+
) {}
|
|
87
|
+
visitSequence(node: Sequence): void {
|
|
88
|
+
node.statements.forEach((stmt) => stmt.accept(this));
|
|
89
|
+
}
|
|
90
|
+
visitFunction(node: Function): void {
|
|
91
|
+
const functionName = node.identifier.value;
|
|
92
|
+
let funcScheme = this.signatureMap.get(functionName);
|
|
93
|
+
if (!funcScheme) {
|
|
94
|
+
const funcTypeVar = this.coreHM.freshVar();
|
|
95
|
+
funcScheme = {
|
|
96
|
+
type: "TypeScheme",
|
|
97
|
+
quantifiers: [],
|
|
98
|
+
body: funcTypeVar,
|
|
99
|
+
constraints: new Map(),
|
|
100
|
+
};
|
|
101
|
+
this.env.set(functionName, funcScheme);
|
|
102
|
+
}
|
|
103
|
+
for (const equation of node.equations) {
|
|
104
|
+
if (!equation.body.is(UnguardedBody)) continue;
|
|
105
|
+
const statements = equation.body.sequence.statements;
|
|
106
|
+
statements
|
|
107
|
+
.filter((stmt) => stmt instanceof Function)
|
|
108
|
+
.forEach((func) => {
|
|
109
|
+
this.env.set(func.identifier.value, funcScheme);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
visitTestGroup(node: TestGroup): void {
|
|
114
|
+
node.group.accept(this);
|
|
115
|
+
}
|
|
116
|
+
visitTest(node: Test): void {
|
|
117
|
+
node.body.accept(this);
|
|
118
|
+
}
|
|
119
|
+
visitAssert(node: Assert): void {}
|
|
120
|
+
fallback(node: ASTNode): void {}
|
|
121
|
+
}
|
|
122
|
+
export class FunctionCheckerVisitor implements Visitor<void> {
|
|
123
|
+
constructor(
|
|
124
|
+
private environments: Environment[],
|
|
125
|
+
private signatureMap: Map<string, TypeScheme>,
|
|
126
|
+
private coreHM: CoreHM,
|
|
127
|
+
private errors: string[],
|
|
128
|
+
) {}
|
|
129
|
+
visitTestGroup(node: TestGroup): void {
|
|
130
|
+
node.group.accept(this);
|
|
131
|
+
}
|
|
132
|
+
visitTest(node: Test): void {
|
|
133
|
+
node.body.accept(this);
|
|
134
|
+
}
|
|
135
|
+
visitAssert(node: Assert): void {
|
|
136
|
+
const inferenceEngine = new InferenceEngine(
|
|
137
|
+
this.signatureMap,
|
|
138
|
+
this.coreHM,
|
|
139
|
+
this.environments,
|
|
140
|
+
);
|
|
141
|
+
node.body.accept(inferenceEngine);
|
|
142
|
+
}
|
|
143
|
+
visitFunction(node: Function): void {
|
|
144
|
+
const functionName = node.identifier.value;
|
|
145
|
+
let funcScheme = this.signatureMap.get(functionName);
|
|
146
|
+
// Handle function without signature
|
|
147
|
+
if (!funcScheme) {
|
|
148
|
+
const firstEq = node.equations[0];
|
|
149
|
+
if (!firstEq.body.is(UnguardedBody)) return;
|
|
150
|
+
this.environments.unshift(new Map());
|
|
151
|
+
const inferenceEngine = new InferenceEngine(
|
|
152
|
+
this.signatureMap,
|
|
153
|
+
this.coreHM,
|
|
154
|
+
this.environments,
|
|
155
|
+
);
|
|
156
|
+
const paramTypes = firstEq.patterns.map(() => this.coreHM.freshVar());
|
|
157
|
+
|
|
158
|
+
paramTypes.forEach((type, i) => {
|
|
159
|
+
try {
|
|
160
|
+
new PatternVisitor(
|
|
161
|
+
this.coreHM,
|
|
162
|
+
this.signatureMap,
|
|
163
|
+
type,
|
|
164
|
+
this.environments,
|
|
165
|
+
inferenceEngine,
|
|
166
|
+
).visit(firstEq.patterns[i]);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
this.errors.push(
|
|
169
|
+
`Type error in '${functionName}': ${error instanceof Error ? error.message : String(error)}`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const equationStatements = firstEq.body.sequence.statements;
|
|
175
|
+
equationStatements.forEach((stmt) =>
|
|
176
|
+
stmt.accept(
|
|
177
|
+
new FunctionCheckerVisitor(
|
|
178
|
+
this.environments,
|
|
179
|
+
this.signatureMap,
|
|
180
|
+
this.coreHM,
|
|
181
|
+
this.errors,
|
|
182
|
+
),
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
const returnNode = equationStatements.find(
|
|
186
|
+
(stmt) => stmt instanceof Return,
|
|
187
|
+
);
|
|
188
|
+
if (!returnNode) return;
|
|
189
|
+
|
|
190
|
+
const returnResult = returnNode.accept(inferenceEngine);
|
|
191
|
+
if (returnResult.success === false) {
|
|
192
|
+
this.errors.push(
|
|
193
|
+
`Type error in '${functionName}': ${returnResult.error}`,
|
|
194
|
+
);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const inferredBodyType = returnResult.value;
|
|
198
|
+
|
|
199
|
+
const fullFuncType = paramTypes.reduceRight(
|
|
200
|
+
(acc, param) => functionType(param, acc),
|
|
201
|
+
inferredBodyType,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// Generalize the inferred type to create a polymorphic type scheme
|
|
205
|
+
this.environments.shift();
|
|
206
|
+
funcScheme = this.coreHM.generalize(this.environments[0], fullFuncType);
|
|
207
|
+
this.signatureMap.set(functionName, funcScheme);
|
|
208
|
+
this.environments[0].set(functionName, funcScheme);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const expectedArity = getArity(this.coreHM.instantiate(funcScheme));
|
|
212
|
+
for (const [index, equation] of node.equations.entries()) {
|
|
213
|
+
if (!equation.body.is(UnguardedBody)) continue;
|
|
214
|
+
if (equation.patterns.length > expectedArity) {
|
|
215
|
+
this.errors.push(
|
|
216
|
+
`Type error in '${functionName}': Too many parameters in equation ${index}. Expected max ${expectedArity}, got ${equation.patterns.length}`,
|
|
217
|
+
);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const funcType = this.coreHM.instantiate(funcScheme);
|
|
222
|
+
let expectedBodyType = funcType;
|
|
223
|
+
|
|
224
|
+
for (let i = 0; i < equation.patterns.length; i++) {
|
|
225
|
+
if (isFunctionType(expectedBodyType)) {
|
|
226
|
+
expectedBodyType = expectedBodyType.args[1];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const patternTypes = getArgumentTypes(funcType);
|
|
230
|
+
|
|
231
|
+
this.environments.unshift(new Map());
|
|
232
|
+
const inferenceEngine = new InferenceEngine(
|
|
233
|
+
this.signatureMap,
|
|
234
|
+
this.coreHM,
|
|
235
|
+
this.environments,
|
|
236
|
+
);
|
|
237
|
+
equation.patterns.forEach((pattern, i) => {
|
|
238
|
+
const argType = patternTypes[i]; // El tipo correspondiente a este patrón
|
|
239
|
+
try {
|
|
240
|
+
pattern.accept(
|
|
241
|
+
new PatternVisitor(
|
|
242
|
+
this.coreHM,
|
|
243
|
+
this.signatureMap,
|
|
244
|
+
argType,
|
|
245
|
+
this.environments,
|
|
246
|
+
inferenceEngine,
|
|
247
|
+
),
|
|
248
|
+
);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
this.errors.push(
|
|
251
|
+
`Type error in '${functionName}': ${error instanceof Error ? error.message : String(error)}`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const equationStatements = equation.body.sequence.statements;
|
|
257
|
+
equationStatements.forEach((stmt) =>
|
|
258
|
+
stmt.accept(
|
|
259
|
+
new FunctionCheckerVisitor(
|
|
260
|
+
this.environments,
|
|
261
|
+
this.signatureMap,
|
|
262
|
+
this.coreHM,
|
|
263
|
+
this.errors,
|
|
264
|
+
),
|
|
265
|
+
),
|
|
266
|
+
);
|
|
267
|
+
const returnNode = equationStatements.find(
|
|
268
|
+
(stmt) => stmt instanceof Return,
|
|
269
|
+
);
|
|
270
|
+
if (!returnNode) return;
|
|
271
|
+
const returnResult = returnNode.accept(inferenceEngine);
|
|
272
|
+
if (returnResult.success === false) {
|
|
273
|
+
this.errors.push(
|
|
274
|
+
`Type error in '${functionName}': ${returnResult.error}`,
|
|
275
|
+
);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const sub = this.coreHM.unify(returnResult.value, expectedBodyType);
|
|
280
|
+
if (sub.success === false) throw Error(sub.error);
|
|
281
|
+
this.environments.shift();
|
|
282
|
+
} catch (error: any) {
|
|
283
|
+
this.errors.push(`Type error in '${functionName}': ${error.message}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
fallback(node: ASTNode): void {}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export class TypeChecker {
|
|
291
|
+
private signatureMap: Map<string, TypeScheme>;
|
|
292
|
+
private errors: string[];
|
|
293
|
+
private typeAliasMap = new Map<string, Type>();
|
|
294
|
+
private coreHM = new CoreHM(this.typeAliasMap, new Map(staticTypeClasses));
|
|
295
|
+
|
|
296
|
+
constructor() {
|
|
297
|
+
this.signatureMap = new Map<string, TypeScheme>();
|
|
298
|
+
this.errors = [];
|
|
299
|
+
}
|
|
300
|
+
check(ast: AST): string[] {
|
|
301
|
+
const recordMap = new Map<string, Type>();
|
|
302
|
+
|
|
303
|
+
// Phase 1: Collect declarations
|
|
304
|
+
const collector = new DeclarationCollectorVisitor(
|
|
305
|
+
this.errors,
|
|
306
|
+
this.typeAliasMap,
|
|
307
|
+
recordMap,
|
|
308
|
+
this.signatureMap,
|
|
309
|
+
this.coreHM,
|
|
310
|
+
);
|
|
311
|
+
for (const node of ast) {
|
|
312
|
+
try {
|
|
313
|
+
node.accept(collector);
|
|
314
|
+
} catch (error) {
|
|
315
|
+
this.errors.push(
|
|
316
|
+
error instanceof Error ? error.message : String(error),
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (this.errors.length > 0) return this.errors;
|
|
322
|
+
|
|
323
|
+
// Phase 2: Infer and check functions
|
|
324
|
+
this.inferencePass(ast);
|
|
325
|
+
|
|
326
|
+
return this.errors;
|
|
327
|
+
}
|
|
328
|
+
public inferExpression(expr: ASTNode): string {
|
|
329
|
+
if (!this.coreHM || !this.signatureMap)
|
|
330
|
+
throw new Error("Environment not initialized.");
|
|
331
|
+
|
|
332
|
+
const globalEnv = new Map<string, TypeScheme>(this.signatureMap);
|
|
333
|
+
const inferenceEngine = new InferenceEngine(
|
|
334
|
+
this.signatureMap,
|
|
335
|
+
this.coreHM,
|
|
336
|
+
[globalEnv],
|
|
337
|
+
);
|
|
338
|
+
const result = expr.accept(inferenceEngine);
|
|
339
|
+
if (result.success === false) throw new Error(result.error);
|
|
340
|
+
|
|
341
|
+
return showType(result.value);
|
|
342
|
+
}
|
|
343
|
+
public getKnownSymbols(): string[] {
|
|
344
|
+
return Array.from(this.signatureMap.keys());
|
|
345
|
+
}
|
|
346
|
+
inferencePass(ast: AST): void {
|
|
347
|
+
// Step 1: Register all functions in env
|
|
348
|
+
const globalEnv = new Map<string, TypeScheme>(this.signatureMap);
|
|
349
|
+
const visitor1 = new FunctionRegistrarVisitor(
|
|
350
|
+
globalEnv,
|
|
351
|
+
this.signatureMap,
|
|
352
|
+
this.coreHM,
|
|
353
|
+
);
|
|
354
|
+
for (const node of ast) {
|
|
355
|
+
node.accept(visitor1);
|
|
356
|
+
}
|
|
357
|
+
// Step 2: Infer and check each function
|
|
358
|
+
const visitor2 = new FunctionCheckerVisitor(
|
|
359
|
+
[globalEnv],
|
|
360
|
+
this.signatureMap,
|
|
361
|
+
this.coreHM,
|
|
362
|
+
this.errors,
|
|
363
|
+
);
|
|
364
|
+
for (const node of ast) {
|
|
365
|
+
node.accept(visitor2);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
type SeenTypeNames = Map<number, string>;
|
|
371
|
+
|
|
372
|
+
const getVarName = (
|
|
373
|
+
id: number,
|
|
374
|
+
name: string | undefined,
|
|
375
|
+
seen: SeenTypeNames,
|
|
376
|
+
): string => {
|
|
377
|
+
if (name) return name;
|
|
378
|
+
if (seen.has(id)) return seen.get(id)!;
|
|
379
|
+
|
|
380
|
+
const index = seen.size;
|
|
381
|
+
const letter = String.fromCharCode(97 + (index % 26));
|
|
382
|
+
const suffix = index >= 26 ? Math.floor(index / 26).toString() : "";
|
|
383
|
+
const generatedName = `${letter}${suffix}`;
|
|
384
|
+
|
|
385
|
+
seen.set(id, generatedName);
|
|
386
|
+
return generatedName;
|
|
387
|
+
};
|
|
388
|
+
const collectTypeVars = (t: Type): TypeVar[] =>
|
|
389
|
+
t.type === "TypeVar" ? [t] : t.args.flatMap(collectTypeVars);
|
|
390
|
+
const formatBody = (t: Type, seen: SeenTypeNames): string => {
|
|
391
|
+
if (t.type === "TypeVar") return getVarName(t.id, t.name, seen);
|
|
392
|
+
|
|
393
|
+
if (isFunctionType(t)) {
|
|
394
|
+
const left = formatBody(t.args[0], seen);
|
|
395
|
+
const right = formatBody(t.args[1], seen);
|
|
396
|
+
return isFunctionType(t.args[0])
|
|
397
|
+
? `(${left}) -> ${right}`
|
|
398
|
+
: `${left} -> ${right}`;
|
|
399
|
+
}
|
|
400
|
+
if (isListType(t)) {
|
|
401
|
+
if (
|
|
402
|
+
t.args[0].type === "TypeConstructor" &&
|
|
403
|
+
t.args[0].name === YUTYPES.YuChar
|
|
404
|
+
) {
|
|
405
|
+
return YUTYPES.YuString;
|
|
406
|
+
}
|
|
407
|
+
return `[${showType(t.args[0])}]`;
|
|
408
|
+
}
|
|
409
|
+
if (isTupleType(t))
|
|
410
|
+
return `(${t.args.map((arg) => showType(arg, seen)).join(", ")})`;
|
|
411
|
+
|
|
412
|
+
const name = t.name;
|
|
413
|
+
return t.args.length
|
|
414
|
+
? `${name} ${t.args.map((a) => formatBody(a, seen)).join(" ")}`
|
|
415
|
+
: name;
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
export function showType(t: Type, seen: SeenTypeNames = new Map()): string {
|
|
419
|
+
const bodyStr = formatBody(t, seen);
|
|
420
|
+
const constraints = Array.from(
|
|
421
|
+
new Set(
|
|
422
|
+
collectTypeVars(t).flatMap(({ constraints, id, name }) =>
|
|
423
|
+
constraints.map((c) => `${c} ${getVarName(id, name, seen)}`),
|
|
424
|
+
),
|
|
425
|
+
),
|
|
426
|
+
);
|
|
427
|
+
if (constraints.length === 0) return bodyStr;
|
|
428
|
+
const context =
|
|
429
|
+
constraints.length === 1 ? constraints[0] : `(${constraints.join(", ")})`;
|
|
430
|
+
return `${context} => ${bodyStr}`;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function getReturnType(type: Type): Type {
|
|
434
|
+
let t: Type = type;
|
|
435
|
+
while (isFunctionType(t)) t = t.args[1];
|
|
436
|
+
return t;
|
|
437
|
+
}
|
|
438
|
+
export function getArgumentTypes(type: Type): Type[] {
|
|
439
|
+
const args: Type[] = [];
|
|
440
|
+
let t: Type = type;
|
|
441
|
+
while (isFunctionType(t)) {
|
|
442
|
+
args.push(t.args[0]);
|
|
443
|
+
t = t.args[1];
|
|
444
|
+
}
|
|
445
|
+
return args;
|
|
446
|
+
}
|
|
447
|
+
export function getArity(type: Type): number {
|
|
448
|
+
return getArgumentTypes(type).length;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export function isFunctionType(t: Type): t is FunctionType {
|
|
452
|
+
return (
|
|
453
|
+
t.type === "TypeConstructor" &&
|
|
454
|
+
t.name === YUTYPES.Arrow &&
|
|
455
|
+
t.args.length === 2
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
export function isListType(t: Type): t is ListType {
|
|
459
|
+
return t.name === YUTYPES.List;
|
|
460
|
+
}
|
|
461
|
+
export function isTupleType(t: Type): t is TupleType {
|
|
462
|
+
return t.name === YUTYPES.Tuple;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// If either side is a string primitive or already inferred as stringType,
|
|
466
|
+
// we treat the whole operation as string concatenation.
|
|
467
|
+
export const isString = (t: Type) =>
|
|
468
|
+
(t.type === "TypeConstructor" && t.name === YUTYPES.YuString) ||
|
|
469
|
+
(t.type === "TypeConstructor" &&
|
|
470
|
+
t.name === YUTYPES.List &&
|
|
471
|
+
t.args[0].type === "TypeConstructor" &&
|
|
472
|
+
t.args[0].name === YUTYPES.YuChar);
|
|
473
|
+
|
|
474
|
+
export function functionType(params: Type, returnType: Type): FunctionType {
|
|
475
|
+
return {
|
|
476
|
+
type: "TypeConstructor",
|
|
477
|
+
name: YUTYPES.Arrow,
|
|
478
|
+
args: [params, returnType],
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
export function listType(elementsType: Type): ListType {
|
|
482
|
+
return {
|
|
483
|
+
type: "TypeConstructor",
|
|
484
|
+
name: YUTYPES.List,
|
|
485
|
+
args: [elementsType],
|
|
486
|
+
};
|
|
487
|
+
}
|