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
package/src/typechecker/core.ts
CHANGED
|
@@ -1,192 +1,195 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return
|
|
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
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
return { success:
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
return
|
|
191
|
-
|
|
192
|
-
|
|
1
|
+
import { YUTYPES } from "../utils/types.js";
|
|
2
|
+
import {
|
|
3
|
+
charType,
|
|
4
|
+
Environment,
|
|
5
|
+
isListType,
|
|
6
|
+
isString,
|
|
7
|
+
Result,
|
|
8
|
+
showType,
|
|
9
|
+
Substitution,
|
|
10
|
+
Type,
|
|
11
|
+
TypeConstructor,
|
|
12
|
+
TypeScheme,
|
|
13
|
+
TypeVar,
|
|
14
|
+
} from "./checker.js";
|
|
15
|
+
|
|
16
|
+
export class CoreHM {
|
|
17
|
+
private nextVarId = 0;
|
|
18
|
+
constructor(
|
|
19
|
+
private aliases: Map<string, Type> = new Map(),
|
|
20
|
+
public typeClasses: Map<string, string[]> = new Map()
|
|
21
|
+
) {}
|
|
22
|
+
public resolve(t: Type): Type {
|
|
23
|
+
if (t.type === "TypeConstructor" && this.aliases.has(t.name))
|
|
24
|
+
return this.resolve(this.aliases.get(t.name)!);
|
|
25
|
+
return t;
|
|
26
|
+
}
|
|
27
|
+
public freshVar(constraints: string[] = []): TypeVar {
|
|
28
|
+
return { type: "TypeVar", id: this.nextVarId++, constraints };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public freeTypeVars(t: Type): Map<number, string[]> {
|
|
32
|
+
const freeVars = new Map<number, string[]>();
|
|
33
|
+
const collect = (type: Type) => {
|
|
34
|
+
if (type.type === "TypeVar") {
|
|
35
|
+
freeVars.set(type.id, type.constraints);
|
|
36
|
+
} else if (type.type === "TypeConstructor") {
|
|
37
|
+
type.args.forEach(collect);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
collect(t);
|
|
41
|
+
return freeVars;
|
|
42
|
+
}
|
|
43
|
+
public generalize(env: Environment, t: Type): TypeScheme {
|
|
44
|
+
const envFreeVars = new Set<number>();
|
|
45
|
+
for (const scheme of env.values()) {
|
|
46
|
+
const schemeFreeVars = this.freeTypeVars(scheme.body);
|
|
47
|
+
for (const id of schemeFreeVars.keys()) {
|
|
48
|
+
if (!scheme.quantifiers.includes(id)) {
|
|
49
|
+
envFreeVars.add(id);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const typeFreeVars = this.freeTypeVars(t);
|
|
55
|
+
|
|
56
|
+
const quantifiers: number[] = [];
|
|
57
|
+
const constraints = new Map<number, string[]>();
|
|
58
|
+
|
|
59
|
+
for (const [id, consts] of typeFreeVars.entries()) {
|
|
60
|
+
if (!envFreeVars.has(id)) {
|
|
61
|
+
quantifiers.push(id);
|
|
62
|
+
if (consts.length > 0) {
|
|
63
|
+
constraints.set(id, consts);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { type: "TypeScheme", quantifiers, body: t, constraints };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public instantiate(scheme: TypeScheme): Type {
|
|
72
|
+
const substitutions = new Map<number, Type>();
|
|
73
|
+
scheme.quantifiers.forEach((id) => {
|
|
74
|
+
const constraints = scheme.constraints.get(id) || [];
|
|
75
|
+
substitutions.set(id, this.freshVar(constraints));
|
|
76
|
+
});
|
|
77
|
+
return this.applySubst(substitutions, scheme.body);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public applySubst(subst: Substitution, t: Type): TypeVar | TypeConstructor {
|
|
81
|
+
if (t.type === "TypeVar") {
|
|
82
|
+
const replacement = subst.get(t.id);
|
|
83
|
+
return replacement ? replacement : t;
|
|
84
|
+
} else if (t.type === "TypeConstructor") {
|
|
85
|
+
return {
|
|
86
|
+
type: "TypeConstructor",
|
|
87
|
+
name: t.name,
|
|
88
|
+
args: t.args.map((arg) => this.applySubst(subst, arg)),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
throw new Error("Unexpected TypeScheme in applySubst");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public composeSubst(s1: Substitution, s2: Substitution): Substitution {
|
|
96
|
+
const result = new Map(s2);
|
|
97
|
+
for (const [id, type] of s1) {
|
|
98
|
+
result.set(id, this.applySubst(s2, type));
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public unify(t1: Type, t2: Type): Result<Substitution> {
|
|
104
|
+
const rt1 = this.resolve(t1);
|
|
105
|
+
const rt2 = this.resolve(t2);
|
|
106
|
+
if (rt1.type === "TypeVar") return this.unifyVar(rt1, rt2);
|
|
107
|
+
if (rt2.type === "TypeVar") return this.unifyVar(rt2, rt1);
|
|
108
|
+
if (rt1.type === "TypeConstructor" && rt2.type === "TypeConstructor") {
|
|
109
|
+
const isStringList = (a: TypeConstructor, b: TypeConstructor) =>
|
|
110
|
+
(isString(a) && isListType(b)) ||
|
|
111
|
+
(isString(b) && isListType(a));
|
|
112
|
+
|
|
113
|
+
if (isStringList(rt1, rt2)) {
|
|
114
|
+
const listType = rt1.name === YUTYPES.List ? rt1 : rt2;
|
|
115
|
+
return this.unify(listType.args[0], charType);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (rt1.name !== rt2.name || rt1.args.length !== rt2.args.length) {
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
error: `Cannot unify ${showType(rt1)} with ${showType(rt2)}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
let sub: Substitution = new Map();
|
|
125
|
+
for (let i = 0; i < rt1.args.length; i++) {
|
|
126
|
+
const arg1 = this.applySubst(sub, rt1.args[i]);
|
|
127
|
+
const arg2 = this.applySubst(sub, rt2.args[i]);
|
|
128
|
+
const argSubRes = this.unify(arg1, arg2);
|
|
129
|
+
if (!argSubRes.success) return argSubRes;
|
|
130
|
+
sub = this.composeSubst(sub, argSubRes.value);
|
|
131
|
+
}
|
|
132
|
+
return { success: true, value: sub };
|
|
133
|
+
}
|
|
134
|
+
return { success: false, error: `Cannot unify non-types` };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public unifyVar(v: TypeVar, t: Type): Result<Substitution> {
|
|
138
|
+
if (t.type === "TypeVar" && t.id === v.id) {
|
|
139
|
+
return { success: true, value: new Map() };
|
|
140
|
+
}
|
|
141
|
+
if (this.occurs(v.id, t)) {
|
|
142
|
+
return { success: false, error: `Occurs check failed` };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (const constraint of v.constraints) {
|
|
146
|
+
this.checkConstraint(constraint, t);
|
|
147
|
+
}
|
|
148
|
+
if (t.type === "TypeVar") {
|
|
149
|
+
for (const constraint of t.constraints) {
|
|
150
|
+
this.checkConstraint(constraint, v);
|
|
151
|
+
}
|
|
152
|
+
// Merge constraints
|
|
153
|
+
const mergedConstraints = [
|
|
154
|
+
...new Set([...v.constraints, ...t.constraints]),
|
|
155
|
+
];
|
|
156
|
+
v.constraints = mergedConstraints;
|
|
157
|
+
t.constraints = mergedConstraints;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { success: true, value: new Map([[v.id, t]]) };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public checkConstraint(constraintName: string, t: Type) {
|
|
164
|
+
const rt = this.resolve(t);
|
|
165
|
+
if (rt.type === "TypeVar") {
|
|
166
|
+
if (!rt.constraints.includes(constraintName)) {
|
|
167
|
+
rt.constraints.push(constraintName);
|
|
168
|
+
}
|
|
169
|
+
} else if (rt.type === "TypeConstructor") {
|
|
170
|
+
let typeName = rt.name;
|
|
171
|
+
if (
|
|
172
|
+
typeName === YUTYPES.List &&
|
|
173
|
+
rt.args.length === 1 &&
|
|
174
|
+
rt.args[0].type === "TypeConstructor" &&
|
|
175
|
+
rt.args[0].name === YUTYPES.YuChar
|
|
176
|
+
) {
|
|
177
|
+
typeName = YUTYPES.YuString;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const instances = this.typeClasses.get(constraintName);
|
|
181
|
+
if (!instances || !instances.includes(typeName)) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Type '${showType(rt)}' is not an instance of '${constraintName}'`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public occurs(varId: number, t: Type): boolean {
|
|
190
|
+
if (t.type === "TypeVar") return t.id === varId;
|
|
191
|
+
if (t.type === "TypeConstructor")
|
|
192
|
+
return t.args.some((a) => this.occurs(varId, a));
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|