various bugfixes and additions to core libraries
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 31 Oct 2022 03:22:21 +0000 (22:22 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 31 Oct 2022 03:22:21 +0000 (22:22 -0500)
core/alloc/arena.onyx
core/container/iter.onyx
core/encoding/csv.onyx [new file with mode: 0644]
core/io/reader.onyx
core/misc/any_utils.onyx
core/std.onyx
core/string.onyx
core/time/date.onyx [new file with mode: 0644]
tests/bugs/double_polymorph_yield_error
tests/bugs/double_polymorph_yield_error.onyx

index 80aa852ed19f616f28ef7fb3f3dcba154a2386cb..17b333ad963f9bc4d41ae7ee99245944827ae1a5 100644 (file)
@@ -33,9 +33,23 @@ arena_alloc_proc :: (data: rawptr, aa: AllocationAction, size: u32, align: u32,
     alloc_arena := cast(^Arena) data;
 
     if aa == .Alloc {
-        // An allocation of this size does not fit into a single arena.
+        // An allocation of this size does not fit into a single arena,
+        // so make a new "special" arena that only stores this allocation.
         if size > alloc_arena.arena_size - sizeof rawptr {
-            return null;
+            ret_arena := cast(^ArenaBlock) raw_alloc(alloc_arena.backing_allocator, size + sizeof rawptr);
+            new_arena := cast(^ArenaBlock) raw_alloc(alloc_arena.backing_allocator, alloc_arena.arena_size);
+
+            if ret_arena == null || new_arena == null do return null;
+
+            alloc_arena.size = sizeof rawptr;
+
+            alloc_arena.current_arena.next = ret_arena;
+            ret_arena.next = new_arena;
+            new_arena.next = null;
+            
+            alloc_arena.current_arena = new_arena;
+
+            return cast(rawptr) (cast(^u8) ret_arena + sizeof rawptr);
         }
 
         if alloc_arena.size % align != 0 {
index 8d8a7aa8d9df23168b46bd1f383b5d774c46cd48..dc6a463c5652682fd9a84c00f62937a15ea518f8 100644 (file)
@@ -31,18 +31,9 @@ ImplicitIterator :: interface (t: $T) {
 }
 
 #overload #precedence 10000
-as_iterator :: (x: $T/ImplicitIterator) => {
-    extract_iterator_type :: macro (x: $T, next: (T) -> ($I, bool)) -> Iterator(I) {
-        x->iter_open();
-
-        return .{
-            data  = x,
-            next  = next,
-            close = x.iter_close
-        };
-    }
-
-    return extract_iterator_type(x, T.iter_next);
+as_iterator :: (x: ^$T/ImplicitIterator) => {
+    x->iter_open();
+    return generator(x, T.iter_next, T.iter_close);
 }
 
 
@@ -712,6 +703,9 @@ comp :: macro (i: $I/Iterable, value: Code) => {
 //
 // Maybe at some point an alternate allocator would be good
 // for this? For now, I think the temporary allocator is sufficient.
+generator :: #match #local {}
+
+#overload
 generator :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool)) -> Iterator(T) {
     v := raw_alloc(context.temp_allocator, sizeof Ctx);
     core.memory.copy(v, ctx, sizeof Ctx);
@@ -722,6 +716,18 @@ generator :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool)) -> Iterator(T) {
     };
 }
 
+#overload
+generator :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool), close: (^Ctx) -> void) -> Iterator(T) {
+    v := raw_alloc(context.temp_allocator, sizeof Ctx);
+    core.memory.copy(v, ctx, sizeof Ctx);
+
+    return .{
+        data = v,
+        next = gen,
+        close = close
+    };
+}
+
 
 #if runtime.Multi_Threading_Enabled {
     #local sync :: core.sync
