From: Brendan Hansen Date: Mon, 13 Feb 2023 04:24:46 +0000 (-0600) Subject: documented conv, base64, and ini; cleanup to format and parse X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=d3f155e86124a902f884af237f70ab660d247c60;p=onyx.git documented conv, base64, and ini; cleanup to format and parse --- diff --git a/core/conv/conv.onyx b/core/conv/conv.onyx index b41e6e29..108f636d 100644 --- a/core/conv/conv.onyx +++ b/core/conv/conv.onyx @@ -4,6 +4,10 @@ Enable_Custom_Formatters :: true use core {string, math} +// +// Converts a string into an integer. Works with positive and +// negative integers. If given a pointer to a string, will +// modify the string to extract the integer part. str_to_i64 :: #match #local {} #overload @@ -61,6 +65,9 @@ str_to_i64 :: (s: ^str, base: u32 = 10) -> i64 { return value * ~~mul; } + +// +// Converts a string to a floating point number. str_to_f64 :: #match #local {} #overload @@ -136,6 +143,11 @@ str_to_f64 :: (s: ^str) -> f64 { } +// +// Converts an integer into a string using the buffer provided. +// Supports upto base 64. If prefix is true, binary numbers are +// prefixed with '0b' and hexadecimal numbers are prefixed with +// '0x'. i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str { is_neg := false; if n < 0 && base == 10 { @@ -200,6 +212,10 @@ i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) return str.{ data = c + 1, count = len }; } + +// +// Converts an unsigned number into a string using the buffer provided. +// Behaves like i64_to_str. u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str { c := ^buf[buf.count - 1]; len := 0; @@ -252,6 +268,10 @@ u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) return str.{ data = c + 1, count = len }; } +// +// Converts a floating point number into a string, using +// the buffer provided. +// // This is better than what used to be, but still relies on converting the integer // part of the float to an integer, which could overflow. f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str { @@ -298,7 +318,7 @@ f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str { // I like the way that parse_int reads better than 'str_to_i64'. // For a soft transistion, I am allowing the programmer to use either. -// At some point, it might be worth deprecating 'str_to_i64', but no +// At some point, it might be worth deprecating 'str_to_i64', but no harm // in leaving it here for now. Same thing applied to all other functions // below. parse_int :: str_to_i64 diff --git a/core/conv/format.onyx b/core/conv/format.onyx index fbac7182..a9c5269c 100644 --- a/core/conv/format.onyx +++ b/core/conv/format.onyx @@ -143,18 +143,26 @@ flush_to_dynstr :: (dynstr: ^[..] u8, to_write: str) => { format :: #match {} -#match format (buffer: [] u8, format: str, va: ..any) -> str { + +#overload +format :: (buffer: [] u8, format: str, va: ..any) -> str { return format_va(buffer, format, ~~va); } -#match format (output: ^Format_Output, format: str, va: ..any) -> str { + +#overload +format :: (output: ^Format_Output, format: str, va: ..any) -> str { return format_va(output, format, ~~va); } -#match format (buffer: ^[..] u8, format: str, va: ..any) { + +#overload +format :: (buffer: ^[..] u8, format: str, va: ..any) { buffer.count = buffer.capacity; out := format_va(*buffer, format, ~~va); buffer.count = out.count; } -#match format (format: str, va: ..any) -> str { + +#overload +format :: (format: str, va: ..any) -> str { buffer : [256] u8; out := make([..] u8); output := Format_Output.{ @@ -169,16 +177,22 @@ format :: #match {} } format_va :: #match {} -#match format_va (buffer: [] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) -> str { + +#overload +format_va :: (buffer: [] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) -> str { output := Format_Output.{ buffer.data, 0, buffer.count, flush }; return format_va(^output, format, va); } -#match format_va (buffer: ^[..] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) { + +#overload +format_va :: (buffer: ^[..] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) { buffer.count = buffer.capacity; out := format_va(*buffer, format, va, flush); buffer.count = out.count; } -#match format_va (format: [] u8, va: [] any, allocator := context.allocator) -> str { + +#overload +format_va :: (format: [] u8, va: [] any, allocator := context.allocator) -> str { buffer : [256] u8; out := make([..] u8, allocator=allocator); output := Format_Output.{ @@ -192,7 +206,8 @@ format_va :: #match {} return out; } -#match format_va (output: ^Format_Output, format: str, va: [] any) -> str { +#overload +format_va :: (output: ^Format_Output, format: str, va: [] any) -> str { vararg_index := 0; while i := 0; i < format.count { @@ -317,6 +332,7 @@ format_va :: #match {} return .{ output.data, output.count }; } + format_any :: (output: ^Format_Output, formatting: ^Format, v: any) { use package runtime.info array :: package core.array; diff --git a/core/conv/parse.onyx b/core/conv/parse.onyx index babc51b9..0ecf2d41 100644 --- a/core/conv/parse.onyx +++ b/core/conv/parse.onyx @@ -8,18 +8,14 @@ use core {map, string, array, math} // x: i32; // parse_any(^x, "12.34"); parse_any :: #match {} -#match parse_any (v: any, to_parse: str, string_allocator := context.allocator) -> bool { - use package runtime.info; - info := get_type_info(v.type); - if info.kind != .Pointer do return false; - - data_type := (cast(^Type_Info_Pointer) info).to; - target := *cast(^rawptr) v.data; - return parse_any(target, data_type, to_parse, string_allocator); +#overload +parse_any :: macro (v: ^$T, to_parse: str, string_allocator := context.allocator) -> bool { + return #this_package.parse_any(v, T, to_parse, string_allocator); } -#match parse_any (target: rawptr, data_type: type_expr, to_parse: str, string_allocator := context.allocator) -> bool { +#overload +parse_any :: (target: rawptr, data_type: type_expr, to_parse: str, string_allocator := context.allocator) -> bool { if custom_parsers->has(data_type) { return custom_parsers[data_type](target, to_parse, string_allocator); } diff --git a/core/encoding/base64.onyx b/core/encoding/base64.onyx index d4c9454b..f1936f66 100644 --- a/core/encoding/base64.onyx +++ b/core/encoding/base64.onyx @@ -1,5 +1,16 @@ package core.encoding.base64 +// +// A simple Base64 encoding and decoding library. Currently +// only supports base64 with + and / characters. A simple +// find and replace could be used to change to other base64 +// standards. +// + +// +// Encodes the given data in base64 into a new buffer, allocated +// from the allocator provided. It is the callers responsibilty +// to free this memory. encode :: (data: [] u8, allocator := context.allocator) -> [] u8 { out := make([..] u8, allocator=allocator); @@ -33,6 +44,9 @@ encode :: (data: [] u8, allocator := context.allocator) -> [] u8 { return out; } +// +// Decodes the given base64 data into a new buffer, allocated +// from the allocator provided. decode :: (data: [] u8, allocator := context.allocator) -> [] u8 { if data.count % 4 != 0 do return null_str; diff --git a/core/encoding/ini.onyx b/core/encoding/ini.onyx index 17b59e0c..fc3c1fd5 100644 --- a/core/encoding/ini.onyx +++ b/core/encoding/ini.onyx @@ -1,22 +1,69 @@ package core.encoding.ini +// +// This library allows for parsing and formatting a simple 'ini' like +// file format. It is very crude, but enables a quick way to create +// a configuration file that can be mapped to a structure. +// +// The data for the ini file is stored in a 2-deep nested structure. +// The outer structure stores the sections, and each section is +// represented as another structure. For example, +// +// Config :: struct { +// metadata: struct { +// name: str; +// version: str; +// }; +// +// player_settings: struct { +// speed: f32; +// display_name: str; +// }; +// } +// +// This corresponds to an ini file with the following data: +// +// [metadata] +// name=some name +// version=0.0.1 +// +// [player_settings] +// speed=1.2 +// display_name=Player +// + use core { alloc, conv, string, io, aprintf } +use core.intrinsics.types { + type_is_struct +} +// +// Represents if the parsing was successful or encountered an error. IniParseResult :: enum { Success; Error; } +// +// Represents an error that occured while parsing. IniParseError :: struct { msg: str; line: u32; } -parse_ini_file :: macro (r: ^io.Reader, output_ptr: ^$T) => { +// +// Parses an ini file into the variable given. Only structures +// work with this procedure, as the structure provides the section +// and value names. +parse_ini_file :: macro (r: ^io.Reader, output_ptr: ^$T/type_is_struct) => { parse_ini_file_inner :: parse_ini_file_inner + + // No need to make the actual implementation polymorphic. + // That can work with an any, but this macro provides proper errors + // a pointer to a structure is not provided. return parse_ini_file_inner(r, output_ptr); } @@ -115,7 +162,18 @@ parse_ini_file_inner :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, Ini return .Success, .{"",0}; } -write_ini_file :: (w: ^io.Writer, output: any) -> bool { + +// +// Outputs a two-level structure into an ini file. The inverse of `parse_ini_file`. +write_ini_file :: macro (w: ^io.Writer, output: $T/type_is_struct) => { + write_ini_file_inner :: write_ini_file_inner + + // See note above in parse_ini_file. + return write_ini_file_inner(w, output); +} + +#local +write_ini_file_inner :: (w: ^io.Writer, output: any) -> bool { info :: runtime.info output_info := cast(^info.Type_Info_Struct) info.get_type_info(output.type);