From 47ef567e108fa4d72551bb5445bd3cacf6ad9e9b Mon Sep 17 00:00:00 2001 From: Sander in 't Veld Date: Sat, 4 Nov 2023 20:34:31 +0100 Subject: [PATCH] Thinking about how to format arrays and structs. --- penne.todo | 5 +- src/generator.rs | 81 +++++++++++++++++--- tests/execution.rs | 12 ++- tests/samples/valid/builtin_file_and_line.pn | 6 ++ tests/samples/valid/builtin_print.pn | 3 + 5 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 tests/samples/valid/builtin_file_and_line.pn diff --git a/penne.todo b/penne.todo index 2578a6e..2ba0d44 100644 --- a/penne.todo +++ b/penne.todo @@ -15,7 +15,7 @@ Functionality: ✔ Pointers with automatic dereference @done (2022-09-20 14:56) ✔ Think about (disallowing) cast between pointer and integer @done (2022-12-24 18:39) The only use case I care about is debugging and maybe offsets. - ☐ printf()/format() needs a %p + ✔ printf()/format() needs a %p @done (2023-11-04 20:25) ✘ builtin function (&T, usize) -> &T @cancelled (2023-07-10 15:14) Not needed if you can cast &[]u8 to &T and &[]T ✔ Cast between pointer types (specifically from &T to &[]u8 and back) @done (2023-08-05 17:40) @@ -241,7 +241,7 @@ Needed for Advent of Code: ✔ Allocate on the stack with len = snprintf(1) and BuildArrayAlloca @done (2023-10-22 18:16) ☐ Allow assigning to variable ☐ dbg!() using builtin magic - ☐ file!() and line!() using builtin magic + ✔ file!() and line!() using builtin magic @done (2023-11-04 20:27) ✘ Equivalent of assert!() @cancelled (2023-09-10 19:55) ✘ Parser macros @cancelled (2023-09-10 19:41) ☐ panic!() using builtin magic @@ -259,5 +259,6 @@ Needed for Advent of Code: ✘ Generic structs (for Vec and String and fmt) @cancelled (2023-07-09 22:58) ☐ Regex? ☐ plan9 regexp9.c from plan9port? + ☐ Debug symbols ☐ Performance ☐ (And Makefile for automated testing, if just running the test is not enough) diff --git a/src/generator.rs b/src/generator.rs index d6c1eb9..68a6033 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1819,6 +1819,27 @@ fn generate_global_cstr( Ok(result) } +fn generate_global_nstr_and_len( + nstr: &str, + llvm: &mut Generator, +) -> Result<(LLVMValueRef, usize), anyhow::Error> +{ + let bytes = nstr.as_bytes(); + let len = bytes.len(); + let string = generate_global_string_literal(bytes, llvm)?; + let result = unsafe { + let mut indices = [llvm.const_i32(0), llvm.const_i32(0)]; + LLVMBuildGEP( + llvm.builder, + string, + indices.as_mut_ptr(), + indices.len() as u32, + cstr!(""), + ) + }; + Ok((result, len)) +} + fn generate_structure_literal( structural_type: &ValueType, members: &[MemberExpression], @@ -2192,6 +2213,34 @@ struct FormatBuffer impl FormatBuffer { + fn add_user_text( + &mut self, + text: &str, + llvm: &mut Generator, + ) -> Result<(), anyhow::Error> + { + let bytes = text.as_bytes(); + if bytes.iter().all(is_snprintf_safe) + { + self.format.extend(bytes); + } + else + { + let (value, len) = generate_global_nstr_and_len(text, llvm)?; + self.add_specifier("%.*s"); + self.insert(llvm.const_usize(len)); + self.insert(value); + } + Ok(()) + } + + fn add_text(&mut self, text: &'static str) + { + let bytes = text.as_bytes(); + assert!(bytes.iter().all(is_snprintf_safe)); + self.format.extend(bytes); + } + fn add_specifier(&mut self, specifier: &'static str) { self.format.extend(specifier.as_bytes()); @@ -2203,18 +2252,21 @@ impl FormatBuffer } } -fn generate_format( - arguments: &[Expression], - llvm: &mut Generator, -) -> Result +fn is_snprintf_safe(byte: &u8) -> bool { - let is_snprintf_safe = |&byte| match byte + match byte { b'%' => false, b'\0' => false, _ => true, - }; + } +} +fn generate_format( + arguments: &[Expression], + llvm: &mut Generator, +) -> Result +{ let mut format_buffer = FormatBuffer::default(); for argument in arguments { @@ -2686,9 +2738,10 @@ fn format_endless( } else { - let text = CString::new("...")?; - let placeholder = generate_global_cstr(text, llvm)?; - buffer.add_specifier("%s"); + let text = "..."; + let (placeholder, len) = generate_global_nstr_and_len(&text, llvm)?; + buffer.add_specifier("%.*s"); + buffer.insert(llvm.const_usize(len)); buffer.insert(placeholder); Ok(()) } @@ -2701,8 +2754,14 @@ fn format_struct( buffer: &mut FormatBuffer, ) -> Result<(), anyhow::Error> { - // TODO pretty print - todo!() + buffer.add_user_text(&struct_name.name, llvm)?; + buffer.add_text(" {"); + + let sname = CString::new(&struct_name.name as &str)?; + let struct_type = unsafe { LLVMGetTypeByName(llvm.module, sname.as_ptr()) }; + + buffer.add_text("}"); + Ok(()) } impl Generatable for builtin::Fd diff --git a/tests/execution.rs b/tests/execution.rs index 86107bd..7814a82 100644 --- a/tests/execution.rs +++ b/tests/execution.rs @@ -632,11 +632,21 @@ fn execute_builtin_print() -> Result<(), anyhow::Error> stdout, "Hello world!\nHello Alice!\nHello a\nb!\nHello 255!\nHello \ -173!\nHello true!\nHello false!\nHello 32000!\nHello 0x7f00!\nHello \ - world!\n" + world!\nHello ...!\n" ); Ok(()) } +#[test] +fn execute_builtin_file_and_line() -> Result<(), anyhow::Error> +{ + let filename = "tests/samples/valid/builtin_file_and_line.pn"; + let output = execute(filename)?; + let stdout = stdout_from_output(output)?; + assert_eq!(stdout, format!("Hello from {filename}:4!\n")); + Ok(()) +} + #[test] fn execute_builtin_format() -> Result<(), anyhow::Error> { diff --git a/tests/samples/valid/builtin_file_and_line.pn b/tests/samples/valid/builtin_file_and_line.pn new file mode 100644 index 0000000..0bec1cf --- /dev/null +++ b/tests/samples/valid/builtin_file_and_line.pn @@ -0,0 +1,6 @@ + +fn main() -> u8 +{ + print!("Hello from ", file!(), ":", line!(), "!\n"); + return: 0 +} diff --git a/tests/samples/valid/builtin_print.pn b/tests/samples/valid/builtin_print.pn index 7273697..85ed89b 100644 --- a/tests/samples/valid/builtin_print.pn +++ b/tests/samples/valid/builtin_print.pn @@ -28,5 +28,8 @@ fn main() -> u8 var world_with_nul = "world\0"; var cstr: &[...]char8 = cast &world_with_nul; print!("Hello ", cstr, "!\n"); + with_endless_placeholder: + var endless: &[...]u8 = cast &world_with_nul; + print!("Hello ", endless, "!\n"); return: 0 }