mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-30 15:10:51 +00:00
add stuff
This commit is contained in:
parent
e8cfd059cc
commit
93dd4c32c8
23
compiler/ast/ast.ts
Normal file
23
compiler/ast/ast.ts
Normal 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
2
compiler/ast/mod.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./ast.ts";
|
||||
export * from "./visitor.ts";
|
62
compiler/ast/visitor.ts
Normal file
62
compiler/ast/visitor.ts
Normal 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
107
compiler/ctx.ts
Normal 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
43
compiler/diagnostics.ts
Normal 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) {
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
type Mod = null;
|
||||
export type Mod = null;
|
||||
|
||||
interface PackEmitter {
|
||||
export interface PackEmitter {
|
||||
emit(pack: Pack): void;
|
||||
}
|
||||
|
||||
class PackCompiler {
|
||||
export class PackCompiler {
|
||||
public constructor(
|
||||
private entryFilePath: string,
|
||||
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
14
compiler/parser/parser.ts
Normal 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
9
compiler/util.ts
Normal 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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user