organized core libraries; bugfixes
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 3 Nov 2022 02:14:02 +0000 (21:14 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 3 Nov 2022 02:14:02 +0000 (21:14 -0500)
29 files changed:
build.sh
core/alloc.onyx [deleted file]
core/alloc/alloc.onyx [new file with mode: 0644]
core/arg_parse.onyx [deleted file]
core/container/map.onyx
core/conv.onyx [deleted file]
core/conv/conv.onyx [new file with mode: 0644]
core/conv/format.onyx [new file with mode: 0644]
core/conv/parse.onyx [new file with mode: 0644]
core/hash.onyx [deleted file]
core/hash/hash.onyx [new file with mode: 0644]
core/io/reader.onyx
core/io/stdio.onyx [new file with mode: 0644]
core/math.onyx [deleted file]
core/math/math.onyx [new file with mode: 0644]
core/memory.onyx [deleted file]
core/memory/memory.onyx [new file with mode: 0644]
core/misc/arg_parse.onyx [new file with mode: 0644]
core/net/net.onyx
core/net/tcp.onyx
core/random.onyx [deleted file]
core/random/random.onyx [new file with mode: 0644]
core/std.onyx
core/stdio.onyx [deleted file]
core/string.onyx [deleted file]
core/string/string.onyx [new file with mode: 0644]
scripts/onyx-pkg.onyx
shared/lib/linux_x86_64/lib/libovmwasm.so
tests/i32map.onyx

index 8cc45b8d1cc0db885f2089f29c98080bac2ad56c..5fa26f96dca76c76c029bd30ad470684a1361296 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -3,6 +3,7 @@
 . ./settings.sh
 
 echo "Installing core libs"
+[ -d "$CORE_DIR/core" ] && sudo rm -r "$CORE_DIR/core"
 sudo mkdir -p "$CORE_DIR"
 sudo cp -r ./core/ "$CORE_DIR"
 
diff --git a/core/alloc.onyx b/core/alloc.onyx
deleted file mode 100644 (file)
index eb2dc29..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-package core.alloc
-
-#load "./alloc/arena"
-#load "./alloc/fixed"
-#load "./alloc/heap"
-#load "./alloc/ring"
-#load "./alloc/pool"
-#load "./alloc/logging"
-#load "./alloc/gc"
-
-as_allocator :: #match {
-    macro (a: Allocator) => a
-}
-
-// This is similar to alloca in C.
-from_stack :: macro (size: u32) -> rawptr {
-    // This should do something about the alignment...
-    // Everything so far has assume that the stack is aligned to 16 bytes.
-    defer __stack_top = ~~(cast(^u8) __stack_top + size);
-    return __stack_top;
-}
-
-array_from_stack :: macro ($T: type_expr, size: u32) -> [] T {
-    defer __stack_top = ~~(cast(^u8) __stack_top + size * sizeof T);
-    return (cast(^T) __stack_top)[0 .. size];
-}
-
-TEMPORARY_ALLOCATOR_SIZE :: 1 << 16; // 16Kb
-
-// The global heap allocator, set up upon program intialization.
-heap_allocator : Allocator;
-
-// The global temp allocator, set up upon program intialization.
-#local #thread_local
-temp_state     : arena.ArenaState;
-
-#thread_local
-temp_allocator : Allocator;
-
-init_temp_allocator :: () {
-    temp_state = arena.make(heap_allocator, TEMPORARY_ALLOCATOR_SIZE);
-    temp_allocator = as_allocator(^temp_state);
-}
-
-clear_temp_allocator :: () {
-    arena.clear(^temp_state);
-}
diff --git a/core/alloc/alloc.onyx b/core/alloc/alloc.onyx
new file mode 100644 (file)
index 0000000..13550f7
--- /dev/null
@@ -0,0 +1,47 @@
+package core.alloc
+
+#load "./arena"
+#load "./fixed"
+#load "./heap"
+#load "./ring"
+#load "./pool"
+#load "./logging"
+#load "./gc"
+
+as_allocator :: #match {
+    macro (a: Allocator) => a
+}
+
+// This is similar to alloca in C.
+from_stack :: macro (size: u32) -> rawptr {
+    // This should do something about the alignment...
+    // Everything so far has assume that the stack is aligned to 16 bytes.
+    defer __stack_top = ~~(cast(^u8) __stack_top + size);
+    return __stack_top;
+}
+
+array_from_stack :: macro ($T: type_expr, size: u32) -> [] T {
+    defer __stack_top = ~~(cast(^u8) __stack_top + size * sizeof T);
+    return (cast(^T) __stack_top)[0 .. size];
+}
+
+TEMPORARY_ALLOCATOR_SIZE :: 1 << 16; // 16Kb
+
+// The global heap allocator, set up upon program intialization.
+heap_allocator : Allocator;
+
+// The global temp allocator, set up upon program intialization.
+#local #thread_local
+temp_state     : arena.ArenaState;
+
+#thread_local
+temp_allocator : Allocator;
+
+init_temp_allocator :: () {
+    temp_state = arena.make(heap_allocator, TEMPORARY_ALLOCATOR_SIZE);
+    temp_allocator = as_allocator(^temp_state);
+}
+
+clear_temp_allocator :: () {
+    arena.clear(^temp_state);
+}
diff --git a/core/arg_parse.onyx b/core/arg_parse.onyx
deleted file mode 100644 (file)
index cde4b24..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-package core.arg_parse
-
-use core
-
-arg_parse :: (c_args: [] cstr, output: any) -> bool {
-    arg_iter := iter.as_iterator(c_args)
-             |> iter.map((x) => string.from_cstr(*x));
-    defer arg_iter.close(arg_iter.data);
-
-    use runtime.info;
-
-    ptr_type := cast(^Type_Info_Pointer) get_type_info(output.type);
-    if ptr_type.kind != .Pointer do return false;
-
-    arg_type := cast(^Type_Info_Struct) get_type_info(ptr_type.to);
-    if arg_type.kind != .Struct do return false;
-
-    data_base := *cast(^rawptr) output.data;
-
-    for #no_close arg: arg_iter {
-        for ^member: arg_type.members {
-            for ^tag: member.tags {
-                if tag.type != str do continue;
-
-                to_match := *cast(^str) tag.data;
-                if arg != to_match do continue;
-
-                switch member.type {
-                    case bool {
-                        *(cast(^bool) (cast(^u8) data_base + member.offset)) = !*(cast(^bool) (cast(^u8) data_base + member.offset));
-                    }
-
-                    case i32 {
-                        value_str, success := iter.take_one(arg_iter, no_close=true);
-                        if !success do return false;
-
-                        value := conv.str_to_i64(value_str);
-                        *(cast(^i32) (cast(^u8) data_base + member.offset)) = ~~value;
-                    }
-
-                    case str {
-                        value, success := iter.take_one(arg_iter, no_close=true);
-                        if !success do return false;
-
-                        *(cast(^str) (cast(^u8) data_base + member.offset)) = value;
-                    }
-
-                    case #default {
-                        printf("Unsupported argument type, {}.\n", output.type);
-                        return false;
-                    }
-                }
-            }
-        }
-    }
-
-    return true;
-}
\ No newline at end of file
index 618e8fffb54610754f9b07de5858c87850e5f072..a372c3a3011c3e5cfc115fb59593019a10f2d57f 100644 (file)
@@ -149,11 +149,21 @@ empty :: (use map: ^Map) -> bool {
 }
 
 format_map :: (output: ^conv.Format_Output, format: ^conv.Format, x: ^Map($K, $V)) {
-    output->write("{\n");
-    for^ x.entries {
-        conv.format(output, "    {\"p} => {\"p}\n", it.key, it.value);
+    if format.pretty_printing {
+        output->write("{\n");
+        for^ x.entries {
+            conv.format(output, "    {\"p} => {\"p}\n", it.key, it.value);
+        }
+        output->write("}");
+
+    } else {
+        output->write("{ ");
+        for^ x.entries {
+            if !#first do output->write(", ");
+            conv.format(output, "{\"p} => {\"p}", it.key, it.value);
+        }
+        output->write(" }");
     }
-    output->write("}");
 }
 
 //
diff --git a/core/conv.onyx b/core/conv.onyx
deleted file mode 100644 (file)
index ef5c476..0000000
+++ /dev/null
@@ -1,992 +0,0 @@
-package core.conv
-
-Enable_Custom_Formatters :: true
-
-#local {
-    use core {map, string, array, math}
-
-    custom_formatters: Map(type_expr, #type (^Format_Output, ^Format, rawptr) -> void);
-    custom_parsers   : Map(type_expr, #type (rawptr, str, Allocator) -> bool);
-}
-
-custom_formatters_initialized :: #init () {
-    map.init(^custom_formatters, default=null_proc);
-    map.init(^custom_parsers,    default=null_proc);
-
-    #if Enable_Custom_Formatters {
-        use package runtime.info;
-
-        for type_idx: type_table.count {
-            type := type_table[type_idx];
-            if type.kind != .Struct do continue;
-
-            s_info := cast(^Type_Info_Struct) type;
-            for s_info.tags {
-                if it.type == Custom_Format {
-                    custom_format := cast(^Custom_Format) it.data;
-                    custom_formatters[cast(type_expr) type_idx] = custom_format.format;
-                }
-
-                if it.type == Custom_Parse {
-                    custom_parse := cast(^Custom_Parse) it.data;
-                    custom_parsers[cast(type_expr) type_idx] = custom_parse.parse;
-                }
-            } 
-        }
-    }
-}
-
-register_custom_formatter :: (formatter: (^Format_Output, ^Format, ^$T) -> void) {
-    custom_formatters[T] = formatter;
-}
-
-register_custom_parser :: (parser: (^$T, str, Allocator) -> bool) {
-    custom_parsers[T] = parser;
-}
-
-Custom_Format :: struct {
-    format: (^Format_Output, ^Format, rawptr) -> void;
-}
-
-Custom_Parse :: struct {
-    parse: (rawptr, str, Allocator) -> bool;
-}
-
-str_to_i64 :: #match #local {}
-
-#overload
-str_to_i64 :: macro (s: str, base: u32 = 10) -> i64 {
-    str_to_i64 :: str_to_i64;
-    s_ := s;
-    return str_to_i64(^s_, base);
-}
-
-#overload
-str_to_i64 :: (s: ^str, base: u32 = 10) -> i64 {
-    use package core
-
-    value: i64 = 0;
-    mul := 1;
-
-    if s.data[0] == #char "-" {
-        mul = -1;
-        string.advance(s, 1);
-    }
-
-    if s.data[0] == #char "+" {
-         string.advance(s, 1);
-    }
-
-    while !string.empty(*s) {
-        switch c := s.data[0]; c {
-            case #char "0" .. #char "9" {
-                value *= ~~base;
-                value += ~~(c - #char "0");
-            }
-
-            case #char "A" .. #char "Z" {
-                if base <= 10 do fallthrough;
-
-                value *= ~~base;
-                value += ~~((c - #char "A") + 10);
-            }
-            
-            case #char "a" .. #char "z" {
-                if base <= 10 do fallthrough;
-
-                value *= ~~base;
-                value += ~~((c - #char "a") + 10);
-            }
-
-            case #default do break break;
-        }
-
-        string.advance(s);
-    }
-
-    return value * ~~mul;
-}
-
-str_to_f64 :: #match #local {}
-
-#overload
-str_to_f64 :: macro (s: str) -> f64 {
-    str_to_f64 :: str_to_f64;
-    s_ := s;
-    return str_to_f64(^s_);
-}
-
-#overload
-str_to_f64 :: (s: ^str) -> f64 {
-    use package core
-
-    string.strip_leading_whitespace(s);
-
-    sign := parse_sign(s);
-    value, _ := parse_digits(s);    
-
-    if s.data[0] == #char "." {
-        string.advance(s, 1);
-        fraction, fraction_digits := parse_digits(s);
-        while fraction_digits > 0 {
-            fraction_digits -= 1;
-            fraction /= 10;
-        }
-        value += fraction;
-    }
-
-    value *= sign;
-
-    if s.data[0] != #char "e" && s.data[0] != #char "E" do return value;
-    string.advance(s, 1);
-
-    exponent_sign := parse_sign(s);
-    exponent, _   := parse_digits(s);
-    if exponent_sign > 0 {
-        while exponent > 0 {
-            value *= 10;
-            exponent -= 1;
-        }
-    } else {
-        while exponent > 0 {
-            value /= 10;
-            exponent -= 1;
-        }
-    }
-
-    return value;
-
-
-    parse_sign :: (s: ^str) -> f64 {
-        switch s.data[0] {
-            case #char "-" { string.advance(s, 1); return -1; }
-            case #char "+" { string.advance(s, 1); return  1; }
-            case #default  { return 1; }
-        }
-    }
-
-    parse_digits :: (s: ^str) -> (f64, digit_count: i32) {
-        value: f64 = 0;
-        count := 0;
-        while s.count > 0 do switch s.data[0] {
-            case #char "0" .. #char "9" {
-                value = value * 10 + ~~cast(i32)(s.data[0] - #char "0");
-                string.advance(s, 1);
-                count += 1;
-            }
-
-            case #default do break break;
-        }
-        return value, count;
-    }
-}
-
-i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
-    is_neg := false;
-    if n < 0 && base == 10 {
-        is_neg = true;
-        n = -n;
-    }
-
-    c := ^buf[buf.count - 1];
-    len := 0;
-
-    BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
-
-    while n > 0 {
-        m := cast(u64) n % base;
-
-        *c = BASE64_MAP[cast(u32) m];
-        len += 1;
-        c -= 1;
-
-        n /= base;
-
-    } else {
-        *c = #char "0";
-        len += 1;
-        c -= 1;
-    }
-
-    if min_length > 0 && len < min_length {
-        for i: min_length - len {
-            *c = #char "0";
-            len += 1;
-            c -= 1;
-        }
-    }
-
-    if prefix {
-        if base == 16 {
-            *c = #char "x";
-            len += 1;
-            c -= 1;
-            *c = #char "0";
-            len += 1;
-            c -= 1;
-        }
-
-        if base == 2 {
-            *c = #char "b";
-            len += 1;
-            c -= 1;
-            *c = #char "0";
-            len += 1;
-            c -= 1;
-        }
-    }
-
-    if is_neg {
-        *c = #char "-";
-        len += 1;
-        c -= 1;
-    }
-
-    return str.{ data = c + 1, count = len };
-}
-
-u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
-    c := ^buf[buf.count - 1];
-    len := 0;
-
-    BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
-
-    while n > 0 {
-        m := cast(u64) n % base;
-
-        *c = BASE64_MAP[cast(u32) m];
-        len += 1;
-        c -= 1;
-
-        n /= base;
-
-    } else {
-        *c = #char "0";
-        len += 1;
-        c -= 1;
-    }
-
-    if min_length > 0 && len < min_length {
-        for i: min_length - len {
-            *c = #char "0";
-            len += 1;
-            c -= 1;
-        }
-    }
-
-    if prefix {
-        if base == 16 {
-            *c = #char "x";
-            len += 1;
-            c -= 1;
-            *c = #char "0";
-            len += 1;
-            c -= 1;
-        }
-
-        if base == 2 {
-            *c = #char "b";
-            len += 1;
-            c -= 1;
-            *c = #char "0";
-            len += 1;
-            c -= 1;
-        }
-    }
-
-    return str.{ data = c + 1, count = len };
-}
-
-// 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 {
-    if math.is_nan(f) {
-        return format(buf, "NaN");
-    }
-
-    if math.is_inf(f) {
-        if f > 0 do return format(buf, "Inf");
-        else     do return format(buf, "-Inf");
-    }
-
-    len := 0;
-
-    if f < 0 {
-        f = -f;
-        buf[0] = #char "-";
-        len += 1;
-    }
-
-    dec_part := f - math.trunc(f);
-    int_part := f - dec_part;
-    dec_part  = math.abs(dec_part);
-
-    s1 := i64_to_str(~~int_part, 10, buf);
-    for i: 0 .. s1.count do buf.data[i + len] = s1.data[i];
-    buf.data[s1.count + len] = #char ".";
-    len += s1.count + 1;
-
-    digits := "0123456789";
-
-    for i: digits_after_decimal {
-        dec_part *= 10;
-        v := math.trunc(dec_part);
-        dec_part -= v;
-
-        buf.data[len + i] = digits[cast(i32) v];
-    }
-    len += digits_after_decimal;
-
-    return str.{ buf.data, len };
-}
-
-// @Remove // old aliases to not break old programs
-str_format :: format
-str_format_va :: format_va
-
-Format_Flush_Callback :: struct {
-    data: rawptr = null;
-    func: (rawptr, str) -> bool = null_proc;
-}
-
-Format_Output :: struct {
-    data: ^u8;
-    count: u32;
-    capacity: u32;
-
-    // When the data buffer fills, this procedure
-    // is called with the data, allowing for the
-    // buffer to be cleared and more to be written.
-    flush: Format_Flush_Callback;
-
-    write :: #match {
-        (use output: ^Format_Output, c: u8) {
-            if count >= capacity {
-                if flush.func == null_proc                   do return;
-                if !flush.func(flush.data, data[0 .. count]) do return;
-                count = 0;
-            }
-
-            data[count] = c;
-            count += 1;
-        },
-
-        (use output: ^Format_Output, s: str) {
-            for c: s {
-                if count >= capacity {
-                    if flush.func == null_proc                   do return;
-                    if !flush.func(flush.data, data[0 .. count]) do return;
-                    count = 0;
-                }
-
-                data[count] = c;
-                count += 1;
-            }
-        }
-    }
-}
-
-Format :: struct {
-    pretty_printing      := false;
-    quote_strings        := false;
-    single_quote_strings := false;
-    dereference          := false;
-    custom_format        := true;
-    interpret_numbers    := true;
-    digits_after_decimal := cast(u32) 4;
-
-    indentation   := cast(u32) 0;
-    base          := cast(u64) 10;
-    minimum_width := cast(u32) 0;
-}
-
-#local
-flush_to_dynstr :: (dynstr: ^[..] u8, to_write: str) => {
-    array.concat(dynstr, to_write);
-    return true;
-}
-
-
-format :: #match {}
-#match format (buffer: [] u8, format: str, va: ..any) -> str {
-    return format_va(buffer, format, ~~va); 
-}
-#match format (output: ^Format_Output, format: str, va: ..any) -> str {
-    return format_va(output, format, ~~va); 
-}
-#match 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 {
-    buffer : [256] u8;
-    out    := make([..] u8);
-    output := Format_Output.{
-        ~~buffer, 0, buffer.count,
-        flush=.{ ^out, flush_to_dynstr }
-    };
-
-    final := format_va(^output, format, ~~va);
-    array.concat(^out, final);
-
-    return out;
-}
-
-format_va :: #match {}
-#match 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.{}) {
-    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 {
-    buffer : [256] u8;
-    out    := make([..] u8, allocator=allocator);
-    output := Format_Output.{
-        ~~buffer, 0, buffer.count,
-        flush=.{ ^out, flush_to_dynstr }
-    };
-
-    final := format_va(^output, format, ~~va);
-    array.concat(^out, final);
-
-    return out;
-}
-
-#match format_va (output: ^Format_Output, format: str, va: [] any) -> str {
-    vararg_index := 0;
-
-    while i := 0; i < format.count {
-        defer i += 1;
-
-        ch := format[i];
-        formatting := Format.{};
-
-        if ch == #char "{" {
-            if format[i + 1] == #char "{" {
-                output->write(#char "{");
-                i += 1;
-                continue;
-            }
-
-            i += 1;
-            while true {
-                ch = format[i];
-
-                switch ch {
-                    case #char "*" {
-                        i += 1;
-                        formatting.dereference = true;
-                    }
-
-                    case #char "." {
-                        i += 1;
-
-                        digits := 0;
-                        while format[i] >= #char "0" && format[i] <= #char "9" {
-                            digits *= 10;
-                            digits += ~~(format[i] - #char "0");
-                            i += 1;
-                        }
-
-                        ch = format[i];
-                        formatting.digits_after_decimal = digits;
-                    }
-
-                    case #char "p" {
-                        i += 1;
-                        formatting.pretty_printing = true;
-                    }
-
-                    case #char "x" {
-                        i += 1;
-                        formatting.base = 16;
-                    }
-
-                    case #char "b" {
-                        i += 1;
-
-                        digits := 0;
-                        while format[i] >= #char "0" && format[i] <= #char "9" {
-                            digits *= 10;
-                            digits += ~~(format[i] - #char "0");
-                            i += 1;
-                        }
-
-                        formatting.base = ~~digits;
-                    }
-
-                    case #char "w" {
-                        i += 1;
-
-                        digits := 0;
-                        while format[i] >= #char "0" && format[i] <= #char "9" {
-                            digits *= 10;
-                            digits += ~~(format[i] - #char "0");
-                            i += 1;
-                        }
-
-                        formatting.minimum_width = ~~digits;
-                    }
-
-                    case #char "!" {
-                        i += 1;
-                        formatting.custom_format = false;
-                    }
-
-                    case #char "\"" {
-                        i += 1;
-                        formatting.quote_strings = true;
-                    }
-
-                    case #char "'" {
-                        i += 1;
-                        formatting.single_quote_strings = true;
-                    }
-
-                    case #char "d" {
-                        i += 1;
-                        formatting.interpret_numbers = false;
-                    }
-
-                    case #char "}" {
-                        arg := va[vararg_index];
-                        vararg_index += 1;
-                        format_any(output, ^formatting, arg);
-
-                        break break;
-                    }
-
-                    case #default do break break;
-                }
-            }
-        }
-
-        if ch == #char "}" {
-            if format[i + 1] == #char "}" {
-                output->write(#char "}");
-                i += 1;
-                continue;
-            }
-
-            continue;
-        }
-
-        output->write(ch);
-    }
-
-    return .{ output.data, output.count };
-}
-
-format_any :: (output: ^Format_Output, formatting: ^Format, v: any) {
-    use package runtime.info
-    array :: package core.array;
-
-    if formatting.dereference {
-        ti := get_type_info(v.type);
-        if ti.kind == .Pointer {
-            formatting.dereference = false;
-
-            new_any: any;
-            new_any.type = (cast(^Type_Info_Pointer) ti).to;
-            new_any.data = *(cast(^rawptr) v.data);
-            format_any(output, formatting, new_any);
-            return;
-        }
-    }
-
-    if formatting.custom_format && custom_formatters->has(v.type) {
-        custom_formatters[v.type](output, formatting, v.data);
-        return;
-    }
-
-    switch v.type {
-        case bool {
-            value := *(cast(^bool) v.data);
-            if value do output->write("true");
-            else     do output->write("false");
-        }
-
-        case u8 {
-            value := *(cast(^u8) v.data);
-
-            if value > 31 {
-                output->write(value);
-
-            } else {
-                ibuf : [128] u8;
-                istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
-                output->write(istr);
-            }
-        }
-
-        int_case :: macro (T: type_expr) {
-            case T {
-                value := *(cast(^T) v.data);
-
-                ibuf : [128] u8;
-                istr := i64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width);
-                output->write(istr);
-            }
-        }
-
-        uint_case :: macro (T: type_expr) {
-            case T {
-                value := *(cast(^T) v.data);
-
-                ibuf : [128] u8;
-                istr := u64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width);
-                output->write(istr);
-            }
-        }
-
-        int_case(i8);
-        int_case(i16);
-        int_case(i32);
-        int_case(i64);
-        uint_case(u16);
-        uint_case(u32);
-        uint_case(u64);
-
-        case f32 {
-            value := *(cast(^f32) v.data);
-
-            fbuf : [128] u8;
-            fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal);
-            output->write(fstr);
-        }
-
-        case f64 {
-            value := *(cast(^f64) v.data);
-
-            fbuf : [128] u8;
-            fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal);
-            output->write(fstr);
-        }
-
-        case str {
-            if formatting.quote_strings do output->write("\"");
-            if formatting.single_quote_strings do output->write("'");
-            width := formatting.minimum_width;
-            to_output := *cast(^str) v.data;
-
-            // @Todo // escape '"' when quote_strings is enabled.
-            output->write(to_output);
-            if to_output.count < width && !(formatting.quote_strings || formatting.single_quote_strings) {
-                for width - to_output.count do output->write(#char " ");
-            }
-
-            if formatting.quote_strings do output->write("\"");
-            if formatting.single_quote_strings do output->write("'");
-        }
-
-        case rawptr {
-            value := *(cast(^rawptr) v.data);
-
-            if value == null {
-                output->write("(null)");
-            } else {
-                ibuf : [128] u8;
-                istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
-                output->write(istr);
-            }
-        }
-
-        case type_expr {
-            value := *(cast(^type_expr) v.data);
-
-            io :: package core.io
-
-            buf : [256] u8;          
-
-            // This is a little gross but the only way to output the type name for a type_expr
-            // is through a io.Writer. That should maybe be changed in the future? Also, I think
-            // 256 bytes is enough for the name of a type but I'm not entirely sure...
-            stream := io.buffer_stream_make(~~buf, fixed=true);
-            writer := io.writer_make(^stream);
-            write_type_name(^writer, value);
-
-            output->write(io.buffer_stream_to_str(^stream));
-        }
-
-        case #default {
-            info := get_type_info(v.type);
-
-            if info.kind == .Struct {
-                s := cast(^Type_Info_Struct) info;
-
-                if s.name.count > 0 {
-                    output->write(s.name);
-                    output->write(" { ");
-                } else {
-                    output->write("{ ");
-                }
-
-                {
-                    format := *formatting;
-                    format.quote_strings = true;
-                    if format.pretty_printing {
-                        format.indentation += 4;
-                    }
-                    
-                    for ^member: s.members {
-                        if member != s.members.data do output->write(", ");
-
-                        if formatting.pretty_printing {
-                            output->write(#char "\n");
-                            for i: format.indentation do output->write(#char " ");
-                        }
-
-                        output->write(member.name);
-                        output->write(" = ");
-
-                        format_any(output, ^format, .{ ~~(cast(^u8) v.data + member.offset), member.type });
-                    }
-                }
-                
-                if formatting.pretty_printing {
-                    output->write(#char "\n");
-                    for i: formatting.indentation do output->write(#char " ");
-                    output->write("}");
-                    
-                } else {
-                    output->write(" }");
-                }
-            }
-
-            if info.kind == .Function {
-                output->write("func[");
-
-                value := *(cast(^i32) v.data);
-
-                ibuf : [128] u8;
-                istr := i64_to_str(~~value, 10, ~~ibuf);
-                output->write(istr);
-
-                output->write("]");
-            }
-
-            if info.kind == .Pointer {
-                value := *(cast(^rawptr) v.data);
-
-                ibuf : [128] u8;
-                istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
-                output->write(istr);
-            }
-
-            // This assumes that the following type_info kinds are basically the same.
-            if info.kind == .Dynamic_Array || info.kind == .Slice || info.kind == .Variadic_Argument {
-                if formatting.pretty_printing {
-                    output->write("[");
-                } else {
-                    output->write("[ ");
-                }
-
-                a := cast(^Type_Info_Dynamic_Array) info;
-                arr := cast(^array.Untyped_Array) v.data;
-                data  := arr.data;
-                count := arr.count;
-
-                format := *formatting;
-                format.quote_strings = true;
-                if format.pretty_printing do format.indentation += 4;
-
-                for i: count {
-                    if i != 0 do output->write(", ");
-
-                    if formatting.pretty_printing {
-                        output->write("\n");
-                        for _: format.indentation do output->write(#char " ");
-                    }
-
-                    format_any(output, ^format, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of });
-                }
-
-
-                if formatting.pretty_printing {
-                    format.indentation -= 4;
-                    output->write("\n");
-                    for _: format.indentation do output->write(#char " ");
-                    output->write(#char "]");
-
-                } else {
-                    output->write(" ]");
-                }
-            }
-
-            if info.kind == .Array {
-                output->write("[ ");
-
-                a := cast(^Type_Info_Array) info;
-                data := v.data;
-
-                for i: a.count {
-                    if i != 0 do output->write(", ");
-
-                    format_any(output, formatting, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of });
-                }
-
-                output->write(" ]");
-            }
-
-            if info.kind == .Enum {
-                e := cast(^Type_Info_Enum) info;
-
-                value: u64;
-                switch e.backing_type {
-                    case i8,  u8  do value = cast(u64) *(cast(^u8) v.data);
-                    case i16, u16 do value = cast(u64) *(cast(^u16) v.data);
-                    case i32, u32 do value = cast(u64) *(cast(^u32) v.data);
-                    case i64, u64 do value = cast(u64) *(cast(^u64) v.data);
-                    case #default do assert(false, "Bad enum backing type");
-                }
-
-                if !formatting.interpret_numbers {
-                    format_any(output, formatting, .{^value, u64});
-                    break;
-                }
-
-                if !e.is_flags {
-                    for ^member: e.members {
-                        if value == member.value {
-                            output->write(member.name);
-                            break break;
-                        }
-                    }
-
-                    output->write("UNKNOWN");
-
-                } else {
-                    first := true;
-                    for ^member: e.members {
-                        if value & member.value != 0 {
-                            if !first do output->write(" | ");
-                            output->write(member.name);
-                            first = false;
-                        }
-                    }
-
-                    if first {
-                        output->write("None");
-                    }
-                }
-            }
-
-            if info.kind == .Distinct {
-                d := cast(^Type_Info_Distinct) info;
-
-                if formatting.interpret_numbers {
-                    output->write(d.name);
-                    output->write("[");
-                }
-
-                format_any(output, formatting, any.{ v.data, d.base_type });
-
-                if formatting.interpret_numbers {
-                    output->write("]");
-                }
-            }
-        }
-    }
-}
-
-//
-// This should be called with a pointer for the first argument.
-//
-//      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);
-}
-
-#match 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);
-    }
-
-    use runtime.info;
-    info := get_type_info(data_type);
-
-    switch data_type {
-        case bool {
-            dest := cast(^bool) target;
-            *dest = false;
-            if to_parse[0] == #char "t" || to_parse[0] == #char "T" {
-                *dest = true;
-            }
-            return true;
-        }
-
-        integer_case :: macro (T: type_expr) {
-            case T {
-                dest := cast(^T) target;
-                *dest = cast(T) str_to_i64(to_parse);
-                return true;
-            }
-        }
-
-        integer_case(i8);
-        integer_case(i16);
-        integer_case(i32);
-        integer_case(i64);
-        integer_case(u8);
-        integer_case(u16);
-        integer_case(u32);
-        integer_case(u64);
-
-        case f32 {
-            dest := cast(^f32) target;
-            *dest = ~~ str_to_f64(to_parse);
-            return true;
-        }
-
-        case f64 {
-            dest := cast(^f64) target;
-            *dest = ~~ str_to_f64(to_parse);
-            return true;
-        }
-
-        case str {
-            if to_parse.count == 0       do return false;
-            if to_parse[0] != #char "\"" do return false;
-            line := to_parse;
-            string.advance(^line);
-
-            //
-            // For now, this will return a substring in the original to_parse.
-            dest := cast(^str) target;
-            *dest = string.read_until(^line, #char "\"") |> string.alloc_copy(string_allocator); // @BUG // This does not handle escaped strings!
-            return true;
-        }
-
-        case #default {
-            if info.kind == .Enum {
-                // TEMPORARY this needs to look at the backing type for the
-                // enum in order to know how large this integer should be.
-                *cast(^u32) target = ~~ str_to_i64(to_parse);
-                return true;
-            }
-
-            if info.kind == .Distinct {
-                d_info := cast(^Type_Info_Distinct) info;
-                return parse_any(target, d_info.base_type, to_parse, string_allocator);
-            }
-        }
-    }
-
-    return false;
-}
-
-
diff --git a/core/conv/conv.onyx b/core/conv/conv.onyx
new file mode 100644 (file)
index 0000000..2d47551
--- /dev/null
@@ -0,0 +1,295 @@
+package core.conv
+
+Enable_Custom_Formatters :: true
+
+use core {map, string, array, math}
+
+str_to_i64 :: #match #local {}
+
+#overload
+str_to_i64 :: macro (s: str, base: u32 = 10) -> i64 {
+    str_to_i64 :: str_to_i64;
+    s_ := s;
+    return str_to_i64(^s_, base);
+}
+
+#overload
+str_to_i64 :: (s: ^str, base: u32 = 10) -> i64 {
+    use package core
+
+    value: i64 = 0;
+    mul := 1;
+
+    if s.data[0] == #char "-" {
+        mul = -1;
+        string.advance(s, 1);
+    }
+
+    if s.data[0] == #char "+" {
+         string.advance(s, 1);
+    }
+
+    while !string.empty(*s) {
+        switch c := s.data[0]; c {
+            case #char "0" .. #char "9" {
+                value *= ~~base;
+                value += ~~(c - #char "0");
+            }
+
+            case #char "A" .. #char "Z" {
+                if base <= 10 do fallthrough;
+
+                value *= ~~base;
+                value += ~~((c - #char "A") + 10);
+            }
+            
+            case #char "a" .. #char "z" {
+                if base <= 10 do fallthrough;
+
+                value *= ~~base;
+                value += ~~((c - #char "a") + 10);
+            }
+
+            case #default do break break;
+        }
+
+        string.advance(s);
+    }
+
+    return value * ~~mul;
+}
+
+str_to_f64 :: #match #local {}
+
+#overload
+str_to_f64 :: macro (s: str) -> f64 {
+    str_to_f64 :: str_to_f64;
+    s_ := s;
+    return str_to_f64(^s_);
+}
+
+#overload
+str_to_f64 :: (s: ^str) -> f64 {
+    use package core
+
+    string.strip_leading_whitespace(s);
+
+    sign := parse_sign(s);
+    value, _ := parse_digits(s);    
+
+    if s.data[0] == #char "." {
+        string.advance(s, 1);
+        fraction, fraction_digits := parse_digits(s);
+        while fraction_digits > 0 {
+            fraction_digits -= 1;
+            fraction /= 10;
+        }
+        value += fraction;
+    }
+
+    value *= sign;
+
+    if s.data[0] != #char "e" && s.data[0] != #char "E" do return value;
+    string.advance(s, 1);
+
+    exponent_sign := parse_sign(s);
+    exponent, _   := parse_digits(s);
+    if exponent_sign > 0 {
+        while exponent > 0 {
+            value *= 10;
+            exponent -= 1;
+        }
+    } else {
+        while exponent > 0 {
+            value /= 10;
+            exponent -= 1;
+        }
+    }
+
+    return value;
+
+
+    parse_sign :: (s: ^str) -> f64 {
+        switch s.data[0] {
+            case #char "-" { string.advance(s, 1); return -1; }
+            case #char "+" { string.advance(s, 1); return  1; }
+            case #default  { return 1; }
+        }
+    }
+
+    parse_digits :: (s: ^str) -> (f64, digit_count: i32) {
+        value: f64 = 0;
+        count := 0;
+        while s.count > 0 do switch s.data[0] {
+            case #char "0" .. #char "9" {
+                value = value * 10 + ~~cast(i32)(s.data[0] - #char "0");
+                string.advance(s, 1);
+                count += 1;
+            }
+
+            case #default do break break;
+        }
+        return value, count;
+    }
+}
+
+i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
+    is_neg := false;
+    if n < 0 && base == 10 {
+        is_neg = true;
+        n = -n;
+    }
+
+    c := ^buf[buf.count - 1];
+    len := 0;
+
+    BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
+
+    while n > 0 {
+        m := cast(u64) n % base;
+
+        *c = BASE64_MAP[cast(u32) m];
+        len += 1;
+        c -= 1;
+
+        n /= base;
+
+    } else {
+        *c = #char "0";
+        len += 1;
+        c -= 1;
+    }
+
+    if min_length > 0 && len < min_length {
+        for i: min_length - len {
+            *c = #char "0";
+            len += 1;
+            c -= 1;
+        }
+    }
+
+    if prefix {
+        if base == 16 {
+            *c = #char "x";
+            len += 1;
+            c -= 1;
+            *c = #char "0";
+            len += 1;
+            c -= 1;
+        }
+
+        if base == 2 {
+            *c = #char "b";
+            len += 1;
+            c -= 1;
+            *c = #char "0";
+            len += 1;
+            c -= 1;
+        }
+    }
+
+    if is_neg {
+        *c = #char "-";
+        len += 1;
+        c -= 1;
+    }
+
+    return str.{ data = c + 1, count = len };
+}
+
+u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
+    c := ^buf[buf.count - 1];
+    len := 0;
+
+    BASE64_MAP := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
+
+    while n > 0 {
+        m := cast(u64) n % base;
+
+        *c = BASE64_MAP[cast(u32) m];
+        len += 1;
+        c -= 1;
+
+        n /= base;
+
+    } else {
+        *c = #char "0";
+        len += 1;
+        c -= 1;
+    }
+
+    if min_length > 0 && len < min_length {
+        for i: min_length - len {
+            *c = #char "0";
+            len += 1;
+            c -= 1;
+        }
+    }
+
+    if prefix {
+        if base == 16 {
+            *c = #char "x";
+            len += 1;
+            c -= 1;
+            *c = #char "0";
+            len += 1;
+            c -= 1;
+        }
+
+        if base == 2 {
+            *c = #char "b";
+            len += 1;
+            c -= 1;
+            *c = #char "0";
+            len += 1;
+            c -= 1;
+        }
+    }
+
+    return str.{ data = c + 1, count = len };
+}
+
+// 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 {
+    if math.is_nan(f) {
+        return format(buf, "NaN");
+    }
+
+    if math.is_inf(f) {
+        if f > 0 do return format(buf, "Inf");
+        else     do return format(buf, "-Inf");
+    }
+
+    len := 0;
+
+    if f < 0 {
+        f = -f;
+        buf[0] = #char "-";
+        len += 1;
+    }
+
+    dec_part := f - math.trunc(f);
+    int_part := f - dec_part;
+    dec_part  = math.abs(dec_part);
+
+    s1 := i64_to_str(~~int_part, 10, buf);
+    for i: 0 .. s1.count do buf.data[i + len] = s1.data[i];
+    buf.data[s1.count + len] = #char ".";
+    len += s1.count + 1;
+
+    digits := "0123456789";
+
+    for i: digits_after_decimal {
+        dec_part *= 10;
+        v := math.trunc(dec_part);
+        dec_part -= v;
+
+        buf.data[len + i] = digits[cast(i32) v];
+    }
+    len += digits_after_decimal;
+
+    return str.{ buf.data, len };
+}
+
+
diff --git a/core/conv/format.onyx b/core/conv/format.onyx
new file mode 100644 (file)
index 0000000..07002c9
--- /dev/null
@@ -0,0 +1,605 @@
+package core.conv
+
+use core {map, string, array, math}
+
+#package {
+    custom_formatters: Map(type_expr, #type (^Format_Output, ^Format, rawptr) -> void);
+    custom_parsers   : Map(type_expr, #type (rawptr, str, Allocator) -> bool);
+}
+
+custom_formatters_initialized :: #init () {
+    map.init(^custom_formatters, default=null_proc);
+    map.init(^custom_parsers,    default=null_proc);
+
+    #if Enable_Custom_Formatters {
+        use package runtime.info;
+
+        for type_idx: type_table.count {
+            type := type_table[type_idx];
+            if type.kind != .Struct do continue;
+
+            s_info := cast(^Type_Info_Struct) type;
+            for s_info.tags {
+                if it.type == Custom_Format {
+                    custom_format := cast(^Custom_Format) it.data;
+                    custom_formatters[cast(type_expr) type_idx] = custom_format.format;
+                }
+
+                if it.type == Custom_Parse {
+                    custom_parse := cast(^Custom_Parse) it.data;
+                    custom_parsers[cast(type_expr) type_idx] = custom_parse.parse;
+                }
+            } 
+        }
+    }
+}
+
+register_custom_formatter :: (formatter: (^Format_Output, ^Format, ^$T) -> void) {
+    custom_formatters[T] = formatter;
+}
+
+register_custom_parser :: (parser: (^$T, str, Allocator) -> bool) {
+    custom_parsers[T] = parser;
+}
+
+Custom_Format :: struct {
+    format: (^Format_Output, ^Format, rawptr) -> void;
+}
+
+Custom_Parse :: struct {
+    parse: (rawptr, str, Allocator) -> bool;
+}
+
+// @Remove // old aliases to not break old programs
+str_format :: format
+str_format_va :: format_va
+
+Format_Flush_Callback :: struct {
+    data: rawptr = null;
+    func: (rawptr, str) -> bool = null_proc;
+}
+
+Format_Output :: struct {
+    data: ^u8;
+    count: u32;
+    capacity: u32;
+
+    // When the data buffer fills, this procedure
+    // is called with the data, allowing for the
+    // buffer to be cleared and more to be written.
+    flush: Format_Flush_Callback;
+
+    write :: #match {
+        (use output: ^Format_Output, c: u8) {
+            if count >= capacity {
+                if flush.func == null_proc                   do return;
+                if !flush.func(flush.data, data[0 .. count]) do return;
+                count = 0;
+            }
+
+            data[count] = c;
+            count += 1;
+        },
+
+        (use output: ^Format_Output, s: str) {
+            for c: s {
+                if count >= capacity {
+                    if flush.func == null_proc                   do return;
+                    if !flush.func(flush.data, data[0 .. count]) do return;
+                    count = 0;
+                }
+
+                data[count] = c;
+                count += 1;
+            }
+        }
+    }
+}
+
+Format :: struct {
+    pretty_printing      := false;
+    quote_strings        := false;
+    single_quote_strings := false;
+    dereference          := false;
+    custom_format        := true;
+    interpret_numbers    := true;
+    digits_after_decimal := cast(u32) 4;
+
+    indentation   := cast(u32) 0;
+    base          := cast(u64) 10;
+    minimum_width := cast(u32) 0;
+}
+
+#local
+flush_to_dynstr :: (dynstr: ^[..] u8, to_write: str) => {
+    array.concat(dynstr, to_write);
+    return true;
+}
+
+
+format :: #match {}
+#match format (buffer: [] u8, format: str, va: ..any) -> str {
+    return format_va(buffer, format, ~~va); 
+}
+#match format (output: ^Format_Output, format: str, va: ..any) -> str {
+    return format_va(output, format, ~~va); 
+}
+#match 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 {
+    buffer : [256] u8;
+    out    := make([..] u8);
+    output := Format_Output.{
+        ~~buffer, 0, buffer.count,
+        flush=.{ ^out, flush_to_dynstr }
+    };
+
+    final := format_va(^output, format, ~~va);
+    array.concat(^out, final);
+
+    return out;
+}
+
+format_va :: #match {}
+#match 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.{}) {
+    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 {
+    buffer : [256] u8;
+    out    := make([..] u8, allocator=allocator);
+    output := Format_Output.{
+        ~~buffer, 0, buffer.count,
+        flush=.{ ^out, flush_to_dynstr }
+    };
+
+    final := format_va(^output, format, ~~va);
+    array.concat(^out, final);
+
+    return out;
+}
+
+#match format_va (output: ^Format_Output, format: str, va: [] any) -> str {
+    vararg_index := 0;
+
+    while i := 0; i < format.count {
+        defer i += 1;
+
+        ch := format[i];
+        formatting := Format.{};
+
+        if ch == #char "{" {
+            if format[i + 1] == #char "{" {
+                output->write(#char "{");
+                i += 1;
+                continue;
+            }
+
+            i += 1;
+            while true {
+                ch = format[i];
+
+                switch ch {
+                    case #char "*" {
+                        i += 1;
+                        formatting.dereference = true;
+                    }
+
+                    case #char "." {
+                        i += 1;
+
+                        digits := 0;
+                        while format[i] >= #char "0" && format[i] <= #char "9" {
+                            digits *= 10;
+                            digits += ~~(format[i] - #char "0");
+                            i += 1;
+                        }
+
+                        ch = format[i];
+                        formatting.digits_after_decimal = digits;
+                    }
+
+                    case #char "p" {
+                        i += 1;
+                        formatting.pretty_printing = true;
+                    }
+
+                    case #char "x" {
+                        i += 1;
+                        formatting.base = 16;
+                    }
+
+                    case #char "b" {
+                        i += 1;
+
+                        digits := 0;
+                        while format[i] >= #char "0" && format[i] <= #char "9" {
+                            digits *= 10;
+                            digits += ~~(format[i] - #char "0");
+                            i += 1;
+                        }
+
+                        formatting.base = ~~digits;
+                    }
+
+                    case #char "w" {
+                        i += 1;
+
+                        digits := 0;
+                        while format[i] >= #char "0" && format[i] <= #char "9" {
+                            digits *= 10;
+                            digits += ~~(format[i] - #char "0");
+                            i += 1;
+                        }
+
+                        formatting.minimum_width = ~~digits;
+                    }
+
+                    case #char "!" {
+                        i += 1;
+                        formatting.custom_format = false;
+                    }
+
+                    case #char "\"" {
+                        i += 1;
+                        formatting.quote_strings = true;
+                    }
+
+                    case #char "'" {
+                        i += 1;
+                        formatting.single_quote_strings = true;
+                    }
+
+                    case #char "d" {
+                        i += 1;
+                        formatting.interpret_numbers = false;
+                    }
+
+                    case #char "}" {
+                        arg := va[vararg_index];
+                        vararg_index += 1;
+                        format_any(output, ^formatting, arg);
+
+                        break break;
+                    }
+
+                    case #default do break break;
+                }
+            }
+        }
+
+        if ch == #char "}" {
+            if format[i + 1] == #char "}" {
+                output->write(#char "}");
+                i += 1;
+                continue;
+            }
+
+            continue;
+        }
+
+        output->write(ch);
+    }
+
+    return .{ output.data, output.count };
+}
+
+format_any :: (output: ^Format_Output, formatting: ^Format, v: any) {
+    use package runtime.info
+    array :: package core.array;
+
+    if formatting.dereference {
+        ti := get_type_info(v.type);
+        if ti.kind == .Pointer {
+            formatting.dereference = false;
+
+            new_any: any;
+            new_any.type = (cast(^Type_Info_Pointer) ti).to;
+            new_any.data = *(cast(^rawptr) v.data);
+            format_any(output, formatting, new_any);
+            return;
+        }
+    }
+
+    if formatting.custom_format && custom_formatters->has(v.type) {
+        custom_formatters[v.type](output, formatting, v.data);
+        return;
+    }
+
+    switch v.type {
+        case bool {
+            value := *(cast(^bool) v.data);
+            if value do output->write("true");
+            else     do output->write("false");
+        }
+
+        case u8 {
+            value := *(cast(^u8) v.data);
+
+            if value > 31 {
+                output->write(value);
+
+            } else {
+                ibuf : [128] u8;
+                istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
+                output->write(istr);
+            }
+        }
+
+        int_case :: macro (T: type_expr) {
+            case T {
+                value := *(cast(^T) v.data);
+
+                ibuf : [128] u8;
+                istr := i64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width);
+                output->write(istr);
+            }
+        }
+
+        uint_case :: macro (T: type_expr) {
+            case T {
+                value := *(cast(^T) v.data);
+
+                ibuf : [128] u8;
+                istr := u64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width);
+                output->write(istr);
+            }
+        }
+
+        int_case(i8);
+        int_case(i16);
+        int_case(i32);
+        int_case(i64);
+        uint_case(u16);
+        uint_case(u32);
+        uint_case(u64);
+
+        case f32 {
+            value := *(cast(^f32) v.data);
+
+            fbuf : [128] u8;
+            fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal);
+            output->write(fstr);
+        }
+
+        case f64 {
+            value := *(cast(^f64) v.data);
+
+            fbuf : [128] u8;
+            fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal);
+            output->write(fstr);
+        }
+
+        case str {
+            if formatting.quote_strings do output->write("\"");
+            if formatting.single_quote_strings do output->write("'");
+            width := formatting.minimum_width;
+            to_output := *cast(^str) v.data;
+
+            // @Todo // escape '"' when quote_strings is enabled.
+            output->write(to_output);
+            if to_output.count < width && !(formatting.quote_strings || formatting.single_quote_strings) {
+                for width - to_output.count do output->write(#char " ");
+            }
+
+            if formatting.quote_strings do output->write("\"");
+            if formatting.single_quote_strings do output->write("'");
+        }
+
+        case rawptr {
+            value := *(cast(^rawptr) v.data);
+
+            if value == null {
+                output->write("(null)");
+            } else {
+                ibuf : [128] u8;
+                istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
+                output->write(istr);
+            }
+        }
+
+        case type_expr {
+            value := *(cast(^type_expr) v.data);
+
+            io :: package core.io
+
+            buf : [256] u8;          
+
+            // This is a little gross but the only way to output the type name for a type_expr
+            // is through a io.Writer. That should maybe be changed in the future? Also, I think
+            // 256 bytes is enough for the name of a type but I'm not entirely sure...
+            stream := io.buffer_stream_make(~~buf, fixed=true);
+            writer := io.writer_make(^stream);
+            write_type_name(^writer, value);
+
+            output->write(io.buffer_stream_to_str(^stream));
+        }
+
+        case #default {
+            info := get_type_info(v.type);
+
+            if info.kind == .Struct {
+                s := cast(^Type_Info_Struct) info;
+
+                if s.name.count > 0 {
+                    output->write(s.name);
+                    output->write(" { ");
+                } else {
+                    output->write("{ ");
+                }
+
+                {
+                    format := *formatting;
+                    format.quote_strings = true;
+                    if format.pretty_printing {
+                        format.indentation += 4;
+                    }
+                    
+                    for ^member: s.members {
+                        if member != s.members.data do output->write(", ");
+
+                        if formatting.pretty_printing {
+                            output->write(#char "\n");
+                            for i: format.indentation do output->write(#char " ");
+                        }
+
+                        output->write(member.name);
+                        output->write(" = ");
+
+                        format_any(output, ^format, .{ ~~(cast(^u8) v.data + member.offset), member.type });
+                    }
+                }
+                
+                if formatting.pretty_printing {
+                    output->write(#char "\n");
+                    for i: formatting.indentation do output->write(#char " ");
+                    output->write("}");
+                    
+                } else {
+                    output->write(" }");
+                }
+            }
+
+            if info.kind == .Function {
+                output->write("func[");
+
+                value := *(cast(^i32) v.data);
+
+                ibuf : [128] u8;
+                istr := i64_to_str(~~value, 10, ~~ibuf);
+                output->write(istr);
+
+                output->write("]");
+            }
+
+            if info.kind == .Pointer {
+                value := *(cast(^rawptr) v.data);
+
+                ibuf : [128] u8;
+                istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true);
+                output->write(istr);
+            }
+
+            // This assumes that the following type_info kinds are basically the same.
+            if info.kind == .Dynamic_Array || info.kind == .Slice || info.kind == .Variadic_Argument {
+                if formatting.pretty_printing {
+                    output->write("[");
+                } else {
+                    output->write("[ ");
+                }
+
+                a := cast(^Type_Info_Dynamic_Array) info;
+                arr := cast(^array.Untyped_Array) v.data;
+                data  := arr.data;
+                count := arr.count;
+
+                format := *formatting;
+                format.quote_strings = true;
+                if format.pretty_printing do format.indentation += 4;
+
+                for i: count {
+                    if i != 0 do output->write(", ");
+
+                    if formatting.pretty_printing {
+                        output->write("\n");
+                        for _: format.indentation do output->write(#char " ");
+                    }
+
+                    format_any(output, ^format, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of });
+                }
+
+
+                if formatting.pretty_printing {
+                    format.indentation -= 4;
+                    output->write("\n");
+                    for _: format.indentation do output->write(#char " ");
+                    output->write(#char "]");
+
+                } else {
+                    output->write(" ]");
+                }
+            }
+
+            if info.kind == .Array {
+                output->write("[ ");
+
+                a := cast(^Type_Info_Array) info;
+                data := v.data;
+
+                for i: a.count {
+                    if i != 0 do output->write(", ");
+
+                    format_any(output, formatting, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of });
+                }
+
+                output->write(" ]");
+            }
+
+            if info.kind == .Enum {
+                e := cast(^Type_Info_Enum) info;
+
+                value: u64;
+                switch e.backing_type {
+                    case i8,  u8  do value = cast(u64) *(cast(^u8) v.data);
+                    case i16, u16 do value = cast(u64) *(cast(^u16) v.data);
+                    case i32, u32 do value = cast(u64) *(cast(^u32) v.data);
+                    case i64, u64 do value = cast(u64) *(cast(^u64) v.data);
+                    case #default do assert(false, "Bad enum backing type");
+                }
+
+                if !formatting.interpret_numbers {
+                    format_any(output, formatting, .{^value, u64});
+                    break;
+                }
+
+                if !e.is_flags {
+                    for ^member: e.members {
+                        if value == member.value {
+                            output->write(member.name);
+                            break break;
+                        }
+                    }
+
+                    output->write("UNKNOWN");
+
+                } else {
+                    first := true;
+                    for ^member: e.members {
+                        if value & member.value != 0 {
+                            if !first do output->write(" | ");
+                            output->write(member.name);
+                            first = false;
+                        }
+                    }
+
+                    if first {
+                        output->write("None");
+                    }
+                }
+            }
+
+            if info.kind == .Distinct {
+                d := cast(^Type_Info_Distinct) info;
+
+                if formatting.interpret_numbers {
+                    output->write(d.name);
+                    output->write("[");
+                }
+
+                format_any(output, formatting, any.{ v.data, d.base_type });
+
+                if formatting.interpret_numbers {
+                    output->write("]");
+                }
+            }
+        }
+    }
+}
diff --git a/core/conv/parse.onyx b/core/conv/parse.onyx
new file mode 100644 (file)
index 0000000..babc51b
--- /dev/null
@@ -0,0 +1,98 @@
+package core.conv
+
+use core {map, string, array, math}
+
+//
+// This should be called with a pointer for the first argument.
+//
+//      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);
+}
+
+#match 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);
+    }
+
+    use runtime.info;
+    info := get_type_info(data_type);
+
+    switch data_type {
+        case bool {
+            dest := cast(^bool) target;
+            *dest = false;
+            if to_parse[0] == #char "t" || to_parse[0] == #char "T" {
+                *dest = true;
+            }
+            return true;
+        }
+
+        integer_case :: macro (T: type_expr) {
+            case T {
+                dest := cast(^T) target;
+                *dest = cast(T) str_to_i64(to_parse);
+                return true;
+            }
+        }
+
+        integer_case(i8);
+        integer_case(i16);
+        integer_case(i32);
+        integer_case(i64);
+        integer_case(u8);
+        integer_case(u16);
+        integer_case(u32);
+        integer_case(u64);
+
+        case f32 {
+            dest := cast(^f32) target;
+            *dest = ~~ str_to_f64(to_parse);
+            return true;
+        }
+
+        case f64 {
+            dest := cast(^f64) target;
+            *dest = ~~ str_to_f64(to_parse);
+            return true;
+        }
+
+        case str {
+            if to_parse.count == 0       do return false;
+            if to_parse[0] != #char "\"" do return false;
+            line := to_parse;
+            string.advance(^line);
+
+            //
+            // For now, this will return a substring in the original to_parse.
+            dest := cast(^str) target;
+            *dest = string.read_until(^line, #char "\"") |> string.alloc_copy(string_allocator); // @BUG // This does not handle escaped strings!
+            return true;
+        }
+
+        case #default {
+            if info.kind == .Enum {
+                // TEMPORARY this needs to look at the backing type for the
+                // enum in order to know how large this integer should be.
+                *cast(^u32) target = ~~ str_to_i64(to_parse);
+                return true;
+            }
+
+            if info.kind == .Distinct {
+                d_info := cast(^Type_Info_Distinct) info;
+                return parse_any(target, d_info.base_type, to_parse, string_allocator);
+            }
+        }
+    }
+
+    return false;
+}
diff --git a/core/hash.onyx b/core/hash.onyx
deleted file mode 100644 (file)
index 364ef6a..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package core.hash
-
-to_u32 :: #match {
-    (key: rawptr) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; },
-    (key: i8)     -> u32 { return ~~ key; },
-    (key: i16)    -> u32 { return 0x9ce7 ^ cast(u32) key; },
-    (key: i32)    -> u32 { return 0xcbf29ce7 ^ cast(u32) key; },
-    (key: i64)    -> u32 { return cast(u32) (cast(u64) 0xcbf29ce7 ^ cast(u64) key); },
-    (key: str)    -> u32 {
-        hash: u32 = 5381;
-        for ch: key do hash += (hash << 5) + ~~ch;
-        return hash;
-    },
-    (key: type_expr) -> u32 { return to_u32(cast(u32) key); },
-
-    #precedence 10000
-    macro (key: $T/HasHashMethod) => key->hash()
-}
-
-Hashable :: interface (t: $T) {
-    { to_u32(t) } -> u32;
-}
-
-#local
-HasHashMethod :: interface (t: $T) {
-    { t->hash() } -> u32;
-}
-
diff --git a/core/hash/hash.onyx b/core/hash/hash.onyx
new file mode 100644 (file)
index 0000000..364ef6a
--- /dev/null
@@ -0,0 +1,28 @@
+package core.hash
+
+to_u32 :: #match {
+    (key: rawptr) -> u32 { return 0xcbf29ce7 ^ cast(u32) key; },
+    (key: i8)     -> u32 { return ~~ key; },
+    (key: i16)    -> u32 { return 0x9ce7 ^ cast(u32) key; },
+    (key: i32)    -> u32 { return 0xcbf29ce7 ^ cast(u32) key; },
+    (key: i64)    -> u32 { return cast(u32) (cast(u64) 0xcbf29ce7 ^ cast(u64) key); },
+    (key: str)    -> u32 {
+        hash: u32 = 5381;
+        for ch: key do hash += (hash << 5) + ~~ch;
+        return hash;
+    },
+    (key: type_expr) -> u32 { return to_u32(cast(u32) key); },
+
+    #precedence 10000
+    macro (key: $T/HasHashMethod) => key->hash()
+}
+
+Hashable :: interface (t: $T) {
+    { to_u32(t) } -> u32;
+}
+
+#local
+HasHashMethod :: interface (t: $T) {
+    { t->hash() } -> u32;
+}
+
index c69aa790ac15815cbe36e005029b76d8071cbaa8..f695029b2d5e3433d886ed39a113f5c9507b9605 100644 (file)
@@ -20,7 +20,9 @@ Reader :: struct {
     // that is available. Only set this if you know that the data
     // will always be available.
     greedy : bool;
+}
 
