slige/compiler/ast/visitor.ts
2025-01-22 22:40:29 +01:00

63 lines
1.4 KiB
TypeScript

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);
}