yukigo-haskell-parser 0.1.0

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 (63) hide show
  1. package/.mocharc.json +4 -0
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +10 -0
  4. package/dist/index.d.ts +7 -0
  5. package/dist/index.js +54 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/parser/grammar.d.ts +28 -0
  8. package/dist/parser/grammar.js +404 -0
  9. package/dist/parser/grammar.js.map +1 -0
  10. package/dist/parser/layoutPreprocessor.d.ts +1 -0
  11. package/dist/parser/layoutPreprocessor.js +22 -0
  12. package/dist/parser/layoutPreprocessor.js.map +1 -0
  13. package/dist/parser/lexer.d.ts +42 -0
  14. package/dist/parser/lexer.js +75 -0
  15. package/dist/parser/lexer.js.map +1 -0
  16. package/dist/parser/preprocessor.d.ts +28 -0
  17. package/dist/parser/preprocessor.js +453 -0
  18. package/dist/parser/preprocessor.js.map +1 -0
  19. package/dist/prelude.d.ts +1 -0
  20. package/dist/prelude.js +205 -0
  21. package/dist/prelude.js.map +1 -0
  22. package/dist/typechecker/DeclarationCollector.d.ts +15 -0
  23. package/dist/typechecker/DeclarationCollector.js +80 -0
  24. package/dist/typechecker/DeclarationCollector.js.map +1 -0
  25. package/dist/typechecker/TypeBuilder.d.ts +12 -0
  26. package/dist/typechecker/TypeBuilder.js +113 -0
  27. package/dist/typechecker/TypeBuilder.js.map +1 -0
  28. package/dist/typechecker/checker.d.ts +80 -0
  29. package/dist/typechecker/checker.js +269 -0
  30. package/dist/typechecker/checker.js.map +1 -0
  31. package/dist/typechecker/core.d.ts +14 -0
  32. package/dist/typechecker/core.js +142 -0
  33. package/dist/typechecker/core.js.map +1 -0
  34. package/dist/typechecker/inference.d.ts +69 -0
  35. package/dist/typechecker/inference.js +984 -0
  36. package/dist/typechecker/inference.js.map +1 -0
  37. package/dist/utils/helpers.d.ts +2 -0
  38. package/dist/utils/helpers.js +25 -0
  39. package/dist/utils/helpers.js.map +1 -0
  40. package/dist/utils/types.d.ts +6 -0
  41. package/dist/utils/types.js +53 -0
  42. package/dist/utils/types.js.map +1 -0
  43. package/package.json +36 -0
  44. package/src/index.ts +55 -0
  45. package/src/parser/grammar.ne +463 -0
  46. package/src/parser/grammar.ts +512 -0
  47. package/src/parser/layoutPreprocessor.ts +21 -0
  48. package/src/parser/lexer.ts +77 -0
  49. package/src/parser/preprocessor.ne +375 -0
  50. package/src/parser/preprocessor.ts +502 -0
  51. package/src/prelude.ts +206 -0
  52. package/src/typechecker/DeclarationCollector.ts +104 -0
  53. package/src/typechecker/TypeBuilder.ts +148 -0
  54. package/src/typechecker/checker.ts +395 -0
  55. package/src/typechecker/core.ts +162 -0
  56. package/src/typechecker/inference.ts +1327 -0
  57. package/src/utils/helpers.ts +29 -0
  58. package/src/utils/types.ts +56 -0
  59. package/tests/parser.spec.ts +823 -0
  60. package/tests/prelude.spec.ts +22 -0
  61. package/tests/preprocessor.spec.ts +69 -0
  62. package/tests/typechecker.spec.ts +310 -0
  63. package/tsconfig.json +17 -0