+#inject Reader {
     is_empty :: reader_empty;
     empty    :: reader_empty;
     read_all :: read_all;
diff --git a/core/io/stdio.onyx b/core/io/stdio.onyx
new file mode 100644 (file)
index 0000000..b79f573
--- /dev/null
@@ -0,0 +1,173 @@
+package core
+// Currently, these symbols are dumped in the 'core' namespace, which means
+// most programs that just 'use package core' can access all of them, which
+// is convenient; However, it doesn't hurt to wonder if they should be the
+// 'core.io' package so these would become like 'io.printf(...)'. Of course,
+// you could always still do 'use package core.io', which would bring these
+// in anyway.
+
+
+#if runtime.runtime == .Custom {
+    #error "'stdio' can only be included in the 'wasi' or 'js' runtime."
+}
+
+stdio_stream: io.Stream;
+
+auto_flush_stdio := true
+
+print :: #match #locked {
+    (x: str) {
+        io.write(^stdio.print_writer, x);
+        if x[x.count - 1] == #char "\n" && auto_flush_stdio do __flush_stdio();
+    },
+
+    (x)    => { io.write(^stdio.print_writer, x); },
+    (x, y) => { io.write(^stdio.print_writer, x, y); },
+}
+
+println :: (x) => {
+    print(x);
+    print("\n");
+}
+
+// Standard formatted print.
+printf :: (format: str, va: ..any) {
+    flush :: (_, to_output) => {
+        io.write(^stdio.print_writer, to_output);
+        __flush_stdio();
+        return true;
+    }
+
+    buffer: [1024] u8;
+    print(conv.format_va(buffer, format, va, .{null, flush}));
+}
+
+// Print to standard error, if available.
+#if #defined(runtime.__output_error) {
+    eprintf :: (format: str, va: ..any) -> str {
+        flush :: (_, to_output) => {
+            runtime.__output_error(to_output);
+            return true;
+        }
+
+        buffer: [1024] u8;
+        runtime.__output_error(conv.format_va(buffer, format, va, .{null, flush}));
+    }
+}
+
+// Print to a dynamically allocated string.
+aprintf :: (format: str, va: ..any) -> str {
+    buffer: [8196] u8;
+    out := conv.format_va(buffer, format, va);
+    return string.alloc_copy(out);
+}
+
+// Print to a TEMPORARY dynamically allocated string.
+tprintf :: (format: str, va: ..any) -> str {
+    buffer: [8196] u8;
+    out := conv.format_va(buffer, format, va);
+    return string.alloc_copy(out, allocator=context.temp_allocator);
+}
+
+byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) {
+    temp: [3] u8;
+
+    u8_ptr := cast(^u8) ptr;
+    for i: byte_count {
+        val := u8_ptr[i];
+
+        temp[0] = map_to_ascii(val >> 4);
+        temp[1] = map_to_ascii(val & 15);
+        temp[2] = #char " ";
+
+        runtime.__output_string(~~temp);
+
+        if i % bytes_per_line == (bytes_per_line - 1) do runtime.__output_string("\n");
+    }
+
+
+    map_to_ascii :: (x: u8) -> u8 {
+        switch x {
+            case 0 do return  #char "0";
+            case 1 do return  #char "1";
+            case 2 do return  #char "2";
+            case 3 do return  #char "3";
+            case 4 do return  #char "4";
+            case 5 do return  #char "5";
+            case 6 do return  #char "6";
+            case 7 do return  #char "7";
+            case 8 do return  #char "8";
+            case 9 do return  #char "9";
+            case 10 do return #char "A";
+            case 11 do return #char "B";
+            case 12 do return #char "C";
+            case 13 do return #char "D";
+            case 14 do return #char "E";
+            case 15 do return #char "F";
+            case #default do return #char "X";
+        }
+    }
+}
+
+
+//
+// Private and internal things
+//
+
+#thread_local stdio : struct {
+    print_stream : io.BufferStream;
+    print_writer : io.Writer;
+}
+
+__stdio_init :: () {
+    stdio.print_stream = io.buffer_stream_make(2048, context.allocator);
+    stdio.print_writer = io.writer_make(^stdio.print_stream);
+    
+    // This shouldn't need to be here, but because ^stdin_vtable is not a compile-time
+    // known value (even through it should be).
+    stdio_stream.vtable = ^stdio_vtable;
+}
+
+
+__flush_stdio :: () {
+    if stdio.print_stream.data.count == 0 do return;
+
+    ^stdio.print_stream
+        |> io.buffer_stream_to_str()
+        |> runtime.__output_string();
+
+    ^stdio.print_stream |> io.stream_flush();
+}
+
+#local stdio_vtable := io.Stream_Vtable.{
+    read = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
+        __flush_stdio();
+        bytes_read := runtime.__read_from_input(buffer);
+        if bytes_read == 0 do return .ReadPending, 0;
+        if bytes_read <  0 do return .EOF, 0;
+
+        return .None, bytes_read;
+    },
+
+    read_byte = (_: ^io.Stream) -> (io.Error, u8) {
+        __flush_stdio();
+        buf: [1] u8;
+        bytes_read := runtime.__read_from_input(buf);
+        if bytes_read <= 0 do return .EOF, 0;
+
+        return .None, buf[0];
+    },
+
+    write = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
+        return io.stream_write(^stdio.print_stream, buffer);
+    },
+
+    write_byte = (_: ^io.Stream, byte: u8) -> io.Error {
+        return io.stream_write_byte(^stdio.print_stream, byte);
+    },
+
+    flush = (_: ^io.Stream) -> io.Error {
+        __flush_stdio();
+        return .None;
+    }
+}
diff --git a/core/math.onyx b/core/math.onyx
deleted file mode 100644 (file)
index 73a829e..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-package core.math
-
-use core.intrinsics {wasm}
-
-// Things that are useful in any math library:
-//  - Trigonometry
-//  - modf, fmod
-
-// Other things that can be useful:
-//  - Vector math
-//  - Matrix math
-//  - Why not tensor math??
-//  - Complex numbers
-//  - Dual numbers
-
-
-E      :: 2.71828182845904523536f;
-PI     :: 3.14159265f;
-TAU    :: 6.28318330f;
-SQRT_2 :: 1.414213562f;
-
-//
-// Trigonometry
-// Basic trig functions have been implemented using taylor series approximations. The
-// approximations are very accurate, but rather computationally expensive. Programs that
-// rely heavily on trig functions would greatly benefit from improvements to the
-// implementations of these functions.
-//
-
-sin :: (t: f32) -> f32 {
-    while t >=  PI do t -= TAU;
-    while t <= -PI do t += TAU;
-
-    res := 0.0f;
-
-    plus_minus := 1.0f;
-    n := 13;
-    while n > 1 {
-        res += plus_minus;
-        res *= t * t / cast(f32) (n * n - n);
-
-        plus_minus = -plus_minus;
-        n -= 2;
-    }
-
-    res += 1.0f;
-    res *= t;
-    return res;
-}
-
-cos :: (t: f32) -> f32 {
-    while t >=  PI do t -= TAU;
-    while t <= -PI do t += TAU;
-
-    res := 0.0f;
-
-    plus_minus := 1.0f;
-    n := 12;
-    while n > 1 {
-        res += plus_minus;
-        res *= t * t / cast(f32) (n * n - n);
-
-        plus_minus = -plus_minus;
-        n -= 2;
-    }
-
-    res += 1.0f;
-    return res;
-}
-
-asin :: (t: f32) -> f32 {
-    assert(false, "asin is not implemented yet!");
-    return 0;
-}
-
-acos :: (t: f32) -> f32 {
-    assert(false, "acos is not implemented yet!");
-    return 0;
-}
-
-atan :: (t: f32) -> f32 {
-    assert(false, "atan is not implemented yet!");
-    return 0;
-}
-
-atan2 :: (t: f32) -> f32 {
-    assert(false, "atan2 is not implemented yet!");
-    return 0;
-}
-
-
-
-
-//
-// Hyperbolic trigonometry.
-// The hyperbolic trigonometry functions are implemented using the naive
-// definitions. There may be fancier, faster and far more precise methods
-// of implementing these, but these definitions should suffice.
-//
-
-sinh :: (t: $T) -> T {
-    et := exp(t);
-    return (et - (1 / et)) / 2;
-}
-
-cosh :: (t: $T) -> T {
-    et := exp(t);
-    return (et + (1 / et)) / 2;
-}
-
-tanh :: (t: $T) -> T {
-    et := exp(t);
-    one_over_et := 1 / et;
-    return (et - one_over_et) / (et + one_over_et);
-}
-
-asinh :: (t: $T) -> T {
-    return ~~ ln(cast(f32) (t + sqrt(t * t + 1)));
-}
-
-acosh :: (t: $T) -> T {
-    return ~~ ln(cast(f32) (t + sqrt(t * t - 1)));
-}
-
-atanh :: (t: $T) -> T {
-    return ~~ ln(cast(f32) ((1 + t) / (1 - t))) / 2;
-}
-
-
-
-//
-// Exponentials and logarithms.
-// Exponentials with floats are implemented using a binary search using square roots, since
-// square roots are intrinsic to WASM and therefore "fast". Expoentials with integers are
-// implemented using a fast algorithm that minimizes the number of the mulitplications that
-// are needed. Logarithms are implemented using a polynomial that is accurate in the range of
-// [1, 2], and then utilizes this identity for values outside of that range,
-//
-//      ln(x) = ln(2^n * v) = n * ln(2) + ln(v),   v is in [1, 2]
-//
-
-// FIX: This definition is very wrong. It casts E to be whatever the type of the argument is,
-// which if it is an integer, will be 2! This should always return a floating point number!
-exp :: (p: $T) -> T do return pow(base = cast(T) E, p = p);
-
-pow :: #match {
-    // Fast implementation of power when raising to an integer power.
-    (base: $T, p: i32) -> T {
-        if base == 0 do return 0;
-        if p == 0    do return 1;
-
-        a: T = 1;
-        while p > 0 {
-            if p % 2 == 1 do a *= base;
-            p = p >> 1;
-            base *= base;
-        }
-
-        return a;
-    },
-
-    // Also make the implementation work for 64-bit integers.
-    (base: $T, p: i64) -> T do return pow(base, cast(i32) p);,
-
-    // Generic power implementation for integers using square roots.
-    (base: $T, p: T) -> T {
-        if p == 0 do return 1;
-        if p < 0  do return 1 / pow(base, -p);
-
-        if p >= 1 {
-            tmp := pow(p = p / 2, base = base);
-            return tmp * tmp;
-        }
-
-        low  : T = 0;
-        high : T = 1;
-
-        sqr := sqrt(base);
-        acc := sqr;
-        mid := high / 2;
-
-        while abs(mid - p) > 0.00001 {
-            sqr = sqrt(sqr);
-
-            if mid <= p {
-                low = mid;
-                acc *= sqr;
-            } else {
-                high = mid;
-                acc /= sqr;
-            }
-
-            mid = (low + high) / 2;
-        }
-
-        return acc;
-    }
-}
-
-power_mod :: (base: u32, exp: u32, mod: u32) -> u32 {
-    t: u64 = 1;
-    e: u64 = ~~exp;
-    b: u64 = ~~base;
-    m: u64 = ~~mod;
-
-    while e > 0 {
-        if e % 2 != 0 do t = (t * b) % m;
-
-        b = (b * b) % m;
-        e /= 2;
-    }
-
-    return ~~(t % m);
-}
-
-ln :: (a: f32) -> f32 {
-    // FIX: This is probably not the most numerically stable solution.
-    if a < 1 {
-        return -ln(1 / a);
-        // log2 := 63 - cast(i32) clz_i64(cast(i64) (1 / a));
-        // x    := a / cast(f32) (1 << log2);
-        // res  := -8.6731532f + (129.946172f + (-558.971892f + (843.967330f - 409.109529f * x) * x) * x) * x;
-        // return res + cast(f32) log2 * 0.69314718f; // ln(2) = 0.69314718
-    }
-
-    log2 := 63 - cast(i32) wasm.clz_i64(cast(i64) a);
-    x    := a / cast(f32) (1 << log2);
-    res  := -1.7417939f + (2.8212026f + (-1.4699568f + (0.44717955f - 0.056570851f * x) * x) * x) * x;
-    res  += cast(f32) log2 * 0.69314718; // ln(2) = 0.69314718
-    return res;
-}
-
-log :: (a: $T, base: $R) -> T {
-    if a <= 0 || base <= 0 do return 0;
-    return ~~(ln(cast(f32) a) / ln(cast(f32) base));
-}
-
-
-
-
-// These function are overloaded in order to use the builtin WASM intrinsics for the
-// operation first, and then default to a polymorphic function that works on any type.
-// The clunky part about these at the moment is that, if you wanted to pass 'max' to
-// a procedure, you would have to pass 'max_poly' instead, because overloaded functions
-// are not resolved when used by value, i.e. foo : (f32, f32) -> f32 = math.max; Even if
-// they would be however, the fact that these overloads are intrinsic means they cannot
-// be reference from the element section and therefore cannot be passed around or used
-// as values.
-max :: #match #local { wasm.max_f32, wasm.max_f64, max_poly }
-max_poly :: (a: $T, b: T) -> T {
-    return a if a >= b else b;
-}
-
-min :: #match #local { wasm.min_f32, wasm.min_f64, min_poly }
-min_poly :: (a: $T, b: T) -> T {
-    return a if a <= b else b;
-}
-
-clamp :: (v: $T, lo: T, hi: T) -> T {
-    if v < lo do return lo;
-    if v > hi do return hi;
-    return v;
-}
-
-sqrt :: #match #local { wasm.sqrt_f32, wasm.sqrt_f64, sqrt_poly }
-sqrt_poly :: (x: $T) -> T {
-    return ~~ wasm.sqrt_f64(~~ x);
-}
-
-abs :: #match #local { wasm.abs_f32, wasm.abs_f64, abs_poly }
-abs_poly :: (x: $T) -> T {
-    return x if x >= 0 else -x;
-}
-
-sign :: (x: $T) -> T {
-    if x > 0 do return 1;
-    if x < 0 do return cast(T) -1;
-    return 0;
-}
-
-copysign :: #match #local { wasm.copysign_f32, wasm.copysign_f64, copysign_poly }
-copysign_poly :: (x: $T, y: T) -> T {
-    return abs(x) * sign(y);
-}
-
-
-
-
-//
-// Floating point rounding
-//
-
-ceil    :: #match #local { wasm.ceil_f32,    wasm.ceil_f64    }
-floor   :: #match #local { wasm.floor_f32,   wasm.floor_f64   }
-trunc   :: #match #local { wasm.trunc_f32,   wasm.trunc_f64   }
-nearest :: #match #local { wasm.nearest_f32, wasm.nearest_f64 }
-
-
-
-//
-// Integer operations
-//
-
-clz          :: #match #local { wasm.clz_i32,    wasm.clz_i64    }
-ctz          :: #match #local { wasm.ctz_i32,    wasm.ctz_i64    }
-popcnt       :: #match #local { wasm.popcnt_i32, wasm.popcnt_i64 }
-rotate_left  :: #match #local { wasm.rotl_i32,   wasm.rotl_i64   }
-rotate_right :: #match #local { wasm.rotr_i32,   wasm.rotr_i64   }
-
-
-
-lerp :: (t: f32, a: $T, b: T) -> T {
-    return ~~(~~a * (1 - t) + ~~b * t);
-}
-
-choose :: (n: $T, k: T) -> T {
-    assert(T == i32 || T == i64 || T == u32 || T == u64, "bad type for choose function");
-
-    ret := 1;
-    for i: (n - k + 1) .. (n + 1) {
-        ret *= i;
-    }
-
-    for i: 1 .. (k + 1) {
-        ret /= i;
-    }
-
-    return ret;
-}
-
-gcd :: (a: $T, b: T) -> T {
-    if a < 0 do a = -a;
-    if b < 0 do b = -b;
-
-    if b == 0 do return a;
-    return gcd(b, a % b);
-}
-
-lcm :: (a: $T, b: T) -> T {
-    return (a * b) / gcd(a, b);
-}
-
-is_nan :: #match #local {}
-
-#overload
-is_nan :: (x: f32) -> bool {
-    v := x;
-    i := *cast(^u32) ^v;
-    return (i & 0x7f800000) == 0x7f800000
-        && (i & 0x007fffff) != 0;
-}
-
-#overload
-is_nan :: (x: f64) -> bool {
-    v := x;
-    i := *cast(^u64) ^v;
-    return (i & 0x7ff0000000000000) == 0x7ff0000000000000
-        && (i & 0x000fffffffffffff) != 0;
-}
-
-
-is_inf :: #match #local {}
-
-#overload
-is_inf :: (x: f32) -> bool {
-    v := x;
-    i := *cast(^u32) ^v;
-    return (i & 0x7f800000) == 0x7f800000
-        && (i & 0x007fffff) == 0;
-}
-
-#overload
-is_inf :: (x: f64) -> bool {
-    v := x;
-    i := *cast(^u64) ^v;
-    return (i & 0x7ff0000000000000) == 0x7ff0000000000000
-        && (i & 0x000fffffffffffff) == 0;
-}
-
-
-
-//
-// Tests
-//
-
-#if #defined(core.Running_Tests) {
-
-use core {test}
-
-@test.test.{"GCD works"}
-(t: ^test.T) {
-    t->assert(gcd(35, 18) == 1, "gcd(35, 18) == 1");
-    t->assert(gcd(35, 15) == 5, "gcd(35, 15) == 5");
-    t->assert(gcd(35, 21) == 7, "gcd(35, 21) == 7");
-    t->assert(gcd(35, 70) == 35, "gcd(35, 70) == 35");
-}
-
-}
diff --git a/core/math/math.onyx b/core/math/math.onyx
new file mode 100644 (file)
index 0000000..73a829e
--- /dev/null
@@ -0,0 +1,398 @@
+package core.math
+
+use core.intrinsics {wasm}
+
+// Things that are useful in any math library:
+//  - Trigonometry
+//  - modf, fmod
+
+// Other things that can be useful:
+//  - Vector math
+//  - Matrix math
+//  - Why not tensor math??
+//  - Complex numbers
+//  - Dual numbers
+
+
+E      :: 2.71828182845904523536f;
+PI     :: 3.14159265f;
+TAU    :: 6.28318330f;
+SQRT_2 :: 1.414213562f;
+
+//
+// Trigonometry
+// Basic trig functions have been implemented using taylor series approximations. The
+// approximations are very accurate, but rather computationally expensive. Programs that
+// rely heavily on trig functions would greatly benefit from improvements to the
+// implementations of these functions.
+//
+
+sin :: (t: f32) -> f32 {
+    while t >=  PI do t -= TAU;
+    while t <= -PI do t += TAU;
+
+    res := 0.0f;
+
+    plus_minus := 1.0f;
+    n := 13;
+    while n > 1 {
+        res += plus_minus;
+        res *= t * t / cast(f32) (n * n - n);
+
+        plus_minus = -plus_minus;
+        n -= 2;
+    }
+
+    res += 1.0f;
+    res *= t;
+    return res;
+}
+
+cos :: (t: f32) -> f32 {
+    while t >=  PI do t -= TAU;
+    while t <= -PI do t += TAU;
+
+    res := 0.0f;
+
+    plus_minus := 1.0f;
+    n := 12;
+    while n > 1 {
+        res += plus_minus;
+        res *= t * t / cast(f32) (n * n - n);
+
+        plus_minus = -plus_minus;
+        n -= 2;
+    }
+
+    res += 1.0f;
+    return res;
+}
+
+asin :: (t: f32) -> f32 {
+    assert(false, "asin is not implemented yet!");
+    return 0;
+}
+
+acos :: (t: f32) -> f32 {
+    assert(false, "acos is not implemented yet!");
+    return 0;
+}
+
+atan :: (t: f32) -> f32 {
+    assert(false, "atan is not implemented yet!");
+    return 0;
+}
+
+atan2 :: (t: f32) -> f32 {
+    assert(false, "atan2 is not implemented yet!");
+    return 0;
+}
+
+
+
+
+//
+// Hyperbolic trigonometry.
+// The hyperbolic trigonometry functions are implemented using the naive
+// definitions. There may be fancier, faster and far more precise methods
+// of implementing these, but these definitions should suffice.
+//
+
+sinh :: (t: $T) -> T {
+    et := exp(t);
+    return (et - (1 / et)) / 2;
+}
+
+cosh :: (t: $T) -> T {
+    et := exp(t);
+    return (et + (1 / et)) / 2;
+}
+
+tanh :: (t: $T) -> T {
+    et := exp(t);
+    one_over_et := 1 / et;
+    return (et - one_over_et) / (et + one_over_et);
+}
+
+asinh :: (t: $T) -> T {
+    return ~~ ln(cast(f32) (t + sqrt(t * t + 1)));
+}
+
+acosh :: (t: $T) -> T {
+    return ~~ ln(cast(f32) (t + sqrt(t * t - 1)));
+}
+
+atanh :: (t: $T) -> T {
+    return ~~ ln(cast(f32) ((1 + t) / (1 - t))) / 2;
+}
+
+
+
+//
+// Exponentials and logarithms.
+// Exponentials with floats are implemented using a binary search using square roots, since
+// square roots are intrinsic to WASM and therefore "fast". Expoentials with integers are
+// implemented using a fast algorithm that minimizes the number of the mulitplications that
+// are needed. Logarithms are implemented using a polynomial that is accurate in the range of
+// [1, 2], and then utilizes this identity for values outside of that range,
+//
+//      ln(x) = ln(2^n * v) = n * ln(2) + ln(v),   v is in [1, 2]
+//
+
+// FIX: This definition is very wrong. It casts E to be whatever the type of the argument is,
+// which if it is an integer, will be 2! This should always return a floating point number!
+exp :: (p: $T) -> T do return pow(base = cast(T) E, p = p);
+
+pow :: #match {
+    // Fast implementation of power when raising to an integer power.
+    (base: $T, p: i32) -> T {
+        if base == 0 do return 0;
+        if p == 0    do return 1;
+
+        a: T = 1;
+        while p > 0 {
+            if p % 2 == 1 do a *= base;
+            p = p >> 1;
+            base *= base;
+        }
+
+        return a;
+    },
+
+    // Also make the implementation work for 64-bit integers.
+    (base: $T, p: i64) -> T do return pow(base, cast(i32) p);,
+
+    // Generic power implementation for integers using square roots.
+    (base: $T, p: T) -> T {
+        if p == 0 do return 1;
+        if p < 0  do return 1 / pow(base, -p);
+
+        if p >= 1 {
+            tmp := pow(p = p / 2, base = base);
+            return tmp * tmp;
+        }
+
+        low  : T = 0;
+        high : T = 1;
+
+        sqr := sqrt(base);
+        acc := sqr;
+        mid := high / 2;
+
+        while abs(mid - p) > 0.00001 {
+            sqr = sqrt(sqr);
+
+            if mid <= p {
+                low = mid;
+                acc *= sqr;
+            } else {
+                high = mid;
+                acc /= sqr;
+            }
+
+            mid = (low + high) / 2;
+        }
+
+        return acc;
+    }
+}
+
+power_mod :: (base: u32, exp: u32, mod: u32) -> u32 {
+    t: u64 = 1;
+    e: u64 = ~~exp;
+    b: u64 = ~~base;
+    m: u64 = ~~mod;
+
+    while e > 0 {
+        if e % 2 != 0 do t = (t * b) % m;
+
+        b = (b * b) % m;
+        e /= 2;
+    }
+
+    return ~~(t % m);
+}
+
+ln :: (a: f32) -> f32 {
+    // FIX: This is probably not the most numerically stable solution.
+    if a < 1 {
+        return -ln(1 / a);
+        // log2 := 63 - cast(i32) clz_i64(cast(i64) (1 / a));
+        // x    := a / cast(f32) (1 << log2);
+        // res  := -8.6731532f + (129.946172f + (-558.971892f + (843.967330f - 409.109529f * x) * x) * x) * x;
+        // return res + cast(f32) log2 * 0.69314718f; // ln(2) = 0.69314718
+    }
+
+    log2 := 63 - cast(i32) wasm.clz_i64(cast(i64) a);
+    x    := a / cast(f32) (1 << log2);
+    res  := -1.7417939f + (2.8212026f + (-1.4699568f + (0.44717955f - 0.056570851f * x) * x) * x) * x;
+    res  += cast(f32) log2 * 0.69314718; // ln(2) = 0.69314718
+    return res;
+}
+
+log :: (a: $T, base: $R) -> T {
+    if a <= 0 || base <= 0 do return 0;
+    return ~~(ln(cast(f32) a) / ln(cast(f32) base));
+}
+
+
+
+
+// These function are overloaded in order to use the builtin WASM intrinsics for the
+// operation first, and then default to a polymorphic function that works on any type.
+// The clunky part about these at the moment is that, if you wanted to pass 'max' to
+// a procedure, you would have to pass 'max_poly' instead, because overloaded functions
+// are not resolved when used by value, i.e. foo : (f32, f32) -> f32 = math.max; Even if
+// they would be however, the fact that these overloads are intrinsic means they cannot
+// be reference from the element section and therefore cannot be passed around or used
+// as values.
+max :: #match #local { wasm.max_f32, wasm.max_f64, max_poly }
+max_poly :: (a: $T, b: T) -> T {
+    return a if a >= b else b;
+}
+
+min :: #match #local { wasm.min_f32, wasm.min_f64, min_poly }
+min_poly :: (a: $T, b: T) -> T {
+    return a if a <= b else b;
+}
+
+clamp :: (v: $T, lo: T, hi: T) -> T {
+    if v < lo do return lo;
+    if v > hi do return hi;
+    return v;
+}
+
+sqrt :: #match #local { wasm.sqrt_f32, wasm.sqrt_f64, sqrt_poly }
+sqrt_poly :: (x: $T) -> T {
+    return ~~ wasm.sqrt_f64(~~ x);
+}
+
+abs :: #match #local { wasm.abs_f32, wasm.abs_f64, abs_poly }
+abs_poly :: (x: $T) -> T {
+    return x if x >= 0 else -x;
+}
+
+sign :: (x: $T) -> T {
+    if x > 0 do return 1;
+    if x < 0 do return cast(T) -1;
+    return 0;
+}
+
+copysign :: #match #local { wasm.copysign_f32, wasm.copysign_f64, copysign_poly }
+copysign_poly :: (x: $T, y: T) -> T {
+    return abs(x) * sign(y);
+}
+
+
+
+
+//
+// Floating point rounding
+//
+
+ceil    :: #match #local { wasm.ceil_f32,    wasm.ceil_f64    }
+floor   :: #match #local { wasm.floor_f32,   wasm.floor_f64   }
+trunc   :: #match #local { wasm.trunc_f32,   wasm.trunc_f64   }
+nearest :: #match #local { wasm.nearest_f32, wasm.nearest_f64 }
+
+
+
+//
+// Integer operations
+//
+
+clz          :: #match #local { wasm.clz_i32,    wasm.clz_i64    }
+ctz          :: #match #local { wasm.ctz_i32,    wasm.ctz_i64    }
+popcnt       :: #match #local { wasm.popcnt_i32, wasm.popcnt_i64 }
+rotate_left  :: #match #local { wasm.rotl_i32,   wasm.rotl_i64   }
+rotate_right :: #match #local { wasm.rotr_i32,   wasm.rotr_i64   }
+
+
+
+lerp :: (t: f32, a: $T, b: T) -> T {
+    return ~~(~~a * (1 - t) + ~~b * t);
+}
+
+choose :: (n: $T, k: T) -> T {
+    assert(T == i32 || T == i64 || T == u32 || T == u64, "bad type for choose function");
+
+    ret := 1;
+    for i: (n - k + 1) .. (n + 1) {
+        ret *= i;
+    }
+
+    for i: 1 .. (k + 1) {
+        ret /= i;
+    }
+
+    return ret;
+}
+
+gcd :: (a: $T, b: T) -> T {
+    if a < 0 do a = -a;
+    if b < 0 do b = -b;
+
+    if b == 0 do return a;
+    return gcd(b, a % b);
+}
+
+lcm :: (a: $T, b: T) -> T {
+    return (a * b) / gcd(a, b);
+}
+
+is_nan :: #match #local {}
+
+#overload
+is_nan :: (x: f32) -> bool {
+    v := x;
+    i := *cast(^u32) ^v;
+    return (i & 0x7f800000) == 0x7f800000
+        && (i & 0x007fffff) != 0;
+}
+
+#overload
+is_nan :: (x: f64) -> bool {
+    v := x;
+    i := *cast(^u64) ^v;
+    return (i & 0x7ff0000000000000) == 0x7ff0000000000000
+        && (i & 0x000fffffffffffff) != 0;
+}
+
+
+is_inf :: #match #local {}
+
+#overload
+is_inf :: (x: f32) -> bool {
+    v := x;
+    i := *cast(^u32) ^v;
+    return (i & 0x7f800000) == 0x7f800000
+        && (i & 0x007fffff) == 0;
+}
+
+#overload
+is_inf :: (x: f64) -> bool {
+    v := x;
+    i := *cast(^u64) ^v;
+    return (i & 0x7ff0000000000000) == 0x7ff0000000000000
+        && (i & 0x000fffffffffffff) == 0;
+}
+
+
+
+//
+// Tests
+//
+
+#if #defined(core.Running_Tests) {
+
+use core {test}
+
+@test.test.{"GCD works"}
+(t: ^test.T) {
+    t->assert(gcd(35, 18) == 1, "gcd(35, 18) == 1");
+    t->assert(gcd(35, 15) == 5, "gcd(35, 15) == 5");
+    t->assert(gcd(35, 21) == 7, "gcd(35, 21) == 7");
+    t->assert(gcd(35, 70) == 35, "gcd(35, 70) == 35");
+}
+
+}
diff --git a/core/memory.onyx b/core/memory.onyx
deleted file mode 100644 (file)
index 3c04d26..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-package core.memory
-
-align :: #match {
-    (size: ^u64, align: u64) {
-        if *size % align != 0 {
-            *size += align - (*size % align);
-        }
-    },
-
-    (size: u64, align: u64) -> u64 {
-        if size % align != 0 {
-            size += align - (size % align);
-        }
-        return size;
-    }        
-}
-
-copy :: core.intrinsics.wasm.memory_copy
-set  :: core.intrinsics.wasm.memory_fill
-
-alloc_slice :: (sl: ^[] $T, count: i32, allocator := context.allocator) {
-    sl.data  = raw_alloc(allocator, sizeof T * count);
-    sl.count = count;
-}
-
-make_slice :: ($T: type_expr, count: i32, allocator := context.allocator) -> [] T {
-    return .{
-        data = raw_alloc(allocator, sizeof T * count),
-        count = count
-    };
-}
-
-free_slice :: (sl: ^[] $T, allocator := context.allocator) {
-    if sl.data == null do return;
-
-    raw_free(allocator, sl.data);
-    sl.data = null;
-    sl.count = 0;
-}
-
-copy_slice :: (sl: [] $T, allocator := context.allocator) -> [] T {
-    data := raw_alloc(allocator, sl.count * sizeof T);
-    copy(data, sl.data, sl.count * sizeof T);
-
-    return .{ data = data, count = sl.count };
-}
-
-resize_slice :: (sl: [] $T, new_size: i32, allocator := context.allocator) -> [] T {
-    new_slice: [] T;
-    new_slice.data = raw_alloc(allocator, sizeof T * new_size);
-    new_slice.count = new_size;
-
-    copy(new_slice.data, sl.data, sl.count * sizeof T);
-
-    return new_slice;
-}
-
-
-#overload
-builtin.__make_overload :: macro (_: ^[] $T, count: u32, allocator := context.allocator) -> [] T {
-    return (package core.memory).make_slice(T, count, allocator);
-}
-
-#overload
-builtin.delete :: macro (x: ^[] $T) {
-    core.memory.free_slice(x);
-}
diff --git a/core/memory/memory.onyx b/core/memory/memory.onyx
new file mode 100644 (file)
index 0000000..3c04d26
--- /dev/null
@@ -0,0 +1,67 @@
+package core.memory
+
+align :: #match {
+    (size: ^u64, align: u64) {
+        if *size % align != 0 {
+            *size += align - (*size % align);
+        }
+    },
+
+    (size: u64, align: u64) -> u64 {
+        if size % align != 0 {
+            size += align - (size % align);
+        }
+        return size;
+    }        
+}
+
+copy :: core.intrinsics.wasm.memory_copy
+set  :: core.intrinsics.wasm.memory_fill
+
+alloc_slice :: (sl: ^[] $T, count: i32, allocator := context.allocator) {
+    sl.data  = raw_alloc(allocator, sizeof T * count);
+    sl.count = count;
+}
+
+make_slice :: ($T: type_expr, count: i32, allocator := context.allocator) -> [] T {
+    return .{
+        data = raw_alloc(allocator, sizeof T * count),
+        count = count
+    };
+}
+
+free_slice :: (sl: ^[] $T, allocator := context.allocator) {
+    if sl.data == null do return;
+
+    raw_free(allocator, sl.data);
+    sl.data = null;
+    sl.count = 0;
+}
+
+copy_slice :: (sl: [] $T, allocator := context.allocator) -> [] T {
+    data := raw_alloc(allocator, sl.count * sizeof T);
+    copy(data, sl.data, sl.count * sizeof T);
+
+    return .{ data = data, count = sl.count };
+}
+
+resize_slice :: (sl: [] $T, new_size: i32, allocator := context.allocator) -> [] T {
+    new_slice: [] T;
+    new_slice.data = raw_alloc(allocator, sizeof T * new_size);
+    new_slice.count = new_size;
+
+    copy(new_slice.data, sl.data, sl.count * sizeof T);
+
+    return new_slice;
+}
+
+
+#overload
+builtin.__make_overload :: macro (_: ^[] $T, count: u32, allocator := context.allocator) -> [] T {
+    return (package core.memory).make_slice(T, count, allocator);
+}
+
+#overload
+builtin.delete :: macro (x: ^[] $T) {
+    core.memory.free_slice(x);
+}
diff --git a/core/misc/arg_parse.onyx b/core/misc/arg_parse.onyx
new file mode 100644 (file)
index 0000000..cde4b24
--- /dev/null
@@ -0,0 +1,58 @@
+package core.arg_parse
+
+use core
+
+arg_parse :: (c_args: [] cstr, output: any) -> bool {
+    arg_iter := iter.as_iterator(c_args)
+             |> iter.map((x) => string.from_cstr(*x));
+    defer arg_iter.close(arg_iter.data);
+
+    use runtime.info;
+
+    ptr_type := cast(^Type_Info_Pointer) get_type_info(output.type);
+    if ptr_type.kind != .Pointer do return false;
+
+    arg_type := cast(^Type_Info_Struct) get_type_info(ptr_type.to);
+    if arg_type.kind != .Struct do return false;
+
+    data_base := *cast(^rawptr) output.data;
+
+    for #no_close arg: arg_iter {
+        for ^member: arg_type.members {
+            for ^tag: member.tags {
+                if tag.type != str do continue;
+
+                to_match := *cast(^str) tag.data;
+                if arg != to_match do continue;
+
+                switch member.type {
+                    case bool {
+                        *(cast(^bool) (cast(^u8) data_base + member.offset)) = !*(cast(^bool) (cast(^u8) data_base + member.offset));
+                    }
+
+                    case i32 {
+                        value_str, success := iter.take_one(arg_iter, no_close=true);
+                        if !success do return false;
+
+                        value := conv.str_to_i64(value_str);
+                        *(cast(^i32) (cast(^u8) data_base + member.offset)) = ~~value;
+                    }
+
+                    case str {
+                        value, success := iter.take_one(arg_iter, no_close=true);
+                        if !success do return false;
+
+                        *(cast(^str) (cast(^u8) data_base + member.offset)) = value;
+                    }
+
+                    case #default {
+                        printf("Unsupported argument type, {}.\n", output.type);
+                        return false;
+                    }
+                }
+            }
+        }
+    }
+
+    return true;
+}
\ No newline at end of file
index 9f0d9d578e201af4f8129b1836c8f37e754f41a1..7a84b0f03cd156a752fcde3cb9c8f3080e644ad4 100644 (file)
@@ -10,6 +10,10 @@ Socket :: struct {
     type: SocketType;
     family: SocketDomain;
 
+}
+
+// Inject methods for the socket
+#inject Socket {
     close     :: socket_close
     setting   :: socket_setting
     is_alive  :: socket_is_alive
