add stuff

This commit is contained in:
sfja 2025-01-22 22:40:29 +01:00
parent e8cfd059cc
commit 93dd4c32c8
8 changed files with 319 additions and 5 deletions

23
compiler/ast/ast.ts Normal file
View File

@ -0,0 +1,23 @@
export type File = {
stmts: Stmt[];
};
export type Stmt = {
id: number;
kind: StmtKind;
};
export type StmtKind =
| { tag: "error" }
| { tag: "mod_block" } & ModBlockStmt
| { tag: "mod_file" } & ModFileStmt;
export type ModBlockStmt = {
ident: string;
stmts: Stmt[];
};
export type ModFileStmt = {
ident: string;
filePath: string;
};

2
compiler/ast/mod.ts Normal file
View File

@ -0,0 +1,2 @@
export * from "./ast.ts";
export * from "./visitor.ts";

62
compiler/ast/visitor.ts Normal file
View File

@ -0,0 +1,62 @@
import { exhausted } from "../util.ts";
import { File, ModBlockStmt, ModFileStmt, Stmt } from "./ast.ts";
export type VisitRes = "stop" | void;
type R = VisitRes;
type PM = unknown[];
export interface Visitor<
P extends PM = [],
> {
visitFile?(file: File, ...p: P): R;
visitStmt?(stmt: Stmt, ...p: P): R;
visitErrorStmt?(stmt: Stmt, ...p: P): R;
visitModBlockStmt?(stmt: Stmt, kind: ModBlockStmt, ...p: P): R;
visitModFileStmt?(stmt: Stmt, kind: ModFileStmt, ...p: P): R;
}
export function visitFile<
P extends PM = [],
>(
v: Visitor<P>,
file: File,
...p: P
) {
if (v.visitFile?.(file, ...p) === "stop") return;
visitStmts(v, file.stmts, ...p);
}
export function visitStmts<
P extends PM = [],
>(
v: Visitor<P>,
stmts: Stmt[],
...p: P
) {
for (const stmt of stmts) {
visitStmt(v, stmt, ...p);
}
}
export function visitStmt<
P extends PM = [],
>(
v: Visitor<P>,
stmt: Stmt,
...p: P
) {
switch (stmt.kind.tag) {
case "error":
if (v.visitErrorStmt?.(stmt, ...p) === "stop") return;
return;
case "mod_block":
if (v.visitModBlockStmt?.(stmt, stmt.kind, ...p) === "stop") return;
visitStmts(v, stmt.kind.stmts, ...p);
return;
case "mod_file":
if (v.visitModFileStmt?.(stmt, stmt.kind, ...p) === "stop") return;
return;
}
exhausted(stmt.kind);
}

107
compiler/ctx.ts Normal file
View File

