230 lines
6.1 KiB
TypeScript
230 lines
6.1 KiB
TypeScript
import * as ast from "./ast/mod.ts";
|
|
import {
|
|
Pos,
|
|
prettyPrintReport,
|
|
printStackTrace,
|
|
Report,
|
|
Span,
|
|
} from "./diagnostics.ts";
|
|
import * as hir from "./middle/hir.ts";
|
|
|
|
export class Ctx {
|
|
private fileIds = new Ids<File>();
|
|
private files = new Map<Key<File>, FileInfo>();
|
|
|
|
private reports: Report[] = [];
|
|
|
|
public fileHasChildWithIdent(file: File, childIdent: string): boolean {
|
|
return this.files.get(idKey(file))!
|
|
.subFiles.has(childIdent);
|
|
}
|
|
|
|
public addFile(
|
|
ident: string,
|
|
absPath: string,
|
|
relPath: string,
|
|
superFile: File | undefined,
|
|
text: string,
|
|
): File {
|
|
const file = this.fileIds.nextThenStep();
|
|
this.files.set(idKey(file), {
|
|
ident,
|
|
absPath,
|
|
relPath,
|
|
superFile,
|
|
subFiles: new Map(),
|
|
text,
|
|
});
|
|
if (superFile) {
|
|
this.files.get(idKey(superFile))!
|
|
.subFiles.set(ident, file);
|
|
}
|
|
return file;
|
|
}
|
|
|
|
public addFileAst(file: File, ast: ast.File) {
|
|
this.files.get(idKey(file))!.ast = ast;
|
|
}
|
|
|
|
public fileInfo(file: File): FileInfo {
|
|
return this.files.get(idKey(file))!;
|
|
}
|
|
|
|
public entryFile(): File {
|
|
return keyId(0);
|
|
}
|
|
|
|
public iterFiles(): Iterator<File> {
|
|
return this.files.keys()
|
|
.map((key) => keyId(key));
|
|
}
|
|
|
|
//
|
|
|
|
private identIds = new Ids<IdentId>();
|
|
private identStringToId = new Map<string, IdentId>();
|
|
private identIdToString = new Map<Key<IdentId>, string>();
|
|
|
|
public internIdent(ident: string): IdentId {
|
|
if (this.identStringToId.has(ident)) {
|
|
return this.identStringToId.get(ident)!;
|
|
}
|
|
const id = this.identIds.nextThenStep();
|
|
this.identStringToId.set(ident, id);
|
|
this.identIdToString.set(idKey(id), ident);
|
|
return id;
|
|
}
|
|
|
|
public identText(ident: IdentId): string {
|
|
return this.identIdToString.get(idKey(ident))!;
|
|
}
|
|
|
|
//
|
|
|
|
private hirIds = new Ids<hir.HirId>();
|
|
|
|
private stmts = new Map<Key<hir.HirId>, hir.Stmt>();
|
|
|
|
/// don't intern the same thing twice
|
|
public internStmt(kind: hir.StmtKind, span: Span): hir.Stmt {
|
|
const id = this.hirIds.nextThenStep();
|
|
const v: hir.Stmt = { id, kind, span };
|
|
this.stmts.set(idKey(id), v);
|
|
return v;
|
|
}
|
|
|
|
private exprs = new Map<Key<hir.HirId>, hir.Expr>();
|
|
|
|
/// don't intern the same thing twice
|
|
public internExpr(kind: hir.ExprKind, span: Span): hir.Expr {
|
|
const id = this.hirIds.nextThenStep();
|
|
const v: hir.Expr = { id, kind, span };
|
|
this.exprs.set(idKey(id), v);
|
|
return v;
|
|
}
|
|
|
|
private pats = new Map<Key<hir.HirId>, hir.Pat>();
|
|
|
|
/// don't intern the same thing twice
|
|
public internPat(kind: hir.PatKind, span: Span): hir.Pat {
|
|
const id = this.hirIds.nextThenStep();
|
|
const v: hir.Pat = { id, kind, span };
|
|
this.pats.set(idKey(id), v);
|
|
return v;
|
|
}
|
|
|
|
private tys = new Map<Key<hir.HirId>, hir.Ty>();
|
|
|
|
/// don't intern the same thing twice
|
|
public internTy(kind: hir.TyKind, span: Span): hir.Ty {
|
|
const id = this.hirIds.nextThenStep();
|
|
const v: hir.Ty = { id, kind, span };
|
|
this.tys.set(idKey(id), v);
|
|
return v;
|
|
}
|
|
|
|
private blocks = new Map<Key<hir.HirId>, hir.Block>();
|
|
|
|
/// don't intern the same thing twice
|
|
public internBlock(block: Omit<hir.Block, "id">): hir.Block {
|
|
const id = this.hirIds.nextThenStep();
|
|
const v: hir.Block = { id, ...block };
|
|
this.blocks.set(idKey(id), v);
|
|
return v;
|
|
}
|
|
|
|
//
|
|
|
|
private defIds = new Ids<hir.DefId>();
|
|
|
|
private defs = new Map<Key<hir.DefId>, hir.HirId>();
|
|
|
|
public addHirIdDef(hirId: hir.HirId): hir.DefId {
|
|
const id = this.defIds.nextThenStep();
|
|
this.defs.set(idKey(id), hirId);
|
|
return id;
|
|
}
|
|
|
|
public defHirId(id: hir.DefId): hir.HirId {
|
|
return this.defs.get(idKey(id))!;
|
|
}
|
|
|
|
//
|
|
|
|
public filePosLineText(file: File, pos: Pos): string {
|
|
const fileTextLines = this.fileInfo(file).text.split("\n");
|
|
return fileTextLines[pos.line - 1];
|
|
}
|
|
|
|
public fileSpanText(file: File, span: Span): string {
|
|
let result = "";
|
|
const fileTextLines = this.fileInfo(file).text.split("\n");
|
|
|
|
for (let i = 0; i < fileTextLines.length; i++) {
|
|
if (i > span.end.line - 1) {
|
|
break;
|
|
}
|
|
if (i >= span.begin.line - 1) {
|
|
result += fileTextLines[i] + "\n";
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public report(rep: Report) {
|
|
this.reports.push(rep);
|
|
this.reportImmediately(rep);
|
|
}
|
|
|
|
public enableReportImmediately = false;
|
|
public enableStacktrace = false;
|
|
private reportImmediately(rep: Report) {
|
|
if (this.enableReportImmediately) {
|
|
prettyPrintReport(this, rep);
|
|
if (this.enableStacktrace) {
|
|
printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public printAsts() {
|
|
for (const [_, info] of this.files) {
|
|
console.log(`${info.absPath}:`);
|
|
console.log(JSON.stringify(info.ast!, null, 2));
|
|
}
|
|
}
|
|
}
|
|
|
|
export type File = IdBase & { readonly _: unique symbol };
|
|
|
|
export type FileInfo = {
|
|
ident: string;
|
|
absPath: string;
|
|
relPath: string;
|
|
superFile?: File;
|
|
subFiles: Map<string, File>;
|
|
text: string;
|
|
ast?: ast.File;
|
|
};
|
|
|
|
export type IdentId = IdBase & { readonly _: unique symbol };
|
|
export type HirId = IdBase & { readonly _: unique symbol };
|
|
export type DefId = IdBase & { readonly _: unique symbol };
|
|
|
|
export type IdBase = { key: number };
|
|
|
|
export type Key<IdType extends IdBase> = IdType["key"];
|
|
export const idKey = <IdType extends IdBase>(id: IdType): Key<IdType> => id.key;
|
|
export const keyId = <IdType extends IdBase>(
|
|
key: Key<IdType>,
|
|
): IdType => ({ key } as IdType);
|
|
|
|
export class Ids<IdType extends IdBase> {
|
|
private next = 0;
|
|
public nextThenStep(): IdType {
|
|
const key = this.next;
|
|
this.next += 1;
|
|
return { key } as IdType;
|
|
}
|
|
}
|