compiler: if-else + copyMove + more

This commit is contained in:
SimonFJ20 2025-02-10 17:53:33 +01:00
parent c5e42844af
commit 21c737d06a
6 changed files with 151 additions and 129 deletions

View File

@ -45,7 +45,9 @@ export class PackCompiler {
const resols = new Resolver(this.ctx, entryFileAst).resolve();
const checker = new Checker(this.ctx, entryFileAst, resols);
console.log(
"=== HIR ===\n" + new HirStringifyer(checker).file(entryFileAst),
"=== HIR ===\n" +
new HirStringifyer(this.ctx, checker)
.file(entryFileAst),
);
const astLowerer = new AstLowerer(
this.ctx,

View File

@ -178,7 +178,10 @@ export class FnLowerer {
place: { local: patLocal, proj: [] },
rval: {
tag: "use",
operand: { tag: "move", place: { local, proj } },
operand: this.copyOrMoveLocal(
local,
this.locals.get(local)!.ty,
),
},
});
return;
@ -290,10 +293,40 @@ export class FnLowerer {
}
private lowerIfExpr(expr: ast.Expr, kind: ast.IfExpr): RVal {
const cond = this.lowerExprToOperand(kind.cond);
const ty = this.ch.exprTy(expr);
const discr = this.lowerExprToOperand(kind.cond);
const condBlock = this.currentBlock!;
if (kind.falsy) {
return todo();
const local = this.local(ty);
const truthBlock = this.pushBlock();
const truthy = this.lowerExpr(kind.truthy);
this.addStmt({
tag: "assign",
place: { local, proj: [] },
rval: truthy,
});
const falsyBlock = this.pushBlock();
const falsy = this.lowerExpr(kind.falsy);
this.addStmt({
tag: "assign",
place: { local, proj: [] },
rval: falsy,
});
const exit = this.pushBlock();
this.setTer({ tag: "goto", target: exit.id }, truthBlock);
this.setTer({ tag: "goto", target: exit.id }, falsyBlock);
this.setTer({
tag: "switch",
discr,
targets: [{ value: 1, target: truthBlock.id }],
otherwise: falsyBlock.id,
}, condBlock);
return { tag: "use", operand: this.copyOrMoveLocal(local, ty) };
} else {
if (this.ch.exprTy(expr).kind.tag !== "null") {
throw new Error();
@ -304,7 +337,7 @@ export class FnLowerer {
this.setTer({ tag: "goto", target: exit.id }, truthBlock);
this.setTer({
tag: "switch",
discr: cond,
discr,
targets: [{ value: 1, target: truthBlock.id }],
otherwise: exit.id,
}, condBlock);
@ -361,7 +394,7 @@ export class FnLowerer {
place: { local, proj: [] },
rval,
});
return { tag: "move", place: { local, proj: [] } };
return this.copyOrMoveLocal(local, ty);
}
}
exhausted(k);
@ -388,24 +421,7 @@ export class FnLowerer {
if (patRes.kind.tag === "fn_param") {
this.paramLocals.set(local, patRes.kind.paramIdx);
}
const isCopyable = (() => {
switch (ty.kind.tag) {
case "error":
case "unknown":
return false;
case "null":
case "int":
case "bool":
return true;
case "fn":
return false;
}
exhausted(ty.kind);
})();
return {
tag: isCopyable ? "copy" : "move",
place: { local, proj: [] },
};
return this.copyOrMoveLocal(local, ty);
}
case "fn": {
const { item, kind } = re.kind;
@ -415,6 +431,27 @@ export class FnLowerer {
exhausted(re.kind);
}
private copyOrMoveLocal(local: LocalId, ty: Ty): Operand {
const isCopyable = (() => {
switch (ty.kind.tag) {
case "error":
case "unknown":
return false;
case "null":
case "int":
case "bool":
return true;
case "fn":
return false;
}
exhausted(ty.kind);
})();
return {
tag: isCopyable ? "copy" : "move",
place: { local, proj: [] },
};
}
private local(ty: Ty, ident?: ast.Ident): LocalId {
const id = this.localIds.nextThenStep();
this.locals.set(id, { id, ty, ident });

View File

@ -134,8 +134,8 @@ export class Parser {
this.step();
}
private parseExpr(): Expr {
return this.parseBinary();
private parseExpr(rs: ExprRestricts = 0): Expr {
return this.parseBinary(rs);
}
private parseBlock(): ParseRes<Block, undefined> {
@ -698,12 +698,12 @@ export class Parser {
private parseIf(): Expr {
const pos = this.span();
this.step();
const cond = this.parseExpr();
const cond = this.parseExpr(ExprRestricts.NoStructs);
if (!this.test("{")) {
this.report("expected block");
return this.expr({ tag: "error" }, pos);
}
const truthy = this.parseExpr();
const truthy = this.parseBlockExpr();
if (!this.test("else")) {
return this.expr({ tag: "if", cond, truthy }, pos);
}
@ -721,16 +721,16 @@ export class Parser {
return this.expr({ tag: "if", cond, truthy, falsy }, pos);
}
private parseBinary(): Expr {
return this.parseOr();
private parseBinary(rs: ExprRestricts): Expr {
return this.parseOr(rs);
}
private parseOr(): Expr {
private parseOr(rs: ExprRestricts): Expr {
const pos = this.span();
let left = this.parseAnd();
let left = this.parseAnd(rs);
while (true) {
if (this.test("or")) {
left = this.parBinTail(left, pos, this.parseAnd, "or");
left = this.parBinTail(left, pos, rs, this.parseAnd, "or");
} else {
break;
}
@ -738,12 +738,12 @@ export class Parser {
return left;
}
private parseAnd(): Expr {
private parseAnd(rs: ExprRestricts): Expr {
const pos = this.span();
let left = this.parseEquality();
let left = this.parseEq(rs);
while (true) {
if (this.test("and")) {
left = this.parBinTail(left, pos, this.parseEquality, "and");
left = this.parBinTail(left, pos, rs, this.parseEq, "and");
} else {
break;
}
@ -751,46 +751,36 @@ export class Parser {
return left;
}
private parseEquality(): Expr {
private parseEq(rs: ExprRestricts): Expr {
const pos = this.span();
const left = this.parseComparison();
if (this.test("==")) {
return this.parBinTail(left, pos, this.parseComparison, "==");
}
if (this.test("!=")) {
return this.parBinTail(left, pos, this.parseComparison, "!=");
const left = this.parseComparison(rs);
if (this.test("==") || this.test("!=")) {
const op = this.current().type as BinaryType;
return this.parBinTail(left, pos, rs, this.parseComparison, op);
}
return left;
}
private parseComparison(): Expr {
private parseComparison(rs: ExprRestricts): Expr {
const pos = this.span();
const left = this.parseAddSub();
if (this.test("<")) {
return this.parBinTail(left, pos, this.parseAddSub, "<");
}
if (this.test(">")) {
return this.parBinTail(left, pos, this.parseAddSub, ">");
}
if (this.test("<=")) {
return this.parBinTail(left, pos, this.parseAddSub, "<=");
}
if (this.test(">=")) {
return this.parBinTail(left, pos, this.parseAddSub, ">=");
const left = this.parseAddSub(rs);
if (
this.test("<") || this.test(">") || this.test("<=") ||
this.test(">=")
) {
const op = this.current().type as BinaryType;
return this.parBinTail(left, pos, rs, this.parseAddSub, op);
}
return left;
}
private parseAddSub(): Expr {
private parseAddSub(rs: ExprRestricts): Expr {
const pos = this.span();
let left = this.parseMulDiv();
let left = this.parseMulDiv(rs);
while (true) {
if (this.test("+")) {
left = this.parBinTail(left, pos, this.parseMulDiv, "+");
continue;
}
if (this.test("-")) {
left = this.parBinTail(left, pos, this.parseMulDiv, "-");
if (this.test("+") || this.test("-")) {
const op = this.current().type as BinaryType;
left = this.parBinTail(left, pos, rs, this.parseMulDiv, op);
continue;
}
break;
@ -798,16 +788,13 @@ export class Parser {
return left;
}
private parseMulDiv(): Expr {
private parseMulDiv(rs: ExprRestricts): Expr {
const pos = this.span();
let left = this.parsePrefix();
let left = this.parsePrefix(rs);
while (true) {
if (this.test("*")) {
left = this.parBinTail(left, pos, this.parsePrefix, "*");
continue;
}
if (this.test("/")) {
left = this.parBinTail(left, pos, this.parsePrefix, "/");
if (this.test("*") || this.test("/")) {
const op = this.current().type as BinaryType;
left = this.parBinTail(left, pos, rs, this.parsePrefix, op);
continue;
}
break;
@ -818,23 +805,24 @@ export class Parser {
private parBinTail(
left: Expr,
span: Span,
parseRight: (this: Parser) => Expr,
rs: ExprRestricts,
parseRight: (this: Parser, rs: ExprRestricts) => Expr,
binaryType: BinaryType,
): Expr {
this.step();
const right = parseRight.call(this);
const right = parseRight.call(this, rs);
return this.expr(
{ tag: "binary", binaryType, left, right },
span,
);
}
private parsePrefix(): Expr {
private parsePrefix(rs: ExprRestricts): Expr {
const pos = this.span();
if (this.test("not") || this.test("-")) {
const unaryType = this.current().type as UnaryType;
this.step();
const expr = this.parsePrefix();
const expr = this.parsePrefix(rs);
return this.expr({ tag: "unary", unaryType, expr }, pos);
}
if (this.test("&")) {
@ -849,19 +837,19 @@ export class Parser {
this.step();
mut = true;
}
const expr = this.parsePrefix();
const expr = this.parsePrefix(rs);
return this.expr({ tag: "ref", expr, mut, refType }, pos);
}
if (this.test("*")) {
this.step();
const expr = this.parsePrefix();
const expr = this.parsePrefix(rs);
return this.expr({ tag: "deref", expr }, pos);
}
return this.parsePostfix();
return this.parsePostfix(rs);
}
private parsePostfix(): Expr {
let subject = this.parseOperand();
private parsePostfix(rs: ExprRestricts): Expr {
let subject = this.parseOperand(rs);
while (true) {
if (this.test(".")) {
subject = this.parseFieldTail(subject);
@ -917,14 +905,14 @@ export class Parser {
return Res.Ok(this.parseExpr());
}
private parseOperand(): Expr {
private parseOperand(rs: ExprRestricts): Expr {
const pos = this.span();
if (this.test("ident")) {
const pathRes = this.parsePath();
if (!pathRes.ok) {
return this.expr({ tag: "error" }, pos);
}
if (this.test("{")) {
if (this.test("{") && !(rs & ExprRestricts.NoStructs)) {
this.step();
const fields = this.parseDelimitedList(
this.parseExprField,
@ -1264,3 +1252,9 @@ export class Parser {
return this.cx.ty(kind, span);
}
}
const ExprRestricts = {
NoStructs: 1 << 0,
} as const;
type ExprRestricts = (typeof ExprRestricts)[keyof typeof ExprRestricts];

View File

@ -4,12 +4,12 @@ fn add(lhs: int, rhs: int) -> int {
}
fn main() {
let a = 5;
let b = 7 + a;
let foo = 5;
let bar = 7 + foo;
if true {}
if foo {} else {}
let c = add(a, b);
let c = add(foo, bar);
}

View File

@ -1,10 +1,11 @@
import * as ast from "@slige/ast";
import { exhausted, todo } from "@slige/common";
import { Ctx, exhausted, todo } from "@slige/common";
import { Checker } from "@slige/check";
import { Ty } from "@slige/ty";
import { Ty, tyToString } from "@slige/ty";
export class HirStringifyer {
public constructor(
private ctx: Ctx,
private ch: Checker,
) {}
@ -14,7 +15,7 @@ export class HirStringifyer {
).join("\n");
}
public stmt(stmt: ast.Stmt, depth = 0): string {
public stmt(stmt: ast.Stmt, d = 0): string {
const k = stmt.kind;
switch (k.tag) {
case "error":
@ -23,18 +24,18 @@ export class HirStringifyer {
return this.item(k.item);
case "let":
return `let ${this.pat(k.pat)}${
k.expr && ` = ${this.expr(k.expr)}` || ""
k.expr && ` = ${this.expr(k.expr, d)}` || ""
};`;
case "return":
return `return${k.expr && ` ${this.expr(k.expr)}` || ""};`;
return `return${k.expr && ` ${this.expr(k.expr, d)}` || ""};`;
case "break":
return `break${k.expr && ` ${this.expr(k.expr)}` || ""};`;
return `break${k.expr && ` ${this.expr(k.expr, d)}` || ""};`;
case "continue":
return `continue;`;
case "assign":
return `${this.expr(k.subject)} = ${this.expr(k.value)};`;
return `${this.expr(k.subject, d)} = ${this.expr(k.value, d)};`;
case "expr":
return `${this.expr(k.expr)};`;
return `${this.expr(k.expr, d)};`;
}
exhausted(k);
}
@ -76,7 +77,7 @@ export class HirStringifyer {
exhausted(k);
}
public expr(expr: ast.Expr, depth = 0): string {
public expr(expr: ast.Expr, d: number): string {
const k = expr.kind;
switch (k.tag) {
case "error":
@ -102,19 +103,21 @@ export class HirStringifyer {
case "index":
return todo(k.tag);
case "call":
return `${this.expr(k.expr)}(${
k.args.map((arg) => this.expr(arg)).join(", ")
return `${this.expr(k.expr, d)}(${
k.args.map((arg) => this.expr(arg, d)).join(", ")
})`;
case "unary":
return todo(k.tag);
case "binary":
return `${this.expr(k.left)} ${k.binaryType} ${
this.expr(k.right)
return `${this.expr(k.left, d)} ${k.binaryType} ${
this.expr(k.right, d)
}`;
case "block":
return todo(k.tag);
return this.block(k.block, d);
case "if":
return `if ${this.expr(k.cond)}`;
return `if ${this.expr(k.cond, d)} ${this.expr(k.truthy, d)}${
k.falsy && ` else ${this.expr(k.falsy, d)}` || ""
}`;
case "loop":
case "while":
case "for":
@ -124,7 +127,7 @@ export class HirStringifyer {
exhausted(k);
}
public pat(pat: ast.Pat, depth = 0): string {
public pat(pat: ast.Pat): string {
const k = pat.kind;
switch (k.tag) {
case "error":
@ -139,41 +142,29 @@ export class HirStringifyer {
exhausted(k);
}
public block(block: ast.Block, depth = 0): string {
public block(block: ast.Block, d: number): string {
if (block.stmts.length === 0 && !block.expr) {
return "{}";
}
return `{\n${
[
...block.stmts
.map((stmt) => this.stmt(stmt, depth + 1)),
...(block.expr ? [this.expr(block.expr)] : []),
.map((stmt) => this.stmt(stmt, d + 1)),
...(block.expr ? [this.expr(block.expr, d + 1)] : []),
]
.map((str) => indent(depth + 1) + str)
.map((str) => indent(d + 1) + str)
.join("\n")
}\n${indent(depth)}}`;
}\n${indent(d)}}`;
}
public path(path: ast.Path): string {
return path.segments.map((seg) => seg.ident.text).join("::");
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 "bool":
return "bool";
case "fn":
return `fn ${k.item.ident}(${
k.params.map((param) => this.ty(param)).join(", ")
}) -> ${this.ty(k.returnTy)}`;
}
exhausted(k);
return tyToString(this.ctx, ty);
}
}

View File

@ -10,11 +10,9 @@ import {
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 {