From: Brendan Hansen Date: Sat, 9 Jan 2021 20:49:25 +0000 (-0600) Subject: started working on abstract stream api X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=1eff1ddf819a32ca06747d1f7b505556b21ca768;p=onyx.git started working on abstract stream api --- diff --git a/bin/onyx b/bin/onyx index fa0aaae9..c3d9cd57 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/bin/onyx-js b/bin/onyx-js index 933c0661..3889ed9a 100755 --- a/bin/onyx-js +++ b/bin/onyx-js @@ -11,6 +11,10 @@ const ENV = { const data = new Uint8Array(wasm_instance.exports.memory.buffer, ptr, len); const str = new TextDecoder().decode(data); process.stdout.write(str); + }, + + exit(status) { + process.exit(status); } } } diff --git a/core/builtin.onyx b/core/builtin.onyx index 643ecede..1ba52ae3 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -31,10 +31,16 @@ null :: cast(rawptr) 0; OnyxContext :: struct { allocator : Allocator; temp_allocator : Allocator; + + assert_handler : proc (msg: str, file: str); } context : OnyxContext; +assert :: proc (cond: bool, msg: str, file: str = str.{ null, 0 }) { + if !cond do context.assert_handler(msg, file); +} + // // Basic allocation structures. @@ -49,7 +55,7 @@ AllocationAction :: enum { Resize; } -allocator_proc :: #type proc (rawptr, AllocationAction, u32, u32, rawptr) -> rawptr; +allocator_proc :: #type proc (data: rawptr, action: AllocationAction, size: u32, align: u32, old_ptr: rawptr) -> rawptr; Allocator :: struct { data: rawptr; diff --git a/core/io/io.onyx b/core/io/io.onyx new file mode 100644 index 00000000..718e5f52 --- /dev/null +++ b/core/io/io.onyx @@ -0,0 +1,17 @@ +package core.io + +Error :: enum { + None :: 0x00; + + // The procedure is not implemented for this kind of stream. + NotImplemented :: 0x01; + + // The stream reached the end. + EOF :: 0x02; + + // The vtable was not set for this stream. + NoVtable :: 0x03; + + // A seek was outside the bounds of the stream. + OutOfBounds :: 0x04; +} \ No newline at end of file diff --git a/core/io/reader.onyx b/core/io/reader.onyx new file mode 100644 index 00000000..336c782a --- /dev/null +++ b/core/io/reader.onyx @@ -0,0 +1,130 @@ +package core.io + +Reader :: struct { + stream : ^Stream; +} + +reader_make :: proc (s: ^Stream) -> Reader { + assert(s.vtable != null, "Stream vtable was not setup correctly."); + + return Reader.{ s }; +} + +read_u32 :: proc (use reader: ^Reader) -> u32 { + n: u32 = 0; + + skip_whitespace(reader); + + curr := cast(u8) 0; + stream_peek_byte(stream, ^curr); + while curr >= #char "0" && curr <= #char "9" { + stream_read_byte(stream, ^curr); + + n *= 10; + n += cast(u32) (curr - #char "0"); + + if stream_peek_byte(stream, ^curr) == Error.EOF do break; + } + + return n; +} + +read_u64 :: proc (use reader: ^Reader) -> u64 { + n: u64 = 0; + + skip_whitespace(reader); + + curr := cast(u8) 0; + stream_peek_byte(stream, ^curr); + while curr >= #char "0" && curr <= #char "9" { + stream_read_byte(stream, ^curr); + + n *= 10; + n += cast(u64) (curr - #char "0"); + + if stream_peek_byte(stream, ^curr) == Error.EOF do break; + } + + return n; +} + +read_line :: proc (use reader: ^Reader, allocator := context.allocator) -> str { + curr_pos : i32; + stream_tell(stream, ^curr_pos); + + count := 0; + curr := cast(u8) 0; + stream_read_byte(stream, ^curr); + while curr != #char "\n" { + count += 1; + if stream_read_byte(stream, ^curr) != Error.None do break; + } + + stream_seek(stream, curr_pos, SeekFrom.Start); + + out := str.{ + data = raw_alloc(allocator, count * sizeof(u8)), + count = count, + }; + + stream_read(stream, out); + return out; +} + +read_word :: proc (use reader: ^Reader, allocator := context.allocator) -> str { + skip_whitespace(reader); + + curr_pos : i32; + stream_tell(stream, ^curr_pos); + + count := 0; + curr := cast(u8) 0; + stream_read_byte(stream, ^curr); + + while true { + if (curr >= #char "a" && curr <= #char "z") + || (curr >= #char "A" && curr <= #char "Z") + || curr == #char "_" { + count += 1; + } else { + break; + } + + if stream_read_byte(stream, ^curr) != Error.None do break; + } + + stream_seek(stream, curr_pos, SeekFrom.Start); + + out := str.{ + data = raw_alloc(allocator, count * sizeof(u8)), + count = count, + }; + + stream_read(stream, out); + return out; +} + +advance_line :: proc (use reader: ^Reader) { + curr := cast(u8) 0; + if stream_read_byte(stream, ^curr) != Error.None do return; + while curr != #char "\n" { + if stream_read_byte(stream, ^curr) != Error.None do return; + } + + stream_read_byte(stream, ^curr); +} + +skip_whitespace :: proc (use reader: ^Reader) { + while true { + byte := cast(u8) 0; + if stream_peek_byte(stream, ^byte) == Error.EOF do break; + + switch byte { + case #char " ", #char "\t", #char "\n", #char "\r" { + stream_read_byte(stream, ^byte); + } + + case #default do return; + } + } +} \ No newline at end of file diff --git a/core/io/stream.onyx b/core/io/stream.onyx new file mode 100644 index 00000000..bd76c745 --- /dev/null +++ b/core/io/stream.onyx @@ -0,0 +1,269 @@ +package core.io + +use package core + +Stream :: struct { + vtable : ^Stream_Vtable; +} + +#private +Stream_Vtable :: struct { + seek : proc (s: ^Stream, to: i32, whence: SeekFrom) -> Error; + tell : proc (s: ^Stream, out: ^i32) -> Error; + + read : proc (s: ^Stream, buffer: [] u8, number_read: ^u32) -> Error; + read_at : proc (s: ^Stream, at: u32, buffer: [] u8, number_read: ^u32) -> Error; + read_byte : proc (s: ^Stream, out: ^u8) -> Error; + unread_byte : proc (s: ^Stream) -> Error; + + write : proc (s: ^Stream, buffer: [] u8, number_written: ^u32) -> Error; + write_at : proc (s: ^Stream, at: u32, buffer: [] u8, number_written: ^u32) -> Error; + write_byte : proc (s: ^Stream, byte: u8) -> Error; + + close : proc (s: ^Stream) -> Error; + flush : proc (s: ^Stream) -> Error; + + size : proc (s: ^Stream) -> i32; +} + +SeekFrom :: enum { + Start :: 0x00; + Current :: 0x01; + End :: 0x02; +} + +stream_seek :: proc (use s: ^Stream, to: i32, whence: SeekFrom) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.seek == null_proc do return Error.NotImplemented; + + return vtable.seek(s, to, whence); +} + +stream_tell :: proc (use s: ^Stream, out: ^i32) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.tell == null_proc do return Error.NotImplemented; + + return vtable.tell(s, out); +} + +stream_read :: proc (use s: ^Stream, buffer: [] u8, number_read: ^u32 = null) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.read == null_proc do return Error.NotImplemented; + + return vtable.read(s, buffer, number_read); +} + +stream_read_at :: proc (use s: ^Stream, at: u32, buffer: [] u8, number_read: ^u32 = null) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.read_at == null_proc do return Error.NotImplemented; + + return vtable.read_at(s, at, buffer, number_read); +} + +stream_read_byte :: proc (use s: ^Stream, out: ^u8) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.read_byte == null_proc do return Error.NotImplemented; + + return vtable.read_byte(s, out); +} + +stream_unread_byte :: proc (use s: ^Stream) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.unread_byte == null_proc do return Error.NotImplemented; + + return vtable.unread_byte(s); +} + +stream_write :: proc (use s: ^Stream, buffer: [] u8, number_written: ^u32 = null) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.write == null_proc do return Error.NotImplemented; + + return vtable.write(s, buffer, number_written); +} + +stream_write_at :: proc (use s: ^Stream, at: u32, buffer: [] u8, number_written: ^u32 = null) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.write_at == null_proc do return Error.NotImplemented; + + return vtable.write_at(s, at, buffer, number_written); +} + +stream_write_byte :: proc (use s: ^Stream, byte: u8) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.write_byte == null_proc do return Error.NotImplemented; + + return vtable.write_byte(s, byte); +} + +stream_close :: proc (use s: ^Stream) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.close == null_proc do return Error.NotImplemented; + + return vtable.close(s); +} + +stream_flush :: proc (use s: ^Stream) -> Error { + if vtable == null do return Error.NoVtable; + if vtable.flush == null_proc do return Error.NotImplemented; + + return vtable.flush(s); +} + +stream_size :: proc (use s: ^Stream) -> i32 { + if vtable == null do return 0; + if vtable.size == null_proc do return 0; + + return vtable.size(s); +} + +stream_peek_byte :: proc (use s: ^Stream, out: ^u8) -> Error { + if err := stream_read_byte(s, out); err != Error.None do return err; + if err := stream_unread_byte(s); err != Error.None do return err; + return Error.None; +} + + + +StringStream :: struct { + use stream : Stream; + + curr_pos : i32; + data : str; +} + +string_stream_make :: proc (s: str) -> StringStream { + return StringStream.{ + stream = Stream.{ + vtable = ^string_stream_vtable, + }, + data = s, + curr_pos = 0, + }; +} + + +#private +string_stream_vtable := Stream_Vtable.{ + seek = proc (s: ^Stream, to: i32, whence: SeekFrom) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if to >= data.count do return Error.OutOfBounds; + + switch whence { + case SeekFrom.Start do curr_pos = to; + case SeekFrom.Current do curr_pos += to; + case SeekFrom.End do curr_pos = data.count - to; // CHECK: Off by one? + } + + return Error.None; + }, + + tell = proc (s: ^Stream, out: ^i32) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if out != null do *out = curr_pos; + return Error.None; + }, + + read = proc (s: ^Stream, buffer: [] u8, number_read: ^u32) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if curr_pos >= data.count do return Error.EOF; + + bytes_to_read := math.min(buffer.count, data.count - curr_pos); + memory.copy(buffer.data, ^data.data[curr_pos], bytes_to_read); + curr_pos += bytes_to_read; + + if number_read != null do *number_read = bytes_to_read; + return Error.None; + }, + + read_at = proc (s: ^Stream, at: u32, buffer: [] u8, number_read: ^u32) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if curr_pos >= data.count do return Error.EOF; + + bytes_to_read := math.min(buffer.count, data.count - at); + memory.copy(buffer.data, ^data.data[at], bytes_to_read); + curr_pos += bytes_to_read; + + if number_read != null do *number_read = bytes_to_read; + return Error.None; + }, + + read_byte = proc (s: ^Stream, out: ^u8) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if curr_pos >= data.count do return Error.EOF; + + if out != null do *out = data[curr_pos]; + + curr_pos += 1; + return Error.None; + }, + + unread_byte = proc (s: ^Stream) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if curr_pos <= 0 do return Error.OutOfBounds; + + curr_pos -= 1; + return Error.None; + }, + + write = proc (s: ^Stream, buffer: [] u8, number_written: ^u32) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if curr_pos >= data.count do return Error.EOF; + + bytes_to_write := math.min(buffer.count, data.count - curr_pos); + memory.copy(^data.data[curr_pos], buffer.data, bytes_to_write); + curr_pos += bytes_to_write; + + if number_written != null do *number_written = bytes_to_write; + return Error.None; + }, + + write_at = proc (s: ^Stream, at: u32, buffer: [] u8, number_written: ^u32) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if curr_pos >= data.count do return Error.EOF; + + bytes_to_write := math.min(buffer.count, data.count - at); + memory.copy(^data.data[at], buffer.data, bytes_to_write); + curr_pos += bytes_to_write; + + if number_written != null do *number_written = bytes_to_write; + return Error.None; + }, + + write_byte = proc (s: ^Stream, byte: u8) -> Error { + ss : ^StringStream = ~~s; + use ss; + + if curr_pos >= data.count do return Error.EOF; + + data[curr_pos] = byte; + curr_pos += 1; + + return Error.None; + }, + + size = proc (s: ^Stream) -> i32 { + ss : ^StringStream = ~~s; + use ss; + + return data.count; + }, + + close = null_proc, + flush = null_proc, +} \ No newline at end of file diff --git a/core/std/js.onyx b/core/std/js.onyx index 2e9c9941..943027ed 100644 --- a/core/std/js.onyx +++ b/core/std/js.onyx @@ -15,6 +15,10 @@ package core #load "core/string/builder" #load "core/string/reader" +#load "core/io/io" +#load "core/io/stream" +#load "core/io/reader" + #load "core/sys/js" diff --git a/core/std/wasi.onyx b/core/std/wasi.onyx index 75fde689..6ff0eed6 100644 --- a/core/std/wasi.onyx +++ b/core/std/wasi.onyx @@ -17,6 +17,10 @@ package core #load "core/string/reader" #load "core/wasi" +#load "core/io/io" +#load "core/io/stream" +#load "core/io/reader" + #load "core/sys/wasi" diff --git a/core/sys/js.onyx b/core/sys/js.onyx index 4f502f91..25b2fb9b 100644 --- a/core/sys/js.onyx +++ b/core/sys/js.onyx @@ -5,6 +5,19 @@ use package main as main output_str :: proc (s: str) -> u32 #foreign "host" "print_str" --- +assert_handler :: proc (msg: str, file: str) { + output_str("Assert failed: "); + output_str(msg); + + if file.data != null { + output_str(" in "); + output_str(file); + } + + process_exit :: proc (status: i32) #foreign "host" "exit" --- + process_exit(1); +} + // The builtin _start proc. // Sets up everything needed for execution. proc () #export "_start" { @@ -12,6 +25,7 @@ proc () #export "_start" { context.allocator = alloc.heap_allocator; context.temp_allocator = alloc.temp_allocator; + context.assert_handler = assert_handler; args : [] cstr; args.data = null; diff --git a/core/sys/wasi.onyx b/core/sys/wasi.onyx index e2de1f69..a3aaf905 100644 --- a/core/sys/wasi.onyx +++ b/core/sys/wasi.onyx @@ -16,6 +16,18 @@ output_str :: proc (s: str) -> u32 { return tmp; } +assert_handler :: proc (msg: str, file: str) { + output_str("Assert failed: "); + output_str(msg); + + if file.data != null { + output_str(" in "); + output_str(file); + } + + proc_exit(1); +} + // The builtin _start proc. // Sets up everything needed for execution. proc () #export "_start" { @@ -23,6 +35,7 @@ proc () #export "_start" { context.allocator = alloc.heap_allocator; context.temp_allocator = alloc.heap_allocator; + context.assert_handler = assert_handler; argc : Size; argv_buf_size : Size; diff --git a/include/onyxtypes.h b/include/onyxtypes.h index 9d951021..0d071135 100644 --- a/include/onyxtypes.h +++ b/include/onyxtypes.h @@ -70,6 +70,7 @@ typedef struct StructMember { struct AstTyped** initial_value; b32 included_through_use : 1; + b32 used : 1; } StructMember; #define TYPE_KINDS \ diff --git a/onyx.exe b/onyx.exe index 5f9676c7..249cd882 100644 Binary files a/onyx.exe and b/onyx.exe differ diff --git a/src/onyxparser.c b/src/onyxparser.c index a70b4eba..9ac5151f 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -1575,6 +1575,16 @@ static AstType* parse_type(OnyxParser* parser) { } + case '(': { + expect_token(parser, '('); + + *next_insertion = parse_type(parser); + next_insertion = NULL; + + expect_token(parser, ')'); + break; + } + default: onyx_report_error(parser->curr->pos, "unexpected token '%b'.", parser->curr->text, parser->curr->length); consume_token(parser); diff --git a/src/onyxtypes.c b/src/onyxtypes.c index 5eaea925..7abaf166 100644 --- a/src/onyxtypes.c +++ b/src/onyxtypes.c @@ -151,7 +151,15 @@ b32 types_are_compatible(Type* t1, Type* t2) { case Type_Kind_Pointer: { if (t2->kind == Type_Kind_Pointer) { - return types_are_compatible(t1->Pointer.elem, t2->Pointer.elem); + if (types_are_compatible(t1->Pointer.elem, t2->Pointer.elem)) return 1; + + if (t1->Pointer.elem->kind == Type_Kind_Struct && t2->Pointer.elem->kind == Type_Kind_Struct) { + Type* t1_struct = t1->Pointer.elem; + Type* t2_struct = t2->Pointer.elem; + + if (t1_struct->Struct.memarr[0]->used) + return types_are_compatible(t2_struct, t1_struct->Struct.memarr[0]->type); + } } if (t2->kind == Type_Kind_Basic && t2->Basic.kind == Basic_Kind_Rawptr) return 1; @@ -370,6 +378,7 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { .name = bh_strdup(alloc, (*member)->token->text), .initial_value = &(*member)->initial_value, .included_through_use = 0, + .used = (((*member)->flags & Ast_Flag_Struct_Mem_Used) != 0), }; bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem); @@ -386,6 +395,7 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) { .name = (*psmem)->name, .initial_value = (*psmem)->initial_value, .included_through_use = 1, + .used = 0, }; bh_table_put(StructMember, s_type->Struct.members, (*psmem)->name, new_smem); diff --git a/tests/general1.onyx b/tests/general1.onyx deleted file mode 100644 index 8e25d1f8..00000000 --- a/tests/general1.onyx +++ /dev/null @@ -1,271 +0,0 @@ -package main - -#load "core/std/js" - -use package core - -print_arr_details :: proc (arr: ^[..] $T) { - print("\nArray details:\n\tSize: "); - println(arr.count); - print("\tCapacity: "); - println(arr.capacity); - print("\tData ptr: "); - println(cast(^void) arr.data); - print("\tSize of elements: "); - println(sizeof T); - print("\tAlignment of elements: "); - println(alignof T); - print("\n"); -} - -print_vec :: proc (v: Vec3) #add_overload print { - print("Vec3("); - print(v.x); - print(", "); - print(v.y); - print(", "); - print(v.z); - print(")"); -} - -// This demonstrates that we have something similar to static 'duck' typing. -get_count :: proc (x: $T) -> u32 do return x.count; - - -// Because of the static duck typing, this will pass as an -// array/slice in most places. -Dummy :: struct { - count : u32 = 5; - data : [5] u32; -} - - -/* This demos some of the power you have with the polymorphic types */ -compose :: proc (a: A, f: proc ($A) -> $B, g: proc (B) -> $C) -> C { - return a |> f() |> g(); -} - - -SOA :: struct { - a : [..] i32; - b : [..] i64; - c : [..] Vec3; -} - -soa_init :: proc (s: ^SOA) { - array.init(^s.a); - array.init(^s.b); - array.init(^s.c); -} - -soa_deinit :: proc (s: ^SOA) { - array.free(^s.a); - array.free(^s.b); - array.free(^s.c); -} - -get_range :: proc (arr: ^[..] $T) -> range { - return 0 .. arr.count; -} - -// NOTE: This function will be very useful for for loops. i.e. -// for i: 0 .. 100 |> by(2) { -// ... -// } -by :: proc (r: range, s: u32) -> range { - return range.{ low = r.low, high = r.high, step = s }; -} - -switch_demo :: proc () { - switch a := 4; a { - case 4, 5, 6 { - println("a was 4, 5, or 6"); - fallthrough fallthrough; - } - - case 10 do println("a was 10"); - - case #default { - println("a was something else."); - } - } -} - -vararg_test :: proc (prefix: str, nums: ..i32) { - print(prefix); - for num: nums { - print(num); - print(" "); - } -} - -NumInfo :: struct { - min : i32; - max : i32; - sum : i32; -} - -get_num_info :: proc (nums: ..i32) -> NumInfo { - ni : NumInfo; - - ni.min = nums[0]; - for num: nums do if num < ni.min do ni.min = num; - - ni.max = nums[0]; - for num: nums do if num > ni.max do ni.max = num; - - ni.sum = 0; - for num: nums do ni.sum += num; - - return ni; -} - -print_strings :: proc (ss: ..str) { - for s: ss do print_str(s); -} - -multi_max :: proc (nums: ..$T) -> T { - print("Got this many args: "); - println(nums.count); - - max := nums[0]; - for num: nums do if num > max do max = num; - return max; -} - -weird_sum :: proc (n1: $T, others: ..T) -> T { - s := n1; - for n: others do s += n; - return s; -} - -main :: proc (args: [] cstr) { - switch_demo(); - - print_strings("This ", "is ", "a ", "test.\n"); - - vararg_test("Here are some numbers:\n", 1, 2, 3, 4, 5); - print("\n\n"); - - multi_max(4, 2, 76, 3, 1203, 2, 4) |> println(); - multi_max(4, 2, 76, 3, 1203, 2, 4) |> println(); - - weird_sum(4, 1) |> println(); - - ni := get_num_info(1, 2, 3, 4, 5); - println("Some information about those numbers:"); - print("Min: "); - println(ni.min); - print("Max: "); - println(ni.max); - print("Sum: "); - println(ni.sum); - print("\n"); - - res := compose(5, proc (x: i32) -> i32 do return x * 3;, - proc (x: i32) -> i32 do return x + 5;); - println(res); - - s : SOA; - soa_init(^s); - defer { - println("Clearing SOA..."); - soa_deinit(^s); - } - - println("Evens from 6 to 34:"); - for i: 6 .. 34 |> by(2) { - print(i); - print(" "); - } - print("\n"); - - print_arr_details(^s.a); - print_arr_details(^s.b); - - for i: 0 .. 100 { - array.push(^s.a, (5 * i) % 21); - array.push(^s.b, 3 * cast(i64) i); - array.push(^s.c, Vec3.{ i, i * i, i * i * i }); - } - - r := ^s.a |> get_range() |> by(3); - print(r); - print_array(^s.a); - print("A has 22? "); - println(array.contains(^s.a, 22)); - - // NOTE: Iterating by value - for vec: s.c { - print(vec); - print(" "); - } - print("\n"); - - // NOTE: Iterating by pointer - for ^vec: s.c { - print(cast(^void) vec); - print(" "); - } - print("\n"); - - small : [12] i32; - for ^it: small do *it = 1234 + cast(u32) it; - - for it: small { - print(it); - print(" "); - } - print("\n"); - - - array.sort(^s.a, cmp_dec); - array.sort(^s.b, cmp_dec); - - print_array(^s.a); - print_array(^s.b); - - println("After adding..."); - print_arr_details(^s.a); - print_arr_details(^s.b); - - print("Array A sum: "); - println(array.fold(^s.a, 0, proc (x: i32, acc: i32) -> i32 do return x + acc;)); - print("\n"); - - pmap : map.Map(rawptr, rawptr); - map.init(^pmap, null, 50); - defer map.free(^pmap); - - for i: 0 .. 100 do map.put(^pmap, ^s.a[i], ^s.b[i]); - - print("Has ^a[20]? "); - println(map.has(^pmap, ^s.a[20])); - - print("Has null? "); - println(map.has(^pmap, null)); - - print("Value at ^a[50]: "); - print(cast(^void) map.get(^pmap, ^s.a[50])); - print(" == "); - println(cast(^void) (^s.b[50])); - - println("Deleteing ^a[20]"); - map.delete(^pmap, ^s.a[20]); - - print("Has ^a[20]? "); - println(map.has(^pmap, ^s.a[20])); -} - - -Vec3 :: struct { - x: i32; - y: i32; - z: i32; -} - -cmp_vec3 :: proc (v1: Vec3, v2: Vec3) -> i32 { - if v1.x != v2.x do return v1.x - v2.x; - if v1.y != v2.y do return v1.y - v2.y; - return v1.z - v2.z; -} diff --git a/tests/general1.onyx.notest b/tests/general1.onyx.notest new file mode 100644 index 00000000..8e25d1f8 --- /dev/null +++ b/tests/general1.onyx.notest @@ -0,0 +1,271 @@ +package main + +#load "core/std/js" + +use package core + +print_arr_details :: proc (arr: ^[..] $T) { + print("\nArray details:\n\tSize: "); + println(arr.count); + print("\tCapacity: "); + println(arr.capacity); + print("\tData ptr: "); + println(cast(^void) arr.data); + print("\tSize of elements: "); + println(sizeof T); + print("\tAlignment of elements: "); + println(alignof T); + print("\n"); +} + +print_vec :: proc (v: Vec3) #add_overload print { + print("Vec3("); + print(v.x); + print(", "); + print(v.y); + print(", "); + print(v.z); + print(")"); +} + +// This demonstrates that we have something similar to static 'duck' typing. +get_count :: proc (x: $T) -> u32 do return x.count; + + +// Because of the static duck typing, this will pass as an +// array/slice in most places. +Dummy :: struct { + count : u32 = 5; + data : [5] u32; +} + + +/* This demos some of the power you have with the polymorphic types */ +compose :: proc (a: A, f: proc ($A) -> $B, g: proc (B) -> $C) -> C { + return a |> f() |> g(); +} + + +SOA :: struct { + a : [..] i32; + b : [..] i64; + c : [..] Vec3; +} + +soa_init :: proc (s: ^SOA) { + array.init(^s.a); + array.init(^s.b); + array.init(^s.c); +} + +soa_deinit :: proc (s: ^SOA) { + array.free(^s.a); + array.free(^s.b); + array.free(^s.c); +} + +get_range :: proc (arr: ^[..] $T) -> range { + return 0 .. arr.count; +} + +// NOTE: This function will be very useful for for loops. i.e. +// for i: 0 .. 100 |> by(2) { +// ... +// } +by :: proc (r: range, s: u32) -> range { + return range.{ low = r.low, high = r.high, step = s }; +} + +switch_demo :: proc () { + switch a := 4; a { + case 4, 5, 6 { + println("a was 4, 5, or 6"); + fallthrough fallthrough; + } + + case 10 do println("a was 10"); + + case #default { + println("a was something else."); + } + } +} + +vararg_test :: proc (prefix: str, nums: ..i32) { + print(prefix); + for num: nums { + print(num); + print(" "); + } +} + +NumInfo :: struct { + min : i32; + max : i32; + sum : i32; +} + +get_num_info :: proc (nums: ..i32) -> NumInfo { + ni : NumInfo; + + ni.min = nums[0]; + for num: nums do if num < ni.min do ni.min = num; + + ni.max = nums[0]; + for num: nums do if num > ni.max do ni.max = num; + + ni.sum = 0; + for num: nums do ni.sum += num; + + return ni; +} + +print_strings :: proc (ss: ..str) { + for s: ss do print_str(s); +} + +multi_max :: proc (nums: ..$T) -> T { + print("Got this many args: "); + println(nums.count); + + max := nums[0]; + for num: nums do if num > max do max = num; + return max; +} + +weird_sum :: proc (n1: $T, others: ..T) -> T { + s := n1; + for n: others do s += n; + return s; +} + +main :: proc (args: [] cstr) { + switch_demo(); + + print_strings("This ", "is ", "a ", "test.\n"); + + vararg_test("Here are some numbers:\n", 1, 2, 3, 4, 5); + print("\n\n"); + + multi_max(4, 2, 76, 3, 1203, 2, 4) |> println(); + multi_max(4, 2, 76, 3, 1203, 2, 4) |> println(); + + weird_sum(4, 1) |> println(); + + ni := get_num_info(1, 2, 3, 4, 5); + println("Some information about those numbers:"); + print("Min: "); + println(ni.min); + print("Max: "); + println(ni.max); + print("Sum: "); + println(ni.sum); + print("\n"); + + res := compose(5, proc (x: i32) -> i32 do return x * 3;, + proc (x: i32) -> i32 do return x + 5;); + println(res); + + s : SOA; + soa_init(^s); + defer { + println("Clearing SOA..."); + soa_deinit(^s); + } + + println("Evens from 6 to 34:"); + for i: 6 .. 34 |> by(2) { + print(i); + print(" "); + } + print("\n"); + + print_arr_details(^s.a); + print_arr_details(^s.b); + + for i: 0 .. 100 { + array.push(^s.a, (5 * i) % 21); + array.push(^s.b, 3 * cast(i64) i); + array.push(^s.c, Vec3.{ i, i * i, i * i * i }); + } + + r := ^s.a |> get_range() |> by(3); + print(r); + print_array(^s.a); + print("A has 22? "); + println(array.contains(^s.a, 22)); + + // NOTE: Iterating by value + for vec: s.c { + print(vec); + print(" "); + } + print("\n"); + + // NOTE: Iterating by pointer + for ^vec: s.c { + print(cast(^void) vec); + print(" "); + } + print("\n"); + + small : [12] i32; + for ^it: small do *it = 1234 + cast(u32) it; + + for it: small { + print(it); + print(" "); + } + print("\n"); + + + array.sort(^s.a, cmp_dec); + array.sort(^s.b, cmp_dec); + + print_array(^s.a); + print_array(^s.b); + + println("After adding..."); + print_arr_details(^s.a); + print_arr_details(^s.b); + + print("Array A sum: "); + println(array.fold(^s.a, 0, proc (x: i32, acc: i32) -> i32 do return x + acc;)); + print("\n"); + + pmap : map.Map(rawptr, rawptr); + map.init(^pmap, null, 50); + defer map.free(^pmap); + + for i: 0 .. 100 do map.put(^pmap, ^s.a[i], ^s.b[i]); + + print("Has ^a[20]? "); + println(map.has(^pmap, ^s.a[20])); + + print("Has null? "); + println(map.has(^pmap, null)); + + print("Value at ^a[50]: "); + print(cast(^void) map.get(^pmap, ^s.a[50])); + print(" == "); + println(cast(^void) (^s.b[50])); + + println("Deleteing ^a[20]"); + map.delete(^pmap, ^s.a[20]); + + print("Has ^a[20]? "); + println(map.has(^pmap, ^s.a[20])); +} + + +Vec3 :: struct { + x: i32; + y: i32; + z: i32; +} + +cmp_vec3 :: proc (v1: Vec3, v2: Vec3) -> i32 { + if v1.x != v2.x do return v1.x - v2.x; + if v1.y != v2.y do return v1.y - v2.y; + return v1.z - v2.z; +}