-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
122 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { expect, test, beforeEach } from "vitest"; | ||
import { typePPrint } from "./pretty-printer"; | ||
import { ConcreteType, TVar, Type, unify } from "./unify"; | ||
|
||
beforeEach(() => { | ||
TVar.resetId(); | ||
}); | ||
|
||
test("0-arity types", () => { | ||
expect(typePPrint(Int)).toBe("Int"); | ||
}); | ||
|
||
test("n-arity types", () => { | ||
expect(typePPrint(List(Int))).toBe("List<Int>"); | ||
expect(typePPrint(Tuple(Int, Bool))).toBe("Tuple<Int, Bool>"); | ||
}); | ||
|
||
test("nested types", () => { | ||
expect(typePPrint(List(Maybe(Int)))).toBe("List<Maybe<Int>>"); | ||
}); | ||
|
||
test("type var", () => { | ||
const $a = TVar.fresh(); | ||
expect(typePPrint($a.asType())).toBe("t0"); | ||
}); | ||
|
||
test("type vars", () => { | ||
const $a = TVar.fresh(), | ||
$b = TVar.fresh(); | ||
|
||
expect(typePPrint(Tuple($a.asType(), $b.asType()))).toBe("Tuple<t0, t1>"); | ||
}); | ||
|
||
test("bound types", () => { | ||
const $a = TVar.fresh(); | ||
unify($a.asType(), Int); | ||
|
||
expect(typePPrint(List($a.asType()))).toBe("List<Int>"); | ||
}); | ||
|
||
test("fn type with no args", () => { | ||
expect(typePPrint(Fn([], Bool))).toBe("Fn() -> Bool"); | ||
}); | ||
|
||
test("fn type with one args", () => { | ||
expect(typePPrint(Fn([Int], Bool))).toBe("Fn(Int) -> Bool"); | ||
}); | ||
|
||
test("fn type with two args ", () => { | ||
expect(typePPrint(Fn([Int, Bool], Bool))).toBe("Fn(Int, Bool) -> Bool"); | ||
}); | ||
|
||
test("higher order function", () => { | ||
const f = Fn([Fn([Int], Bool)], Bool); | ||
expect(typePPrint(f)).toBe("Fn(Fn(Int) -> Bool) -> Bool"); | ||
}); | ||
|
||
test("tv as arg", () => { | ||
const $a = TVar.fresh(); | ||
expect(typePPrint(Fn([$a.asType()], $a.asType()))).toBe("Fn(t0) -> t0"); | ||
}); | ||
|
||
test("n-arity type nested in a fn", () => { | ||
const f = Fn([List(Int)], Maybe(Bool)); | ||
expect(typePPrint(f)).toBe("Fn(List<Int>) -> Maybe<Bool>"); | ||
}); | ||
|
||
function named(name: string, ...args: Type[]): ConcreteType { | ||
return { type: "named", name, args }; | ||
} | ||
|
||
const Int = named("Int"); | ||
const Bool = named("Bool"); | ||
function List(p: Type) { | ||
return named("List", p); | ||
} | ||
|
||
function Maybe(p: Type) { | ||
return named("Maybe", p); | ||
} | ||
|
||
function Tuple(...ps: Type[]): ConcreteType { | ||
return named("Tuple", ...ps); | ||
} | ||
|
||
function Fn(args: Type[], ret: Type): ConcreteType { | ||
return { type: "fn", args, return: ret }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { TVar, Type, generalize } from "./unify"; | ||
|
||
function pprintHelper(t: Type): string { | ||
switch (t.type) { | ||
case "var": { | ||
const resolved = t.var.resolve(); | ||
switch (resolved.type) { | ||
case "quantified": | ||
return `t${resolved.id}`; | ||
case "bound": | ||
return pprintHelper(resolved.value); | ||
case "unbound": | ||
throw new Error("[unreachable]"); | ||
} | ||
} | ||
|
||
case "fn": { | ||
const args = t.args.map(pprintHelper).join(", "); | ||
return `Fn(${args}) -> ${pprintHelper(t.return)}`; | ||
} | ||
|
||
case "named": { | ||
if (t.args.length === 0) { | ||
return t.name; | ||
} | ||
const args = t.args.map(pprintHelper).join(", "); | ||
return `${t.name}<${args}>`; | ||
} | ||
} | ||
} | ||
|
||
export function typePPrint(t: Type): string { | ||
return pprintHelper(generalize(t)); | ||
} |