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