slige-mirror/compiler/resolve/resolver.ts
2025-02-03 08:52:54 +01:00

169 lines
5.2 KiB
TypeScript

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";
export class Resolver implements ast.Visitor {
private ribs = Ribs.withRootMod();
private currentFile!: File;
public constructor(
private ctx: Ctx,
private entryFileAst: ast.File,
) {}
public resolve() {
ast.visitFile(this, this.entryFileAst);
}
visitFile(file: ast.File): ast.VisitRes {
this.currentFile = this.entryFileAst.file;
ast.visitStmts(this, file.stmts);
this.resolveFnBlocks();
}
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" });
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";
}
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";
}
visitEnumItem(item: ast.Item, kind: ast.EnumItem): ast.VisitRes {
todo();
}
visitStructItem(item: ast.Item, kind: ast.StructItem): ast.VisitRes {
todo();
}
private fnBlocksToResolve: [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]);
return "stop";
}
private resolveFnBlocks() {
for (const [item, kind] of this.fnBlocksToResolve) {
const ribPoint = this.ribs.checkpoint();
this.ribs.pushRib({ tag: "fn" });
for (const param of kind.params) {
ast.visitParam(this, param);
}
ast.visitBlock(this, kind.body!);
this.ribs.returnToCheckpoint(ribPoint);
}
this.fnBlocksToResolve = [];
}
visitPathExpr(expr: ast.Expr, kind: ast.PathExpr): ast.VisitRes {
todo();
}
visitUseItem(item: ast.Item, kind: ast.UseItem): ast.VisitRes {
todo();
}
visitTypeAliasItem(item: ast.Item, kind: ast.TypeAliasItem): ast.VisitRes {
todo();
}
visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
this.ribs.defVal(kind.ident.id, { tag: "local", id: pat.id });
return "stop";
}
visitPathPat(pat: ast.Pat, kind: ast.PathPat): ast.VisitRes {
todo();
return "stop";
}
visitBlock(block: ast.Block): ast.VisitRes {
ast.visitStmts(this, block.stmts);
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 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);
}
}