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