diff --git a/src/main.rs b/src/main.rs index 0555950..badbbe7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use core::panic; use std::collections::HashMap; use std::env; +use std::fmt::format; use std::fs; use std::iter::Peekable; use std::process::exit; @@ -218,9 +219,147 @@ fn compile(file_content: &String, intrinsics: &HashMap<&str, (Vec, Vec { None }; + if !interpret + { + match generate_assembly_linux_x64(&operations, &functions, &intrinsics, &arrays, debug) + { + Ok(()) => return Ok(None), + Err(error) => return Err(error.to_string()), + } + } return Ok(output); } +struct AssemblyData +{ + strings: String, + code: String, + arrays: String, +} + +fn merge_assemblies(data: &mut AssemblyData, data2: AssemblyData) +{ + data.arrays += data2.arrays.as_str(); + data.code += data2.code.as_str(); + data.strings += data2.strings.as_str(); +} + +const ASSEMBLY_LINUX_X64_QUEUE_LENGTH: u32 = 1024; +const ASSEMBLY_LINUX_X64_HEADER: &str = "format ELF64 executable 3\n"; +const ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE: &str = "\tcmp r8, r9\n\tcmove r8, r10\n\tcmove r9, r10\n"; +const ASSEMBLY_LINUX_X64_EXIT: &str = "\tmov rax, 60\n\tmov rdi, 0\n\tsyscall\n"; + +fn generate_assembly_linux_x64(operations: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, arrays: &Vec, debug: bool) -> Result<(), std::io::Error> +{ + let mut data = AssemblyData + { + arrays: format!("segment readable writeable\n\tqueue: rq {}\n", ASSEMBLY_LINUX_X64_QUEUE_LENGTH), + strings: String::from("segment readable\n"), + code: String::from("segment executable\n"), + }; + for array in arrays + { + data.arrays += format!("\tarr_{}: rq {}\n", array.name, array.length).as_str(); + } + for function in functions + { + merge_assemblies(&mut data, generate_assembly_linux_x64_function(function.name.as_str(), &function.content, functions, intrinsics, arrays, debug)); + } + merge_assemblies(&mut data, generate_assembly_linux_x64_function("_start", operations, functions, intrinsics, arrays, debug)); + data.code += ASSEMBLY_LINUX_X64_EXIT; + + return fs::write("out.asm", format!("{}{}{}{}", ASSEMBLY_LINUX_X64_HEADER, data.code, data.arrays, data.strings)); +} + +// r8: head +// r9: tail +// r10: base + +fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, arrays: &Vec, debug: bool) -> AssemblyData +{ + let mut data = AssemblyData + { + arrays: String::new(), + code: String::new(), + strings: String::new(), + }; + for operation in operations + { + match operation + { + Operation::Dequeue(line, col) => + { + data.code += format!("\t;;deq {}:{}\n", line, col).as_str(); + data.code += "\tinc r8\n"; + data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; + } + Operation::Enqueue(datatype, value, line, col) => + { + data.code += format!("\t;;enq {:?} {} {}:{}\n", datatype, value, line, col).as_str(); + match datatype + { + Datatype::Int | Datatype::Bool => + { + data.code += format!("\tmov qword [queue+r9], {}\n", value).as_str(); + } + _ => todo!("enq {:?}", datatype) + } + data.code += "\tinc r9\n"; + } + Operation::While(while_operations, line, col) => + { + data.code += format!("\t;;while {}:{}\n", line, col).as_str(); + data.code += format!("while_{}_{}:\n", line, col).as_str(); + data.code += "\tcmp qword [queue+r8], 0\n"; + data.code += format!("\tje while_{}_{}_end\n", line, col).as_str(); + data.code += "\tinc r8\n"; + data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; + merge_assemblies(&mut data, generate_assembly_linux_x64_block(while_operations, functions, intrinsics, arrays, debug)); + data.code += format!("\tjmp while_{}_{}\n", line, col).as_str(); + data.code += format!("while_{}_{}_end:\n", line, col).as_str(); + data.code += "\tinc r8\n"; + data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; + } + Operation::Dup(line, col) => + { + data.code += format!("\t;;dup {}:{}\n", line, col).as_str(); + data.code += "\tmov qword [queue+r9], [queue+r8]\n"; + data.code += "\tinc r9\n"; + } + Operation::Intrinsic(name, line, col) => + { + data.code += format!("\t;;intrinsic {} {}:{}", name, line, col).as_str(); + match name.as_str() + { + "print" => + { + // For now printing numbers directly is unsupported + data.code += "\trax, 1\n"; + data.code += "\trdi, 1\n"; + data.code += "\tmov rsi, [queue+r8]\n"; + data.code += "\tmov " + } + _ => todo!("intrinsic {} {}:{}", name, line, col) + } + } + _ => todo!("{:?}", operation) + } + } + return data; +} + +fn generate_assembly_linux_x64_function(name: &str, operations: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, arrays: &Vec, debug: bool) -> AssemblyData +{ + let mut data = AssemblyData + { + arrays: String::new(), + code: format!("{}:\n", name), + strings: String::new(), + }; + merge_assemblies(&mut data, generate_assembly_linux_x64_block(operations, functions, intrinsics, arrays, debug)); + return data; +} + fn interpret_program(operations: &Vec, queue: &mut Vec, functions: &Vec, arrays: &mut Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, debug: bool) -> Result { let mut output = String::new();