yukigo-haskell-parser 0.1.0 → 0.1.1

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 (64) hide show
  1. package/.mocharc.json +3 -3
  2. package/README.md +10 -10
  3. package/dist/index.d.ts +13 -2
  4. package/dist/index.js +112 -32
  5. package/dist/index.js.map +1 -1
  6. package/dist/parser/grammar.js +220 -231
  7. package/dist/parser/grammar.js.map +1 -1
  8. package/dist/parser/lexer.d.ts +70 -15
  9. package/dist/parser/lexer.js +259 -50
  10. package/dist/parser/lexer.js.map +1 -1
  11. package/dist/prelude.d.ts +1 -1
  12. package/dist/prelude.js +280 -204
  13. package/dist/prelude.js.map +1 -1
  14. package/dist/typechecker/DeclarationCollector.d.ts +6 -1
  15. package/dist/typechecker/DeclarationCollector.js +39 -0
  16. package/dist/typechecker/DeclarationCollector.js.map +1 -1
  17. package/dist/typechecker/TypeBuilder.d.ts +1 -1
  18. package/dist/typechecker/TypeBuilder.js +6 -2
  19. package/dist/typechecker/TypeBuilder.js.map +1 -1
  20. package/dist/typechecker/checker.d.ts +15 -4
  21. package/dist/typechecker/checker.js +108 -25
  22. package/dist/typechecker/checker.js.map +1 -1
  23. package/dist/typechecker/core.d.ts +4 -0
  24. package/dist/typechecker/core.js +45 -19
  25. package/dist/typechecker/core.js.map +1 -1
  26. package/dist/typechecker/inference.d.ts +5 -1
  27. package/dist/typechecker/inference.js +95 -21
  28. package/dist/typechecker/inference.js.map +1 -1
  29. package/dist/utils/helpers.d.ts +1 -1
  30. package/dist/utils/helpers.js +1 -1
  31. package/dist/utils/helpers.js.map +1 -1
  32. package/dist/utils/types.d.ts +1 -1
  33. package/dist/utils/types.js +7 -0
  34. package/dist/utils/types.js.map +1 -1
  35. package/package.json +3 -3
  36. package/src/index.ts +192 -55
  37. package/src/parser/grammar.ne +519 -463
  38. package/src/parser/grammar.ts +230 -239
  39. package/src/parser/lexer.ts +353 -77
  40. package/src/prelude.ts +282 -206
  41. package/src/typechecker/DeclarationCollector.ts +157 -104
  42. package/src/typechecker/TypeBuilder.ts +150 -148
  43. package/src/typechecker/checker.ts +501 -395
  44. package/src/typechecker/core.ts +192 -162
  45. package/src/typechecker/inference.ts +1421 -1327
  46. package/src/utils/helpers.ts +29 -29
  47. package/src/utils/types.ts +63 -56
  48. package/tests/hspec.spec.ts +92 -0
  49. package/tests/lexer.spec.ts +175 -0
  50. package/tests/parser.spec.ts +829 -823
  51. package/tests/prelude.spec.ts +17 -22
  52. package/tests/typechecker.spec.ts +327 -310
  53. package/tsconfig.json +26 -17
  54. package/tsconfig.tsbuildinfo +1 -0
  55. package/dist/parser/layoutPreprocessor.d.ts +0 -1
  56. package/dist/parser/layoutPreprocessor.js +0 -22
  57. package/dist/parser/layoutPreprocessor.js.map +0 -1
  58. package/dist/parser/preprocessor.d.ts +0 -28
  59. package/dist/parser/preprocessor.js +0 -453
  60. package/dist/parser/preprocessor.js.map +0 -1
  61. package/src/parser/layoutPreprocessor.ts +0 -21
  62. package/src/parser/preprocessor.ne +0 -375
  63. package/src/parser/preprocessor.ts +0 -502
  64. package/tests/preprocessor.spec.ts +0 -69
