started working on abstract stream api
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 9 Jan 2021 20:49:25 +0000 (14:49 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 9 Jan 2021 20:49:25 +0000 (14:49 -0600)
16 files changed:
bin/onyx
bin/onyx-js
core/builtin.onyx
core/io/io.onyx [new file with mode: 0644]
core/io/reader.onyx [new file with mode: 0644]
core/io/stream.onyx [new file with mode: 0644]
core/std/js.onyx
core/std/wasi.onyx
core/sys/js.onyx
core/sys/wasi.onyx
include/onyxtypes.h
onyx.exe
src/onyxparser.c
src/onyxtypes.c
tests/general1.onyx [deleted file]
tests/general1.onyx.notest [new file with mode: 0644]

index fa0aaae9b2e543003afd87b22715999dd7aec7b1..c3d9cd57d081302a2c6a7df8cfbb3a2718231e75 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 933c066194ef887c4c1db523521bcb46bc554796..3889ed9a91daf55e78ea44ace808ee63ed0eb580 100755 (executable)
@@ -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);
         }
     }   
 }
index 643ecedeb5a65cd2e04858de63de63be119367f0..1ba52ae3f05c9809b7e6acdac33b8ccc39e92502 100644 (file)
@@ -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 (file)
index 0000000..718e5f5
--- /dev/null
@@ -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 (file)
index 0000000..336c782
--- /dev/null
@@ -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 (file)
index 0000000..bd76c74
--- /dev/null
@@ -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
index 2e9c9941ccb8dd1056400ffcb10aa8a2acce08df..943027ed18ea537df1744ef5a51337c509f9cafe 100644 (file)
@@ -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"
 
 
index 75fde6899d38d885366f10037422567df2554576..6ff0eed669a72e6f1f9d4949e42d2f751516ebae 100644 (file)
@@ -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"
 
 
index 4f502f91eb7318c6d1744e3ac54773986a54cecb..25b2fb9b724928e8bfff5eb95779b0540653da39 100644 (file)
@@ -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;
index e2de1f69f3c03cbeee8dc4f4de0aee4ca2b8c8af..a3aaf9059d0376b9fa62e5099289a83b9340079b 100644 (file)
@@ -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;
index 9d95102167cfac6f7cc65a1b2bc9834c7c16d191..0d071135340b2e2a0f201120e226f4c1f455f087 100644 (file)
@@ -70,6 +70,7 @@ typedef struct StructMember {
 
     struct AstTyped** initial_value;
     b32 included_through_use : 1;
+    b32 used : 1;
 } StructMember;
 
 #define TYPE_KINDS \
index 5f9676c7f1794f9a4bce20b3d124c71b1026861a..249cd882ce72a784bd93ef7f5b08af307496b2c3 100644 (file)
Binary files a/onyx.exe and b/onyx.exe differ
index a70b4eba489d3204b224bed21cd73547d67ff242..9ac5151f6c83156aed9cab7006086a5cdfd991c4 100644 (file)
@@ -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);
index 5eaea9259ac12e05e8d804d80b441ea26ac9435b..7abaf16600bde14eb2fdac93508460145a4f3b0b 100644 (file)
@@ -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 (file)
index 8e25d1f..0000000
+++ /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 (file)
index 0000000..8e25d1f
--- /dev/null
@@ -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;
+}