From: Brendan Hansen Date: Mon, 31 Oct 2022 03:22:21 +0000 (-0500) Subject: various bugfixes and additions to core libraries X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=24c72e04d3f4b0684906564afbf38e18f49ef2e7;p=onyx.git various bugfixes and additions to core libraries --- diff --git a/core/alloc/arena.onyx b/core/alloc/arena.onyx index 80aa852e..17b333ad 100644 --- a/core/alloc/arena.onyx +++ b/core/alloc/arena.onyx @@ -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 { diff --git a/core/container/iter.onyx b/core/container/iter.onyx index 8d8a7aa8..dc6a463c 100644 --- a/core/container/iter.onyx +++ b/core/container/iter.onyx @@ -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 index 00000000..62665648 --- /dev/null +++ b/core/encoding/csv.onyx @@ -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"); + } + } +} + diff --git a/core/io/reader.onyx b/core/io/reader.onyx index 58f9986b..c69aa790 100644 --- a/core/io/reader.onyx +++ b/core/io/reader.onyx @@ -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); } diff --git a/core/misc/any_utils.onyx b/core/misc/any_utils.onyx index 412a89bf..84b51f7f 100644 --- a/core/misc/any_utils.onyx +++ b/core/misc/any_utils.onyx @@ -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 { diff --git a/core/std.onyx b/core/std.onyx index 210d9fed..c13c94f7 100644 --- a/core/std.onyx +++ b/core/std.onyx @@ -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" diff --git a/core/string.onyx b/core/string.onyx index 1b9d9cf7..1e37901f 100644 --- a/core/string.onyx +++ b/core/string.onyx @@ -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 index 00000000..72c9cdfe --- /dev/null +++ b/core/time/date.onyx @@ -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; + diff --git a/tests/bugs/double_polymorph_yield_error b/tests/bugs/double_polymorph_yield_error index f605338a..16f43dcb 100644 --- a/tests/bugs/double_polymorph_yield_error +++ b/tests/bugs/double_polymorph_yield_error @@ -1,3 +1,3 @@ -0x110A0 +5 false bool diff --git a/tests/bugs/double_polymorph_yield_error.onyx b/tests/bugs/double_polymorph_yield_error.onyx index eec85551..96e4b032 100644 --- a/tests/bugs/double_polymorph_yield_error.onyx +++ b/tests/bugs/double_polymorph_yield_error.onyx @@ -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 +}