documented conv, base64, and ini; cleanup to format and parse
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 13 Feb 2023 04:24:46 +0000 (22:24 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 13 Feb 2023 04:24:46 +0000 (22:24 -0600)
core/conv/conv.onyx
core/conv/format.onyx
core/conv/parse.onyx
core/encoding/base64.onyx
core/encoding/ini.onyx

index b41e6e29f6ef2f099c98b0bffd880961554858a1..108f636d413c091588cac8da1d380bee7cd87ed4 100644 (file)
@@ -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
index fbac718242e2e675146e75dbb125e03409bd204f..a9c5269c6fd3d643babb008b7f9e6744c13df705 100644 (file)
@@ -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;
index babc51b96a28fc9278b6c81e1d2902c1758a6eae..0ecf2d41d5a1efe11c2dbe004a7314d22652a23a 100644 (file)
@@ -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);
     }
index d4c9454be8480c990cd0f8491f2e015070c38d90..f1936f666bd013a63e953e60160d240c00473cc1 100644 (file)
@@ -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;
 
index 17b59e0c4c3fc00457dbc68b4736a529f6160a0d..fc3c1fd52852b12723e8f6d64a438c4ec2be0358 100644 (file)
@@ -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);