package/src/prelude.ts ADDED
@@ -0,0 +1,206 @@
1
+ import { YukigoHaskellParser } from "./index.js"
2
+
3
+ export const preludeCode = `data Ordering = LT | EQ | GT
4
+ data Maybe a = Nothing | Just a
5
+ (,) :: a -> b -> (a, b)
6
+ (,) x y = (x, y)
7
+ ($) :: (a -> b) -> a -> b
8
+ ($) f x = f x
9
+ (.) :: (b -> c) -> (a -> b) -> a -> c
10
+ (.) f g a = (\\x -> f (g x)) a
11
+ (:) :: a -> [a] -> [a]
12
+ (:) x xs = x : xs
13
+ (++) :: [a] -> [a] -> [a]
14
+ (++) [] ys = ys
15
+ (++) (x:xs) ys = x : (xs ++ ys)
16
+ (&&) :: (Eq a) => a -> a -> Boolean
17
+ (||) :: (Eq a) => a -> a -> Boolean
18
+ (&&) x y = x && y
19
+ (||) x y = x || y
20
+ (==) :: (Ord a) => a -> a -> Boolean
21
+ (/=) :: (Ord a) => a -> a -> Boolean
22
+ (<) :: (Ord a) => a -> a -> Boolean
23
+ (>) :: (Ord a) => a -> a -> Boolean
24
+ (<=) :: (Ord a) => a -> a -> Boolean
25
+ (>=) :: (Ord a) => a -> a -> Boolean
26
+ (==) x y = x == y
27
+ (/=) x y = x /= y
28
+ (<) x y = x < y
29
+ (>) x y = x > y
30
+ (<=) x y = x <= y
31
+ (>=) x y = x >= y
32
+ (+) :: Num a => a -> a -> a
33
+ (-) :: Num a => a -> a -> a
34
+ (+) x y = x + y
35
+ (-) x y = x - y
36
+ (**) :: (Floating a) => a -> a -> a
37
+ (**) x y = x ** y
38
+ (^) :: (Num a, Integral b) => a -> b -> a
39
+ (^) x y = x ^ y
40
+ (^^) :: (Fractional a, Integral b) => a -> b -> a
41
+ (^^) x y = x ^^ y
42
+ (*) :: (Num a) => a -> a -> a
43
+ (*) x y = x * y
44
+ (/) :: Fractional a => a -> a -> a
45
+ (/) _ 0 = error "Prelude./: divide by zero"
46
+ (/) x y = x / y
47
+ (!!) :: [a] -> Int -> a
48
+ (!!) xs n | n < 0 = error "Prelude.!!: negative index"
49
+ (!!) [] _ = error "Prelude.!!: index too large"
50
+ (!!) (x:_) 0 = x
51
+ (!!) (_:xs) n = xs !! (n - 1)
52
+ not :: Bool -> Bool
53
+ not True = False
54
+ not False = True
55
+ quot :: Integral a => a -> a -> a
56
+ quot _ 0 = error "divide by zero"
57
+ quot 0 _ = 0
58
+ quot a b = let { absA = abs a; absB = abs b; result = quotPositive absA absB } in if (a < 0) == (b < 0) then result else -result
59
+ rem :: Integral a => a -> a -> a
60
+ rem _ 0 = error "divide by zero"
61
+ rem 0 _ = 0
62
+ rem a b = let { absA = abs a; absB = abs b; remainder = remPositive absA absB } in if a < 0 then -remainder else remainder
63
+ quotPositive :: Integral a => a -> a -> a
64
+ quotPositive n d | n < d = 0 | otherwise = 1 + quotPositive (n - d) d
65
+ remPositive :: Integral a => a -> a -> a
66
+ remPositive n d | n < d = n | otherwise = remPositive (n - d) d
67
+ div :: Integral a => a -> a -> a
68
+ div _ 0 = error "divide by zero"
69
+ div 0 _ = 0
70
+ div a b = let { q = quot a b; r = rem a b } in if r == 0 || (a < 0) == (b < 0) then q else q - 1
71
+ mod :: Integral a => a -> a -> a
72
+ mod _ 0 = error "divide by zero"
73
+ mod 0 _ = 0
74
+ mod a b = a - (div a b) * b
75
+ signum :: (Num a) => a -> a
76
+ signum x | x == 0 = 0 | x > 0 = 1 | otherwise = -1
77
+ abs :: (Num a) => a -> a
78
+ abs n = if n < 0 then -n else n
79
+ sqrt :: (Num a) => a -> a
80
+ sqrt x = x ** 0.5
81
+ max :: a -> a -> a
82
+ max x y = if x <= y then y else x
83
+ min :: a -> a -> a
84
+ min x y = if x <= y then x else y
85
+ even :: (Integral a) => a -> Bool
86
+ odd :: (Integral a) => a -> Bool
87
+ even n = n \`rem\` 2 == 0
88
+ odd n = (not . even) n
89
+ length :: [a] -> Int
90
+ length [] = 0
91
+ length (_:l) = 1 + length l
92
+ genericLength :: (Integral a) => [b] -> a
93
+ genericLength [] = 0
94
+ genericLength (x:xs) = 1 + genericLength xs
95
+ null :: [a] -> Bool
96
+ null [] = True
97
+ null (_:_) = False
98
+ union :: (Eq a) => [a] -> [a] -> [a]
99
+ union xs ys = unionBy (==) xs ys
100
+ deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
101
+ deleteBy eq x [] = []
102
+ deleteBy eq x (y:ys) = if x \`eq\` y then ys else y : deleteBy eq x ys
103
+ deleteFirstsBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
104
+ deleteFirstsBy eq xs ys = foldl (flip (deleteBy eq)) xs ys
105
+ nubBy :: (a -> a -> Bool) -> [a] -> [a]
106
+ nubBy eq [] = []
107
+ nubBy eq (x:xs) = x : nubBy eq (filter (\\y -> not (eq x y)) xs)
108
+ unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
109
+ unionBy eq xs ys = xs ++ deleteFirstsBy eq (nubBy eq ys) xs
110
+ intersect :: (Eq a) => [a] -> [a] -> [a]
111
+ intersect xs ys = intersectBy (==) xs ys
112
+ intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
113
+ intersectBy eq xs ys = [x | x <- xs, any (eq x) ys]
114
+ elem :: (Eq a) => a -> [a] -> Bool
115
+ notElem :: (Eq a) => a -> [a] -> Bool
116
+ elem x xs = any (\\y -> y == x) xs
117
+ notElem x xs = all (\\y -> y /= x) xs
118
+ maximum :: (Ord a) => [a] -> a
119
+ minimum :: (Ord a) => [a] -> a
120
+ maximum [] = error "Prelude.maximum: empty list"
121
+ maximum xs = foldl1 max xs
122
+ minimum [] = error "Prelude.minimum: empty list"
123
+ minimum xs = foldl1 min xs
124
+ sum :: (Num a) => [a] -> a
125
+ product :: (Num a) => [a] -> a
126
+ sum xs = foldl (+) 0 xs
127
+ product xs = foldl (*) 1 xs
128
+ concat :: [[a]] -> [a]
129
+ concat xss = foldr (++) [] xss
130
+ concatMap :: (a -> [b]) -> [a] -> [b]
131
+ concatMap f xs = (concat . map) f xs
132
+ take :: Int -> [a] -> [a]
133
+ take n _ | n <= 0 = []
134
+ take _ [] = []
135
+ take n (x:xs) = x : take (n - 1) xs
136
+ drop :: Int -> [a] -> [a]
137
+ drop n xs | n <= 0 = xs
138
+ drop _ [] = []
139
+ drop n (_:xs) = drop (n - 1) xs
140
+ head :: [a] -> a
141
+ head (x:_) = x
142
+ head [] = error "Prelude.head: empty list"
143
+ tail :: [a] -> [a]
144
+ tail (_:xs) = xs
145
+ tail [] = error "Prelude.tail: empty list"
146
+ last :: [a] -> a
147
+ last [x] = x
148
+ last (_:xs) = last xs
149
+ last [] = error "Prelude.last: empty list"
150
+ init :: [a] -> [a]
151
+ init [x] = []
152
+ init (x:xs) = x : init xs
153
+ init [] = error "Prelude.init: empty list"
154
+ zip :: [a] -> [b] -> [(a, b)]
155
+ zip xs ys = zipWith (,) xs ys
156
+ zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
157
+ zipWith z (a:as) (b:bs) = z a b : zipWith z as bs
158
+ zipWith _ _ _ = []
159
+ reverse :: [a] -> [a]
160
+ reverse xs = foldl (flip (:)) [] xs
161
+ filter :: (a -> Bool) -> [a] -> [a]
162
+ filter p [] = []
163
+ filter p (x:xs) | p x = x : filter p xs | otherwise = filter p xs
164
+ map :: (a -> b) -> [a] -> [b]
165
+ map f [] = []
166
+ map f (x:xs) = f x : map f xs
167
+ any :: (a -> Bool) -> [a] -> Bool
168
+ all :: (a -> Bool) -> [a] -> Bool
169
+ any f (x:xs) = f x || any f xs
170
+ all f (x:xs) = f x && all f xs
171
+ foldl :: (a -> b -> a) -> a -> [b] -> a
172
+ foldl f z [] = z
173
+ foldl f z (x:xs) = foldl f (f z x) xs
174
+ foldl1 :: (a -> a -> a) -> [a] -> a
175
+ foldl1 f (x:xs) = foldl f x xs
176
+ foldl1 _ [] = error "Prelude.foldl1: empty list"
177
+ foldr :: (a -> b -> b) -> b -> [a] -> b
178
+ foldr f z [] = z
179
+ foldr f z (x:xs) = f x (foldr f z xs)
180
+ foldr1 :: (a -> a -> a) -> [a] -> a
181
+ foldr1 f [x] = x
182
+ foldr1 f (x:xs) = f x (foldr1 f xs)
183
+ foldr1 _ [] = error "Prelude.foldr1: empty list"
184
+ find :: Ord a => (a -> Bool) -> [a] -> Maybe a
185
+ find _ [] = Nothing
186
+ find f (x:xs) | f x = Just x | otherwise = find f xs
187
+ sort :: (Ord a) => [a] -> [a]
188
+ sort xs = sortBy compare xs
189
+ sortBy :: (a -> a -> Ordering) -> [a] -> [a]
190
+ sortBy cmp xs = foldr (insertBy cmp) [] xs
191
+ compare :: a -> a -> Ordering
192
+ compare x y | x == y = EQ | x <= y = LT | otherwise = GT
193
+ insertBy :: (a -> a -> Ordering) -> a -> [a] -> [a]
194
+ insertBy cmp x [] = [x]
195
+ insertBy cmp x ys@(y:ys') = case cmp x y of { GT -> y : insertBy cmp x ys'; _ -> x : ys }
196
+ flip :: (a -> b -> c) -> b -> a -> c
197
+ flip f x y = f y x
198
+ repeat :: a -> [a]
199
+ repeat x = x : (repeat x)
200
+ iterate :: (a -> a) -> a -> [a]
201
+ iterate f x = x : iterate f (f x)
202
+ replicate :: Int -> a -> [a]
203
+ replicate n x = take n (repeat x)
204
+ cycle :: [a] -> [a]
205
+ cycle [] = error "Prelude.cycle: empty list"
206
+ cycle xs = xs ++ (cycle xs)`
@@ -0,0 +1,104 @@
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
+ }
@@ -0,0 +1,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 } 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
+ }