@@ -1,162 +1,192 @@
1
- import { typeClasses } from "../utils/types.js";
2
- import {
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
-
16
- public freshVar(constraints: string[] = []): TypeVar {
17
- return { type: "TypeVar", id: this.nextVarId++, constraints };
18
- }
19
-
20
- public freeTypeVars(t: Type): Map<number, string[]> {
21
- const freeVars = new Map<number, string[]>();
22
- const collect = (type: Type) => {
23
- if (type.type === "TypeVar") {
24
- freeVars.set(type.id, type.constraints);
25
- } else if (type.type === "TypeConstructor") {
26
- type.args.forEach(collect);
27
- }
28
- };
29
- collect(t);
30
- return freeVars;
31
- }
32
- public generalize(env: Environment, t: Type): TypeScheme {
33
- const envFreeVars = new Set<number>();
34
- for (const scheme of env.values()) {
35
- const schemeFreeVars = this.freeTypeVars(scheme.body);
36
- for (const id of schemeFreeVars.keys()) {
37
- if (!scheme.quantifiers.includes(id)) {
38
- envFreeVars.add(id);
39
- }
40
- }
41
- }
42
-
43
- const typeFreeVars = this.freeTypeVars(t);
44
-
45
- const quantifiers: number[] = [];
46
- const constraints = new Map<number, string[]>();
47
-
48
- for (const [id, consts] of typeFreeVars.entries()) {
49
- if (!envFreeVars.has(id)) {
50
- quantifiers.push(id);
51
- if (consts.length > 0) {
52
- constraints.set(id, consts);
53
- }
54
- }
55
- }
56
-
57
- return { type: "TypeScheme", quantifiers, body: t, constraints };
58
- }
59
-
60
- public instantiate(scheme: TypeScheme): Type {
61
- const substitutions = new Map<number, Type>();
62
- scheme.quantifiers.forEach((id) => {
63
- const constraints = scheme.constraints.get(id) || [];
64
- substitutions.set(id, this.freshVar(constraints));
65
- });
66
- return this.applySubst(substitutions, scheme.body);
67
- }
68
-
69
- public applySubst(subst: Substitution, t: Type): TypeVar | TypeConstructor {
70
- if (t.type === "TypeVar") {
71
- const replacement = subst.get(t.id);
72
- return replacement ? replacement : t;
73
- } else if (t.type === "TypeConstructor") {
74
- return {
75
- type: "TypeConstructor",
76
- name: t.name,
77
- args: t.args.map((arg) => this.applySubst(subst, arg)),
78
- };
79
- }
80
-
81
- throw new Error("Unexpected TypeScheme in applySubst");
82
- }
83
-
84
- public composeSubst(s1: Substitution, s2: Substitution): Substitution {
85
- const result = new Map(s2);
86
- for (const [id, type] of s1) {
87
- result.set(id, this.applySubst(s2, type));
88
- }
89
- return result;
90
- }
91
-
92
- public unify(t1: Type, t2: Type): Result<Substitution> {
93
- if (t1.type === "TypeVar") return this.unifyVar(t1, t2);
94
- if (t2.type === "TypeVar") return this.unifyVar(t2, t1);
95
- if (t1.type === "TypeConstructor" && t2.type === "TypeConstructor") {
96
- if (t1.name !== t2.name || t1.args.length !== t2.args.length) {
97
- return {
98
- success: false,
99
- error: `Cannot unify ${showType(t1)} with ${showType(t2)}`,
100
- };
101
- }
102
- let sub: Substitution = new Map();
103
- for (let i = 0; i < t1.args.length; i++) {
104
- const arg1 = this.applySubst(sub, t1.args[i]);
105
- const arg2 = this.applySubst(sub, t2.args[i]);
106
- const argSubRes = this.unify(arg1, arg2);
107
- if (!argSubRes.success) return argSubRes;
108
- sub = this.composeSubst(sub, argSubRes.value);
109
- }
110
- return { success: true, value: sub };
111
- }
112
- return { success: false, error: `Cannot unify non-types` };
113
- }
114
-
115
- public unifyVar(v: TypeVar, t: Type): Result<Substitution> {
116
- if (t.type === "TypeVar" && t.id === v.id) {
117
- return { success: true, value: new Map() };
118
- }
119
- if (this.occurs(v.id, t)) {
120
- return { success: false, error: `Occurs check failed` };
121
- }
122
-
123
- for (const constraint of v.constraints) {
124
- this.checkConstraint(constraint, t);
125
- }
126
- if (t.type === "TypeVar") {
127
- for (const constraint of t.constraints) {
128
- this.checkConstraint(constraint, v);
129
- }
130
- // Merge constraints
131
- const mergedConstraints = [
132
- ...new Set([...v.constraints, ...t.constraints]),
133
- ];
134
- v.constraints = mergedConstraints;
135
- t.constraints = mergedConstraints;
136
- }
137
-
138
- return { success: true, value: new Map([[v.id, t]]) };
139
- }
140
-
141
- public checkConstraint(constraintName: string, t: Type) {
142
- if (t.type === "TypeVar") {
143
- if (!t.constraints.includes(constraintName)) {
144
- t.constraints.push(constraintName);
145
- }
146
- } else if (t.type === "TypeConstructor") {
147
- const instances = typeClasses.get(constraintName);
148
- if (!instances || !instances.includes(t.name)) {
149
- throw new Error(
150
- `Type '${showType(t)}' is not an instance of '${constraintName}'`
151
- );
152
- }
153
- }
154
- }
155
-
156
- public occurs(varId: number, t: Type): boolean {
157
- if (t.type === "TypeVar") return t.id === varId;
158
- if (t.type === "TypeConstructor")
159
- return t.args.some((a) => this.occurs(varId, a));
160
- return false;
161
- }
162
- }
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
+ }