slige-mirror/compiler/middle/lower_ast.ts
2025-01-29 23:18:30 +01:00

416 lines
14 KiB
TypeScript

import { Ctx, DefId, IdentId } from "../ctx.ts";
import * as ast from "../ast/ast.ts";
import {
Block,
Expr,
Ident,
Item,
Pat,
Path,
PathSegment,
QPath,
Stmt,
Ty,
} from "./hir.ts";
import { exhausted, Res as Result, todo } from "../util.ts";
import { Rib } from "./rib.ts";
import { Res } from "./res.ts";
export class AstLowerer {
private ribs: Rib[] = [];
public constructor(
private ctx: Ctx,
) {}
public lower() {
const file = this.ctx.entryFile();
const ast = this.ctx.fileInfo(file).ast!;
this.lowerFile(ast);
}
private lowerFile(file: ast.File) {
this.lowerStmts(file.stmts);
}
private lowerStmts(stmts: ast.Stmt[]): Stmt[] {
return stmts.map((stmt) => this.lowerStmt(stmt));
}
private lowerStmt(stmt: ast.Stmt): Stmt {
const span = stmt.span;
const kind = stmt.kind;
switch (kind.tag) {
case "error":
return this.ctx.internStmt(kind, span);
case "item":
return this.ctx.internStmt({
tag: "item",
item: this.lowerItem(kind.item),
}, span);
case "let":
return this.lowerLetStmt(stmt, kind);
case "return":
return this.ctx.internStmt({
tag: "return",
expr: kind.expr && this.lowerExpr(kind.expr),
}, span);
case "break":
return this.ctx.internStmt({
tag: "break",
expr: kind.expr && this.lowerExpr(kind.expr),
}, span);
case "continue":
return this.ctx.internStmt({
tag: "continue",
}, span);
case "assign":
return this.ctx.internStmt({
tag: "assign",
assignType: kind.assignType,
subject: this.lowerExpr(kind.subject),
value: this.lowerExpr(kind.value),
}, span);
case "expr":
return this.ctx.internStmt({
tag: "expr",
expr: this.lowerExpr(kind.expr),
}, span);
}
exhausted(kind);
}
private lowerLetStmt(stmt: ast.Stmt, kind: ast.LetStmt): Stmt {
const expr = kind.expr && this.lowerExpr(kind.expr);
const ty = kind.ty && this.lowerTy(kind.ty);
this.pushRib({ kind: { tag: "normal" }, bindings: new Map() });
const pat = this.lowerPat(kind.pat);
return this.ctx.internStmt({ tag: "let", pat, ty, expr }, stmt.span);
}
private lowerItem(item: ast.Item): Item {
return todo();
}
private lowerExpr(expr: ast.Expr): Expr {
const span = expr.span;
const kind = expr.kind;
switch (kind.tag) {
case "error":
return this.ctx.internExpr(kind, span);
case "path":
return this.ctx.internExpr({
tag: "path",
path: this.lowerPath(kind.path, kind.qty),
}, span);
case "null":
return this.ctx.internExpr(kind, span);
case "int":
return this.ctx.internExpr(kind, span);
case "bool":
return this.ctx.internExpr(kind, span);
case "str":
return this.ctx.internExpr(kind, span);
case "group":
return this.ctx.internExpr({
tag: "group",
expr: this.lowerExpr(kind.expr),
}, span);
case "array":
return this.ctx.internExpr({
tag: "array",
exprs: kind.exprs.map((expr) => this.lowerExpr(expr)),
}, span);
case "repeat":
return this.ctx.internExpr({
tag: "repeat",
expr: this.lowerExpr(kind.expr),
length: this.lowerExpr(kind.length),
}, span);
case "struct":
return this.ctx.internExpr({
tag: "struct",
path: kind.path && this.lowerPath(kind.path),
fields: kind.fields.map(({ ident, expr, span }) => ({
ident,
expr: this.lowerExpr(expr),
span,
})),
}, span);
case "ref":
return this.ctx.internExpr({
tag: "ref",
expr: this.lowerExpr(kind.expr),
refType: kind.refType,
mut: kind.mut,
}, span);
case "deref":
return this.ctx.internExpr({
tag: "deref",
expr: this.lowerExpr(kind.expr),
}, span);
case "elem":
return this.ctx.internExpr({
tag: "elem",
expr: this.lowerExpr(kind.expr),
elem: kind.elem,
}, span);
case "field":
return this.ctx.internExpr({
tag: "field",
expr: this.lowerExpr(kind.expr),
ident: kind.ident,
}, span);
case "index":
return this.ctx.internExpr({
tag: "index",
expr: this.lowerExpr(kind.expr),
index: this.lowerExpr(kind.index),
}, span);
case "call":
return this.ctx.internExpr({
tag: "call",
expr: this.lowerExpr(kind.expr),
args: kind.args.map((arg) => this.lowerExpr(arg)),
}, span);
case "unary":
return this.ctx.internExpr({
tag: "unary",
expr: this.lowerExpr(kind.expr),
unaryType: kind.unaryType,
}, span);
case "binary":
return this.ctx.internExpr({
tag: "binary",
left: this.lowerExpr(kind.left),
right: this.lowerExpr(kind.right),
binaryType: kind.binaryType,
}, span);
case "block":
return this.ctx.internExpr({
tag: "block",
block: this.lowerBlock(kind.block),
}, span);
case "if":
return this.ctx.internExpr({
tag: "if",
cond: this.lowerExpr(kind.cond),
truthy: this.lowerBlock(kind.truthy),
falsy: kind.falsy && this.lowerExpr(kind.falsy),
}, span);
case "loop":
return this.ctx.internExpr({
tag: "loop",
body: this.lowerBlock(kind.body),
}, span);
case "while":
throw new Error("not implemented");
case "for":
throw new Error("not implemented");
case "c_for":
throw new Error("not implemented");
}
exhausted(kind);
}
private lowerPat(pat: ast.Pat): Pat {
const span = pat.span;
const kind = pat.kind;
switch (kind.tag) {
case "error":
return this.ctx.internPat(kind, span);
case "bind": {
const v = this.ctx.internPat(kind, span);
this.rib().bindings.set(kind.ident.id, {
tag: "local",
id: v.id,
});
return v;
}
case "path":
return this.ctx.internPat({
tag: "path",
path: this.lowerPath(kind.path, kind.qty),
}, span);
}
exhausted(kind);
}
private lowerTy(ty: ast.Ty): Ty {
const span = ty.span;
const kind = ty.kind;
switch (kind.tag) {
case "error":
return this.ctx.internTy(kind, span);
case "null":
return this.ctx.internTy(kind, span);
case "int":
return this.ctx.internTy(kind, span);
case "bool":
return this.ctx.internTy(kind, span);
case "str":
return this.ctx.internTy(kind, span);
case "path":
return this.ctx.internTy({
tag: "path",
path: this.lowerPath(kind.path, kind.qty),
}, span);
case "ref":
return this.ctx.internTy({
tag: "ref",
ty: this.lowerTy(kind.ty),
mut: kind.mut,
}, span);
case "ptr":
return this.ctx.internTy({
tag: "ptr",
ty: this.lowerTy(kind.ty),
mut: kind.mut,
}, span);
case "slice":
return this.ctx.internTy({
tag: "slice",
ty: this.lowerTy(kind.ty),
}, span);
case "array":
return this.ctx.internTy({
tag: "array",
ty: this.lowerTy(kind.ty),
length: this.lowerExpr(kind.length),
}, span);
case "anon_struct":
return this.ctx.internTy({
tag: "anon_struct",
fields: kind.fields.map(({ ident, ty, span }) => ({
ident,
ty: this.lowerTy(ty),
span,
})),
}, span);
}
exhausted(kind);
}
private lowerBlock(block: ast.Block): Block {
const point = this.ribPoint();
this.pushRib({
kind: { tag: "mod", mod: { kind: { tag: "block" } } },
bindings: new Map(),
});
const stmts = block.stmts.map((stmt) => this.lowerStmt(stmt));
const expr = block.expr && this.lowerExpr(block.expr);
this.returnToRibPoint(point);
return this.ctx.internBlock({ stmts, expr, span: block.span });
}
private lowerPath(path: ast.Path, qty?: ast.Ty): QPath {
if (qty) {
const ty = this.lowerTy(qty);
if (path.segments.length !== 1) {
throw new Error();
}
const seg = path.segments[0];
return {
tag: "type_relative",
ty,
seg: {
ident: seg.ident,
res: todo(),
genericArgs: seg.genericArgs &&
seg.genericArgs.map((ty) => this.lowerTy(ty)),
inferArgs: false,
span: path.span,
},
};
}
const [res, segments] = this.resolvePathSegs(path.segments);
return {
tag: "resolved",
path: { segments, res, span: path.span },
};
}
private resolvePathSegs(segs: ast.PathSegment[]): [Res, PathSegment[]] {
if (segs.length <= 1) {
const seg = segs[0];
const res = this.resolveTyIdent(seg.ident);
return [res, [{
ident: seg.ident,
res,
genericArgs: seg.genericArgs &&
seg.genericArgs.map((ty) => this.lowerTy(ty)),
inferArgs: false,
span: seg.span,
}]];
}
const seg = segs.at(-1)!;
const [innerRes, resSegs] = this.resolvePathSegs(
segs.slice(0, segs.length - 1),
);
switch (innerRes.tag) {
case "error":
return [innerRes, [...resSegs, {
ident: seg.ident,
res: innerRes,
inferArgs: false,
span: seg.span,
}]];
case "def": {
const irk = innerRes.kind;
switch (irk.tag) {
case "mod": {
const mod = this.ctx.modDef(innerRes.id);
const def = this.ctx.modItem();
return todo();
}
case "struct":
return todo();
case "enum":
return todo();
case "variant":
return todo();
case "fn":
return todo();
case "ctor":
return todo();
case "use":
return todo();
case "field":
return todo();
}
exhausted(irk);
throw new Error();
}
case "local":
throw new Error("should not be possible");
}
exhausted(innerRes);
}
private resolveTyIdent(ident: Ident): Res {
}
private resolveValIdent(ident: Ident): Res {
}
private rib(): Rib {
return this.ribs.at(-1)!;
}
private pushRib(rib: Rib) {
this.ribs.push(rib);
}
private popRib() {
this.ribs.pop();
}
private ribPoint(): number {
return this.ribs.length;
}
private returnToRibPoint(point: number) {
this.ribs = this.ribs.slice(0, point);
}
}