coed spilt
This commit is contained in:
		
							parent
							
								
									b2a59de1b2
								
							
						
					
					
						commit
						27e066f5b1
					
				
							
								
								
									
										241
									
								
								compiler.ts
									
									
									
									
									
								
							
							
						
						
									
										241
									
								
								compiler.ts
									
									
									
									
									
								
							| @ -1,241 +0,0 @@ | ||||
| import { Expr } from "./parsed"; | ||||
| 
 | ||||
| export type Register = "acc" | "op"; | ||||
| export type Value = number; | ||||
| export type Location = 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, | ||||
| } | { | ||||
|     type: "jump_zero", | ||||
|     register: Register, | ||||
|     location: Location, | ||||
| } | { | ||||
|     type: "jump", | ||||
|     location: Location, | ||||
| }) & ({ jumpedTo: true, label: number } | { jumpedTo?: false }); | ||||
| 
 | ||||
| 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; | ||||
|             } | ||||
|             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}`); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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": { | ||||
|                 result += `    ; load` | ||||
|                 result += `    mov ${this.reg64(ins.register)}, ${this.value(ins.value)}\n`; | ||||
|                 break; | ||||
|             } | ||||
|             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 | ||||
|                 `;
 | ||||
|             } | ||||
|             case "jump_zero": { | ||||
|                 return ` | ||||
|                     ; jump_zero | ||||
|                      | ||||
|                 ` | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										60
									
								
								grammar.ne
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								grammar.ne
									
									
									
									
									
								
							| @ -1,60 +0,0 @@ | ||||
| @preprocessor typescript | ||||
| 
 | ||||
| @{% | ||||
| import { Expr } from "./parsed"; | ||||
| %} | ||||
| 
 | ||||
| expr -> term {% id %} | ||||
| 
 | ||||
| term -> term _ "+" _ factor | ||||
| 		{% ([left, _0, _1, _2, right]): Expr => | ||||
| 			({ exprType: "binary", binaryType: "add", left, right }) %} | ||||
| 	| term _ "-" _ factor | ||||
| 		{% ([left, _0, _1, _2, right]): Expr => | ||||
| 			({ exprType: "binary", binaryType: "subtract", left, right }) %} | ||||
| 	| factor {% id %} | ||||
| 
 | ||||
| factor -> factor _ "*" _ unary | ||||
| 		{% ([left, _0, _1, _2, right]): Expr => | ||||
| 			({ exprType: "binary", binaryType: "multiply", left, right }) %} | ||||
| 	| factor _ "/" _ unary | ||||
| 		{% ([left, _0, _1, _2, right]): Expr => | ||||
| 			({ exprType: "binary", binaryType: "divide", left, right }) %} | ||||
| 	| unary {% id %} | ||||
| 
 | ||||
| unary -> "+" _ unary | ||||
| 		{% ([_0, _1, subject]): Expr => | ||||
| 			({ exprType: "unary", unaryType: "plus", subject }) %} | ||||
| 	| "-" _ unary | ||||
| 		{% ([_0, _1, subject]): Expr => | ||||
| 			({ exprType: "unary", unaryType: "negate", subject }) %} | ||||
| 	| operand {% id %} | ||||
| 
 | ||||
| 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:+ | ||||
| 
 | ||||
| ws -> [ \t\r\n] | ||||
| 
 | ||||
| @ -1,7 +1,8 @@ | ||||
| { | ||||
|   "license": "MIT", | ||||
|   "scripts": { | ||||
|     "start": "npm run build && ts-node main.ts", | ||||
|     "build": "nearleyc grammar.ne -o grammar.out.ts" | ||||
|     "start": "npm run build && ts-node src/main.ts", | ||||
|     "build": "nearleyc src/grammar.ne -o src/grammar.out.ts" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@types/moo": "^0.5.5", | ||||
| @ -10,6 +11,7 @@ | ||||
|     "moo": "^0.5.2", | ||||
|     "nearley": "^2.20.1", | ||||
|     "ts-node": "^10.9.1", | ||||
|     "ts-results": "^3.3.0", | ||||
|     "typescript": "^5.1.6" | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										103
									
								
								src/compiler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/compiler.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| import { Instruction } from "./ir"; | ||||
| import { Expr } from "./parsed"; | ||||
| import { assertExhaustive } from "./utils"; | ||||
| 
 | ||||
| 
 | ||||
| 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; | ||||
|             } | ||||
|             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: { | ||||
|                 assertExhaustive(expr); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										63
									
								
								src/grammar.ne
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/grammar.ne
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| @preprocessor typescript | ||||
| 
 | ||||
| @{% | ||||
| import { Expr } from "./parsed"; | ||||
| 
 | ||||
| const expr = (e: Expr): Expr => e; | ||||
| 
 | ||||
| %} | ||||
| 
 | ||||
| expr -> term {% id %} | ||||
| 
 | ||||
| term -> term _ "+" _ factor | ||||
| 		{% ([left, _0, _1, _2, right]) => | ||||
| 			expr({ exprType: "binary", binaryType: "add", left, right }) %} | ||||
| 	| term _ "-" _ factor | ||||
| 		{% ([left, _0, _1, _2, right]) => | ||||
| 			expr({ exprType: "binary", binaryType: "subtract", left, right }) %} | ||||
| 	| factor {% id %} | ||||
| 
 | ||||
| factor -> factor _ "*" _ unary | ||||
| 		{% ([left, _0, _1, _2, right]) => | ||||
| 			expr({ exprType: "binary", binaryType: "multiply", left, right }) %} | ||||
| 	| factor _ "/" _ unary | ||||
| 		{% ([left, _0, _1, _2, right]) => | ||||
| 			expr({ exprType: "binary", binaryType: "divide", left, right }) %} | ||||
| 	| unary {% id %} | ||||
| 
 | ||||
| unary -> "+" _ unary | ||||
| 		{% ([_0, _1, subject]) => | ||||
| 			expr({ exprType: "unary", unaryType: "plus", subject }) %} | ||||
| 	| "-" _ unary | ||||
| 		{% ([_0, _1, subject]) => | ||||
| 			expr({ exprType: "unary", unaryType: "negate", subject }) %} | ||||
| 	| operand {% id %} | ||||
| 
 | ||||
| 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:+ | ||||
| 
 | ||||
| ws -> [ \t\r\n] | ||||
| 
 | ||||
							
								
								
									
										28
									
								
								src/ir.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/ir.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| export type Register = "acc" | "op"; | ||||
| export type Value = number; | ||||
| export type Location = 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, | ||||
| } | { | ||||
|     type: "jump_zero", | ||||
|     register: Register, | ||||
|     location: Location, | ||||
| } | { | ||||
|     type: "jump", | ||||
|     location: Location, | ||||
| }) & ({ jumpedTo: true, label: number } | { jumpedTo?: false }); | ||||
| @ -1,8 +1,8 @@ | ||||
| import { Parser, Grammar } from "nearley" | ||||
| import compiledGrammar from "./grammar.out" | ||||
| import { Compiler, Instruction, Register, Value, X86Generator as X8664Generator, locateAndSetJumpedToInstructions } from "./compiler"; | ||||
| import { Compiler, locateAndSetJumpedToInstructions } from "./compiler"; | ||||
| import fs from "fs/promises"; | ||||
| import { exec } from "child_process"; | ||||
| import { parse } from "./parser"; | ||||
| import { X8664Generator } from "./x86_64_generator"; | ||||
| 
 | ||||
| function executeCommand(command: string) { | ||||
|     return new Promise<void>((resolve, reject) => { | ||||
| @ -22,16 +22,13 @@ function executeCommand(command: string) { | ||||
| } | ||||
| 
 | ||||
| async function main(args: string[]) { | ||||
|     const parser = new Parser(Grammar.fromCompiled(compiledGrammar)); | ||||
| 
 | ||||
|     const input = args[2]; | ||||
| 
 | ||||
|     if (input === null) | ||||
|         throw new Error("input fucked") | ||||
| 
 | ||||
|     parser.feed(input); | ||||
| 
 | ||||
|     const ast = parser.results[0]; | ||||
|     const ast = parse(input).unwrap(); | ||||
| 
 | ||||
|     console.log(JSON.stringify(ast, null, 4)) | ||||
| 
 | ||||
							
								
								
									
										20
									
								
								src/parser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/parser.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| import { Grammar, Parser } from "nearley"; | ||||
| import { Err, Ok, Result } from "ts-results"; | ||||
| import compiledGrammar from "./grammar.out" | ||||
| import { Expr } from "./parsed"; | ||||
| 
 | ||||
| export function parse(text: string): Result<Expr, string> { | ||||
|     const parser = new Parser(Grammar.fromCompiled(compiledGrammar)); | ||||
|     try { | ||||
|         parser.feed(text); | ||||
|     } catch (parseError) { | ||||
|         console.log(parseError) | ||||
|     } | ||||
|     const result = parser.results?.at(0); | ||||
|     if (!result) | ||||
|         return Err("failed to parse"); | ||||
|     if (parser.results.length > 1) | ||||
|         return Err("ambigous parse result"); | ||||
|     return Ok(result); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										12
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| 
 | ||||
| export function assertExhaustive(matchedItem?: never): never { | ||||
|     throw new Error(`unexhaustive match, unmatched value: ${matchedItem}`); | ||||
| } | ||||
| 
 | ||||
| export function toString(value: unknown): string { | ||||
|     const stringified = String(value); | ||||
|     if (stringified === "[object Object]") | ||||
|         return JSON.stringify(value, null, 4) | ||||
|     return stringified; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										148
									
								
								src/x86_64_generator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/x86_64_generator.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,148 @@ | ||||
| import { Instruction, Register, Value } from "./ir"; | ||||
| import { assertExhaustive } from "./utils"; | ||||
| 
 | ||||
| export class X8664Generator { | ||||
|     private result = ""; | ||||
| 
 | ||||
|     public generate(instructions: Instruction[]): string { | ||||
|         this.result = ""; | ||||
|         for (const ins of instructions) { | ||||
|             this.generateInstruction(ins, instructions); | ||||
|         } | ||||
|         return this.asmWithHeaders(this.result); | ||||
|     } | ||||
| 
 | ||||
|     private generateInstruction(ins: Instruction, instructions: Instruction[]) { | ||||
|         let result = ""; | ||||
|         if (ins.jumpedTo) | ||||
|             result += `.L${ins.label}:\n`; | ||||
|         switch (ins.type) { | ||||
|             case "load": | ||||
|                 this.add(` | ||||
|                     ; load | ||||
|                     mov ${this.reg64(ins.register)}, ${this.value(ins.value)} | ||||
|                 `);
 | ||||
|                 break; | ||||
|             case "push": | ||||
|                 this.add(` | ||||
|                     ; push | ||||
|                     push ${this.reg64(ins.register)} | ||||
|                 `);
 | ||||
|             case "pop": | ||||
|                 this.add(` | ||||
|                     ; pop | ||||
|                     pop ${this.reg64(ins.register)} | ||||
|                 `);
 | ||||
|                 break; | ||||
|             case "negate": | ||||
|                 this.add(` | ||||
|                     ; neg | ||||
|                     mov ${this.reg64(ins.dest)}, ${this.reg64(ins.src)} | ||||
|                     neg ${this.reg64(ins.dest)} | ||||
|                 `);
 | ||||
|                 break; | ||||
|             case "add": | ||||
|                 this.add(` | ||||
|                     ; add | ||||
|                     mov ${this.reg64(ins.dest)}, ${this.reg64(ins.left)} | ||||
|                     add ${this.reg64(ins.dest)}, ${this.reg64(ins.right)} | ||||
|                 `);
 | ||||
|                 break; | ||||
|             case "sub": | ||||
|                 this.add(` | ||||
|                     ; sub | ||||
|                     mov ${this.reg64(ins.dest)}, ${this.reg64(ins.left)} | ||||
|                     sub ${this.reg64(ins.dest)}, ${this.reg64(ins.right)} | ||||
|                 `);
 | ||||
|                 break; | ||||
|             case "mul": | ||||
|                 this.add(` | ||||
|                     ; mul | ||||
|                     mov ${this.reg64(ins.dest)}, ${this.reg64(ins.left)} | ||||
|                     imul ${this.reg64(ins.dest)}, ${this.reg64(ins.right)} | ||||
|                 `);
 | ||||
|                 break; | ||||
|             case "div": | ||||
|                 this.add(` | ||||
|                     ; 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 | ||||
|                 `);
 | ||||
|                 break; | ||||
|             case "jump_zero": | ||||
|                 this.add(` | ||||
|                     ; jump_zero | ||||
|                     cmp ${ins.register} | ||||
|                     jz .L${(() => { | ||||
|                         const dest = instructions[ins.location]; | ||||
|                         if (!dest.jumpedTo) | ||||
|                             throw new Error("impossible"); | ||||
|                         return dest.label; | ||||
|                     })()} | ||||
|                 `);
 | ||||
|                 break; | ||||
|             case "jump": | ||||
|                 this.add(` | ||||
|                     ; jump | ||||
|                     jz .L${(() => { | ||||
|                         const dest = instructions[ins.location]; | ||||
|                         if (!dest.jumpedTo) | ||||
|                             throw new Error("impossible"); | ||||
|                         return dest.label; | ||||
|                     })()} | ||||
|                 `);
 | ||||
|                 break; | ||||
|             default: | ||||
|                 assertExhaustive(ins); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     private add(lines: string) { | ||||
|         this.result += lines | ||||
|             .trim() | ||||
|             .split("\n") | ||||
|             .map(s => s.replace(/^(    )+/, "    ")) | ||||
|             .join("\n") | ||||
|             + "\n"; | ||||
|     } | ||||
| 
 | ||||
|     private asmWithHeaders(asm: string) { | ||||
|         return ` | ||||
|             bits 64 | ||||
|             global _start | ||||
| 
 | ||||
|             _start: | ||||
|             \t${asm} | ||||
|             exit: | ||||
|             \tmov rdi, rax | ||||
|             \tmov rax, 60 | ||||
|             \tsyscall | ||||
| 
 | ||||
|         `.replace(/ /g, "").replace(/\t/g, "    ");
 | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,89 +0,0 @@ | ||||
| Arguments:  | ||||
|   /usr/bin/node /usr/bin/yarn add ts-done | ||||
| 
 | ||||
| PATH:  | ||||
|   /home/pieter/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/pieter/.cargo/bin:/home/pieter/.yarn/bin:/home/pieter/.cabal/bin:/home/pieter/.ghcup/bin | ||||
| 
 | ||||
| Yarn version:  | ||||
|   1.22.19 | ||||
| 
 | ||||
| Node version:  | ||||
|   20.4.0 | ||||
| 
 | ||||
| Platform:  | ||||
|   linux x64 | ||||
| 
 | ||||
| Trace:  | ||||
|   Error: https://registry.yarnpkg.com/ts-done: Not found | ||||
|       at params.callback [as _callback] (/usr/lib/node_modules/yarn/lib/cli.js:66145:18) | ||||
|       at self.callback (/usr/lib/node_modules/yarn/lib/cli.js:140890:22) | ||||
|       at Request.emit (node:events:512:28) | ||||
|       at Request.<anonymous> (/usr/lib/node_modules/yarn/lib/cli.js:141862:10) | ||||
|       at Request.emit (node:events:512:28) | ||||
|       at IncomingMessage.<anonymous> (/usr/lib/node_modules/yarn/lib/cli.js:141784:12) | ||||
|       at Object.onceWrapper (node:events:626:28) | ||||
|       at IncomingMessage.emit (node:events:524:35) | ||||
|       at endReadableNT (node:internal/streams/readable:1378:12) | ||||
|       at process.processTicksAndRejections (node:internal/process/task_queues:82:21) | ||||
| 
 | ||||
| npm manifest:  | ||||
|   { | ||||
|     "scripts": { | ||||
|       "build": "nearleyc grammar.ne -o grammar.out.js" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|       "moo": "^0.5.2", | ||||
|       "nearley": "^2.20.1" | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| yarn manifest:  | ||||
|   No manifest | ||||
| 
 | ||||
| Lockfile:  | ||||
|   # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. | ||||
|   # yarn lockfile v1 | ||||
|    | ||||
|    | ||||
|   commander@^2.19.0: | ||||
|     version "2.20.3" | ||||
|     resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" | ||||
|     integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== | ||||
|    | ||||
|   discontinuous-range@1.0.0: | ||||
|     version "1.0.0" | ||||
|     resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" | ||||
|     integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ== | ||||
|    | ||||
|   moo@^0.5.0, moo@^0.5.2: | ||||
|     version "0.5.2" | ||||
|     resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" | ||||
|     integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== | ||||
|    | ||||
|   nearley@^2.20.1: | ||||
|     version "2.20.1" | ||||
|     resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" | ||||
|     integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== | ||||
|     dependencies: | ||||
|       commander "^2.19.0" | ||||
|       moo "^0.5.0" | ||||
|       railroad-diagrams "^1.0.0" | ||||
|       randexp "0.4.6" | ||||
|    | ||||
|   railroad-diagrams@^1.0.0: | ||||
|     version "1.0.0" | ||||
|     resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" | ||||
|     integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A== | ||||
|    | ||||
|   randexp@0.4.6: | ||||
|     version "0.4.6" | ||||
|     resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" | ||||
|     integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== | ||||
|     dependencies: | ||||
|       discontinuous-range "1.0.0" | ||||
|       ret "~0.1.10" | ||||
|    | ||||
|   ret@~0.1.10: | ||||
|     version "0.1.15" | ||||
|     resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" | ||||
|     integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== | ||||
| @ -154,6 +154,11 @@ ts-node@^10.9.1: | ||||
|     v8-compile-cache-lib "^3.0.1" | ||||
|     yn "3.1.1" | ||||
| 
 | ||||
| ts-results@^3.3.0: | ||||
|   version "3.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/ts-results/-/ts-results-3.3.0.tgz#68623a6c18e65556287222dab76498a28154922f" | ||||
|   integrity sha512-FWqxGX2NHp5oCyaMd96o2y2uMQmSu8Dey6kvyuFdRJ2AzfmWo3kWa4UsPlCGlfQ/qu03m09ZZtppMoY8EMHuiA== | ||||
| 
 | ||||
| typescript@^5.1.6: | ||||
|   version "5.1.6" | ||||
|   resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user