mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 18:16:31 +00:00
134 lines
3.7 KiB
TypeScript
134 lines
3.7 KiB
TypeScript
|
import { Expr, Stmt, Sym } from "./ast.ts";
|
||
|
import { Pos } from "./Token.ts";
|
||
|
|
||
|
|
||
|
type Ident = string
|
||
|
|
||
|
|
||
|
type SymMap = { [ident: string]: Sym }
|
||
|
|
||
|
class Syms {
|
||
|
private syms: SymMap = {};
|
||
|
|
||
|
public constructor(private parent?: Syms) {}
|
||
|
|
||
|
public define(ident: string, sym: Sym) {
|
||
|
this.syms[ident] = sym;
|
||
|
}
|
||
|
|
||
|
public definedLocally(ident: string): boolean {
|
||
|
return ident in this.syms;
|
||
|
}
|
||
|
|
||
|
public get(ident: string): { ok: true, sym: Sym } | { ok: false } {
|
||
|
if (ident in this.syms)
|
||
|
return { ok: true, sym: this.syms[ident] };
|
||
|
if (this.parent)
|
||
|
return this.parent.get(ident);
|
||
|
return { ok: false };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Chapter 7 is generally fucked, remember to give feedback to Simon
|
||
|
class Resolver {
|
||
|
private root = new Syms();
|
||
|
|
||
|
private resolveIdentExpr(expr: Expr, syms: Syms) {
|
||
|
if (expr.kind.type !== "ident")
|
||
|
throw new Error("expected ident");
|
||
|
const ident = expr.kind;
|
||
|
const symResult = syms.get(ident.value);
|
||
|
if (!symResult.ok) {
|
||
|
this.reportUseOfUndefined(ident.value, expr.pos, syms);
|
||
|
return;
|
||
|
}
|
||
|
const sym = symResult.sym
|
||
|
expr.kind = {
|
||
|
type: "sym",
|
||
|
ident: ident.value,
|
||
|
defType: sym.type,
|
||
|
};
|
||
|
if (sym.stmt)
|
||
|
expr.kind.stmt = sym.stmt;
|
||
|
if (sym.param)
|
||
|
expr.kind.param = sym.param;
|
||
|
}
|
||
|
|
||
|
private resolveExpr(expr: Expr, syms: Syms) {
|
||
|
if (expr.kind.type === "error") {
|
||
|
return;
|
||
|
}
|
||
|
if (expr.kind.type === "ident") {
|
||
|
this.resolveIdentexpr(expr, syms);
|
||
|
return;
|
||
|
}
|
||
|
// ...
|
||
|
if (expr.kind.type === "binary") {
|
||
|
this.resolveExpr(expr.kind.left, syms);
|
||
|
this.resolveExpr(expr.kind.right, syms);
|
||
|
return;
|
||
|
}
|
||
|
// ...
|
||
|
if (expr.kind.type === "block") {
|
||
|
const childSyms = new Syms(syms);
|
||
|
for (const stmt of expr.kind.stmts) {
|
||
|
this.resolveStmt(stmt, childSyms);
|
||
|
}
|
||
|
if (expr.kind.expr) {
|
||
|
this.resolveExpr(expr.kind.expr, childSyms);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
// ...
|
||
|
throw new Error(`unknown expression ${expr.kind.type}`);
|
||
|
}
|
||
|
|
||
|
private resolveIdentexpr(expr: Expr, syms: Syms) {
|
||
|
|
||
|
}
|
||
|
|
||
|
private resolveStmt(stmt: Stmt, syms: Syms) {
|
||
|
if (stmt.kind.type === "error") {
|
||
|
return;
|
||
|
}
|
||
|
if (stmt.kind.type === "let") {
|
||
|
this.resolveLetStmt(stmt, syms);
|
||
|
return;
|
||
|
}
|
||
|
if (stmt.kind.type === "fn") {
|
||
|
this.resolveFnStmt(stmt, syms);
|
||
|
return;
|
||
|
}
|
||
|
if (stmt.kind.type === "return") {
|
||
|
if (stmt.kind.expr)
|
||
|
this.resolveExpr(stmt.kind.expr, syms);
|
||
|
return;
|
||
|
}
|
||
|
// ...
|
||
|
throw new Error(`unknown statement ${stmt.kind.type}`);
|
||
|
}
|
||
|
private resolveLetStmt(stmt: Stmt, syms: Syms) {
|
||
|
|
||
|
}
|
||
|
|
||
|
private resolveFnStmt(stmt: Stmt, syms: Syms) {
|
||
|
|
||
|
}
|
||
|
|
||
|
private reportUseOfUndefined(ident: Ident, pos: Pos, syms: Syms) {
|
||
|
console.error(`use of undefined symbol '${ident}' at ${pos.line}${pos.col}`);
|
||
|
}
|
||
|
|
||
|
private reportAlreadyDefined(ident: Ident, pos: Pos, syms: Syms) {
|
||
|
console.error(`symbol already defined '${ident}', at ${pos.line}${pos.col}`);
|
||
|
const prev = syms.get(ident);
|
||
|
if (!prev.ok)
|
||
|
throw new Error("expected to be defined");
|
||
|
if (!prev.sym.pos)
|
||
|
return;
|
||
|
const { line: prevLine, col: prevCol } = prev.sym.pos;
|
||
|
console.error(`previous definition of '${ident}' at ${prevLine}:${prevCol}`);
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|