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.
- package/.mocharc.json +4 -0
- package/CHANGELOG.md +19 -0
- package/README.md +10 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/grammar.d.ts +28 -0
- package/dist/parser/grammar.js +404 -0
- package/dist/parser/grammar.js.map +1 -0
- package/dist/parser/layoutPreprocessor.d.ts +1 -0
- package/dist/parser/layoutPreprocessor.js +22 -0
- package/dist/parser/layoutPreprocessor.js.map +1 -0
- package/dist/parser/lexer.d.ts +42 -0
- package/dist/parser/lexer.js +75 -0
- package/dist/parser/lexer.js.map +1 -0
- package/dist/parser/preprocessor.d.ts +28 -0
- package/dist/parser/preprocessor.js +453 -0
- package/dist/parser/preprocessor.js.map +1 -0
- package/dist/prelude.d.ts +1 -0
- package/dist/prelude.js +205 -0
- package/dist/prelude.js.map +1 -0
- package/dist/typechecker/DeclarationCollector.d.ts +15 -0
- package/dist/typechecker/DeclarationCollector.js +80 -0
- package/dist/typechecker/DeclarationCollector.js.map +1 -0
- package/dist/typechecker/TypeBuilder.d.ts +12 -0
- package/dist/typechecker/TypeBuilder.js +113 -0
- package/dist/typechecker/TypeBuilder.js.map +1 -0
- package/dist/typechecker/checker.d.ts +80 -0
- package/dist/typechecker/checker.js +269 -0
- package/dist/typechecker/checker.js.map +1 -0
- package/dist/typechecker/core.d.ts +14 -0
- package/dist/typechecker/core.js +142 -0
- package/dist/typechecker/core.js.map +1 -0
- package/dist/typechecker/inference.d.ts +69 -0
- package/dist/typechecker/inference.js +984 -0
- package/dist/typechecker/inference.js.map +1 -0
- package/dist/utils/helpers.d.ts +2 -0
- package/dist/utils/helpers.js +25 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/types.d.ts +6 -0
- package/dist/utils/types.js +53 -0
- package/dist/utils/types.js.map +1 -0
- package/package.json +36 -0
- package/src/index.ts +55 -0
- package/src/parser/grammar.ne +463 -0
- package/src/parser/grammar.ts +512 -0
- package/src/parser/layoutPreprocessor.ts +21 -0
- package/src/parser/lexer.ts +77 -0
- package/src/parser/preprocessor.ne +375 -0
- package/src/parser/preprocessor.ts +502 -0
- package/src/prelude.ts +206 -0
- package/src/typechecker/DeclarationCollector.ts +104 -0
- package/src/typechecker/TypeBuilder.ts +148 -0
- package/src/typechecker/checker.ts +395 -0
- package/src/typechecker/core.ts +162 -0
- package/src/typechecker/inference.ts +1327 -0
- package/src/utils/helpers.ts +29 -0
- package/src/utils/types.ts +56 -0
- package/tests/parser.spec.ts +823 -0
- package/tests/prelude.spec.ts +22 -0
- package/tests/preprocessor.spec.ts +69 -0
- package/tests/typechecker.spec.ts +310 -0
- 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
|
+
}
|