import * as path from "jsr:@std/path"; import { Parser } from "./parser/parser.ts"; import * as ast from "./ast/mod.ts"; import { Ctx } from "./ctx.ts"; import { File } from "./ctx.ts"; export type Pack = { rootMod: Mod; }; export type Mod = null; export interface PackEmitter { emit(pack: Pack): void; } export class PackCompiler { private ctx = new Ctx(); public constructor( private entryFilePath: string, private emitter: PackEmitter, ) {} public compile() { FileTreeAstCollector .fromEntryFile(this.ctx, this.entryFilePath) .collect(); } } type _P = { file: File }; export class FileTreeAstCollector implements ast.Visitor<[_P]> { private subFilePromise = Promise.resolve(); private constructor( private ctx: Ctx, private superFile: File | undefined, private ident: string, private absPath: string, private relPath: string, ) {} public static fromEntryFile( ctx: Ctx, entryFilePath: string, ): FileTreeAstCollector { return new FileTreeAstCollector( ctx, undefined, "root", entryFilePath, entryFilePath, ); } public async collect(): Promise { 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(file, text).parse(); this.ctx.addFileAst(file, fileAst); ast.visitFile(this, fileAst, { file }); await this.subFilePromise; } visitModFileItem( item: ast.Item, kind: ast.ModFileItem, { file }: _P, ): ast.VisitRes { const { ident: { text: ident }, filePath: relPath } = kind; const absPath = path.join(path.dirname(this.absPath), relPath); this.subFilePromise = this.subFilePromise .then(() => { if (this.ctx.fileHasChildWithIdent(file, ident)) { this.ctx.report({ severity: "fatal", msg: `module '${ident}' already declared`, file, span: item.span, }); Deno.exit(1); } return new FileTreeAstCollector( this.ctx, file, ident, absPath, relPath, ) .collect(); }); return "stop"; } }