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