From: Brendan Hansen Date: Fri, 28 Aug 2020 13:38:04 +0000 (-0500) Subject: refactored slice types to not be a struct type internally X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=924ad8460e6b94ad5c143871a6bc1eb62a4bcbb8;p=onyx.git refactored slice types to not be a struct type internally --- diff --git a/core/builtin.onyx b/core/builtin.onyx index e0854492..f8e269e5 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -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 index 00000000..b36ea634 --- /dev/null +++ b/core/file.onyx @@ -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 index 00000000..9fc618e0 --- /dev/null +++ b/core/stdio.onyx @@ -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 diff --git a/core/string.onyx b/core/string.onyx index f768239d..933c8b0c 100644 --- a/core/string.onyx +++ b/core/string.onyx @@ -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, }; diff --git a/core/wasi.onyx b/core/wasi.onyx index 5ca3d597..f2af84de 100644 --- a/core/wasi.onyx +++ b/core/wasi.onyx @@ -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(); } diff --git a/docs/api_design b/docs/api_design index d11d4f74..d6c85c8e 100644 --- a/docs/api_design +++ b/docs/api_design @@ -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 diff --git a/docs/plan b/docs/plan index 1a21eaf2..898280e8 100644 --- 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 index 00000000..47fdaf29 --- /dev/null +++ b/docs/polymorphic_plan @@ -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" -> + +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. diff --git a/include/bh.h b/include/bh.h index d1930ca8..0a44d9af 100644 --- a/include/bh.h +++ b/include/bh.h @@ -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 diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h index 512160ae..b18db701 100644 --- a/include/onyxastnodes.h +++ b/include/onyxastnodes.h @@ -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; } diff --git a/include/onyxtypes.h b/include/onyxtypes.h index 38c4595e..7c15348c 100644 --- a/include/onyxtypes.h +++ b/include/onyxtypes.h @@ -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 index 00000000..afd54e0a --- /dev/null +++ b/misc/cloc_onyx_def @@ -0,0 +1,5 @@ +Onyx + filter remove_inline //.*$ + filter call_regexp_common C++ + extension onyx + 3rd_gen_scale 2.30 diff --git a/misc/onyx.sublime-syntax b/misc/onyx.sublime-syntax index 6356d84f..9a6f2e04 100644 --- a/misc/onyx.sublime-syntax +++ b/misc/onyx.sublime-syntax @@ -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 e9ba9510..d2dcc382 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/wasi_test.onyx b/progs/wasi_test.onyx index 567d3434..8c9d72b1 100644 --- a/progs/wasi_test.onyx +++ b/progs/wasi_test.onyx @@ -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 diff --git a/src/onyxchecker.c b/src/onyxchecker.c index b53c61ca..b4ac6fc4 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -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, diff --git a/src/onyxsymres.c b/src/onyxsymres.c index 35f0819e..a715b943 100644 --- a/src/onyxsymres.c +++ b/src/onyxsymres.c @@ -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; } } } diff --git a/src/onyxtypes.c b/src/onyxtypes.c index a3e93e54..762f9975 100644 --- a/src/onyxtypes.c +++ b/src/onyxtypes.c @@ -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 diff --git a/src/onyxwasm.c b/src/onyxwasm.c index a3740b11..e1ca422a 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -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);