Compare commits

..

No commits in common. "60efd931f54a46948b4d048555588806ec1687a7" and "26a83080aad382f144c5eddef49bd8e12a6b5736" have entirely different histories.

12 changed files with 155 additions and 586 deletions

View File

@ -62,9 +62,7 @@ export type ItemKind =
| { tag: "type_alias" } & TypeAliasItem; | { tag: "type_alias" } & TypeAliasItem;
export type ModBlockItem = { block: Block }; export type ModBlockItem = { block: Block };
export type ModFileItem = { filePath: string; file?: CtxFile };
export type ModFileItem = { filePath: string; file?: CtxFile; ast?: File };
export type EnumItem = { variants: Variant[] }; export type EnumItem = { variants: Variant[] };
export type StructItem = { data: VariantData }; export type StructItem = { data: VariantData };

View File

@ -1,33 +0,0 @@
import { Ctx } from "../ctx.ts";
import { exhausted, todo } from "../util.ts";
import { Block, Item } from "./ast.ts";
export function itemToString(ctx: Ctx, item: Item): string {
const ident = ctx.identText(item.ident.id);
const k = item.kind;
switch (k.tag) {
case "error":
return `<error>`;
case "mod_block": {
const block = blockToString(ctx, k.block);
return `mod ${item} ${block}`;
}
case "mod_file":
return todo();
case "enum":
return todo();
case "struct":
return todo();
case "fn":
return todo();
case "use":
return todo();
case "type_alias":
return todo();
}
return exhausted(k);
}
export function blockToString(ctx: Ctx, block: Block): string {
return todo();
}

View File

@ -1,185 +0,0 @@
import * as ast from "../ast/mod.ts";
import { Ctx, File } from "../ctx.ts";
import { Span } from "../diagnostics.ts";
import { Resols } from "../resolve/resolver.ts";
import { tyToString } from "../ty/to_string.ts";
import { Ty } from "../ty/ty.ts";
import { exhausted, Res, todo } from "../util.ts";
export class Checker {
private itemTys = new Map<number, Ty>();
private exprTys = new Map<number, Ty>();
private tyTys = new Map<number, Ty>();
private currentFile: File;
public constructor(
private ctx: Ctx,
private entryFileAst: ast.File,
private resols: Resols,
) {
this.currentFile = ctx.entryFile();
}
private checkBlock(block: ast.Block, expected: Ty): Ty {
this.checkStmts(block.stmts);
return block.expr &&
this.checkExpr(block.expr, expected) ||
Ty({ tag: "null" });
}
private checkStmts(stmts: ast.Stmt[]) {
}
public fnItemTy(item: ast.Item, kind: ast.FnItem): Ty {
return this.itemTys.get(item.id) || this.checkFnItem(item, kind);
}
private checkFnItem(item: ast.Item, kind: ast.FnItem): Ty {
const params = kind.params.map((param) => this.tyTy(param.ty));
const returnTy = kind.returnTy && this.tyTy(kind.returnTy) ||
Ty({ tag: "null" });
return Ty({ tag: "fn", item, kind, params, returnTy });
}
public exprTy(expr: ast.Expr): Ty {
return this.exprTys.get(expr.id) ||
this.checkExpr(expr, Ty({ tag: "unknown" }));
}
private checkExpr(expr: ast.Expr, expected: Ty): Ty {
const k = expr.kind;
switch (k.tag) {
case "error":
return Ty({ tag: "error" });
case "path":
return this.checkPathExpr(expr, k, expected);
case "null":
return todo();
case "int":
return todo();
case "bool":
return todo();
case "str":
return todo();
case "group":
return todo();
case "array":
return todo();
case "repeat":
return todo();
case "struct":
return todo();
case "ref":
return todo();
case "deref":
return todo();
case "elem":
return todo();
case "field":
return todo();
case "index":
return todo();
case "call":
return todo();
case "unary":
return todo();
case "binary":
return todo();
case "block":
return todo();
case "if":
return todo();
case "loop":
return todo();
case "while":
return todo();
case "for":
return todo();
case "c_for":
return todo();
}
exhausted(k);
}
private checkPathExpr(
expr: ast.Expr,
kind: ast.PathExpr,
expected: Ty,
): Ty {
const res = this.resols.exprRes(expr.id);
switch (res.kind.tag) {
case "error":
return Ty({ tag: "error" });
case "fn": {
const fn = res.kind.item;
const ty = this.fnItemTy(fn, res.kind.kind);
const resu = this.resolveTys(ty, expected);
if (!resu.ok) {
this.report(resu.val, expr.span);
return Ty({ tag: "error" });
}
return resu.val;
}
case "local": {
const ty = this.exprTy(expr);
const resu = this.resolveTys(ty, expected);
if (!resu.ok) {
this.report(resu.val, expr.span);
return Ty({ tag: "error" });
}
return resu.val;
}
}
exhausted(res.kind);
}
private tyTy(ty: ast.Ty): Ty {
return this.tyTys.get(ty.id) ||
this.checkTy(ty);
}
private checkTy(ty: ast.Ty): Ty {
return todo();
}
private report(msg: string, span: Span) {
this.ctx.report({
severity: "error",
file: this.currentFile,
span,
msg,
});
}
private resolveTys(a: Ty, b: Ty): Res<Ty, string> {
const as = tyToString(this.ctx, a);
const bs = tyToString(this.ctx, b);
const incompat = () =>
Res.Err(
`type '${as}' not compatible with type '${bs}'`,
);
switch (a.kind.tag) {
case "error":
return Res.Ok(b);
case "unknown":
return Res.Ok(b);
case "null": {
if (b.kind.tag !== "null") {
return incompat();
}
return Res.Ok(a);
}
case "fn": {
if (b.kind.tag !== "fn") {
return incompat();
}
if (b.kind.item.id === a.kind.item.id) {
return incompat();
}
return Res.Ok(a);
}
}
exhausted(a.kind);
}
}

