mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 09:22:22 -07:00
582 lines
12 KiB
Odin
582 lines
12 KiB
Odin
#import "os.odin";
|
|
#import "mem.odin";
|
|
#import "utf8.odin";
|
|
|
|
PRINT_BUF_SIZE :: 1<<12;
|
|
|
|
Buffer :: struct {
|
|
data: []byte;
|
|
length: int;
|
|
}
|
|
|
|
|
|
fprint :: proc(fd: os.Handle, args: ...any) -> int {
|
|
data: [PRINT_BUF_SIZE]byte;
|
|
buf := Buffer{data[:], 0};
|
|
bprint(^buf, ...args);
|
|
os.write(fd, buf.data[:buf.length]);
|
|
return buf.length;
|
|
}
|
|
|
|
fprintln :: proc(fd: os.Handle, args: ...any) -> int {
|
|
data: [PRINT_BUF_SIZE]byte;
|
|
buf := Buffer{data[:], 0};
|
|
bprintln(^buf, ...args);
|
|
os.write(fd, buf.data[:buf.length]);
|
|
return buf.length;
|
|
}
|
|
fprintf :: proc(fd: os.Handle, fmt: string, args: ...any) -> int {
|
|
data: [PRINT_BUF_SIZE]byte;
|
|
buf := Buffer{data[:], 0};
|
|
bprintf(^buf, fmt, ...args);
|
|
os.write(fd, buf.data[:buf.length]);
|
|
return buf.length;
|
|
}
|
|
|
|
|
|
print :: proc(args: ...any) -> int {
|
|
return fprint(os.stdout, ...args);
|
|
}
|
|
println :: proc(args: ...any) -> int {
|
|
return fprintln(os.stdout, ...args);
|
|
}
|
|
printf :: proc(fmt: string, args: ...any) -> int {
|
|
return fprintf(os.stdout, fmt, ...args);
|
|
}
|
|
|
|
|
|
|
|
fprint_type :: proc(fd: os.Handle, info: ^Type_Info) {
|
|
data: [PRINT_BUF_SIZE]byte;
|
|
buf := Buffer{data[:], 0};
|
|
bprint_type(^buf, info);
|
|
os.write(fd, buf.data[:buf.length]);
|
|
}
|
|
|
|
|
|
|
|
print_byte_buffer :: proc(buf: ^Buffer, b: []byte) {
|
|
if buf.length < buf.data.count {
|
|
n := min(buf.data.count-buf.length, b.count);
|
|
if n > 0 {
|
|
copy(buf.data[buf.length:], b[:n]);
|
|
buf.length += n;
|
|
}
|
|
}
|
|
}
|
|
|
|
bprint_string :: proc(buf: ^Buffer, s: string) {
|
|
print_byte_buffer(buf, s as []byte);
|
|
}
|
|
|
|
|
|
byte_reverse :: proc(b: []byte) {
|
|
n := b.count;
|
|
for i : 0..<n/2 {
|
|
b[i], b[n-1-i] = b[n-1-i], b[i];
|
|
}
|
|
}
|
|
|
|
bprint_rune :: proc(buf: ^Buffer, r: rune) {
|
|
b, n := utf8.encode_rune(r);
|
|
bprint_string(buf, b[:n] as string);
|
|
}
|
|
|
|
bprint_space :: proc(buf: ^Buffer) { bprint_rune(buf, ' '); }
|
|
bprint_nl :: proc (buf: ^Buffer) { bprint_rune(buf, '\n'); }
|
|
|
|
__NUM_TO_CHAR_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$";
|
|
|
|
bprint_bool :: proc(buffer: ^Buffer, b: bool) {
|
|
bprint_string(buffer, if b { give "true" } else { give "false" });
|
|
}
|
|
|
|
bprint_pointer :: proc(buffer: ^Buffer, p: rawptr) #inline {
|
|
bprint_string(buffer, "0x");
|
|
bprint_u64(buffer, p as uint as u64);
|
|
}
|
|
|
|
// bprint_f16 :: proc(buffer: ^Buffer, f: f32) #inline { print__f64(buffer, f as f64, 4); }
|
|
bprint_f32 :: proc(buffer: ^Buffer, f: f32) #inline { print__f64(buffer, f as f64, 7); }
|
|
bprint_f64 :: proc(buffer: ^Buffer, f: f64) #inline { print__f64(buffer, f as f64, 16); }
|
|
bprint_u64 :: proc(buffer: ^Buffer, value: u64) {
|
|
i := value;
|
|
buf :[20]byte;
|
|
len := 0;
|
|
if i == 0 {
|
|
buf[len] = '0';
|
|
len += 1;
|
|
}
|
|
while i > 0 {
|
|
buf[len] = __NUM_TO_CHAR_TABLE[i % 10];
|
|
len += 1;
|
|
i /= 10;
|
|
}
|
|
byte_reverse(buf[:len]);
|
|
bprint_string(buffer, buf[:len] as string);
|
|
}
|
|
bprint_i64 :: proc(buffer: ^Buffer, value: i64) {
|
|
// TODO(bill): Cleanup printing
|
|
i := value;
|
|
if i < 0 {
|
|
i = -i;
|
|
bprint_rune(buffer, '-');
|
|
}
|
|
bprint_u64(buffer, i as u64);
|
|
}
|
|
|
|
/*
|
|
bprint_u128 :: proc(buffer: ^Buffer, value u128) {
|
|
a := value transmute [2]u64;
|
|
if a[1] != 0 {
|
|
bprint_u64(buffer, a[1]);
|
|
}
|
|
bprint_u64(buffer, a[0]);
|
|
}
|
|
bprint_i128 :: proc(buffer: ^Buffer, value i128) {
|
|
i := value;
|
|
if i < 0 {
|
|
i = -i;
|
|
bprint_rune(buffer, '-');
|
|
}
|
|
bprint_u128(buffer, i as u128);
|
|
}
|
|
*/
|
|
|
|
|
|
print__f64 :: proc(buffer: ^Buffer, value: f64, decimal_places: int) {
|
|
f := value;
|
|
if f == 0 {
|
|
bprint_rune(buffer, '0');
|
|
return;
|
|
}
|
|
if f < 0 {
|
|
bprint_rune(buffer, '-');
|
|
f = -f;
|
|
}
|
|
|
|
i := f as u64;
|
|
bprint_u64(buffer, i);
|
|
f -= i as f64;
|
|
|
|
bprint_rune(buffer, '.');
|
|
|
|
mult: f64 = 10.0;
|
|
while decimal_places >= 0 {
|
|
i = (f * mult) as u64;
|
|
bprint_u64(buffer, i as u64);
|
|
f -= i as f64 / mult;
|
|
mult *= 10;
|
|
decimal_places -= 1;
|
|
}
|
|
}
|
|
|
|
bprint_type :: proc(buf: ^Buffer, ti: ^Type_Info) {
|
|
if ti == nil {
|
|
return;
|
|
}
|
|
|
|
using Type_Info;
|
|
match type info : ti {
|
|
case Named:
|
|
bprint_string(buf, info.name);
|
|
case Integer:
|
|
match {
|
|
case ti == type_info(int): bprint_string(buf, "int");
|
|
case ti == type_info(uint): bprint_string(buf, "uint");
|
|
default:
|
|
bprint_string(buf, if info.signed { give "i" } else { give "u"});
|
|
bprint_u64(buf, 8*info.size as u64);
|
|
}
|
|
|
|
case Float:
|
|
match info.size {
|
|
case 4: bprint_string(buf, "f32");
|
|
case 8: bprint_string(buf, "f64");
|
|
}
|
|
case String: bprint_string(buf, "string");
|
|
case Boolean: bprint_string(buf, "bool");
|
|
case Pointer:
|
|
if info.elem == nil {
|
|
bprint_string(buf, "rawptr");
|
|
} else {
|
|
bprint_string(buf, "^");
|
|
bprint_type(buf, info.elem);
|
|
}
|
|
case Maybe:
|
|
bprint_string(buf, "?");
|
|
bprint_type(buf, info.elem);
|
|
case Procedure:
|
|
bprint_string(buf, "proc");
|
|
if info.params == nil {
|
|
bprint_string(buf, "()");
|
|
} else {
|
|
count := (info.params as ^Tuple).fields.count;
|
|
if count == 1 { bprint_string(buf, "("); }
|
|
bprint_type(buf, info.params);
|
|
if count == 1 { bprint_string(buf, ")"); }
|
|
}
|
|
if info.results != nil {
|
|
bprint_string(buf, " -> ");
|
|
bprint_type(buf, info.results);
|
|
}
|
|
case Tuple:
|
|
count := info.fields.count;
|
|
if count != 1 { bprint_string(buf, "("); }
|
|
for i : 0..<count {
|
|
if i > 0 { bprint_string(buf, ", "); }
|
|
|
|
f := info.fields[i];
|
|
|
|
if f.name.count > 0 {
|
|
bprint_string(buf, f.name);
|
|
bprint_string(buf, ": ");
|
|
}
|
|
bprint_type(buf, f.type_info);
|
|
}
|
|
if count != 1 { bprint_string(buf, ")"); }
|
|
|
|
case Array:
|
|
bprint_string(buf, "[");
|
|
bprint_i64(buf, info.count as i64);
|
|
bprint_string(buf, "]");
|
|
bprint_type(buf, info.elem);
|
|
case Slice:
|
|
bprint_string(buf, "[");
|
|
bprint_string(buf, "]");
|
|
bprint_type(buf, info.elem);
|
|
case Vector:
|
|
bprint_string(buf, "[vector ");
|
|
bprint_i64(buf, info.count as i64);
|
|
bprint_string(buf, "]");
|
|
bprint_type(buf, info.elem);
|
|
|
|
case Struct:
|
|
bprint_string(buf, "struct ");
|
|
if info.packed { bprint_string(buf, "#packed "); }
|
|
if info.ordered { bprint_string(buf, "#ordered "); }
|
|
bprint_string(buf, "{");
|
|
for field, i : info.fields {
|
|
if i > 0 {
|
|
bprint_string(buf, ", ");
|
|
}
|
|
bprint_any(buf, field.name);
|
|
bprint_string(buf, ": ");
|
|
bprint_type(buf, field.type_info);
|
|
}
|
|
bprint_string(buf, "}");
|
|
|
|
case Union:
|
|
bprint_string(buf, "union {");
|
|
for field, i : info.fields {
|
|
if i > 0 {
|
|
bprint_string(buf, ", ");
|
|
}
|
|
bprint_any(buf, field.name);
|
|
bprint_string(buf, ": ");
|
|
bprint_type(buf, field.type_info);
|
|
}
|
|
bprint_string(buf, "}");
|
|
|
|
case Raw_Union:
|
|
bprint_string(buf, "raw_union {");
|
|
for field, i : info.fields {
|
|
if i > 0 {
|
|
bprint_string(buf, ", ");
|
|
}
|
|
bprint_any(buf, field.name);
|
|
bprint_string(buf, ": ");
|
|
bprint_type(buf, field.type_info);
|
|
}
|
|
bprint_string(buf, "}");
|
|
|
|
case Enum:
|
|
bprint_string(buf, "enum ");
|
|
bprint_type(buf, info.base);
|
|
bprint_string(buf, " {}");
|
|
|
|
}
|
|
}
|
|
|
|
|
|
make_any :: proc(type_info: ^Type_Info, data: rawptr) -> any {
|
|
a :any;
|
|
a.type_info = type_info;
|
|
a.data = data;
|
|
return a;
|
|
}
|
|
|
|
bprint_any :: proc(buf: ^Buffer, arg: any) {
|
|
if arg.type_info == nil {
|
|
bprint_string(buf, "<nil>");
|
|
return;
|
|
}
|
|
|
|
if arg.data == nil {
|
|
bprint_string(buf, "<nil>");
|
|
return;
|
|
}
|
|
|
|
using Type_Info;
|
|
match type info : arg.type_info {
|
|
case Named:
|
|
a := make_any(info.base, arg.data);
|
|
match type b : info.base {
|
|
case Struct:
|
|
bprint_string(buf, info.name);
|
|
bprint_string(buf, "{");
|
|
for f, i : b.fields {
|
|
if i > 0 {
|
|
bprint_string(buf, ", ");
|
|
}
|
|
bprint_string(buf, f.name);
|
|
// bprint_any(buf, f.offset);
|
|
bprint_string(buf, " = ");
|
|
data := arg.data as ^byte + f.offset;
|
|
bprint_any(buf, make_any(f.type_info, data));
|
|
}
|
|
bprint_string(buf, "}");
|
|
|
|
default:
|
|
bprint_any(buf, a);
|
|
}
|
|
|
|
case Integer:
|
|
match type i : arg {
|
|
case i8: bprint_i64(buf, i as i64);
|
|
case u8: bprint_u64(buf, i as u64);
|
|
case i16: bprint_i64(buf, i as i64);
|
|
case u16: bprint_u64(buf, i as u64);
|
|
case i32: bprint_i64(buf, i as i64);
|
|
case u32: bprint_u64(buf, i as u64);
|
|
case i64: bprint_i64(buf, i);
|
|
case u64: bprint_u64(buf, i);
|
|
// case i128: bprint_i128(buf, i);
|
|
// case u128: bprint_u128(buf, i);
|
|
|
|
case int: bprint_i64(buf, i as i64);
|
|
case uint: bprint_u64(buf, i as u64);
|
|
}
|
|
|
|
case Float:
|
|
match type f : arg {
|
|
// case f16: bprint_f64(buf, f as f64);
|
|
case f32: bprint_f32(buf, f);
|
|
case f64: bprint_f64(buf, f);
|
|
// case f128: bprint_f64(buf, f as f64);
|
|
}
|
|
|
|
case String:
|
|
match type s : arg {
|
|
case string: bprint_string(buf, s);
|
|
}
|
|
|
|
case Boolean:
|
|
match type b : arg {
|
|
case bool: bprint_bool(buf, b);
|
|
}
|
|
|
|
case Pointer:
|
|
match type p : arg {
|
|
case ^Type_Info: bprint_type(buf, p);
|
|
default: bprint_pointer(buf, (arg.data as ^rawptr)^);
|
|
}
|
|
|
|
case Maybe:
|
|
size := mem.size_of_type_info(info.elem);
|
|
data := slice_ptr(arg.data as ^byte, size+1);
|
|
if data[size] != 0 {
|
|
bprint_any(buf, make_any(info.elem, arg.data));
|
|
} else {
|
|
bprint_string(buf, "nil");
|
|
}
|
|
|
|
case Array:
|
|
bprintf(buf, "[%]%{", info.count, info.elem);
|
|
defer bprint_string(buf, "}");
|
|
|
|
for i : 0..<info.count {
|
|
if i > 0 {
|
|
bprint_string(buf, ", ");
|
|
}
|
|
|
|
data := arg.data as ^byte + i*info.elem_size;
|
|
bprint_any(buf, make_any(info.elem, data));
|
|
}
|
|
|
|
case Slice:
|
|
slice := arg.data as ^[]byte;
|
|
bprintf(buf, "[]%{", info.elem);
|
|
defer bprint_string(buf, "}");
|
|
|
|
for i : 0..<slice.count {
|
|
if i > 0 {
|
|
bprint_string(buf, ", ");
|
|
}
|
|
|
|
data := slice.data + i*info.elem_size;
|
|
bprint_any(buf, make_any(info.elem, data));
|
|
}
|
|
|
|
case Vector:
|
|
is_bool :: proc(type_info: ^Type_Info) -> bool {
|
|
match type info : type_info {
|
|
case Named:
|
|
return is_bool(info.base);
|
|
case Boolean:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bprintf(buf, "[vector %]%{", info.count, info.elem);
|
|
defer bprint_string(buf, "}");
|
|
|
|
if is_bool(info.elem) {
|
|
return;
|
|
}
|
|
|
|
for i : 0..<info.count {
|
|
if i > 0 {
|
|
bprint_string(buf, ", ");
|
|
}
|
|
|
|
data := arg.data as ^byte + i*info.elem_size;
|
|
bprint_any(buf, make_any(info.elem, data));
|
|
}
|
|
|
|
|
|
case Struct:
|
|
bprintf(buf, "%{", arg.type_info);
|
|
defer bprint_string(buf, "}");
|
|
|
|
for f, i : info.fields {
|
|
if i > 0 {
|
|
bprint_string(buf, ", ");
|
|
}
|
|
bprint_string(buf, f.name);
|
|
bprint_string(buf, " = ");
|
|
data := arg.data as ^byte + f.offset;
|
|
ti := f.type_info;
|
|
bprint_any(buf, make_any(ti, data));
|
|
}
|
|
|
|
case Union:
|
|
bprint_string(buf, "(union)");
|
|
case Raw_Union:
|
|
bprint_string(buf, "(raw_union)");
|
|
|
|
case Enum:
|
|
bprint_any(buf, make_any(info.base, arg.data));
|
|
|
|
case Procedure:
|
|
bprint_type(buf, arg.type_info);
|
|
bprint_string(buf, " @ ");
|
|
bprint_pointer(buf, (arg.data as ^rawptr)^);
|
|
}
|
|
}
|
|
|
|
|
|
bprintf :: proc(buf: ^Buffer, fmt: string, args: ...any) -> int {
|
|
is_digit :: proc(r: rune) -> bool #inline {
|
|
return '0' <= r && r <= '9';
|
|
}
|
|
|
|
parse_int :: proc(s: string, offset: int) -> (int, int) {
|
|
result := 0;
|
|
|
|
for _ : offset..<s.count {
|
|
c := s[offset] as rune;
|
|
if !is_digit(c) {
|
|
break;
|
|
}
|
|
|
|
result *= 10;
|
|
result += (c - '0') as int;
|
|
}
|
|
|
|
return result, offset;
|
|
}
|
|
|
|
prev := 0;
|
|
implicit_index := 0;
|
|
|
|
while i := 0; i < fmt.count { defer i += 1;
|
|
r := fmt[i] as rune;
|
|
index := implicit_index;
|
|
|
|
if r != '%' {
|
|
continue;
|
|
}
|
|
|
|
bprint_string(buf, fmt[prev:i]);
|
|
i += 1; // Skip %
|
|
if i < fmt.count {
|
|
next := fmt[i] as rune;
|
|
|
|
if next == '%' {
|
|
bprint_string(buf, "%");
|
|
i += 1;
|
|
prev = i;
|
|
continue;
|
|
}
|
|
|
|
if is_digit(next) {
|
|
index, i = parse_int(fmt, i);
|
|
}
|
|
}
|
|
|
|
if 0 <= index && index < args.count {
|
|
bprint_any(buf, args[index]);
|
|
implicit_index = index+1;
|
|
} else {
|
|
// TODO(bill): Error check index out bounds
|
|
bprint_string(buf, "<invalid>");
|
|
}
|
|
|
|
prev = i;
|
|
}
|
|
|
|
bprint_string(buf, fmt[prev:]);
|
|
return buf.length;
|
|
}
|
|
|
|
|
|
bprint :: proc(buf: ^Buffer, args: ...any) -> int {
|
|
is_type_string :: proc(info: ^Type_Info) -> bool {
|
|
using Type_Info;
|
|
if info == nil {
|
|
return false;
|
|
}
|
|
|
|
match type i : type_info_base(info) {
|
|
case String:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
prev_string := false;
|
|
for arg, i : args {
|
|
is_string := arg.data != nil && is_type_string(arg.type_info);
|
|
if i > 0 && !is_string && !prev_string {
|
|
bprint_space(buf);
|
|
}
|
|
bprint_any(buf, arg);
|
|
prev_string = is_string;
|
|
}
|
|
return buf.length;
|
|
}
|
|
|
|
bprintln :: proc(buf: ^Buffer, args: ...any) -> int {
|
|
for arg, i : args {
|
|
if i > 0 {
|
|
bprint_space(buf);
|
|
}
|
|
bprint_any(buf, arg);
|
|
}
|
|
bprint_nl(buf);
|
|
return buf.length;
|
|
}
|