#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.. 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.. 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, ""); return; } if arg.data == nil { bprint_string(buf, ""); 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.. 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.. 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.. 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.."); } 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; }