reflection foundation, tuples, debug.print

This commit is contained in:
2026-07-14 23:45:30 +02:00
parent 267947e79d
commit 0b2055d64b
24 changed files with 126598 additions and 115394 deletions
+36
View File
@@ -0,0 +1,36 @@
c :: import "@ffi/c"
io :: import "@std/io"
hide write func(_ ?*mut anyopaque, _ io.WriteStream, bytes []u8) usize ! io.WriteError {
request usize = bytes.len
maximum usize :: usize(maxval!(c_long))
if request > maximum {
request = maximum
}
while true {
count c_long :: c.write(2, bytes.ptr, c_ulong(request))
if count >= 0 {
return usize(count)
}
if c.__error()^ != 4 {
return .write_failed
}
}
}
hide read func(_ ?*mut anyopaque, _ io.ReadStream, _ []mut u8) usize ! io.ReadError {
return .read_failed
}
hide vtable io.IoVTable :: io.IoVTable {
read = read,
write = write,
}
print func($format []u8, $Args type, args Args) void {
writer io.Writer :: io.Writer {
impl = io.Io {context = none, vtable = &vtable},
stream = .stderr,
}
io.print(writer, format, Args, args) catch |_| {}
}
+177
View File
@@ -1,4 +1,5 @@
c :: import "@ffi/c"
meta :: import "@std/meta"
ReadError :: enum {
read_failed
@@ -74,6 +75,182 @@ write_all func(writer Writer, bytes []u8) void ! WriteError {
return
}
hide write_decimal_signed func(writer Writer, value i64) void ! WriteError {
buffer [21]mut u8 = undefined
end usize = buffer.len
current i64 = value
while true {
digit_value i64 :: rem!(current, 10)
digit u8 = 0
if digit_value < 0 {
digit = u8(-digit_value)
} else {
digit = u8(digit_value)
}
end -= 1
buffer[end] = '0' + digit
current = divtrunc!(current, 10)
if current == 0 {
break
}
}
if value < 0 {
end -= 1
buffer[end] = '-'
}
try write_all(writer, buffer[end..])
return
}
hide write_decimal_unsigned func(writer Writer, value u64) void ! WriteError {
buffer [21]mut u8 = undefined
end usize = buffer.len
current u64 = value
while true {
digit u8 :: u8(rem!(current, 10))
end -= 1
buffer[end] = '0' + digit
current = divtrunc!(current, 10)
if current == 0 {
break
}
}
try write_all(writer, buffer[end..])
return
}
hide FormatTokenKind :: enum {
unused
literal
string
decimal
}
hide FormatToken :: struct {
kind FormatTokenKind
start usize
end usize
arg usize
}
hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
tokens [N]mut FormatToken = undefined
for (usize(0))..format.len |index| {
tokens[index] = FormatToken {kind = .unused, start = 0, end = 0, arg = 0}
}
field_count usize = 0
match typeinfo!(Args) {
.record |record|: {
if !record.is_tuple {
compile_error!("io.print arguments must be a tuple")
}
field_count = record.fields.len
}
else: compile_error!("io.print arguments must be a tuple")
}
token_count usize = 0
argument_count usize = 0
literal_start usize = 0
cursor usize = 0
while cursor < format.len {
byte :: format[cursor]
if byte == '{' {
if cursor + 1 >= format.len {
compile_error!("io.print format has an unmatched '{'")
}
if cursor > literal_start {
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, arg = 0}
token_count += 1
}
next :: format[cursor + 1]
if next == '{' {
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, arg = 0}
token_count += 1
cursor += 2
literal_start = cursor
continue
}
if cursor + 2 >= format.len or format[cursor + 2] != '}' {
compile_error!("io.print format expects '{s}' or '{d}'")
}
kind FormatTokenKind = .unused
if next == 's' {
kind = .string
} else if next == 'd' {
kind = .decimal
} else {
compile_error!("io.print format has an unknown specifier")
}
tokens[token_count] = FormatToken {kind = kind, start = 0, end = 0, arg = argument_count}
token_count += 1
argument_count += 1
cursor += 3
literal_start = cursor
continue
}
if byte == '}' {
if cursor + 1 >= format.len or format[cursor + 1] != '}' {
compile_error!("io.print format has an unmatched '}'")
}
if cursor > literal_start {
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, arg = 0}
token_count += 1
}
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, arg = 0}
token_count += 1
cursor += 2
literal_start = cursor
continue
}
cursor += 1
}
if literal_start < format.len {
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = format.len, arg = 0}
}
if argument_count != field_count {
compile_error!("io.print format argument count does not match the tuple")
}
return tokens
}
print func(
writer Writer,
$format []u8,
$Args type,
args Args,
) void ! WriteError {
match typeinfo!(Args) {
.record |record|: inline for $parse_format(format.len, format, Args) |token| {
if (token.kind == .literal) {
try write_all(writer, format[token.start..token.end])
}
if (token.kind == .string or token.kind == .decimal) {
inline for record.fields |field| {
if (field.index == token.arg) {
if (token.kind == .string) {
try write_all(writer, field!(args, field.name))
} else {
match typeinfo!(field.type) {
.integer: {
if minval!(field.type) < 0 {
try write_decimal_signed(writer, i64(field!(args, field.name)))
} else {
try write_decimal_unsigned(writer, u64(field!(args, field.name)))
}
}
else: compile_error!("io.print '{d}' requires an integer argument")
}
}
}
}
}
}
else: compile_error!("io.print arguments must be a tuple")
}
return
}
hide system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
request usize = buffer.len
maximum usize :: usize(maxval!(c_long))
+37
View File
@@ -0,0 +1,37 @@
Layout :: enum {
auto
c
}
FieldInfo :: struct {
name []u8
type type
index usize
}
RecordInfo :: struct {
name ?[]u8
fields []FieldInfo
is_tuple bool
layout Layout
}
TypeInfo :: union(enum) {
invalid void
void void
anyopaque void
bool void
integer void
float void
array void
pointer void
slice void
range void
optional void
function void
enum void
record RecordInfo
union void
fallible void
distinct void
}
+5
View File
@@ -0,0 +1,5 @@
io :: import "@std/io"
Init :: struct {
io io.Io
}