yukigo-haskell-parser 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 (63) hide show
  1. package/.mocharc.json +4 -0
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +10 -0
  4. package/dist/index.d.ts +7 -0
  5. package/dist/index.js +54 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/parser/grammar.d.ts +28 -0
  8. package/dist/parser/grammar.js +404 -0
  9. package/dist/parser/grammar.js.map +1 -0
  10. package/dist/parser/layoutPreprocessor.d.ts +1 -0
  11. package/dist/parser/layoutPreprocessor.js +22 -0
  12. package/dist/parser/layoutPreprocessor.js.map +1 -0
  13. package/dist/parser/lexer.d.ts +42 -0
  14. package/dist/parser/lexer.js +75 -0
  15. package/dist/parser/lexer.js.map +1 -0
  16. package/dist/parser/preprocessor.d.ts +28 -0
  17. package/dist/parser/preprocessor.js +453 -0
  18. package/dist/parser/preprocessor.js.map +1 -0
  19. package/dist/prelude.d.ts +1 -0
  20. package/dist/prelude.js +205 -0
  21. package/dist/prelude.js.map +1 -0
  22. package/dist/typechecker/DeclarationCollector.d.ts +15 -0
  23. package/dist/typechecker/DeclarationCollector.js +80 -0
  24. package/dist/typechecker/DeclarationCollector.js.map +1 -0
  25. package/dist/typechecker/TypeBuilder.d.ts +12 -0
  26. package/dist/typechecker/TypeBuilder.js +113 -0
  27. package/dist/typechecker/TypeBuilder.js.map +1 -0
  28. package/dist/typechecker/checker.d.ts +80 -0
  29. package/dist/typechecker/checker.js +269 -0
  30. package/dist/typechecker/checker.js.map +1 -0
  31. package/dist/typechecker/core.d.ts +14 -0
  32. package/dist/typechecker/core.js +142 -0
  33. package/dist/typechecker/core.js.map +1 -0
  34. package/dist/typechecker/inference.d.ts +69 -0
  35. package/dist/typechecker/inference.js +984 -0
  36. package/dist/typechecker/inference.js.map +1 -0
  37. package/dist/utils/helpers.d.ts +2 -0
  38. package/dist/utils/helpers.js +25 -0
  39. package/dist/utils/helpers.js.map +1 -0
  40. package/dist/utils/types.d.ts +6 -0
  41. package/dist/utils/types.js +53 -0
  42. package/dist/utils/types.js.map +1 -0
  43. package/package.json +36 -0
  44. package/src/index.ts +55 -0
  45. package/src/parser/grammar.ne +463 -0
  46. package/src/parser/grammar.ts +512 -0
  47. package/src/parser/layoutPreprocessor.ts +21 -0
  48. package/src/parser/lexer.ts +77 -0
  49. package/src/parser/preprocessor.ne +375 -0
  50. package/src/parser/preprocessor.ts +502 -0
  51. package/src/prelude.ts +206 -0
  52. package/src/typechecker/DeclarationCollector.ts +104 -0
  53. package/src/typechecker/TypeBuilder.ts +148 -0
  54. package/src/typechecker/checker.ts +395 -0
  55. package/src/typechecker/core.ts +162 -0
  56. package/src/typechecker/inference.ts +1327 -0
  57. package/src/utils/helpers.ts +29 -0
  58. package/src/utils/types.ts +56 -0
  59. package/tests/parser.spec.ts +823 -0
  60. package/tests/prelude.spec.ts +22 -0
  61. package/tests/preprocessor.spec.ts +69 -0
  62. package/tests/typechecker.spec.ts +310 -0
  63. package/tsconfig.json +17 -0
