compiler: finish example ast lower

This commit is contained in:
SimonFJ20 2025-02-06 14:52:45 +01:00
parent e8c666412c
commit f4886e045c
14 changed files with 644 additions and 88 deletions

View File

@ -48,7 +48,13 @@ export class Checker {
const exprTy = kind.expr && Ok(this.exprTy(kind.expr));
const tyTy = kind.ty && Ok(this.tyTy(kind.ty));
const ty = exprTy && tyTy && this.resolveTys(exprTy.val, tyTy.val);
const ty = exprTy !== undefined
? tyTy !== undefined
? this.resolveTys(exprTy.val, tyTy.val)
: exprTy
: exprTy;
this.stmtChecked.add(stmt.id);
if (ty === undefined) {
this.report("type amfibious, specify type or value", stmt.span);
@ -65,8 +71,6 @@ export class Checker {
this.report(res.val, stmt.span);
return Ty({ tag: "error" });
}
this.stmtChecked.add(stmt.id);
}
private assignPatTy(pat: ast.Pat, ty: Ty): Res<void, string> {
@ -108,9 +112,9 @@ export class Checker {
case "path":
return this.checkPathExpr(expr, k, expected);
case "null":
return todo();
return Ty({ tag: "null" });
case "int":
return todo();
return Ty({ tag: "int" });
case "bool":
return todo();
case "str":
@ -134,7 +138,7 @@ export class Checker {
case "index":
return todo();
case "call":
return todo();
return this.checkCallExpr(expr, k, expected);
case "unary":
return todo();
case "binary":
@ -188,6 +192,35 @@ export class Checker {
exhausted(res.kind);
}
private checkCallExpr(
expr: ast.Expr,
kind: ast.CallExpr,
expected: Ty,
): Ty {
const fnTy = this.exprTy(kind.expr);
if (fnTy.kind.tag !== "fn") {
if (fnTy.kind.tag === "error") {
return fnTy;
}
const ty = tyToString(this.ctx, fnTy);
console.log(kind.expr.span);
this.report(`type '${ty}' not fucking callable`, kind.expr.span);
return Ty({ tag: "error" });
}
const paramTys = fnTy.kind.params;
if (paramTys.length !== kind.args.length) {
this.report(
"not enough/too many fucking arguments",
kind.expr.span,
);
return Ty({ tag: "error" });
}
const _args = kind.args.map((arg, i) =>
this.checkExpr(arg, paramTys[i])
);
return fnTy.kind.returnTy;
}
private tyTy(ty: ast.Ty): Ty {
return this.tyTys.get(ty.id) ||
this.checkTy(ty);
@ -227,10 +260,29 @@ export class Checker {
return todo();
case "bind": {
switch (patRes.kind.tag) {
case "fn_param":
return todo();
case "let":
return todo();
case "fn_param": {
const fnTy = this.fnItemTy(
patRes.kind.item,
patRes.kind.kind,
);
if (fnTy.kind.tag !== "fn") {
throw new Error();
}
const paramTy = fnTy.kind.params[patRes.kind.paramIdx];
this.assignPatTy(
patRes.kind.kind.params[patRes.kind.paramIdx].pat,
paramTy,
);
const ty = this.patTy(pat);
this.patTys.set(pat.id, ty);
return ty;
}
case "let": {
this.checkLetStmtTy(patRes.kind.stmt, patRes.kind.kind);
const ty = this.patTy(pat);
this.patTys.set(pat.id, ty);
return ty;
}
}
exhausted(patRes.kind);
return todo();
@ -242,6 +294,12 @@ export class Checker {
}
private resolveTys(a: Ty, b: Ty): Res<Ty, string> {
if (a.kind.tag === "error" || b.kind.tag === "error") {
return Res.Ok(a);
}
if (b.kind.tag === "unknown") {
return Res.Ok(a);
}
const as = tyToString(this.ctx, a);
const bs = tyToString(this.ctx, b);
const incompat = () =>
@ -249,10 +307,8 @@ export class Checker {
`type '${as}' not compatible with type '${bs}'`,
);
switch (a.kind.tag) {
case "error":
return Res.Ok(b);
case "unknown":
return Res.Ok(b);
return this.resolveTys(b, a);
case "null": {
if (b.kind.tag !== "null") {
return incompat();

View File

@ -25,6 +25,10 @@ export type Report = {
pos?: Pos;
};
export type ReportLocation =
| { file: File; span: Span }
| { file: File; pos: Pos };
function severityColor(severity: "fatal" | "error" | "warning" | "info") {
switch (severity) {
case "fatal":
@ -40,6 +44,13 @@ function severityColor(severity: "fatal" | "error" | "warning" | "info") {
}
export function prettyPrintReport(ctx: Ctx, rep: Report) {
if (rep.span && rep.span.begin.idx === rep.span.end.idx) {
return prettyPrintReport(ctx, {
...rep,
span: undefined,
pos: rep.span.begin,
});
}
const { severity, msg } = rep;
const origin = rep.origin ? `\x1b[1m${rep.origin}:\x1b[0m ` : "";
console.error(
@ -47,71 +58,72 @@ export function prettyPrintReport(ctx: Ctx, rep: Report) {
severityColor(severity)
}${severity}:\x1b[0m \x1b[37m${msg}\x1b[0m`,
);
if (rep.file && (rep.span || rep.pos)) {
const errorLineOffset = 2;
const { absPath: path } = ctx.fileInfo(rep.file);
const { line, col } = rep.span?.begin ?? rep.pos!;
console.error(` --> ./${path}:${line}:${col}`);
if (rep.span) {
const spanLines = ctx.fileSpanText(rep.file, rep.span).split("\n");
spanLines.pop();
if (spanLines.length == 1) {
console.error(
`${rep.span.begin.line.toString().padStart(4, " ")}| ${
spanLines[0]
}`,
);
console.error(
` | ${severityColor(severity)}${
" ".repeat(rep.span.begin.col)
}${
"~".repeat(rep.span.end.col - rep.span.begin.col)
}\x1b[0m`,
);
return;
}
for (let i = 0; i < spanLines.length; i++) {
console.error(
`${
(rep.span.begin.line + i).toString().padStart(4, " ")
}| ${spanLines[i]}`,
);
if (i == 0) {
console.error(
` | ${" ".repeat(rep.span.begin.col - 1)}${
severityColor(severity)
}${
"~".repeat(
spanLines[i].length - (rep.span.begin.col - 1),
)
}\x1b[0m`,
);
} else if (i == spanLines.length - 1) {
console.error(
` | ${severityColor(severity)}${
"~".repeat(rep.span.end.col)
}\x1b[0m`,
);
} else {
console.error(
` | ${severityColor(severity)}${
"~".repeat(spanLines[i].length)
}\x1b[0m`,
);
}
}
} else if (rep.pos) {
if (!rep.file) {
return;
}
const errorLineOffset = 2;
const { absPath: path } = ctx.fileInfo(rep.file);
const { line, col } = rep.span?.begin ?? rep.pos!;
console.error(` --> ./${path}:${line}:${col}`);
if (rep.span) {
const spanLines = ctx.fileSpanText(rep.file, rep.span).split("\n");
spanLines.pop();
if (spanLines.length == 1) {
console.error(
`${rep.pos.line.toString().padStart(4, " ")}| ${
ctx.filePosLineText(rep.file, rep.pos)
`${rep.span.begin.line.toString().padStart(4, " ")}| ${
spanLines[0]
}`,
);
console.error(
` | ${severityColor(severity)}${
" ".repeat(rep.pos.col)
}^\x1b[0m`,
" ".repeat(rep.span.begin.col - 1)
}${
"~".repeat(rep.span.end.col - rep.span.begin.col + 1)
}\x1b[0m`,
);
return;
}
for (let i = 0; i < spanLines.length; i++) {
console.error(
`${(rep.span.begin.line + i).toString().padStart(4, " ")}| ${
spanLines[i]
}`,
);
if (i == 0) {
console.error(
` | ${" ".repeat(rep.span.begin.col - 1)}${
severityColor(severity)
}${
"~".repeat(
spanLines[i].length - (rep.span.begin.col - 1),
)
}\x1b[0m`,
);
} else if (i == spanLines.length - 1) {
console.error(
` | ${severityColor(severity)}${
"~".repeat(rep.span.end.col)
}\x1b[0m`,
);
} else {
console.error(
` | ${severityColor(severity)}${
"~".repeat(spanLines[i].length)
}\x1b[0m`,
);
}
}
} else if (rep.pos) {
console.error(
`${rep.pos.line.toString().padStart(4, " ")}| ${
ctx.filePosLineText(rep.file, rep.pos)
}`,
);
console.error(
` | ${severityColor(severity)}${
" ".repeat(rep.pos.col - 1)
}^\x1b[0m`,
);
}
}

View File

@ -1,15 +1,24 @@
{
"workspace": ["./ast", "./check", "./middle", "./parse", "./resolve", "./ty", "./common"],
"workspace": [
"./ast",
"./check",
"./middle",
"./parse",
"./resolve",
"./ty",
"./common",
"./stringify"
],
"lint": {
"rules": {
"tags": ["recommended"],
"exclude": [
"verbatim-module-syntax",
"no-unused-vars"
],
]
}
},
"fmt": {
"indentWidth": 4
},
}
}

View File

@ -5,6 +5,7 @@ import { Ctx, File } from "@slige/common";
import { Resolver } from "./resolve/resolver.ts";
import { Checker } from "./check/checker.ts";
import { AstLowerer } from "./middle/ast_lower.ts";
import { HirStringifyer } from "@slige/stringify";
async function main() {
const filePath = Deno.args[0];
@ -43,7 +44,17 @@ export class PackCompiler {
.collect();
const resols = new Resolver(this.ctx, entryFileAst).resolve();
const checker = new Checker(this.ctx, entryFileAst, resols);
new AstLowerer(this.ctx, resols, checker, entryFileAst).lower();
console.log(
"=== HIR ===\n" + new HirStringifyer(checker).file(entryFileAst),
);
const astLowerer = new AstLowerer(
this.ctx,
resols,
checker,
entryFileAst,
);
astLowerer.lower();
console.log("=== MIR ===\n" + astLowerer.mirString());
}
public enableDebug() {

View File

@ -3,10 +3,20 @@ import { Checker } from "@slige/check";
import { AstId, Ctx, exhausted, IdMap, Ids, Res, todo } from "@slige/common";
import { Resols } from "@slige/resolve";
import { Ty } from "@slige/ty";
import { BinaryType, Operand, StmtKind, TerKind } from "./mir.ts";
import {
BinaryType,
Operand,
Place,
ProjElem,
StmtKind,
TerKind,
} from "./mir.ts";
import { Block, BlockId, Fn, Local, LocalId, RVal, Stmt, Ter } from "./mir.ts";
import { MirFnStringifyer } from "@slige/stringify";
export class AstLowerer implements ast.Visitor {
private fns = new IdMap<AstId, Fn>();
public constructor(
private ctx: Ctx,
private re: Resols,
@ -19,7 +29,18 @@ export class AstLowerer implements ast.Visitor {
}
visitFnItem(item: ast.Item, kind: ast.FnItem): ast.VisitRes {
new FnLowerer(this.ctx, this.re, this.ch, item, kind).lower();
const fn = new FnLowerer(this.ctx, this.re, this.ch, item, kind)
.lower();
if (fn.ok) {
this.fns.set(item.id, fn.val);
}
}
public mirString(): string {
return this.fns.values()
.map((fn) => new MirFnStringifyer(this.ctx).fn(fn))
.toArray()
.join("\n");
}
}
@ -46,7 +67,10 @@ export class FnLowerer {
const entry = this.pushBlock();
const fnTy = this.ch.fnItemTy(this.item, this.kind);
const returnPlace = this.local(fnTy);
if (fnTy.kind.tag !== "fn") {
throw new Error();
}
const returnPlace = this.local(fnTy.kind.returnTy);
const returnVal = this.lowerBlock(this.kind.body!);
this.addStmt({
@ -81,12 +105,68 @@ export class FnLowerer {
case "error":
return { tag: "error" };
case "item":
return todo(k.tag);
case "let":
return this.lowerLetStmt(stmt, k);
case "return":
case "break":
case "continue":
case "assign":
case "expr":
return todo(k.tag);
}
exhausted(k);
}
private lowerLetStmt(_stmt: ast.Stmt, kind: ast.LetStmt) {
const val = kind.expr && this.lowerExpr(kind.expr);
this.allocatePat(kind.pat);
if (val) {
const local = this.local(this.ch.patTy(kind.pat));
this.addStmt({
tag: "assign",
place: { local: local, proj: [] },
rval: val,
});
this.assignPat(kind.pat, local, []);
}
}
private allocatePat(pat: ast.Pat) {
const k = pat.kind;
switch (k.tag) {
case "error":
return;
case "bind": {
const ty = this.ch.patTy(pat);
const local = this.local(ty);
this.reLocals.set(pat.id, local);
return;
}
case "path":
return todo();
}
exhausted(k);
}
private assignPat(pat: ast.Pat, local: LocalId, proj: ProjElem[]) {
const k = pat.kind;
switch (k.tag) {
case "error":
return;
case "bind": {
const patLocal = this.reLocals.get(pat.id)!;
this.addStmt({
tag: "assign",
place: { local: patLocal, proj: [] },
rval: {
tag: "use",
operand: { tag: "move", place: { local, proj } },
},
});
return;
}
case "path":
return todo();
}
exhausted(k);
@ -100,7 +180,18 @@ export class FnLowerer {
case "path":
return this.lowerPathExpr(expr, k);
case "null":
return {
tag: "use",
operand: { tag: "const", val: { tag: "null" } },
};
case "int":
return {
tag: "use",
operand: {
tag: "const",
val: { tag: "int", value: k.value },
},
};
case "bool":
case "str":
case "group":
@ -112,7 +203,9 @@ export class FnLowerer {
case "elem":
case "field":
case "index":
return todo(k.tag);
case "call":
return this.lowerCallExpr(expr, k);
case "unary":
return todo(k.tag);
case "binary":
@ -133,8 +226,14 @@ export class FnLowerer {
switch (re.kind.tag) {
case "error":
return { tag: "error" };
case "fn":
return todo();
case "fn": {
const ty = this.ch.fnItemTy(re.kind.item, re.kind.kind);
const local = this.local(ty);
return {
tag: "use",
operand: { tag: "move", place: { local, proj: [] } },
};
}
case "local": {
const ty = this.ch.exprTy(expr);
const local = this.local(ty);
@ -164,6 +263,17 @@ export class FnLowerer {
exhausted(re.kind);
}
private lowerCallExpr(expr: ast.Expr, kind: ast.CallExpr): RVal {
const args = kind.args.map((arg) =>
this.rvalAsOperand(this.lowerExpr(arg), this.ch.exprTy(arg))
);
const func = this.rvalAsOperand(
this.lowerExpr(kind.expr),
this.ch.exprTy(expr),
);
return { tag: "call", func, args };
}
private lowerBinaryExpr(expr: ast.Expr, kind: ast.BinaryExpr): RVal {
const left = this.rvalAsOperand(
this.lowerExpr(kind.left),

View File

@ -51,8 +51,7 @@ export type TerKind =
}
| { tag: "return" }
| { tag: "unreachable" }
| { tag: "drop"; place: Place; target: BlockId }
| { tag: "call"; func: Operand; args: Operand[]; dest: Operand };
| { tag: "drop"; place: Place; target: BlockId };
export type SwitchTarget = {
value: number;
@ -67,7 +66,6 @@ export type Place = {
// https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/type.PlaceElem.html
export type ProjElem =
| { tag: "deref" }
| { tag: "repeat" }
| { tag: "field"; fieldIdx: number }
| { tag: "index"; local: LocalId }
| { tag: "downcast"; variantIdx: number };
@ -80,7 +78,8 @@ export type RVal =
| { tag: "ref"; place: Place; mut: boolean }
| { tag: "ptr"; place: Place; mut: boolean }
| { tag: "binary"; binaryType: BinaryType; left: Operand; right: Operand }
| { tag: "unary"; unaryType: UnaryType; operand: Operand };
| { tag: "unary"; unaryType: UnaryType; operand: Operand }
| { tag: "call"; func: Operand; args: Operand[] };
export type BinaryType =
| "add"

View File

@ -0,0 +1,2 @@
export * from "./mir.ts";
export * from "./ast_lower.ts";

View File

@ -203,7 +203,7 @@ export class Lexer implements TokenIter {
let val = this.current();
this.step();
while (this.test(tailPat)) {
end = begin;
end = this.pos();
val += this.current();
this.step();
}

View File

@ -25,7 +25,7 @@ export type PatResolve = {
};
export type PatResolveKind =
| { tag: "fn_param"; paramIdx: number }
| { tag: "fn_param"; item: ast.Item; kind: ast.FnItem; paramIdx: number }
| { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt };
export const ResolveError = (ident: ast.Ident): Resolve => ({

View File

@ -118,12 +118,17 @@ export class Resolver implements ast.Visitor {
}
private popAndVisitFnBodies() {
for (const [_item, kind] of this.fnBodiesToCheck.at(-1)!) {
for (const [item, kind] of this.fnBodiesToCheck.at(-1)!) {
const outerSyms = this.syms;
this.syms = new FnSyms(this.syms);
this.syms = new LocalSyms(this.syms);
for (const [paramIdx, param] of kind.params.entries()) {
this.patResolveStack.push({ tag: "fn_param", paramIdx });
this.patResolveStack.push({
tag: "fn_param",
item,
kind,
paramIdx,
});
ast.visitParam(this, param);
this.patResolveStack.pop();
}

View File

@ -0,0 +1,4 @@
{
"name": "@slige/stringify",
"exports": "./mod.ts",
}

View File

@ -0,0 +1,178 @@
import * as ast from "@slige/ast";
import { exhausted, todo } from "@slige/common";
import { Checker } from "@slige/check";
import { Ty } from "@slige/ty";
export class HirStringifyer {
public constructor(
private ch: Checker,
) {}
public file(file: ast.File, depth = 0): string {
return file.stmts.map((stmt) =>
indent(depth) + this.stmt(stmt, depth + 1)
).join("\n");
}
public stmt(stmt: ast.Stmt, depth = 0): string {
const k = stmt.kind;
switch (k.tag) {
case "error":
return "<error>;";
case "item":
return this.item(k.item);
case "let":
return `let ${this.pat(k.pat)}${
k.expr && ` = ${this.expr(k.expr)}` || ""
};`;
case "return":
return `return${k.expr && ` ${this.expr(k.expr)}` || ""};`;
case "break":
return `break${k.expr && ` ${this.expr(k.expr)}` || ""};`;
case "continue":
return `continue;`;
case "assign":
return `${this.expr(k.subject)} = ${this.expr(k.value)};`;
case "expr":
return `${this.expr(k.expr)};`;
}
exhausted(k);
}
public item(item: ast.Item, depth = 0): string {
const ident = item.ident.text;
const pub = item.pub ? "pub " : "";
const k = item.kind;
switch (k.tag) {
case "error":
return "<error>;";
case "mod_block":
return `${pub}mod ${ident} ${this.block(k.block, depth)}`;
case "mod_file":
return `${pub}mod ${ident} {\n${
this.file(k.ast!, depth + 1)
}\n}`;
case "enum":
return todo();
case "struct":
return todo();
case "fn": {
const ty = this.ch.fnItemTy(item, k);
if (ty.kind.tag !== "fn") {
throw new Error();
}
const params = k.params
.map((param) => this.pat(param.pat))
.join(", ");
return `${pub}fn ${ident}(${params}) -> ${
this.ty(ty.kind.returnTy)
} ${this.block(k.body!, depth)}`;
}
case "use":
return todo();
case "type_alias":
return todo();
}
exhausted(k);
}
public expr(expr: ast.Expr, depth = 0): string {
const k = expr.kind;
switch (k.tag) {
case "error":
return "<error>";
case "path":
return this.path(k.path);
case "null":
return "null";
case "int":
return `${k.value}`;
case "bool":
return `${k.value}`;
case "str":
return `"${k.value}"`;
case "group":
case "array":
case "repeat":
case "struct":
case "ref":
case "deref":
case "elem":
case "field":
case "index":
return todo(k.tag);
case "call":
return `${this.expr(k.expr)}(${
k.args.map((arg) => this.expr(arg)).join(", ")
})`;
case "unary":
return todo(k.tag);
case "binary":
return `${this.expr(k.left)} ${k.binaryType} ${
this.expr(k.right)
}`;
case "block":
case "if":
case "loop":
case "while":
case "for":
case "c_for":
return todo(k.tag);
}
exhausted(k);
}
public pat(pat: ast.Pat, depth = 0): string {
const k = pat.kind;
switch (k.tag) {
case "error":
return "<error>";
case "bind":
return `${k.mut ? "mut " : ""}${k.ident.text}: ${
this.ty(this.ch.patTy(pat))
}`;
case "path":
return todo();
}
exhausted(k);
}
public block(block: ast.Block, depth = 0): string {
return `{\n${
[
...block.stmts
.map((stmt) => this.stmt(stmt, depth + 1)),
...(block.expr ? [this.expr(block.expr)] : []),
]
.map((str) => indent(depth + 1) + str)
.join("\n")
}\n${indent(depth)}}`;
}
public path(path: ast.Path): string {
return path.segments.map((seg) => seg.ident.text).join("::");
}
public ty(ty: Ty): string {
const k = ty.kind;
switch (k.tag) {
case "error":
return "<error>";
case "unknown":
return "<unknown>";
case "null":
return "null";
case "int":
return "int";
case "fn":
return `fn ${k.item.ident}(${
k.params.map((param) => this.ty(param)).join(", ")
}) -> ${this.ty(k.returnTy)}`;
}
exhausted(k);
}
}
function indent(depth: number): string {
return " ".repeat(depth);
}

View File

@ -0,0 +1,168 @@
import {
Block,
BlockId,
Const,
Fn,
Local,
LocalId,
Operand,
Place,
ProjElem,
RVal,
Stmt,
StmtKind,
Ter,
} from "@slige/middle";
import { Ctx, exhausted, IdMap, todo } from "@slige/common";
import { Checker } from "@slige/check";
import { Ty, tyToString } from "@slige/ty";
export class MirFnStringifyer {
private blockIds = new IdMap<BlockId, number>();
private localIds = new IdMap<LocalId, number>();
public constructor(
private ctx: Ctx,
) {}
public fn(fn: Fn): string {
return `fn ${fn.label} {\n${
fn.locals.values().toArray()
.map((local) => this.localDef(local))
.join("\n")
}\n${
fn.blocks.values().toArray()
.map((block) => this.block(block))
.join("\n")
}\n}`.replaceAll("#", " ");
}
private localDef(local: Local): string {
const id = this.localIds.size;
this.localIds.set(local.id, id);
return `#let %${id}: ${tyToString(this.ctx, local.ty)}`;
}
private block(block: Block): string {
const id = this.blockIds.size;
this.blockIds.set(block.id, id);
return `#.b${id}: {\n${
block.stmts
.map((stmt) => this.stmt(stmt))
.join("\n")
}\n${this.ter(block.terminator)}\n#}`;
}
private stmt(stmt: Stmt): string {
const k = stmt.kind;
switch (k.tag) {
case "error":
return "##<error>;";
case "assign":
return `##${this.place(k.place)} = ${this.rval(k.rval)}`;
case "fake_read":
case "deinit":
case "live":
case "dead":
case "mention":
return todo();
}
exhausted(k);
}
private ter(ter: Ter): string {
const k = ter.kind;
switch (k.tag) {
case "unset":
return "##<unset>;";
case "goto":
case "switch":
return todo(k.tag);
case "return":
return `##return;`;
case "unreachable":
case "drop":
return todo(k.tag);
}
exhausted(k);
}
private place(place: Place): string {
return this.placeWithProj(place.local, place.proj);
}
private placeWithProj(local: LocalId, elems: ProjElem[]): string {
if (elems.length === 0) {
return this.local(local);
}
const elem = elems[0];
const tail = elems.slice(1);
switch (elem.tag) {
case "deref":
return `*${this.placeWithProj(local, tail)}`;
case "field":
return `${this.placeWithProj(local, tail)}.${elem.fieldIdx}`;
case "index":
return `${this.placeWithProj(local, tail)}[${
this.local(elem.local)
}]`;
case "downcast":
return todo();
}
exhausted(elem);
}
private rval(rval: RVal): string {
switch (rval.tag) {
case "error":
return "<error>";
case "use":
return `${this.operand(rval.operand)}`;
case "repeat":
case "ref":
case "ptr":
return todo(rval.tag);
case "binary":
return `${this.operand(rval.left)} ${rval.binaryType} ${
this.operand(rval.right)
}`;
case "unary":
return todo(rval.tag);
case "call":
return `${this.operand(rval.func)}(${
rval.args.map((arg) => this.operand(arg)).join(", ")
})`;
}
exhausted(rval);
}
private operand(operand: Operand): string {
switch (operand.tag) {
case "copy":
return `copy ${this.place(operand.place)}`;
case "move":
return `move ${this.place(operand.place)}`;
case "const":
return `${this.constVal(operand.val)}`;
}
exhausted(operand);
}
private constVal(val: Const): string {
switch (val.tag) {
case "null":
return `null`;
case "int":
return `${val.value}`;
case "bool":
return `${val.value}`;
case "string":
return `"${val.value}"`;
}
exhausted(val);
}
private local(local: LocalId): string {
return `%${this.localIds.get(local)!}`;
}
}

View File

@ -0,0 +1,2 @@
export * from "./hir.ts";
export * from "./mir.ts";