Skip to content

Commit

Permalink
Thinking about how to format arrays and structs.
Browse files Browse the repository at this point in the history
  • Loading branch information
SLiV9 committed Nov 4, 2023
1 parent 232afb4 commit 47ef567
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 14 deletions.
5 changes: 3 additions & 2 deletions penne.todo
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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)
81 changes: 70 additions & 11 deletions src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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());
Expand All @@ -2203,18 +2252,21 @@ impl FormatBuffer
}
}

fn generate_format(
arguments: &[Expression],
llvm: &mut Generator,
) -> Result<LLVMValueRef, anyhow::Error>
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<LLVMValueRef, anyhow::Error>
{
let mut format_buffer = FormatBuffer::default();
for argument in arguments
{
Expand Down Expand Up @@ -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(())
}
Expand All @@ -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
Expand Down
12 changes: 11 additions & 1 deletion tests/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>
{
Expand Down
6 changes: 6 additions & 0 deletions tests/samples/valid/builtin_file_and_line.pn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

fn main() -> u8
{
print!("Hello from ", file!(), ":", line!(), "!\n");
return: 0
}
3 changes: 3 additions & 0 deletions tests/samples/valid/builtin_print.pn
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 47ef567

Please sign in to comment.