@@ -0,0 +1,984 @@
1
+ import { VariablePattern, } from "@yukigo/ast";
2
+ import { getArgumentTypes, getArity, isFunctionType, showType, functionType, isListType, isTupleType, listType, booleanType, numberType, stringType, FunctionRegistrarVisitor, FunctionCheckerVisitor, getReturnType, } from "./checker.js";
3
+ import { TypeBuilder } from "./TypeBuilder.js";
4
+ export class PatternVisitor {
5
+ coreHM;
6
+ signatureMap;
7
+ expectedType;
8
+ envs;
9
+ inferenceEngine;
10
+ constructor(coreHM, signatureMap, expectedType, envs, inferenceEngine) {
11
+ this.coreHM = coreHM;
12
+ this.signatureMap = signatureMap;
13
+ this.expectedType = expectedType;
14
+ this.envs = envs;
15
+ this.inferenceEngine = inferenceEngine;
16
+ }
17
+ visitVariablePattern(node) {
18
+ const varName = node.name.value.toString();
19
+ if (this.envs[0].has(varName))
20
+ throw new Error(`Duplicate variable name '${varName}'`);
21
+ this.envs[0].set(varName, {
22
+ type: "TypeScheme",
23
+ quantifiers: [],
24
+ body: this.expectedType,
25
+ constraints: new Map(),
26
+ });
27
+ }
28
+ visitLiteralPattern(node) {
29
+ const value = node.name;
30
+ const inferredLiteral = value.accept(this.inferenceEngine);
31
+ if (inferredLiteral.success === false)
32
+ throw Error(inferredLiteral.error);
33
+ let literalType = inferredLiteral.value;
34
+ const unifyResult = this.coreHM.unify(literalType, this.expectedType);
35
+ if (unifyResult.success === false)
36
+ throw new Error(`Literal pattern type mismatch: expected ${showType(this.expectedType)} but found ${showType(literalType)}`);
37
+ }
38
+ visitApplicationPattern(node) {
39
+ const appCtorScheme = this.signatureMap.get(node.symbol.value);
40
+ if (!appCtorScheme)
41
+ throw new Error(`Unknown constructor: ${node.symbol.value}`);
42
+ const appCtorType = this.coreHM.instantiate(appCtorScheme);
43
+ // Handle both curried and non-curried constructor types
44
+ let currentType = appCtorType;
45
+ let argIndex = 0;
46
+ const patternTypes = getArgumentTypes(appCtorType);
47
+ patternTypes.forEach((argType, i) => {
48
+ node.args[argIndex].accept(new PatternVisitor(this.coreHM, this.signatureMap, argType, this.envs, this.inferenceEngine));
49
+ });
50
+ // Check if we've consumed all expected arguments
51
+ const unifyResult = this.coreHM.unify(currentType, this.expectedType);
52
+ if (unifyResult.success === false)
53
+ throw new Error(`Constructor ${node.symbol.value} arguments don't match expected type: ${unifyResult.error}`);
54
+ }
55
+ visitTuplePattern(node) {
56
+ let tupleElementTypes;
57
+ if (this.expectedType.type === "TypeVar") {
58
+ tupleElementTypes = node.elements.map(() => this.coreHM.freshVar());
59
+ const tupleType = {
60
+ type: "TypeConstructor",
61
+ name: "Tuple",
62
+ args: tupleElementTypes,
63
+ };
64
+ const unifyResult = this.coreHM.unify(this.expectedType, tupleType);
65
+ if (unifyResult.success === false)
66
+ throw new Error(`Pattern type mismatch: ${unifyResult.error}`);
67
+ }
68
+ else if (isTupleType(this.expectedType)) {
69
+ if (node.elements.length !== this.expectedType.args.length)
70
+ throw new Error(`Tuple arity mismatch`);
71
+ tupleElementTypes = this.expectedType.args;
72
+ }
73
+ else {
74
+ throw new Error(`Pattern expects a tuple but found non-tuple type: ${showType(this.expectedType)}`);
75
+ }
76
+ node.elements.forEach((pattern, i) => {
77
+ new PatternVisitor(this.coreHM, this.signatureMap, tupleElementTypes[i], this.envs, this.inferenceEngine).visit(pattern);
78
+ });
79
+ }
80
+ visitListPattern(node) {
81
+ let elemType;
82
+ if (this.expectedType.type === "TypeVar") {
83
+ elemType = this.coreHM.freshVar();
84
+ const listT = listType(elemType);
85
+ const unifyResult = this.coreHM.unify(this.expectedType, listT);
86
+ if (unifyResult.success === false)
87
+ throw new Error(`Pattern type mismatch: ${unifyResult.error}`);
88
+ }
89
+ else if (isListType(this.expectedType)) {
90
+ elemType = this.expectedType.args[0];
91
+ }
92
+ else {
93
+ throw new Error(`Pattern expects a list but found non-list type`);
94
+ }
95
+ node.elements.forEach((pat) => {
96
+ new PatternVisitor(this.coreHM, this.signatureMap, elemType, this.envs, this.inferenceEngine).visit(pat);
97
+ });
98
+ }
99
+ visitFunctorPattern(node) {
100
+ throw new Error("Method not implemented.");
101
+ }
102
+ visitAsPattern(node) {
103
+ // Process the main pattern
104
+ new PatternVisitor(this.coreHM, this.signatureMap, this.expectedType, this.envs, this.inferenceEngine).visit(node.pattern);
105
+ // Process the alias if it's a variable
106
+ if (node.alias instanceof VariablePattern) {
107
+ const varName = node.alias.name.value.toString();
108
+ if (this.envs[0].has(varName))
109
+ throw new Error(`Duplicate variable name: ${varName}`);
110
+ this.envs[0].set(varName, {
111
+ type: "TypeScheme",
112
+ quantifiers: [],
113
+ body: this.expectedType,
114
+ constraints: new Map(),
115
+ });
116
+ }
117
+ }
118
+ visitWildcardPattern(node) {
119
+ return {
120
+ success: true,
121
+ value: this.coreHM.freshVar(),
122
+ };
123
+ }
124
+ visitUnionPattern(node) {
125
+ throw new Error("Method not implemented.");
126
+ }
127
+ visitConstructorPattern(node) {
128
+ const ctorScheme = this.signatureMap.get(node.constr);
129
+ if (!ctorScheme)
130
+ throw new Error(`Unknown constructor: ${node.constr}`);
131
+ const ctorType = this.coreHM.instantiate(ctorScheme);
132
+ // Extract argument types and return type from constructor
133
+ const argTypes = getArgumentTypes(ctorType);
134
+ const returnType = getReturnType(ctorType);
135
+ if (argTypes.length !== node.patterns.length)
136
+ throw new Error(`Constructor ${node.constr} expects ${argTypes.length} arguments but pattern has ${node.patterns.length}`);
137
+ // Unify the constructor's return type with expected pattern type
138
+ const unifyResult = this.coreHM.unify(returnType, this.expectedType);
139
+ if (!unifyResult.success)
140
+ throw new Error(`Constructor ${node.constr} return type ${showType(returnType)} doesn't match expected type ${showType(this.expectedType)}`);
141
+ // Apply substitutions to argument types
142
+ const subst = unifyResult.value;
143
+ const unifiedArgTypes = argTypes.map((argType) => this.coreHM.applySubst(subst, argType));
144
+ // Process each argument pattern with its correct type
145
+ node.patterns.forEach((pattern, i) => {
146
+ new PatternVisitor(this.coreHM, this.signatureMap, unifiedArgTypes[i], this.envs, this.inferenceEngine).visit(pattern);
147
+ });
148
+ }
149
+ visitConsPattern(node) {
150
+ const elemType = this.coreHM.freshVar();
151
+ const listT = listType(elemType);
152
+ // Unify the expected type with the list type
153
+ const unifyResult = this.coreHM.unify(this.expectedType, listT);
154
+ if (unifyResult.success === false)
155
+ throw new Error(`Pattern type mismatch: ${unifyResult.error}`);
156
+ // After unification, get the element type from the unified type
157
+ const unifiedElemType = this.coreHM.applySubst(unifyResult.value, elemType);
158
+ // Process head pattern with the element type
159
+ new PatternVisitor(this.coreHM, this.signatureMap, unifiedElemType, this.envs, this.inferenceEngine).visit(node.head);
160
+ // Process tail pattern with a list of the same element type
161
+ const tailType = listType(unifiedElemType);
162
+ new PatternVisitor(this.coreHM, this.signatureMap, tailType, this.envs, this.inferenceEngine).visit(node.tail);
163
+ }
164
+ visit(node) {
165
+ node.accept(this);
166
+ }
167
+ }
168
+ export class InferenceEngine {
169
+ signatureMap;
170
+ coreHM;
171
+ envs;
172
+ constructor(signatureMap, coreHM, envs) {
173
+ this.signatureMap = signatureMap;
174
+ this.coreHM = coreHM;
175
+ this.envs = envs;
176
+ }
177
+ visitNumberPrimitive(node) {
178
+ return {
179
+ success: true,
180
+ value: numberType,
181
+ };
182
+ }
183
+ visitBooleanPrimitive(node) {
184
+ return {
185
+ success: true,
186
+ value: booleanType,
187
+ };
188
+ }
189
+ visitStringPrimitive(node) {
190
+ return {
191
+ success: true,
192
+ value: stringType,
193
+ };
194
+ }
195
+ visitListPrimitive(node) {
196
+ if (node.elements.length === 0) {
197
+ // Empty list - polymorphic
198
+ const elemType = this.coreHM.freshVar();
199
+ return {
200
+ success: true,
201
+ value: listType(elemType),
202
+ };
203
+ }
204
+ // Infer type of first element
205
+ const firstResult = node.elements[0].accept(this);
206
+ if (firstResult.success === false)
207
+ return firstResult;
208
+ // Check all elements match first element's type
209
+ for (const element of node.elements.slice(1)) {
210
+ const elemResult = element.accept(this);
211
+ if (elemResult.success === false)
212
+ return elemResult;
213
+ const unifyResult = this.coreHM.unify(elemResult.value, firstResult.value);
214
+ if (unifyResult.success === false) {
215
+ return {
216
+ success: false,
217
+ error: `List elements must have the same type`,
218
+ };
219
+ }
220
+ }
221
+ return {
222
+ success: true,
223
+ value: listType(firstResult.value),
224
+ };
225
+ }
226
+ visitNilPrimitive(node) {
227
+ return {
228
+ success: true,
229
+ value: {
230
+ type: "TypeConstructor",
231
+ name: "YuNil",
232
+ args: [],
233
+ },
234
+ };
235
+ }
236
+ visitCharPrimitive(node) {
237
+ return {
238
+ success: true,
239
+ value: {
240
+ type: "TypeConstructor",
241
+ name: "YuChar",
242
+ args: [],
243
+ },
244
+ };
245
+ }
246
+ visitSymbolPrimitive(node) {
247
+ const name = node.value;
248
+ const searchRes = searchInEnvironments(this.envs, name);
249
+ if (searchRes.success === false)
250
+ return searchRes;
251
+ return { success: true, value: this.coreHM.instantiate(searchRes.value) };
252
+ }
253
+ visitArithmeticUnaryOperation(node) {
254
+ const operandResult = node.operand.accept(this);
255
+ if (!operandResult.success)
256
+ return operandResult;
257
+ const unifyOperand = this.coreHM.unify(operandResult.value, numberType);
258
+ if (!unifyOperand.success)
259
+ return {
260
+ success: false,
261
+ error: `Operand of ${node.operator} must be a number`,
262
+ };
263
+ return { success: true, value: numberType };
264
+ }
265
+ visitArithmeticBinaryOperation(node) {
266
+ const leftResult = node.left.accept(this);
267
+ if (!leftResult.success)
268
+ return leftResult;
269
+ const rightResult = node.right.accept(this);
270
+ if (!rightResult.success)
271
+ return rightResult;
272
+ const unifyLeft = this.coreHM.unify(leftResult.value, numberType);
273
+ if (!unifyLeft.success)
274
+ return {
275
+ success: false,
276
+ error: `Left operand of ${node.operator} must be a number`,
277
+ };
278
+ const unifyRight = this.coreHM.unify(rightResult.value, numberType);
279
+ if (!unifyRight.success)
280
+ return {
281
+ success: false,
282
+ error: `Right operand of ${node.operator} must be a number`,
283
+ };
284
+ return { success: true, value: numberType };
285
+ }
286
+ visitListUnaryOperation(node) {
287
+ switch (node.operator) {
288
+ case "DetectMin":
289
+ case "DetectMax": {
290
+ const operandResult = node.operand.accept(this);
291
+ if (!operandResult.success)
292
+ return operandResult;
293
+ // Operand must be a list of ordenables (YuNumber, YuString, YuChar)
294
+ const ordType = this.coreHM.freshVar(["Ord"]);
295
+ const listT = listType(ordType);
296
+ const unifyOperand = this.coreHM.unify(operandResult.value, listT);
297
+ if (!unifyOperand.success)
298
+ return {
299
+ success: false,
300
+ error: `${node.operator} expects operand to be an Ordenable Type`,
301
+ };
302
+ const finalType = this.coreHM.applySubst(unifyOperand.value, ordType);
303
+ // Result is a the resolved type of the elements of the list
304
+ return { success: true, value: finalType };
305
+ }
306
+ case "Size": {
307
+ const operandResult = node.operand.accept(this);
308
+ if (!operandResult.success)
309
+ return operandResult;
310
+ // Operand must be a YuList
311
+ const freshInputVar = this.coreHM.freshVar();
312
+ const listInputType = listType(freshInputVar);
313
+ const unifyOperand = this.coreHM.unify(operandResult.value, listInputType);
314
+ if (!unifyOperand.success)
315
+ return {
316
+ success: false,
317
+ error: `${node.operator} expects operand must be a YuList`,
318
+ };
319
+ // Result is a YuNumber
320
+ return { success: true, value: numberType };
321
+ }
322
+ default:
323
+ return {
324
+ success: false,
325
+ error: `Unknown Unary List operation with operator ${node.operator}.`,
326
+ };
327
+ }
328
+ }
329
+ visitListBinaryOperation(node) {
330
+ switch (node.operator) {
331
+ case "Collect": {
332
+ const leftResult = node.left.accept(this);
333
+ if (!leftResult.success)
334
+ return leftResult;
335
+ const rightResult = node.right.accept(this);
336
+ if (!rightResult.success)
337
+ return rightResult;
338
+ const funcArity = getArity(leftResult.value);
339
+ if (funcArity !== 1)
340
+ return {
341
+ success: false,
342
+ error: `${node.operator}'s left operand expects to have only one argument`,
343
+ };
344
+ // Right-hand side must be a list [a]
345
+ const freshInputVar = this.coreHM.freshVar();
346
+ const listInputType = listType(freshInputVar);
347
+ const unifyRight = this.coreHM.unify(rightResult.value, listInputType);
348
+ if (!unifyRight.success)
349
+ return {
350
+ success: false,
351
+ error: `${node.operator}'s right operand must be a list`,
352
+ };
353
+ const elementType = unifyRight.value.get(freshInputVar.id);
354
+ // Left-hand side must be a function: elementType -> outputType
355
+ const freshOutputVar = this.coreHM.freshVar();
356
+ const funcType = functionType(elementType, freshOutputVar);
357
+ const unifyLeft = this.coreHM.unify(leftResult.value, funcType);
358
+ if (!unifyLeft.success) {
359
+ return {
360
+ success: false,
361
+ error: `${node.operator}'s left operand must be a function of type ${showType(elementType)} -> a`,
362
+ };
363
+ }
364
+ // Result is a list of the function's output type: [outputType]
365
+ const resultType = listType(freshOutputVar);
366
+ const subType = this.coreHM.applySubst(unifyLeft.value, resultType);
367
+ // Return the result type, now fully resolved with substitutions
368
+ return { success: true, value: subType };
369
+ }
370
+ case "Select": {
371
+ const leftResult = node.left.accept(this);
372
+ if (!leftResult.success)
373
+ return leftResult;
374
+ const rightResult = node.right.accept(this);
375
+ if (!rightResult.success)
376
+ return rightResult;
377
+ const funcArity = getArity(leftResult.value);
378
+ if (funcArity !== 1)
379
+ return {
380
+ success: false,
381
+ error: `${node.operator}'s left operand expects to have only one argument`,
382
+ };
383
+ // Right-hand side must be a list [a]
384
+ const freshInputVar = this.coreHM.freshVar();
385
+ const listInputType = listType(freshInputVar);
386
+ const unifyRight = this.coreHM.unify(rightResult.value, listInputType);
387
+ if (!unifyRight.success)
388
+ return {
389
+ success: false,
390
+ error: `${node.operator}'s right operand must be a list`,
391
+ };
392
+ const elementType = unifyRight.value.get(freshInputVar.id);
393
+ // Left-hand side must be a function: elementType -> Bool
394
+ const funcType = functionType(elementType, booleanType);
395
+ const unifyLeft = this.coreHM.unify(leftResult.value, funcType);
396
+ if (!unifyLeft.success) {
397
+ return {
398
+ success: false,
399
+ error: `${node.operator}'s left operand must be a function of type ${showType(funcType)}`,
400
+ };
401
+ }
402
+ // Result is a list of the function's output type: [outputType]
403
+ const resultType = listType(elementType);
404
+ const subType = this.coreHM.applySubst(unifyLeft.value, resultType);
405
+ // Return the result type, now fully resolved with substitutions
406
+ return { success: true, value: subType };
407
+ }
408
+ case "Detect": {
409
+ const leftResult = node.left.accept(this);
410
+ if (!leftResult.success)
411
+ return leftResult;
412
+ const rightResult = node.right.accept(this);
413
+ if (!rightResult.success)
414
+ return rightResult;
415
+ const funcArity = getArity(leftResult.value);
416
+ if (funcArity !== 1)
417
+ return {
418
+ success: false,
419
+ error: `${node.operator}'s left operand expects to have only one argument`,
420
+ };
421
+ // Right-hand side must be a list [a]
422
+ const freshInputVar = this.coreHM.freshVar();
423
+ const listInputType = listType(freshInputVar);
424
+ const unifyRight = this.coreHM.unify(rightResult.value, listInputType);
425
+ if (!unifyRight.success)
426
+ return {
427
+ success: false,
428
+ error: `${node.operator}'s right operand must be a list`,
429
+ };
430
+ const elementType = unifyRight.value.get(freshInputVar.id);
431
+ // Left-hand side must be a function: elementType -> Bool
432
+ const funcType = functionType(elementType, booleanType);
433
+ const unifyLeft = this.coreHM.unify(leftResult.value, funcType);
434
+ if (!unifyLeft.success) {
435
+ return {
436
+ success: false,
437
+ error: `${node.operator}'s left operand must be a function of type ${showType(funcType)}`,
438
+ };
439
+ }
440
+ // Result is a list of the function's output type: [outputType]
441
+ const subType = this.coreHM.applySubst(unifyLeft.value, elementType);
442
+ // Return the result type, now fully resolved with substitutions
443
+ return { success: true, value: subType };
444
+ }
445
+ case "AnySatisfy":
446
+ case "AllSatisfy": {
447
+ const leftResult = node.left.accept(this);
448
+ if (!leftResult.success)
449
+ return leftResult;
450
+ const rightResult = node.right.accept(this);
451
+ if (!rightResult.success)
452
+ return rightResult;
453
+ const funcArity = getArity(leftResult.value);
454
+ if (funcArity !== 1)
455
+ return {
456
+ success: false,
457
+ error: `${node.operator}'s left operand expects to have only one argument`,
458
+ };
459
+ // Right-hand side must be a list [a]
460
+ const freshInputVar = this.coreHM.freshVar();
461
+ const listInputType = listType(freshInputVar);
462
+ const unifyRight = this.coreHM.unify(rightResult.value, listInputType);
463
+ if (!unifyRight.success)
464
+ return {
465
+ success: false,
466
+ error: `${node.operator}'s right operand must be a list`,
467
+ };
468
+ const elementType = unifyRight.value.get(freshInputVar.id);
469
+ // Left-hand side must be a function: elementType -> Bool
470
+ const funcType = functionType(elementType, booleanType);
471
+ const unifyLeft = this.coreHM.unify(leftResult.value, funcType);
472
+ if (!unifyLeft.success) {
473
+ return {
474
+ success: false,
475
+ error: `${node.operator}'s left operand must be a function of type ${showType(funcType)}`,
476
+ };
477
+ }
478
+ // Result is a list of the function's output type: [outputType]
479
+ const subType = this.coreHM.applySubst(unifyLeft.value, booleanType);
480
+ // Return the result type, now fully resolved with substitutions
481
+ return { success: true, value: subType };
482
+ }
483
+ case "Concat": {
484
+ const leftResult = node.left.accept(this);
485
+ if (!leftResult.success)
486
+ return leftResult;
487
+ const rightResult = node.right.accept(this);
488
+ if (!rightResult.success)
489
+ return rightResult;
490
+ // Create a fresh type variable for the element type
491
+ const elemType = this.coreHM.freshVar();
492
+ const expectedListType = listType(elemType);
493
+ // First, unify left operand with list type
494
+ const unifyLeft = this.coreHM.unify(leftResult.value, expectedListType);
495
+ if (!unifyLeft.success) {
496
+ return {
497
+ success: false,
498
+ error: `Left operand of concat must be a list, got ${showType(leftResult.value)}`,
499
+ };
500
+ }
501
+ // Apply the substitution from left unification to both the element type
502
+ // and the right operand type
503
+ const substElemType = this.coreHM.applySubst(unifyLeft.value, elemType);
504
+ const substRightType = this.coreHM.applySubst(unifyLeft.value, rightResult.value);
505
+ const expectedRightType = listType(substElemType);
506
+ // Now unify the right operand with the updated expected type
507
+ const unifyRight = this.coreHM.unify(substRightType, expectedRightType);
508
+ if (!unifyRight.success) {
509
+ return {
510
+ success: false,
511
+ error: `Concat operation requires both operands to be lists of the same type.`,
512
+ };
513
+ }
514
+ // Combine substitutions and apply to get final result type
515
+ const combinedSubst = this.coreHM.composeSubst(unifyRight.value, unifyLeft.value);
516
+ const finalType = this.coreHM.applySubst(combinedSubst, listType(elemType));
517
+ return { success: true, value: finalType };
518
+ }
519
+ case "GetAt": {
520
+ const leftResult = node.left.accept(this);
521
+ if (!leftResult.success)
522
+ return leftResult;
523
+ const rightResult = node.right.accept(this);
524
+ if (!rightResult.success)
525
+ return rightResult;
526
+ // Create a fresh type variable for the element type
527
+ const elemType = this.coreHM.freshVar();
528
+ const expectedListType = listType(elemType);
529
+ // First, unify left operand with list type
530
+ const unifyLeft = this.coreHM.unify(leftResult.value, expectedListType);
531
+ if (!unifyLeft.success) {
532
+ return {
533
+ success: false,
534
+ error: `Left operand of concat must be a list, got ${showType(leftResult.value)}`,
535
+ };
536
+ }
537
+ // Left-hand side must be a number: YuNumber
538
+ const unifyRight = this.coreHM.unify(rightResult.value, numberType);
539
+ if (!unifyRight.success) {
540
+ return {
541
+ success: false,
542
+ error: `${node.operator}'s left operand must be a ${showType(numberType)}`,
543
+ };
544
+ }
545
+ const substElemType = this.coreHM.applySubst(unifyLeft.value, elemType);
546
+ return { success: true, value: substElemType };
547
+ }
548
+ default:
549
+ return {
550
+ success: false,
551
+ error: `Unknown Binary List operation with operator ${node.operator}.`,
552
+ };
553
+ }
554
+ }
555
+ visitComparisonOperation(node) {
556
+ const leftResult = node.left.accept(this);
557
+ if (!leftResult.success)
558
+ return leftResult;
559
+ const rightResult = node.right.accept(this);
560
+ if (!rightResult.success)
561
+ return rightResult;
562
+ const unifyResult = this.coreHM.unify(leftResult.value, rightResult.value);
563
+ if (!unifyResult.success) {
564
+ return {
565
+ success: false,
566
+ error: `Comparison operands must have the same type`,
567
+ };
568
+ }
569
+ return {
570
+ success: true,
571
+ value: booleanType,
572
+ };
573
+ }
574
+ visitLogicalBinaryOperation(node) {
575
+ const operator = node.operator;
576
+ const leftResult = node.left.accept(this);
577
+ if (!leftResult.success)
578
+ return leftResult;
579
+ const rightResult = node.right.accept(this);
580
+ if (!rightResult.success)
581
+ return rightResult;
582
+ const leftSub = this.coreHM.unify(leftResult.value, booleanType);
583
+ if (!leftSub.success)
584
+ return {
585
+ success: false,
586
+ error: `Left side of ${operator} must be a boolean`,
587
+ };
588
+ const rightSub = this.coreHM.unify(rightResult.value, booleanType);
589
+ if (!rightSub.success)
590
+ return {
591
+ success: false,
592
+ error: `Right side of ${operator} must be a boolean`,
593
+ };
594
+ return { success: true, value: booleanType };
595
+ }
596
+ visitLogicalUnaryOperation(node) {
597
+ throw new Error("Method not implemented.");
598
+ }
599
+ visitBitwiseBinaryOperation(node) {
600
+ throw new Error("Method not implemented.");
601
+ }
602
+ visitBitwiseUnaryOperation(node) {
603
+ throw new Error("Method not implemented.");
604
+ }
605
+ visitStringOperation(node) {
606
+ const leftResult = node.left.accept(this);
607
+ if (!leftResult.success)
608
+ return leftResult;
609
+ const rightResult = node.right.accept(this);
610
+ if (!rightResult.success)
611
+ return rightResult;
612
+ const unifyLeft = this.coreHM.unify(leftResult.value, stringType);
613
+ const unifyRight = this.coreHM.unify(rightResult.value, stringType);
614
+ if (!unifyLeft.success || !unifyRight.success)
615
+ return {
616
+ success: false,
617
+ error: `String operation requires string operands`,
618
+ };
619
+ return { success: true, value: stringType };
620
+ }
621
+ visitUnifyOperation(node) {
622
+ throw new Error("Method not implemented.");
623
+ }
624
+ visitAssignOperation(node) {
625
+ throw new Error("Method not implemented.");
626
+ }
627
+ visitTupleExpr(node) {
628
+ const elementResults = node.elements.map((e) => e.accept(this));
629
+ const errors = elementResults.filter((r) => !r.success);
630
+ if (elementResults.every((res) => res.success === true)) {
631
+ const elementTypes = elementResults.map((r) => r.value);
632
+ const tupleType = {
633
+ type: "TypeConstructor",
634
+ name: `Tuple`,
635
+ args: elementTypes,
636
+ };
637
+ return { success: true, value: tupleType };
638
+ }
639
+ else {
640
+ return errors[0];
641
+ }
642
+ }
643
+ visitFieldExpr(node) {
644
+ throw new Error("Method not implemented.");
645
+ }
646
+ visitDataExpr(node) {
647
+ const ctorScheme = this.signatureMap.get(node.name.value);
648
+ if (!ctorScheme) {
649
+ return {
650
+ success: false,
651
+ error: `Unknown constructor: ${node.name.value}`,
652
+ };
653
+ }
654
+ const ctorType = this.coreHM.instantiate(ctorScheme);
655
+ // Data constructors should be functions
656
+ if (!isFunctionType(ctorType))
657
+ return { success: false, error: "Constructors should be FunctionType" };
658
+ // Check arguments
659
+ let currentType = ctorType;
660
+ for (const arg of node.contents) {
661
+ const argResult = arg.expression.accept(this);
662
+ if (!argResult.success)
663
+ return argResult;
664
+ if (!isFunctionType(currentType)) {
665
+ return {
666
+ success: false,
667
+ error: `Too many arguments to constructor ${node.name.value}`,
668
+ };
669
+ }
670
+ const unifyResult = this.coreHM.unify(argResult.value, currentType.args[0]);
671
+ if (!unifyResult.success) {
672
+ return {
673
+ success: false,
674
+ error: `Argument type mismatch for constructor ${node.name.value}`,
675
+ };
676
+ }
677
+ currentType = currentType.args[1];
678
+ }
679
+ return { success: true, value: currentType };
680
+ }
681
+ visitConsExpr(node) {
682
+ const headResult = node.head.accept(this);
683
+ if (!headResult.success)
684
+ return headResult;
685
+ const tailResult = node.tail.accept(this);
686
+ if (!tailResult.success)
687
+ return tailResult;
688
+ // Create a list type with a fresh element type
689
+ const elemType = this.coreHM.freshVar();
690
+ const listT = listType(elemType);
691
+ // Unify the tail result with the list type
692
+ const unifyResult = this.coreHM.unify(tailResult.value, listT);
693
+ if (unifyResult.success === false) {
694
+ return {
695
+ success: false,
696
+ error: `Tail of cons must be a list: ${unifyResult.error}`,
697
+ };
698
+ }
699
+ // Now we know the tail is a list, so we can get the element type
700
+ const unifiedElemType = this.coreHM.applySubst(unifyResult.value, elemType);
701
+ // Head must match list element type
702
+ const headUnifyResult = this.coreHM.unify(headResult.value, unifiedElemType);
703
+ if (headUnifyResult.success === false) {
704
+ return {
705
+ success: false,
706
+ error: `Head type doesn't match list element type: ${headUnifyResult.error}`,
707
+ };
708
+ }
709
+ // The result is the list type
710
+ return {
711
+ success: true,
712
+ value: this.coreHM.applySubst(unifyResult.value, listT),
713
+ };
714
+ }
715
+ visitLetInExpr(node) {
716
+ const signatureMap = new Map();
717
+ node.declarations.statements.forEach((stmt) => stmt.accept(new FunctionRegistrarVisitor(this.envs[0], signatureMap, this.coreHM)));
718
+ const errors = [];
719
+ node.declarations.statements.forEach((stmt) => stmt.accept(new FunctionCheckerVisitor(this.envs, signatureMap, this.coreHM, errors)));
720
+ if (errors.length > 0) {
721
+ return { success: false, error: errors.join() };
722
+ }
723
+ return node.expression.accept(this);
724
+ }
725
+ visitOtherwise(node) {
726
+ return { success: true, value: booleanType };
727
+ }
728
+ visitCompositionExpression(node) {
729
+ const fResult = node.left.accept(this);
730
+ const gResult = node.right.accept(this);
731
+ if (!fResult.success)
732
+ return fResult;
733
+ if (!gResult.success)
734
+ return gResult;
735
+ const a = this.coreHM.freshVar();
736
+ const b = this.coreHM.freshVar();
737
+ const c = this.coreHM.freshVar();
738
+ const fType = functionType(b, c);
739
+ const gType = functionType(a, b);
740
+ const fSub = this.coreHM.unify(fResult.value, fType);
741
+ const gSub = this.coreHM.unify(gResult.value, gType);
742
+ if (!fSub.success)
743
+ return {
744
+ success: false,
745
+ error: "Left operand of composition must be a function",
746
+ };
747
+ if (!gSub.success)
748
+ return {
749
+ success: false,
750
+ error: "Right operand of composition must be a function",
751
+ };
752
+ const composedType = functionType(a, c);
753
+ return { success: true, value: composedType };
754
+ }
755
+ visitLambda(node) {
756
+ // Create fresh type variables for parameters
757
+ const paramTypes = node.parameters.map(() => this.coreHM.freshVar());
758
+ this.envs.unshift(new Map());
759
+ // Add parameters to environment
760
+ node.parameters.forEach((param, i) => {
761
+ try {
762
+ param.accept(new PatternVisitor(this.coreHM, this.signatureMap, paramTypes[i], this.envs, this));
763
+ }
764
+ catch (error) {
765
+ return {
766
+ success: false,
767
+ error: error.message,
768
+ };
769
+ }
770
+ });
771
+ // Infer body type
772
+ const inferrer = new InferenceEngine(this.signatureMap, this.coreHM, this.envs);
773
+ const bodyResult = node.body.accept(inferrer);
774
+ if (!bodyResult.success)
775
+ return bodyResult;
776
+ // Construct function type
777
+ const funcType = paramTypes.reduceRight((acc, param) => functionType(param, acc), bodyResult.value);
778
+ return { success: true, value: funcType };
779
+ }
780
+ visitApplication(node) {
781
+ const funcResult = node.functionExpr.accept(this);
782
+ if (funcResult.success === false)
783
+ return funcResult;
784
+ const argResult = node.parameter.accept(this);
785
+ if (argResult.success === false)
786
+ return argResult;
787
+ const resultType = this.coreHM.freshVar();
788
+ const funcType = functionType(argResult.value, resultType);
789
+ const unifyResult = this.coreHM.unify(funcResult.value, funcType);
790
+ if (unifyResult.success === false) {
791
+ return {
792
+ success: false,
793
+ error: `Cannot apply ${showType(argResult.value)} to type ${showType(funcResult.value)}`,
794
+ };
795
+ }
796
+ const substResultType = this.coreHM.applySubst(unifyResult.value, resultType);
797
+ return { success: true, value: substResultType };
798
+ }
799
+ visitYield(node) {
800
+ throw new Error("Method not implemented.");
801
+ }
802
+ visitRaise(node) {
803
+ const bodyResult = node.body.accept(this);
804
+ if (!bodyResult.success)
805
+ return bodyResult;
806
+ const unifyResult = this.coreHM.unify(stringType, bodyResult.value);
807
+ if (!unifyResult.success)
808
+ return {
809
+ success: false,
810
+ error: "Body of Raise expression must be a YuString",
811
+ };
812
+ return { success: true, value: this.coreHM.freshVar() };
813
+ }
814
+ visitIf(node) {
815
+ const condResult = node.condition.accept(this);
816
+ if (!condResult.success)
817
+ return condResult;
818
+ const condSub = this.coreHM.unify(condResult.value, booleanType);
819
+ if (!condSub.success)
820
+ return { success: false, error: "Condition must be a boolean" };
821
+ const thenResult = node.then.accept(this);
822
+ if (!thenResult.success)
823
+ return thenResult;
824
+ const elseResult = node.elseExpr.accept(this);
825
+ if (!elseResult.success)
826
+ return elseResult;
827
+ const unifyResult = this.coreHM.unify(thenResult.value, elseResult.value);
828
+ if (!unifyResult.success)
829
+ return {
830
+ success: false,
831
+ error: `Branch types don't match: ${showType(thenResult.value)} vs ${showType(elseResult.value)}`,
832
+ };
833
+ return thenResult;
834
+ }
835
+ // visitGuardedBody(node: GuardedBody): Result<Type> {
836
+ // return node.body.accept(this);
837
+ // }
838
+ visitReturn(node) {
839
+ return node.body.accept(this);
840
+ }
841
+ visitTypeCast(node) {
842
+ return {
843
+ success: true,
844
+ value: new TypeBuilder(this.coreHM).build(node.body).type,
845
+ };
846
+ }
847
+ visitPrint(node) {
848
+ const exprResult = node.expression.accept(this);
849
+ if (exprResult.success === false)
850
+ return exprResult;
851
+ const t1 = this.coreHM.freshVar(["Show"]);
852
+ const unifyResult = this.coreHM.unify(t1, exprResult.value);
853
+ if (unifyResult.success === false)
854
+ return unifyResult;
855
+ return { success: true, value: stringType };
856
+ }
857
+ visitListComprehension(node) {
858
+ // Generator(s) must unify with 'YuBoolean'
859
+ for (const generator of node.generators) {
860
+ const inferGenResult = generator.accept(this);
861
+ if (inferGenResult.success === false)
862
+ return inferGenResult;
863
+ const unifyGenResult = this.coreHM.unify(inferGenResult.value, booleanType);
864
+ if (unifyGenResult.success === false)
865
+ return unifyGenResult;
866
+ }
867
+ // The projection must unify to 'a'
868
+ const exprResult = node.projection.accept(this);
869
+ if (exprResult.success === false)
870
+ return exprResult;
871
+ const list = listType(exprResult.value);
872
+ return { success: true, value: list };
873
+ }
874
+ visitGenerator(node) {
875
+ // A generator must unify to a 'YuList a'
876
+ const inferResult = node.expression.accept(this);
877
+ if (inferResult.success === false)
878
+ return inferResult;
879
+ const elemType = this.coreHM.freshVar();
880
+ const genericList = listType(elemType);
881
+ const unifyResult = this.coreHM.unify(inferResult.value, genericList);
882
+ if (unifyResult.success === false)
883
+ return unifyResult;
884
+ const subsElemType = this.coreHM.applySubst(unifyResult.value, elemType);
885
+ const schemeElemType = this.coreHM.generalize(this.envs[0], subsElemType);
886
+ const bindingName = node.variable.value;
887
+ if (this.envs[0].has(bindingName))
888
+ return {
889
+ success: false,
890
+ error: `Multiple declarations of '${bindingName}'`,
891
+ };
892
+ this.envs[0].set(bindingName, schemeElemType);
893
+ return { success: true, value: booleanType }; // Shady af but helps unify only with booleanType in visitListComprehension
894
+ }
895
+ visitFor(node) {
896
+ throw new Error("Method not implemented.");
897
+ }
898
+ visitSwitch(node) {
899
+ const firstBranch = node.cases[0];
900
+ // Infer type of case key
901
+ const caseResult = node.value.accept(this);
902
+ if (caseResult.success === false)
903
+ return caseResult;
904
+ // Unify first branch condition with case key
905
+ try {
906
+ firstBranch.condition.accept(new PatternVisitor(this.coreHM, this.signatureMap, caseResult.value, this.envs, this));
907
+ }
908
+ catch (error) {
909
+ return { success: false, error: error.message };
910
+ }
911
+ // Infer first branch result
912
+ const firstBranchType = firstBranch.body.accept(this);
913
+ if (firstBranchType.success === false)
914
+ return firstBranchType;
915
+ // Every branch should return same type as the first branch
916
+ for (const branch of node.cases.slice(1)) {
917
+ // Unify condition with case key
918
+ try {
919
+ branch.condition.accept(new PatternVisitor(this.coreHM, this.signatureMap, caseResult.value, this.envs, this));
920
+ }
921
+ catch (error) {
922
+ return { success: false, error: error.message };
923
+ }
924
+ // Unify branch result with first branch
925
+ const branchType = branch.body.accept(this);
926
+ if (branchType.success === false)
927
+ return branchType;
928
+ const unifyBranchResult = this.coreHM.unify(firstBranchType.value, branchType.value);
929
+ if (unifyBranchResult.success === false)
930
+ return unifyBranchResult;
931
+ }
932
+ return firstBranchType;
933
+ }
934
+ visitRangeExpression(node) {
935
+ const startResult = node.start.accept(this);
936
+ if (!startResult.success)
937
+ return startResult;
938
+ const endResult = node.end.accept(this);
939
+ if (!endResult.success)
940
+ return endResult;
941
+ const rangeElemType = this.coreHM.freshVar(["Ord", "Enum"]);
942
+ // Unify both start and end with this constrained type
943
+ const unifyStart = this.coreHM.unify(startResult.value, rangeElemType);
944
+ if (!unifyStart.success) {
945
+ return {
946
+ success: false,
947
+ error: `Range start must be of an enumerable and orderable type, got ${showType(startResult.value)}`,
948
+ };
949
+ }
950
+ // Apply substitution from start unification to end type before unifying
951
+ const substitutedEnd = this.coreHM.applySubst(unifyStart.value, endResult.value);
952
+ const unifyEnd = this.coreHM.unify(substitutedEnd, this.coreHM.applySubst(unifyStart.value, rangeElemType));
953
+ if (!unifyEnd.success) {
954
+ return {
955
+ success: false,
956
+ error: `Range end must match start type; expected ${showType(startResult.value)}, got ${showType(endResult.value)}`,
957
+ };
958
+ }
959
+ // Combine substitutions
960
+ const combinedSubst = this.coreHM.composeSubst(unifyEnd.value, unifyStart.value);
961
+ const finalElemType = this.coreHM.applySubst(combinedSubst, rangeElemType);
962
+ // Result is a list of the element type
963
+ return {
964
+ success: true,
965
+ value: listType(finalElemType),
966
+ };
967
+ }
968
+ visit(node) {
969
+ return node.accept(this);
970
+ }
971
+ }
972
+ const searchInEnvironments = (envs, key) => {
973
+ let result;
974
+ for (const env of envs) {
975
+ if (env.has(key)) {
976
+ result = env.get(key);
977
+ break;
978
+ }
979
+ }
980
+ if (!result)
981
+ return { success: false, error: `Unbound variable '${key}'` };
982
+ return { success: true, value: result };
983
+ };
984
+ //# sourceMappingURL=inference.js.map