diff --git a/core/encoding/csv.onyx b/core/encoding/csv.onyx
new file mode 100644 (file)
index 0000000..6266564
--- /dev/null
@@ -0,0 +1,113 @@
+package core.encoding.csv
+
+use core {string, array, iter, conv, io}
+use core.misc {any_as}
+use runtime.info {
+    get_type_info,
+    Type_Info_Struct
+}
+
+CSV :: struct (Output_Type: type_expr) {
+    entries: [..] Output_Type;
+}
+
+CSV_Column :: struct {
+    name: str;
+}
+
+#inject CSV {
+    make :: ($T: type_expr) => {
+        r := CSV(T).{};
+        r.entries = make(typeof r.entries);
+
+        return r;
+    }
+
+    delete :: (csv: ^CSV) {
+        delete(^csv.entries);
+    }
+    
+    ingress :: (csv: ^CSV, contents: str, headers_present := true) -> bool {
+        Header :: struct {
+            type: type_expr;
+            offset: i32;
+        }
+
+        s := contents;
+        any_headers := make([..] Header);
+        defer delete(^any_headers);
+
+        output_type_info: ^Type_Info_Struct = ~~ get_type_info(csv.Output_Type);
+
+        if headers_present {
+            header_line, s' := string.bisect(s, #char "\n");
+            for header: string.split_iter(header_line, #char ",") {
+                member := array.first(output_type_info.members, #(do {
+                    if tag := array.first(it.tags, #(it.type == CSV_Column)); tag {
+                        return any_as(*tag, CSV_Column).name == header;
+                    }
+
+                    return false;
+                }));
+
+                any_headers << (.{member.type, member.offset}) if member else .{void, -1};
+            }
+
+        } else {
+            for ^member: output_type_info.members {
+                any_headers << .{ member.type, member.offset };
+            }
+        }
+
+        for line: string.split_iter(s, #char "\n") {
+            out: csv.Output_Type;
+
+            for entry:
+                string.split_iter(line, #char ",")
+                |> iter.enumerate()
+            {
+                header := ^any_headers[entry.index];
+                if header.offset == -1 do continue;
+
+                target := cast(^u8) ^out + header.offset;
+
+                if header.type == str {
+                    *cast(^str) target = string.alloc_copy(entry.value);
+                } else {
+                    conv.parse_any(target, header.type, entry.value);
+                }
+            }
+
+            csv.entries << out;
+        }
+    }
+
+    egress :: (csv: ^CSV, writer: ^io.Writer, include_headers := true) {
+        output_type_info: ^Type_Info_Struct = ~~ get_type_info(csv.Output_Type);
+
+        if include_headers {
+            for ^member: output_type_info.members {
+                if !#first do io.write(writer, ",");
+
+                if tag := array.first(member.tags, #(it.type == CSV_Column)); tag {
+                    io.write(writer, any_as(*tag, CSV_Column).name);
+                } else {
+                    io.write(writer, member.name);
+                }
+            }
+
+            io.write(writer, "\n");
+        }
+
+        for ^it: csv.entries {
+            for ^member: output_type_info.members {
+                if !#first do io.write(writer, ",");
+
+                io.write_format_va(writer, "{}", .[ .{cast(^u8) it + member.offset, member.type} ]);
+            }
+
+            io.write(writer, "\n");
+        }
+    }
+}
+
index 58f9986b336574190fe81263037877cd70f6560e..c69aa790ac15815cbe36e005029b76d8071cbaa8 100644 (file)
@@ -65,6 +65,7 @@ reader_reset :: (use reader: ^Reader) {
     done = false;
 }
 
+#overload delete :: reader_free
 reader_free :: (use reader: ^Reader) {
     memory.free_slice(^buffer, buffer_allocator);
 }