View File

@ -53,7 +53,7 @@ export class Ctx {
return keyId(0); return keyId(0);
} }
public iterFiles(): IteratorObject<File> { public iterFiles(): Iterator<File> {
return this.files.keys() return this.files.keys()
.map((key) => keyId(key)); .map((key) => keyId(key));
} }

View File

@ -3,8 +3,6 @@ import { Parser } from "./parse/parser.ts";
import * as ast from "./ast/mod.ts"; import * as ast from "./ast/mod.ts";
import { Ctx } from "./ctx.ts"; import { Ctx } from "./ctx.ts";
import { File } from "./ctx.ts"; import { File } from "./ctx.ts";
import { Resolver } from "./resolve/resolver.ts";
import { Checker } from "./check/checker.ts";
async function main() { async function main() {
const filePath = Deno.args[0]; const filePath = Deno.args[0];
@ -38,11 +36,10 @@ export class PackCompiler {
) {} ) {}
public async compile() { public async compile() {
const [entryFile, entryFileAst] = await FileTreeAstCollector await FileTreeAstCollector
.fromEntryFile(this.ctx, this.astCx, this.entryFilePath) .fromEntryFile(this.ctx, this.astCx, this.entryFilePath)
.collect(); .collect();
const resols = new Resolver(this.ctx, entryFileAst).resolve(); this.ctx.printAsts();
const checker = new Checker(this.ctx, entryFileAst, resols);
} }
public enableDebug() { public enableDebug() {
@ -79,7 +76,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
); );
} }
public async collect(): Promise<[File, ast.File]> { public async collect(): Promise<File> {
const text = await Deno.readTextFile(this.absPath); const text = await Deno.readTextFile(this.absPath);
const file = this.ctx.addFile( const file = this.ctx.addFile(
this.ident, this.ident,
@ -92,7 +89,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
this.ctx.addFileAst(file, fileAst); this.ctx.addFileAst(file, fileAst);
ast.visitFile(this, fileAst, { file }); ast.visitFile(this, fileAst, { file });
await this.subFilePromise; await this.subFilePromise;
return [file, fileAst]; return file;
} }
visitModFileItem( visitModFileItem(
@ -114,7 +111,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
}); });
Deno.exit(1); Deno.exit(1);
} }
const [modFile, modAst] = await new FileTreeAstCollector( const modFile = await new FileTreeAstCollector(
this.ctx, this.ctx,
this.astCx, this.astCx,
file, file,
@ -124,7 +121,6 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
) )
.collect(); .collect();
kind.file = modFile; kind.file = modFile;
kind.ast = modAst;
}); });
return "stop"; return "stop";
} }

