From 573402c97e4d3a49e870af077e189fbbf2736efd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 7 Mar 2024 13:39:44 +0000 Subject: [PATCH] Begin work on `%w` - 'w'rite as valid Odin literals --- core/fmt/fmt.odin | 397 ++++++++++++++++++++++++++-------------------- 1 file changed, 222 insertions(+), 175 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index cadc9f1dd..6ec495500 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -972,7 +972,7 @@ fmt_bad_verb :: proc(fi: ^Info, verb: rune) { // fmt_bool :: proc(fi: ^Info, b: bool, verb: rune) { switch verb { - case 't', 'v': + case 't', 'v', 'w': fmt_string(fi, b ? "true" : "false", 's') case: fmt_bad_verb(fi, verb) @@ -1209,7 +1209,7 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { switch verb { case 'c', 'r', 'v': io.write_rune(fi.writer, r, &fi.n) - case 'q': + case 'q', 'w': fi.n += io.write_quoted_rune(fi.writer, r) case: fmt_int(fi, u64(r), false, 32, verb) @@ -1226,7 +1226,8 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { // fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { switch verb { - case 'v': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER) + case 'v', 'w': + _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER) case 'b': _fmt_int(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER) case 'o': _fmt_int(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER) case 'i', 'd': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER) @@ -1261,7 +1262,8 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { // fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: rune) { switch verb { - case 'v': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER) + case 'v', 'w': + _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER) case 'b': _fmt_int_128(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER) case 'o': _fmt_int_128(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER) case 'i', 'd': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER) @@ -1351,7 +1353,7 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b // fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { switch verb { - case 'g', 'G', 'v': + case 'g', 'G', 'v', 'w': _fmt_float_as(fi, v, bit_size, verb, 'g', -1) case 'f', 'F': _fmt_float_as(fi, v, bit_size, verb, 'f', 3) @@ -1424,7 +1426,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) { io.write_string(fi.writer, s, &fi.n) } - case 'q': // quoted string + case 'q', 'w': // quoted string io.write_quoted_string(fi.writer, s, '"', &fi.n) case 'x', 'X': @@ -1467,7 +1469,7 @@ fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) { fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { u := u64(uintptr(p)) switch verb { - case 'p', 'v': + case 'p', 'v', 'w': if !fi.hash && verb == 'v' { io.write_string(fi.writer, "0x", &fi.n) } @@ -1535,7 +1537,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { // Inputs: // - fi: Pointer to the Info struct containing format settings. // - v: The enum value to format. -// - verb: The format specifier character (e.g. 'i','d','f','s','v','q'). +// - verb: The format specifier character (e.g. 'i','d','f','s','v','q','w'). // fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if v.id == nil || v.data == nil { @@ -1559,6 +1561,15 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i') io.write_string(fi.writer, ")", &fi.n) } + case 'w': + if str, ok := enum_value_to_string(v); ok { + io.write_byte(fi.writer, '.', &fi.n) + io.write_string(fi.writer, str, &fi.n) + } else { + io.write_string(fi.writer, "%!(BAD ENUM VALUE=", &fi.n) + fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i') + io.write_string(fi.writer, ")", &fi.n) + } } } } @@ -1747,8 +1758,8 @@ fmt_write_indent :: proc(fi: ^Info) { // - verb: The formatting verb to be used for the array elements. // fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: int, elem_id: typeid, verb: rune) { - io.write_byte(fi.writer, '[', &fi.n) - defer io.write_byte(fi.writer, ']', &fi.n) + io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n) + defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n) if count <= 0 { return @@ -1821,6 +1832,13 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: r, w := utf8.decode_rune_in_string(value) value = value[w:] if value == "" || value[0] == ',' { + if verb^ == 'w' { + // TODO(bill): is this a good idea overriding that field tags if 'w' is used? + switch r { + case 's': r = 'q' + case: r = 'w' + } + } verb^ = r if len(value) > 0 && value[0] == ',' { field_name := value[1:] @@ -1832,7 +1850,7 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: switch r { case 's', 'q': handle_optional_len(data, info, field_name, optional_len) - case 'v': + case 'v', 'w': #partial switch reflect.type_kind(info.types[idx].id) { case .String, .Multi_Pointer, .Array, .Slice, .Dynamic_Array: handle_optional_len(data, info, field_name, optional_len) @@ -1854,7 +1872,7 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: // - type_name: The name of the type being formatted // fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_Struct, type_name: string) { - if the_verb != 'v' { + if the_verb != 'v' && the_verb != 'w' { fmt_bad_verb(fi, the_verb) return } @@ -1871,7 +1889,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St is_soa := info.soa_kind != .None io.write_string(fi.writer, type_name, &fi.n) - io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n) + io.write_byte(fi.writer, '[' if is_soa && the_verb == 'v' else '{', &fi.n) fi.record_level += 1 defer fi.record_level -= 1 @@ -1889,7 +1907,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St if hash { for _ in 0.. 0 { @@ -2130,141 +2148,143 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) } // Built-in Custom Formatters for core library types - switch a in v { - case runtime.Source_Code_Location: - io.write_string(fi.writer, a.file_path, &fi.n) + if verb != 'w' { + switch a in v { + case runtime.Source_Code_Location: + io.write_string(fi.writer, a.file_path, &fi.n) - when ODIN_ERROR_POS_STYLE == .Default { - io.write_byte(fi.writer, '(', &fi.n) - io.write_int(fi.writer, int(a.line), 10, &fi.n) - io.write_byte(fi.writer, ':', &fi.n) - io.write_int(fi.writer, int(a.column), 10, &fi.n) - io.write_byte(fi.writer, ')', &fi.n) - } else when ODIN_ERROR_POS_STYLE == .Unix { - io.write_byte(fi.writer, ':', &fi.n) - io.write_int(fi.writer, int(a.line), 10, &fi.n) - io.write_byte(fi.writer, ':', &fi.n) - io.write_int(fi.writer, int(a.column), 10, &fi.n) - io.write_byte(fi.writer, ':', &fi.n) - } else { - #panic("Unhandled ODIN_ERROR_POS_STYLE") - } - return - - case time.Duration: - ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) { - v := v - w := len(buf) - print := false - for _ in 0.. int { - v := v - w := len(buf) - if v == 0 { - w -= 1 - buf[w] = '0' + when ODIN_ERROR_POS_STYLE == .Default { + io.write_byte(fi.writer, '(', &fi.n) + io.write_int(fi.writer, int(a.line), 10, &fi.n) + io.write_byte(fi.writer, ':', &fi.n) + io.write_int(fi.writer, int(a.column), 10, &fi.n) + io.write_byte(fi.writer, ')', &fi.n) + } else when ODIN_ERROR_POS_STYLE == .Unix { + io.write_byte(fi.writer, ':', &fi.n) + io.write_int(fi.writer, int(a.line), 10, &fi.n) + io.write_byte(fi.writer, ':', &fi.n) + io.write_int(fi.writer, int(a.column), 10, &fi.n) + io.write_byte(fi.writer, ':', &fi.n) } else { - for v > 0 { - w -= 1 - buf[w] = byte(v%10) + '0' + #panic("Unhandled ODIN_ERROR_POS_STYLE") + } + return + + case time.Duration: + ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) { + v := v + w := len(buf) + print := false + for _ in 0.. int { + v := v + w := len(buf) + if v == 0 { + w -= 1 + buf[w] = '0' + } else { + for v > 0 { + w -= 1 + buf[w] = byte(v%10) + '0' + v /= 10 + } + } + return w } - w, u = ffrac(buf[:w], u, prec) - w = fint(buf[:w], u) - } else { - w -= 1 - buf[w] = 's' - w, u = ffrac(buf[:w], u, 9) - w = fint(buf[:w], u%60) - u /= 60 - if u > 0 { + + buf: [32]byte + w := len(buf) + u := u64(a) + neg := a < 0 + if neg { + u = -u + } + + if u < u64(time.Second) { + prec: int w -= 1 - buf[w] = 'm' + buf[w] = 's' + w -= 1 + switch { + case u == 0: + io.write_string(fi.writer, "0s", &fi.n) + return + case u < u64(time.Microsecond): + prec = 0 + buf[w] = 'n' + case u < u64(time.Millisecond): + prec = 3 + // U+00B5 'µ' micro sign == 0xC2 0xB5 + w -= 1 // Need room for two bytes + copy(buf[w:], "µ") + case: + prec = 6 + buf[w] = 'm' + } + w, u = ffrac(buf[:w], u, prec) + w = fint(buf[:w], u) + } else { + w -= 1 + buf[w] = 's' + w, u = ffrac(buf[:w], u, 9) w = fint(buf[:w], u%60) u /= 60 if u > 0 { w -= 1 - buf[w] = 'h' - w = fint(buf[:w], u) + buf[w] = 'm' + w = fint(buf[:w], u%60) + u /= 60 + if u > 0 { + w -= 1 + buf[w] = 'h' + w = fint(buf[:w], u) + } } } + + if neg { + w -= 1 + buf[w] = '-' + } + io.write_string(fi.writer, string(buf[w:]), &fi.n) + return + + case time.Time: + t := a + y, mon, d := time.date(t) + h, min, s := time.clock(t) + ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9 + write_padded_number(fi, i64(y), 4) + io.write_byte(fi.writer, '-', &fi.n) + write_padded_number(fi, i64(mon), 2) + io.write_byte(fi.writer, '-', &fi.n) + write_padded_number(fi, i64(d), 2) + io.write_byte(fi.writer, ' ', &fi.n) + + write_padded_number(fi, i64(h), 2) + io.write_byte(fi.writer, ':', &fi.n) + write_padded_number(fi, i64(min), 2) + io.write_byte(fi.writer, ':', &fi.n) + write_padded_number(fi, i64(s), 2) + io.write_byte(fi.writer, '.', &fi.n) + write_padded_number(fi, (ns), 9) + io.write_string(fi.writer, " +0000 UTC", &fi.n) + return } - - if neg { - w -= 1 - buf[w] = '-' - } - io.write_string(fi.writer, string(buf[w:]), &fi.n) - return - - case time.Time: - t := a - y, mon, d := time.date(t) - h, min, s := time.clock(t) - ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9 - write_padded_number(fi, i64(y), 4) - io.write_byte(fi.writer, '-', &fi.n) - write_padded_number(fi, i64(mon), 2) - io.write_byte(fi.writer, '-', &fi.n) - write_padded_number(fi, i64(d), 2) - io.write_byte(fi.writer, ' ', &fi.n) - - write_padded_number(fi, i64(h), 2) - io.write_byte(fi.writer, ':', &fi.n) - write_padded_number(fi, i64(min), 2) - io.write_byte(fi.writer, ':', &fi.n) - write_padded_number(fi, i64(s), 2) - io.write_byte(fi.writer, '.', &fi.n) - write_padded_number(fi, (ns), 9) - io.write_string(fi.writer, " +0000 UTC", &fi.n) - return } #partial switch b in info.base.variant { @@ -2275,6 +2295,23 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) case runtime.Type_Info_Bit_Set: fmt_bit_set(fi, v, verb = verb) case: + if verb == 'w' { + #partial switch b in info.base.variant { + case runtime.Type_Info_Array, + runtime.Type_Info_Enumerated_Array, + runtime.Type_Info_Dynamic_Array, + runtime.Type_Info_Slice, + runtime.Type_Info_Struct, + runtime.Type_Info_Union, + runtime.Type_Info_Enum, + runtime.Type_Info_Map, + runtime.Type_Info_Bit_Set, + runtime.Type_Info_Simd_Vector, + runtime.Type_Info_Matrix, + runtime.Type_Info_Bit_Field: + io.write_string(fi.writer, info.name, &fi.n) + } + } fmt_value(fi, any{v.data, info.base.id}, verb) } } @@ -2341,8 +2378,13 @@ fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union, // - info: A runtime.Type_Info_Matrix struct containing matrix type information. // fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix) { - io.write_string(fi.writer, "matrix[", &fi.n) - defer io.write_byte(fi.writer, ']', &fi.n) + if verb == 'w' { + io.write_byte(fi.writer, '{', &fi.n) + } else { + io.write_string(fi.writer, "matrix", &fi.n) + io.write_byte(fi.writer, '[', &fi.n) + } + defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n) fi.indent += 1 @@ -2413,8 +2455,8 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit return false } - io.write_string(fi.writer, type_name if len(type_name) != 0 else "bit_field", &fi.n) - io.write_string(fi.writer, "{", &fi.n) + io.write_string(fi.writer, type_name if len(type_name) != 0 || verb == 'w' else "bit_field", &fi.n) + io.write_byte(fi.writer, '{', &fi.n) hash := fi.hash; defer fi.hash = hash indent := fi.indent; defer fi.indent -= 1 @@ -2631,11 +2673,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { defer fi.record_level -= 1 if fi.hash { - io.write_string(fi.writer, "[\n", &fi.n) + io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n) + io.write_byte(fi.writer, '\n', &fi.n) defer { io.write_byte(fi.writer, '\n', &fi.n) fmt_write_indent(fi) - io.write_byte(fi.writer, ']', &fi.n) + io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n) } indent := fi.indent fi.indent += 1 @@ -2659,8 +2702,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { io.write_string(fi.writer, ",\n", &fi.n) } } else { - io.write_byte(fi.writer, '[', &fi.n) - defer io.write_byte(fi.writer, ']', &fi.n) + io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n) + io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n) for i in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } @@ -2725,38 +2768,42 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Map: - if verb != 'v' { + switch verb { + case: fmt_bad_verb(fi, verb) - return - } - - io.write_string(fi.writer, "map[", &fi.n) - defer io.write_byte(fi.writer, ']', &fi.n) - fi.record_level += 1 - defer fi.record_level -= 1 - - m := (^mem.Raw_Map)(v.data) - if m != nil { - if info.map_info == nil { - return + case 'v', 'w': + if verb == 'v' { + io.write_string(fi.writer, "map", &fi.n) } - map_cap := uintptr(runtime.map_cap(m^)) - ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info) - j := 0 - for bucket_index in 0.. 0 { - io.write_string(fi.writer, ", ", &fi.n) + fi.record_level += 1 + defer fi.record_level -= 1 + + m := (^mem.Raw_Map)(v.data) + if m != nil { + if info.map_info == nil { + return } - j += 1 + map_cap := uintptr(runtime.map_cap(m^)) + ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info) + j := 0 + for bucket_index in 0.. 0 { + io.write_string(fi.writer, ", ", &fi.n) + } + j += 1 - fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v') - io.write_string(fi.writer, "=", &fi.n) - fmt_arg(fi, any{rawptr(value), info.value.id}, 'v') + key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index) + value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index) + + fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, verb) + io.write_string(fi.writer, "=", &fi.n) + fmt_arg(fi, any{rawptr(value), info.value.id}, verb) + } } } @@ -2811,11 +2858,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { // - fi: A pointer to an Info struct containing formatting information. // - c: The complex128 value to be formatted. // - bits: The number of bits in the complex number (32 or 64). -// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H'). +// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H', 'w'). // fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { switch verb { - case 'f', 'F', 'v', 'h', 'H': + case 'f', 'F', 'v', 'h', 'H', 'w': r, i := real(c), imag(c) fmt_float(fi, r, bits/2, verb) if !fi.plus && i >= 0 { @@ -2835,11 +2882,11 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { // - fi: A pointer to an Info struct containing formatting information. // - q: The quaternion256 value to be formatted. // - bits: The number of bits in the quaternion number (64, 128, or 256). -// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H'). +// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H', 'w'). // fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { switch verb { - case 'f', 'F', 'v', 'h', 'H': + case 'f', 'F', 'v', 'h', 'H', 'w': r, i, j, k := real(q), imag(q), jmag(q), kmag(q) fmt_float(fi, r, bits/4, verb)