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