View File

@ -1,40 +0,0 @@
import { Span } from "../diagnostics.ts";
export type Stmt = {
kind: StmtKind;
};
export type StmtKind =
| { tag: "error" }
| { tag: "assign" } & AssignStmt
| { tag: "fake_read" } & FakeReadStmt
| { tag: "deinit" } & DeinitStmt
| { tag: "live" } & LiveStmt
| { tag: "dead" } & DeadStmt
| { tag: "mention" } & MentionStmt;
export type AssignStmt = { place: Place; rval: RVal };
export type FakeReadStmt = { place: Place };
export type DeinitStmt = { place: Place };
export type LiveStmt = { local: Local };
export type DeadStmt = { local: Local };
export type MentionStmt = { place: Place };
export type Place = {
local: Local;
proj: ProjElem[];
};
// https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/type.PlaceElem.html
export type ProjElem =
| { tag: "deref" }
| { tag: "repeat" }
| { tag: "field"; fieldIdx: number }
| { tag: "index": local: Local }
| { tag: }
// https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.Rvalue.html
export type RVal = {};
export type Local = {};

View File

@ -1,12 +1,5 @@
fn add(lhs: int, rhs: int) -> int { fn main() -> int {
lhs + rhs
}
fn main() {
let a = 5; let a = 5;
let b = 7;
let c = add(a, b);
} }

View File

