diff --git a/.gitignore b/.gitignore index ea8c4bf..b0d7b19 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +out* \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 68ca38b..9135491 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,6 +77,7 @@ enum Operation Apply(String, String, i32, i32), Depth(i32, i32), QueueDiagnostic(i32, i32), + Interrupt(i32, i32), } fn main() @@ -92,6 +93,7 @@ fn main() ("<", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))), (">", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))), (">=", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))), + ("<=", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))), ("==", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))), ("!=", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))), ("&&", (Vec::from([Datatype::Bool, Datatype::Bool]), Vec::from([Datatype::Bool]))), @@ -246,7 +248,7 @@ fn merge_assemblies(data: &mut AssemblyData, data2: AssemblyData) 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 r11, r12\n\tcmove r11, r13\n\tcmove r12, r13\n"; +const ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE: &str = "\tcmp r12, r13\n\tcmove r12, r14\n\tcmove r13, r14\n"; const ASSEMBLY_LINUX_X64_EXIT: &str = "\tmov rax, 60\n\tmov rdi, 0\n\tsyscall\n"; const ASSEMBLY_LINUX_X64_DYNAMIC_DATA_LENGTH: u32 = 16384; @@ -255,14 +257,15 @@ fn generate_assembly_linux_x64(operations: &Vec, functions: &Vec, functions: &Vec, functions: &Vec, functions: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, arrays: &Vec, debug: bool) -> AssemblyData { @@ -325,7 +328,7 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve Operation::Dequeue(line, col) => { data.code += format!("\t;;deq {}:{}\n", line, col).as_str(); - data.code += "\tinc r11\n"; + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; } Operation::Enqueue(datatype, value, line, col) => @@ -335,53 +338,56 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve { Datatype::Int => { - data.code += format!("\tmov qword [queue+8*r12], {}\n", value).as_str(); + data.code += format!("\tmov qword [queue+8*r13], {}\n", value).as_str(); } Datatype::Bool => { - data.code += format!("\tmov qword [queue+8*r12], {}\n", if value == "true" { 1 } else { 0 }).as_str(); + data.code += format!("\tmov qword [queue+8*r13], {}\n", if value == "true" { 1 } else { 0 }).as_str(); } Datatype::String => { - data.strings += format!("\tstr_{}_{}: db {}, \"{}\", 0\n", line, col, value.len() + 1, value).as_str(); + data.strings += format!("\tstr_{}_{}: dq {}, \"{}\", 0\n", line, col, value.len(), value).as_str(); data.code += format!("\tlea rax, [str_{}_{}]\n", line, col).as_str(); - data.code += "\tmov [queue+8*r12], rax\n"; + data.code += "\tmov [queue+8*r13], rax\n"; } } - data.code += "\tinc r12\n"; + data.code += "\tinc r13\n"; } Operation::Requeue(line, col) => { data.code += format!("\t;;req {}:{}\n", line, col).as_str(); - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tmov [queue+8*r12], rax\n"; - data.code += "\tinc r11\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tmov [queue+8*r13], rax\n"; + data.code += "\tinc r12\n"; + data.code += "\tinc r13\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+8*r11], 0\n"; + data.code += "\tcmp qword [queue+8*r12], 0\n"; data.code += format!("\tje while_{}_{}_end\n", line, col).as_str(); - data.code += "\tinc r11\n"; + data.code += format!("while_{}_{}:\n", line, col).as_str(); + data.code += "\tinc r12\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 += "\tcmp qword [queue+8*r12], 0\n"; + data.code += format!("\tjne while_{}_{}\n", line, col).as_str(); data.code += format!("while_{}_{}_end:\n", line, col).as_str(); - data.code += "\tinc r11\n"; + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; + } Operation::If(if_operations, maybe_else_operations, line, col) => { data.code += format!("\t;;if {}:{}\n", line, col).as_str(); - data.code += "\tcmp qword [queue+8*r11], 0\n"; - data.code += format!("\tjne else_{}_{}\n", line, col).as_str(); - data.code += "\tinc r11\n"; + data.code += "\tcmp qword [queue+8*r12], 0\n"; + data.code += format!("\tje else_{}_{}\n", line, col).as_str(); + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; merge_assemblies(&mut data, generate_assembly_linux_x64_block(if_operations, functions, intrinsics, arrays, debug)); data.code += format!("\tjmp if_{}_{}_end\n", line, col).as_str(); data.code += format!("else_{}_{}:\n", line, col).as_str(); - data.code += "\tinc r11\n"; + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; if let Some(else_operations) = maybe_else_operations { @@ -392,9 +398,9 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve Operation::Dup(line, col) => { data.code += format!("\t;;dup {}:{}\n", line, col).as_str(); - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tmov [queue+8*r12], rax\n"; - data.code += "\tinc r12\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tmov [queue+8*r13], rax\n"; + data.code += "\tinc r13\n"; } Operation::Intrinsic(name, line, col) => { @@ -407,7 +413,7 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve data.code += "\tmov rax, 1\n"; data.code += "\tmov rdi, 1\n"; // load address - data.code += "\tmov rsi, [queue+8*r11]\n"; + data.code += "\tmov rsi, [queue+8*r12]\n"; // size data.code += "\tmov rdx, [rsi]\n"; // data @@ -416,7 +422,7 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve data.code += "\tinc rdx\n"; data.code += "\tsyscall\n"; // TODO: factor this out - data.code += "\tinc r11\n"; + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; } "println" => @@ -425,7 +431,7 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve data.code += "\tmov rax, 1\n"; data.code += "\tmov rdi, 1\n"; // load address - data.code += "\tmov rsi, [queue+8*r11]\n"; + data.code += "\tmov rsi, [queue+8*r12]\n"; // size data.code += "\tmov rdx, [rsi]\n"; // data @@ -434,76 +440,80 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve data.code += "\tinc rdx\n"; data.code += "\tsyscall\n"; // TODO: factor this out - data.code += "\tinc r11\n"; + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; // TODO: Don't syscall twice + data.code += "\tmov rax, 1\n"; data.code += "\tlea rsi, [newline]\n"; data.code += "\tmov rdx, 1\n"; data.code += "\tsyscall\n"; } "intToStr" => { - data.code += "\tmov qword rdi, [queue+8*r11]\n"; - data.code += "\tinc r11\n"; - data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; - data.code += "\tcall intToStr\n"; - data.code += "\tmov [queue+8*r12], rax\n"; + data.code += "\tmov qword rdi, [queue+8*r12]\n"; data.code += "\tinc r12\n"; + data.code += "\tcall intToStr\n"; + data.code += "\tmov [queue+8*r13], rax\n"; + data.code += "\tinc r13\n"; } "-" => { - data.code += "\tmov qword rax, [queue+8*r11]\n"; - data.code += "\tinc r11\n"; - data.code += "\tmov qword rbx, [queue+8*r11]\n"; - data.code += "\tinc r11\n"; + data.code += "\tmov qword rax, [queue+8*r12]\n"; + data.code += "\tinc r12\n"; + data.code += "\tmov qword rbx, [queue+8*r12]\n"; + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; data.code += "\tsub rax, rbx\n"; - data.code += "\tmov [queue+8*r12], rax\n"; - data.code += "\tinc r12\n"; + data.code += "\tmov [queue+8*r13], rax\n"; + data.code += "\tinc r13\n"; } ">" => { - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tcmp qword rax, [queue+8*r11+1]\n"; - data.code += "\txor rax, rax\n"; - data.code += "\tsetg al\n"; - data.code += "\tmov qword [queue+8*r12], rax\n"; - data.code += "\tadd r11, 2\n"; + data.code += "\tmov rbx, 0\n"; + data.code += "\tmov rcx, 1\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tcmp qword rax, [queue+8*r12+8]\n"; + data.code += "\tcmovg rbx, rcx\n"; + data.code += "\tmov qword [queue+8*r13], rbx\n"; + data.code += "\tadd r12, 2\n"; + data.code += "\tinc r13\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; - data.code += "\tinc r12\n"; } "<" => { - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tcmp qword rax, [queue+8*r11+1]\n"; - data.code += "\txor rax, rax\n"; - data.code += "\tsetl al\n"; - data.code += "\tmov qword [queue+8*r12], rax\n"; - data.code += "\tadd r11, 2\n"; + data.code += "\tmov rbx, 0\n"; + data.code += "\tmov rcx, 1\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tcmp qword rax, [queue+8*r12+8]\n"; + data.code += "\tcmovl rbx, rcx\n"; + data.code += "\tmov qword [queue+8*r13], rbx\n"; + data.code += "\tadd r12, 2\n"; + data.code += "\tinc r13\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; - data.code += "\tinc r12\n"; } ">=" => { - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tcmp qword rax, [queue+8*r11+1]\n"; - data.code += "\txor rax, rax\n"; - data.code += "\tsetge al\n"; - data.code += "\tmov qword [queue+8*r12], rax\n"; - data.code += "\tadd r11, 2\n"; + data.code += "\tmov rbx, 0\n"; + data.code += "\tmov rcx, 1\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tcmp qword rax, [queue+8*r12+8]\n"; + data.code += "\tcmovge rbx, rcx\n"; + data.code += "\tmov qword [queue+8*r13], rbx\n"; + data.code += "\tadd r12, 2\n"; + data.code += "\tinc r13\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; - data.code += "\tinc r12\n"; } "<=" => { - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tcmp qword rax, [queue+8*r11+1]\n"; - data.code += "\txor rax, rax\n"; - data.code += "\tsetle al\n"; - data.code += "\tmov qword [queue+8*r12], rax\n"; - data.code += "\tadd r11, 2\n"; + data.code += "\tmov rbx, 0\n"; + data.code += "\tmov rcx, 1\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tcmp qword rax, [queue+8*r12+8]\n"; + data.code += "\tcmovle rbx, rcx\n"; + data.code += "\tmov qword [queue+8*r13], rbx\n"; + data.code += "\tadd r12, 2\n"; + data.code += "\tinc r13\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; - data.code += "\tinc r12\n"; } _ => todo!("intrinsic {} {}:{}", name, line, col) } @@ -515,19 +525,19 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve { "read" => { - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tinc r11\n"; - data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; - data.code += format!("\tmov qword [queue+8*r12], [arr_{}+rax]\n", name).as_str(); + data.code += "\tmov rax, [queue+8*r12]\n"; data.code += "\tinc r12\n"; + data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; + data.code += format!("\tmov qword [queue+8*r13], [arr_{}+rax]\n", name).as_str(); + data.code += "\tinc r13\n"; } "write" => { - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tinc r11\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; - data.code += format!("\tmov qword [arr_{}+rax], [queue+8*r11]\n", name).as_str(); - data.code += "\tinc r11\n"; + data.code += format!("\tmov qword [arr_{}+rax], [queue+8*r12]\n", name).as_str(); + data.code += "\tinc r12\n"; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE; } _ => todo!("apply {}", word) @@ -539,36 +549,42 @@ fn generate_assembly_linux_x64_block(operations: &Vec, functions: &Ve let function = functions.iter().find(|x| &x.name == name).unwrap(); for _ in 0..function.ins.len() { - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tmov [queue+8*r12], rax\n"; - data.code += "\tinc r11\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tmov [queue+8*r13], rax\n"; data.code += "\tinc r12\n"; + data.code += "\tinc r13\n"; } data.code += "\t;; move pointers\n"; // save the current base - data.code += "\tmov rbx, r13\n"; + data.code += "\tpush r14\n"; // save the current head - data.code += "\tmov r15, r11\n"; + data.code += "\tpush r12\n"; // prepare the layout - data.code += "\tmov r13, r12\n"; - data.code += format!("\tsub r13, {}\n", function.ins.len()).as_str(); - data.code += "\tmov r11, r13\n"; + data.code += "\tmov r14, r13\n"; + data.code += format!("\tsub r14, {}\n", function.ins.len()).as_str(); + data.code += "\tmov r12, r14\n"; // call data.code += format!("\tcall {}\n", name).as_str(); // move the sub-queue back to the base for _ in 0..function.outs.len() { - data.code += "\tmov rax, [queue+8*r11]\n"; - data.code += "\tmov [queue+8*r13], rax\n"; - data.code += "\tinc r11\n"; - data.code += "\tinc r13\n"; + data.code += "\tmov rax, [queue+8*r12]\n"; + data.code += "\tmov [queue+8*r14], rax\n"; + data.code += "\tinc r12\n"; + data.code += "\tinc r14\n"; } // restore the tail - data.code += "\tmov r12, r13\n"; - // restore the base - data.code += "\tmov r13, rbx\n"; + data.code += "\tmov r13, r14\n"; // restore the head - data.code += "\tmov r11, r15\n"; + data.code += "\tpop r12\n"; + // restore the base + data.code += "\tpop r14\n"; + } + Operation::Interrupt(line, col) => + { + data.code += "lea r8, [queue]\n"; + data.code += format!("mov r9, {}\n", 1000*line + col).as_str(); + data.code += "int3\n"; } _ => todo!("{:?}", operation) } @@ -684,17 +700,23 @@ fn interpret_program(operations: &Vec, queue: &mut Vec, funct let second = queue.remove(0).parse::().unwrap(); queue.push((first > second).to_string()); } + "<" => + { + let first = queue.remove(0).parse::().unwrap(); + let second = queue.remove(0).parse::().unwrap(); + queue.push((first < second).to_string()); + } ">=" => { let first = queue.remove(0).parse::().unwrap(); let second = queue.remove(0).parse::().unwrap(); queue.push((first >= second).to_string()); } - "<" => + "<=" => { let first = queue.remove(0).parse::().unwrap(); let second = queue.remove(0).parse::().unwrap(); - queue.push((first < second).to_string()); + queue.push((first <= second).to_string()); } "==" => { @@ -802,6 +824,7 @@ fn interpret_program(operations: &Vec, queue: &mut Vec, funct { println!("---Queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, queue.len(), queue); } + Operation::Interrupt(_, _) => {} } if debug { @@ -848,6 +871,7 @@ fn typecheck_block(operations: &Vec, ins: &Vec, outs: &Vec< { match operation { + Operation::Interrupt(line, col) | Operation::Enqueue(_, _, line, col) | Operation::Dequeue(line, col) | Operation::Requeue(line, col) | @@ -1026,6 +1050,7 @@ fn get_return_type(operations: &Vec, ins: &Vec, functions: { println!("---Type queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, type_queue.len(), type_queue); } + Operation::Interrupt(_, _) => {} Operation::Apply(name, word, line, col) => { match word.as_str() @@ -1097,7 +1122,7 @@ fn validate_function_calls_in_block(block: &Vec, functions: &Vec {}, + Operation::Requeue(_, _) | Operation::Dup(_, _) | Operation::Swap(_, _) | Operation::Apply(_, _, _, _) | Operation::Interrupt(_, _) => {}, Operation::FunctionCall(function_name, line, col) => { if !functions.iter().any(|x| &x.name == function_name) @@ -1449,6 +1474,10 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable>, in { operations.push(Operation::QueueDiagnostic(*line, *col)); } + else if word == "interrupt" + { + operations.push(Operation::Interrupt(*line, *col)); + } else if Some(word.as_str()) == delimiter { return Ok(operations);