From 21c737d06a5b95ef44baa4a2583a5b7bdca47962 Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Mon, 10 Feb 2025 17:53:33 +0100 Subject: [PATCH] compiler: if-else + copyMove + more --- slige/compiler/main.ts | 4 +- slige/compiler/middle/ast_lower.ts | 83 +++++++++++++++------ slige/compiler/parse/parser.ts | 112 ++++++++++++++--------------- slige/compiler/program.slg | 8 +-- slige/compiler/stringify/hir.ts | 71 ++++++++---------- slige/compiler/stringify/mir.ts | 2 - 6 files changed, 151 insertions(+), 129 deletions(-) diff --git a/slige/compiler/main.ts b/slige/compiler/main.ts index 7bc3c46..ab36a1c 100644 --- a/slige/compiler/main.ts +++ b/slige/compiler/main.ts @@ -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, diff --git a/slige/compiler/middle/ast_lower.ts b/slige/compiler/middle/ast_lower.ts index 669f150..5d46b29 100644 --- a/slige/compiler/middle/ast_lower.ts +++ b/slige/compiler/middle/ast_lower.ts @@ -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 }); diff --git a/slige/compiler/parse/parser.ts b/slige/compiler/parse/parser.ts index 5faa099..5982b50 100644 --- a/slige/compiler/parse/parser.ts +++ b/slige/compiler/parse/parser.ts @@ -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 { @@ -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]; diff --git a/slige/compiler/program.slg b/slige/compiler/program.slg index 5d9dedc..fe7847f 100644 --- a/slige/compiler/program.slg +++ b/slige/compiler/program.slg @@ -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); } diff --git a/slige/compiler/stringify/hir.ts b/slige/compiler/stringify/hir.ts index 4fac324..b6599a8 100644 --- a/slige/compiler/stringify/hir.ts +++ b/slige/compiler/stringify/hir.ts @@ -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 ""; - case "unknown": - return ""; - 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); } } diff --git a/slige/compiler/stringify/mir.ts b/slige/compiler/stringify/mir.ts index d1d201b..4396d98 100644 --- a/slige/compiler/stringify/mir.ts +++ b/slige/compiler/stringify/mir.ts @@ -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 {