mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-30 22:10:52 +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;
|
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
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