fix comptime specialization, implement io.print

This commit is contained in:
2026-07-15 08:24:50 +02:00
parent 0b2055d64b
commit 1165cfb7c0
7 changed files with 733 additions and 155 deletions
+47 -34
View File
@@ -130,13 +130,13 @@ hide FormatToken :: struct {
kind FormatTokenKind
start usize
end usize
arg usize
field []u8
}
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}
tokens[index] = FormatToken {kind = .unused, start = 0, end = 0, field = ""}
}
field_count usize = 0
match typeinfo!(Args) {
@@ -160,12 +160,12 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
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}
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, field = ""}
token_count += 1
}
next :: format[cursor + 1]
if next == '{' {
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, arg = 0}
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, field = ""}
token_count += 1
cursor += 2
literal_start = cursor
@@ -182,7 +182,15 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
} else {
compile_error!("io.print format has an unknown specifier")
}
tokens[token_count] = FormatToken {kind = kind, start = 0, end = 0, arg = argument_count}
if argument_count >= field_count {
compile_error!("io.print format argument count does not match the tuple")
}
tokens[token_count] = FormatToken {
kind = kind,
start = 0,
end = 0,
field = format_field_name(Args, argument_count),
}
token_count += 1
argument_count += 1
cursor += 3
@@ -194,10 +202,10 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
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}
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = cursor, field = ""}
token_count += 1
}
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, arg = 0}
tokens[token_count] = FormatToken {kind = .literal, start = cursor, end = cursor + 1, field = ""}
token_count += 1
cursor += 2
literal_start = cursor
@@ -206,7 +214,7 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
cursor += 1
}
if literal_start < format.len {
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = format.len, arg = 0}
tokens[token_count] = FormatToken {kind = .literal, start = literal_start, end = format.len, field = ""}
}
if argument_count != field_count {
compile_error!("io.print format argument count does not match the tuple")
@@ -214,39 +222,44 @@ hide parse_format func($N usize, $format []u8, $Args type) [N]mut FormatToken {
return tokens
}
hide format_field_name func($T type, index usize) []u8 {
match typeinfo!(T) {
.record |record|: return record.fields[index].name
else: compile_error!("io.print arguments must be a tuple")
}
}
hide write_decimal func(writer Writer, $T type, value T) void ! WriteError {
match typeinfo!(T) {
.integer: if minval!(T) < 0 {
try write_decimal_signed(writer, i64(value))
} else {
try write_decimal_unsigned(writer, u64(value))
}
else: compile_error!("io.print '{d}' requires an integer argument")
}
return
}
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")
}
}
}
}
}
inline for parse_format(format.len, format, Args) |token| {
if (token.kind == .unused) {
break
}
else: compile_error!("io.print arguments must be a tuple")
if (token.kind == .literal) {
try write_all(writer, format[token.start..token.end])
continue
}
if (token.kind == .string) {
try write_all(writer, field!(args, token.field))
continue
}
try write_decimal(writer, field!(args, token.field))
}
return
}