refactored slice types to not be a struct type internally
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 28 Aug 2020 13:38:04 +0000 (08:38 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 28 Aug 2020 13:38:04 +0000 (08:38 -0500)
19 files changed:
core/builtin.onyx
core/file.onyx [new file with mode: 0644]
core/stdio.onyx [new file with mode: 0644]
core/string.onyx
core/wasi.onyx
docs/api_design
docs/plan
docs/polymorphic_plan [new file with mode: 0644]
include/bh.h
include/onyxastnodes.h
include/onyxtypes.h
misc/cloc_onyx_def [new file with mode: 0644]
misc/onyx.sublime-syntax
onyx
progs/wasi_test.onyx
src/onyxchecker.c
src/onyxsymres.c
src/onyxtypes.c
src/onyxwasm.c

index e0854492d86b182e638cb8ad98ef9db49c8c9417..f8e269e59d8dcceb90b0677939b3f6f8e820fe7e 100644 (file)
@@ -32,6 +32,10 @@ free :: proc (use a: Allocator, ptr: rawptr) {
     func(data, AllocAction.Free, 0, 0, ptr);
 }
 
+calloc  :: proc (size: u32) -> rawptr do return alloc(context.allocator, size);
+cresize :: proc (ptr: rawptr, size: u32) -> rawptr do return resize(context.allocator, ptr, size);
+cfree   :: proc (ptr: rawptr) do free(context.allocator, ptr);
+
 context : struct {
        allocator      : Allocator;
        temp_allocator : Allocator;
diff --git a/core/file.onyx b/core/file.onyx
new file mode 100644 (file)
index 0000000..b36ea63
--- /dev/null
@@ -0,0 +1,121 @@
+package file
+
+// Many of these functions will be improved when
+// multiple return values are implemented.
+
+use package builtin
+use package wasi
+
+OpenMode :: enum {
+    Read;
+    Write;
+    Append; 
+}
+
+File :: struct {
+    fd : FileDescriptor;
+}
+
+file_open :: proc (file: ^File, path: string, mode := OpenMode.Read, flags := FDFlags.Sync) -> bool {
+    // Currently the directory's file descriptor appears to always be 3
+    DIR_FD :: 3;
+
+    open_flags := cast(OFlags) 0;
+    rights := Rights.DataSync
+            | Rights.Sync
+            | Rights.FilestatGet
+            | Rights.FilestatSetSize
+            | Rights.FilestatSetTimes
+            | Rights.Advise
+            | Rights.Allocate
+            | Rights.PathOpen
+            | Rights.PathCreateFile;
+    fdflags := flags;
+
+    switch mode {
+        case OpenMode.Write {
+            open_flags |= OFlags.Creat | OFlags.Trunc;
+            rights     |= Rights.Write;
+        }
+
+        case OpenMode.Append {
+            open_flags |= OFlags.Creat;
+            rights     |= Rights.Write;
+            fdflags    |= FDFlags.Append;
+        }
+
+        case OpenMode.Read {
+            rights |= Rights.Read | Rights.Seek | Rights.Tell;
+        }
+    }
+
+    if err := path_open(
+        DIR_FD,
+        LookupFlags.SymLinkFollow,
+        path,
+        open_flags,
+        rights,
+        rights,
+        fdflags,
+        ^file.fd);
+        err != Errno.Success {
+        return false;
+    }
+
+    return true;
+}
+
+file_close :: proc (file: File) -> bool {
+    if fd_close(file.fd) != Errno.Success {
+        return false;
+    }
+
+    return true;
+}
+
+file_get_size :: proc (file: File) -> i64 {
+    //size: i64 = 0l;
+    //prev: i64;
+
+    //if fd_seek(file.fd, 0l,   Whence.Cur, ^prev) != Errno.Success do return -1l;
+    //if fd_seek(file.fd, 0l,   Whence.End, ^size) != Errno.Success do return -1l;
+    //if fd_seek(file.fd, prev, Whence.Set, ^prev) != Errno.Success do return -1l;
+
+    fs: FileStat;
+    if fd_filestat_get(file.fd, ^fs) != Errno.Success do return -1l;
+
+    return fs.size;
+}
+
+file_get_contents_from_file :: proc (file: File) -> string {
+    size := cast(u32) file_get_size(file);
+
+    data := cast(^u8) alloc(context.allocator, size);
+
+    prev_loc: i64;
+    fd_tell(file.fd, ^prev_loc);
+
+    dummy: i64;
+    fd_seek(file.fd, 0l, Whence.Set, ^dummy);
+
+    dummy2: u32; 
+    buf := IOVec.{ cast(rawptr) data, size };
+    fd_pread(file.fd, IOVecArray.{ ^buf, 1 }, 0l, ^dummy2);
+
+    fd_seek(file.fd, prev_loc, Whence.Set, ^dummy);
+
+    return data[0 : size];
+}
+
+file_get_contents :: proc #overloaded {
+    file_get_contents_from_file,
+
+    proc (path: string) -> string {
+        tmp_file: File;
+
+        if !file_open(^tmp_file, path, OpenMode.Read) do return "";
+        defer file_close(tmp_file);
+
+        return file_get_contents(tmp_file);
+    }
+}
\ No newline at end of file
diff --git a/core/stdio.onyx b/core/stdio.onyx
new file mode 100644 (file)
index 0000000..9fc618e
--- /dev/null
@@ -0,0 +1,48 @@
+package stdio
+
+use package builtin
+use package core
+use package wasi
+
+print_string  :: proc (s: string) {
+       string_builder_append(^cout_state.sb, s);
+       if s.data[s.count - 1] == #char "\n" do print_flush();
+}
+
+print_cstring :: proc (s: cstring) do string_builder_append(^cout_state.sb, s);
+print_u64     :: proc (n: u64, base := 10l) do string_builder_append(^cout_state.sb, n);
+print_u32     :: proc (n: u32, base := 10)  do string_builder_append(^cout_state.sb, cast(u64) n, cast(u64) base);
+
+print :: proc #overloaded {
+       print_string,
+       print_cstring,
+       print_u64,
+       print_u32,
+}
+
+print_flush :: proc {
+       ^cout_state.sb |> string_builder_to_string() |> raw_print(cout_state.fd);
+       ^cout_state.sb |> string_builder_clear();
+}
+
+#private
+raw_print :: proc (s: string, fd: FileDescriptor) -> u32 {
+    vec := IOVec.{ buf = s.data, len = s.count };
+    tmp : Size;
+    fd_write(fd, IOVecArray.{ ^vec, 1 }, ^tmp);
+    fd_datasync(fd);
+    return tmp;
+}
+
+ConsoleOutput :: struct {
+       sb : StringBuilder;
+       fd : FileDescriptor;
+}
+
+#private
+cout_state : ConsoleOutput
+
+stdio_init :: proc {
+       cout_state.fd = cast(FileDescriptor) 1;
+       cout_state.sb = string_builder_make(2048);
+}
\ No newline at end of file
index f768239d14b077359ec8b3e32c078a3c09dbe45b..933c8b0c4d986b6a0a5dd5c5433cc417dd9af792 100644 (file)
@@ -33,27 +33,27 @@ string_length :: proc #overloaded {
 #private
 string_length_string :: proc (s: string) -> u32 do return s.count;
 
