slige/compiler/resolver.ts

280 lines
8.5 KiB
TypeScript
Raw Normal View History

import { EType, Expr, Stmt } from "./ast.ts";
import {
AstVisitor,
visitEType,
visitExpr,
visitParam,
VisitRes,
visitStmt,
visitStmts,
} from "./ast_visitor.ts";
2024-12-11 11:36:19 +00:00
import { printStackTrace, Reporter } from "./info.ts";
2024-12-10 13:36:41 +00:00
import {
2024-12-29 04:39:22 +00:00
EntryModSyms,
2024-12-10 13:36:41 +00:00
FnSyms,
LeafSyms,
2024-12-29 04:39:22 +00:00
ModSyms,
2024-12-10 13:36:41 +00:00
Syms,
} from "./resolver_syms.ts";
2024-12-10 20:42:15 +00:00
import { Pos } from "./token.ts";
2024-11-26 18:37:21 +00:00
2024-12-13 22:22:08 +00:00
export class Resolver implements AstVisitor<[Syms]> {
2024-12-11 11:36:19 +00:00
public constructor(private reporter: Reporter) {
}
2024-12-11 02:11:00 +00:00
public resolve(stmts: Stmt[]): VisitRes {
2025-01-02 16:54:46 +00:00
const syms = new EntryModSyms("root");
2024-12-31 04:32:41 +00:00
this.scout(stmts, syms);
visitStmts(stmts, this, syms);
2024-12-29 04:39:22 +00:00
return "stop";
}
2024-12-31 04:32:41 +00:00
private scout(stmts: Stmt[], syms: Syms) {
for (const stmt of stmts) {
if (stmt.kind.type === "fn") {
if (syms.definedLocally(stmt.kind.ident)) {
this.reportAlreadyDefined(stmt.kind.ident, stmt.pos, syms);
return;
}
const ident = stmt.kind.ident;
2025-01-02 16:54:46 +00:00
stmt.kind.sym = syms.define(ident, {
2024-12-31 04:32:41 +00:00
ident: stmt.kind.ident,
type: "fn",
2025-01-02 16:54:46 +00:00
fullPath: `${syms.pathString()}::${ident}`,
2024-12-31 04:32:41 +00:00
pos: stmt.pos,
stmt,
});
} else if (stmt.kind.type === "type_alias") {
const ident = stmt.kind.param.ident;
if (syms.definedLocally(ident)) {
this.reportAlreadyDefined(ident, stmt.pos, syms);
return;
}
syms.define(ident, {
ident,
type: "type_alias",
2025-01-02 16:54:46 +00:00
fullPath: `${syms.pathString()}::${ident}`,
2024-12-31 04:32:41 +00:00
pos: stmt.kind.param.pos,
stmt,
param: stmt.kind.param,
});
}
}
}
2024-12-29 04:39:22 +00:00
visitModStmt(stmt: Stmt, syms: Syms): VisitRes {
if (stmt.kind.type !== "mod") {
throw new Error("expected let statement");
}
2025-01-02 16:54:46 +00:00
const modSyms = new ModSyms(syms, stmt.kind.ident);
2024-12-29 04:39:22 +00:00
const { mod, ident } = stmt.kind;
2024-12-31 04:32:41 +00:00
this.scout(mod.ast, modSyms);
visitStmts(mod.ast, this, modSyms);
2024-12-29 04:39:22 +00:00
if (syms.definedLocally(ident)) {
this.reportAlreadyDefined(ident, stmt.pos, syms);
return;
}
syms.define(ident, {
type: "mod",
ident,
2025-01-02 16:54:46 +00:00
fullPath: `${syms.pathString()}::${ident}`,
2024-12-29 04:39:22 +00:00
pos: stmt.pos,
syms: modSyms,
});
2024-12-13 22:22:08 +00:00
return "stop";
}
visitLetStmt(stmt: Stmt, syms: Syms): VisitRes {
if (stmt.kind.type !== "let") {
throw new Error("expected let statement");
}
visitExpr(stmt.kind.value, this, syms);
const ident = stmt.kind.param.ident;
if (syms.definedLocally(ident)) {
this.reportAlreadyDefined(ident, stmt.pos, syms);
return;
2024-11-26 18:37:21 +00:00
}
2025-01-02 16:54:46 +00:00
stmt.kind.param.sym = syms.define(ident, {
2024-12-13 22:22:08 +00:00
ident,
type: "let",
2025-01-02 16:54:46 +00:00
fullPath: ident,
2024-12-13 22:22:08 +00:00
pos: stmt.kind.param.pos,
stmt,
param: stmt.kind.param,
});
return "stop";
2024-11-26 18:37:21 +00:00
}
2025-01-17 06:44:53 +00:00
visitTypeAliasStmt(stmt: Stmt, _syms: Syms): VisitRes {
2024-12-31 04:32:41 +00:00
if (stmt.kind.type !== "type_alias") {
throw new Error("expected type_alias statement");
2024-12-10 13:36:41 +00:00
}
2024-12-31 04:32:41 +00:00
// nothing to do here
2024-12-10 13:36:41 +00:00
}
2024-12-13 22:22:08 +00:00
visitFnStmt(stmt: Stmt, syms: Syms): VisitRes {
if (stmt.kind.type !== "fn") {
throw new Error("expected fn statement");
2024-12-06 11:21:57 +00:00
}
2024-12-13 22:22:08 +00:00
const fnScopeSyms = new FnSyms(syms);
for (const param of stmt.kind.genericParams ?? []) {
2024-12-22 03:23:17 +00:00
if (fnScopeSyms.definedLocally(param.ident)) {
this.reportAlreadyDefined(param.ident, param.pos, syms);
continue;
}
fnScopeSyms.define(param.ident, {
ident: param.ident,
type: "generic",
2025-01-02 16:54:46 +00:00
fullPath: param.ident,
2024-12-22 03:23:17 +00:00
pos: param.pos,
2024-12-26 01:38:32 +00:00
stmt,
genericParam: param,
2024-12-22 03:23:17 +00:00
});
}
2024-12-13 22:22:08 +00:00
for (const param of stmt.kind.params) {
if (fnScopeSyms.definedLocally(param.ident)) {
this.reportAlreadyDefined(param.ident, param.pos, syms);
continue;
2024-12-06 11:21:57 +00:00
}
visitParam(param, this, fnScopeSyms);
2024-12-13 22:22:08 +00:00
fnScopeSyms.define(param.ident, {
ident: param.ident,
type: "fn_param",
2025-01-02 16:54:46 +00:00
fullPath: param.ident,
2024-12-13 22:22:08 +00:00
pos: param.pos,
param,
});
2024-12-06 11:21:57 +00:00
}
if (stmt.kind.returnType) {
visitEType(stmt.kind.returnType, this, fnScopeSyms);
}
2024-12-13 22:22:08 +00:00
visitExpr(stmt.kind.body, this, fnScopeSyms);
return "stop";
2024-11-26 18:37:21 +00:00
}
2024-12-13 22:22:08 +00:00
visitIdentExpr(expr: Expr, syms: Syms): VisitRes {
2024-12-10 09:39:12 +00:00
if (expr.kind.type !== "ident") {
2024-11-27 14:10:48 +00:00
throw new Error("expected ident");
2024-12-10 09:39:12 +00:00
}
const ident = expr.kind.ident;
const symResult = syms.get(ident);
2024-11-27 14:10:48 +00:00
if (!symResult.ok) {
this.reportUseOfUndefined(ident, expr.pos, syms);
2024-11-27 14:10:48 +00:00
return;
}
2024-12-10 09:39:12 +00:00
const sym = symResult.sym;
expr.kind = { type: "sym", ident, sym };
2024-12-13 22:22:08 +00:00
return "stop";
2024-11-26 18:37:21 +00:00
}
2024-12-29 04:39:22 +00:00
visitPathExpr(expr: Expr, syms: Syms): VisitRes {
if (expr.kind.type !== "path") {
throw new Error("expected ident");
}
visitExpr(expr.kind.subject, this, syms);
if (expr.kind.subject.kind.type !== "sym") {
throw new Error("this error is not handled properly");
}
const subjectSym = expr.kind.subject.kind.sym;
if (subjectSym.type !== "mod") {
this.reporter.reportError({
reporter: "Resolver",
msg: `path expression are not implemented for '${subjectSym.type}' symbols`,
pos: expr.pos,
});
printStackTrace();
return "stop";
}
const getRes = subjectSym.syms.get(expr.kind.ident);
if (!getRes.ok) {
this.reportUseOfUndefined(
expr.kind.ident,
expr.pos,
subjectSym.syms,
);
return "stop";
}
expr.kind = {
type: "sym",
ident: expr.kind.ident,
sym: getRes.sym,
};
return "stop";
}
2024-12-13 22:22:08 +00:00
visitBlockExpr(expr: Expr, syms: Syms): VisitRes {
if (expr.kind.type !== "block") {
throw new Error();
2024-12-10 09:39:12 +00:00
}
2024-12-13 22:22:08 +00:00
const childSyms = new LeafSyms(syms);
2024-12-31 04:32:41 +00:00
this.scout(expr.kind.stmts, childSyms);
2024-12-13 22:22:08 +00:00
visitStmts(expr.kind.stmts, this, childSyms);
if (expr.kind.expr) {
visitExpr(expr.kind.expr, this, childSyms);
2024-11-27 14:10:48 +00:00
}
2024-12-13 22:22:08 +00:00
return "stop";
2024-11-26 18:37:21 +00:00
}
visitForExpr(expr: Expr, syms: Syms): VisitRes {
if (expr.kind.type !== "for") {
throw new Error();
}
const childSyms = new LeafSyms(syms);
if (expr.kind.decl) visitStmt(expr.kind.decl, this, syms);
if (expr.kind.cond) visitExpr(expr.kind.cond, this, syms);
if (expr.kind.incr) visitStmt(expr.kind.incr, this, syms);
visitExpr(expr.kind.body, this, childSyms);
return "stop";
}
visitIdentEType(etype: EType, syms: Syms): VisitRes {
if (etype.kind.type !== "ident") {
throw new Error();
}
const ident = etype.kind.ident;
const symResult = syms.get(ident);
if (!symResult.ok) {
this.reportUseOfUndefined(ident, etype.pos, syms);
return;
}
const sym = symResult.sym;
etype.kind = { type: "sym", ident, sym };
return "stop";
}
2024-12-10 13:36:41 +00:00
private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) {
2024-12-11 02:11:00 +00:00
this.reporter.reportError({
reporter: "Resolver",
msg: `use of undefined symbol '${ident}'`,
pos,
});
2024-12-11 11:36:19 +00:00
printStackTrace();
2024-11-26 18:37:21 +00:00
}
2024-12-10 09:39:12 +00:00
private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) {
2024-12-11 02:11:00 +00:00
this.reporter.reportError({
reporter: "Resolver",
msg: `symbol already defined '${ident}'`,
pos,
});
2024-11-26 18:37:21 +00:00
const prev = syms.get(ident);
2024-12-10 09:39:12 +00:00
if (!prev.ok) {
2024-11-26 18:37:21 +00:00
throw new Error("expected to be defined");
2024-12-10 09:39:12 +00:00
}
if (!prev.sym.pos) {
2024-11-26 18:37:21 +00:00
return;
2024-12-10 09:39:12 +00:00
}
2024-12-11 02:11:00 +00:00
this.reporter.addNote({
reporter: "Resolver",
msg: `previous definition of '${ident}'`,
pos: prev.sym.pos,
});
2024-12-11 11:36:19 +00:00
printStackTrace();
2024-11-26 18:37:21 +00:00
}
2024-12-10 09:39:12 +00:00
}