index 2a3349f0a834f791734ffee86211eb28b5e9190b..8885b5a8e07ad37b9d6fd66f25a30e574150a9fb 100644 (file)
@@ -1,6 +1,6 @@
 package core.net
 
-use core {sync, thread, array, memory, alloc, os}
+use core {sync, thread, array, memory, alloc, os, iter}
 
 #if !runtime.Multi_Threading_Enabled {
     #error "Expected multi-threading to be enabled for TCP server.";
@@ -57,35 +57,32 @@ TCP_Event :: struct {
     }
 }
 
-tcp_get_events :: (use conn: ^TCP_Connection) -> Iterator(TCP_Event) {
-    next :: (use conn: ^TCP_Connection) -> (TCP_Event, bool) {
-        if event_cursor == events.count do return .{}, false;
+tcp_get_events :: (use conn: ^TCP_Connection) => {
+    conn.event_cursor = 0;
 
-        defer event_cursor += 1;
-        return events[event_cursor], true;
-    }
+    return iter.generator(
+        conn,
 
-    close :: (use conn: ^TCP_Connection) {
-        for events {
-            switch it.kind {
-                case .Data {
-                    raw_free(event_allocator, (cast(^TCP_Event.Data) it.data).contents.data);
-                }
-            }
+        (use conn: ^TCP_Connection) -> (TCP_Event, bool) {
+            if event_cursor == events.count do return .{}, false;
 
-            raw_free(event_allocator, it.data);
-        }
+            defer event_cursor += 1;
+            return events[event_cursor], true;
+        },
 
-        array.clear(^events);
-    }
+        (use conn: ^TCP_Connection) {
+            for events {
+                switch it.kind {
+                    case .Data {
+                        raw_free(event_allocator, (cast(^TCP_Event.Data) it.data).contents.data);
+                    }
+                }
 
-    conn.event_cursor = 0;
+                raw_free(event_allocator, it.data);
+            }
 
-    return .{
-        data  = conn,
-        next  = next,
-        close = close,
-    };
+            array.clear(^events);
+        });
 }
 
 
@@ -96,24 +93,6 @@ tcp_get_events :: (use conn: ^TCP_Connection) -> Iterator(TCP_Event) {
 TCP_Server :: struct {
     use connection: TCP_Connection;
 
-    Client :: struct {
-        use socket  : Socket;
-        address : Socket_Address;
-        state   : State;
-
-        recv_ready_event_present := false;
-
-        State :: enum {
-            Alive;
-            Being_Killed;
-            Dying;
-            Dead;
-        }
-
-        read_complete :: (use this: ^Client) {
-            recv_ready_event_present = false;
-        }
-    }
     client_allocator: Allocator;
     clients: [] ^Client;
 
@@ -136,6 +115,29 @@ TCP_Server :: struct {
     kill_client   :: tcp_server_kill_client
 }
 
+#inject TCP_Server {
+    Client :: struct {
+        use socket  : Socket;
+        address : Socket_Address;
+        state   : State;
+
+        recv_ready_event_present := false;
+
+        State :: enum {
+            Alive;
+            Being_Killed;
+            Dying;
+            Dead;
+        }
+    }
+}
+
+#inject TCP_Server.Client {
+    read_complete :: (use this: ^TCP_Server.Client) {
+        recv_ready_event_present = false;
+    }
+}
+
 tcp_server_make :: (max_clients := 32, allocator := context.allocator) -> ^TCP_Server {
     socket, err := socket_create(.Inet, .Stream); // IPv6?
     if err != .None do return null;
diff --git a/core/random.onyx b/core/random.onyx
deleted file mode 100644 (file)
index 527fb16..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-package core.random
-
-#local seed : i64 = 8675309
-
-#local RANDOM_MULTIPLIER :: 25214903917
-#local RANDOM_INCREMENT  :: cast(i64) 11
-// #local RANDOM_MODULUS    :: 1 << 32
-
-set_seed :: #match {
-    (s: u32) do seed = ~~s; ,
-    (s: u64) do seed =   s; ,
-}
-
-int :: (s := ^seed) -> u32 {
-    *s = *s * RANDOM_MULTIPLIER + RANDOM_INCREMENT;
-    return cast(u32) ((*s >> 16) & ~~0xffffffff);
-}
-
-between :: (lo: i32, hi: i32) -> i32 do return int () % (hi + 1 - lo) + lo;
-
-float :: (lo := 0.0f, hi := 1.0f) -> f32 {
-    return (cast(f32) (int() % (1 << 20)) / cast(f32) (1 << 20)) * (hi - lo) + lo;
-}
-
-choice :: (a: [] $T) -> T {
-    return a[between(0, a.count - 1)];
-}
-
-string :: (bytes_long: u32, alpha_numeric := false, allocator := context.allocator) -> str {
-    memory :: package core.memory
-
-    s := memory.make_slice(u8, bytes_long, allocator=allocator);
-    for^ s {
-        if alpha_numeric {
-            #persist alpha_numeral := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-            *it = choice(alpha_numeral);
-        } else {
-            *it = ~~between(32, 127);
-        }
-    }
-    return s;
-}
diff --git a/core/random/random.onyx b/core/random/random.onyx
new file mode 100644 (file)
index 0000000..527fb16
--- /dev/null
@@ -0,0 +1,42 @@
+package core.random
+
+#local seed : i64 = 8675309
+
+#local RANDOM_MULTIPLIER :: 25214903917
+#local RANDOM_INCREMENT  :: cast(i64) 11
+// #local RANDOM_MODULUS    :: 1 << 32
+
+set_seed :: #match {
+    (s: u32) do seed = ~~s; ,
+    (s: u64) do seed =   s; ,
+}
+
+int :: (s := ^seed) -> u32 {
+    *s = *s * RANDOM_MULTIPLIER + RANDOM_INCREMENT;
+    return cast(u32) ((*s >> 16) & ~~0xffffffff);
+}
+
+between :: (lo: i32, hi: i32) -> i32 do return int () % (hi + 1 - lo) + lo;
+
+float :: (lo := 0.0f, hi := 1.0f) -> f32 {
+    return (cast(f32) (int() % (1 << 20)) / cast(f32) (1 << 20)) * (hi - lo) + lo;
+}
+
+choice :: (a: [] $T) -> T {
+    return a[between(0, a.count - 1)];
+}
+
+string :: (bytes_long: u32, alpha_numeric := false, allocator := context.allocator) -> str {
+    memory :: package core.memory
+
+    s := memory.make_slice(u8, bytes_long, allocator=allocator);
+    for^ s {
+        if alpha_numeric {
+            #persist alpha_numeral := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+            *it = choice(alpha_numeral);
+        } else {
+            *it = ~~between(32, 127);
+        }
+    }
+    return s;
+}
index 0b2329a64b52c361d6430b5233bf6b324874f7e4..669887c57d99a0fc3075c9f7e4a262c1d7da54b3 100644 (file)
@@ -1,8 +1,8 @@
 package core
 
 
