reflection foundation, tuples, debug.print
This commit is contained in:
+177
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user