slige-mirror/compiler/main.ts
2025-02-04 15:06:19 +01:00

135 lines
3.8 KiB
TypeScript

import * as path from "jsr:@std/path";
import { Parser } from "./parse/parser.ts";
import * as ast from "./ast/mod.ts";
import { Ctx, File } from "./ctx.ts";
import { Resolver } from "./resolve/resolver.ts";
import { Checker } from "./check/checker.ts";
import { AstLowerer } from "./middle/ast_lower.ts";
async function main() {
const filePath = Deno.args[0];
const compiler = new PackCompiler(filePath, new NullEmitter());
compiler.enableDebug();
await compiler.compile();
}
export type Pack = {
rootMod: Mod;
};
export type Mod = null;
export interface PackEmitter {
emit(pack: Pack): void;
}
export class NullEmitter implements PackEmitter {
emit(pack: Pack): void {
}
}
export class PackCompiler {
private ctx = new Ctx();
private astCx = new ast.Cx();
public constructor(
private entryFilePath: string,
private emitter: PackEmitter,
) {}
public async compile() {
const [entryFile, entryFileAst] = await FileTreeAstCollector
.fromEntryFile(this.ctx, this.astCx, this.entryFilePath)
.collect();
const resols = new Resolver(this.ctx, entryFileAst).resolve();
const checker = new Checker(this.ctx, entryFileAst, resols);
new AstLowerer(this.ctx, resols, checker, entryFileAst).lower();
}
public enableDebug() {
this.ctx.enableReportImmediately = true;
this.ctx.enableStacktrace = true;
}
}
type _P = { file: File };
export class FileTreeAstCollector implements ast.Visitor<[_P]> {
private subFilePromise = Promise.resolve();
private constructor(
private ctx: Ctx,
private astCx: ast.Cx,
private superFile: File | undefined,
private ident: string,
private absPath: string,
private relPath: string,
) {}
public static fromEntryFile(
ctx: Ctx,
astCx: ast.Cx,
entryFilePath: string,
): FileTreeAstCollector {
return new FileTreeAstCollector(
ctx,
astCx,
undefined,
"root",
entryFilePath,
entryFilePath,
);
}
public async collect(): Promise<[File, ast.File]> {
const text = await Deno.readTextFile(this.absPath);
const file = this.ctx.addFile(
this.ident,
this.absPath,
this.relPath,
this.superFile,
text,
);
const fileAst = new Parser(this.ctx, this.astCx, file).parse();
this.ctx.addFileAst(file, fileAst);
ast.visitFile(this, fileAst, { file });
await this.subFilePromise;
return [file, fileAst];
}
visitModFileItem(
item: ast.Item,
kind: ast.ModFileItem,
{ file }: _P,
): ast.VisitRes {
const ident = this.ctx.identText(item.ident.id);
const { filePath: relPath } = kind;
const absPath = path.join(path.dirname(this.absPath), relPath);
this.subFilePromise = this.subFilePromise
.then(async () => {
if (this.ctx.fileHasChildWithIdent(file, ident)) {
this.ctx.report({
severity: "fatal",
msg: `module '${ident}' already declared`,
file,
span: item.span,
});
Deno.exit(1);
}
const [modFile, modAst] = await new FileTreeAstCollector(
this.ctx,
this.astCx,
file,
ident,
absPath,
relPath,
)
.collect();
kind.file = modFile;
kind.ast = modAst;
});
return "stop";
}
}
main();