135 lines
3.8 KiB
TypeScript
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();
|