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,157 +1,160 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ASTNode,
|
|
3
|
-
Record,
|
|
4
|
-
TypeAlias,
|
|
5
|
-
TypeSignature,
|
|
6
|
-
Visitor,
|
|
7
|
-
TestGroup,
|
|
8
|
-
Test,
|
|
9
|
-
Assert,
|
|
10
|
-
TypeClass,
|
|
11
|
-
Instance,
|
|
12
|
-
Equation,
|
|
13
|
-
TypePattern,
|
|
14
|
-
SimpleType,
|
|
15
|
-
ListType,
|
|
16
|
-
} from "yukigo-ast";
|
|
17
|
-
import {
|
|
18
|
-
functionType,
|
|
19
|
-
Type,
|
|
20
|
-
TypeConstructor,
|
|
21
|
-
TypeScheme,
|
|
22
|
-
TypeVar,
|
|
23
|
-
} from "./checker.js";
|
|
24
|
-
import { CoreHM } from "./core.js";
|
|
25
|
-
import { TypeBuilder } from "./TypeBuilder.js";
|
|
26
|
-
import { typeMappings } from "../utils/types.js";
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
constructor(
|
|
32
|
-
private errors: string[],
|
|
33
|
-
private typeAliasMap: Map<string, Type>,
|
|
34
|
-
private recordMap: Map<string, Type>,
|
|
35
|
-
private signatureMap: Map<string, TypeScheme>,
|
|
36
|
-
private coreHM: CoreHM
|
|
37
|
-
) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
yukigoTypeName =
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
this.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
1
|
+
import {
|
|
2
|
+
ASTNode,
|
|
3
|
+
Record,
|
|
4
|
+
TypeAlias,
|
|
5
|
+
TypeSignature,
|
|
6
|
+
Visitor,
|
|
7
|
+
TestGroup,
|
|
8
|
+
Test,
|
|
9
|
+
Assert,
|
|
10
|
+
TypeClass,
|
|
11
|
+
Instance,
|
|
12
|
+
Equation,
|
|
13
|
+
TypePattern,
|
|
14
|
+
SimpleType,
|
|
15
|
+
ListType,
|
|
16
|
+
} from "yukigo-ast";
|
|
17
|
+
import {
|
|
18
|
+
functionType,
|
|
19
|
+
Type,
|
|
20
|
+
TypeConstructor,
|
|
21
|
+
TypeScheme,
|
|
22
|
+
TypeVar,
|
|
23
|
+
} from "./checker.js";
|
|
24
|
+
import { CoreHM } from "./core.js";
|
|
25
|
+
import { TypeBuilder } from "./TypeBuilder.js";
|
|
26
|
+
import { typeMappings } from "../utils/types.js";
|
|
27
|
+
|
|
28
|
+
export class DeclarationCollectorVisitor implements Visitor<void> {
|
|
29
|
+
private builder: TypeBuilder;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
private errors: string[],
|
|
33
|
+
private typeAliasMap: Map<string, Type>,
|
|
34
|
+
private recordMap: Map<string, Type>,
|
|
35
|
+
private signatureMap: Map<string, TypeScheme>,
|
|
36
|
+
private coreHM: CoreHM,
|
|
37
|
+
) {
|
|
38
|
+
this.builder = new TypeBuilder(this.coreHM);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
visitTypeAlias(node: TypeAlias) {
|
|
42
|
+
const typeAliasIdentifier = node.identifier.value;
|
|
43
|
+
if (
|
|
44
|
+
this.typeAliasMap.has(typeAliasIdentifier) ||
|
|
45
|
+
this.recordMap.has(typeAliasIdentifier)
|
|
46
|
+
) {
|
|
47
|
+
this.errors.push(`Multiple declaration of '${typeAliasIdentifier}'.`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const { type, constraints } = this.builder.build(node.value);
|
|
52
|
+
this.typeAliasMap.set(typeAliasIdentifier, type);
|
|
53
|
+
}
|
|
54
|
+
visitRecord(node: Record) {
|
|
55
|
+
const recordIdentifier = node.name.value;
|
|
56
|
+
if (
|
|
57
|
+
this.typeAliasMap.has(recordIdentifier) ||
|
|
58
|
+
this.recordMap.has(recordIdentifier)
|
|
59
|
+
) {
|
|
60
|
+
this.errors.push(`Multiple declaration of '${recordIdentifier}'.`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Save the record type itself
|
|
65
|
+
const recordType: TypeConstructor = {
|
|
66
|
+
type: "TypeConstructor",
|
|
67
|
+
name: recordIdentifier,
|
|
68
|
+
args: [],
|
|
69
|
+
};
|
|
70
|
+
this.recordMap.set(recordIdentifier, recordType);
|
|
71
|
+
|
|
72
|
+
// Add constructors to signature map as type schemes
|
|
73
|
+
for (const cons of node.contents) {
|
|
74
|
+
if (this.signatureMap.has(cons.name.value)) {
|
|
75
|
+
this.errors.push(`Constructor '${cons.name}' is already defined`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const paramTypes = cons.fields.map((field) => this.builder.build(field.value));
|
|
79
|
+
const returnType: TypeConstructor = {
|
|
80
|
+
type: "TypeConstructor",
|
|
81
|
+
name: recordIdentifier,
|
|
82
|
+
args: paramTypes.map(() => this.coreHM.freshVar()),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const funcType: Type = paramTypes.reduceRight(
|
|
86
|
+
(acc, param) => functionType(param.type, acc),
|
|
87
|
+
returnType,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// Generalize all free variables in the constructor type
|
|
91
|
+
const scheme = this.coreHM.generalize(new Map(), funcType);
|
|
92
|
+
this.signatureMap.set(cons.name.value, scheme);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
visitTypeClass(node: TypeClass) {
|
|
97
|
+
const className = node.name.value;
|
|
98
|
+
if (!this.coreHM.typeClasses.has(className)) {
|
|
99
|
+
this.coreHM.typeClasses.set(className, []);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Register method signatures
|
|
103
|
+
for (const sig of node.signatures) {
|
|
104
|
+
this.visitTypeSignature(sig);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
visitInstance(node: Instance) {
|
|
109
|
+
const className = node.className.value;
|
|
110
|
+
let yukigoTypeName = "YuUnknown";
|
|
111
|
+
|
|
112
|
+
if (node.type instanceof SimpleType) {
|
|
113
|
+
yukigoTypeName = typeMappings[node.type.value] || node.type.value;
|
|
114
|
+
} else if (node.type instanceof ListType) {
|
|
115
|
+
yukigoTypeName = "YuList";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let instances = this.coreHM.typeClasses.get(className);
|
|
119
|
+
if (!instances) {
|
|
120
|
+
instances = [];
|
|
121
|
+
this.coreHM.typeClasses.set(className, instances);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!instances.includes(yukigoTypeName)) {
|
|
125
|
+
instances.push(yukigoTypeName);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
visitTypeSignature(node: TypeSignature) {
|
|
130
|
+
const functionName = node.identifier.value;
|
|
131
|
+
if (this.signatureMap.has(functionName)) {
|
|
132
|
+
this.errors.push(
|
|
133
|
+
`Function '${functionName}' has multiple type signatures`,
|
|
134
|
+
);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const typeVarMap = new Map<string, TypeVar>();
|
|
138
|
+
const { type, constraints } = this.builder.build(node.body, typeVarMap);
|
|
139
|
+
const quantifiers = Array.from(typeVarMap.values()).map((tv) => tv.id);
|
|
140
|
+
this.signatureMap.set(functionName, {
|
|
141
|
+
type: "TypeScheme",
|
|
142
|
+
quantifiers,
|
|
143
|
+
body: type,
|
|
144
|
+
constraints,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
visitTestGroup(node: TestGroup): void {
|
|
148
|
+
node.group.accept(this);
|
|
149
|
+
}
|
|
150
|
+
visitTest(node: Test): void {
|
|
151
|
+
node.body.accept(this);
|
|
152
|
+
}
|
|
153
|
+
visitAssert(node: Assert): void {
|
|
154
|
+
// assertions don't contain declarations
|
|
155
|
+
}
|
|
156
|
+
visit(node: ASTNode): void {
|
|
157
|
+
node.accept(this);
|
|
158
|
+
}
|
|
159
|
+
fallback(node: ASTNode): void {}
|
|
160
|
+
}
|
|
@@ -1,150 +1,153 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SimpleType,
|
|
3
|
-
TypeVar as ASTTypeVar,
|
|
4
|
-
ListType,
|
|
5
|
-
TupleType,
|
|
6
|
-
ParameterizedType,
|
|
7
|
-
TypeApplication,
|
|
8
|
-
Constraint,
|
|
9
|
-
Type as YukigoType,
|
|
10
|
-
ConstrainedType,
|
|
11
|
-
ASTNode,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
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
|
-
if (primitive ===
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
...this.coreHM.freshVar(),
|
|
62
|
-
name: n.value,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
for (const constraint of n.constraints) {
|
|
66
|
-
this.processConstraint(constraint, typeVarMap, constraintsMap);
|
|
67
|
-
}
|
|
68
|
-
return typeVarMap.get(n.value)!;
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
visitListType: (n: ListType): Type => {
|
|
72
|
-
const elementType = n.values.accept(visitor);
|
|
73
|
-
return {
|
|
74
|
-
type: "TypeConstructor",
|
|
75
|
-
name:
|
|
76
|
-
args: [elementType],
|
|
77
|
-
};
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
visitTupleType: (n: TupleType): Type => {
|
|
81
|
-
const elementTypes = n.values.map((v) => v.accept(visitor));
|
|
82
|
-
return {
|
|
83
|
-
type: "TypeConstructor",
|
|
84
|
-
name:
|
|
85
|
-
args: elementTypes,
|
|
86
|
-
};
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
visitTypeApplication: (n: TypeApplication): Type => {
|
|
90
|
-
return this.coreHM.freshVar();
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
visitParameterizedType: (n: ParameterizedType): Type => {
|
|
94
|
-
// This represents function types with constraints, e.g.:
|
|
95
|
-
// (a, b) => c with Eq a
|
|
96
|
-
const returnType = n.returnType.accept(visitor);
|
|
97
|
-
const paramTypes = n.inputs.map((input) => input.accept(visitor));
|
|
98
|
-
|
|
99
|
-
// Build function type: a -> b -> c
|
|
100
|
-
const funcType = paramTypes.reduceRight(
|
|
101
|
-
(acc, param) => functionType(param, acc),
|
|
102
|
-
returnType
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
// Process constraints
|
|
106
|
-
for (const constraint of n.constraints) {
|
|
107
|
-
this.processConstraint(constraint, typeVarMap, constraintsMap);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return funcType;
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
visitConstrainedType: (n: ConstrainedType): Type => {
|
|
114
|
-
// This is unusual at top level; usually constraints appear on type vars or signatures
|
|
115
|
-
// For now, ignore or throw
|
|
116
|
-
throw new Error("ConstrainedType not expected in this context");
|
|
117
|
-
},
|
|
118
|
-
visit(node: ASTNode): Type {
|
|
119
|
-
return node.accept(visitor);
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
constraintsMap.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
SimpleType,
|
|
3
|
+
TypeVar as ASTTypeVar,
|
|
4
|
+
ListType,
|
|
5
|
+
TupleType,
|
|
6
|
+
ParameterizedType,
|
|
7
|
+
TypeApplication,
|
|
8
|
+
Constraint,
|
|
9
|
+
Type as YukigoType,
|
|
10
|
+
ConstrainedType,
|
|
11
|
+
ASTNode,
|
|
12
|
+
Visitor,
|
|
13
|
+
} from "yukigo-ast";
|
|
14
|
+
import { typeMappings, YUTYPES } from "../utils/types.js";
|
|
15
|
+
import { CoreHM } from "./core.js";
|
|
16
|
+
import { functionType, Type, TypeConstructor, TypeVar, stringType, charType } from "./checker.js";
|
|
17
|
+
import { UnexpectedNode } from "../utils/helpers.js";
|
|
18
|
+
|
|
19
|
+
export class TypeBuilder {
|
|
20
|
+
constructor(private coreHM: CoreHM) {}
|
|
21
|
+
|
|
22
|
+
build(
|
|
23
|
+
node: YukigoType,
|
|
24
|
+
typeVarMap: Map<string, TypeVar> = new Map()
|
|
25
|
+
): { type: Type; constraints: Map<number, string[]> } {
|
|
26
|
+
const constraintsMap = new Map<number, string[]>();
|
|
27
|
+
|
|
28
|
+
// Local visitor to walk Yukigo type AST
|
|
29
|
+
const visitor: Visitor<Type> = {
|
|
30
|
+
visitSimpleType: (n: SimpleType): Type => {
|
|
31
|
+
if (/^[a-z]/.test(n.value)) {
|
|
32
|
+
// Lowercase = type variable
|
|
33
|
+
if (!typeVarMap.has(n.value)) {
|
|
34
|
+
typeVarMap.set(n.value, {
|
|
35
|
+
...this.coreHM.freshVar(),
|
|
36
|
+
name: n.value,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// Collect constraints on this type var
|
|
40
|
+
for (const constraint of n.constraints) {
|
|
41
|
+
this.processConstraint(constraint, typeVarMap, constraintsMap);
|
|
42
|
+
}
|
|
43
|
+
return typeVarMap.get(n.value)!;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Uppercase = type constructor (e.g., Int, Bool, List)
|
|
47
|
+
const primitive = typeMappings[n.value] || n.value;
|
|
48
|
+
if (primitive === YUTYPES.YuString) return stringType;
|
|
49
|
+
if (primitive === YUTYPES.YuChar) return charType;
|
|
50
|
+
return {
|
|
51
|
+
type: "TypeConstructor",
|
|
52
|
+
name: primitive,
|
|
53
|
+
args: [],
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
visitTypeVar: (n: ASTTypeVar): Type => {
|
|
58
|
+
// Same as SimpleType for type variables
|
|
59
|
+
if (!typeVarMap.has(n.value)) {
|
|
60
|
+
typeVarMap.set(n.value, {
|
|
61
|
+
...this.coreHM.freshVar(),
|
|
62
|
+
name: n.value,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
for (const constraint of n.constraints) {
|
|
66
|
+
this.processConstraint(constraint, typeVarMap, constraintsMap);
|
|
67
|
+
}
|
|
68
|
+
return typeVarMap.get(n.value)!;
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
visitListType: (n: ListType): Type => {
|
|
72
|
+
const elementType = n.values.accept(visitor);
|
|
73
|
+
return {
|
|
74
|
+
type: "TypeConstructor",
|
|
75
|
+
name: YUTYPES.List,
|
|
76
|
+
args: [elementType],
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
visitTupleType: (n: TupleType): Type => {
|
|
81
|
+
const elementTypes = n.values.map((v) => v.accept(visitor));
|
|
82
|
+
return {
|
|
83
|
+
type: "TypeConstructor",
|
|
84
|
+
name: YUTYPES.Tuple,
|
|
85
|
+
args: elementTypes,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
visitTypeApplication: (n: TypeApplication): Type => {
|
|
90
|
+
return this.coreHM.freshVar();
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
visitParameterizedType: (n: ParameterizedType): Type => {
|
|
94
|
+
// This represents function types with constraints, e.g.:
|
|
95
|
+
// (a, b) => c with Eq a
|
|
96
|
+
const returnType = n.returnType.accept(visitor);
|
|
97
|
+
const paramTypes = n.inputs.map((input) => input.accept(visitor));
|
|
98
|
+
|
|
99
|
+
// Build function type: a -> b -> c
|
|
100
|
+
const funcType = paramTypes.reduceRight(
|
|
101
|
+
(acc, param) => functionType(param, acc),
|
|
102
|
+
returnType
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Process constraints
|
|
106
|
+
for (const constraint of n.constraints) {
|
|
107
|
+
this.processConstraint(constraint, typeVarMap, constraintsMap);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return funcType;
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
visitConstrainedType: (n: ConstrainedType): Type => {
|
|
114
|
+
// This is unusual at top level; usually constraints appear on type vars or signatures
|
|
115
|
+
// For now, ignore or throw
|
|
116
|
+
throw new Error("ConstrainedType not expected in this context");
|
|
117
|
+
},
|
|
118
|
+
visit(node: ASTNode): Type {
|
|
119
|
+
return node.accept(visitor);
|
|
120
|
+
},
|
|
121
|
+
fallback(node: ASTNode): Type {
|
|
122
|
+
throw new UnexpectedNode(node.constructor.toString(), "TypeBuilder")
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const type = node.accept(visitor);
|
|
127
|
+
return { type, constraints: constraintsMap };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private processConstraint(
|
|
131
|
+
constraint: Constraint,
|
|
132
|
+
typeVarMap: Map<string, { type: "TypeVar"; id: number }>,
|
|
133
|
+
constraintsMap: Map<number, string[]>
|
|
134
|
+
): void {
|
|
135
|
+
// Assume constraint is of form `Eq a`, so first param is a type var
|
|
136
|
+
if (constraint.parameters.length === 0) return;
|
|
137
|
+
|
|
138
|
+
const firstParam = constraint.parameters[0];
|
|
139
|
+
// Only handle if it's a type variable
|
|
140
|
+
if (
|
|
141
|
+
(firstParam instanceof SimpleType || firstParam instanceof ASTTypeVar) &&
|
|
142
|
+
/^[a-z]/.test(firstParam.value)
|
|
143
|
+
) {
|
|
144
|
+
const tv = typeVarMap.get(firstParam.value);
|
|
145
|
+
if (tv) {
|
|
146
|
+
if (!constraintsMap.has(tv.id)) {
|
|
147
|
+
constraintsMap.set(tv.id, []);
|
|
148
|
+
}
|
|
149
|
+
constraintsMap.get(tv.id)!.push(constraint.name);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|