yukigo-haskell-parser 0.1.0 → 0.1.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 (65) hide show
  1. package/.mocharc.json +3 -3
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +10 -10
  4. package/dist/index.d.ts +13 -2
  5. package/dist/index.js +112 -32
  6. package/dist/index.js.map +1 -1
  7. package/dist/parser/grammar.js +220 -231
  8. package/dist/parser/grammar.js.map +1 -1
  9. package/dist/parser/lexer.d.ts +70 -15
  10. package/dist/parser/lexer.js +259 -50
  11. package/dist/parser/lexer.js.map +1 -1
  12. package/dist/prelude.d.ts +1 -1
  13. package/dist/prelude.js +280 -204
  14. package/dist/prelude.js.map +1 -1
  15. package/dist/typechecker/DeclarationCollector.d.ts +6 -1
  16. package/dist/typechecker/DeclarationCollector.js +39 -0
  17. package/dist/typechecker/DeclarationCollector.js.map +1 -1
  18. package/dist/typechecker/TypeBuilder.d.ts +1 -1
  19. package/dist/typechecker/TypeBuilder.js +6 -2
  20. package/dist/typechecker/TypeBuilder.js.map +1 -1
  21. package/dist/typechecker/checker.d.ts +15 -4
  22. package/dist/typechecker/checker.js +108 -25
  23. package/dist/typechecker/checker.js.map +1 -1
  24. package/dist/typechecker/core.d.ts +4 -0
  25. package/dist/typechecker/core.js +45 -19
  26. package/dist/typechecker/core.js.map +1 -1
  27. package/dist/typechecker/inference.d.ts +5 -1
  28. package/dist/typechecker/inference.js +95 -21
  29. package/dist/typechecker/inference.js.map +1 -1
  30. package/dist/utils/helpers.d.ts +1 -1
  31. package/dist/utils/helpers.js +1 -1
  32. package/dist/utils/helpers.js.map +1 -1
  33. package/dist/utils/types.d.ts +1 -1
  34. package/dist/utils/types.js +7 -0
  35. package/dist/utils/types.js.map +1 -1
  36. package/package.json +3 -3
  37. package/src/index.ts +192 -55
  38. package/src/parser/grammar.ne +519 -463
  39. package/src/parser/grammar.ts +230 -239
  40. package/src/parser/lexer.ts +353 -77
  41. package/src/prelude.ts +282 -206
  42. package/src/typechecker/DeclarationCollector.ts +157 -104
  43. package/src/typechecker/TypeBuilder.ts +150 -148
  44. package/src/typechecker/checker.ts +501 -395
  45. package/src/typechecker/core.ts +192 -162
  46. package/src/typechecker/inference.ts +1421 -1327
  47. package/src/utils/helpers.ts +29 -29
  48. package/src/utils/types.ts +63 -56
  49. package/tests/hspec.spec.ts +92 -0
  50. package/tests/lexer.spec.ts +175 -0
  51. package/tests/parser.spec.ts +829 -823
  52. package/tests/prelude.spec.ts +17 -22
  53. package/tests/typechecker.spec.ts +327 -310
  54. package/tsconfig.json +26 -17
  55. package/tsconfig.tsbuildinfo +1 -0
  56. package/dist/parser/layoutPreprocessor.d.ts +0 -1
  57. package/dist/parser/layoutPreprocessor.js +0 -22
  58. package/dist/parser/layoutPreprocessor.js.map +0 -1
  59. package/dist/parser/preprocessor.d.ts +0 -28
  60. package/dist/parser/preprocessor.js +0 -453
  61. package/dist/parser/preprocessor.js.map +0 -1
  62. package/src/parser/layoutPreprocessor.ts +0 -21
  63. package/src/parser/preprocessor.ne +0 -375
  64. package/src/parser/preprocessor.ts +0 -502
  65. 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
+ }