add ast stuff

This commit is contained in:
SimonFJ20 2025-01-23 14:19:27 +01:00
parent 6a5fce18fe
commit ab5a830f66
3 changed files with 345 additions and 25 deletions

View File

@ -5,22 +5,149 @@ export type File = {
};
export type Stmt = {
id: number;
kind: StmtKind;
span: Span;
};
export type StmtKind =
| { tag: "error" }
| { tag: "mod_block" } & ModBlockStmt
| { tag: "mod_file" } & ModFileStmt;
| { tag: "item" } & ItemStmt
| { tag: "let" } & LetStmt
| { tag: "return" } & ReturnStmt
| { tag: "break" } & BreakStmt
| { tag: "assign" } & AssignStmt
| { tag: "expr" } & ExprStmt;
export type ModBlockStmt = {
ident: string;
export type ItemStmt = { item: Item };
export type LetStmt = {
subject: Pat;
expr?: Expr;
};
export type ReturnStmt = { expr?: Expr };
export type BreakStmt = { expr?: Expr };
export type AssignStmt = {
assignType: AssignType;
subject: Expr;
value: Expr;
};
export type AssignType = "=" | "+=" | "-=";
export type ExprStmt = { expr: Expr };
export type Item = {
kind: ItemKind;
span: Span;
ident: Ident;
pub: boolean;
};
export type ItemKind =
| { tag: "error" }
| { tag: "mod_block" } & ModBlockItem
| { tag: "mod_file" } & ModFileItem
| { tag: "enum" } & EnumItem
| { tag: "struct" } & StructItem
| { tag: "fn" } & FnItem
| { tag: "use" } & UseItem
| { tag: "static" } & StaticItem
| { tag: "type_alias" } & TypeAliasItem;
export type ModBlockItem = {
ident: Ident;
stmts: Stmt[];
};
export type ModFileStmt = {
ident: string;
export type ModFileItem = {
ident: Ident;
filePath: string;
};
export type EnumItem = { variants: Variant[] };
export type StructItem = { data: VariantData };
export type FnItem = { _: 0 };
export type UseItem = { _: 0 };
export type StaticItem = { _: 0 };
export type TypeAliasItem = { _: 0 };
export type Variant = {
ident: Ident;
data: VariantData;
pub: boolean;
span: Span;
};
export type VariantData = {
kind: VariantDataKind;
span: Span;
};
export type VariantDataKind =
| { tag: "error" }
| { tag: "unit" }
| { tag: "tuple" } & TupleVariantData
| { tag: "struct" } & StructVariantData;
export type TupleVariantData = { elems: VariantData[] };
export type StructVariantData = { fields: FieldDef[] };
export type FieldDef = {
ident: Ident;
ty: Ty;
pub: boolean;
span: Span;
};
export type Expr = {
kind: ExprKind;
span: Span;
};
export type ExprKind =
| { tag: "error" }
| { tag: "ident" } & Ident;
export type Pat = {
kind: PatKind;
span: Span;
};
export type PatKind =
| { tag: "error" }
| { tag: "bind" } & BindPat;
export type BindPat = {
ident: Ident;
mut: boolean;
};
export type Ty = {
kind: TyKind;
span: Span;
};
export type TyKind =
| { tag: "error" }
| { tag: "ident" } & Ident
| { tag: "ref" } & RefTy
| { tag: "ptr" } & PtrTy
| { tag: "slice" } & SliceTy
| { tag: "array" } & ArrayTy;
export type RefTy = { ty: Ty; mut: boolean };
export type PtrTy = { ty: Ty; mut: boolean };
export type SliceTy = { ty: Ty };
export type ArrayTy = { ty: Ty; length: Expr };
export type TupleTy = { elems: Ty[] };
export type StructTy = { fields: FieldDef[] };
export type AnonFieldDef = {
ident: Ident;
ty: Ty;
span: Span;
};
export type Ident = { text: string; span: Span };

View File

@ -1,5 +1,32 @@
import { exhausted } from "../util.ts";
import { File, ModBlockStmt, ModFileStmt, Stmt } from "./ast.ts";
import {
ArrayTy,
AssignStmt,
BindPat,
BreakStmt,
EnumItem,
Expr,
ExprStmt,
File,
FnItem,
Ident,
Item,
ItemStmt,
LetStmt,
ModBlockItem,
ModFileItem,
Pat,
PtrTy,
RefTy,
ReturnStmt,
SliceTy,
StaticItem,
Stmt,
StructItem,
Ty,
TypeAliasItem,
UseItem,
} from "./ast.ts";
export type VisitRes = "stop" | void;
@ -12,8 +39,36 @@ export interface Visitor<
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;
visitItemStmt?(stmt: Stmt, kind: ItemStmt, ...p: P): R;
visitLetStmt?(stmt: Stmt, kind: LetStmt, ...p: P): R;
visitReturnStmt?(stmt: Stmt, kind: ReturnStmt, ...p: P): R;
visitBreakStmt?(stmt: Stmt, kind: BreakStmt, ...p: P): R;
visitAssignStmt?(stmt: Stmt, kind: AssignStmt, ...p: P): R;
visitExprStmt?(stmt: Stmt, kind: ExprStmt, ...p: P): R;
visitItem?(item: Item, ...p: P): R;
visitErrorItem?(item: Item, ...p: P): R;
visitModBlockItem?(item: Item, kind: ModBlockItem, ...p: P): R;
visitModFileItem?(item: Item, kind: ModFileItem, ...p: P): R;
visitEnumItem?(item: Item, kind: EnumItem, ...p: P): R;
visitStructItem?(item: Item, kind: StructItem, ...p: P): R;
visitFnItem?(item: Item, kind: FnItem, ...p: P): R;
visitUseItem?(item: Item, kind: UseItem, ...p: P): R;
visitStaticItem?(item: Item, kind: StaticItem, ...p: P): R;
visitTypeAliasItem?(item: Item, kind: TypeAliasItem, ...p: P): R;
visitExpr?(expr: Expr, ...p: P): R;
visitErrorExpr?(expr: Expr, ...p: P): R;
visitIdentExpr?(expr: Expr, kind: Ident, ...p: P): R;
visitPat?(pat: Pat, ...p: P): R;
visitErrorPat?(pat: Pat, ...p: P): R;
visitBindPat?(pat: Pat, kind: BindPat, ...p: P): R;
visitTy?(ty: Ty, ...p: P): R;
visitErrorTy?(ty: Ty, ...p: P): R;
visitIdentTy?(ty: Ty, kind: Ident, ...p: P): R;
visitRefTy?(ty: Ty, kind: RefTy, ...p: P): R;
visitPtrTy?(ty: Ty, kind: PtrTy, ...p: P): R;
visitSliceTy?(ty: Ty, kind: SliceTy, ...p: P): R;
visitArrayTy?(ty: Ty, kind: ArrayTy, ...p: P): R;
visitIdent?(ident: Ident, ...p: P): R;
}
export function visitFile<
@ -46,17 +101,136 @@ export function visitStmt<
stmt: Stmt,
...p: P
) {
switch (stmt.kind.tag) {
const kind = stmt.kind;
switch (kind.tag) {
case "error":
if (v.visitErrorStmt?.(stmt, ...p) === "stop") return;
return;
case "item":
if (v.visitItemStmt?.(stmt, kind, ...p) === "stop") return;
return;
case "let":
if (v.visitLetStmt?.(stmt, kind, ...p) === "stop") return;
return;
case "return":
if (v.visitReturnStmt?.(stmt, kind, ...p) === "stop") return;
return;
case "break":
if (v.visitBreakStmt?.(stmt, kind, ...p) === "stop") return;
return;
case "assign":
if (v.visitAssignStmt?.(stmt, kind, ...p) === "stop") return;
return;
case "expr":
if (v.visitExprStmt?.(stmt, kind, ...p) === "stop") return;
return;
}
exhausted(kind);
}
export function visitItem<
P extends PM = [],
>(
v: Visitor<P>,
item: Item,
...p: P
) {
const kind = item.kind;
switch (kind.tag) {
case "error":
if (v.visitErrorItem?.(item, ...p) === "stop") return;
return;
case "mod_block":
if (v.visitModBlockStmt?.(stmt, stmt.kind, ...p) === "stop") return;
visitStmts(v, stmt.kind.stmts, ...p);
if (v.visitModBlockItem?.(item, kind, ...p) === "stop") return;
return;
case "mod_file":
if (v.visitModFileStmt?.(stmt, stmt.kind, ...p) === "stop") return;
if (v.visitModFileItem?.(item, kind, ...p) === "stop") return;
return;
case "enum":
if (v.visitEnumItem?.(item, kind, ...p) === "stop") return;
return;
case "struct":
if (v.visitStructItem?.(item, kind, ...p) === "stop") return;
return;
case "fn":
if (v.visitFnItem?.(item, kind, ...p) === "stop") return;
return;
case "use":
if (v.visitUseItem?.(item, kind, ...p) === "stop") return;
return;
case "static":
if (v.visitStaticItem?.(item, kind, ...p) === "stop") return;
return;
case "type_alias":
if (v.visitTypeAliasItem?.(item, kind, ...p) === "stop") return;
return;
}
exhausted(stmt.kind);
exhausted(kind);
}
export function visitExpr<
P extends PM = [],
>(
v: Visitor<P>,
expr: Expr,
...p: P
) {
const kind = expr.kind;
switch (kind.tag) {
case "error":
if (v.visitErrorExpr?.(expr, ...p) === "stop") return;
return;
case "ident":
if (v.visitIdentExpr?.(expr, kind, ...p) === "stop") return;
return;
}
exhausted(kind);
}
export function visitPat<
P extends PM = [],
>(
v: Visitor<P>,
pat: Pat,
...p: P
) {
const kind = pat.kind;
switch (kind.tag) {
case "error":
if (v.visitErrorPat?.(pat, ...p) === "stop") return;
return;
case "bind":
if (v.visitBindPat?.(pat, kind, ...p) === "stop") return;
return;
}
exhausted(kind);
}
export function visitTy<
P extends PM = [],
>(
v: Visitor<P>,
ty: Ty,
...p: P
) {
const kind = ty.kind;
switch (kind.tag) {
case "error":
if (v.visitErrorTy?.(ty, ...p) === "stop") return;
return;
case "ident":
if (v.visitIdentTy?.(ty, kind, ...p) === "stop") return;
return;
}
exhausted(kind);
}
export function visitIdent<
P extends PM = [],
>(
v: Visitor<P>,
ident: Ident,
...p: P
) {
v.visitIdent?.(ident, ...p);
}

View File

@ -15,20 +15,25 @@ export interface PackEmitter {
}
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 FileTreeCollector implements ast.Visitor<[_P]> {
export class FileTreeAstCollector implements ast.Visitor<[_P]> {
private subFilePromise = Promise.resolve();
public constructor(
private constructor(
private ctx: Ctx,
private superFile: File | undefined,
private ident: string,
@ -36,6 +41,19 @@ export class FileTreeCollector implements ast.Visitor<[_P]> {
private relPath: string,
) {}
public static fromEntryFile(
ctx: Ctx,
entryFilePath: string,
): FileTreeAstCollector {
return new FileTreeAstCollector(
ctx,
undefined,
"root",
entryFilePath,
entryFilePath,
);
}
public async collect(): Promise<void> {
const text = await Deno.readTextFile(this.absPath);
const file = this.ctx.addFile(
@ -51,12 +69,12 @@ export class FileTreeCollector implements ast.Visitor<[_P]> {
await this.subFilePromise;
}
visitModFileStmt(
stmt: ast.Stmt,
kind: ast.ModFileStmt,
visitModFileItem(
item: ast.Item,
kind: ast.ModFileItem,
{ file }: _P,
): ast.VisitRes {
const { ident, filePath: relPath } = kind;
const { ident: { text: ident }, filePath: relPath } = kind;
const absPath = path.join(path.dirname(this.absPath), relPath);
this.subFilePromise = this.subFilePromise
.then(() => {
@ -65,10 +83,11 @@ export class FileTreeCollector implements ast.Visitor<[_P]> {
severity: "fatal",
msg: `module '${ident}' already declared`,
file,
span: stmt.span,
span: item.span,
});
Deno.exit(1);
}
return new FileTreeCollector(
return new FileTreeAstCollector(
this.ctx,
file,
ident,