index 412a89bf946fb27811a608953ffaa9a5f0d17e48..84b51f7f735ca27d78ac7a83f867e352dde2fbb5 100644 (file)
@@ -10,6 +10,11 @@ use runtime.info {
 
 to_any :: macro (x: ^$T) => any.{x, T};
 
+any_as :: (a: any, $T: type_expr) -> ^T {
+    if a.type != T do return null;
+    return cast(^T) a.data;
+}
+
 any_deference :: (v: any) -> any {
     t := get_type_info(v.type);
     if t.kind == .Pointer {
index 210d9fed19aeddb6a139e13442e362fcb28d8b75..c13c94f793dc220b7ed7dc19c003e1dc4f0a856a 100644 (file)
@@ -41,6 +41,8 @@ package core
 
 #load "./misc/any_utils"
 
+#load "./encoding/csv"
+
 #local runtime :: package runtime
 #if runtime.runtime == .Wasi || runtime.runtime == .Onyx {
     #load "./os/file"
@@ -53,6 +55,7 @@ package core
 
     #load "./os/process"
     #load "./time/time"
+    #load "./time/date"
 
     #load "./net/net"
     #load "./net/tcp"
index 1b9d9cf736c3adfccab80cfcaf9e3aed0a0bfd87..1e37901ffa5413805deb798cc8d297fe6f4fbf28 100644 (file)
@@ -509,6 +509,22 @@ split :: (s: str, delim: u8, allocator := context.allocator) -> []str {
     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
diff --git a/core/time/date.onyx b/core/time/date.onyx
new file mode 100644 (file)
index 0000000..72c9cdf
--- /dev/null
@@ -0,0 +1,89 @@
+package core.time
+
+use core
+
+@conv.Custom_Format.{_format}
+@conv.Custom_Parse.{_parse}
+Date :: struct {
+    year: i32;
+
+    // Note that `month` and `day` are 0-based.
+    month, day: i32;
+}
+
+#inject Date {
+    month_durations := u32.[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+    add_months :: (d: Date, days: i32) -> Date {
+        nd := d;
+
+        nd.month += 1;
+        if nd.month >= 12 {
+            nd.month = 0;
+            nd.year += 1;
+        }
+
+        return nd;
+    }
+
+    add_days :: (d: Date, days: i32) -> Date {
+        nd := d;
+        nd.day += days;
+
+        while nd.day >= Date.month_durations[nd.month] {
+            nd.day -= Date.month_durations[nd.month];
+
+            // February leap year case
+            if nd.month == 1 {
+                if nd.year % 4 == 0 && (nd.year % 100 != 0 || nd.year % 400 == 0) {
+                    nd.day -= 1;
+                }
+            }
+
+            nd.month += 1;
+            if nd.month >= 12 {
+                nd.month = 0;
+                nd.year += 1;
+            }
+        }
+
+        return nd;
+    }
+
+    before :: (d1, d2: Date) -> bool {
+        if d1.year != d2.year do return d1.year < d2.year;
+        if d1.month != d2.month do return d1.month < d2.month;
+        return d1.day < d2.day;
+    }
+
+    after :: (d1, d2: Date) -> bool {
+        if d1.year != d2.year do return d1.year > d2.year;
+        if d1.month != d2.month do return d1.month > d2.month;
+        return d1.day > d2.day;
+    }
+
+    _parse :: (d: ^Date, text: str, _: Allocator) -> bool {
+        year,  t  := string.bisect(text, #char "-");
+        month, t' := string.bisect(t,    #char "-");
+        day,   t' := string.bisect(t,    #char "-");
+
+        d.year  = ~~  conv.str_to_i64(year);
+        d.month = ~~ (conv.str_to_i64(month) - 1);
+        d.day   = ~~ (conv.str_to_i64(day) - 1);
+
+        return true;
+    }
+
+    _format :: (output: ^conv.Format_Output, format: ^conv.Format, date: ^Date) {
+        conv.format(output, "{}-{}-{}", date.year, date.month + 1, date.day + 1);
+    }
+}
+
+#operator  + macro (d: Date, days: i32) => d->add_days(days);
+#operator  - macro (d: Date, days: i32) => d->add_days(-days);
+
+#operator == (d1, d2: Date) =>
+    d1.year == d2.year &&
+    d1.month == d2.month &&
+    d1.day == d2.day;
+
index f605338ad1e4c332930dc44d87ea1553289405e5..16f43dcb482cc1bf3aecc15f0b07bbcd79b11773 100644 (file)
@@ -1,3 +1,3 @@
-0x110A0
+5
 false
 bool
index eec85551bb22b11e23ff44604875fc115ee30910..96e4b032b5613b871ed81d4d05cc7d4003c99ac1 100644 (file)
@@ -3,7 +3,7 @@
 use core
 
 q :: (v: ^$C, g: (^C) -> $T) {
-    println(v);
+    println(*v);
     println(g(v));
     println(T);
 }
@@ -12,4 +12,4 @@ main :: () {
     v := 5;
 
     q(^v, _ => false);
-}
\ No newline at end of file
+}