Skip to content

Commit

Permalink
added type pretty printer
Browse files Browse the repository at this point in the history
  • Loading branch information
ascandone committed Jan 17, 2024
1 parent c69b657 commit f8dd7fb
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
88 changes: 88 additions & 0 deletions src/typecheck/pretty-printer.test.ts
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 };
}
34 changes: 34 additions & 0 deletions src/typecheck/pretty-printer.ts
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));
}

0 comments on commit f8dd7fb

Please sign in to comment.