impl
This commit is contained in:
parent
204e71acc7
commit
27ec81441b
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
|
||||
node_modules
|
||||
*.out.*
|
||||
a.out
|
||||
|
||||
|
182
compiler.ts
Normal file
182
compiler.ts
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
35
grammar.ne
35
grammar.ne
@ -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]
|
||||
|
||||
|
53
main.ts
53
main.ts
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user