diff --git a/compiler.ts b/compiler.ts index 067d8b3..235a077 100644 --- a/compiler.ts +++ b/compiler.ts @@ -2,8 +2,9 @@ import { Expr } from "./parsed"; export type Register = "acc" | "op"; export type Value = number; +export type Location = number; -export type Instruction = { +export type Instruction = ({ type: "push" | "pop", register: Register, } | { @@ -19,7 +20,14 @@ export type Instruction = { type: "negate", src: Register, dest: Register, -} +} | { + type: "jump_zero", + register: Register, + location: Location, +} | { + type: "jump", + location: Location, +}) & ({ jumpedTo: true, label: number } | { jumpedTo?: false }); export class Compiler { public result: Instruction[] = []; @@ -70,6 +78,33 @@ export class Compiler { this.result.push({ type: binaryType, left: "acc", right: "op", dest: "acc" }); break; } + case "if": { + this.compileExpr(expr.condition); + + const jumpToFalsyIndex = this.result.length; + this.result.push({ type: "jump_zero", register: "acc", location: 0 }); + + this.compileExpr(expr.truthy); + + const skipFalsyIndex = this.result.length; + this.result.push({ type: "jump", location: 0 }); + + let jumpToFalsyRef = this.result[jumpToFalsyIndex]; + if (jumpToFalsyRef.type !== "jump_zero") throw new Error("unreachable"); + jumpToFalsyRef.location = this.result.length; + + this.compileExpr(expr.falsy); + + let skipFalsyRef = this.result[skipFalsyIndex]; + if (skipFalsyRef.type !== "jump") throw new Error("unreachable"); + skipFalsyRef.location = this.result.length; + + break; + } + case "block": { + this.compileExpr(expr.expr); + break; + } default: { const exhaustiveCheck: never = expr; throw new Error(`Unhandled color case: ${exhaustiveCheck}`); @@ -78,17 +113,35 @@ export class Compiler { } } +export function locateAndSetJumpedToInstructions(instructions: Instruction[]) { + let nextLabel = 0; + for (const ins of instructions) { + if (ins.type === "jump_zero" || ins.type === "jump") { + instructions[ins.location] = { + ...instructions[ins.location], + jumpedTo: true, + label: nextLabel, + }; + nextLabel += 1; + } + } +} + export class X86Generator { public generate(instructions: Instruction[]): string { return this.asmWithHeaders(instructions.map((ins) => this.generateInstruction(ins)).join("")); } private generateInstruction(ins: Instruction): string { + let result = ""; + if (ins.jumpedTo) { + + } switch (ins.type) { case "load": { - return ` - mov ${this.reg64(ins.register)}, ${this.value(ins.value)} - `; + result += ` ; load` + result += ` mov ${this.reg64(ins.register)}, ${this.value(ins.value)}\n`; + break; } case "push": { return ` @@ -141,6 +194,12 @@ export class X86Generator { mov ${this.reg64(ins.dest)}, rax `; } + case "jump_zero": { + return ` + ; jump_zero + + ` + } } } diff --git a/grammar.ne b/grammar.ne index 48b967d..6ae56ea 100644 --- a/grammar.ne +++ b/grammar.ne @@ -30,11 +30,29 @@ unary -> "+" _ unary ({ exprType: "unary", unaryType: "negate", subject }) %} | operand {% id %} -operand -> "(" _ expr _ ")" {% ([_0, _1, expr]): Expr => expr %} - | [0-9]:+ +operand -> int {% id %} + | group {% id %} + | block {% id %} + | if {% id %} + +int -> [0-9]:+ {% ([token]): Expr => ({ exprType: "int", value: parseInt(token.join("")) }) %} +group -> "(" _ expr _ ")" {% (v): Expr => v[2] %} + +block -> "{" _ expr _ "}" {% (v): Expr => ({ exprType: "block", expr: v[2] }) %} + +if -> "if" __ expr _ block _ "else" _ block + {% + (v): Expr => ({ + exprType: "if", + condition: v[2], + truthy: v[4], + falsy: v[8] + }) + %} + _ -> __:? __ -> ws:+ diff --git a/main.ts b/main.ts index 91340e8..51a4b9c 100644 --- a/main.ts +++ b/main.ts @@ -1,6 +1,6 @@ import { Parser, Grammar } from "nearley" import compiledGrammar from "./grammar.out" -import { Compiler, Instruction, Register, Value, X86Generator as X8664Generator } from "./compiler"; +import { Compiler, Instruction, Register, Value, X86Generator as X8664Generator, locateAndSetJumpedToInstructions } from "./compiler"; import fs from "fs/promises"; import { exec } from "child_process"; @@ -37,7 +37,9 @@ async function main(args: string[]) { const compiler = new Compiler(); compiler.compileExpr(ast); - const ir = compiler.result; + let ir = compiler.result; + console.log(ir); + locateAndSetJumpedToInstructions(ir); console.log(ir); const generator = new X8664Generator(); diff --git a/parsed.ts b/parsed.ts index 2d1f166..6b99038 100644 --- a/parsed.ts +++ b/parsed.ts @@ -13,6 +13,14 @@ export type Expr = { binaryType: "add" | "subtract" | "multiply" | "divide", left: Expr, right: Expr, +} | { + exprType: "if", + condition: Expr, + truthy: Expr, + falsy: Expr, +} | { + exprType: "block", + expr: Expr, };