-#load "./alloc"
-#load "./memory"
+#load "./alloc/alloc"
+#load "./memory/memory"
 
 #load "./container/array"
 #load "./container/avl_tree"
@@ -13,12 +13,15 @@ package core
 #load "./container/bucket_array"
 #load "./container/heap"
 
-#load "./conv"
-#load "./math"
-#load "./random"
-#load "./hash"
+#load "./conv/conv"
+#load "./conv/format"
+#load "./conv/parse"
 
-#load "./string"
+#load "./math/math"
+#load "./random/random"
+#load "./hash/hash"
+
+#load "./string/string"
 #load "./string/reader"
 #load "./string/buffer"
 #load "./string/char_utils"
@@ -36,10 +39,10 @@ package core
 #load "./runtime/build_opts"
 #load "./runtime/common"
 #load "./runtime/default_link_options"
-#load "./arg_parse"
 
 #load "./test/testing"
 
+#load "./misc/arg_parse"
 #load "./misc/any_utils"
 
 #local runtime :: package runtime
@@ -73,7 +76,7 @@ package core
 #if runtime.runtime == .Js     { #load "./runtime/js" }
 #if runtime.runtime != .Custom {
     #load "./runtime/info/helper"
-    #load "./stdio"
+    #load "./io/stdio"
 
     #load "./encoding/base64"
     #load "./encoding/csv"