-string_concat :: proc (a: Allocator, s1: string, s2: string) -> string {
+string_concat :: proc (s1: string, s2: string) -> string {
     len1 :: string_length(s1);
     len2 :: string_length(s2);
 
-    data := cast(^u8) alloc(a, len1 + len2);
+    data := cast(^u8) calloc(len1 + len2);
     for i: 0, len1 do data[i]        = s1.data[i];
     for i: 0, len2 do data[i + len1] = s2.data[i];
 
     return string.{ data, len1 + len2 };
 }
 
-string_free :: proc (a: Allocator, s: string) do free(a, s.data);
+string_free :: proc (s: string) do cfree(s.data);
 
 // This is an example doc string
 // You can have as many comments as you want
 // It documents the string_split function
-string_split :: proc (a: Allocator, str: string, delim: u8) -> []string {
+string_split :: proc (str: string, delim: u8) -> []string {
     delim_count := 0;
     for i: 0, str.count do if str.data[i] == delim do delim_count += 1;
 
-    strarr := cast(^string) alloc(a, sizeof string * (delim_count + 1));
+    strarr := cast(^string) calloc(sizeof string * (delim_count + 1));
 
     curr_str := 0;
     begin := 0;
@@ -83,6 +83,11 @@ string_substr :: proc (str: string, sub: string) -> string {
     return str.data[0:0];
 }
 
+string_contains :: proc (str: string, c: u8) -> bool {
+    for i: 0, str.count do if str.data[i] == c do return true;
+    return false;
+}
+
 StringBuilder :: struct {
     alloc : Allocator;
 
@@ -91,15 +96,15 @@ StringBuilder :: struct {
     cap   : u32 = 0;
 }
 
-string_builder_make :: proc (a: Allocator, initial_cap: u32) -> StringBuilder {
+string_builder_make :: proc (initial_cap: u32) -> StringBuilder {
     data: ^u8 = null;
 
     if initial_cap > 0 {
-        data = cast(^u8) alloc(a, initial_cap);
+        data = cast(^u8) calloc(initial_cap);
     }
 
     return StringBuilder.{
-        alloc = a,
+        alloc = context.allocator,
         data  = data,
         cap   = initial_cap,
     };
index 5ca3d59743901bc017d63ee14974b2824921dc3a..f2af84defe41105828183197608e6ca22dc74d39 100644 (file)
@@ -1,8 +1,11 @@
 package wasi
 
+#include_file "stdio"
+
 use package main as main
 use package builtin
 use package memory
+use package stdio as io
 
 Size      :: #type u32;
 Filesize  :: #type u64;
@@ -478,5 +481,9 @@ proc #export "_start" {
 
        args_get(argv, argv_buf);
 
+    io.stdio_init();
+
        main.main(argv[0 : argc]);
+
+    io.print_flush();
 }
index d11d4f746147b544a89dd20e411303bed4d01972..d6c85c8e102226a4c53f706dd7429e96c2c33941 100644 (file)
@@ -23,11 +23,3 @@ The standard high level topics to cover are:
 
 
 
-
-Package main:
-       main :: proc (args: [] cstring) -> i32  
-
-Package string:
-       string_split :: proc (a: Allocator, s: string, delim: u8) -> [] string
-               at /usr/share/onyx/core/string.onyx:50,0
-               Auto documentation for string_split
index 1a21eaf21a02aaa2adfdc9f447b43ace4f84a71a..898280e826934e7e45c43f6228a2adc5f3b8b205 100644 (file)
--- a/docs/plan
+++ b/docs/plan
@@ -223,6 +223,8 @@ HOW:
 
         [ ] transmute
 
+        [ ] explicit memory controls at top level
+
         [ ] Put type info in data section so it is runtime accessible
             - type name
             - size
diff --git a/docs/polymorphic_plan b/docs/polymorphic_plan
new file mode 100644 (file)
index 0000000..47fdaf2
--- /dev/null
@@ -0,0 +1,41 @@
+Current basic plan for polymorphic procedures
+
+0. Cleanup some of the aspects of the type system and make slices their own special type.
+
+1. Detect polymorphic parameters
+       Polymorphic parameters will have a $ in from a symbol in their type
+
+2. Polymorphic procedures (polyproc) will have a seperate entity type
+       Most of the stages will ignore them however
+
+3. Polyprocs will store a tabel on them mapping from specific, filled in types to the corresponding function.
+       "T=u32;R=[] u8" -> <function>
+
+4. When a polyproc is called, the polymoprhic parameters are parallel-recursived to find the type matching the polyparam.
+       For example:
+
+               foo :: proc (a: ^[] $T, b: u32) -> T {
+                       return a.data[b];
+               }
+
+               arr : [128] []u8;
+               // init arr
+               a := arr[4 : 10];
+               foo(^a, 2);
+
+       When foo is called, we look at the polymorphic parameters, a in this example, are do the following recursion:
+
+               (^[] $T, ^[] []u8)      Both are pointers, so remove ^
+               ([] $T, [] []u8)        Both are slices, so remove []
+               ($T, []u8)              T is resolved to be []u8 in this case
+
+       If at any point, both sides cannot be removed, it is an invalid parameter.
+
+5. When the specific types of the polyproc are resolved, if no matching function already exists, a copy is made.
+       Copies are made from an un-symbol-resolved version of the procedure.
+       Some nodes can be marked as NO_COPY which signals that a copy should not be made.
+
+6. After a copy is made, it is fed through symbol resolution using the correct scope, and then type checking, and then function header and function entities are added to the entity list in the correct position.
+
+
+Ideally, nothing should change with the WASM output.
index d1930ca83357312484019d5c0f1568ef13770cfe..0a44d9af1d23ab937f057282d0a3afee86c22d6d 100644 (file)
@@ -168,7 +168,8 @@ u8* double_to_ieee754(f64 f, b32 reverse);
 #define BH_BIT(x)                        (1 << (x))
 #define BH_MASK_SET(var, set, mask)     ((set) ? (var) |= (mask) : (var) &= ~(mask))
 
-#define fori(var, lo, hi)                for (i64 var = (lo); var < (hi); var++)
+#define fori(var, lo, hi)                 for (i64 var = (lo); var < (hi); var++)
+#define forir(var, hi, lo)                for (i64 var = (hi); var >= (lo); var--)
 #define forll(T, var, start, step)        for (T* var = (start); var != NULL; var = var->step)
 
 #ifdef BH_DEBUG
index 512160aeab3c6999c1035530680fe752406405aa..b18db701fb38e663c5294b2bce9f15e10ef4622d 100644 (file)
@@ -595,6 +595,7 @@ static inline CallingConvention type_function_get_cc(Type* type) {
     if (type == NULL) return CC_Undefined;
     if (type->kind != Type_Kind_Function) return CC_Undefined;
     if (type->Function.return_type->kind == Type_Kind_Struct) return CC_Return_Stack;
+    if (type->Function.return_type->kind == Type_Kind_Slice) return CC_Return_Stack;
     return CC_Return_Wasm;
 }
 
index 38c4595e76eaeae412b4672340b654cb73be75e3..7c15348c3e33e69b8d21db4c9a6757530745dc98 100644 (file)
@@ -68,6 +68,7 @@ typedef struct StructMember {
         bh_arr(StructMember *) memarr;                            \
     })                                                            \
     TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
+    TYPE_KIND(Slice, struct { Type *ptr_to_data; })               \
     TYPE_KIND(Enum, struct { char* name; Type* backing; }) 
 
 typedef enum TypeKind {
@@ -118,7 +119,9 @@ Type* type_make_slice(bh_allocator alloc, Type* of);
 const char* type_get_name(Type* type);
 u32 type_get_alignment_log2(Type* type);
 
-b32 type_struct_lookup_member(Type* type, char* member, StructMember* smem);
+b32 type_lookup_member(Type* type, char* member, StructMember* smem);
+b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem);
+
 b32 type_struct_is_simple(Type* type);
 
 b32 type_is_pointer(Type* type);
@@ -131,5 +134,9 @@ b32 type_is_integer(Type* type);
 b32 type_is_numeric(Type* type);
 b32 type_is_compound(Type* type);
 b32 type_results_in_void(Type* type);
+b32 type_is_structlike(Type* type);
+b32 type_is_structlike_strict(Type* type);
+u32 type_structlike_mem_count(Type* type);
+u32 type_structlike_is_simple(Type* type);
 
 #endif // #ifndef ONYX_TYPES
diff --git a/misc/cloc_onyx_def b/misc/cloc_onyx_def
new file mode 100644 (file)
index 0000000..afd54e0
--- /dev/null
@@ -0,0 +1,5 @@
+Onyx
+    filter remove_inline //.*$
+    filter call_regexp_common C++
+    extension onyx
+    3rd_gen_scale 2.30
index 6356d84f162c8f8ad1efdcfbb9360025c76809ff..9a6f2e04ae9468f1bff4ae4e115958a0f741b4ac 100644 (file)
@@ -27,9 +27,9 @@ contexts:
       scope: keyword.control.onyx
 
     - match: '\b(bool|void|i8|u8|i16|u16|i32|u32|i64|u64|f32|f64|rawptr)\b'
-      scope: constant.type.onyx
+      scope: keyword.control.onyx
 
-    - match: '\b(true|false|null)\b'
+    - match: '\b(true|false|null|context)\b'
       scope: constant.numeric.onyx
 
     # Numbers
diff --git a/onyx b/onyx
index e9ba9510d82409729dad4b5c96d015f22ca6beb5..d2dcc382e547f0ea9b6db18bdb999e51ddecde85 100755 (executable)
Binary files a/onyx and b/onyx differ
index 567d3434c320bc7c8b6f19790e061873a1ca057b..8c9d72b1a0183fa37d310c1bc4fba89478d0d295 100644 (file)
@@ -8,6 +8,7 @@ package main
 #include_file "intrinsics"
 #include_file "random"
 #include_file "string"
+#include_file "file"
 
 use package builtin
 
@@ -19,66 +20,11 @@ use package memory
 use package wasi
 use package intrinsics
 use package random
-
-print_u64 :: proc (n_: u64, base := 10l) {
-    n := n_;
-    str: [256] u8;
-    for i: 0, 256 do str[i] = #char "\0";
-
-    c := cast(^u8) ^str[255];
-    *c = #char "\0";
-    c -= 1;
-
-    s :: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
-
-    if n == 0l {
-        *c = #char "0";
-        c -= 1;
-    } else {
-        while n > 0l {
-            m :: n % base;
-
-            *c = s.data[cast(u32) m];
-            c -= 1;
-
-            n /= base;
-        }
-    }
-
-    if base == 16l {
-        *c = #char "x";
-        c -= 1;
-        *c = #char "0";
-        c -= 1;
-    }
-
-    if base == 2l {
-        *c = #char "b";
-        c -= 1;
-        *c = #char "0";
-        c -= 1;
-    }
-
-    print(c + 1);
-}
-
-print_string :: proc (s: string) -> u32 {
-    vec := IOVec.{ buf = s.data, len = s.count };
-    tmp : Size;
-    fd_write(1, IOVecArray.{ ^vec, 1 }, ^tmp);
-    fd_datasync(1);
-
-    return tmp;
-}
-
-print_u8 :: proc (s: cstring) -> u32 {
-    return string_make(s) |> print_string();
-}
-
-print :: proc #overloaded { print_string, print_u8 }
+use package file
+use package stdio
 
 print_rights :: proc (rights: Rights) {
-    print_u64(cast(u64) rights, 2l);
+    print(cast(u32) rights, 2);
     print("\n");
 
     if rights & Rights.DataSync != cast(Rights) 0 do print("DataSync\n");
@@ -214,7 +160,7 @@ main :: proc (args: []cstring) {
 
     random_seed(cast(u32) now);
 
-    sb := string_builder_make(heap_allocator, 256);
+    sb := string_builder_make(256);
 
     timer := timer_start();
     defer {
@@ -235,26 +181,9 @@ main :: proc (args: []cstring) {
         |> string_builder_to_string()
         |> print();
 
-    fd: FileDescriptor = -1;
-    if err := path_open(3, LookupFlags.SymLinkFollow, string_make(args.data[1]), cast(OFlags) 0, Rights.DataSync | Rights.Write | Rights.Read | Rights.Tell | Rights.Seek | Rights.Advise | Rights.PathOpen | Rights.PathCreateFile, Rights.DataSync | Rights.Write | Rights.Read | Rights.Tell | Rights.Seek | Rights.Advise | Rights.PathOpen | Rights.PathCreateFile, FDFlags.Sync, ^fd); err != Errno.Success {
-        print("Failed to open file\n");
-        print("Error code: ");
-        print_u64(cast(u64) err, 16l);
-        proc_exit(1);
-    }
-    defer fd_close(fd);
-
-    print_u64(cast(u64) fd, 16l);
-    print("\n");
-
-    filelen : Filesize;
-    if fd_seek(fd, 0l, Whence.End, ^filelen) != Errno.Success {
-        print("Failed to seek in file\n");
-        proc_exit(1);
-    }
-    print("the size is: ");
-    print_u64(cast(u64) filelen);
-    print("\n");
+    cont := file_get_contents(string_make(args.data[1]));
+    defer cfree(cont.data);
+    print(cont);
 
     sum := 0l;
     for i: 0, 20000 do if is_prime(i) do sum += cast(u64) i;
@@ -262,8 +191,8 @@ main :: proc (args: []cstring) {
     print_u64(sum);
     print("\n");
 
-    matches := string_split(heap_allocator, "This is a test string to test splitting. It surprisingly works very well.", #char " ");
-    defer free(heap_allocator, matches.data);
+    matches := string_split("This is a test string to test splitting. It surprisingly works very well.", #char " ");
+    defer cfree(matches.data);
 
     string_builder_clear(^sb);
     for i: 0, matches.count {
@@ -274,8 +203,8 @@ main :: proc (args: []cstring) {
     ^sb |> string_builder_to_string() |> print();
 
     program := "+ + * s - /";
-    tokens := string_split(heap_allocator, program, #char " ");
-    defer free(heap_allocator, tokens.data);
+    tokens := string_split(program, #char " ");
+    defer cfree(tokens.data);
 
     acc := 0;
     for i: 0, tokens.count {
@@ -349,6 +278,8 @@ main :: proc (args: []cstring) {
 
     sl := make_i32_arr();
     print_u64(cast(u64) sl.count);
+
+    print_u64(cast(u64) fib(20));
 }
 
 foobar :: proc (a: i32, b := 1, c := 5l) {
@@ -358,4 +289,20 @@ foobar :: proc (a: i32, b := 1, c := 5l) {
     print("\n");
     print_u64(c);
     print("\n");
-}
\ No newline at end of file
+}
+
+fib :: proc (n: i32) -> i32 {
+    switch n {
+        case 0 do return 1;
+        case 1 do return 1;
+        case #default {
+            return fib(n - 1) + fib(n - 2);
+        }
+    }
+
+    return 0;
+}
+
+//make_slice :: proc (ptr: ^$T, count: u32) -> [] T {
+//    return ptr[0 : count];
+//}
\ No newline at end of file
index b53c61ca0eed438fb08e7a12af35fb35402612f8..b4ac6fc442ce84413087a06782659d094f341ae1 100644 (file)
@@ -268,8 +268,8 @@ b32 check_call(AstCall* call) {
             return 1;
         }
 
-        if (actual_param->value->type->kind == Type_Kind_Struct) {
-            if (!type_struct_is_simple(actual_param->value->type)) {
+        if (type_is_structlike_strict(actual_param->value->type)) {
+            if (!type_structlike_is_simple(actual_param->value->type)) {
                 onyx_message_add(Msg_Type_Literal,
                         actual_param->token->pos,
                         "can only pass simple structs as parameters (no nested structures). passing by pointer is the only way for now.");
@@ -523,14 +523,14 @@ b32 check_binaryop_compare(AstBinaryOp** pbinop) {
         return 1;
     }
 
-    if (binop->left->type->kind == Type_Kind_Struct) {
+    if (type_is_structlike_strict(binop->left->type)) {
         onyx_message_add(Msg_Type_Literal,
                 binop->token->pos,
                 "invalid type for left side of binary operator");
         return 1;
     }
 
-    if (binop->right->type->kind == Type_Kind_Struct) {
+    if (type_is_structlike_strict(binop->right->type)) {
         onyx_message_add(Msg_Type_Literal,
                 binop->token->pos,
                 "invalid type for right side of binary operator");
@@ -728,8 +728,9 @@ b32 check_unaryop(AstUnaryOp** punop) {
 b32 check_struct_literal(AstStructLiteral* sl) {
     fill_in_type((AstTyped *) sl);
 
-    TypeStruct* st = &sl->type->Struct;
-    if (st->mem_count != bh_arr_length(sl->values)) {
+    u32 mem_count = type_structlike_mem_count(sl->type);
+
+    if (mem_count != bh_arr_length(sl->values)) {
         onyx_message_add(Msg_Type_Literal,
                 sl->token->pos,
                 "incorrect number of initial values for this type");
@@ -737,20 +738,26 @@ b32 check_struct_literal(AstStructLiteral* sl) {
     }
 
     AstTyped** actual = sl->values;
-    StructMember** formal = st->memarr;
+    StructMember smem;
 
-    fori (i, 0, st->mem_count) {
+    fori (i, 0, mem_count) {
         if (check_expression(actual)) return 1;
 
-        if (!types_are_compatible((*formal)->type, (*actual)->type)) {
+        // NOTE: Not checking the return on this function because
+        // this for loop is bounded by the number of members in the
+        // type.
+        type_lookup_member_by_idx(sl->type, i, &smem);
+        Type* formal = smem.type;
+
+        if (!types_are_compatible(formal, (*actual)->type)) {
             onyx_message_add(Msg_Type_Assignment_Mismatch,
                     sl->token->pos,
-                    type_get_name((*formal)->type),
+                    type_get_name(formal),
                     type_get_name((*actual)->type));
             return 1;
         }
 
-        actual++, formal++;
+        actual++;
     }
 
     return 0;
@@ -884,16 +891,16 @@ b32 check_field_access(AstFieldAccess** pfield) {
     AstFieldAccess* field = *pfield;
     if (check_expression(&field->expr)) return 1;
 
-    if (!type_is_struct(field->expr->type)) {
+    if (!type_is_structlike(field->expr->type)) {
         onyx_message_add(Msg_Type_Literal,
             field->token->pos,
-            "expected expression of kind struct or pointer to struct");
+            "cannot access field on non structures");
         return 1;
     }
 
     token_toggle_end(field->token);
     StructMember smem;
-    if (!type_struct_lookup_member(field->expr->type, field->token->text, &smem)) {
+    if (!type_lookup_member(field->expr->type, field->token->text, &smem)) {
         onyx_message_add(Msg_Type_No_Field,
             field->token->pos,
             field->token->text,
index 35f0819e9fe9b12937b1e57c0da294ea08a693ac..a715b943676df1f9fd52d0da2d9c29b5049a68aa 100644 (file)
@@ -228,21 +228,21 @@ static void symres_struct_literal(AstStructLiteral* sl) {
     sl->type_node = (AstType *) sl->stnode;
     sl->type = type_build_from_ast(semstate.allocator, sl->type_node);
 
-    if (sl->type->kind != Type_Kind_Struct) {
+    if (!type_is_structlike_strict(sl->type)) {
         onyx_message_add(Msg_Type_Literal,
                 sl->token->pos,
-                "type is not a struct type (BAD ERROR MESSAGE)");
+                "type is not a constructable using a struct literal");
         return;
     }
 
     if (bh_arr_length(sl->values) == 0) {
-        bh_arr_set_length(sl->values, sl->type->Struct.mem_count);
+        bh_arr_set_length(sl->values, type_structlike_mem_count(sl->type));
         bh_arr_zero(sl->values);
 
         StructMember s;
         bh_arr_each(AstStructMember *, smem, sl->named_values) {
             token_toggle_end((*smem)->token);
-            if (!type_struct_lookup_member(sl->type, (*smem)->token->text, &s)) {
+            if (!type_lookup_member(sl->type, (*smem)->token->text, &s)) {
                 onyx_message_add(Msg_Type_No_Field,
                         (*smem)->token->pos,
                         (*smem)->token->text, type_get_name(sl->type));
@@ -261,20 +261,22 @@ static void symres_struct_literal(AstStructLiteral* sl) {
             sl->values[s.idx] = (*smem)->initial_value;
         }
 
-        AstStructType* st = (AstStructType *) sl->type_node;
-        bh_arr_each(StructMember*, smem, sl->type->Struct.memarr) {
-            u32 idx = (*smem)->idx;
-
-            if (sl->values[idx] == NULL) {
-                if (st->members[idx]->initial_value == NULL) {
-                    onyx_message_add(Msg_Type_Field_No_Value,
-                            sl->token->pos,
-                            st->members[idx]->token->text,
-                            st->members[idx]->token->length);
-                    return;
+        if (sl->type->kind == Type_Kind_Struct) {
+            AstStructType* st = (AstStructType *) sl->type_node;
+            bh_arr_each(StructMember*, smem, sl->type->Struct.memarr) {
+                u32 idx = (*smem)->idx;
+
+                if (sl->values[idx] == NULL) {
+                    if (st->members[idx]->initial_value == NULL) {
+                        onyx_message_add(Msg_Type_Field_No_Value,
+                                sl->token->pos,
+                                st->members[idx]->token->text,
+                                st->members[idx]->token->length);
+                        return;
+                    }
+
+                    sl->values[idx] = st->members[idx]->initial_value;
                 }
-
-                sl->values[idx] = st->members[idx]->initial_value;
             }
         }
     }
index a3e93e540c1d094d8806d800d25b8e2bed0881e5..762f997567e7c17ea693cf9b7adb6e988ee67372 100644 (file)
@@ -89,6 +89,11 @@ b32 types_are_surface_compatible(Type* t1, Type* t2) {
             return t1 == t2;
         }
 
+        case Type_Kind_Slice: {
+            if (t2->kind != Type_Kind_Slice) return 0;
+            return types_are_compatible(t1->Slice.ptr_to_data->Pointer.elem, t2->Slice.ptr_to_data->Pointer.elem);
+        }
+
         default:
             assert(("Invalid type", 0));
             break;
@@ -179,6 +184,11 @@ b32 types_are_compatible(Type* t1, Type* t2) {
             return 1;
         }
 
+        case Type_Kind_Slice: {
+            if (t2->kind != Type_Kind_Slice) return 0;
+            return types_are_compatible(t1->Slice.ptr_to_data->Pointer.elem, t2->Slice.ptr_to_data->Pointer.elem);
+        }
+
         default:
             assert(("Invalid type", 0));
             break;
@@ -197,6 +207,7 @@ u32 type_size_of(Type* type) {
         case Type_Kind_Array:    return type->Array.size;
         case Type_Kind_Struct:   return type->Struct.size;
         case Type_Kind_Enum:     return type_size_of(type->Enum.backing);
+        case Type_Kind_Slice:    return 8;
         default:                 return 0;
     }
 }
@@ -211,6 +222,7 @@ u32 type_alignment_of(Type* type) {
         case Type_Kind_Array:    return type_alignment_of(type->Array.elem);
         case Type_Kind_Struct:   return type->Struct.alignment;
         case Type_Kind_Enum:     return type_alignment_of(type->Enum.backing);
+        case Type_Kind_Slice:    return 4;
         default: return 1;
     }
 }
@@ -403,28 +415,11 @@ Type* type_make_pointer(bh_allocator alloc, Type* to) {
 }
 
 Type* type_make_slice(bh_allocator alloc, Type* of) {
-    Type* s_type = bh_alloc(alloc, sizeof(Type));
-    s_type->kind = Type_Kind_Struct;
-    s_type->Struct.name = bh_aprintf(global_heap_allocator, "[] %s", type_get_name(of));
-    s_type->Struct.mem_count = 2;
-    s_type->Struct.memarr = NULL;
-
-    bh_table_init(global_heap_allocator, s_type->Struct.members, s_type->Struct.mem_count);
-    bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
-
-    StructMember smem;
-    smem = (StructMember) { .offset = 0, .type = type_make_pointer(alloc, of), .idx = 0, };
-    bh_table_put(StructMember, s_type->Struct.members, "data", smem);
-    smem = (StructMember) { .offset = 4, .type = &basic_types[Basic_Kind_U32], .idx = 1, };
-    bh_table_put(StructMember, s_type->Struct.members, "count", smem);
-
-    bh_arr_push(s_type->Struct.memarr, &bh_table_get(StructMember, s_type->Struct.members, "data"));
-    bh_arr_push(s_type->Struct.memarr, &bh_table_get(StructMember, s_type->Struct.members, "count"));
-
-    s_type->Struct.alignment = 4;
-    s_type->Struct.size = 8;
+    Type* slice_type = bh_alloc(alloc, sizeof(Type));
+    slice_type->kind = Type_Kind_Slice;
+    slice_type->Slice.ptr_to_data = type_make_pointer(alloc, of);
 
-    return s_type;
+    return slice_type;
 }
 
 const char* type_get_name(Type* type) {
@@ -460,15 +455,70 @@ u32 type_get_alignment_log2(Type* type) {
     return 2;
 }
 
-b32 type_struct_lookup_member(Type* type, char* member, StructMember* smem) {
-    if (!type_is_struct(type)) return 0;
+b32 type_lookup_member(Type* type, char* member, StructMember* smem) {
     if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
 
-    TypeStruct* stype = &type->Struct;
+    switch (type->kind) {
+        case Type_Kind_Struct: {
+            TypeStruct* stype = &type->Struct;
 
-    if (!bh_table_has(StructMember, stype->members, member)) return 0;
-    *smem = bh_table_get(StructMember, stype->members, member);
-    return 1;
+            if (!bh_table_has(StructMember, stype->members, member)) return 0;
+            *smem = bh_table_get(StructMember, stype->members, member);
+            return 1;
+        }
+
+        case Type_Kind_Slice: {
+            if (strcmp(member, "data") == 0) {
+                smem->idx = 0;
+                smem->offset = 0;
+                smem->type = type->Slice.ptr_to_data;
+                return 1;
+            }
+            if (strcmp(member, "count") == 0) {
+                smem->idx = 1;
+                smem->offset = 4;
+                smem->type = &basic_types[Basic_Kind_U32];
+                return 1;
+            }
+
+            return 0;
+        }
+
+        default: return 0;
+    }
+}
+
+b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem) {
+    if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
+
+    switch (type->kind) {
+        case Type_Kind_Struct: {
+            TypeStruct* stype = &type->Struct;
+
+            if (idx > stype->mem_count) return 0;
+            *smem = *stype->memarr[idx];
+            return 1;
+        }
+
+        case Type_Kind_Slice: {
+            if (idx == 0) {
+                smem->idx = 0;
+                smem->offset = 0;
+                smem->type = type->Slice.ptr_to_data;
+                return 1;
+            }
+            if (idx == 1) {
+                smem->idx = 1;
+                smem->offset = 4;
+                smem->type = &basic_types[Basic_Kind_U32];
+                return 1;
+            }
+
+            return 0;
+        }
+
+        default: return 0;
+    }
 }
 
 b32 type_struct_is_simple(Type* type) {
@@ -542,3 +592,35 @@ b32 type_results_in_void(Type* type) {
             && (type->Function.return_type->kind == Type_Kind_Basic)
             && (type->Function.return_type->Basic.kind == Basic_Kind_Void));
 }
+
+b32 type_is_structlike(Type* type) {
+    if (type->kind == Type_Kind_Struct) return 1;
+    if (type->kind == Type_Kind_Slice)  return 1;
+    if (type->kind == Type_Kind_Pointer) {
+        if (type->Pointer.elem->kind == Type_Kind_Struct) return 1;
+        if (type->Pointer.elem->kind == Type_Kind_Slice)  return 1;
+    }
+    return 0;
+}
+
+b32 type_is_structlike_strict(Type* type) {
+    if (type->kind == Type_Kind_Struct) return 1;
+    if (type->kind == Type_Kind_Slice)  return 1;
+    return 0;
+}
+
+u32 type_structlike_mem_count(Type* type) {
+    switch (type->kind) {
+        case Type_Kind_Struct: return type->Struct.mem_count;
+        case Type_Kind_Slice:  return 2;
+        default: return 0;
+    }
+}
+
+u32 type_structlike_is_simple(Type* type) {
+    switch (type->kind) {
+        case Type_Kind_Struct: return type_struct_is_simple(type);
+        case Type_Kind_Slice:  return 1;
+        default: return 0;
+    }
+}
\ No newline at end of file
index a3740b11fdcc48c3dd48ec7ca94cb73dd580a233..e1ca422a030626d7729a5c89bc52e54fd4d43f42 100644 (file)
@@ -205,6 +205,10 @@ static WasmType onyx_type_to_wasm_type(Type* type) {
         return WASM_TYPE_VOID;
     }
 
+    if (type->kind == Type_Kind_Slice) {
+        return WASM_TYPE_VOID;
+    }
+
     if (type->kind == Type_Kind_Enum) {
         return onyx_type_to_wasm_type(type->Enum.backing);
     }
@@ -469,7 +473,7 @@ COMPILE_FUNC(statement, AstNode* stmt) {
 COMPILE_FUNC(assignment, AstBinaryOp* assign) {
     bh_arr(WasmInstruction) code = *pcode;
 
-    if (assign->right->type->kind == Type_Kind_Struct) {
+    if (type_is_structlike_strict(assign->right->type)) {
         compile_expression(mod, &code, assign->right);
         compile_struct_lval(mod, &code, assign->left);
 
@@ -540,7 +544,7 @@ COMPILE_FUNC(assignment, AstBinaryOp* assign) {
 COMPILE_FUNC(store_instruction, Type* type, u32 offset) {
     bh_arr(WasmInstruction) code = *pcode;
 
-    if (type->kind == Type_Kind_Struct) {
+    if (type_is_structlike_strict(type)) {
         compile_struct_store(mod, pcode, type, offset);
         return;
     }
@@ -583,7 +587,7 @@ COMPILE_FUNC(store_instruction, Type* type, u32 offset) {
 COMPILE_FUNC(load_instruction, Type* type, u32 offset) {
     bh_arr(WasmInstruction) code = *pcode;
 
-    if (type->kind == Type_Kind_Struct) {
+    if (type_is_structlike_strict(type)) {
         compile_struct_load(mod, pcode, type, offset);
         return;
     }
@@ -1193,7 +1197,7 @@ COMPILE_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return) {
     u64 offset = field->offset;
     AstTyped* source_expr = field->expr;
     while (source_expr->kind == Ast_Kind_Field_Access
-            && (source_expr->type->kind == Type_Kind_Struct)) {
+            && type_is_structlike_strict(source_expr->type)) {
         offset += ((AstFieldAccess *) source_expr)->offset;
         source_expr = (AstTyped *) ((AstFieldAccess *) source_expr)->expr;
     }
@@ -1255,10 +1259,14 @@ COMPILE_FUNC(struct_load, Type* type, u64 offset) {
 
     bh_arr(WasmInstruction) code = *pcode;
 
-    assert(type->kind == Type_Kind_Struct);
+    assert(type_is_structlike_strict(type));
 
-    if (type->Struct.mem_count == 1) {
-        compile_load_instruction(mod, &code, type->Struct.memarr[0]->type, offset);
+    u32 mem_count = type_structlike_mem_count(type);
+    StructMember smem;
+
+    if (mem_count == 1) {
+        type_lookup_member_by_idx(type, 0, &smem);
+        compile_load_instruction(mod, &code, smem.type, offset);
         *pcode = code;
         return;
     }
@@ -1266,11 +1274,10 @@ COMPILE_FUNC(struct_load, Type* type, u64 offset) {
     u64 tmp_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
     WIL(WI_LOCAL_TEE, tmp_idx);
 
-    b32 first = 1;
-    bh_arr_each(StructMember *, smem, type->Struct.memarr) {
-        if (first) first = 0;
-        else       WIL(WI_LOCAL_GET, tmp_idx);
-        compile_load_instruction(mod, &code, (*smem)->type, offset + (*smem)->offset);
+    fori (i, 0, mem_count) {
+        type_lookup_member_by_idx(type, i, &smem);
+        if (i != 0) WIL(WI_LOCAL_GET, tmp_idx);
+        compile_load_instruction(mod, &code, smem.type, offset + smem.offset);
     }
 
     local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
@@ -1287,7 +1294,7 @@ COMPILE_FUNC(struct_lval, AstTyped* lval) {
 
     bh_arr(WasmInstruction) code = *pcode;
 
-    assert(lval->type->kind == Type_Kind_Struct);
+    assert(type_is_structlike_strict(lval->type));
 
     u64 offset = 0;
 
@@ -1316,30 +1323,35 @@ COMPILE_FUNC(struct_store, Type* type, u64 offset) {
 
     bh_arr(WasmInstruction) code = *pcode;
 
-    assert(type->kind == Type_Kind_Struct);
+    assert(type_is_structlike_strict(type));
 
     u64 loc_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
     WIL(WI_LOCAL_SET, loc_idx);
 
-    bh_arr_rev_each(StructMember *, smem, type->Struct.memarr) {
-        if ((*smem)->type->kind == Type_Kind_Struct) {
+    StructMember smem;
+
+    u32 mem_count = type_structlike_mem_count(type);
+    forir (i, mem_count - 1, 0) {
+        type_lookup_member_by_idx(type, i, &smem);
+
+        if (type_is_structlike_strict(smem.type)) {
             if (bh_arr_last(code).type == WI_LOCAL_SET && bh_arr_last(code).data.l == loc_idx) {
                 bh_arr_last(code).type = WI_LOCAL_TEE;
             } else {
                 WIL(WI_LOCAL_GET, loc_idx);
             }
 
-            compile_struct_store(mod, &code, (*smem)->type, offset + (*smem)->offset);
+            compile_struct_store(mod, &code, smem.type, offset + smem.offset);
 
         } else {
-            WasmType wt = onyx_type_to_wasm_type((*smem)->type);
+            WasmType wt = onyx_type_to_wasm_type(smem.type);
             u64 tmp_idx = local_raw_allocate(mod->local_alloc, wt);
 
             WIL(WI_LOCAL_SET, tmp_idx);
             WIL(WI_LOCAL_GET, loc_idx);
             WIL(WI_LOCAL_GET, tmp_idx);
 
-            compile_store_instruction(mod, &code, (*smem)->type, offset + (*smem)->offset);
+            compile_store_instruction(mod, &code, smem.type, offset + smem.offset);
 
             local_raw_free(mod->local_alloc, wt);
         }
@@ -1393,11 +1405,11 @@ COMPILE_FUNC(location, AstTyped* expr) {
         case Ast_Kind_Field_Access: {
             AstFieldAccess* field = (AstFieldAccess *) expr;
 
-            if (field->expr->kind == Ast_Kind_Param && field->expr->type->kind == Type_Kind_Struct) {
+            if (field->expr->kind == Ast_Kind_Param && type_is_structlike_strict(field->expr->type)) {
                 StructMember smem;
 
                 token_toggle_end(field->token);
-                type_struct_lookup_member(field->expr->type, field->token->text, &smem);
+                type_lookup_member(field->expr->type, field->token->text, &smem);
                 token_toggle_end(field->token);
 
                 u64 localidx = bh_imap_get(&mod->local_map, (u64) field->expr) + smem.idx;
@@ -1439,10 +1451,10 @@ COMPILE_FUNC(expression, AstTyped* expr) {
         case Ast_Kind_Param: {
             u64 localidx = bh_imap_get(&mod->local_map, (u64) expr);
 
-            if (expr->type->kind == Type_Kind_Struct) {
-                TypeStruct* st = &expr->type->Struct;
+            if (type_is_structlike_strict(expr->type)) {
+                u32 mem_count = type_structlike_mem_count(expr->type);
 
-                fori (idx, 0, st->mem_count) {
+                fori (idx, 0, mem_count) {
                     WIL(WI_LOCAL_GET, localidx + idx);
                 }
 
@@ -1555,11 +1567,11 @@ COMPILE_FUNC(expression, AstTyped* expr) {
         case Ast_Kind_Field_Access: {
             AstFieldAccess* field = (AstFieldAccess* ) expr;
 
-            if (field->expr->kind == Ast_Kind_Param && field->expr->type->kind == Type_Kind_Struct) {
+            if (field->expr->kind == Ast_Kind_Param && type_is_structlike_strict(field->expr->type)) {
                 StructMember smem;
 
                 token_toggle_end(field->token);
-                type_struct_lookup_member(field->expr->type, field->token->text, &smem);
+                type_lookup_member(field->expr->type, field->token->text, &smem);
                 token_toggle_end(field->token);
 
                 u64 localidx = bh_imap_get(&mod->local_map, (u64) field->expr) + smem.idx;
@@ -1572,7 +1584,7 @@ COMPILE_FUNC(expression, AstTyped* expr) {
                 StructMember smem;
 
                 token_toggle_end(field->token);
-                type_struct_lookup_member(field->expr->type, field->token->text, &smem);
+                type_lookup_member(field->expr->type, field->token->text, &smem);
                 token_toggle_end(field->token);
 
                 if (smem.idx == 0)
@@ -1703,6 +1715,15 @@ COMPILE_FUNC(cast, AstUnaryOp* cast) {
         return;
     }
 
+    if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
+        onyx_message_add(Msg_Type_Literal,
+                cast->token->pos,
+                "cannot cast to or from a slice");
+        WI(WI_DROP);
+        *pcode = code;
+        return;
+    }
+
     if (to->kind == Type_Kind_Function) {
         onyx_message_add(Msg_Type_Literal,
                 cast->token->pos,
@@ -1778,7 +1799,7 @@ COMPILE_FUNC(return, AstReturn* ret) {
 
     if (ret->expr) {
         if (mod->curr_cc == CC_Return_Stack) {
-            if (ret->expr->type->kind == Type_Kind_Struct) {
+            if (type_is_structlike_strict(ret->expr->type)) {
                 compile_expression(mod, &code, ret->expr);
 
                 WIL(WI_LOCAL_GET, mod->stack_base_idx);
@@ -1857,13 +1878,16 @@ static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft) {
     i32 param_count = ft->Function.param_count;
     i32 params_left = param_count;
     while (params_left-- > 0) {
-        if ((*param_type)->kind == Type_Kind_Struct) {
-            bh_arr_each(StructMember *, smem, (*param_type)->Struct.memarr) {
-                *(t++) = (char) onyx_type_to_wasm_type((*smem)->type);
-            }
+        if (type_is_structlike_strict(*param_type)) {
+            u32 mem_count = type_structlike_mem_count(*param_type);
+            StructMember smem;
 
-            param_count += (*param_type)->Struct.mem_count - 1;
+            fori (i, 0, mem_count) {
+                type_lookup_member_by_idx(*param_type, i, &smem);
+                *(t++) = (char) onyx_type_to_wasm_type(smem.type);
+            }
 
+            param_count += mem_count - 1;
         } else {
             *(t++) = (char) onyx_type_to_wasm_type(*param_type);
         }
@@ -1989,9 +2013,9 @@ static void compile_function(OnyxWasmModule* mod, AstFunction* fd) {
         // NOTE: Generate the local map
         u64 localidx = 0;
         bh_arr_each(AstParam, param, fd->params) {
-            if (param->local->type->kind == Type_Kind_Struct) {
+            if (type_is_structlike_strict(param->local->type)) {
                 bh_imap_put(&mod->local_map, (u64) param->local, localidx | LOCAL_IS_WASM);
-                localidx += param->local->type->Struct.mem_count;
+                localidx += type_structlike_mem_count(param->local->type);
 
             } else {
                 bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);