From: Brendan Hansen Date: Fri, 26 Aug 2022 02:49:28 +0000 (-0500) Subject: standard library cleanup part 1; added time library X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=4eb9137503b5b77ce378e971bdcf3f02392971f5;p=onyx.git standard library cleanup part 1; added time library --- diff --git a/core/alloc/auto_heap.onyx b/core/alloc/auto_heap.onyx index bd8cbe0b..03334251 100644 --- a/core/alloc/auto_heap.onyx +++ b/core/alloc/auto_heap.onyx @@ -33,11 +33,16 @@ auto_heap_make :: (backing := context.allocator) -> AutoHeapState { return hs; } -auto_heap_free :: (hs: ^AutoHeapState) { +auto_heap_clear :: (hs: ^AutoHeapState) { for^ hs.set.entries { raw_free(hs.backing_allocator, it.value); } + hs.set->clear(); +} + +auto_heap_free :: (hs: ^AutoHeapState) { + auto_heap_clear(hs); hs.set->free(); } diff --git a/core/container/array.onyx b/core/container/array.onyx index e400b9d8..ca36054c 100644 --- a/core/container/array.onyx +++ b/core/container/array.onyx @@ -12,26 +12,30 @@ package core.array // --------------------------------- // Dynamic Arrays // --------------------------------- -make :: #match #locked { - ($T: type_expr, capacity := 4, allocator := context.allocator) -> [..] T { - arr : [..] T; - init(^arr, capacity, allocator); - return arr; - }, - - (base: [] $T, allocator := context.allocator) -> [..] T { - arr: [..] T; - init(^arr, base.count, allocator); - for^ base do arr << *it; - return arr; - } +make :: #match #local {} + +#overload +make :: ($T: type_expr, capacity := 4, allocator := context.allocator) -> [..] T { + arr : [..] T; + init(^arr, capacity, allocator); + return arr; +} + +#overload +make :: (base: [] $T, allocator := context.allocator) -> [..] T { + arr: [..] T; + init(^arr, base.count, allocator); + for^ base do arr << *it; + return arr; } -#match __make_overload macro (_: ^[..] $T, allocator := context.allocator) -> [..] T { +#overload +__make_overload :: macro (_: ^[..] $T, allocator := context.allocator) -> [..] T { return (package core.array).make(T, allocator=allocator); } -#match __make_overload macro (_: ^[..] $T, capacity: u32, allocator := context.allocator) -> [..] T { +#overload +__make_overload :: macro (_: ^[..] $T, capacity: u32, allocator := context.allocator) -> [..] T { return (package core.array).make(T, capacity, allocator); } @@ -50,8 +54,9 @@ free :: (arr: ^[..] $T) { arr.data = null; } -#match (package builtin).delete macro (x: ^[..] $T) { - (package core.array).free(x); +#overload +builtin.delete :: macro (x: ^[..] $T) { + core.array.free(x); } copy :: #match #locked { @@ -330,37 +335,39 @@ reverse :: (arr: [] $T) { // Simple insertion sort // cmp should return >0 if left > right // -sort :: #match #locked { - (arr: [] $T, cmp: (T, T) -> i32) { - for i: 1 .. arr.count { - x := arr.data[i]; - j := i - 1; - - @ShortCircuitLogicalOps // This is written this way because '&&' does not short circuit right now. - while j >= 0 { - if cmp(arr.data[j], x) > 0 { - arr.data[j + 1] = arr.data[j]; - j -= 1; - } else { - break; - } - } +sort :: #match #local {} - arr.data[j + 1] = x; +#overload +sort :: (arr: [] $T, cmp: (T, T) -> i32) { + for i: 1 .. arr.count { + x := arr.data[i]; + j := i - 1; + + @ShortCircuitLogicalOps // This is written this way because '&&' does not short circuit right now. + while j >= 0 { + if cmp(arr.data[j], x) > 0 { + arr.data[j + 1] = arr.data[j]; + j -= 1; + } else { + break; + } } - }, - (arr: [] $T, cmp: (^T, ^T) -> i32) { - for i: 1 .. arr.count { - j := i; - - while j > 0 { - if cmp(^arr.data[j - 1], ^arr.data[j]) > 0 { - arr.data[j], arr.data[j - 1] = arr.data[j - 1], arr.data[j]; - j -= 1; - } else { - break; - } + arr.data[j + 1] = x; + } +} + +#overload +sort :: (arr: [] $T, cmp: (^T, ^T) -> i32) { + for i: 1 .. arr.count { + j := i; + + while j > 0 { + if cmp(^arr.data[j - 1], ^arr.data[j]) > 0 { + arr.data[j], arr.data[j - 1] = arr.data[j - 1], arr.data[j]; + j -= 1; + } else { + break; } } } @@ -381,34 +388,36 @@ quicksort :: #match #locked { quicksort_impl(arr, cmp, pivot + 1, hi); } - quicksort_partition :: #match #locked { - (arr: [] $T, cmp: (T, T) -> i32, lo, hi: i32) -> i32 { - pivot := arr[hi]; - i := lo - 1; + quicksort_partition :: #match #local {} + + #overload + quicksort_partition :: (arr: [] $T, cmp: (T, T) -> i32, lo, hi: i32) -> i32 { + pivot := arr[hi]; + i := lo - 1; - for j: lo .. hi+1 { - if cmp(arr[j], pivot) <= 0 { - i += 1; - arr[i], arr[j] = arr[j], arr[i]; - } + for j: lo .. hi+1 { + if cmp(arr[j], pivot) <= 0 { + i += 1; + arr[i], arr[j] = arr[j], arr[i]; } + } - return i; - }, + return i; + } - (arr: [] $T, cmp: (^T, ^T) -> i32, lo, hi: i32) -> i32 { - pivot := ^arr[hi]; - i := lo - 1; + #overload + quicksort_partition :: (arr: [] $T, cmp: (^T, ^T) -> i32, lo, hi: i32) -> i32 { + pivot := ^arr[hi]; + i := lo - 1; - for j: lo .. hi+1 { - if cmp(^arr[j], pivot) <= 0 { - i += 1; - arr[i], arr[j] = arr[j], arr[i]; - } + for j: lo .. hi+1 { + if cmp(^arr[j], pivot) <= 0 { + i += 1; + arr[i], arr[j] = arr[j], arr[i]; } - - return i; } + + return i; } } diff --git a/core/container/iter.onyx b/core/container/iter.onyx index 3dc54efa..829db3b6 100644 --- a/core/container/iter.onyx +++ b/core/container/iter.onyx @@ -37,8 +37,9 @@ close :: (it: Iterator($T)) { it.close(it.data); } -filter :: #match {} -#match filter (it: Iterator($T), predicate: (T) -> bool, allocator := iter_allocator) -> Iterator(T) { +filter :: #match #local {} +#overload +filter :: (it: Iterator($T), predicate: (T) -> bool, allocator := iter_allocator) -> Iterator(T) { FilterIterator :: struct (T: type_expr) { iterator: Iterator(T); predicate: (T) -> bool; @@ -76,7 +77,8 @@ filter :: #match {} }; } -#match filter (it: Iterator($T), ctx: $Ctx, predicate: (T, Ctx) -> bool, allocator := iter_allocator) -> Iterator(T) { +#overload +filter :: (it: Iterator($T), ctx: $Ctx, predicate: (T, Ctx) -> bool, allocator := iter_allocator) -> Iterator(T) { FilterIterator :: struct (T: type_expr, Ctx: type_expr) { iterator: Iterator(T); predicate: (T, Ctx) -> bool; @@ -116,8 +118,10 @@ filter :: #match {} }; } -map :: #match {} -#match map (it: Iterator($T), transform: (T) -> $R, allocator := iter_allocator) -> Iterator(R) { +map :: #match #local {} + +#overload +map :: (it: Iterator($T), transform: (T) -> $R, allocator := iter_allocator) -> Iterator(R) { MapIterator :: struct (T: type_expr, R: type_expr) { iterator: Iterator(T); transform: (T) -> R; @@ -148,7 +152,8 @@ map :: #match {} }; } -#match map (it: Iterator($T), ctx: $Ctx, transform: (T, Ctx) -> $R, allocator := iter_allocator) -> Iterator(R) { +#overload +map :: (it: Iterator($T), ctx: $Ctx, transform: (T, Ctx) -> $R, allocator := iter_allocator) -> Iterator(R) { MapIterator :: struct (T: type_expr, R: type_expr, Ctx: type_expr) { iterator: Iterator(T); transform: (T, Ctx) -> R; @@ -405,14 +410,17 @@ const :: (value: $T) -> Iterator(T) { value: T; } -enumerate :: #match {} -#match enumerate macro (it: $T, start_index: i32 = 0) -> #auto where Iterable(T) { +enumerate :: #match #local {} + +#overload +enumerate :: macro (it: $T, start_index: i32 = 0) -> #auto where Iterable(T) { as_iterator :: as_iterator enumerate :: enumerate return enumerate(as_iterator(it), start_index); } -#match enumerate (it: Iterator($T), start_index: i32 = 0) -> Iterator(Enumeration_Value(T)) { +#overload +enumerate :: (it: Iterator($T), start_index: i32 = 0) -> Iterator(Enumeration_Value(T)) { Enumeration_Context :: struct (T: type_expr) { iterator: Iterator(T); current_index: i32; @@ -443,7 +451,8 @@ enumerate :: #match {} }; } -#match as_iterator from_array +#overload +as_iterator :: from_array from_array :: (arr: [] $T) -> Iterator(^T) { Context :: struct (T: type_expr) { data: ^T; @@ -472,7 +481,8 @@ from_array :: (arr: [] $T) -> Iterator(^T) { }; } -#match as_iterator (x: ^[..] $T) -> Iterator(T) { +#overload +as_iterator :: (x: ^[..] $T) -> Iterator(T) { Context :: struct (T: type_expr) { arr: ^[..] T; current: u32; @@ -508,7 +518,8 @@ from_array :: (arr: [] $T) -> Iterator(^T) { }; } -#match as_iterator (x: ^[..] $T, by_pointer: bool) -> Iterator(^T) { +#overload +as_iterator :: (x: ^[..] $T, by_pointer: bool) -> Iterator(^T) { Context :: struct (T: type_expr) { arr: ^[..] T; current: u32; @@ -544,7 +555,8 @@ from_array :: (arr: [] $T) -> Iterator(^T) { }; } -#match as_iterator (r: range) -> Iterator(i32) { +#overload +as_iterator :: (r: range) -> Iterator(i32) { Context :: struct { r: range; v: i32; @@ -579,14 +591,17 @@ from_array :: (arr: [] $T) -> Iterator(^T) { }; } -fold :: #match {} +fold :: #match #local {} + @Cleanup // some way to shorten this would be nice -#match fold macro (it: $T, init: $R, combine: $S) -> #auto where Iterable(T) { +#overload +fold :: macro (it: $T, init: $R, combine: $S) -> #auto where Iterable(T) { fold :: fold return fold(as_iterator(it), init, combine); } -#match fold (it: Iterator($T), initial_value: $R, combine: (T, R) -> R) -> R { +#overload +fold :: (it: Iterator($T), initial_value: $R, combine: (T, R) -> R) -> R { for value: it { initial_value = combine(value, initial_value); } @@ -594,38 +609,47 @@ fold :: #match {} return initial_value; } -count :: #match {} -#match count macro (it: $T, cond: $F) -> #auto where Iterable(T) { +count :: #match #local {} + +#overload +count :: macro (it: $T, cond: $F) -> #auto where Iterable(T) { count :: count return count(as_iterator(it), cond); } -#match count (it: Iterator($T), cond: (T) -> bool) -> i32 { +#overload +count :: (it: Iterator($T), cond: (T) -> bool) -> i32 { c := 0; for value: it do if cond(value) do c += 1; return c; } -some :: #match {} -#match some macro (it: $T, cond: $F) -> #auto where Iterable(T) { +some :: #match #local {} + +#overload +some :: macro (it: $T, cond: $F) -> #auto where Iterable(T) { some :: some as_iterator :: as_iterator return some(as_iterator(it), cond); } -#match some (it: Iterator($T), cond: (T) -> bool) -> bool { +#overload +some :: (it: Iterator($T), cond: (T) -> bool) -> bool { for value: it do if cond(value) do return true; return false; } -every :: #match {} -#match every macro (it: $T, cond: $F) -> #auto where Iterable(T) { +every :: #match #local {} + +#overload +every :: macro (it: $T, cond: $F) -> #auto where Iterable(T) { every :: every as_iterator :: as_iterator return every(as_iterator(it), cond); } -#match every (it: Iterator($T), cond: (T) -> bool) -> bool { +#overload +every :: (it: Iterator($T), cond: (T) -> bool) -> bool { for value: it do if !cond(value) do return false; return true; } @@ -640,17 +664,20 @@ to_array :: (it: Iterator($T), allocator := context.allocator) -> [..] T { } -#local runtime :: package runtime #if runtime.Multi_Threading_Enabled { - #local sync :: package core.sync - distributor :: #match {} - #match distributor macro (it: $T) -> #auto where Iterable(T) { + #local sync :: core.sync + + distributor :: #match #local {} + + #overload + distributor :: macro (it: $T) -> #auto where Iterable(T) { distributor :: distributor; as_iterator :: as_iterator; return distributor(as_iterator(it)); } - #match distributor (it: Iterator) -> Iterator(it.Iter_Type) { + #overload + distributor :: (it: Iterator) -> Iterator(it.Iter_Type) { Context :: struct (T: type_expr) { mutex: sync.Mutex; iterator: Iterator(T); @@ -686,15 +713,18 @@ to_array :: (it: Iterator($T), allocator := context.allocator) -> [..] T { return .{c, #solidify next {T=it.Iter_Type}, #solidify close {T=it.Iter_Type}}; } - parallel_for :: #match {} - #match parallel_for macro (iterable: $I, thread_count: u32, thread_data: ^$Ctx, body: Code) where Iterable(I) { + parallel_for :: #match #local {} + + #overload + parallel_for :: macro (iterable: $I, thread_count: u32, thread_data: ^$Ctx, body: Code) where Iterable(I) { parallel_for :: parallel_for; as_iterator :: as_iterator; parallel_for(as_iterator(iterable), thread_count, thread_data, body); } - #match parallel_for macro (iter: Iterator($T), thread_count: u32, thread_data: ^$Ctx, body: Code) { + #overload + parallel_for :: macro (iter: Iterator($T), thread_count: u32, thread_data: ^$Ctx, body: Code) { thread :: package core.thread; alloc :: package core.alloc; distributor :: distributor; diff --git a/core/math.onyx b/core/math.onyx index 592f86bb..a0e46fd7 100644 --- a/core/math.onyx +++ b/core/math.onyx @@ -246,12 +246,12 @@ log :: (a: $T, base: $R) -> T { // 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 { wasm.max_f32, wasm.max_f64, max_poly } +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 { wasm.min_f32, wasm.min_f64, min_poly } +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; } @@ -262,12 +262,12 @@ clamp :: (v: $T, lo: T, hi: T) -> T { return v; } -sqrt :: #match { wasm.sqrt_f32, wasm.sqrt_f64, sqrt_poly } +sqrt :: #match #local { wasm.sqrt_f32, wasm.sqrt_f64, sqrt_poly } sqrt_poly :: (x: $T) -> T { return ~~ wasm.sqrt_f64(~~ x); } -abs :: #match { wasm.abs_f32, wasm.abs_f64, abs_poly } +abs :: #match #local { wasm.abs_f32, wasm.abs_f64, abs_poly } abs_poly :: (x: $T) -> T { return x if x >= 0 else -x; } @@ -278,7 +278,7 @@ sign :: (x: $T) -> T { return 0; } -copysign :: #match { wasm.copysign_f32, wasm.copysign_f64, copysign_poly } +copysign :: #match #local { wasm.copysign_f32, wasm.copysign_f64, copysign_poly } copysign_poly :: (x: $T, y: T) -> T { return abs(x) * sign(y); } @@ -290,10 +290,10 @@ copysign_poly :: (x: $T, y: T) -> T { // Floating point rounding // -ceil :: #match { wasm.ceil_f32, wasm.ceil_f64 } -floor :: #match { wasm.floor_f32, wasm.floor_f64 } -trunc :: #match { wasm.trunc_f32, wasm.trunc_f64 } -nearest :: #match { wasm.nearest_f32, wasm.nearest_f64 } +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 } @@ -301,11 +301,11 @@ nearest :: #match { wasm.nearest_f32, wasm.nearest_f64 } // Integer operations // -clz :: #match { wasm.clz_i32, wasm.clz_i64 } -ctz :: #match { wasm.ctz_i32, wasm.ctz_i64 } -popcnt :: #match { wasm.popcnt_i32, wasm.popcnt_i64 } -rotate_left :: #match { wasm.rotl_i32, wasm.rotl_i64 } -rotate_right :: #match { wasm.rotr_i32, wasm.rotr_i64 } +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 } diff --git a/core/memory.onyx b/core/memory.onyx index 5ea845b1..3c04d26a 100644 --- a/core/memory.onyx +++ b/core/memory.onyx @@ -1,21 +1,22 @@ package core.memory -use package core.intrinsics.wasm { memory_copy, memory_fill } - -copy :: (dest: rawptr, src: rawptr, count: i32) do memory_copy(dest, src, count); -set :: (dest: rawptr, byte: u8, count: i32) do memory_fill(dest, byte, count); +align :: #match { + (size: ^u64, align: u64) { + if *size % align != 0 { + *size += align - (*size % align); + } + }, -// Old and slow copy and set -copy_ :: (dst_: rawptr, src_: rawptr, len: u32) { - dst := cast(^u8) dst_; - src := cast(^u8) src_; - for i: 0 .. len do dst[i] = src[i]; + (size: u64, align: u64) -> u64 { + if size % align != 0 { + size += align - (size % align); + } + return size; + } } -set_ :: (start: rawptr, value: u8, length: u32) { - s := cast(^u8) start; - for i: 0 .. length do s[i] = value; -} +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); @@ -29,10 +30,6 @@ make_slice :: ($T: type_expr, count: i32, allocator := context.allocator) -> [] }; } -#match __make_overload macro (_: ^[] $T, count: u32, allocator := context.allocator) -> [] T { - return (package core.memory).make_slice(T, count, allocator); -} - free_slice :: (sl: ^[] $T, allocator := context.allocator) { if sl.data == null do return; @@ -41,10 +38,6 @@ free_slice :: (sl: ^[] $T, allocator := context.allocator) { sl.count = 0; } -#match (package builtin).delete macro (x: ^[] $T) { - (package core.memory).free_slice(x); -} - copy_slice :: (sl: [] $T, allocator := context.allocator) -> [] T { data := raw_alloc(allocator, sl.count * sizeof T); copy(data, sl.data, sl.count * sizeof T); @@ -62,17 +55,13 @@ resize_slice :: (sl: [] $T, new_size: i32, allocator := context.allocator) -> [] return new_slice; } -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; - } +#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/std.onyx b/core/std.onyx index 99a01875..dbab9d7e 100644 --- a/core/std.onyx +++ b/core/std.onyx @@ -46,7 +46,9 @@ package core #if runtime.runtime == .Onyx { #load "./runtime/onyx_run" + #load "./os/process" + #load "./time/time" #load "./net/net" #load "./net/tcp" diff --git a/core/stdio.onyx b/core/stdio.onyx index 20e2113f..4fa33c86 100644 --- a/core/stdio.onyx +++ b/core/stdio.onyx @@ -7,11 +7,12 @@ package core // in anyway. -#local runtime :: package runtime #if runtime.runtime == .Custom { #error "'stdio' can only be included in the 'wasi' or 'js' runtime." } +stdin: io.Stream; + auto_flush_stdio := true print :: #match #locked { @@ -42,15 +43,15 @@ printf :: (format: str, va: ..any) { } // Print to standard error, if available. -#if #defined((package runtime).__output_error) { +#if #defined(runtime.__output_error) { eprintf :: (format: str, va: ..any) -> str { flush :: (_, to_output) => { - (package runtime).__output_error(to_output); + runtime.__output_error(to_output); return true; } buffer: [1024] u8; - (package runtime).__output_error(conv.format_va(buffer, format, va, .{null, flush})); + runtime.__output_error(conv.format_va(buffer, format, va, .{null, flush})); } } @@ -157,5 +158,3 @@ __flush_stdio :: () { return .None, buf[0]; } } - -stdin: io.Stream; diff --git a/core/string.onyx b/core/string.onyx index ef876113..b4134f09 100644 --- a/core/string.onyx +++ b/core/string.onyx @@ -2,6 +2,9 @@ package core.string use package 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; @@ -24,131 +27,117 @@ copy :: (orig: str, dest: str) { memory.copy(dest.data, orig.data, len); } +#match as_str from_cstr from_cstr :: (s: cstr) -> str { return .{ data = s, count = length(s) }; } -free :: (s: str, allocator := context.allocator) do raw_free(allocator, s.data); - -length :: #match { - (s: str) -> u32 { - return s.count; - }, - - (s: cstr) -> u32 { - len := 0; - c := s; - while *c != #char "\0" { - len += 1; - c += 1; - } - - return len; - }, -} - -concat :: #match { - (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); +length :: #match #local {} - return str.{ data, len1 + len2 }; - }, +#overload +length :: (s: str) => s.count; - @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. - (allocator: Allocator, strings: ..str) -> str { - total_length := 0; - for s: strings do total_length += s.count; +#overload +length :: (s: cstr) -> u32 { + len := 0; + c := s; + while *c != #char "\0" { + len += 1; + c += 1; + } - 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 len; +} - return str.{ data, total_length }; - }, - (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; +concat :: #match #local {} - 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; - } +#overload +concat :: (s1: str, s2: str, allocator := context.allocator) -> str { + len1 := length(s1); + len2 := length(s2); - return buffer[0 .. total_copied]; - }, + data := cast(^u8) raw_alloc(allocator, len1 + len2); + memory.copy(data, s1.data, len1); + memory.copy(data + len1, s2.data, len2); - (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 }; - } + 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; + } -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)); + return str.{ data, total_length }; +} - curr_str := 0; - begin := 0; +#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; - for i: 0 .. s.count { - if s[i] == delim { - strarr[curr_str] = s.data[begin .. i]; - begin = i + 1; - curr_str += 1; - } + 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; } - strarr[curr_str] = s.data[begin .. s.count]; + return buffer[0 .. total_copied]; +} - return strarr[0 .. delim_count + 1]; +#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 { - (s: str, c: u8) -> bool { - for ch: s do if ch == c do return true; - return false; - }, - (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; - } +contains :: #match #local {} + +#overload +contains :: (s: str, c: u8) -> bool { + for ch: s do if ch == c do return true; + return false; +} - j += 1; +#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; } - return true; + j += 1; } - return false; - }, + 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 @@ -209,46 +198,61 @@ last_index_of :: (s: str, c: u8) -> i32 { return -1; } -strip_whitespace :: #match { - (s: ^str) { strip_leading_whitespace(s); strip_trailing_whitespace(s); }, - (s: str) => s |> strip_leading_whitespace() |> strip_trailing_whitespace() + +strip_whitespace :: #match #local {} + +#overload +strip_whitespace :: (s: ^str) { + strip_leading_whitespace(s); + strip_trailing_whitespace(s); } -strip_leading_whitespace :: #match { - (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; - } +#overload +strip_whitespace :: (s: str) => + s |> strip_leading_whitespace() + |> strip_trailing_whitespace() + + +strip_leading_whitespace :: #match #local {} - case #default do return; +#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; } - }, - - (s: str) -> str { - out := s; - strip_leading_whitespace(^out); - return out; - }, -} - -strip_trailing_whitespace :: #match { - (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; + 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; } - }, - (s: str) -> str { - out := s; - strip_trailing_whitespace(^out); - return out; - }, + case #default do return; + } +} + +#overload +strip_trailing_whitespace :: (s: str) -> str { + out := s; + strip_trailing_whitespace(^out); + return out; } to_uppercase :: (s: str) -> str { @@ -271,52 +275,61 @@ to_lowercase :: (s: str) -> str { return s; } -trim_start :: #match { - (s: ^str, char: u8) { - while s.data[0] == char { - s.data += 1; - s.count -= 1; - } - }, - (s: str, char: u8) -> str { - out := s; - trim_start(^out, char); - return out; +trim_start :: #match #local {} + +#overload +trim_start :: (s: ^str, char: u8) { + while s.data[0] == char { + s.data += 1; + s.count -= 1; } } -trim_end :: #match { - (s: ^str, char: u8) { - while s.data[s.count - 1] == char { - s.count -= 1; - } - }, +#overload +trim_start :: (s: str, char: u8) -> str { + out := s; + trim_start(^out, char); + return out; +} + - (s: str, char: u8) -> str { - out := s; - trim_end(^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; } } -advance :: #match { - (s: ^str, chars := 1) { - chars = math.min(chars, s.count); +#overload +trim_end :: (s: str, char: u8) -> str { + out := s; + trim_end(^out, char); + return out; +} + - s.data += chars; - s.count -= chars; - }, +advance :: #match #local {} - (s: str, chars := 1) -> str { - chars = math.min(chars, s.count); - out := s; +#overload +advance :: (s: ^str, chars := 1) { + chars = math.min(chars, s.count); - out.data += chars; - out.count -= chars; + s.data += chars; + s.count -= chars; +} - return out; - } +#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) { @@ -325,70 +338,72 @@ replace :: (s: str, to_replace: u8, replace_with: u8) { } } -read_until :: #match #locked { - (s: ^str, upto: u8, skip := 0) -> str { - if s.count == 0 do return ""; +read_until :: #match #local {} - out : str; - out.data = s.data; - out.count = 0; +#overload +read_until :: (s: ^str, upto: u8, skip := 0) -> str { + if s.count == 0 do return ""; - rem := skip; - for ch: *s { - if ch == upto { - if rem <= 0 do break; - else do rem -= 1; - } + out : str; + out.data = s.data; + out.count = 0; - out.count += 1; + rem := skip; + for ch: *s { + if ch == upto { + if rem <= 0 do break; + else do rem -= 1; } - s.data += out.count; - s.count -= out.count; + out.count += 1; + } - return out; - }, + s.data += out.count; + s.count -= out.count; - (s: ^str, upto: str, skip := 0) -> str { - if s.count == 0 do return ""; + return out; +} - out := str.{ data = s.data }; +#overload +read_until :: (s: ^str, upto: str, skip := 0) -> str { + if s.count == 0 do return ""; - rem := skip; - i := 0; - while i <= s.count - upto.count { - match := true; - j := i; - for upto { - if s.data[j] != it { - match = false; - break; - } + out := str.{ data = s.data }; - j += 1; + rem := skip; + i := 0; + while i <= s.count - upto.count { + match := true; + j := i; + for upto { + if s.data[j] != it { + match = false; + break; } - if match { - if rem <= 0 do break; - else do rem -= 1; - } + j += 1; + } - i += 1; + if match { + if rem <= 0 do break; + else do rem -= 1; } - if i > s.count - upto.count { - out = *s; - s.data += out.count; - s.count = 0; + i += 1; + } - } else { - out.count = i - 1; - s.data += out.count + (upto.count - 1); - s.count -= out.count + (upto.count - 1); - } + if i > s.count - upto.count { + out = *s; + s.data += out.count; + s.count = 0; - return out; + } else { + out.count = i - 1; + s.data += out.count + (upto.count - 1); + s.count -= out.count + (upto.count - 1); } + + return out; } read_until_any :: (s: ^str, skip: u32, uptos: ..u8) -> str { @@ -426,6 +441,24 @@ advance_line :: (s: ^str) { s.count -= adv + 1; } -as_str :: #match { - from_cstr +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]; } diff --git a/core/time/time.onyx b/core/time/time.onyx new file mode 100644 index 00000000..6021e272 --- /dev/null +++ b/core/time/time.onyx @@ -0,0 +1,57 @@ +package core.time + +// +// This module provides a thin wrapper for the builtin POSIX +// time functionality of: +// - localtime, gmtime +// - strptime, strftime +// + +#if runtime.runtime != .Onyx { + #error "'core.time' should only be used with the Onyx runtime."; +} + +// +// This structure has to match the 'struct tm' +// defined in time.h of POSIX. +Timestamp :: struct #size (sizeof u32 * 12) { + sec: u32; + min: u32; + hour: u32; + mday: u32; + mon: u32; + year: u32; + wday: u32; + yday: u32; + isdst: u32; +} + +localtime :: __time_localtime +gmtime :: __time_gmtime + +strftime :: (buf: [] u8, format: [] u8, tm: ^Timestamp) -> str { + f := cast(cstr) core.alloc.from_stack(format.length + 1); + core.memory.copy(f, format.data, format.length); + f[format.length] = 0; + + len := __time_strftime(buf, f, tm); + return buf[0..len]; +} + +strptime :: (buf: [] u8, format: [] u8, tm: ^Timestamp) -> bool { + f := cast(cstr) core.alloc.from_stack(format.length + 1); + core.memory.copy(f, format.data, format.length); + f[format.length] = 0; + + return __time_strptime(buf, f, tm); +} + +#local { + #foreign "onyx_runtime" { + __time_localtime :: (time: u64, tm: ^Timestamp) -> void --- + __time_gmtime :: (time: u64, tm: ^Timestamp) -> void --- + __time_strftime :: (buf: [] u8, format: cstr, tm: ^Timestamp) -> u32 --- + __time_strptime :: (buf: [] u8, format: cstr, tm: ^Timestamp) -> bool --- + } +} + diff --git a/include/astnodes.h b/include/astnodes.h index 51acba0a..3f206957 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -1064,6 +1064,7 @@ struct AstOverloadedFunction { bh_imap all_overloads; b32 locked : 1; + b32 only_local_functions : 1; }; // @CLEANUP: Is this really necessary? diff --git a/include/lex.h b/include/lex.h index f8d5d1c5..f1395b72 100644 --- a/include/lex.h +++ b/include/lex.h @@ -118,5 +118,6 @@ void onyx_lex_tokens(OnyxTokenizer* tokenizer); b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2); b32 token_text_equals(OnyxToken* tkn, char* text); +b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2); #endif diff --git a/src/astnodes.c b/src/astnodes.c index b0c4194a..71d36652 100644 --- a/src/astnodes.c +++ b/src/astnodes.c @@ -796,7 +796,7 @@ TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) { else if (node->kind == Ast_Kind_Zero_Value) { if (node_type == NULL) { node->type = type; - return TYPE_MATCH_SUCCESS; + return TYPE_MATCH_SUCCESS; // Shouldn't this be on the next line? And have node_type == node->type checked? } } diff --git a/src/lex.c b/src/lex.c index 1f0dc6a3..c34cc951 100644 --- a/src/lex.c +++ b/src/lex.c @@ -539,3 +539,12 @@ b32 token_text_equals(OnyxToken* tkn, char* text) { return 1; } + +b32 token_same_file(OnyxToken *tkn1, OnyxToken *tkn2) { + if (!tkn1 || !tkn2) return 0; + + if (tkn1->pos.filename == tkn2->pos.filename) return 1; + + // :Security? + return strcmp(tkn1->pos.filename, tkn2->pos.filename) == 0; +} diff --git a/src/onyx_runtime.c b/src/onyx_runtime.c index 10f3a82a..7827e1d3 100644 --- a/src/onyx_runtime.c +++ b/src/onyx_runtime.c @@ -914,6 +914,36 @@ ONYX_DEF(__time, (), (WASM_I64)) { + +// +// Dates and Times +// +ONYX_DEF(__time_localtime, (WASM_I64, WASM_I32), ()) { + u64 t = params->data[0].of.i64; + localtime_r(&t, ONYX_PTR(params->data[1].of.i32)); + return NULL; +} + +ONYX_DEF(__time_gmtime, (WASM_I64, WASM_I32), ()) { + u64 t = params->data[0].of.i64; + gmtime_r(&t, ONYX_PTR(params->data[1].of.i32)); + return NULL; +} + +ONYX_DEF(__time_strftime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) { + u32 len = strftime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32)); + results->data[0] = WASM_I32_VAL(len); + return NULL; +} + +ONYX_DEF(__time_strptime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) { + char *rem = strptime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32)); + results->data[0] = WASM_I32_VAL(rem != NULL); + return NULL; +} + + + // // Networking // @@ -1336,6 +1366,11 @@ ONYX_LIBRARY { ONYX_FUNC(__sleep) ONYX_FUNC(__time) + ONYX_FUNC(__time_localtime) + ONYX_FUNC(__time_gmtime) + ONYX_FUNC(__time_strftime) + ONYX_FUNC(__time_strptime) + ONYX_FUNC(__net_create_socket) ONYX_FUNC(__net_close_socket) ONYX_FUNC(__net_setting) diff --git a/src/parser.c b/src/parser.c index e7a08a2a..fae5d2b8 100644 --- a/src/parser.c +++ b/src/parser.c @@ -2342,12 +2342,23 @@ static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, Onyx locked = 1; } + b32 local = 0; + if (parse_possible_directive(parser, "local")) { + local = 1; + } + + // This could be checked elsewhere? + if (locked && local) { + onyx_report_error(token->pos, Error_Critical, "Only one of '#locked' and '#local' can because use at a time."); + } + expect_token(parser, '{'); AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function); ofunc->token = token; ofunc->flags |= Ast_Flag_Comptime; ofunc->locked = locked; + ofunc->only_local_functions = local; bh_arr_new(global_heap_allocator, ofunc->overloads, 4); @@ -3175,14 +3186,10 @@ static void parse_top_level_statement(OnyxParser* parser) { ENTITY_SUBMIT(operator); return; } - else if (parse_possible_directive(parser, "match")) { + else if (parse_possible_directive(parser, "match") || parse_possible_directive(parser, "overload")) { AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload); add_overload->token = dir_token; - parser->parse_calls = 0; - add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0); - parser->parse_calls = 1; - if (parse_possible_directive(parser, "precedence")) { AstNumLit* pre = parse_int_literal(parser); if (parser->hit_unexpected_token) return; @@ -3192,6 +3199,21 @@ static void parse_top_level_statement(OnyxParser* parser) { add_overload->precedence = 0; } + parser->parse_calls = 0; + add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0); + parser->parse_calls = 1; + + // Allow for + // #match + // something :: (....) { + // } + // + // This will make converting something to a overloaded + // function easier and require less copying by the programmer. + if (next_tokens_are(parser, 2, ':', ':')) { + consume_tokens(parser, 2); + } + add_overload->overload = parse_expression(parser, 0); ENTITY_SUBMIT(add_overload); diff --git a/src/symres.c b/src/symres.c index f006c764..50b87b61 100644 --- a/src/symres.c +++ b/src/symres.c @@ -1374,6 +1374,13 @@ static SymresStatus symres_process_directive(AstNode* directive) { return Symres_Error; } + if (ofunc->only_local_functions) { + if (!token_same_file(add_overload->token, ofunc->token)) { + onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as this option is not within the same file as the original #match declared with #local."); + onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match."); + } + } + SYMRES(expression, (AstTyped **) &add_overload->overload); add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload); break; diff --git a/tests/bugs/defer_block_in_macro.onyx b/tests/bugs/defer_block_in_macro.onyx index 10735324..091f6e31 100644 --- a/tests/bugs/defer_block_in_macro.onyx +++ b/tests/bugs/defer_block_in_macro.onyx @@ -8,7 +8,7 @@ test_macro :: macro () { } } -time :: macro (name: str) { +time_block :: macro (name: str) { start := 10; defer { end := 1010; @@ -18,5 +18,5 @@ time :: macro (name: str) { main :: (args) => { test_macro(); - time("main"); + time_block("main"); } diff --git a/tests/overload_precedence.onyx b/tests/overload_precedence.onyx index b5dad387..b4b2ecc0 100644 --- a/tests/overload_precedence.onyx +++ b/tests/overload_precedence.onyx @@ -15,6 +15,6 @@ main :: (args: [] cstr) { overloaded(z=10); } -#match overloaded #precedence 3 (q: i32) { println("Option Q"); } -#match overloaded #precedence 2 (r: i32) { println("Option R"); } -#match overloaded #precedence 1 (m: i32) { println("Option M"); } +#match #precedence 3 overloaded (q: i32) { println("Option Q"); } +#match #precedence 2 overloaded (r: i32) { println("Option R"); } +#match #precedence 1 overloaded (m: i32) { println("Option M"); }