From 659e5359b2399927ffdd44d441d41a8a6e96228a Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 8 Jan 2017 01:10:55 +0000 Subject: [PATCH] fmt.printf - Go style due to runtime type safety --- code/demo.odin | 20 +- core/_preload.odin | 8 +- core/fmt.odin | 1198 ++++++++++++++++++++++++++++---------------- 3 files changed, 791 insertions(+), 435 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 420ce5533..1fe4c564d 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,7 +1,21 @@ #import "fmt.odin"; main :: proc() { - x := "-stats"; - y := "-begin"; - fmt.println(x == y); + fmt.printf("%f", 123); } + +/* +Standard Library Development: +* Formatted printf +* The Variable +* math + - Trig + - Random + - atoi +* Memory allocation +* File IO +* Timing + - Regular and OS + + +*/ diff --git a/core/_preload.odin b/core/_preload.odin index 0d73e5462..ad023db57 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -299,7 +299,7 @@ __string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= __assert :: proc(file: string, line, column: int, msg: string) #inline { - fmt.fprintf(os.stderr, "%(%:%) Runtime assertion: %\n", + fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n", file, line, column, msg); __debug_trap(); } @@ -308,7 +308,7 @@ __bounds_check_error :: proc(file: string, line, column: int, index, count: int) if 0 <= index && index < count { return; } - fmt.fprintf(os.stderr, "%(%:%) Index % is out of bounds range 0..<%\n", + fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..<%d\n", file, line, column, index, count); __debug_trap(); } @@ -317,7 +317,7 @@ __slice_expr_error :: proc(file: string, line, column: int, low, high: int) { if 0 <= low && low <= high { return; } - fmt.fprintf(os.stderr, "%(%:%) Invalid slice indices: [%:%]\n", + fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d:%d]\n", file, line, column, low, high); __debug_trap(); } @@ -325,7 +325,7 @@ __substring_expr_error :: proc(file: string, line, column: int, low, high: int) if 0 <= low && low <= high { return; } - fmt.fprintf(os.stderr, "%(%:%) Invalid substring indices: [%:%]\n", + fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d:%d]\n", file, line, column, low, high); __debug_trap(); } diff --git a/core/fmt.odin b/core/fmt.odin index 1e965fa0a..37295f4f7 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -2,16 +2,64 @@ #import "mem.odin"; #import "utf8.odin"; -PRINT_BUF_SIZE :: 1<<12; +DEFAULT_BUFFER_SIZE :: 1<<12; Buffer :: struct { data: []byte; length: int; } +buffer_write :: 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; + } + } +} +buffer_write_string :: proc(buf: ^Buffer, s: string) { + buffer_write(buf, s as []byte); +} +buffer_write_byte :: proc(buf: ^Buffer, b: byte) { + if buf.length < buf.data.count { + buf.data[buf.length] = b; + buf.length += 1; + } +} +buffer_write_rune :: proc(buf: ^Buffer, r: rune) { + if r < utf8.RUNE_SELF { + buffer_write_byte(buf, r as byte); + return; + } + + b, n := utf8.encode_rune(r); + buffer_write(buf, b[:n]); +} + +Fmt_Info :: struct { + minus: bool; + plus: bool; + space: bool; + zero: bool; + hash: bool; + width_set: bool; + prec_set: bool; + + width: int; + prec: int; + + reordered: bool; + good_arg_index: bool; + + buf: ^Buffer; + arg: any; // Temporary +} + + fprint :: proc(fd: os.Handle, args: ...any) -> int { - data: [PRINT_BUF_SIZE]byte; + data: [DEFAULT_BUFFER_SIZE]byte; buf := Buffer{data[:], 0}; bprint(^buf, ...args); os.write(fd, buf.data[:buf.length]); @@ -19,14 +67,14 @@ fprint :: proc(fd: os.Handle, args: ...any) -> int { } fprintln :: proc(fd: os.Handle, args: ...any) -> int { - data: [PRINT_BUF_SIZE]byte; + data: [DEFAULT_BUFFER_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; + data: [DEFAULT_BUFFER_SIZE]byte; buf := Buffer{data[:], 0}; bprintf(^buf, fmt, ...args); os.write(fd, buf.data[:buf.length]); @@ -45,133 +93,15 @@ printf :: proc(fmt: string, args: ...any) -> int { } - fprint_type :: proc(fd: os.Handle, info: ^Type_Info) { - data: [PRINT_BUF_SIZE]byte; + data: [DEFAULT_BUFFER_SIZE]byte; buf := Buffer{data[:], 0}; - bprint_type(^buf, info); + buffer_write_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) { +buffer_write_type :: proc(buf: ^Buffer, ti: ^Type_Info) { if ti == nil { return; } @@ -179,368 +109,136 @@ bprint_type :: proc(buf: ^Buffer, ti: ^Type_Info) { using Type_Info; match type info : ti { case Named: - bprint_string(buf, info.name); + buffer_write_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"); + case ti == type_info(int): buffer_write_string(buf, "int"); + case ti == type_info(uint): buffer_write_string(buf, "uint"); default: - bprint_string(buf, if info.signed { give "i" } else { give "u"}); - bprint_u64(buf, 8*info.size as u64); + buffer_write_string(buf, if info.signed { give "i" } else { give "u"}); + fi := Fmt_Info{buf = buf}; + fmt_int(^fi, 8*info.size as u64, false, 'd'); } case Float: match info.size { - case 4: bprint_string(buf, "f32"); - case 8: bprint_string(buf, "f64"); + case 4: buffer_write_string(buf, "f32"); + case 8: buffer_write_string(buf, "f64"); } - case String: bprint_string(buf, "string"); - case Boolean: bprint_string(buf, "bool"); + case String: buffer_write_string(buf, "string"); + case Boolean: buffer_write_string(buf, "bool"); case Pointer: if info.elem == nil { - bprint_string(buf, "rawptr"); + buffer_write_string(buf, "rawptr"); } else { - bprint_string(buf, "^"); - bprint_type(buf, info.elem); + buffer_write_string(buf, "^"); + buffer_write_type(buf, info.elem); } case Maybe: - bprint_string(buf, "?"); - bprint_type(buf, info.elem); + buffer_write_string(buf, "?"); + buffer_write_type(buf, info.elem); case Procedure: - bprint_string(buf, "proc"); + buffer_write_string(buf, "proc"); if info.params == nil { - bprint_string(buf, "()"); + buffer_write_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 count == 1 { buffer_write_string(buf, "("); } + buffer_write_type(buf, info.params); + if count == 1 { buffer_write_string(buf, ")"); } } if info.results != nil { - bprint_string(buf, " -> "); - bprint_type(buf, info.results); + buffer_write_string(buf, " -> "); + buffer_write_type(buf, info.results); } case Tuple: count := info.fields.count; - if count != 1 { bprint_string(buf, "("); } + if count != 1 { buffer_write_string(buf, "("); } for i : 0.. 0 { bprint_string(buf, ", "); } + if i > 0 { buffer_write_string(buf, ", "); } f := info.fields[i]; if f.name.count > 0 { - bprint_string(buf, f.name); - bprint_string(buf, ": "); + buffer_write_string(buf, f.name); + buffer_write_string(buf, ": "); } - bprint_type(buf, f.type_info); + buffer_write_type(buf, f.type_info); } - if count != 1 { bprint_string(buf, ")"); } + if count != 1 { buffer_write_string(buf, ")"); } case Array: - bprint_string(buf, "["); - bprint_i64(buf, info.count as i64); - bprint_string(buf, "]"); - bprint_type(buf, info.elem); + buffer_write_string(buf, "["); + fi := Fmt_Info{buf = buf}; + fmt_int(^fi, info.count as u64, false, 'd'); + buffer_write_string(buf, "]"); + buffer_write_type(buf, info.elem); case Slice: - bprint_string(buf, "["); - bprint_string(buf, "]"); - bprint_type(buf, info.elem); + buffer_write_string(buf, "["); + buffer_write_string(buf, "]"); + buffer_write_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); + buffer_write_string(buf, "[vector "); + fi := Fmt_Info{buf = buf}; + fmt_int(^fi, info.count as u64, false, 'd'); + buffer_write_string(buf, "]"); + buffer_write_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, "{"); + buffer_write_string(buf, "struct "); + if info.packed { buffer_write_string(buf, "#packed "); } + if info.ordered { buffer_write_string(buf, "#ordered "); } + buffer_write_string(buf, "{"); for field, i : info.fields { if i > 0 { - bprint_string(buf, ", "); + buffer_write_string(buf, ", "); } - bprint_any(buf, field.name); - bprint_string(buf, ": "); - bprint_type(buf, field.type_info); + buffer_write_string(buf, field.name); + buffer_write_string(buf, ": "); + buffer_write_type(buf, field.type_info); } - bprint_string(buf, "}"); + buffer_write_string(buf, "}"); case Union: - bprint_string(buf, "union {"); + buffer_write_string(buf, "union {"); for field, i : info.fields { if i > 0 { - bprint_string(buf, ", "); + buffer_write_string(buf, ", "); } - bprint_any(buf, field.name); - bprint_string(buf, ": "); - bprint_type(buf, field.type_info); + buffer_write_string(buf, field.name); + buffer_write_string(buf, ": "); + buffer_write_type(buf, field.type_info); } - bprint_string(buf, "}"); + buffer_write_string(buf, "}"); case Raw_Union: - bprint_string(buf, "raw_union {"); + buffer_write_string(buf, "raw_union {"); for field, i : info.fields { if i > 0 { - bprint_string(buf, ", "); + buffer_write_string(buf, ", "); } - bprint_any(buf, field.name); - bprint_string(buf, ": "); - bprint_type(buf, field.type_info); + buffer_write_string(buf, field.name); + buffer_write_string(buf, ": "); + buffer_write_type(buf, field.type_info); } - bprint_string(buf, "}"); + buffer_write_string(buf, "}"); case Enum: - bprint_string(buf, "enum "); - bprint_type(buf, info.base); - bprint_string(buf, " {}"); + buffer_write_string(buf, "enum "); + buffer_write_type(buf, info.base); + buffer_write_string(buf, " {}"); } } make_any :: proc(type_info: ^Type_Info, data: rawptr) -> any { - a :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 { @@ -556,26 +254,670 @@ bprint :: proc(buf: ^Buffer, args: ...any) -> int { return false; } + fi: Fmt_Info; + fi.buf = buf; 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); + buffer_write_rune(buf, ' '); } - bprint_any(buf, arg); + fmt_value(^fi, arg, 'v'); prev_string = is_string; } return buf.length; } bprintln :: proc(buf: ^Buffer, args: ...any) -> int { + fi: Fmt_Info; + fi.buf = buf; + for arg, i : args { if i > 0 { - bprint_space(buf); + buffer_write_rune(buf, ' '); } - bprint_any(buf, arg); + fmt_value(^fi, arg, 'v'); } - bprint_nl(buf); + buffer_write_rune(buf, '\n'); return buf.length; } + + + + + + +parse_int :: proc(s: string, offset: int) -> (int, int, bool) { + is_digit :: proc(r: rune) -> bool #inline { + return '0' <= r && r <= '9'; + } + + result := 0; + ok := true; + + i := 0; + for _ : offset.. (int, int, bool) { + parse_arg_number :: proc(format: string) -> (int, int, bool) { + if format.count < 3 { + return 0, 1, false; + } + + for i : 1.. (int, int, bool) { + num := 0; + new_arg_index := arg_index; + ok := true; + if arg_index < args.count { + arg := args[arg_index]; + arg.type_info = type_info_base(arg.type_info); + match type i : arg { + case int: num = i; + case i8: num = i as int; + case i16: num = i as int; + case i32: num = i as int; + case i64: num = i as int; + case u8: num = i as int; + case u16: num = i as int; + case u32: num = i as int; + case u64: num = i as int; + default: + ok = false; + } + } + + return num, new_arg_index, ok; +} + + +fmt_bad_verb :: proc(using fi: ^Fmt_Info, verb: rune) { + buffer_write_string(buf, "%!"); + buffer_write_rune(buf, verb); + buffer_write_byte(buf, '('); + if arg.type_info != nil { + buffer_write_type(buf, arg.type_info); + buffer_write_byte(buf, '='); + fmt_value(fi, arg, 'v'); + } else { + buffer_write_string(buf, ""); + } + buffer_write_byte(buf, ')'); +} + +fmt_bool :: proc(using fi: ^Fmt_Info, b: bool, verb: rune) { + match verb { + case 't', 'v': + buffer_write_string(buf, if b { give "true" } else { give "false" }); + default: + fmt_bad_verb(fi, verb); + } +} + + +fmt_write_padding :: proc(fi: ^Fmt_Info, width: int) { + if width <= 0 { + return; + } + pad_byte: byte = ' '; + if fi.zero { + pad_byte = '0'; + } + + count := min(width, fi.buf.data.count-fi.buf.length); + start := fi.buf.length; + for i : start.. buf.count { + // TODO(bill):???? + panic("fmt_integer buffer overrun. Width and precision too big"); + } + } + + prec := 0; + if fi.prec_set { + prec = fi.prec; + if prec == 0 && u == 0 { + old_zero := fi.zero; + fi.zero = false; + fmt_write_padding(fi, fi.width); + fi.zero = old_zero; + return; + } + } else if fi.zero && fi.width_set { + prec = fi.width; + if negative || fi.plus || fi.space { + // There needs to be space for the "sign" + prec -= 1; + } + } + + i := buf.count; + + match base { + case 2, 8, 10, 16: + break; + default: + panic("fmt_integer: unknown base, whoops"); + } + + while b := base as u64; u >= b { + i -= 1; + next := u / b; + buf[i] = digits[u%b]; + u = next; + } + i -= 1; + buf[i] = digits[u]; + while i > 0 && prec > buf.count-i { + i -= 1; + buf[i] = '0'; + } + + if fi.hash { + i -= 1; + match base { + case 2: buf[i] = 'b'; + case 8: buf[i] = 'o'; + case 10: buf[i] = 'd'; + case 16: buf[i] = digits[16]; + } + i -= 1; + buf[i] = '0'; + } + + if negative { + i -= 1; + buf[i] = '-'; + } else if fi.plus { + i -= 1; + buf[i] = '+'; + } else if fi.space { + i -= 1; + buf[i] = ' '; + } + + old_zero := fi.zero; + defer fi.zero = old_zero; + fi.zero = false; + + if !fi.width_set || fi.width == 0 { + buffer_write(fi.buf, buf[i:]); + } else { + width := fi.width - utf8.rune_count(buf[i:] as string); + if fi.minus { + // Right pad + buffer_write(fi.buf, buf[i:]); + fmt_write_padding(fi, width); + } else { + // Left pad + fmt_write_padding(fi, width); + buffer_write(fi.buf, buf[i:]); + } + } + +} + +__DIGITS_LOWER := "0123456789abcdefx"; +__DIGITS_UPPER := "0123456789ABCDEFX"; + +fmt_rune :: proc(fi: ^Fmt_Info, r: rune) { + buffer_write_rune(fi.buf, r); +} + +fmt_int :: proc(fi: ^Fmt_Info, u: u64, signed: bool, verb: rune) { + match verb { + case 'v': fmt_integer(fi, u, 10, signed, __DIGITS_LOWER); + case 'b': fmt_integer(fi, u, 2, signed, __DIGITS_LOWER); + case 'o': fmt_integer(fi, u, 8, signed, __DIGITS_LOWER); + case 'd': fmt_integer(fi, u, 10, signed, __DIGITS_LOWER); + case 'x': fmt_integer(fi, u, 16, signed, __DIGITS_LOWER); + case 'X': fmt_integer(fi, u, 16, signed, __DIGITS_UPPER); + case 'c': fmt_rune(fi, u as rune); + case 'U': + r := u as rune; + if r < 0 || r > utf8.MAX_RUNE { + fmt_bad_verb(fi, verb); + } else { + buffer_write_string(fi.buf, "U+"); + fmt_integer(fi, u, 16, false, __DIGITS_UPPER); + } + + default: + fmt_bad_verb(fi, verb); + } +} +fmt_float :: proc(fi: ^Fmt_Info, v: f64, bits: int, verb: rune) { + // TODO(bill): Actually print a float correctly + // THIS IS FUCKING SHIT! + + match verb { + case 'e', 'E', 'f', 'F', 'g', 'G': + break; + default: + fmt_bad_verb(fi, verb); + return; + } + + f := v; + + if f == 0 { + buffer_write_byte(fi.buf, '0'); + return; + } + + if f < 0 { + buffer_write_byte(fi.buf, '-'); + f = -f; + } + i := f as u64; + fmt_int(fi, i, false, 'd'); + f -= i as f64; + buffer_write_byte(fi.buf, '.'); + + decimal_places := 5; + if bits == 64 { + decimal_places = 9; + } + if fi.prec_set { + decimal_places = fi.prec; + } + + while mult: f64 = 10.0; decimal_places >= 0 { + i = (f * mult) as u64; + fmt_int(fi, i, false, 'd'); + f -= i as f64 / mult; + mult *= 10; + decimal_places -= 1; + } +} +fmt_string :: proc(fi: ^Fmt_Info, s: string, verb: rune) { + match verb { + case 'v', 's': + buffer_write_string(fi.buf, s); + default: + fmt_bad_verb(fi, verb); + } +} + +fmt_pointer :: proc(fi: ^Fmt_Info, p: rawptr, verb: rune) { + if verb != 'p' { + fmt_bad_verb(fi, verb); + return; + } + u := p as uint as u64; + if !fi.hash { + buffer_write_string(fi.buf, "0x"); + } + fmt_integer(fi, u, 16, false, __DIGITS_UPPER); +} + + +fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { + if v.data == nil || v.type_info == nil { + buffer_write_string(fi.buf, ""); + return; + } + + using Type_Info; + match type info : v.type_info { + case Named: + if verb != 'v' { + fmt_bad_verb(fi, verb); + return; + } + + a := make_any(info.base, v.data); + match type b : info.base { + case Struct: + buffer_write_string(fi.buf, info.name); + buffer_write_byte(fi.buf, '{'); + for f, i : b.fields { + if i > 0 { + buffer_write_string(fi.buf, ", "); + } + buffer_write_string(fi.buf, f.name); + // bprint_any(fi.buf, f.offset); + buffer_write_string(fi.buf, " = "); + data := v.data as ^byte + f.offset; + fmt_arg(fi, make_any(f.type_info, data), 'v'); + } + buffer_write_byte(fi.buf, '}'); + + default: + fmt_value(fi, a, verb); + } + + case Boolean: fmt_arg(fi, v, verb); + case Float: fmt_arg(fi, v, verb); + case Integer: fmt_arg(fi, v, verb); + case String: fmt_arg(fi, v, verb); + + case Pointer: + fmt_pointer(fi, (v.data as ^rawptr)^, verb); + + case Maybe: + // TODO(bill): Correct verbs for Maybe types? + size := mem.size_of_type_info(info.elem); + data := slice_ptr(v.data as ^byte, size+1); + if data[size] != 0 { + fmt_arg(fi, make_any(info.elem, v.data), verb); + } else { + buffer_write_string(fi.buf, "nil"); + } + + case Array: + if verb != 'v' { + fmt_bad_verb(fi, verb); + return; + } + + buffer_write_byte(fi.buf, '['); + defer buffer_write_byte(fi.buf, ']'); + for i : 0.. 0 { + buffer_write_string(fi.buf, ", "); + } + data := v.data as ^byte + i*info.elem_size; + fmt_arg(fi, make_any(info.elem, data), 'v'); + } + + case Slice: + if verb != 'v' { + fmt_bad_verb(fi, verb); + return; + } + + buffer_write_byte(fi.buf, '['); + defer buffer_write_byte(fi.buf, ']'); + slice := v.data as ^[]byte; + for i : 0.. 0 { + buffer_write_string(fi.buf, ", "); + } + data := slice.data + i*info.elem_size; + fmt_arg(fi, make_any(info.elem, data), 'v'); + } + + 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; + } + + buffer_write_byte(fi.buf, '<'); + defer buffer_write_byte(fi.buf, '>'); + + if is_bool(info.elem) { + return; + } + + for i : 0.. 0 { + buffer_write_string(fi.buf, ", "); + } + + data := v.data as ^byte + i*info.elem_size; + fmt_value(fi, make_any(info.elem, data), 'v'); + } + + case Struct: + buffer_write_byte(fi.buf, '{'); + defer buffer_write_byte(fi.buf, '}'); + + for f, i : info.fields { + if i > 0 { + buffer_write_string(fi.buf, ", "); + } + buffer_write_string(fi.buf, f.name); + buffer_write_string(fi.buf, " = "); + data := v.data as ^byte + f.offset; + ti := f.type_info; + fmt_value(fi, make_any(ti, data), 'v'); + } + + case Union: + buffer_write_string(fi.buf, "(union)"); + case Raw_Union: + buffer_write_string(fi.buf, "(raw_union)"); + + case Enum: + fmt_value(fi, make_any(info.base, v.data), verb); + + case Procedure: + buffer_write_type(fi.buf, v.type_info); + buffer_write_string(fi.buf, " @ "); + fmt_pointer(fi, (v.data as ^rawptr)^, 'p'); + } +} + +fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) { + if arg.data == nil || arg.type_info == nil { + buffer_write_string(fi.buf, ""); + return; + } + fi.arg = arg; + + if verb == 'T' { // Type Info + buffer_write_type(fi.buf, arg.type_info); + return; + } + + base_arg := arg; + base_arg.type_info = type_info_base(base_arg.type_info); + match type a : base_arg { + case bool: fmt_bool(fi, a, verb); + case f32: fmt_float(fi, a as f64, 32, verb); + case f64: fmt_float(fi, a, 64, verb); + + case int: fmt_int(fi, a as u64, true, verb); + case i8: fmt_int(fi, a as u64, true, verb); + case i16: fmt_int(fi, a as u64, true, verb); + case i32: fmt_int(fi, a as u64, true, verb); + case i64: fmt_int(fi, a as u64, true, verb); + case uint: fmt_int(fi, a as u64, false, verb); + case u8: fmt_int(fi, a as u64, false, verb); + case u16: fmt_int(fi, a as u64, false, verb); + case u32: fmt_int(fi, a as u64, false, verb); + case u64: fmt_int(fi, a as u64, false, verb); + case string: fmt_string(fi, a, verb); + default: fmt_value(fi, arg, verb); + } + +} + + +bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int { + fi := Fmt_Info{}; + end := fmt.count; + arg_index := 0; + was_prev_index := false; + while i := 0; i < end { + fi = Fmt_Info{buf = b, good_arg_index = true}; + + prev_i := i; + while i < end && fmt[i] != '%' { + i += 1; + } + if i > prev_i { + buffer_write_string(b, fmt[prev_i:i]); + } + if i >= end { + break; + } + + // Process a "verb" + i += 1; + + + while i < end { + skip_loop := false; + c := fmt[i]; + match c { + case '+': + fi.plus = true; + case '-': + fi.minus = true; + fi.zero = false; + case ' ': + fi.space = true; + case '#': + fi.hash = true; + case '0': + fi.zero = !fi.minus; + default: + skip_loop = true; + } + + if skip_loop { + break; + } + i += 1; + } + + arg_index, i, was_prev_index = arg_number(^fi, arg_index, fmt, i, args.count); + + // Width + if i < end && fmt[i] == '*' { + i += 1; + fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index); + if !fi.width_set { + buffer_write_string(b, "%!(BAD WIDTH)"); + } + + if fi.width < 0 { + fi.width = -fi.width; + fi.minus = true; + fi.zero = false; + } + was_prev_index = false; + } else { + fi.width, i, fi.width_set = parse_int(fmt, i); + if was_prev_index && fi.width_set { // %[6]2d + fi.good_arg_index = false; + } + } + + // Precision + if i < end && fmt[i] == '.' { + i += 1; + if was_prev_index { // %[6].2d + fi.good_arg_index = false; + } + arg_index, i, was_prev_index = arg_number(^fi, arg_index, fmt, i, args.count); + if i < end && fmt[i] == '*' { + i += 1; + fi.prec, arg_index, fi.prec_set = int_from_arg(args, arg_index); + if fi.prec < 0 { + fi.prec = 0; + fi.prec_set = false; + } + if !fi.prec_set { + buffer_write_string(fi.buf, "%!(BAD PRECISION)"); + } + was_prev_index = false; + } else { + fi.prec, i, fi.prec_set = parse_int(fmt, i); + if !fi.prec_set { + fi.prec_set = true; + fi.prec = 0; + } + } + } + + if !was_prev_index { + arg_index, i, was_prev_index = arg_number(^fi, arg_index, fmt, i, args.count); + } + + if i >= end { + buffer_write_string(b, "%!(NO VERB)"); + break; + } + + verb, w := utf8.decode_rune(fmt[i:]); + i += w; + + if verb == '%' { + buffer_write_byte(b, '%'); + } else if !fi.good_arg_index { + buffer_write_string(b, "%!(BAD ARGUMENT NUMBER)"); + } else if arg_index >= args.count { + buffer_write_string(b, "%!(MISSING ARGUMENT)"); + } else { + fmt_arg(^fi, args[arg_index], verb); + arg_index += 1; + } + } + + if !fi.reordered && arg_index < args.count { + buffer_write_string(b, "%!(EXTRA "); + for arg, index : args[arg_index:] { + if index > 0 { + buffer_write_string(b, ", "); + } + if arg.data == nil || arg.type_info == nil { + buffer_write_string(b, ""); + } else { + fmt_arg(^fi, arg, 'v'); + } + } + buffer_write_string(b, ")"); + } + + return b.length; +}