From 2501d50f9c5119e2fc6b0453f2f5fd07a1e1ee34 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Sat, 25 Mar 2023 23:45:53 -0700 Subject: [PATCH 1/5] fmt docs --- core/fmt/fmt.odin | 658 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 561 insertions(+), 97 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index c52869daa..d3ef777fc 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -44,6 +44,34 @@ Info :: struct { // Custom formatter signature. It returns true if the formatting was successful and false when it could not be done User_Formatter :: #type proc(fi: ^Info, arg: any, verb: rune) -> bool +// Example User Formatter: +// SomeType :: struct { +// value: int, +// } +// // Custom Formatter for SomeType +// User_Formatter :: proc(fi: ^fmt.Info, arg: any, verb: rune) -> bool { +// m := cast(^SomeType)arg.data +// switch verb { +// case 'v', 'd': +// fmt.fmt_int(fi, u64(m.value), true, 32, verb) +// case: +// return false +// } +// return true +// } +// // +// main :: proc() { +// // Ensure the fmt._user_formatters map is initialized +// if fmt._user_formatters == nil { +// fmt._user_formatters = new(map[typeid]fmt.User_Formatter) +// } +// // Register the custom formatter directly to the _user_formatters map +// fmt._user_formatters[type_info_of(SomeType).id] = User_Formatter +// // Use the custom formatter +// x := SomeType{42} +// fmt.println("My custom type value: ", x) +// } + Register_User_Formatter_Error :: enum { None, No_User_Formatter, @@ -54,13 +82,26 @@ Register_User_Formatter_Error :: enum { // it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary _user_formatters: ^map[typeid]User_Formatter -// set_user_formatters assigns m to a global value allowing the user have custom print formatting for specific -// types +// Sets user-defined formatters for custom print formatting of specific types +// +// Inputs: +// - m: A pointer to a map of typeids to User_Formatter structs. +// +// NOTE: Must be called before using register_user_formatter. +// set_user_formatters :: proc(m: ^map[typeid]User_Formatter) { _user_formatters = m } -// register_user_formatter assigns a formatter to a specific typeid. set_user_formatters must be called -// before any use of this procedure. +// Registers a user-defined formatter for a specific typeid +// +// Inputs: +// - id: The typeid of the custom type. +// - formatter: The User_Formatter function for the custom type. +// +// Returns: A Register_User_Formatter_Error value indicating the success or failure of the operation. +// +// WARNING: set_user_formatters must be called before using this procedure. +// register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Register_User_Formatter_Error { if _user_formatters == nil { return .No_User_Formatter @@ -71,75 +112,151 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist _user_formatters[id] = formatter return .None } - - -// aprint procedure return a string that was allocated with the current context -// They must be freed accordingly +// Creates a formatted string +// +// *Allocates Using Context's Allocator* +// +// Inputs: +// - args: A variadic list of arguments to be formatted. +// - sep: An optional separator string (default is a single space). +// +// Returns: A formatted string. +// aprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str) sbprint(buf=&str, args=args, sep=sep) return strings.to_string(str) } -// aprintln procedure return a string that was allocated with the current context -// They must be freed accordingly +// Creates a formatted string with a newline character at the end +// +// *Allocates Using Context's Allocator* +// +// Inputs: +// - args: A variadic list of arguments to be formatted. +// - sep: An optional separator string (default is a single space). +// +// Returns: A formatted string with a newline character at the end. +// aprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str) sbprintln(buf=&str, args=args, sep=sep) return strings.to_string(str) } -// aprintf procedure return a string that was allocated with the current context -// They must be freed accordingly +// Creates a formatted string using a format string and arguments +// +// *Allocates Using Context's Allocator* +// +// Inputs: +// - fmt: A format string with placeholders for the provided arguments. +// - args: A variadic list of arguments to be formatted. +// +// Returns: A formatted string. The returned string must be freed accordingly. +// aprintf :: proc(fmt: string, args: ..any) -> string { str: strings.Builder strings.builder_init(&str) sbprintf(&str, fmt, ..args) return strings.to_string(str) } - - -// tprint procedure return a string that was allocated with the current context's temporary allocator +// Creates a formatted string +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - args: A variadic list of arguments to be formatted. +// - sep: An optional separator string (default is a single space). +// +// Returns: A formatted string. +// tprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprint(buf=&str, args=args, sep=sep) return strings.to_string(str) } -// tprintln procedure return a string that was allocated with the current context's temporary allocator +// Creates a formatted string with a newline character at the end +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - args: A variadic list of arguments to be formatted. +// - sep: An optional separator string (default is a single space). +// +// Returns: A formatted string with a newline character at the end. +// tprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprintln(buf=&str, args=args, sep=sep) return strings.to_string(str) } -// tprintf procedure return a string that was allocated with the current context's temporary allocator +// Creates a formatted string using a format string and arguments +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - fmt: A format string with placeholders for the provided arguments. +// - args: A variadic list of arguments to be formatted. +// +// Returns: A formatted string. +// tprintf :: proc(fmt: string, args: ..any) -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprintf(&str, fmt, ..args) return strings.to_string(str) } - - -// bprint procedures return a string using a buffer from an array +// Creates a formatted string using a buffer from an array +// +// Inputs: +// - buf: The source buffer +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: A formatted string +// bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprint(buf=&sb, args=args, sep=sep) } -// bprintln procedures return a string using a buffer from an array +// Creates a formatted string with a newline character at the end using a buffer from an array +// +// Inputs: +// - buf: The source buffer +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: A formatted string with a newline character at the end +// bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprintln(buf=&sb, args=args, sep=sep) } -// bprintf procedures return a string using a buffer from an array +// Creates a formatted string using a buffer from an array and a format string +// +// Inputs: +// - buf: The source buffer +// - fmt: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted string +// bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprintf(&sb, fmt, ..args) } - - -// formatted assert +// Runtime assertion with a formatted message +// +// Inputs: +// - condition: The boolean condition to be asserted +// - fmt: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// - loc: The location of the caller +// +// Returns: True if the condition is met, otherwise triggers a runtime assertion with a formatted message +// assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool { if !condition { p := context.assertion_failure_proc @@ -151,8 +268,13 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati } return condition } - -// formatted panic +// Runtime panic with a formatted message +// +// Inputs: +// - fmt: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// - loc: The location of the caller +// panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { p := context.assertion_failure_proc if p == nil { @@ -161,8 +283,16 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { message := tprintf(fmt, ..args) p("Panic", message, loc) } - -// formatted printing for cstrings +// Creates a formatted C string +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - format: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted C string +// caprintf :: proc(format: string, args: ..any) -> cstring { str: strings.Builder strings.builder_init(&str) @@ -171,8 +301,16 @@ caprintf :: proc(format: string, args: ..any) -> cstring { s := strings.to_string(str) return cstring(raw_data(s)) } - -// c string with temp allocator +// Creates a formatted C string +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - format: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted C string +// ctprintf :: proc(format: string, args: ..any) -> cstring { str: strings.Builder strings.builder_init(&str, context.temp_allocator) @@ -181,27 +319,54 @@ ctprintf :: proc(format: string, args: ..any) -> cstring { s := strings.to_string(str) return cstring(raw_data(s)) } - -// sbprint formats using the default print settings and writes to buf +// Formats using the default print settings and writes to the given strings.Builder +// +// Inputs: +// - buf: A pointer to a strings.Builder to store the formatted string +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: A formatted string +// sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprint(w=strings.to_writer(buf), args=args, sep=sep) return strings.to_string(buf^) } - -// sbprintln formats using the default print settings and writes to buf +// Formats and writes to a strings.Builder buffer using the default print settings +// +// Inputs: +// - buf: A pointer to a strings.Builder buffer +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: The resulting formatted string +// sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprintln(w=strings.to_writer(buf), args=args, sep=sep) return strings.to_string(buf^) } - -// sbprintf formats according to the specififed format string and writes to buf +// Formats and writes to a strings.Builder buffer according to the specified format string +// +// Inputs: +// - buf: A pointer to a strings.Builder buffer +// - fmt: The format string +// - args: A variadic list of arguments to be formatted +// +// Returns: The resulting formatted string +// sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { wprintf(w=strings.to_writer(buf), fmt=fmt, args=args) return strings.to_string(buf^) } - - -// wprint formats using the default print settings and writes to w +// Formats and writes to an io.Writer using the default print settings +// +// Inputs: +// - w: An io.Writer to write to +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: The number of bytes written +// wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info fi.writer = w @@ -232,8 +397,15 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { return fi.n } - -// wprintln formats using the default print settings and writes to w +// Formats and writes to an io.Writer using the default print settings with a newline character at the end +// +// Inputs: +// - w: An io.Writer to write to +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: The number of bytes written +// wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info fi.writer = w @@ -249,8 +421,15 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { io.flush(auto_cast w) return fi.n } - -// wprintf formats according to the specififed format string and writes to w +// Formats and writes to an io.Writer according to the specified format string +// +// Inputs: +// - w: An io.Writer to write to +// - fmt: The format string +// - args: A variadic list of arguments to be formatted +// +// Returns: The number of bytes written +// wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi: Info arg_index: int = 0 @@ -526,23 +705,43 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { return fi.n } - -// wprint_type is a utility procedure to write a ^runtime.Type_Info value to w +// Writes a ^runtime.Type_Info value to an io.Writer +// +// Inputs: +// - w: An io.Writer to write to +// - info: A pointer to a runtime.Type_Info value +// +// Returns: The number of bytes written and an io.Error if encountered +// wprint_type :: proc(w: io.Writer, info: ^runtime.Type_Info) -> (int, io.Error) { n, err := reflect.write_type(w, info) io.flush(auto_cast w) return n, err } -// wprint_typeid is a utility procedure to write a typeid value to w +// Writes a typeid value to an io.Writer +// +// Inputs: +// - w: An io.Writer to write to +// - id: A typeid value +// +// Returns: The number of bytes written and an io.Error if encountered +// wprint_typeid :: proc(w: io.Writer, id: typeid) -> (int, io.Error) { n, err := reflect.write_type(w, type_info_of(id)) io.flush(auto_cast w) return n, err } - - - - +// Parses an integer from a given string starting at a specified offset +// +// Inputs: +// - s: The string to parse the integer from +// - offset: The position in the string to start parsing the integer +// +// Returns: +// - result: The parsed integer +// - new_offset: The position in the string after parsing the integer +// - ok: A boolean indicating if the parsing was successful +// _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: bool) { is_digit :: #force_inline proc(r: byte) -> bool { return '0' <= r && r <= '9' } @@ -560,7 +759,20 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: ok = new_offset > offset return } - +// Parses an argument number from a format string and determines if it's valid +// +// Inputs: +// - fi: A pointer to an Info structure +// - arg_index: The current argument index +// - format: The format string to parse +// - offset: The current position in the format string +// - arg_count: The total number of arguments +// +// Returns: +// - index: The parsed argument index +// - new_offset: The new position in the format string +// - ok: A boolean indicating if the parsed argument number is valid +// _arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count: int) -> (index, new_offset: int, ok: bool) { parse_arg_number :: proc(format: string) -> (int, int, bool) { if len(format) < 3 { @@ -594,7 +806,17 @@ _arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count fi.good_arg_index = false return arg_index, offset+width, false } - +// Retrieves an integer from a list of any type at the specified index +// +// Inputs: +// - args: A list of values of any type +// - arg_index: The index to retrieve the integer from +// +// Returns: +// - int: The integer value at the specified index +// - new_arg_index: The new argument index +// - ok: A boolean indicating if the conversion to integer was successful +// int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { num := 0 new_arg_index := arg_index @@ -609,8 +831,12 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { return num, new_arg_index, ok } - - +// Writes a bad verb error message +// +// Inputs: +// - fi: A pointer to an Info structure +// - verb: The invalid format verb +// fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { prev_in_bad := fi.in_bad defer fi.in_bad = prev_in_bad @@ -628,7 +854,13 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { } io.write_byte(writer, ')', &fi.n) } - +// Formats a boolean value according to the specified format verb +// +// Inputs: +// - fi: A pointer to an Info structure +// - b: The boolean value to format +// - verb: The format verb +// fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { switch verb { case 't', 'v': @@ -637,8 +869,12 @@ fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { fmt_bad_verb(fi, verb) } } - - +// Writes padding characters for formatting +// +// Inputs: +// - fi: A pointer to an Info structure +// - width: The number of padding characters to write +// fmt_write_padding :: proc(fi: ^Info, width: int) { if width <= 0 { return @@ -653,7 +889,18 @@ fmt_write_padding :: proc(fi: ^Info, width: int) { io.write_byte(fi.writer, pad_byte, &fi.n) } } - +// Formats an integer value with specified base, sign, bit size, and digits +// +// Inputs: +// - fi: A pointer to an Info structure +// - u: The integer value to format +// - base: The base for integer formatting +// - is_signed: A boolean indicating if the integer is signed +// - bit_size: The bit size of the integer +// - digits: A string containing the digits for formatting +// +// WARNING: May panic if the width and precision are too big, causing a buffer overrun +// _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, digits: string) { _, neg := strconv.is_integer_negative(u, is_signed, bit_size) @@ -718,7 +965,18 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d fi.zero = false _pad(fi, s) } - +// Formats an int128 value based on the provided formatting options. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - u: The int128 value to be formatted. +// - base: The base to be used for formatting the integer (e.g. 2, 8, 10, 12, 16). +// - is_signed: Whether the value should be treated as signed or unsigned. +// - bit_size: The number of bits of the value (e.g. 64, 128). +// - digits: A string containing the digit characters to use for the formatted integer. +// +// WARNING: Panics if the formatting options result in a buffer overrun. +// _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: int, digits: string) { _, neg := strconv.is_integer_negative_128(u, is_signed, bit_size) @@ -783,10 +1041,16 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i fi.zero = false _pad(fi, s) } - +// Hex Values: __DIGITS_LOWER := "0123456789abcdefx" __DIGITS_UPPER := "0123456789ABCDEFX" - +// Formats a rune value according to the specified formatting verb. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - r: The rune value to be formatted. +// - verb: The formatting verb to use (e.g. 'c', 'r', 'v', 'q'). +// fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { switch verb { case 'c', 'r', 'v': @@ -797,7 +1061,15 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { fmt_int(fi, u64(r), false, 32, verb) } } - +// Formats an integer value according to the specified formatting verb. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - u: The integer value to be formatted. +// - is_signed: Whether the value should be treated as signed or unsigned. +// - bit_size: The number of bits of the value (e.g. 32, 64). +// - verb: The formatting verb to use (e.g. 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X', 'c', 'r', 'U'). +// 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) @@ -822,7 +1094,15 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { fmt_bad_verb(fi, verb) } } - +// Formats an int128 value according to the specified formatting verb. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - u: The int128 value to be formatted. +// - is_signed: Whether the value should be treated as signed or unsigned. +// - bit_size: The number of bits of the value (e.g. 64, 128). +// - verb: The formatting verb to use (e.g. 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X', 'c', 'r', 'U'). +// 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) @@ -847,7 +1127,12 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru fmt_bad_verb(fi, verb) } } - +// Pads a formatted string with the appropriate padding, based on the provided formatting options. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - s: The string to be padded. +// _pad :: proc(fi: ^Info, s: string) { if !fi.width_set { io.write_string(fi.writer, s, &fi.n) @@ -864,7 +1149,17 @@ _pad :: proc(fi: ^Info, s: string) { io.write_string(fi.writer, s, &fi.n) } } - +// Formats a floating-point number with a specific format and precision. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - v: The floating-point number to format. +// - bit_size: The size of the floating-point number in bits (16, 32, or 64). +// - verb: The format specifier character. +// - float_fmt: The byte format used for formatting the float (either 'f' or 'e'). +// +// NOTE: Can return "NaN", "+Inf", "-Inf", "+", or "-". +// _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte) { prec := fi.prec if fi.prec_set else 3 buf: [386]byte @@ -881,7 +1176,14 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b _pad(fi, str) } - +// Formats a floating-point number with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - v: The floating-point number to format. +// - bit_size: The size of the floating-point number in bits (16, 32, or 64). +// - verb: The format specifier character. +// fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { switch verb { case 'f', 'F', 'g', 'G', 'v': @@ -914,8 +1216,13 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { fmt_bad_verb(fi, verb) } } - - +// Formats a string with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - s: The string to format. +// - verb: The format specifier character (e.g. 's', 'v', 'q', 'x', 'X'). +// fmt_string :: proc(fi: ^Info, s: string, verb: rune) { s, verb := s, verb if ol, ok := fi.optional_len.?; ok { @@ -973,10 +1280,23 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) { fmt_bad_verb(fi, verb) } } +// Formats a C-style string with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - s: The C-style string to format. +// - verb: The format specifier character (Ref fmt_string). +// fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) { fmt_string(fi, string(s), verb) } - +// Formats a raw pointer with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - p: The raw pointer to format. +// - verb: The format specifier character (e.g. 'p', 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X'). +// fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { u := u64(uintptr(p)) switch verb { @@ -997,7 +1317,13 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { fmt_bad_verb(fi, verb) } } - +// Formats a Structure of Arrays (SoA) pointer with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - p: The SoA pointer to format. +// - verb: The format specifier character. +// fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) { io.write_string(fi.writer, "#soa{data=0x", &fi.n) _fmt_int(fi, u64(uintptr(p.data)), 16, false, 8*size_of(rawptr), __DIGITS_UPPER) @@ -1005,8 +1331,13 @@ fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) { _fmt_int(fi, u64(p.index), 10, false, 8*size_of(rawptr), __DIGITS_UPPER) io.write_string(fi.writer, "}", &fi.n) } - - +// String representation of an enum value. +// +// Inputs: +// - val: The enum value. +// +// Returns: The string representation of the enum value and a boolean indicating success. +// enum_value_to_string :: proc(val: any) -> (string, bool) { v := val v.id = runtime.typeid_base(v.id) @@ -1036,7 +1367,14 @@ enum_value_to_string :: proc(val: any) -> (string, bool) { return "", false } - +// Returns the enum value of a string representation. +// +// $T: The typeid of the enum type. +// Inputs: +// - s: The string representation of the enum value. +// +// Returns: The enum value and a boolean indicating success. +// string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { ti := runtime.type_info_base(type_info_of(T)) if e, ok := ti.variant.(runtime.Type_Info_Enum); ok { @@ -1050,7 +1388,13 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { } return T{}, false } - +// Formats an enum value with a specific format. +// +// 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'). +// fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if v.id == nil || v.data == nil { io.write_string(fi.writer, "", &fi.n) @@ -1076,8 +1420,15 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { } } } - - +// Converts a stored enum value to a string representation +// +// Inputs: +// - enum_type: A pointer to the runtime.Type_Info of the enumeration. +// - ev: The runtime.Type_Info_Enum_Value of the stored enum value. +// - offset: An optional integer to adjust the enumeration value (default is 0). +// +// Returns: A tuple containing the string representation of the enum value and a bool indicating success. +// stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.Type_Info_Enum_Value, offset: int = 0) -> (string, bool) { et := runtime.type_info_base(enum_type) ev := ev @@ -1105,7 +1456,13 @@ stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.T return "", false } - +// Formats a bit set and writes it to the provided Info structure +// +// Inputs: +// - fi: A pointer to the Info structure where the formatted bit set will be written. +// - v: The bit set value to be formatted. +// - name: An optional string for the name of the bit set (default is an empty string). +// fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { if ti == nil { @@ -1199,13 +1556,26 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { } } } - +// Writes the specified number of indents to the provided Info structure +// +// Inputs: +// - fi: A pointer to the Info structure where the indents will be written. +// fmt_write_indent :: proc(fi: ^Info) { for in 0.. (do_continue: bool) { handle_optional_len :: proc(data: rawptr, info: reflect.Type_Info_Struct, field_name: string, optional_len: ^int) { @@ -1294,7 +1674,15 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: } return false } - +// Formats a struct for output, handling various struct types (e.g., SOA, raw unions) +// +// Inputs: +// - fi: A mutable pointer to an Info struct containing formatting state +// - v: The value to be formatted +// - the_verb: The formatting verb to be used (e.g. 'v') +// - info: Type information about the struct +// - 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' { fmt_bad_verb(fi, the_verb) @@ -1448,7 +1836,15 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St } } } - +// Searches for the first NUL-terminated element in a given buffer +// +// Inputs: +// - ptr: The raw pointer to the buffer. +// - elem_size: The size of each element in the buffer. +// - max_n: The maximum number of elements to search (use -1 for no limit). +// +// Returns: The number of elements before the first NUL-terminated element. +// @(private) search_nul_termination :: proc(ptr: rawptr, elem_size: int, max_n: int) -> (n: int) { for p := uintptr(ptr); max_n < 0 || n < max_n; p += uintptr(elem_size) { @@ -1459,7 +1855,16 @@ search_nul_termination :: proc(ptr: rawptr, elem_size: int, max_n: int) -> (n: i } return n } - +// Formats a NUL-terminated array into a string representation +// +// Inputs: +// - fi: Pointer to the formatting Info struct. +// - data: The raw pointer to the array data. +// - max_n: The maximum number of elements to process. +// - elem_size: The size of each element in the array. +// - elem: Pointer to the type information of the array element. +// - verb: The formatting verb. +// fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) { if data == nil { io.write_string(fi.writer, "", &fi.n) @@ -1468,7 +1873,16 @@ fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size: n := search_nul_termination(data, elem_size, max_n) fmt_array(fi, data, n, elem_size, elem, verb) } - +// Formats an array into a string representation +// +// Inputs: +// - fi: Pointer to the formatting Info struct. +// - data: The raw pointer to the array data. +// - n: The number of elements in the array. +// - elem_size: The size of each element in the array. +// - elem: Pointer to the type information of the array element. +// - verb: The formatting verb (e.g. 's','q','p'). +// fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) { if data == nil && n > 0 { io.write_string(fi.writer, "nil") @@ -1523,7 +1937,16 @@ fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflec fmt_write_array(fi, data, n, elem_size, elem.id, verb) } } - +// Formats a named type into a string representation +// +// Inputs: +// - fi: Pointer to the formatting Info struct. +// - v: The value to format. +// - verb: The formatting verb. +// - info: The named type information. +// +// NOTE: This procedure supports built-in custom formatters for core library types such as runtime.Source_Code_Location, time.Duration, and time.Time. +// fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) { write_padded_number :: proc(fi: ^Info, i: i64, width: int) { n := width-1 @@ -1672,7 +2095,15 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) fmt_value(fi, any{v.data, info.base.id}, verb) } } - +// Formats a union type into a string representation +// +// Inputs: +// - fi: Pointer to the formatting Info struct. +// - v: The value to format. +// - verb: The formatting verb. +// - info: The union type information. +// - type_size: The size of the union type. +// fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union, type_size: int) { if type_size == 0 { io.write_string(fi.writer, "nil", &fi.n) @@ -1718,7 +2149,14 @@ fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union, fmt_arg(fi, any{v.data, id}, verb) } } - +// Formats a matrix as a string +// +// Inputs: +// - fi: A pointer to an Info struct containing formatting information. +// - v: The matrix value to be formatted. +// - verb: The formatting verb rune. +// - 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) @@ -1761,7 +2199,15 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix fmt_write_indent(fi) } } - +// Formats a value based on its type and formatting verb +// +// Inputs: +// - fi: A pointer to an Info struct containing formatting information. +// - v: The value to be formatted. +// - verb: The formatting verb rune. +// +// NOTE: Uses user formatters if available and not ignored. +// fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if v.data == nil || v.id == nil { io.write_string(fi.writer, "", &fi.n) @@ -2106,7 +2552,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fmt_matrix(fi, v, verb, info) } } - +// Formats a complex number based on the given formatting verb +// +// Inputs: +// - 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'). +// fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { switch verb { case 'f', 'F', 'v', 'h', 'H': @@ -2123,7 +2576,14 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { return } } - +// Formats a quaternion number based on the given formatting verb +// +// Inputs: +// - 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'). +// fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { switch verb { case 'f', 'F', 'v', 'h', 'H': @@ -2154,7 +2614,15 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { return } } - +// Formats an argument based on its type and the given formatting verb +// +// Inputs: +// - fi: A pointer to an Info struct containing formatting information. +// - arg: The value to be formatted. +// - verb: The formatting verb rune (e.g. 'T'). +// +// NOTE: Uses user formatters if available and not ignored. +// fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { if arg == nil { io.write_string(fi.writer, "") @@ -2271,7 +2739,3 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { } } - - - - From 0892d84c17cbde40d79e09dfb8e35d47ac7f5eb1 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Sat, 25 Mar 2023 23:55:37 -0700 Subject: [PATCH 2/5] corrected bprint --- core/fmt/fmt.odin | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index d3ef777fc..d0074ebe1 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -208,10 +208,10 @@ tprintf :: proc(fmt: string, args: ..any) -> string { sbprintf(&str, fmt, ..args) return strings.to_string(str) } -// Creates a formatted string using a buffer from an array +// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. // // Inputs: -// - buf: The source buffer +// - buf: The backing buffer // - args: A variadic list of arguments to be formatted // - sep: An optional separator string (default is a single space) // @@ -221,10 +221,10 @@ bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprint(buf=&sb, args=args, sep=sep) } -// Creates a formatted string with a newline character at the end using a buffer from an array +// Creates a formatted string using a supplied buffer as the backing array, appends newline. Writes into the buffer. // // Inputs: -// - buf: The source buffer +// - buf: The backing buffer // - args: A variadic list of arguments to be formatted // - sep: An optional separator string (default is a single space) // @@ -234,10 +234,10 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprintln(buf=&sb, args=args, sep=sep) } -// Creates a formatted string using a buffer from an array and a format string +// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. // // Inputs: -// - buf: The source buffer +// - buf: The backing buffer // - fmt: A format string with placeholders for the provided arguments // - args: A variadic list of arguments to be formatted // From 34b037f19b201ee81f362a559038c6abe443cde9 Mon Sep 17 00:00:00 2001 From: jon lipstate <52809771+jon-lipstate@users.noreply.github.com> Date: Sun, 26 Mar 2023 11:23:37 -0700 Subject: [PATCH 3/5] Update fmt.odin Update example to use set/register procs. --- core/fmt/fmt.odin | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index d0074ebe1..f79c5b39f 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -53,23 +53,20 @@ User_Formatter :: #type proc(fi: ^Info, arg: any, verb: rune) -> bool // m := cast(^SomeType)arg.data // switch verb { // case 'v', 'd': -// fmt.fmt_int(fi, u64(m.value), true, 32, verb) +// fmt.fmt_int(fi, u64(m.value), true, 64, verb) // case: // return false // } // return true // } -// // // main :: proc() { // // Ensure the fmt._user_formatters map is initialized -// if fmt._user_formatters == nil { -// fmt._user_formatters = new(map[typeid]fmt.User_Formatter) -// } -// // Register the custom formatter directly to the _user_formatters map -// fmt._user_formatters[type_info_of(SomeType).id] = User_Formatter +// fmt.set_user_formatters(new(map[typeid]fmt.User_Formatter)) +// err := fmt.register_user_formatter(type_info_of(SomeType).id, User_Formatter) +// assert(err == .None) // // Use the custom formatter // x := SomeType{42} -// fmt.println("My custom type value: ", x) +// fmt.println("Custom type value: ", x) // } Register_User_Formatter_Error :: enum { @@ -90,7 +87,12 @@ _user_formatters: ^map[typeid]User_Formatter // NOTE: Must be called before using register_user_formatter. // set_user_formatters :: proc(m: ^map[typeid]User_Formatter) { - _user_formatters = m + if _user_formatters==nil { + _user_formatters = m + } + else { + panic("set_user_formatters must not be called more than once.") + } } // Registers a user-defined formatter for a specific typeid // From 030405dbb6f4ac53cd2063e8598433044cc3fe3e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 27 Mar 2023 11:06:29 +0200 Subject: [PATCH 4/5] Update fmt.odin Fix hardcoded 64 bit, use assert instead of branched panic. --- core/fmt/fmt.odin | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index f79c5b39f..3cd655eaf 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -53,7 +53,7 @@ User_Formatter :: #type proc(fi: ^Info, arg: any, verb: rune) -> bool // m := cast(^SomeType)arg.data // switch verb { // case 'v', 'd': -// fmt.fmt_int(fi, u64(m.value), true, 64, verb) +// fmt.fmt_int(fi, u64(m.value), true, 8 * size_of(SomeType), verb) // case: // return false // } @@ -87,12 +87,8 @@ _user_formatters: ^map[typeid]User_Formatter // NOTE: Must be called before using register_user_formatter. // set_user_formatters :: proc(m: ^map[typeid]User_Formatter) { - if _user_formatters==nil { - _user_formatters = m - } - else { - panic("set_user_formatters must not be called more than once.") - } + assert(_user_formatters == nil, "set_user_formatters must not be called more than once.") + _user_formatters = m } // Registers a user-defined formatter for a specific typeid // From 7ffca8ed5888c54e1d2cfba4408e23a4edf4961b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 27 Mar 2023 11:12:21 +0200 Subject: [PATCH 5/5] Fix caprintf comment --- core/fmt/fmt.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 3cd655eaf..8a5f54516 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -283,7 +283,7 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { } // Creates a formatted C string // -// *Allocates Using Context's Temporary Allocator* +// *Allocates Using Context's Allocator* // // Inputs: // - format: A format string with placeholders for the provided arguments