kodesprog/compiler.ts
2023-07-22 00:09:00 +02:00

183 lines
5.1 KiB
TypeScript

import { Expr } from "./parsed";
export type Register = "acc" | "op";
export type Value = number;
export type Instruction = {
type: "push" | "pop",
register: Register,
} | {
type: "load",
register: Register,
value: Value,
} | {
type: "add" | "mul" | "sub" | "div",
left: Register,
right: Register,
dest: Register,
} | {
type: "negate",
src: Register,
dest: Register,
}
export class Compiler {
public result: Instruction[] = [];
public compileExpr(expr: Expr) {
switch (expr.exprType) {
case "int": {
this.result.push({ type: "load", register: "acc", value: expr.value })
break;
}
case "unary": {
this.compileExpr(expr.subject);
switch (expr.unaryType) {
case "plus": {
break;
}
case "negate": {
this.result.push({ type: "negate", src: "acc", dest: "acc" })
break;
}
}
break;
}
case "binary": {
this.compileExpr(expr.right);
this.result.push({ type: "push", register: "acc" })
this.compileExpr(expr.left);
this.result.push({ type: "pop", register: "op" });
let binaryType: "add" | "sub" | "mul" | "div";
switch (expr.binaryType) {
case "add": {
binaryType = "add";
break;
}
case "subtract": {
binaryType = "sub";
break;
}
case "multiply": {
binaryType = "mul";
break;
}
case "divide": {
binaryType = "div";
break;
}
}
this.result.push({ type: binaryType, left: "acc", right: "op", dest: "acc" });
break;
}
default: {
const exhaustiveCheck: never = expr;
throw new Error(`Unhandled color case: ${exhaustiveCheck}`);
}
}
}
}
export class X86Generator {
public generate(instructions: Instruction[]): string {
return this.asmWithHeaders(instructions.map((ins) => this.generateInstruction(ins)).join(""));
}
private generateInstruction(ins: Instruction): string {
switch (ins.type) {
case "load": {
return `
mov ${this.reg64(ins.register)}, ${this.value(ins.value)}
`;
}
case "push": {
return `
; push
push ${this.reg64(ins.register)}
`;
}
case "pop": {
return `
; pop
pop ${this.reg64(ins.register)}
`;
}
case "negate": {
return `
; neg
mov ${this.reg64(ins.dest)}, ${this.reg64(ins.src)}
neg ${this.reg64(ins.dest)}
`;
}
case "add": {
return `
; add
mov ${this.reg64(ins.dest)}, ${this.reg64(ins.left)}
add ${this.reg64(ins.dest)}, ${this.reg64(ins.right)}
`;
}
case "sub": {
return `
; sub
mov ${this.reg64(ins.dest)}, ${this.reg64(ins.left)}
sub ${this.reg64(ins.dest)}, ${this.reg64(ins.right)}
`;
}
case "mul": {
return `
; mul
mov ${this.reg64(ins.dest)}, ${this.reg64(ins.left)}
imul ${this.reg64(ins.dest)}, ${this.reg64(ins.right)}
`;
}
case "div": {
return `
; div
mov rdi, ${this.reg64(ins.right)}
mov rax, ${this.reg64(ins.left)}
xor rdx, rdx
cqo
idiv rdi
mov ${this.reg64(ins.dest)}, rax
`;
}
}
}
private asmWithHeaders(asm: string) {
return `
bits 64
global _start
_start:
${asm}
exit:
mov rdi, rax
mov rax, 60
syscall
`;
}
private reg64(reg: Register): string {
switch (reg) {
case "acc": return "rax";
case "op": return "rdx";
}
}
private reg32(reg: Register): string {
switch (reg) {
case "acc": return "eax";
case "op": return "edx";
}
}
private value(value: Value): string {
return value.toString();
}
}