mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
compiler: if-else + copyMove + more
This commit is contained in:
parent
c5e42844af
commit
21c737d06a
@ -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,
|
||||
|
@ -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 });
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user