@ -0,0 +1,107 @@
import * as ast from "./ast/mod.ts";
import { Diag } from "./diagnostics.ts";
export class Ctx {
private fileIds = new Ids();
private files = new Map<Id<File>, FileInfo>();
private diags: Diag[] = [];
public fileHasChildWithIdent(file: File, childIdent: string): boolean {
return this.files.get(id(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(id(file), {
ident,
absPath,
relPath,
superFile,
subFiles: new Map(),
text,
});
if (superFile) {
this.files.get(id(superFile))!
.subFiles.set(ident, file);
}
return file;
}
public addFileAst(file: File, ast: ast.File) {
this.files.get(id(file))!.ast = ast;
}
public fileInfo(file: File): FileInfo {
this.files.get(id(file))!;
}
public reportFatal(file: File, msg: string) {
console.error(`fatal: ${msg}`);
this.reportImmediately(file, msg);
}
public enableReportImmediately = false;
public enableStacktrace = false;
private reportImmediately(file: File, msg: string) {
if (!this.enableReportImmediately) {
return;
}
if (!this.enableStacktrace) {
return;
}
class StackTracer extends Error {
constructor() {
super("StackTracer");
}
}
try {
//throw new ReportNotAnError();
} catch (error) {
if (!(error instanceof StackTracer)) {
throw error;
}
console.log(
error.stack?.replace(
"Error: StackTracer",
"Stack trace:",
) ??
error,
);
}
}
}
export type File = IdBase;
export type FileInfo = {
ident: string;
absPath: string;
relPath: string;
superFile?: File;
subFiles: Map<string, File>;
text: string;
ast?: ast.File;
};
export type IdBase = { id: number };
export type Id<IdType extends IdBase> = IdType["id"];
export const id = <IdType extends IdBase>(id: IdType): Id<IdType> => id.id;
export class Ids<IdType extends IdBase> {
private next = 0;
public nextThenStep(): IdType {
const id = this.next;
this.next += 1;
return { id } as IdType;
}
}

43
compiler/diagnostics.ts Normal file
View File

@ -0,0 +1,43 @@
import { Ctx, File } from "./ctx.ts";
export type Span = {
begin: Pos;
end: Pos;
};
export type Pos = {
idx: number;
line: number;
col: number;
};
export type Diag = {
severity: "fatal" | "error" | "warning" | "info";
origin?: string;
msg: string;
file?: File;
span?: Span;
pos?: Pos;
};
export function prettyPrintDiag(ctx: Ctx, diag: Diag) {
const { severity, msg } = diag;
const origin = diag.origin ? `${diag.origin}: ` : "";
console.error(`${origin}${severity}: ${msg}`);
if (diag.file && (diag.span || diag.pos)) {
const { absPath: path, text } = ctx.fileInfo(diag.file);
const { line, col } = diag.span?.begin ?? diag.pos!;
console.error(` at ./${path}:${line}:${col}`);
if (diag.span) {
let begin = diag.span.begin.idx;
while (begin >= 0 && text[begin - 1] != "\n") {
begin -= 1;
}
let end = diag.span.end.idx;
while (end < text.length && text[end + 1] != "\n") {
end += 1;
}
} else if (diag.pos) {
}
}
}

View File

@ -1,14 +1,20 @@
type Pack = { 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; rootMod: Mod;
}; };
type Mod = null; export type Mod = null;
interface PackEmitter { export interface PackEmitter {
emit(pack: Pack): void; emit(pack: Pack): void;
} }
class PackCompiler { export class PackCompiler {
public constructor( public constructor(
private entryFilePath: string, private entryFilePath: string,
private emitter: PackEmitter, private emitter: PackEmitter,
@ -18,5 +24,53 @@ class PackCompiler {
} }
} }
class ModResolver { type _P = { file: File };
export class FileTreeCollector implements ast.Visitor<[_P]> {
private subFilePromise = Promise.resolve();
public constructor(
private ctx: Ctx,
private superFile: File | undefined,
private ident: string,
private absPath: string,
private relPath: string,
) {}
public async collect(): Promise<void> {
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;
}
visitModFileStmt(
_stmt: ast.Stmt,
kind: ast.ModFileStmt,
{ file }: _P,
): ast.VisitRes {
const { ident, filePath: relPath } = kind;
const absPath = path.join(path.dirname(this.absPath), relPath);
this.subFilePromise = this.subFilePromise
.then(() => {
if (this.ctx.fileHasChildWithIdent(file, ident)) {
}
return new FileTreeCollector(
this.ctx,
file,
ident,
absPath,
relPath,
)
.collect();
});
return "stop";
}
} }

14
compiler/parser/parser.ts Normal file
View File

@ -0,0 +1,14 @@
import { File } from "../ast/ast.ts";
import { File as CtxFile } from "../ctx.ts";
import { todo } from "../util.ts";
export class Parser {
public constructor(
private file: CtxFile,
private text: string,
) {}
public parse(): File {
return todo();
}
}

9
compiler/util.ts Normal file
View File

@ -0,0 +1,9 @@
export function todo<T>(msg?: string): T {
class NotImplemented extends Error {}
throw new NotImplemented(msg);
}
export function exhausted(_: never) {
class Unexhausted extends Error {}
throw new Unexhausted();
}