-
Notifications
You must be signed in to change notification settings - Fork 1
/
unparse.ts
107 lines (98 loc) · 2.74 KB
/
unparse.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { TypeValue, TypeSubstitution } from "./types";
import { getResolvedTypeVariables } from "./ftv";
const priorityMap: {
[P in TypeValue["kind"]]: number;
} = {
Bool: 0,
Int: 0,
Float: 0,
TypeParameter: 0,
List: 10,
Function: 20,
};
function idToAlpha(id: number) {
let ret = [String.fromCharCode(97 + (id % 26))];
while ((id = ~~(id / 26))) {
const d = id % 26;
ret.unshift(String.fromCodePoint(96 + d));
}
return "'" + ret.join("");
}
type Context = {
idMap(id: number): number;
baseId: number;
};
function group(parent: TypeValue, child: TypeValue, ctx: Context) {
const fragment = printType(child, ctx);
if (priorityMap[child.kind] <= priorityMap[parent.kind]) {
return fragment;
} else {
return `(${fragment})`;
}
}
function weakGroup(parent: TypeValue, child: TypeValue, ctx: Context) {
const fragment = printType(child, ctx);
if (priorityMap[child.kind] < priorityMap[parent.kind]) {
return fragment;
} else {
return `(${fragment})`;
}
}
function printType(type: TypeValue, ctx: Context): string {
switch (type.kind) {
case "Int":
return "int";
case "Float":
return "float";
case "Bool":
return "bool";
case "TypeParameter":
return idToAlpha(ctx.idMap(type.id));
case "List":
return `${group(type, type.elementType, ctx)} list`;
case "Function":
return `${weakGroup(type, type.paramType, ctx)} -> ${group(type, type.returnType, ctx)}`;
}
}
function minId(type: TypeValue, current: number = Number.MAX_SAFE_INTEGER): number {
switch (type.kind) {
case "Int":
case "Float":
case "Bool":
return current;
case "TypeParameter":
return type.id < current ? type.id : current;
case "List":
return minId(type.elementType, current);
case "Function":
return Math.min(minId(type.paramType, current), minId(type.returnType, current));
}
}
export type TypePrinterOptions = {
readonly remapWithSubstitutions?: readonly TypeSubstitution[];
};
export function createTypePrinter(opts: TypePrinterOptions = {}) {
const solvedVariables = opts.remapWithSubstitutions && getResolvedTypeVariables(opts.remapWithSubstitutions);
const solvedIds =
solvedVariables &&
solvedVariables.reduce(
(acc, v) => {
acc[v.id] = true;
return acc;
},
[] as (boolean | undefined)[],
);
const ctx: Context = {
idMap(id) {
if (!solvedIds) return id - this.baseId;
solvedIds[id] = undefined;
const shift = solvedIds.slice(this.baseId, id).reduce((n, solved) => (solved ? n + 1 : n), 0);
return id - shift - this.baseId;
},
baseId: 0,
};
return (type: TypeValue) => {
const baseId = minId(type);
return printType(type, { ...ctx, baseId });
};
}