typchk or smthng idk
This commit is contained in:
parent
1f9cbea832
commit
60efd931f5
@ -62,7 +62,9 @@ export type ItemKind =
|
||||
| { tag: "type_alias" } & TypeAliasItem;
|
||||
|
||||
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 StructItem = { data: VariantData };
|
||||
|
||||
|
33
compiler/ast/to_string.ts
Normal file
33
compiler/ast/to_string.ts
Normal file
@ -0,0 +1,33 @@
|
||||
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();
|
||||
}
|
185
compiler/check/checker.ts
Normal file
185
compiler/check/checker.ts
Normal file
@ -0,0 +1,185 @@
|
||||
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);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import * as ast from "./ast/mod.ts";
|
||||
import { Ctx } from "./ctx.ts";
|
||||
import { File } from "./ctx.ts";
|
||||
import { Resolver } from "./resolve/resolver.ts";
|
||||
import { Checker } from "./check/checker.ts";
|
||||
|
||||
async function main() {
|
||||
const filePath = Deno.args[0];
|
||||
@ -37,13 +38,11 @@ export class PackCompiler {
|
||||
) {}
|
||||
|
||||
public async compile() {
|
||||
await FileTreeAstCollector
|
||||
const [entryFile, entryFileAst] = await FileTreeAstCollector
|
||||
.fromEntryFile(this.ctx, this.astCx, this.entryFilePath)
|
||||
.collect();
|
||||
// this.ctx.printAsts();
|
||||
const entryFile = this.ctx.entryFile();
|
||||
const entryFileAst = this.ctx.fileInfo(entryFile).ast!;
|
||||
new Resolver(this.ctx, entryFileAst).resolve();
|
||||
const resols = new Resolver(this.ctx, entryFileAst).resolve();
|
||||
const checker = new Checker(this.ctx, entryFileAst, resols);
|
||||
}
|
||||
|
||||
public enableDebug() {
|
||||
@ -80,7 +79,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
||||
);
|
||||
}
|
||||
|
||||
public async collect(): Promise<File> {
|
||||
public async collect(): Promise<[File, ast.File]> {
|
||||
const text = await Deno.readTextFile(this.absPath);
|
||||
const file = this.ctx.addFile(
|
||||
this.ident,
|
||||
@ -93,7 +92,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
||||
this.ctx.addFileAst(file, fileAst);
|
||||
ast.visitFile(this, fileAst, { file });
|
||||
await this.subFilePromise;
|
||||
return file;
|
||||
return [file, fileAst];
|
||||
}
|
||||
|
||||
visitModFileItem(
|
||||
@ -115,7 +114,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
||||
});
|
||||
Deno.exit(1);
|
||||
}
|
||||
const modFile = await new FileTreeAstCollector(
|
||||
const [modFile, modAst] = await new FileTreeAstCollector(
|
||||
this.ctx,
|
||||
this.astCx,
|
||||
file,
|
||||
@ -125,6 +124,7 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
||||
)
|
||||
.collect();
|
||||
kind.file = modFile;
|
||||
kind.ast = modAst;
|
||||
});
|
||||
return "stop";
|
||||
}
|
||||
|
40
compiler/middle/mir.ts
Normal file
40
compiler/middle/mir.ts
Normal file
@ -0,0 +1,40 @@
|
||||
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 = {};
|
||||
|
@ -9,3 +9,4 @@ fn main() {
|
||||
let c = add(a, b);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,94 +1,130 @@
|
||||
import * as ast from "../ast/mod.ts";
|
||||
import { IdentId, idKey, Key } from "../ctx.ts";
|
||||
import { Res } from "../util.ts";
|
||||
|
||||
type Ident = Key<IdentId>;
|
||||
export interface Syms {
|
||||
getVal(ident: ast.Ident): Resolve;
|
||||
getTy(ident: ast.Ident): Resolve;
|
||||
|
||||
export class Ribs {
|
||||
private tyRibs: Rib[] = [];
|
||||
private valRibs: Rib[] = [];
|
||||
defVal(ident: ast.Ident, kind: ResolveKind): Res<void, Redef>;
|
||||
defTy(ident: ast.Ident, kind: ResolveKind): Res<void, Redef>;
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
export type Resolve = {
|
||||
ident: ast.Ident;
|
||||
kind: ResolveKind;
|
||||
};
|
||||
|
||||
public static withRootMod(): Ribs {
|
||||
const ribs = new Ribs();
|
||||
ribs.pushRib({ tag: "mod", mod: { items: new Map() } });
|
||||
return ribs;
|
||||
export type ResolveKind =
|
||||
| { tag: "error" }
|
||||
| { tag: "fn"; item: ast.Item; kind: ast.FnItem }
|
||||
| { tag: "local" };
|
||||
|
||||
export const ResolveError = (ident: ast.Ident): Resolve => ({
|
||||
ident,
|
||||
kind: { tag: "error" },
|
||||
});
|
||||
|
||||
export type Redef = {
|
||||
ident: ast.Ident;
|
||||
};
|
||||
|
||||
export class SymsOneNsTab {
|
||||
private defs = new Map<Key<IdentId>, Resolve>();
|
||||
|
||||
public get(ident: ast.Ident): Resolve | undefined {
|
||||
return this.defs.get(idKey(ident.id))!;
|
||||
}
|
||||
|
||||
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 val(ident: IdentId) {
|
||||
}
|
||||
|
||||
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];
|
||||
public def(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> {
|
||||
if (this.defs.has(idKey(ident.id))) {
|
||||
return Res.Err({ ident: this.defs.get(idKey(ident.id))!.ident });
|
||||
}
|
||||
this.defs.set(idKey(ident.id), { ident, kind });
|
||||
return Res.Ok(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
export type Mod = {
|
||||
parent?: Mod;
|
||||
items: Map<Ident, Res>;
|
||||
};
|
||||
export class SymsNsTab {
|
||||
private vals = new SymsOneNsTab();
|
||||
private tys = new SymsOneNsTab();
|
||||
|
||||
export type Rib = {
|
||||
bindings: Map<Ident, Res>;
|
||||
kind: RibKind;
|
||||
};
|
||||
public getVal(ident: ast.Ident): Resolve | undefined {
|
||||
return this.vals.get(ident);
|
||||
}
|
||||
public getTy(ident: ast.Ident): Resolve | undefined {
|
||||
return this.tys.get(ident);
|
||||
}
|
||||
|
||||
export type RibKind =
|
||||
| { tag: "normal" }
|
||||
| { tag: "fn" }
|
||||
| { tag: "item" }
|
||||
| { tag: "mod"; mod: Mod };
|
||||
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 type Res =
|
||||
| { tag: "def"; def: Def }
|
||||
| { tag: "local"; id: number };
|
||||
export class RootSyms implements Syms {
|
||||
private syms = new SymsNsTab();
|
||||
|
||||
export type Def = {
|
||||
type: DefType;
|
||||
id: number;
|
||||
};
|
||||
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";
|
||||
|
@ -1,60 +1,67 @@
|
||||
import * as ast from "../ast/mod.ts";
|
||||
import { Ctx, File, IdentId, idKey, Key } from "../ctx.ts";
|
||||
import { todo } from "../util.ts";
|
||||
import { Def, DefType, Mod, Res, Ribs } from "./cx.ts";
|
||||
import { Ctx, File } from "../ctx.ts";
|
||||
import { exhausted, todo } from "../util.ts";
|
||||
import {
|
||||
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 {
|
||||
private ribs = Ribs.withRootMod();
|
||||
private currentFile!: File;
|
||||
private rootSyms = new RootSyms();
|
||||
private syms: Syms = this.rootSyms;
|
||||
|
||||
private exprResols = new Map<number, Resolve>();
|
||||
|
||||
public constructor(
|
||||
private ctx: Ctx,
|
||||
private entryFileAst: ast.File,
|
||||
) {}
|
||||
|
||||
public resolve() {
|
||||
public resolve(): Resols {
|
||||
ast.visitFile(this, this.entryFileAst);
|
||||
return new Resols(
|
||||
this.exprResols,
|
||||
);
|
||||
}
|
||||
|
||||
visitFile(file: ast.File): ast.VisitRes {
|
||||
this.currentFile = this.entryFileAst.file;
|
||||
this.currentFile = file.file;
|
||||
ast.visitStmts(this, file.stmts);
|
||||
this.resolveFnBlocks();
|
||||
this.visitFnBodies();
|
||||
}
|
||||
|
||||
visitLetStmt(stmt: ast.Stmt, kind: ast.LetStmt): ast.VisitRes {
|
||||
kind.expr && ast.visitExpr(this, kind.expr);
|
||||
kind.ty && ast.visitTy(this, kind.ty);
|
||||
this.ribs.pushRib({ tag: "normal" });
|
||||
kind.expr && ast.visitExpr(this, kind.expr);
|
||||
this.syms = new LocalSyms(this.syms);
|
||||
ast.visitPat(this, kind.pat);
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitModBlockItem(item: ast.Item, kind: ast.ModBlockItem): ast.VisitRes {
|
||||
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";
|
||||
todo();
|
||||
}
|
||||
|
||||
visitModFileItem(item: ast.Item, kind: ast.ModFileItem): ast.VisitRes {
|
||||
const mod: Mod = {
|
||||
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";
|
||||
ast.visitFile(this, kind.ast!);
|
||||
todo();
|
||||
}
|
||||
|
||||
visitEnumItem(item: ast.Item, kind: ast.EnumItem): ast.VisitRes {
|
||||
@ -65,29 +72,55 @@ export class Resolver implements ast.Visitor {
|
||||
todo();
|
||||
}
|
||||
|
||||
private fnBlocksToResolve: [ast.Item, ast.FnItem][] = [];
|
||||
private fnBodiesToCheck: [ast.Item, ast.FnItem][] = [];
|
||||
|
||||
visitFnItem(item: ast.Item, kind: ast.FnItem): ast.VisitRes {
|
||||
this.defineVal(item.ident, this.defFn(item.ident.id, item, kind));
|
||||
this.fnBlocksToResolve.push([item, kind]);
|
||||
this.syms.defVal(item.ident, { tag: "fn", item, kind });
|
||||
this.fnBodiesToCheck.push([item, kind]);
|
||||
return "stop";
|
||||
}
|
||||
|
||||
private resolveFnBlocks() {
|
||||
for (const [item, kind] of this.fnBlocksToResolve) {
|
||||
const ribPoint = this.ribs.checkpoint();
|
||||
this.ribs.pushRib({ tag: "fn" });
|
||||
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);
|
||||
}
|
||||
ast.visitBlock(this, kind.body!);
|
||||
this.ribs.returnToCheckpoint(ribPoint);
|
||||
this.syms = outerSyms;
|
||||
}
|
||||
this.fnBlocksToResolve = [];
|
||||
this.fnBodiesToCheck = [];
|
||||
}
|
||||
|
||||
visitPathExpr(expr: ast.Expr, kind: ast.PathExpr): ast.VisitRes {
|
||||
todo();
|
||||
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 {
|
||||
@ -99,70 +132,70 @@ export class Resolver implements ast.Visitor {
|
||||
}
|
||||
|
||||
visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
|
||||
this.ribs.defVal(kind.ident.id, { tag: "local", id: pat.id });
|
||||
return "stop";
|
||||
const res = this.syms.defVal(kind.ident, { tag: "local" });
|
||||
if (!res.ok) {
|
||||
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 {
|
||||
todo();
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitBlock(block: ast.Block): ast.VisitRes {
|
||||
ast.visitStmts(this, block.stmts);
|
||||
this.visitFnBodies();
|
||||
block.expr && ast.visitExpr(this, block.expr);
|
||||
this.resolveFnBlocks();
|
||||
return "stop";
|
||||
}
|
||||
|
||||
private defIdCounter = 0;
|
||||
|
||||
private modDefs = new Map<number, [ast.Item, Mod]>();
|
||||
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 {
|
||||
const res = path.segments.slice(1, path.segments.length)
|
||||
.reduce((innerRes, seg) => {
|
||||
const k = innerRes.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
return innerRes;
|
||||
case "fn":
|
||||
this.ctx.report({
|
||||
severity: "error",
|
||||
file: this.currentFile,
|
||||
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;
|
||||
}
|
||||
|
||||
private fnDefs = new Map<number, [ast.Item, ast.FnItem]>();
|
||||
private fnDef(id: number): [ast.Item, ast.FnItem] {
|
||||
return this.fnDefs.get(id)!;
|
||||
}
|
||||
private defFn(_ident: IdentId, item: ast.Item, kind: ast.FnItem): Res {
|
||||
const id = this.defIdCounter++;
|
||||
this.fnDefs.set(id, [item, kind]);
|
||||
return { tag: "def", def: { id, type: "fn" } };
|
||||
}
|
||||
|
||||
private defineTy(ident: ast.Ident, res: Res) {
|
||||
if (this.ribs.hasTy(ident.id)) {
|
||||
const text = this.ctx.identText(ident.id);
|
||||
this.ctx.report({
|
||||
severity: "error",
|
||||
file: this.currentFile,
|
||||
span: ident.span,
|
||||
msg: `redefinition of type '${text}'`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.ribs.defTy(ident.id, res);
|
||||
}
|
||||
|
||||
private defineVal(ident: ast.Ident, res: Res) {
|
||||
if (this.ribs.hasVal(ident.id)) {
|
||||
console.log(this.ribs);
|
||||
const text = this.ctx.identText(ident.id);
|
||||
this.ctx.report({
|
||||
severity: "error",
|
||||
file: this.currentFile,
|
||||
span: ident.span,
|
||||
msg: `redefinition of value '${text}'`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.ribs.defVal(ident.id, res);
|
||||
}
|
||||
// const text = this.ctx.identText(ident.id);
|
||||
// this.ctx.report({
|
||||
// severity: "error",
|
||||
// file: this.currentFile,
|
||||
// span: ident.span,
|
||||
// msg: `redefinition of type '${text}'`,
|
||||
// });
|
||||
//
|
||||
// const text = this.ctx.identText(ident.id);
|
||||
// this.ctx.report({
|
||||
// severity: "error",
|
||||
// file: this.currentFile,
|
||||
// span: ident.span,
|
||||
// msg: `redefinition of value '${text}'`,
|
||||
// });
|
||||
}
|
||||
|
24
compiler/ty/to_string.ts
Normal file
24
compiler/ty/to_string.ts
Normal file
@ -0,0 +1,24 @@
|
||||
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);
|
||||
}
|
19
compiler/ty/ty.ts
Normal file
19
compiler/ty/ty.ts
Normal file
@ -0,0 +1,19 @@
|
||||
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;
|
||||
};
|
@ -3,7 +3,7 @@ export function todo<T>(msg?: string): T {
|
||||
throw new NotImplemented(msg);
|
||||
}
|
||||
|
||||
export function exhausted(_: never) {
|
||||
export function exhausted<T>(_: never): T {
|
||||
class Unexhausted extends Error {}
|
||||
throw new Unexhausted();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user