@ -1,130 +1,91 @@
import * as ast from "../ast/mod.ts";
import { IdentId, idKey, Key } from "../ctx.ts"; import { IdentId, idKey, Key } from "../ctx.ts";
import { Res } from "../util.ts";
export interface Syms { type Ident = Key<IdentId>;
getVal(ident: ast.Ident): Resolve;
getTy(ident: ast.Ident): Resolve;
defVal(ident: ast.Ident, kind: ResolveKind): Res<void, Redef>; export class Ribs {
defTy(ident: ast.Ident, kind: ResolveKind): Res<void, Redef>; private tyRibs: Rib[] = [];
private valRibs: Rib[] = [];
private constructor() {}
public static withRootMod(): Ribs {
const ribs = new Ribs();
ribs.pushRib({ tag: "mod", mod: { items: new Map() } });
return ribs;
}
public pushRib(kind: RibKind) {
this.tyRibs.push({ bindings: new Map(), kind });
this.valRibs.push({ bindings: new Map(), kind });
}
public hasTy(ident: IdentId): boolean {
return this.tyRibs.at(-1)!.bindings.has(idKey(ident));
}
public defTy(ident: IdentId, res: Res) {
this.tyRibs.at(-1)!.bindings.set(idKey(ident), res);
}
public hasVal(ident: IdentId): boolean {
return this.valRibs.at(-1)!.bindings.has(idKey(ident));
}
public defVal(ident: IdentId, res: Res) {
this.valRibs.at(-1)!.bindings.set(idKey(ident), res);
}
public checkpoint(): number {
return this.tyRibs.length;
}
public returnToCheckpoint(checkpoint: number) {
this.tyRibs = this.tyRibs.slice(checkpoint, this.tyRibs.length);
this.valRibs = this.valRibs.slice(checkpoint, this.valRibs.length);
}
public nearestMod(): Mod {
return [
this.tyRibs
.toReversed()
.find((r) => r.kind.tag === "mod")!,
]
.map((r) => (r.kind.tag === "mod" && r.kind.mod) as Mod)[0];
}
} }
export type Resolve = { export type Mod = {
ident: ast.Ident; parent?: Mod;
kind: ResolveKind; items: Map<Ident, Res>;
}; };
export type ResolveKind = export type Rib = {
| { tag: "error" } bindings: Map<Ident, Res>;
| { tag: "fn"; item: ast.Item; kind: ast.FnItem } kind: RibKind;
| { tag: "local" };
export const ResolveError = (ident: ast.Ident): Resolve => ({
ident,
kind: { tag: "error" },
});
export type Redef = {
ident: ast.Ident;
}; };
export class SymsOneNsTab { export type RibKind =
private defs = new Map<Key<IdentId>, Resolve>(); | { tag: "normal" }
| { tag: "fn" }
| { tag: "item" }
| { tag: "mod"; mod: Mod };
public get(ident: ast.Ident): Resolve | undefined { export type Res =
return this.defs.get(idKey(ident.id))!; | { tag: "def"; def: Def }
} | { tag: "local"; id: number };
public def(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> { export type Def = {
if (this.defs.has(idKey(ident.id))) { type: DefType;
return Res.Err({ ident: this.defs.get(idKey(ident.id))!.ident }); id: number;
} };
this.defs.set(idKey(ident.id), { ident, kind });
return Res.Ok(undefined);
}
}
export class SymsNsTab {
private vals = new SymsOneNsTab();
private tys = new SymsOneNsTab();
public getVal(ident: ast.Ident): Resolve | undefined {
return this.vals.get(ident);
}
public getTy(ident: ast.Ident): Resolve | undefined {
return this.tys.get(ident);
}
public defVal(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
return this.vals.def(ident, kind);
}
public defTy(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
return this.tys.def(ident, kind);
}
}
export class RootSyms implements Syms {
private syms = new SymsNsTab();
getVal(ident: ast.Ident): Resolve {
return this.syms.getVal(ident) || ResolveError(ident);
}
getTy(ident: ast.Ident): Resolve {
return this.syms.getTy(ident) || ResolveError(ident);
}
defVal(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
return this.syms.defVal(ident, kind);
}
defTy(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
return this.syms.defTy(ident, kind);
}
}
export class FnSyms implements Syms {
private syms = new SymsNsTab();
public constructor(
private parent: Syms,
) {}
getVal(ident: ast.Ident): Resolve {
const res = this.syms.getVal(ident) || this.parent.getVal(ident);
if (res.kind.tag === "local") {
return ResolveError(ident);
}
return res;
}
getTy(ident: ast.Ident): Resolve {
return this.syms.getTy(ident) || this.parent.getTy(ident);
}
defVal(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
return this.syms.defVal(ident, kind);
}
defTy(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
return this.syms.defTy(ident, kind);
}
}
export class LocalSyms implements Syms {
private syms = new SymsNsTab();
public constructor(
private parent: Syms,
) {}
getVal(ident: ast.Ident): Resolve {
return this.syms.getVal(ident) || this.parent.getVal(ident);
}
getTy(ident: ast.Ident): Resolve {
return this.syms.getTy(ident) || this.parent.getTy(ident);
}
defVal(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
return this.syms.defVal(ident, kind);
}
defTy(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
return this.syms.defTy(ident, kind);
}
}
export type DefType =
| "mod"
| "enum"
| "struct"
| "variant"
| "ty_alias"
| "ty_param"
| "fn"
| "use"
| "field";

View File

@ -1,67 +1,57 @@
import * as ast from "../ast/mod.ts"; import * as ast from "../ast/mod.ts";
import { Ctx, File } from "../ctx.ts"; import { Ctx, File, IdentId, idKey, Key } from "../ctx.ts";
import { exhausted, todo } from "../util.ts"; import { todo } from "../util.ts";
import { import { Def, DefType, Mod, Res, Ribs } from "./cx.ts";
FnSyms,
LocalSyms,
Resolve,
ResolveError,
RootSyms,
Syms,
} from "./cx.ts";
export class Resols {
public constructor(
private exprResols: Map<number, Resolve>,
) {}
public exprRes(id: number): Resolve {
if (!this.exprResols.has(id)) {
throw new Error();
}
return this.exprResols.get(id)!;
}
}
export class Resolver implements ast.Visitor { export class Resolver implements ast.Visitor {
private ribs = Ribs.withRootMod();
private currentFile!: File; private currentFile!: File;
private rootSyms = new RootSyms();
private syms: Syms = this.rootSyms;
private exprResols = new Map<number, Resolve>();
public constructor( public constructor(
private ctx: Ctx, private ctx: Ctx,
private entryFileAst: ast.File, private entryFileAst: ast.File,
) {} ) {
public resolve(): Resols {
ast.visitFile(this, this.entryFileAst); ast.visitFile(this, this.entryFileAst);
return new Resols(
this.exprResols,
);
} }
visitFile(file: ast.File): ast.VisitRes { visitFile(file: ast.File): ast.VisitRes {
this.currentFile = file.file; this.currentFile = this.entryFileAst.file;
ast.visitStmts(this, file.stmts); ast.visitStmts(this, file.stmts);
this.visitFnBodies();
} }
visitLetStmt(stmt: ast.Stmt, kind: ast.LetStmt): ast.VisitRes { visitLetStmt(stmt: ast.Stmt, kind: ast.LetStmt): ast.VisitRes {
kind.ty && ast.visitTy(this, kind.ty);
kind.expr && ast.visitExpr(this, kind.expr); kind.expr && ast.visitExpr(this, kind.expr);
this.syms = new LocalSyms(this.syms); kind.ty && ast.visitTy(this, kind.ty);
this.ribs.pushRib({ tag: "normal" });
ast.visitPat(this, kind.pat); ast.visitPat(this, kind.pat);
return "stop";
} }
visitModBlockItem(item: ast.Item, kind: ast.ModBlockItem): ast.VisitRes { visitModBlockItem(item: ast.Item, kind: ast.ModBlockItem): ast.VisitRes {
todo(); const mod: Mod = {
parent: this.ribs.nearestMod(),
items: new Map(),
};
const ribPoint = this.ribs.checkpoint();
ast.visitBlock(this, kind.block);
this.ribs.pushRib({ tag: "mod", mod });
this.ribs.returnToCheckpoint(ribPoint);
this.defineTy(item.ident, this.defMod(item.ident.id, item, mod));
return "stop";
} }
visitModFileItem(item: ast.Item, kind: ast.ModFileItem): ast.VisitRes { visitModFileItem(item: ast.Item, kind: ast.ModFileItem): ast.VisitRes {
ast.visitFile(this, kind.ast!); const mod: Mod = {
todo(); parent: this.ribs.nearestMod(),
items: new Map(),
};
const ribPoint = this.ribs.checkpoint();
const fileAst = this.ctx.fileInfo(kind.file!).ast!;
ast.visitFile(this, fileAst);
this.ribs.pushRib({ tag: "mod", mod });
this.ribs.returnToCheckpoint(ribPoint);
this.defineTy(item.ident, this.defMod(item.ident.id, item, mod));
return "stop";
} }
visitEnumItem(item: ast.Item, kind: ast.EnumItem): ast.VisitRes { visitEnumItem(item: ast.Item, kind: ast.EnumItem): ast.VisitRes {
@ -72,55 +62,8 @@ export class Resolver implements ast.Visitor {
todo(); todo();
} }
private fnBodiesToCheck: [ast.Item, ast.FnItem][] = [];
visitFnItem(item: ast.Item, kind: ast.FnItem): ast.VisitRes { visitFnItem(item: ast.Item, kind: ast.FnItem): ast.VisitRes {
this.syms.defVal(item.ident, { tag: "fn", item, kind }); todo();
this.fnBodiesToCheck.push([item, kind]);
return "stop";
}
private visitFnBodies() {
for (const [_item, kind] of this.fnBodiesToCheck) {
const outerSyms = this.syms;
this.syms = new FnSyms(this.syms);
this.syms = new LocalSyms(this.syms);
for (const param of kind.params) {
ast.visitParam(this, param);
}
this.syms = outerSyms;
}
this.fnBodiesToCheck = [];
}
visitPathExpr(expr: ast.Expr, kind: ast.PathExpr): ast.VisitRes {
if (kind.path.segments.length === 1) {
const res = this.syms.getVal(kind.path.segments[0].ident);
switch (res.kind.tag) {
case "error":
return "stop";
case "fn":
this.exprResols.set(expr.id, res);
return "stop";
case "local":
this.exprResols.set(expr.id, res);
return "stop";
}
exhausted(res.kind);
}
const pathRes = this.resolveInnerPath(kind.path);
switch (pathRes.kind.tag) {
case "error":
todo();
return "stop";
case "fn":
todo();
return "stop";
case "local":
todo();
return "stop";
}
exhausted(pathRes.kind);
} }
visitUseItem(item: ast.Item, kind: ast.UseItem): ast.VisitRes { visitUseItem(item: ast.Item, kind: ast.UseItem): ast.VisitRes {
@ -132,70 +75,49 @@ export class Resolver implements ast.Visitor {
} }
visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes { visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
const res = this.syms.defVal(kind.ident, { tag: "local" }); this.ribs.defVal(kind.ident.id, { tag: "local", id: pat.id });
if (!res.ok) { return "stop";
const text = this.ctx.identText(kind.ident.id);
this.ctx.report({
severity: "error",
file: this.currentFile,
span: kind.ident.span,
msg: `redefinition of value '${text}'`,
});
}
} }
visitPathPat(pat: ast.Pat, kind: ast.PathPat): ast.VisitRes { visitPathPat(pat: ast.Pat, kind: ast.PathPat): ast.VisitRes {
todo(); return "stop";
} }
visitBlock(block: ast.Block): ast.VisitRes { private defIdCounter = 0;
ast.visitStmts(this, block.stmts);
this.visitFnBodies(); private modDefs = new Map<number, [ast.Item, Mod]>();
block.expr && ast.visitExpr(this, block.expr); private modDef(id: number): [ast.Item, Mod] {
return this.modDefs.get(id)!;
}
private defMod(_ident: IdentId, item: ast.Item, mod: Mod): Res {
const id = this.defIdCounter++;
this.modDefs.set(id, [item, mod]);
return { tag: "def", def: { id, type: "mod" } };
} }
private resolveInnerPath(path: ast.Path): Resolve { private defineTy(ident: ast.Ident, res: Res) {
const res = path.segments.slice(1, path.segments.length) if (this.ribs.hasTy(ident.id)) {
.reduce((innerRes, seg) => { const text = this.ctx.identText(ident.id);
const k = innerRes.kind; this.ctx.report({
switch (k.tag) { severity: "error",
case "error": file: this.currentFile,
return innerRes; span: ident.span,
case "fn": msg: `redefinition of type '${text}'`,
this.ctx.report({ });
severity: "error", }
file: this.currentFile, this.ribs.defTy(ident.id, res);
span: seg.ident.span,
msg: "function, not pathable",
});
return ResolveError(seg.ident);
case "local":
this.ctx.report({
severity: "error",
file: this.currentFile,
span: seg.ident.span,
msg: "local variable, not pathable",
});
return ResolveError(seg.ident);
}
exhausted(k);
}, this.syms.getTy(path.segments[0].ident));
return res;
} }
// const text = this.ctx.identText(ident.id); private defineVal(ident: ast.Ident, res: Res) {
// this.ctx.report({ if (this.ribs.hasVal(ident.id)) {
// severity: "error", const text = this.ctx.identText(ident.id);
// file: this.currentFile, this.ctx.report({
// span: ident.span, severity: "error",
// msg: `redefinition of type '${text}'`, file: this.currentFile,
// }); span: ident.span,
// msg: `redefinition of value '${text}'`,
// const text = this.ctx.identText(ident.id); });
// this.ctx.report({ }
// severity: "error", this.ribs.defVal(ident.id, res);
// file: this.currentFile, }
// span: ident.span,
// msg: `redefinition of value '${text}'`,
// });
} }

View File

@ -1,24 +0,0 @@
import { Ctx } from "../ctx.ts";
import { exhausted } from "../util.ts";
import { Ty } from "./ty.ts";
export function tyToString(ctx: Ctx, ty: Ty): string {
const k = ty.kind;
switch (k.tag) {
case "error":
return `<error>`;
case "unknown":
return `<unknown>`;
case "null":
return `null`;
case "fn": {
const identText = ctx.identText(k.item.ident.id);
const params = k.params
.map((param) => tyToString(ctx, param))
.join(", ");
const reTy = tyToString(ctx, k.returnTy);
return `fn ${identText}(${params}) -> ${reTy}`;
}
}
exhausted(k);
}

View File

@ -1,19 +0,0 @@
import * as ast from "../ast/mod.ts";
export type Ty = {
kind: TyKind;
};
export const Ty = (kind: TyKind): Ty => ({ kind });
export type TyKind =
| { tag: "error" }
| { tag: "unknown" }
| { tag: "null" }
| {
tag: "fn";
item: ast.Item;
kind: ast.FnItem;
params: Ty[];
returnTy: Ty;
};

View File

@ -3,7 +3,7 @@ export function todo<T>(msg?: string): T {
throw new NotImplemented(msg); throw new NotImplemented(msg);
} }
export function exhausted<T>(_: never): T { export function exhausted(_: never) {
class Unexhausted extends Error {} class Unexhausted extends Error {}
throw new Unexhausted(); throw new Unexhausted();
} }