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