This commit is contained in:
Theis Pieter Hollebeek 2023-07-22 00:09:00 +02:00
parent 204e71acc7
commit 27ec81441b
6 changed files with 252 additions and 23 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules
*.out.*
a.out

182
compiler.ts Normal file
View File

@ -0,0 +1,182 @@
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();
}
}

View File

@ -6,32 +6,37 @@ import { Expr } from "./parsed";
expr -> term {% id %}
term -> term "+" factor
{% ([left, _, right]): Expr =>
term -> term _ "+" _ factor
{% ([left, _0, _1, _2, right]): Expr =>
({ exprType: "binary", binaryType: "add", left, right }) %}
| term "-" factor
{% ([left, _, right]): Expr =>
| term _ "-" _ factor
{% ([left, _0, _1, _2, right]): Expr =>
({ exprType: "binary", binaryType: "subtract", left, right }) %}
| factor {% id %}
factor -> factor "*" unary
{% ([left, _, right]): Expr =>
factor -> factor _ "*" _ unary
{% ([left, _0, _1, _2, right]): Expr =>
({ exprType: "binary", binaryType: "multiply", left, right }) %}
| factor "/" unary
{% ([left, _, right]): Expr =>
| factor _ "/" _ unary
{% ([left, _0, _1, _2, right]): Expr =>
({ exprType: "binary", binaryType: "divide", left, right }) %}
| unary {% id %}
unary -> "+" unary
{% ([_, subject]): Expr =>
unary -> "+" _ unary
{% ([_0, _1, subject]): Expr =>
({ exprType: "unary", unaryType: "plus", subject }) %}
| "-" unary
{% ([_, subject]): Expr =>
| "-" _ unary
{% ([_0, _1, subject]): Expr =>
({ exprType: "unary", unaryType: "negate", subject }) %}
| operand {% id %}
operand -> "(" expr ")" {% ([_, expr]): Expr => expr %}
| ("0" | [1-9][0-9]:*) {% ([token]): Expr => ({ exprType: "int", value: parseInt(token.value) }) %}
operand -> "(" _ expr _ ")" {% ([_0, _1, expr]): Expr => expr %}
| [0-9]:+
{% ([token]): Expr =>
({ exprType: "int", value: parseInt(token.join("")) }) %}
_ -> __:?
__ -> [ \t\r\n]:+
__ -> ws:+
ws -> [ \t\r\n]

View File

53
main.ts
View File

@ -1,16 +1,55 @@
import { Parser, Grammar } from "nearley"
import compiledGrammar from "./grammar.out"
import { Compiler, Instruction, Register, Value, X86Generator as X8664Generator } from "./compiler";
import fs from "fs/promises";
import { exec } from "child_process";
const parser = new Parser(Grammar.fromCompiled(compiledGrammar));
function executeCommand(command: string) {
return new Promise<void>((resolve, reject) => {
exec(command, {}, (error, stdout, stderr) => {
if (stdout) console.log(stdout);
if (stderr) console.error(stderr);
const input = "1 + 2";
if (error) {
console.error(error);
reject();
return;
}
if (input === null)
throw new Error("input fucked")
resolve();
});
});
}
parser.feed(input);
async function main(args: string[]) {
const parser = new Parser(Grammar.fromCompiled(compiledGrammar));
const ast = parser.results;
const input = args[2];
console.log(JSON.stringify(ast, null, 4))
if (input === null)
throw new Error("input fucked")
parser.feed(input);
const ast = parser.results[0];
console.log(JSON.stringify(ast, null, 4))
const compiler = new Compiler();
compiler.compileExpr(ast);
const ir = compiler.result;
console.log(ir);
const generator = new X8664Generator();
const asm = generator.generate(ir);
console.log(asm);
await fs.writeFile("generated.out.asm", asm);
await executeCommand("nasm -f elf64 -o generated.out.o generated.out.asm");
await executeCommand("ld generated.out.o -o a.out");
}
main(process.argv);

View File

@ -1,4 +1,6 @@
export type ExprType = Expr["exprType"]
export type Expr = {
exprType: "int",
value: number,