diff --git a/core/stdio.onyx b/core/stdio.onyx
deleted file mode 100644 (file)
index b79f573..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-package core
-// Currently, these symbols are dumped in the 'core' namespace, which means
-// most programs that just 'use package core' can access all of them, which
-// is convenient; However, it doesn't hurt to wonder if they should be the
-// 'core.io' package so these would become like 'io.printf(...)'. Of course,
-// you could always still do 'use package core.io', which would bring these
-// in anyway.
-
-
-#if runtime.runtime == .Custom {
-    #error "'stdio' can only be included in the 'wasi' or 'js' runtime."
-}
-
-stdio_stream: io.Stream;
-
-auto_flush_stdio := true
-
-print :: #match #locked {
-    (x: str) {
-        io.write(^stdio.print_writer, x);
-        if x[x.count - 1] == #char "\n" && auto_flush_stdio do __flush_stdio();
-    },
-
-    (x)    => { io.write(^stdio.print_writer, x); },
-    (x, y) => { io.write(^stdio.print_writer, x, y); },
-}
-
-println :: (x) => {
-    print(x);
-    print("\n");
-}
-
-// Standard formatted print.
-printf :: (format: str, va: ..any) {
-    flush :: (_, to_output) => {
-        io.write(^stdio.print_writer, to_output);
-        __flush_stdio();
-        return true;
-    }
-
-    buffer: [1024] u8;
-    print(conv.format_va(buffer, format, va, .{null, flush}));
-}
-
-// Print to standard error, if available.
-#if #defined(runtime.__output_error) {
-    eprintf :: (format: str, va: ..any) -> str {
-        flush :: (_, to_output) => {
-            runtime.__output_error(to_output);
-            return true;
-        }
-
-        buffer: [1024] u8;
-        runtime.__output_error(conv.format_va(buffer, format, va, .{null, flush}));
-    }
-}
-
-// Print to a dynamically allocated string.
-aprintf :: (format: str, va: ..any) -> str {
-    buffer: [8196] u8;
-    out := conv.format_va(buffer, format, va);
-    return string.alloc_copy(out);
-}
-
-// Print to a TEMPORARY dynamically allocated string.
-tprintf :: (format: str, va: ..any) -> str {
-    buffer: [8196] u8;
-    out := conv.format_va(buffer, format, va);
-    return string.alloc_copy(out, allocator=context.temp_allocator);
-}
-
-byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) {
-    temp: [3] u8;
-
-    u8_ptr := cast(^u8) ptr;
-    for i: byte_count {
-        val := u8_ptr[i];
-
-        temp[0] = map_to_ascii(val >> 4);
-        temp[1] = map_to_ascii(val & 15);
-        temp[2] = #char " ";
-
-        runtime.__output_string(~~temp);
-
-        if i % bytes_per_line == (bytes_per_line - 1) do runtime.__output_string("\n");
-    }
-
-
-    map_to_ascii :: (x: u8) -> u8 {
-        switch x {
-            case 0 do return  #char "0";
-            case 1 do return  #char "1";
-            case 2 do return  #char "2";
-            case 3 do return  #char "3";
-            case 4 do return  #char "4";
-            case 5 do return  #char "5";
-            case 6 do return  #char "6";
-            case 7 do return  #char "7";
-            case 8 do return  #char "8";
-            case 9 do return  #char "9";
-            case 10 do return #char "A";
-            case 11 do return #char "B";
-            case 12 do return #char "C";
-            case 13 do return #char "D";
-            case 14 do return #char "E";
-            case 15 do return #char "F";
-            case #default do return #char "X";
-        }
-    }
-}
-
-
-//
-// Private and internal things
-//
-
-#thread_local stdio : struct {
-    print_stream : io.BufferStream;
-    print_writer : io.Writer;
-}
-
-__stdio_init :: () {
-    stdio.print_stream = io.buffer_stream_make(2048, context.allocator);
-    stdio.print_writer = io.writer_make(^stdio.print_stream);
-    
-    // This shouldn't need to be here, but because ^stdin_vtable is not a compile-time
-    // known value (even through it should be).
-    stdio_stream.vtable = ^stdio_vtable;
-}
-
-
-__flush_stdio :: () {
-    if stdio.print_stream.data.count == 0 do return;
-
-    ^stdio.print_stream
-        |> io.buffer_stream_to_str()
-        |> runtime.__output_string();
-
-    ^stdio.print_stream |> io.stream_flush();
-}
-
-#local stdio_vtable := io.Stream_Vtable.{
-    read = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
-        __flush_stdio();
-        bytes_read := runtime.__read_from_input(buffer);
-        if bytes_read == 0 do return .ReadPending, 0;
-        if bytes_read <  0 do return .EOF, 0;
-
-        return .None, bytes_read;
-    },
-
-    read_byte = (_: ^io.Stream) -> (io.Error, u8) {
-        __flush_stdio();
-        buf: [1] u8;
-        bytes_read := runtime.__read_from_input(buf);
-        if bytes_read <= 0 do return .EOF, 0;
-
-        return .None, buf[0];
-    },
-
-    write = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
-        return io.stream_write(^stdio.print_stream, buffer);
-    },
-
-    write_byte = (_: ^io.Stream, byte: u8) -> io.Error {
-        return io.stream_write_byte(^stdio.print_stream, byte);
-    },
-
-    flush = (_: ^io.Stream) -> io.Error {
-        __flush_stdio();
-        return .None;
-    }
-}
diff --git a/core/string.onyx b/core/string.onyx
deleted file mode 100644 (file)
index 1e37901..0000000
+++ /dev/null
@@ -1,543 +0,0 @@
-package core.string
-
-use core
-
-as_str :: #match {}
-
-free :: (s: str, allocator := context.allocator) do raw_free(allocator, s.data);
-
-alloc_copy :: (original: str, allocator := context.allocator) -> str {
-    new_str : str;
-    new_str.data = raw_alloc(allocator, sizeof u8 * original.count);
-    new_str.count = original.count;
-    copy(original, new_str);
-    return new_str;
-}
-
-temp_copy :: (original: str) -> str {
-    new_str := make([] u8, original.count, allocator=context.temp_allocator);
-    copy(original, new_str);
-    return new_str;
-}
-
-copy :: (orig: str, dest: str) {
-    len := orig.count;
-    if dest.count < len do len = dest.count;
-
-    memory.copy(dest.data, orig.data, len);
-}
-
-#match as_str from_cstr
-from_cstr :: (s: cstr) -> str {
-    return .{ data = s, count = length(s) };
-}
-
-
-length :: #match #local {}
-
-#overload
-length :: (s: str) => s.count;
-
-#overload
-length :: (s: cstr) -> u32 {
-    len := 0;
-    c := s;
-    while *c != #char "\0" {
-        len += 1;
-        c += 1;
-    }
-
-    return len;
-}
-
-
-concat :: #match #local {}
-
-#overload
-concat :: (s1: str, s2: str, allocator := context.allocator) -> str {
-    len1 := length(s1);
-    len2 := length(s2);
-
-    data := cast(^u8) raw_alloc(allocator, len1 + len2);
-    memory.copy(data, s1.data, len1);
-    memory.copy(data + len1, s2.data, len2);
-
-    return str.{ data, len1 + len2 };
-}
-
-// @Cleanup // Don't love that the allocator is necessary here,
-// but it is impossible to specify a default value for the
-// allocator while having a variadic number of strings. This
-// is only due to the languages constraints however. This
-// could easily be changed since there is no ambiguity.
-#overload
-concat :: (allocator: Allocator, strings: ..str) -> str {
-    total_length := 0;
-    for s: strings do total_length += s.count;
-
-    data := cast(^u8) raw_alloc(allocator, total_length);
-    offset := 0;
-    for s: strings {
-        memory.copy(data + offset, s.data, s.count);
-        offset += s.count;
-    }
-
-    return str.{ data, total_length };
-}
-
-#overload
-concat :: (buffer: [] u8, strings: ..str) -> str {
-    total_copied := 0;
-    for s: strings {
-        // Should never greater than, but better safe than sorry.
-        if total_copied >= buffer.count do break;
-
-        bytes_to_copy := math.min(s.count, buffer.count - total_copied);
-        memory.copy(buffer.data + total_copied, s.data, bytes_to_copy);
-        total_copied += bytes_to_copy;
-    }
-
-    return buffer[0 .. total_copied];
-}
-
-#overload
-concat :: (into: ^[..] u8, strings: ..str) -> str {
-    for s: strings {
-        array.ensure_capacity(into, into.count + s.count);
-        memory.copy(into.data + into.count, s.data, s.count);
-        into.count += s.count;
-    }
-    return .{ into.data, into.count };
-}
-
-
-contains :: #match #local {}
-
-#overload
-contains :: (s: str, c: u8) -> bool {
-    for ch: s do if ch == c do return true;
-    return false;
-}
-
-#overload
-contains :: (s: str, substr: str) -> bool {
-    while i := 0; i < s.count {
-        while j := 0; j < substr.count {
-            if s[i + j] != substr[j] {
-                i += j + 1;
-                continue continue;
-            }
-
-            j += 1;
-        }
-
-        return true;
-    }
-
-    return false;
-}
-
-
-// @TODO
-// Check this for edge cases and other bugs. I'm not confident
-// it will work perfectly yet.                   - brendanfh 2020/12/21
-compare :: (str1: str, str2: str) -> i32 {
-    i := 0;
-    while i < str1.count && i < str2.count {
-        if str1[i] == str2[i] do i += 1;
-        else                  do break;
-    }
-
-    if i == str1.count && i == str2.count do return 0;
-    return ~~(str1[i] - str2[i]);
-}
-
-equal :: (str1: str, str2: str) -> bool {
-    if str1.count != str2.count do return false;
-    while i := 0; i < str1.count {
-        if str1[i] != str2[i] do return false;
-        i += 1;
-    }
-    return true;
-}
-
-equal_insensitive :: (s1, s2: str) -> bool {
-    if s1.count != s2.count do return false;
-    while i := 0; i < s1.count {
-        defer i += 1;
-        if s1[i] == s2[i] do continue;
-
-        c1 := s1[i];
-        c2 := s2[i];
-        if c1 >= #char "A" && c1 <= #char "Z" do c1 += 32;
-        if c2 >= #char "A" && c2 <= #char "Z" do c2 += 32;
-        if c1 != c2 do return false;
-    }
-    return true;
-}
-
-#operator == equal
-#operator != macro (s1: str, s2: str) => !(s1 == s2);
-
-starts_with :: (s: str, prefix: str) -> bool {
-    if s.count < prefix.count do return false;
-    while i := 0; i < prefix.count {
-        if s[i] != prefix[i] do return false;
-        i += 1;
-    }
-    return true;
-}
-
-ends_with :: (s: str, suffix: str) -> bool {
-    if s.count < suffix.count do return false;
-    while i := 0; i < suffix.count {
-        if s[s.count - 1 - i] != suffix[suffix.count - 1 - i] do return false;
-        i += 1;
-    }
-    return true;
-}
-
-empty    :: (s: str) => s.count == 0 || s.data == null;
-
-is_empty :: (s: str) -> bool #deprecated "Use 'string.empty' instead." {
-    s.count == 0 || s.data == null;
-}
-
-index_of :: (s: str, c: u8) -> i32 {
-    for s.count {
-        if s[it] == c do return it;
-    }
-    return -1;
-}
-
-last_index_of :: (s: str, c: u8) -> i32 {
-    for range.{s.count, 0, -1} {
-        if s[it] == c do return it;
-    }
-    return -1;
-}
-
-
-strip_whitespace :: #match #local {}
-
-#overload
-strip_whitespace :: (s: ^str) {
-    strip_leading_whitespace(s);
-    strip_trailing_whitespace(s);
-}
-
-#overload
-strip_whitespace :: (s:  str) =>
-    s |> strip_leading_whitespace()
-      |> strip_trailing_whitespace()
-
-
-strip_leading_whitespace :: #match #local {}
-
-#overload
-strip_leading_whitespace :: (s: ^str) {
-    while s.count > 0 do switch s.data[0] {
-        case #char " ", #char "\t", #char "\n", #char "\r" {
-            s.data += 1;
-            s.count -= 1;
-        }
-
-        case #default do return;
-    }
-}
-
-#overload
-strip_leading_whitespace :: (s: str) -> str {
-    out := s;
-    strip_leading_whitespace(^out);
-    return out;
-}
-
-
-strip_trailing_whitespace :: #match #local {}
-
-#overload
-strip_trailing_whitespace :: (s: ^str) {
-    while s.count >= 1 do switch s.data[s.count - 1] {
-        case #char " ", #char "\t", #char "\n", #char "\r" {
-            s.count -= 1;
-        }
-
-        case #default do return;
-    }
-}
-
-#overload
-strip_trailing_whitespace :: (s: str) -> str {
-    out := s;
-    strip_trailing_whitespace(^out);
-    return out;
-}
-
-to_uppercase :: (s: str) -> str {
-    for^ ch: s {
-        if *ch >= #char "a" && *ch <= #char "z" {
-            *ch -= 32;
-        }
-    }
-
-    return s;
-}
-
-to_lowercase :: (s: str) -> str {
-    for^ ch: s {
-        if *ch >= #char "A" && *ch <= #char "Z" {
-            *ch += 32;
-        }
-    }
-
-    return s;
-}
-
-
-trim_start :: #match #local {}
-
-#overload
-trim_start :: (s: ^str, char: u8) {
-    while s.data[0] == char {
-        s.data += 1;
-        s.count -= 1;
-    }
-}
-
-#overload
-trim_start :: (s: str, char: u8) -> str {
-    out := s;
-    trim_start(^out, char);
-    return out;
-}
-
-
-trim_end :: #match #local {}
-
-#overload
-trim_end :: (s: ^str, char: u8) {
-    while s.data[s.count - 1] == char {
-        s.count -= 1;
-    }
-}
-
-#overload
-trim_end :: (s: str, char: u8) -> str {
-    out := s;
-    trim_end(^out, char);
-    return out;
-}
-
-
-advance :: #match #local {}
-
-#overload
-advance :: (s: ^str, chars := 1) {
-    chars = math.min(chars, s.count);
-
-    s.data += chars;
-    s.count -= chars;
-}
-
-#overload
-advance :: (s: str, chars := 1) -> str {
-    chars = math.min(chars, s.count);
-    out := s;
-
-    out.data += chars;
-    out.count -= chars;
-
-    return out;
-}
-
-replace :: (s: str, to_replace: u8, replace_with: u8) {
-    for ^c: s {
-        if *c == to_replace do *c = replace_with;
-    }
-}
-
-read_until :: #match #local {}
-
-#overload
-read_until :: (s: ^str, upto: u8, skip := 0) -> str {
-    if s.count == 0 do return "";
-
-    out : str;
-    out.data = s.data;
-    out.count = 0;
-
-    rem := skip;
-    for ch: *s {
-        if ch == upto {
-            if rem <= 0 do break;
-            else do rem -= 1;
-        }
-
-        out.count += 1;
-    }
-
-    s.data += out.count;
-    s.count -= out.count;
-
-    return out;
-}
-
-#overload
-read_until :: (s: ^str, upto: str, skip := 0) -> str {
-    if s.count == 0 do return "";
-
-    out := str.{ data = s.data };
-
-    rem := skip;
-    i := 0;
-    while i <= s.count - upto.count {
-        match := true;
-        j := i;
-        for upto {
-            if s.data[j] != it {
-                match = false;
-                break;
-            }
-
-            j += 1;
-        }
-
-        if match {
-            if rem <= 0 do break;
-            else do rem -= 1;
-        }
-
-        i += 1;
-    }
-
-    if i > s.count - upto.count {
-        out = *s;
-        s.data  += out.count;
-        s.count  = 0;
-
-    } else {
-        out.count = i;
-        s.data  += out.count;
-        s.count -= out.count;
-    }
-
-    return out;
-}
-
-read_alphanum :: (s: ^str) -> str {
-    if s.count == 0 do return "";
-
-    out : str;
-    out.data = s.data;
-    out.count = 0;
-
-    for ch: *s {
-        switch ch {
-            case #char "a" .. #char "z",
-                 #char "A" .. #char "Z",
-                 #char "0" .. #char "9" {
-                out.count += 1;
-            }
-
-            case #default {
-                break break;
-            }
-        }
-    }
-
-    s.data += out.count;
-    s.count -= out.count;
-
-    return out;
-}
-
-read_until_any :: (s: ^str, skip: u32, uptos: ..u8) -> str {
-    if s.count == 0 do return "";
-
-    out : str;
-    out.data = s.data;
-    out.count = 0;
-
-    rem := skip;
-    for ch: *s {
-        for upto: uptos {
-            if ch == upto {
-                if rem <= 0 do break break;
-                else do rem -= 1;
-            }
-        }
-
-        out.count += 1;
-    }
-
-    s.data += out.count;
-    s.count -= out.count;
-
-    return out;
-}
-
-advance_line :: (s: ^str) {
-    if s.count == 0 do return;
-
-    adv := 0;
-    while s.data[adv] != #char "\n" do adv += 1;
-
-    s.data += adv + 1;
-    s.count -= adv + 1;
-}
-
-split :: (s: str, delim: u8, allocator := context.allocator) -> []str {
-    delim_count := 0;
-    for i: 0 .. s.count do if s[i] == delim do delim_count += 1;
-
-    strarr := cast(^str) raw_alloc(allocator, sizeof str * (delim_count + 1));
-
-    curr_str := 0;
-    begin := 0;
-
-    for i: 0 .. s.count {
-        if s[i] == delim {
-            strarr[curr_str] = s.data[begin .. i];
-            begin = i + 1;
-            curr_str += 1;
-        }
-    }
-
-    strarr[curr_str] = s.data[begin .. s.count];
-
-    return strarr[0 .. delim_count + 1];
-}
-
-split_iter :: (s: str, delim: u8) -> Iterator(str) {
-    return iter.generator(
-        ^.{ s = s, delim = delim },
-
-        (ctx: ^$T) -> (str, bool) {
-            if string.empty(ctx.s) {
-                return "", false;
-            }
-
-            ret: str;
-            ret, ctx.s = bisect(ctx.s, ctx.delim);
-            return ret, true;
-        }
-    );
-}
-
-//
-// Splits a string into two parts, divided by the
-// first instance of the provided character. Either
-// string can be empty if the first instance of the
-// character occurs at the very beginning or end of
-// the string, or if it does not occur at all.
-//
-bisect :: (s: str, c: u8) -> (str, str) {
-    index := index_of(s, c); 
-    if index == -1 {
-        return s, "";
-    }
-
-    return s[0 .. index], s[index+1 .. s.length];
-}
-
diff --git a/core/string/string.onyx b/core/string/string.onyx
new file mode 100644 (file)
index 0000000..1e37901
--- /dev/null
@@ -0,0 +1,543 @@
+package core.string
+
+use core
+
+as_str :: #match {}
+
+free :: (s: str, allocator := context.allocator) do raw_free(allocator, s.data);
+
+alloc_copy :: (original: str, allocator := context.allocator) -> str {
+    new_str : str;
+    new_str.data = raw_alloc(allocator, sizeof u8 * original.count);
+    new_str.count = original.count;
+    copy(original, new_str);
+    return new_str;
+}
+
+temp_copy :: (original: str) -> str {
+    new_str := make([] u8, original.count, allocator=context.temp_allocator);
+    copy(original, new_str);
+    return new_str;
+}
+
+copy :: (orig: str, dest: str) {
+    len := orig.count;
+    if dest.count < len do len = dest.count;
+
+    memory.copy(dest.data, orig.data, len);
+}
+
+#match as_str from_cstr
+from_cstr :: (s: cstr) -> str {
+    return .{ data = s, count = length(s) };
+}
+
+
+length :: #match #local {}
+
+#overload
+length :: (s: str) => s.count;
+
+#overload
+length :: (s: cstr) -> u32 {
+    len := 0;
+    c := s;
+    while *c != #char "\0" {
+        len += 1;
+        c += 1;
+    }
+
+    return len;
+}
+
+
+concat :: #match #local {}
+
+#overload
+concat :: (s1: str, s2: str, allocator := context.allocator) -> str {
+    len1 := length(s1);
+    len2 := length(s2);
+
+    data := cast(^u8) raw_alloc(allocator, len1 + len2);
+    memory.copy(data, s1.data, len1);
+    memory.copy(data + len1, s2.data, len2);
+
+    return str.{ data, len1 + len2 };
+}
+
+// @Cleanup // Don't love that the allocator is necessary here,
+// but it is impossible to specify a default value for the
+// allocator while having a variadic number of strings. This
+// is only due to the languages constraints however. This
+// could easily be changed since there is no ambiguity.
+#overload
+concat :: (allocator: Allocator, strings: ..str) -> str {
+    total_length := 0;
+    for s: strings do total_length += s.count;
+
+    data := cast(^u8) raw_alloc(allocator, total_length);
+    offset := 0;
+    for s: strings {
+        memory.copy(data + offset, s.data, s.count);
+        offset += s.count;
+    }
+
+    return str.{ data, total_length };
+}
+
+#overload
+concat :: (buffer: [] u8, strings: ..str) -> str {
+    total_copied := 0;
+    for s: strings {
+        // Should never greater than, but better safe than sorry.
+        if total_copied >= buffer.count do break;
+
+        bytes_to_copy := math.min(s.count, buffer.count - total_copied);
+        memory.copy(buffer.data + total_copied, s.data, bytes_to_copy);
+        total_copied += bytes_to_copy;
+    }
+
+    return buffer[0 .. total_copied];
+}
+
+#overload
+concat :: (into: ^[..] u8, strings: ..str) -> str {
+    for s: strings {
+        array.ensure_capacity(into, into.count + s.count);
+        memory.copy(into.data + into.count, s.data, s.count);
+        into.count += s.count;
+    }
+    return .{ into.data, into.count };
+}
+
+
+contains :: #match #local {}
+
+#overload
+contains :: (s: str, c: u8) -> bool {
+    for ch: s do if ch == c do return true;
+    return false;
+}
+
+#overload
+contains :: (s: str, substr: str) -> bool {
+    while i := 0; i < s.count {
+        while j := 0; j < substr.count {
+            if s[i + j] != substr[j] {
+                i += j + 1;
+                continue continue;
+            }
+
+            j += 1;
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+
+// @TODO
+// Check this for edge cases and other bugs. I'm not confident
+// it will work perfectly yet.                   - brendanfh 2020/12/21
+compare :: (str1: str, str2: str) -> i32 {
+    i := 0;
+    while i < str1.count && i < str2.count {
+        if str1[i] == str2[i] do i += 1;
+        else                  do break;
+    }
+
+    if i == str1.count && i == str2.count do return 0;
+    return ~~(str1[i] - str2[i]);
+}
+
+equal :: (str1: str, str2: str) -> bool {
+    if str1.count != str2.count do return false;
+    while i := 0; i < str1.count {
+        if str1[i] != str2[i] do return false;
+        i += 1;
+    }
+    return true;
+}
+
+equal_insensitive :: (s1, s2: str) -> bool {
+    if s1.count != s2.count do return false;
+    while i := 0; i < s1.count {
+        defer i += 1;
+        if s1[i] == s2[i] do continue;
+
+        c1 := s1[i];
+        c2 := s2[i];
+        if c1 >= #char "A" && c1 <= #char "Z" do c1 += 32;
+        if c2 >= #char "A" && c2 <= #char "Z" do c2 += 32;
+        if c1 != c2 do return false;
+    }
+    return true;
+}
+
+#operator == equal
+#operator != macro (s1: str, s2: str) => !(s1 == s2);
+
+starts_with :: (s: str, prefix: str) -> bool {
+    if s.count < prefix.count do return false;
+    while i := 0; i < prefix.count {
+        if s[i] != prefix[i] do return false;
+        i += 1;
+    }
+    return true;
+}
+
+ends_with :: (s: str, suffix: str) -> bool {
+    if s.count < suffix.count do return false;
+    while i := 0; i < suffix.count {
+        if s[s.count - 1 - i] != suffix[suffix.count - 1 - i] do return false;
+        i += 1;
+    }
+    return true;
+}
+
+empty    :: (s: str) => s.count == 0 || s.data == null;
+
+is_empty :: (s: str) -> bool #deprecated "Use 'string.empty' instead." {
+    s.count == 0 || s.data == null;
+}
+
+index_of :: (s: str, c: u8) -> i32 {
+    for s.count {
+        if s[it] == c do return it;
+    }
+    return -1;
+}
+
+last_index_of :: (s: str, c: u8) -> i32 {
+    for range.{s.count, 0, -1} {
+        if s[it] == c do return it;
+    }
+    return -1;
+}
+
+
+strip_whitespace :: #match #local {}
+
+#overload
+strip_whitespace :: (s: ^str) {
+    strip_leading_whitespace(s);
+    strip_trailing_whitespace(s);
+}
+
+#overload
+strip_whitespace :: (s:  str) =>
+    s |> strip_leading_whitespace()
+      |> strip_trailing_whitespace()
+
+
+strip_leading_whitespace :: #match #local {}
+
+#overload
+strip_leading_whitespace :: (s: ^str) {
+    while s.count > 0 do switch s.data[0] {
+        case #char " ", #char "\t", #char "\n", #char "\r" {
+            s.data += 1;
+            s.count -= 1;
+        }
+
+        case #default do return;
+    }
+}
+
+#overload
+strip_leading_whitespace :: (s: str) -> str {
+    out := s;
+    strip_leading_whitespace(^out);
+    return out;
+}
+
+
+strip_trailing_whitespace :: #match #local {}
+
+#overload
+strip_trailing_whitespace :: (s: ^str) {
+    while s.count >= 1 do switch s.data[s.count - 1] {
+        case #char " ", #char "\t", #char "\n", #char "\r" {
+            s.count -= 1;
+        }
+
+        case #default do return;
+    }
+}
+
+#overload
+strip_trailing_whitespace :: (s: str) -> str {
+    out := s;
+    strip_trailing_whitespace(^out);
+    return out;
+}
+
+to_uppercase :: (s: str) -> str {
+    for^ ch: s {
+        if *ch >= #char "a" && *ch <= #char "z" {
+            *ch -= 32;
+        }
+    }
+
+    return s;
+}
+
+to_lowercase :: (s: str) -> str {
+    for^ ch: s {
+        if *ch >= #char "A" && *ch <= #char "Z" {
+            *ch += 32;
+        }
+    }
+
+    return s;
+}
+
+
+trim_start :: #match #local {}
+
+#overload
+trim_start :: (s: ^str, char: u8) {
+    while s.data[0] == char {
+        s.data += 1;
+        s.count -= 1;
+    }
+}
+
+#overload
+trim_start :: (s: str, char: u8) -> str {
+    out := s;
+    trim_start(^out, char);
+    return out;
+}
+
+
+trim_end :: #match #local {}
+
+#overload
+trim_end :: (s: ^str, char: u8) {
+    while s.data[s.count - 1] == char {
+        s.count -= 1;
+    }
+}
+
+#overload
+trim_end :: (s: str, char: u8) -> str {
+    out := s;
+    trim_end(^out, char);
+    return out;
+}
+
+
+advance :: #match #local {}
+
+#overload
+advance :: (s: ^str, chars := 1) {
+    chars = math.min(chars, s.count);
+
+    s.data += chars;
+    s.count -= chars;
+}
+
+#overload
+advance :: (s: str, chars := 1) -> str {
+    chars = math.min(chars, s.count);
+    out := s;
+
+    out.data += chars;
+    out.count -= chars;
+
+    return out;
+}
+
+replace :: (s: str, to_replace: u8, replace_with: u8) {
+    for ^c: s {
+        if *c == to_replace do *c = replace_with;
+    }
+}
+
+read_until :: #match #local {}
+
+#overload
+read_until :: (s: ^str, upto: u8, skip := 0) -> str {
+    if s.count == 0 do return "";
+
+    out : str;
+    out.data = s.data;
+    out.count = 0;
+
+    rem := skip;
+    for ch: *s {
+        if ch == upto {
+            if rem <= 0 do break;
+            else do rem -= 1;
+        }
+
+        out.count += 1;
+    }
+
+    s.data += out.count;
+    s.count -= out.count;
+
+    return out;
+}
+
+#overload
+read_until :: (s: ^str, upto: str, skip := 0) -> str {
+    if s.count == 0 do return "";
+
+    out := str.{ data = s.data };
+
+    rem := skip;
+    i := 0;
+    while i <= s.count - upto.count {
+        match := true;
+        j := i;
+        for upto {
+            if s.data[j] != it {
+                match = false;
+                break;
+            }
+
+            j += 1;
+        }
+
+        if match {
+            if rem <= 0 do break;
+            else do rem -= 1;
+        }
+
+        i += 1;
+    }
+
+    if i > s.count - upto.count {
+        out = *s;
+        s.data  += out.count;
+        s.count  = 0;
+
+    } else {
+        out.count = i;
+        s.data  += out.count;
+        s.count -= out.count;
+    }
+
+    return out;
+}
+
+read_alphanum :: (s: ^str) -> str {
+    if s.count == 0 do return "";
+
+    out : str;
+    out.data = s.data;
+    out.count = 0;
+
+    for ch: *s {
+        switch ch {
+            case #char "a" .. #char "z",
+                 #char "A" .. #char "Z",
+                 #char "0" .. #char "9" {
+                out.count += 1;
+            }
+
+            case #default {
+                break break;
+            }
+        }
+    }
+
+    s.data += out.count;
+    s.count -= out.count;
+
+    return out;
+}
+
+read_until_any :: (s: ^str, skip: u32, uptos: ..u8) -> str {
+    if s.count == 0 do return "";
+
+    out : str;
+    out.data = s.data;
+    out.count = 0;
+
+    rem := skip;
+    for ch: *s {
+        for upto: uptos {
+            if ch == upto {
+                if rem <= 0 do break break;
+                else do rem -= 1;
+            }
+        }
+
+        out.count += 1;
+    }
+
+    s.data += out.count;
+    s.count -= out.count;
+
+    return out;
+}
+
+advance_line :: (s: ^str) {
+    if s.count == 0 do return;
+
+    adv := 0;
+    while s.data[adv] != #char "\n" do adv += 1;
+
+    s.data += adv + 1;
+    s.count -= adv + 1;
+}
+
+split :: (s: str, delim: u8, allocator := context.allocator) -> []str {
+    delim_count := 0;
+    for i: 0 .. s.count do if s[i] == delim do delim_count += 1;
+
+    strarr := cast(^str) raw_alloc(allocator, sizeof str * (delim_count + 1));
+
+    curr_str := 0;
+    begin := 0;
+
+    for i: 0 .. s.count {
+        if s[i] == delim {
+            strarr[curr_str] = s.data[begin .. i];
+            begin = i + 1;
+            curr_str += 1;
+        }
+    }
+
+    strarr[curr_str] = s.data[begin .. s.count];
+
+    return strarr[0 .. delim_count + 1];
+}
+
+split_iter :: (s: str, delim: u8) -> Iterator(str) {
+    return iter.generator(
+        ^.{ s = s, delim = delim },
+
+        (ctx: ^$T) -> (str, bool) {
+            if string.empty(ctx.s) {
+                return "", false;
+            }
+
+            ret: str;
+            ret, ctx.s = bisect(ctx.s, ctx.delim);
+            return ret, true;
+        }
+    );
+}
+
+//
+// Splits a string into two parts, divided by the
+// first instance of the provided character. Either
+// string can be empty if the first instance of the
+// character occurs at the very beginning or end of
+// the string, or if it does not occur at all.
+//
+bisect :: (s: str, c: u8) -> (str, str) {
+    index := index_of(s, c); 
+    if index == -1 {
+        return s, "";
+    }
+
+    return s[0 .. index], s[index+1 .. s.length];
+}
+
index 926040edcb6149a324e46af51e20b2158cfca775..a580cdb757814f25c3dee83de6f6e68ab6eb3cd3 100644 (file)
@@ -425,6 +425,27 @@ run_list_versions :: (args: [] cstr) {
     }
 }
 
+#tag Command.{ "run", "Run the command provided in `config.run_cmd`." }
+run_run_command :: (args: [] cstr) {
+    if !string.empty(config.config.run_cmd) {
+        run_command_and_forward_output(config.config.run_cmd);
+    }
+}
+
+#tag Command.{ "debug", "Run the command provided in `config.debug_cmd`." }
+run_debug_command :: (args: [] cstr) {
+    if !string.empty(config.config.debug_cmd) {
+        run_command_and_forward_output(config.config.debug_cmd);
+    }
+}
+
+#tag Command.{ "test", "Run the command provided in `config.test_cmd`." }
+run_test_command :: (args: [] cstr) {
+    if !string.empty(config.config.test_cmd) {
+        run_command_and_forward_output(config.config.test_cmd);
+    }
+}
+
 
 #local {
     #if runtime.compiler_os == .Linux {
@@ -562,6 +583,22 @@ run_native_library_installation :: (folder: str) -> bool {
     }
 }
 
+run_command_and_forward_output :: (cmd: str) => {
+    args := string.split(cmd, #char " ", context.temp_allocator);
+    prog := args[0];
+    args  = args[1 .. args.count];
+
+    run_proc := os.process_spawn(prog, args);
+    r := io.reader_make(^run_proc);
+
+    while !(r->empty()) {
+        line := r->read_line(consume_newline=true);
+        print(line);
+    }
+
+    return os.process_wait(^run_proc);
+}
+
 
 #tag conv.Custom_Parse.{parse}
 #tag conv.Custom_Format.{format}
@@ -791,6 +828,9 @@ Config :: struct {
     Config :: struct {
         lib_source_directory: str = "./lib";
         lib_bin_directory: str    = "./bin";
+        run_cmd: str;
+        debug_cmd: str;
+        test_cmd: str;
     }
     config: Config = .{};
 
@@ -931,8 +971,7 @@ parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseE
         output.type = (cast(^info.Type_Info_Pointer) t).to;
     }
 
-    // Set the temporary allocator to an arena that will be freed at the end.
-    alloc.arena.auto(32 * 1024,  #(context.temp_allocator));
+    defer alloc.clear_temp_allocator();
 
     active_item_ptr  := null;
     active_item_type := void;
index 09751b8ad553586d5954f279e4ec5c4cb6fdd705..6319498f846a1ad98771deebf4a2847b5d969fa3 100755 (executable)
Binary files a/shared/lib/linux_x86_64/lib/libovmwasm.so and b/shared/lib/linux_x86_64/lib/libovmwasm.so differ
index d7a88f1215c3ff1ab1461e8389f8d81e5e4c448a..38cb257497bb661e44ac363e7cdcd7a7ea9d10d9 100644 (file)
@@ -19,7 +19,7 @@ main :: (args: [] cstr) {
 
     printf("{}{}\n", map.get(^imap, 50), map.get(^imap, 1234));
 
-    printf("{*}\n", ^imap);
+    printf("{*p}\n", ^imap);
 
     map.delete(^imap, 50);
     println(map.has(^imap, 50));