Added initial implementation of structs
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 20 Jul 2020 18:49:18 +0000 (13:49 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 20 Jul 2020 18:49:18 +0000 (13:49 -0500)
14 files changed:
.vimspector.json
include/onyxastnodes.h
include/onyxmsgs.h
include/onyxtypes.h
onyx
progs/structs.onyx [new file with mode: 0644]
src/onyx.c
src/onyxchecker.c
src/onyxmsgs.c
src/onyxparser.c
src/onyxsymres.c
src/onyxtypes.c
src/onyxutils.c
src/onyxwasm.c

index 404230c70b10b310d524800b812d6deafcae12bb..f64d90eb482f11d516831e5c5b4712ede73a95e4 100644 (file)
@@ -6,7 +6,7 @@
                 "type": "cppdbg",
                 "request": "launch",
                 "program": "${workspaceFolder}/onyx",
-                "args": ["progs/arrays.onyx"],
+                "args": ["progs/structs.onyx"],
                 "stopAtEntry": true,
                 "cwd": "${workspaceFolder}",
                 "environment": [],
index 14281d03d99d02bdbbf87a5615a51cdcb80db7b2..f33ef189eee245ee1c121c9c74017a5ebc71a8b1 100644 (file)
@@ -19,6 +19,7 @@ typedef struct AstArgument AstArgument;
 typedef struct AstAddressOf AstAddressOf;
 typedef struct AstDereference AstDereference;
 typedef struct AstArrayAccess AstArrayAccess;
+typedef struct AstFieldAccess AstFieldAccess;
 
 typedef struct AstAssign AstAssign;
 typedef struct AstReturn AstReturn;
@@ -32,6 +33,8 @@ typedef struct AstType AstType;
 typedef struct AstBasicType AstBasicType;
 typedef struct AstPointerType AstPointerType;
 typedef struct AstFunctionType AstFunctionType;
+typedef struct AstStructType AstStructType;
+typedef struct AstStructMember AstStructMember;
 
 typedef struct AstBinding AstBinding;
 typedef struct AstUse AstUse;
@@ -71,8 +74,11 @@ typedef enum AstKind {
     Ast_Kind_Basic_Type,
     Ast_Kind_Pointer_Type,
     Ast_Kind_Function_Type,
+    Ast_Kind_Struct_Type,
     Ast_Kind_Type_End,
 
+    Ast_Kind_Struct_Member,
+
     Ast_Kind_NumLit,
     Ast_Kind_StrLit,
     Ast_Kind_Param,
@@ -83,6 +89,7 @@ typedef enum AstKind {
     Ast_Kind_Address_Of,
     Ast_Kind_Dereference,
     Ast_Kind_Array_Access,
+    Ast_Kind_Field_Access,
 
     Ast_Kind_If,
     Ast_Kind_For,
@@ -97,17 +104,20 @@ typedef enum AstKind {
 // only 32-bits of flags to play with
 typedef enum AstFlags {
     // Top-level flags
-    Ast_Flag_Exported        = BH_BIT(0),
-    Ast_Flag_Foreign         = BH_BIT(1),
-    Ast_Flag_Const           = BH_BIT(2),
-    Ast_Flag_Comptime        = BH_BIT(3),
+    Ast_Flag_Exported         = BH_BIT(0),
+    Ast_Flag_Foreign          = BH_BIT(1),
+    Ast_Flag_Const            = BH_BIT(2),
+    Ast_Flag_Comptime         = BH_BIT(3),
 
     // Function flags
-    Ast_Flag_Inline          = BH_BIT(8),
-    Ast_Flag_Intrinsic       = BH_BIT(9),
+    Ast_Flag_Inline           = BH_BIT(8),
+    Ast_Flag_Intrinsic        = BH_BIT(9),
 
     // Expression flags
-    Ast_Flag_Expr_Ignored    = BH_BIT(8),
+    Ast_Flag_Expr_Ignored     = BH_BIT(8),
+    
+    // Type flags
+    Ast_Flag_Type_Is_Resolved = BH_BIT(8),
 } AstFlags;
 
 typedef enum UnaryOp {
@@ -210,6 +220,7 @@ struct AstArgument      { AstTyped_base; AstTyped *value; };
 struct AstAddressOf     { AstTyped_base; AstTyped *expr; };
 struct AstDereference   { AstTyped_base; AstTyped *expr; };
 struct AstArrayAccess   { AstTyped_base; AstTyped *addr; AstTyped *expr; u64 elem_size; };
+struct AstFieldAccess   { AstTyped_base; AstTyped *expr; u64 offset; };
 
 // Intruction Node
 struct AstReturn        { AstNode_base;  AstTyped* expr; };
@@ -243,13 +254,24 @@ struct AstIf {
 // without the 'next' member. This is because types
 // can't be in expressions so a 'next' thing
 // doesn't make sense.
-#define AstType_members { AstKind kind; u32 flags; char* name; }
+#define AstType_members { AstKind kind; u32 flags; OnyxToken* token; char* name; }
 #define AstType_base struct AstType_members;
 struct AstType AstType_members;
 
 struct AstBasicType     { AstType_base; Type* type; };
 struct AstPointerType   { AstType_base; AstType* elem; };
 struct AstFunctionType  { AstType_base; AstType* return_type; u64 param_count; AstType* params[]; };
+struct AstStructType {
+    AstType_base;
+
+    bh_arr(AstStructMember *) members;
+
+    // NOTE: Used to cache the actual type, since building
+    // a struct type is kind of complicated and should
+    // only happen once.
+    Type *stcache;
+};
+struct AstStructMember { AstTyped_base; u64 offset; };
 
 // Top level nodes
 struct AstBinding       { AstTyped_base; AstNode* node; };
@@ -300,11 +322,14 @@ struct AstOverloadedFunction {
 // processed later down the pipeline.
 typedef enum EntityType {
     Entity_Type_Unknown,
-    Entity_Type_Function,
-    Entity_Type_Overloaded_Function,
-    Entity_Type_Global,
+    Entity_Type_Function_Header,
+    Entity_Type_Global_Header,
+    Entity_Type_Expression,
     Entity_Type_String_Literal,
-    Entity_Type_Expression
+    Entity_Type_Struct,
+    Entity_Type_Global,
+    Entity_Type_Overloaded_Function,
+    Entity_Type_Function,
 } EntityType;
 
 typedef struct Entity {
@@ -316,6 +341,7 @@ typedef struct Entity {
         AstGlobal             *global;
         AstTyped              *expr;
         AstStrLit             *strlit;
+        AstStructType         *struct_type;
     };
 } Entity;
 
@@ -362,7 +388,8 @@ static inline b32 is_lval(AstNode* node) {
     return (node->kind == Ast_Kind_Local)
         || (node->kind == Ast_Kind_Global)
         || (node->kind == Ast_Kind_Dereference)
-        || (node->kind == Ast_Kind_Array_Access);
+        || (node->kind == Ast_Kind_Array_Access)
+        || (node->kind == Ast_Kind_Field_Access);
 }
 
 static inline b32 binop_is_assignment(AstBinaryOp* binop) {
index 9c98c2d096d59ceaaf2140fe62b75a6ae2ee5c29..2af7b721ece169a12291cc456c87175a94c56237 100644 (file)
@@ -28,6 +28,8 @@ typedef enum MsgType {
     Msg_Type_Function_Return_Mismatch,
     Msg_Type_Function_Param_Mismatch,
 
+    Msg_Type_Duplicate_Member,
+
     Msg_Type_Unresolved_Type,
     Msg_Type_Unresolved_Symbol,
 
index 485a0d48cef03916a4109aa7c62ca0e77d734a4b..f7feda572ecd9597d28b96027624454f8c79fe39 100644 (file)
@@ -46,10 +46,16 @@ typedef struct TypeBasic {
 // NOTE: Forward declaration for some of the types below
 typedef struct Type Type;
 
+typedef struct StructMember {
+    u64 offset;
+    Type *type;
+} StructMember;
+
 #define TYPE_KINDS \
     TYPE_KIND(Basic, TypeBasic)                                 \
     TYPE_KIND(Pointer, struct { TypeBasic base; Type *elem; })  \
-    TYPE_KIND(Function, struct { Type *return_type; u64 param_count; Type* params[]; })
+    TYPE_KIND(Function, struct { Type *return_type; u64 param_count; Type* params[]; }) \
+    TYPE_KIND(Struct, struct { char* name; u32 size; u32 mem_count; bh_table(StructMember) members; })
 
 typedef enum TypeKind {
     Type_Kind_Invalid,
@@ -85,13 +91,18 @@ extern Type basic_types[];
 
 struct AstType;
 b32 types_are_compatible(Type* t1, Type* t2);
+u32 type_size_of(Type* type);
 Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node);
 
 Type* type_make_pointer(bh_allocator alloc, Type* to);
 
 const char* type_get_name(Type* type);
+u32 type_get_alignment_log2(Type* type);
+
+StructMember type_struct_lookup_member(Type* type, char* member);
 
 b32 type_is_pointer(Type* type);
+b32 type_is_struct(Type* type);
 b32 type_is_bool(Type* type);
 
 #endif // #ifndef ONYX_TYPES
diff --git a/onyx b/onyx
index c3e5c62f1665c043594d570501cc361a9c825401..31c4540f6fa05819643c6f039f7520aa5d69acfc 100755 (executable)
Binary files a/onyx and b/onyx differ
diff --git a/progs/structs.onyx b/progs/structs.onyx
new file mode 100644 (file)
index 0000000..55c676e
--- /dev/null
@@ -0,0 +1,129 @@
+use "progs/print_funcs"
+use "progs/intrinsics"
+
+Vec3 :: struct {
+    x : f32;
+    y : f32;
+    z : f32;
+}
+
+Foo :: struct {
+    v   : Vec3;
+    i   : i32;
+    l   : i64;
+    f   : f32;
+    d   : f64;
+    foo : ^Foo;
+}
+
+alloc :: proc (size: u32) -> rawptr {
+    heap_u32    :: __heap_start as ^u32;
+
+    curr_offset := *heap_u32;
+    if curr_offset == 0 curr_offset = 8;
+
+    *heap_u32   = curr_offset + size;
+
+    return ((__heap_start as u32) + curr_offset) as rawptr;
+}
+
+foo_make :: proc -> ^Foo {
+    return alloc(40) as ^Foo; 
+}
+
+foo_get :: proc (fooarr: ^Foo, i: i32) -> ^Foo {
+    return ^fooarr[i];
+}
+
+proc #export "main" {
+    // foo := foo_make();
+    // other_foo := foo_make();
+    // other_foo.i = 1234;
+
+    // foo.foo = other_foo;
+    // print(foo.foo.i);
+
+    foo1 := foo_make();
+    foo1.v.y = 123.0f;
+    print(foo1.v.y);
+
+    foo := alloc(40* 5) as ^Foo;
+
+    for i: 1, 6 {
+        foo[i - 1].v.x = (i + 3) as f32;
+        foo[i - 1].v.y = (i + 4) as f32;
+
+        foo[i - 1].i = i;
+        foo[i - 1].l = (i * 10) as i64;
+        foo[i - 1].f = (i * 100) as f32;
+        foo[i - 1].d = (i * 1000) as f64;
+
+        foo[i - 1].foo = foo;
+    }
+
+    print(foo[3].v.x); // 7
+    print(foo[4].i); // 5
+    print(foo[2].d); // 3000.0
+    print(foo[3].f); // 400.0
+    print(foo[0].l); // 10
+
+    print(foo as i32);
+    print((^(foo[3].l)) as i32); // 84
+
+    print(foo[2].foo[4].foo[3].f); // 400.0
+
+    foo_get(foo, 2).f = 1234.5f;
+    print(foo[2].f);
+
+    print(1000000000000);
+    link_test();
+}
+
+Mut1 :: struct {
+    bar : i32;
+    other : Mut2;
+}
+
+Mut2 :: struct {
+    foo : i32;
+    other : ^Foo;
+}
+
+mut_func :: proc #export (mut: ^Mut1) -> ^Foo {
+    return mut.other.other;
+}
+
+
+Link :: struct {
+    data : i32;
+    other_data : i32;
+    next : ^Link;
+}
+
+link_create :: proc (data: i32, parent: ^Link) -> ^Link {
+    link := alloc(12) as ^Link;
+    link.data = data;
+    link.other_data = 1234;
+    link.next = parent;
+    return link;
+}
+
+link_print :: proc (start: ^Link) {
+    walker := start;
+    while (walker as i32) != 0 {
+        print(walker.next.data);
+        walker = walker.next;
+    }
+}
+
+link_test :: proc #export "main2" {
+    dummy :: "H";
+
+    node1 := link_create(1, 0 as ^Link);
+    node2 := link_create(2, node1);
+    node3 := link_create(3, node2);
+    node4 := link_create(4, node3);
+    node5 := link_create(5, node4);
+
+    link_print(node5);
+}
\ No newline at end of file
index a29394415a1c782edb246c2a2d1e7054b222d96d..a19f67aa08f5e77abd00e17b7576af740418e6d5 100644 (file)
@@ -166,6 +166,10 @@ static ParseResults parse_source_file(CompilerState* compiler_state, bh_file_con
     return onyx_parse(&parser);
 }
 
+static i32 sort_entities(const void* e1, const void* e2) {
+    return ((Entity *)e1)->type - ((Entity *)e2)->type;
+}
+
 static void merge_parse_results(CompilerState* compiler_state, ParseResults* results) {
     bh_arr_each(AstUse *, use_node, results->uses) {
         char* formatted_name = bh_aprintf(
@@ -179,39 +183,66 @@ static void merge_parse_results(CompilerState* compiler_state, ParseResults* res
     bh_arr_each(AstBinding *, binding_node, results->bindings)
         bh_arr_push(compiler_state->prog_info.bindings, *binding_node);
 
+    Entity ent;
     bh_arr_each(AstNode *, node, results->nodes_to_process) {
-        Entity ent = { Entity_Type_Unknown };
-
         AstKind nkind = (*node)->kind;
         switch (nkind) {
-            case Ast_Kind_Function:
+            case Ast_Kind_Function: {
+                ent.type     = Entity_Type_Function_Header;
+                ent.function = (AstFunction *) *node;
+                bh_arr_push(compiler_state->prog_info.entities, ent);
+
                 ent.type     = Entity_Type_Function;
                 ent.function = (AstFunction *) *node;
+                bh_arr_push(compiler_state->prog_info.entities, ent);
                 break;
+            }
 
-            case Ast_Kind_Overloaded_Function:
+            case Ast_Kind_Overloaded_Function: {
                 ent.type                = Entity_Type_Overloaded_Function;
                 ent.overloaded_function = (AstOverloadedFunction *) *node;
+                bh_arr_push(compiler_state->prog_info.entities, ent);
                 break;
+            }
+
+            case Ast_Kind_Global: {
+                ent.type   = Entity_Type_Global_Header;
+                ent.global = (AstGlobal *) *node;
+                bh_arr_push(compiler_state->prog_info.entities, ent);
 
-            case Ast_Kind_Global:
                 ent.type   = Entity_Type_Global;
                 ent.global = (AstGlobal *) *node;
+                bh_arr_push(compiler_state->prog_info.entities, ent);
                 break;
+            }
 
-            case Ast_Kind_StrLit:
+            case Ast_Kind_StrLit: {
                 ent.type   = Entity_Type_String_Literal;
                 ent.strlit = (AstStrLit *) *node;
+                bh_arr_push(compiler_state->prog_info.entities, ent);
+                break;
+            }
+
+            case Ast_Kind_Struct_Type: {
+                ent.type = Entity_Type_Struct;
+                ent.struct_type = (AstStructType *) *node;
+                bh_arr_push(compiler_state->prog_info.entities, ent);
                 break;
+            }
 
-            default:
+            default: {
                 ent.type = Entity_Type_Expression;
                 ent.expr = (AstTyped *) *node;
+                bh_arr_push(compiler_state->prog_info.entities, ent);
                 break;
+            }
         }
-
-        bh_arr_push(compiler_state->prog_info.entities, ent);
     }
+
+    qsort(compiler_state->prog_info.entities,
+            bh_arr_length(compiler_state->prog_info.entities),
+            sizeof(Entity),
+            sort_entities);
 }
 
 static CompilerProgress process_source_file(CompilerState* compiler_state, char* filename) {
index d5626dcb1d99d1b5517a2f5a42a7c8ab8b32e615..0ff7d4fb551411fda439d9d0b2a2d33ac7675ef9 100644 (file)
@@ -1,6 +1,7 @@
 #define BH_DEBUG
 #include "onyxsempass.h"
 #include "onyxparser.h"
+#include "onyxutils.h"
 
 #define CHECK(kind, ...) static b32 check_ ## kind (__VA_ARGS__)
 
@@ -17,9 +18,11 @@ CHECK(expression, AstTyped* expr);
 CHECK(address_of, AstAddressOf* aof);
 CHECK(dereference, AstDereference* deref);
 CHECK(array_access, AstArrayAccess* expr);
+CHECK(field_access, AstFieldAccess* field);
 CHECK(global, AstGlobal* global);
 CHECK(function, AstFunction* func);
 CHECK(overloaded_function, AstOverloadedFunction* func);
+CHECK(struct, AstStructType* s_node);
 
 static inline void fill_in_type(AstTyped* node) {
     if (node->type == NULL)
@@ -380,7 +383,8 @@ CHECK(address_of, AstAddressOf* aof) {
     if (check_expression(aof->expr)) return 1;
 
     if (aof->expr->kind != Ast_Kind_Array_Access
-            && aof->expr->kind != Ast_Kind_Dereference) {
+            && aof->expr->kind != Ast_Kind_Dereference
+            && aof->expr->kind != Ast_Kind_Field_Access) {
         onyx_message_add(Msg_Type_Literal,
                 aof->token->pos,
                 "cannot take the address of this");
@@ -434,7 +438,26 @@ CHECK(array_access, AstArrayAccess* aa) {
     }
 
     aa->type = aa->addr->type->Pointer.elem;
-    aa->elem_size = aa->type->Basic.size;
+    aa->elem_size = type_size_of(aa->type);
+
+    return 0;
+}
+
+CHECK(field_access, AstFieldAccess* field) {
+    if (check_expression(field->expr)) return 1;
+
+    if (!type_is_struct(field->expr->type)) {
+        onyx_message_add(Msg_Type_Literal,
+            field->token->pos,
+            "expected expression of kind struct or pointer to struct");
+        return 1;
+    }
+
+    token_toggle_end(field->token);
+    StructMember smem = type_struct_lookup_member(field->expr->type, field->token->text);
+    field->offset = smem.offset;
+    field->type = smem.type;
+    token_toggle_end(field->token);
 
     return 0;
 }
@@ -485,6 +508,7 @@ CHECK(expression, AstTyped* expr) {
         case Ast_Kind_Address_Of:   retval = check_address_of((AstAddressOf *) expr); break;
         case Ast_Kind_Dereference:  retval = check_dereference((AstDereference *) expr); break;
         case Ast_Kind_Array_Access: retval = check_array_access((AstArrayAccess *) expr); break;
+        case Ast_Kind_Field_Access: retval = check_field_access((AstFieldAccess *) expr); break;
 
         case Ast_Kind_Global:
             if (expr->type == NULL) {
@@ -657,6 +681,34 @@ CHECK(overloaded_function, AstOverloadedFunction* func) {
     return 0;
 }
 
+CHECK(struct, AstStructType* s_node) {
+    bh_table(i32) mem_set;
+    bh_table_init(global_heap_allocator, mem_set, bh_arr_length(s_node->members));
+
+    bh_arr_each(AstStructMember *, member, s_node->members) {
+        token_toggle_end((*member)->token);
+
+        if (bh_table_has(i32, mem_set, (*member)->token->text)) {
+            onyx_message_add(Msg_Type_Duplicate_Member,
+                    (*member)->token->pos,
+                    (*member)->token->text);
+
+            token_toggle_end((*member)->token);
+            return 1;
+        }
+
+        bh_table_put(i32, mem_set, (*member)->token->text, 1);
+        token_toggle_end((*member)->token);
+    }
+
+    bh_table_free(mem_set);
+
+    // NOTE: fills in the stcache
+    type_build_from_ast(semstate.allocator, (AstType *) s_node);
+
+    return 0;
+}
+
 CHECK(node, AstNode* node) {
     switch (node->kind) {
         case Ast_Kind_Function:             return check_function((AstFunction *) node);
@@ -694,8 +746,15 @@ void onyx_type_check(ProgramInfo* program) {
                 if (check_expression(entity->expr)) return;
                 break;
 
+            case Entity_Type_Struct:
+                if (check_struct(entity->struct_type)) return;
+                break;
+
             case Entity_Type_String_Literal: break;
 
+            case Entity_Type_Function_Header: break;
+            case Entity_Type_Global_Header: break;
+
             default: DEBUG_HERE; break;
         }
     }
index 2a979dff4d8716843e3e8b80051354acb8aacefd..2df639bfeba8890f05158da17d7c78a9946b1f7b 100644 (file)
@@ -26,6 +26,8 @@ static const char* msg_formats[] = {
     "returning '%s' from function that returns '%s'",
     "function '%b' expected type '%s' in position '%d', got '%s'",
 
+    "duplicate declaration of struct member '%s'",
+
     "unable to resolve type for symbol '%b'",
     "unable to resolve symbol '%b'",
 };
index 03de4daa3c35580c8c90c1107f448efe79a7aa93..fe9ec61d0499bfaae4c4f435b83a131c30bee43e 100644 (file)
@@ -14,22 +14,23 @@ static void unconsume_token(OnyxParser* parser);
 static b32 is_terminating_token(TokenType token_type);
 static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type);
 
-static AstNumLit*   parse_numeric_literal(OnyxParser* parser);
-static AstTyped*    parse_factor(OnyxParser* parser);
-static AstTyped*    parse_expression(OnyxParser* parser);
-static AstIf*       parse_if_stmt(OnyxParser* parser);
-static AstWhile*    parse_while_stmt(OnyxParser* parser);
-static AstFor*      parse_for_stmt(OnyxParser* parser);
-static b32          parse_symbol_declaration(OnyxParser* parser, AstNode** ret);
-static AstReturn*   parse_return_statement(OnyxParser* parser);
-static AstBlock*    parse_block(OnyxParser* parser);
-static AstNode*     parse_statement(OnyxParser* parser);
-static AstType*     parse_type(OnyxParser* parser);
-static AstLocal*    parse_function_params(OnyxParser* parser);
-static AstFunction* parse_function_definition(OnyxParser* parser);
-static AstTyped*    parse_global_declaration(OnyxParser* parser);
-static AstTyped*    parse_top_level_expression(OnyxParser* parser);
-static AstNode*     parse_top_level_statement(OnyxParser* parser);
+static AstNumLit*     parse_numeric_literal(OnyxParser* parser);
+static AstTyped*      parse_factor(OnyxParser* parser);
+static AstTyped*      parse_expression(OnyxParser* parser);
+static AstIf*         parse_if_stmt(OnyxParser* parser);
+static AstWhile*      parse_while_stmt(OnyxParser* parser);
+static AstFor*        parse_for_stmt(OnyxParser* parser);
+static b32            parse_symbol_declaration(OnyxParser* parser, AstNode** ret);
+static AstReturn*     parse_return_statement(OnyxParser* parser);
+static AstBlock*      parse_block(OnyxParser* parser);
+static AstNode*       parse_statement(OnyxParser* parser);
+static AstType*       parse_type(OnyxParser* parser);
+static AstStructType* parse_struct(OnyxParser* parser);
+static AstLocal*      parse_function_params(OnyxParser* parser);
+static AstFunction*   parse_function_definition(OnyxParser* parser);
+static AstTyped*      parse_global_declaration(OnyxParser* parser);
+static AstTyped*      parse_top_level_expression(OnyxParser* parser);
+static AstNode*       parse_top_level_statement(OnyxParser* parser);
 
 static void consume_token(OnyxParser* parser) {
     parser->prev = parser->curr;
@@ -270,27 +271,44 @@ static AstTyped* parse_factor(OnyxParser* parser) {
             return NULL;
     }
 
-    while (parser->curr->type == '[') {
-        AstArrayAccess* aa_node = make_node(AstArrayAccess, Ast_Kind_Array_Access);
-        aa_node->token = expect_token(parser, '[');
-        aa_node->addr  = retval;
-        aa_node->expr  = parse_expression(parser);
+    while (parser->curr->type == '[' || parser->curr->type == Token_Type_Keyword_Cast
+        || parser->curr->type == '.') {
 
-        expect_token(parser, ']');
+        switch ((u16) parser->curr->type) {
+            case '[': {
+                AstArrayAccess* aa_node = make_node(AstArrayAccess, Ast_Kind_Array_Access);
+                aa_node->token = expect_token(parser, '[');
+                aa_node->addr  = retval;
+                aa_node->expr  = parse_expression(parser);
 
-        retval = (AstTyped *) aa_node;
-    }
+                expect_token(parser, ']');
 
-    while (parser->curr->type == Token_Type_Keyword_Cast) {
+                retval = (AstTyped *) aa_node;
+                break;
+            }
 
-        AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
-        cast_node->token = expect_token(parser, Token_Type_Keyword_Cast);
-        cast_node->type_node = parse_type(parser);
-        cast_node->operation = Unary_Op_Cast;
-        cast_node->expr = retval;
-        retval = (AstTyped *) cast_node;
-    }
+            case '.': {
+                consume_token(parser);
+                AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
+                field->token = expect_token(parser, Token_Type_Symbol);
+                field->expr  = retval;
 
+                retval = (AstTyped *) field;
+                break;
+            }
+
+            case Token_Type_Keyword_Cast: {
+                AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+                cast_node->token = expect_token(parser, Token_Type_Keyword_Cast);
+                cast_node->type_node = parse_type(parser);
+                cast_node->operation = Unary_Op_Cast;
+                cast_node->expr = retval;
+
+                retval = (AstTyped *) cast_node;
+                break;
+            }
+        }
+    }
 
     return retval;
 }
@@ -691,9 +709,9 @@ static AstType* parse_type(OnyxParser* parser) {
 
     while (1) {
         if (parser->curr->type == '^') {
-            consume_token(parser);
             AstPointerType* new = make_node(AstPointerType, Ast_Kind_Pointer_Type);
             new->flags |= Basic_Flag_Pointer;
+            new->token = expect_token(parser, '^');
             *next_insertion = (AstType *) new;
             next_insertion = &new->elem;
         }
@@ -722,6 +740,28 @@ static AstType* parse_type(OnyxParser* parser) {
     return root;
 }
 
+static AstStructType* parse_struct(OnyxParser* parser) {
+    AstStructType* s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
+    s_node->token = expect_token(parser, Token_Type_Keyword_Struct);
+
+    bh_arr_new(global_heap_allocator, s_node->members, 4);
+
+    expect_token(parser, '{');
+    while (parser->curr->type != '}') {
+        AstStructMember* mem = make_node(AstStructMember, Ast_Kind_Struct_Member);
+        mem->offset = 0;
+
+        mem->token = expect_token(parser, Token_Type_Symbol);
+        expect_token(parser, ':');
+        mem->type_node = parse_type(parser);
+        expect_token(parser, ';');
+
+        bh_arr_push(s_node->members, mem);
+    }
+
+    return s_node;
+}
+
 // e
 // '(' (<symbol>: <type>,?)* ')'
 static AstLocal* parse_function_params(OnyxParser* parser) {
@@ -935,6 +975,9 @@ static AstTyped* parse_top_level_expression(OnyxParser* parser) {
     else if (parser->curr->type == Token_Type_Keyword_Global) {
         return parse_global_declaration(parser);
     }
+    else if (parser->curr->type == Token_Type_Keyword_Struct) {
+        return (AstTyped *) parse_struct(parser);
+    }
     else {
         return parse_expression(parser);
     }
@@ -944,54 +987,58 @@ static AstTyped* parse_top_level_expression(OnyxParser* parser) {
 // <symbol> :: <expr>
 static AstNode* parse_top_level_statement(OnyxParser* parser) {
     switch (parser->curr->type) {
-        case Token_Type_Keyword_Use:
-            {
-                AstUse* use_node = make_node(AstUse, Ast_Kind_Use);
-                use_node->token = expect_token(parser, Token_Type_Keyword_Use);
-                use_node->filename = expect_token(parser, Token_Type_Literal_String);
+        case Token_Type_Keyword_Use: {
+            AstUse* use_node = make_node(AstUse, Ast_Kind_Use);
+            use_node->token = expect_token(parser, Token_Type_Keyword_Use);
+            use_node->filename = expect_token(parser, Token_Type_Literal_String);
 
-                return (AstNode *) use_node;
-            }
+            return (AstNode *) use_node;
+        }
 
         case Token_Type_Keyword_Proc:
             parse_top_level_expression(parser);
             return NULL;
 
-        case Token_Type_Symbol:
-            {
-                OnyxToken* symbol = parser->curr;
-                consume_token(parser);
+        case Token_Type_Symbol: {
+            OnyxToken* symbol = parser->curr;
+            consume_token(parser);
 
-                expect_token(parser, ':');
-                expect_token(parser, ':');
+            expect_token(parser, ':');
+            expect_token(parser, ':');
 
-                AstTyped* node = parse_top_level_expression(parser);
+            AstTyped* node = parse_top_level_expression(parser);
 
-                if (node->kind == Ast_Kind_Function) {
-                    AstFunction* func = (AstFunction *) node;
+            if (node->kind == Ast_Kind_Function) {
+                AstFunction* func = (AstFunction *) node;
 
-                    if (func->exported_name == NULL)
-                        func->exported_name = symbol;
+                if (func->exported_name == NULL)
+                    func->exported_name = symbol;
 
-                } else if (node->kind == Ast_Kind_Global) {
-                    AstGlobal* global = (AstGlobal *) node;
+            } else if (node->kind == Ast_Kind_Global) {
+                AstGlobal* global = (AstGlobal *) node;
 
-                    if (global->exported_name == NULL)
-                        global->exported_name = symbol;
+                if (global->exported_name == NULL)
+                    global->exported_name = symbol;
 
-                } else if (node->kind != Ast_Kind_Overloaded_Function
-                        && node->kind != Ast_Kind_StrLit) {
-                    // HACK
-                    bh_arr_push(parser->results.nodes_to_process, (AstNode *) node);
-                }
+            } else if (node->kind != Ast_Kind_Overloaded_Function
+                    && node->kind != Ast_Kind_StrLit) {
 
-                AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
-                binding->token = symbol;
-                binding->node = (AstNode *) node;
+                if (node->kind == Ast_Kind_Struct_Type) {
+                    ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
+                        "%b", symbol->text, symbol->length);
+                }
 
-                return (AstNode *) binding;
+                // HACK
+                bh_arr_push(parser->results.nodes_to_process, (AstNode *) node);
             }
 
+            AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+            binding->token = symbol;
+            binding->node = (AstNode *) node;
+
+            return (AstNode *) binding;
+        }
+
         default: break;
     }
 
index 02cff8b6ef8c0889786e191d853f28ea2d3127a8..28a62d69a6f6f3ee7150466f19621b6a2f0b5e42 100644 (file)
@@ -1,19 +1,19 @@
 #define BH_DEBUG
 #include "onyxsempass.h"
 
-AstBasicType basic_type_void   = { { Ast_Kind_Basic_Type, 0, "void"   }, &basic_types[Basic_Kind_Void]  };
-AstBasicType basic_type_bool   = { { Ast_Kind_Basic_Type, 0, "bool"   }, &basic_types[Basic_Kind_Bool]  };
-AstBasicType basic_type_i8     = { { Ast_Kind_Basic_Type, 0, "i8"     }, &basic_types[Basic_Kind_I8]    };
-AstBasicType basic_type_u8     = { { Ast_Kind_Basic_Type, 0, "u8"     }, &basic_types[Basic_Kind_U8]    };
-AstBasicType basic_type_i16    = { { Ast_Kind_Basic_Type, 0, "i16"    }, &basic_types[Basic_Kind_I16]   };
-AstBasicType basic_type_u16    = { { Ast_Kind_Basic_Type, 0, "u16"    }, &basic_types[Basic_Kind_U16]   };
-AstBasicType basic_type_i32    = { { Ast_Kind_Basic_Type, 0, "i32"    }, &basic_types[Basic_Kind_I32]   };
-AstBasicType basic_type_u32    = { { Ast_Kind_Basic_Type, 0, "u32"    }, &basic_types[Basic_Kind_U32]   };
-AstBasicType basic_type_i64    = { { Ast_Kind_Basic_Type, 0, "i64"    }, &basic_types[Basic_Kind_I64]   };
-AstBasicType basic_type_u64    = { { Ast_Kind_Basic_Type, 0, "u64"    }, &basic_types[Basic_Kind_U64]   };
-AstBasicType basic_type_f32    = { { Ast_Kind_Basic_Type, 0, "f32"    }, &basic_types[Basic_Kind_F32]   };
-AstBasicType basic_type_f64    = { { Ast_Kind_Basic_Type, 0, "f64"    }, &basic_types[Basic_Kind_F64]   };
-AstBasicType basic_type_rawptr = { { Ast_Kind_Basic_Type, 0, "rawptr" }, &basic_types[Basic_Kind_Rawptr] };
+AstBasicType basic_type_void   = { { Ast_Kind_Basic_Type, 0, NULL, "void"   }, &basic_types[Basic_Kind_Void]  };
+AstBasicType basic_type_bool   = { { Ast_Kind_Basic_Type, 0, NULL, "bool"   }, &basic_types[Basic_Kind_Bool]  };
+AstBasicType basic_type_i8     = { { Ast_Kind_Basic_Type, 0, NULL, "i8"     }, &basic_types[Basic_Kind_I8]    };
+AstBasicType basic_type_u8     = { { Ast_Kind_Basic_Type, 0, NULL, "u8"     }, &basic_types[Basic_Kind_U8]    };
+AstBasicType basic_type_i16    = { { Ast_Kind_Basic_Type, 0, NULL, "i16"    }, &basic_types[Basic_Kind_I16]   };
+AstBasicType basic_type_u16    = { { Ast_Kind_Basic_Type, 0, NULL, "u16"    }, &basic_types[Basic_Kind_U16]   };
+AstBasicType basic_type_i32    = { { Ast_Kind_Basic_Type, 0, NULL, "i32"    }, &basic_types[Basic_Kind_I32]   };
+AstBasicType basic_type_u32    = { { Ast_Kind_Basic_Type, 0, NULL, "u32"    }, &basic_types[Basic_Kind_U32]   };
+AstBasicType basic_type_i64    = { { Ast_Kind_Basic_Type, 0, NULL, "i64"    }, &basic_types[Basic_Kind_I64]   };
+AstBasicType basic_type_u64    = { { Ast_Kind_Basic_Type, 0, NULL, "u64"    }, &basic_types[Basic_Kind_U64]   };
+AstBasicType basic_type_f32    = { { Ast_Kind_Basic_Type, 0, NULL, "f32"    }, &basic_types[Basic_Kind_F32]   };
+AstBasicType basic_type_f64    = { { Ast_Kind_Basic_Type, 0, NULL, "f64"    }, &basic_types[Basic_Kind_F64]   };
+AstBasicType basic_type_rawptr = { { Ast_Kind_Basic_Type, 0, NULL, "rawptr" }, &basic_types[Basic_Kind_Rawptr] };
 
 AstNumLit builtin_heap_start = { Ast_Kind_NumLit, Ast_Flag_Const, NULL, NULL, (AstType *) &basic_type_rawptr, NULL, 0 };
 
@@ -151,6 +151,19 @@ static AstType* symres_type(AstType* type) {
         return type;
     }
 
+    if (type->kind == Ast_Kind_Struct_Type) {
+        AstStructType* s_node = (AstStructType *) type;
+        if (s_node->flags & Ast_Flag_Type_Is_Resolved) return type;
+
+        s_node->flags |= Ast_Flag_Type_Is_Resolved;
+
+        bh_arr_each(AstStructMember *, member, s_node->members) {
+            (*member)->type_node = symres_type((*member)->type_node);
+        }
+
+        return type;
+    }
+
     assert(("Bad type node", 0));
     return NULL;
 }
@@ -222,6 +235,10 @@ static void symres_expression(AstTyped** expr) {
             symres_expression(&((AstArrayAccess *)(*expr))->expr);
             break;
 
+        case Ast_Kind_Field_Access:
+            symres_expression(&((AstFieldAccess *)(*expr))->expr);
+            break;
+
         default:
             DEBUG_HERE;
             break;
@@ -367,6 +384,7 @@ void onyx_resolve_symbols(ProgramInfo* program) {
             case Entity_Type_Overloaded_Function: symres_overloaded_function(entity->overloaded_function); break;
             case Entity_Type_Global:              symres_global(entity->global); break;
             case Entity_Type_Expression:          symres_expression(&entity->expr); break;
+            case Entity_Type_Struct:              symres_type((AstType *) entity->struct_type); break;
 
             default: break;
         }
index f8e9cd3041d225987dfe206b730d4e441fa56019..f23b880295ddd1e4a047231baea286e9581a47be 100644 (file)
@@ -23,6 +23,56 @@ Type basic_types[] = {
     { Type_Kind_Basic, 0, { Basic_Kind_Rawptr, Basic_Flag_Pointer,                       4, "rawptr" } },
 };
 
+b32 types_are_surface_compatible(Type* t1, Type* t2) {
+    // NOTE: If they are pointing to the same thing,
+    // it is safe to assume they are the same type
+    if (t1 == t2) return 1;
+    if (t1 == NULL || t2 == NULL) return 0;
+
+    switch (t1->kind) {
+        case Type_Kind_Basic:
+            if (t2->kind == Type_Kind_Basic) {
+                // HACK: Not sure if this is right way to check this?
+                if (t1 == t2) return 1;
+
+                if ((t1->Basic.flags & Basic_Flag_Integer) && (t2->Basic.flags & Basic_Flag_Integer)) {
+                    return t1->Basic.size == t2->Basic.size;
+                }
+            }
+            break;
+
+        case Type_Kind_Pointer:
+            if (t2->kind == Type_Kind_Pointer) return 1;
+            break;
+
+        case Type_Kind_Struct: {
+            if (t2->kind != Type_Kind_Struct) return 0;
+            if (t1->Struct.mem_count != t2->Struct.mem_count) return 0;
+            if (strcmp(t1->Struct.name, t2->Struct.name) == 0) return 1;
+
+            b32 works = 1;
+            bh_table_each_start(StructMember, t1->Struct.members);
+                if (!bh_table_has(StructMember, t2->Struct.members, (char *) key)) return 0;
+                StructMember other = bh_table_get(StructMember, t2->Struct.members, (char *) key);
+                if (other.offset != value.offset) return 0;
+
+                if (!types_are_compatible(value.type, other.type)) {
+                    works = 0;
+                    break;
+                }
+            bh_table_each_end;
+
+            return works;
+        }
+
+        default:
+            assert(("Invalid type", 0));
+            break;
+    }
+
+    return 0;
+}
+
 b32 types_are_compatible(Type* t1, Type* t2) {
     // NOTE: If they are pointing to the same thing,
     // it is safe to assume they are the same type
@@ -47,6 +97,26 @@ b32 types_are_compatible(Type* t1, Type* t2) {
             }
             break;
 
+        case Type_Kind_Struct: {
+            if (t2->kind != Type_Kind_Struct) return 0;
+            if (t1->Struct.mem_count != t2->Struct.mem_count) return 0;
+            if (strcmp(t1->Struct.name, t2->Struct.name) == 0) return 1;
+
+            b32 works = 1;
+            bh_table_each_start(StructMember, t1->Struct.members);
+                if (!bh_table_has(StructMember, t2->Struct.members, (char *) key)) return 0;
+                StructMember other = bh_table_get(StructMember, t2->Struct.members, (char *) key);
+                if (other.offset != value.offset) return 0;
+
+                if (!types_are_surface_compatible(value.type, other.type)) {
+                    works = 0;
+                    break;
+                }
+            bh_table_each_end;
+
+            return works;
+        }
+
         default:
             assert(("Invalid type", 0));
             break;
@@ -55,6 +125,18 @@ b32 types_are_compatible(Type* t1, Type* t2) {
     return 0;
 }
 
+u32 type_size_of(Type* type) {
+    if (type == NULL) return 0;
+
+    switch (type->kind) {
+        case Type_Kind_Basic:    return type->Basic.size;
+        case Type_Kind_Pointer:  return 4;
+        case Type_Kind_Function: return 0;
+        case Type_Kind_Struct:   return type->Struct.size;
+        default:                 return 0;
+    }
+}
+
 Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
     if (type_node == NULL) return NULL;
 
@@ -81,9 +163,50 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
             return func_type;
         }
 
+        case Ast_Kind_Struct_Type: {
+            AstStructType* s_node = (AstStructType *) type_node;
+            if (s_node->stcache != NULL) return s_node->stcache;
+
+            Type* s_type = bh_alloc(alloc, sizeof(Type));
+            s_node->stcache = s_type;
+            s_type->kind = Type_Kind_Struct;
+
+            s_type->Struct.name = s_node->name;
+            s_type->Struct.mem_count = bh_arr_length(s_node->members);
+            bh_table_init(global_heap_allocator, s_type->Struct.members, s_type->Struct.mem_count);
+
+            u32 offset = 0;
+            u32 min_alignment = 1;
+            bh_arr_each(AstStructMember *, member, s_node->members) {
+                (*member)->type = type_build_from_ast(alloc, (*member)->type_node);
+
+                // TODO: Add alignment checking here
+
+                StructMember smem = {
+                    .offset = offset,
+                    .type = (*member)->type
+                };
+
+                token_toggle_end((*member)->token);
+                bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem);
+                token_toggle_end((*member)->token);
+
+                offset += type_size_of((*member)->type);
+            }
+
+            // TODO: Again, add alignment
+            s_type->Struct.size = offset;
+
+            return s_type;
+        }
+
         case Ast_Kind_Basic_Type:
             return ((AstBasicType *) type_node)->type;
 
+        case Ast_Kind_Symbol:
+            assert(("symbol node in type expression", 0));
+            return NULL;
+
         default:
             assert(("Node is not a type node", 0));
             return NULL;
@@ -107,14 +230,50 @@ const char* type_get_name(Type* type) {
     switch (type->kind) {
         case Type_Kind_Basic: return type->Basic.name;
         case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem));
+        case Type_Kind_Struct: return type->Struct.name;
         default: return "unknown";
     }
 }
 
+u32 type_get_alignment_log2(Type* type) {
+    i32 store_size = type_size_of(type);
+    i32 is_integer = (type->Basic.flags & Basic_Flag_Integer)
+                  || (type->Basic.flags & Basic_Flag_Pointer);
+
+    if (is_integer) {
+        if      (store_size == 1) return 0;
+        else if (store_size == 2) return 1;
+        else if (store_size == 4) return 2;
+        else if (store_size == 8) return 3;
+    } else {
+        if      (store_size == 4) return 2;
+        else if (store_size == 8) return 3;
+    }
+
+    return 2;
+}
+
+StructMember type_struct_lookup_member(Type* type, char* member) {
+    if (!type_is_struct(type)) return (StructMember) { 0 };
+    if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
+
+    TypeStruct* stype = &type->Struct;
+
+    if (!bh_table_has(StructMember, stype->members, member)) return (StructMember) { 0 };
+    StructMember smem = bh_table_get(StructMember, stype->members, member);
+    return smem;
+}
+
 b32 type_is_pointer(Type* type) {
     return type->kind == Type_Kind_Pointer || (type->Basic.flags & Basic_Flag_Pointer) != 0;
 }
 
+b32 type_is_struct(Type* type) {
+    if (type->kind == Type_Kind_Struct) return 1;
+    if (type->kind == Type_Kind_Pointer && type->Pointer.elem->kind == Type_Kind_Struct) return 1;
+    return 0;
+}
+
 b32 type_is_bool(Type* type) {
     return type != NULL && type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Bool;
 }
index 541accf5011b333a6a86fc32897a6cd72a563057..edc56e130dd6d44c24044b3570515d525cc60872 100644 (file)
@@ -26,11 +26,14 @@ static const char* ast_node_names[] = {
     "UN_OP",
     "BIN_OP",
 
-    "TYPE_START (BAD)"
+    "TYPE_START (BAD)",
     "TYPE",
     "POINTER_TYPE",
     "FUNCTION_TYPE",
-    "TYPE_END (BAD)"
+    "STRUCT TYPE",
+    "TYPE_END (BAD)",
+
+    "STRUCT MEMBER",
 
     "NUMERIC LITERAL",
     "STRING LITERAL",
@@ -41,6 +44,7 @@ static const char* ast_node_names[] = {
     "ADDRESS OF",
     "DEREFERENCE",
     "ARRAY_ACCESS",
+    "FIELD_ACCESS",
 
     "IF",
     "FOR",
index b5a361d2df1708350f069881adbe699286b42611..cc3e89e5855ccf328d54f61382800f18dc793f98 100644 (file)
@@ -227,20 +227,24 @@ static WasmType onyx_type_to_wasm_type(Type* type) {
 #define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data }))
 #define COMPILE_FUNC(kind, ...) static void compile_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__)
 
-COMPILE_FUNC(function_body,  AstFunction* fd);
-COMPILE_FUNC(block,          AstBlock* block);
-COMPILE_FUNC(statement,      AstNode* stmt);
-COMPILE_FUNC(assignment,     AstBinaryOp* assign);
-COMPILE_FUNC(if,             AstIf* if_node);
-COMPILE_FUNC(while,          AstWhile* while_node);
-COMPILE_FUNC(for,            AstFor* for_node);
-COMPILE_FUNC(binop,          AstBinaryOp* binop);
-COMPILE_FUNC(unaryop,        AstUnaryOp* unop);
-COMPILE_FUNC(call,           AstCall* call);
-COMPILE_FUNC(intrinsic_call, AstIntrinsicCall* call);
-COMPILE_FUNC(expression,     AstTyped* expr);
-COMPILE_FUNC(cast,           AstUnaryOp* cast);
-COMPILE_FUNC(return,         AstReturn* ret);
+COMPILE_FUNC(function_body,         AstFunction* fd);
+COMPILE_FUNC(block,                 AstBlock* block);
+COMPILE_FUNC(statement,             AstNode* stmt);
+COMPILE_FUNC(assignment,            AstBinaryOp* assign);
+COMPILE_FUNC(store_instruction,     Type* type, u32 alignment, u32 offset);
+COMPILE_FUNC(load_instruction,      Type* type, u32 offset);
+COMPILE_FUNC(if,                    AstIf* if_node);
+COMPILE_FUNC(while,                 AstWhile* while_node);
+COMPILE_FUNC(for,                   AstFor* for_node);
+COMPILE_FUNC(binop,                 AstBinaryOp* binop);
+COMPILE_FUNC(unaryop,               AstUnaryOp* unop);
+COMPILE_FUNC(call,                  AstCall* call);
+COMPILE_FUNC(intrinsic_call,        AstIntrinsicCall* call);
+COMPILE_FUNC(array_access_location, AstArrayAccess* aa);
+COMPILE_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return);
+COMPILE_FUNC(expression,            AstTyped* expr);
+COMPILE_FUNC(cast,                  AstUnaryOp* cast);
+COMPILE_FUNC(return,                AstReturn* ret);
 
 COMPILE_FUNC(function_body, AstFunction* fd) {
     if (fd->body == NULL) return;
@@ -337,46 +341,32 @@ COMPILE_FUNC(assignment, AstBinaryOp* assign) {
         compile_expression(mod, &code, deref->expr);
         compile_expression(mod, &code, assign->right);
 
-        i32 store_size = deref->type->Basic.size;
-        i32 is_integer = (deref->type->Basic.flags & Basic_Flag_Integer)
-                      || (deref->type->Basic.flags & Basic_Flag_Pointer);
-
-        if (is_integer) {
-            if      (store_size == 1)   WID(WI_I32_STORE_8,  ((WasmInstructionData) { 0, 0 }));
-            else if (store_size == 2)   WID(WI_I32_STORE_16, ((WasmInstructionData) { 1, 0 }));
-            else if (store_size == 4)   WID(WI_I32_STORE,    ((WasmInstructionData) { 2, 0 }));
-            else if (store_size == 8)   WID(WI_I64_STORE,    ((WasmInstructionData) { 3, 0 }));
-        } else {
-            if      (store_size == 4)   WID(WI_F32_STORE, ((WasmInstructionData) { 2, 0 }));
-            else if (store_size == 8)   WID(WI_F64_STORE, ((WasmInstructionData) { 3, 0 }));
-        }
+        compile_store_instruction(mod, &code,
+                deref->type,
+                type_get_alignment_log2(deref->type),
+                0);
 
     } else if (lval->kind == Ast_Kind_Array_Access) {
         AstArrayAccess* aa = (AstArrayAccess *) lval;
-        compile_expression(mod, &code, aa->expr);
-        if (aa->elem_size != 1) {
-            WID(WI_I32_CONST, aa->elem_size);
-            WI(WI_I32_MUL);
-        }
-        compile_expression(mod, &code, aa->addr);
-        WI(WI_I32_ADD);
-
+        compile_array_access_location(mod, &code, aa);
         compile_expression(mod, &code, assign->right);
 
-        i32 store_size = aa->type->Basic.size;
-        i32 is_integer = (aa->type->Basic.flags & Basic_Flag_Integer)
-                      || (aa->type->Basic.flags & Basic_Flag_Pointer);
+        compile_store_instruction(mod, &code,
+                aa->type,
+                type_get_alignment_log2(aa->type),
+                0);
 
-        if (is_integer) {
-            if      (store_size == 1)   WID(WI_I32_STORE_8,  ((WasmInstructionData) { 0, 0 }));
-            else if (store_size == 2)   WID(WI_I32_STORE_16, ((WasmInstructionData) { 1, 0 }));
-            else if (store_size == 4)   WID(WI_I32_STORE,    ((WasmInstructionData) { 2, 0 }));
-            else if (store_size == 8)   WID(WI_I64_STORE,    ((WasmInstructionData) { 3, 0 }));
-        } else {
-            if      (store_size == 4)   WID(WI_F32_STORE, ((WasmInstructionData) { 2, 0 }));
-            else if (store_size == 8)   WID(WI_F64_STORE, ((WasmInstructionData) { 3, 0 }));
-        }
+    } else if (lval->kind == Ast_Kind_Field_Access) {
+        AstFieldAccess* field = (AstFieldAccess *) lval;
 
+        u64 offset = 0;
+        compile_field_access_location(mod, &code, field, &offset);
+        compile_expression(mod, &code, assign->right);
+
+        compile_store_instruction(mod, &code,
+                field->type,
+                type_get_alignment_log2(field->type),
+                offset);
     } else {
         assert(("Invalid lval", 0));
     }
@@ -384,6 +374,60 @@ COMPILE_FUNC(assignment, AstBinaryOp* assign) {
     *pcode = code;
 }
 
+COMPILE_FUNC(store_instruction, Type* type, u32 alignment, u32 offset) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    i32 store_size = type_size_of(type);
+    i32 is_integer = (type->Basic.flags & Basic_Flag_Integer)
+                  || (type->Basic.flags & Basic_Flag_Pointer);
+
+    if (is_integer) {
+        if      (store_size == 1)   WID(WI_I32_STORE_8,  ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 2)   WID(WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 4)   WID(WI_I32_STORE,    ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 8)   WID(WI_I64_STORE,    ((WasmInstructionData) { alignment, offset }));
+    } else {
+        if      (store_size == 4)   WID(WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
+        else if (store_size == 8)   WID(WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
+    }
+
+    *pcode = code;
+}
+
+COMPILE_FUNC(load_instruction, Type* type, u32 offset) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    i32 load_size   = type_size_of(type);
+    i32 is_integer  = (type->Basic.flags & Basic_Flag_Integer)
+                   || (type->Basic.flags & Basic_Flag_Pointer);
+    i32 is_unsigned = type->Basic.flags & Basic_Flag_Unsigned;
+
+    WasmInstructionType instr = WI_NOP;
+    i32 alignment = type_get_alignment_log2(type);
+
+    if (is_integer) {
+        if      (load_size == 1) instr = WI_I32_LOAD_8_S;
+        else if (load_size == 2) instr = WI_I32_LOAD_16_S;
+        else if (load_size == 4) instr = WI_I32_LOAD;
+        else if (load_size == 8) instr = WI_I64_LOAD;
+
+        if (load_size < 4 && is_unsigned) instr += 1;
+    } else {
+        if      (load_size == 4) instr = WI_F32_LOAD;
+        else if (load_size == 8) instr = WI_F64_LOAD;
+    }
+
+    WID(instr, ((WasmInstructionData) { alignment, offset }));
+
+    if (instr == WI_NOP) {
+        onyx_message_add(Msg_Type_Literal,
+                (OnyxFilePos) { 0 },
+                "failed to generate load instruction");
+    }
+
+    *pcode = code;
+}
+
 COMPILE_FUNC(if, AstIf* if_node) {
     bh_arr(WasmInstruction) code = *pcode;
 
@@ -703,6 +747,42 @@ COMPILE_FUNC(intrinsic_call, AstIntrinsicCall* call) {
     *pcode = code;
 }
 
+COMPILE_FUNC(array_access_location, AstArrayAccess* aa) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    compile_expression(mod, &code, aa->expr);
+    if (aa->elem_size != 1) {
+        WID(WI_I32_CONST, aa->elem_size);
+        WI(WI_I32_MUL);
+    }
+    compile_expression(mod, &code, aa->addr);
+    WI(WI_I32_ADD);
+
+    *pcode = code;
+}
+
+COMPILE_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    u64 offset = field->offset;
+    AstTyped* source_expr = field->expr;
+    while (source_expr->kind == Ast_Kind_Field_Access
+            && (source_expr->type->kind == Type_Kind_Struct)) {
+        offset += ((AstFieldAccess *) source_expr)->offset;
+        source_expr = (AstTyped *) ((AstFieldAccess *) source_expr)->expr;
+    }
+
+    if (source_expr->kind == Ast_Kind_Array_Access) {
+        compile_array_access_location(mod, &code, (AstArrayAccess *) source_expr);
+    } else {
+        compile_expression(mod, &code, source_expr);
+    }
+
+    *offset_return = offset;
+
+    *pcode = code;
+}
+
 COMPILE_FUNC(expression, AstTyped* expr) {
     bh_arr(WasmInstruction) code = *pcode;
 
@@ -767,13 +847,15 @@ COMPILE_FUNC(expression, AstTyped* expr) {
 
                 case Ast_Kind_Array_Access: {
                     AstArrayAccess* aa = (AstArrayAccess *) aof->expr;
+                    compile_array_access_location(mod, &code, aa);
+                    break;
+                }
 
-                    compile_expression(mod, &code, aa->expr);
-                    if (aa->elem_size != 1) {
-                        WID(WI_I32_CONST, aa->elem_size);
-                        WI(WI_I32_MUL);
-                    }
-                    compile_expression(mod, &code, aa->addr);
+                case Ast_Kind_Field_Access: {
+                    AstFieldAccess* field = (AstFieldAccess *) aof->expr;
+                    u64 offset = 0;
+                    compile_field_access_location(mod, &code, field, &offset);
+                    WID(WI_I32_CONST, offset);
                     WI(WI_I32_ADD);
                     break;
                 }
@@ -790,72 +872,23 @@ COMPILE_FUNC(expression, AstTyped* expr) {
         case Ast_Kind_Dereference: {
             AstDereference* deref = (AstDereference *) expr;
             compile_expression(mod, &code, deref->expr);
-
-            i32 load_size   = deref->type->Basic.size;
-            i32 is_integer  = (deref->type->Basic.flags & Basic_Flag_Integer)
-                           || (deref->type->Basic.flags & Basic_Flag_Pointer);
-            i32 is_unsigned = deref->type->Basic.flags & Basic_Flag_Unsigned;
-
-            WasmInstructionType instr = WI_NOP;
-            i32 alignment = log2_dumb(load_size);
-
-            if (is_integer) {
-                if      (load_size == 1) instr = WI_I32_LOAD_8_S;
-                else if (load_size == 2) instr = WI_I32_LOAD_16_S;
-                else if (load_size == 4) instr = WI_I32_LOAD;
-                else if (load_size == 8) instr = WI_I64_LOAD;
-
-                if (load_size < 4 && is_unsigned) instr += 1;
-            } else {
-                if      (load_size == 4) instr = WI_F32_LOAD;
-                else if (load_size == 8) instr = WI_F64_LOAD;
-            }
-
-            if (instr != WI_NOP) {
-                WID(instr, ((WasmInstructionData) { alignment, 0 }));
-            } else {
-                DEBUG_HERE;
-            }
-
+            compile_load_instruction(mod, &code, deref->type, 0);
             break;
         }
 
         case Ast_Kind_Array_Access: {
             AstArrayAccess* aa = (AstArrayAccess *) expr;
-            compile_expression(mod, &code, aa->expr);
-            if (aa->elem_size != 1) {
-                WID(WI_I32_CONST, aa->elem_size);
-                WI(WI_I32_MUL);
-            }
-            compile_expression(mod, &code, aa->addr);
-            WI(WI_I32_ADD);
-
-            i32 load_size   = aa->type->Basic.size;
-            i32 is_integer  = (aa->type->Basic.flags & Basic_Flag_Integer)
-                           || (aa->type->Basic.flags & Basic_Flag_Pointer);
-            i32 is_unsigned = aa->type->Basic.flags & Basic_Flag_Unsigned;
-
-            WasmInstructionType instr = WI_NOP;
-            i32 alignment = log2_dumb(load_size);
-
-            if (is_integer) {
-                if      (load_size == 1) instr = WI_I32_LOAD_8_S;
-                else if (load_size == 2) instr = WI_I32_LOAD_16_S;
-                else if (load_size == 4) instr = WI_I32_LOAD;
-                else if (load_size == 8) instr = WI_I64_LOAD;
-
-                if (load_size < 4 && is_unsigned) instr += 1;
-            } else {
-                if      (load_size == 4) instr = WI_F32_LOAD;
-                else if (load_size == 8) instr = WI_F64_LOAD;
-            }
+            compile_array_access_location(mod, &code, aa);
+            compile_load_instruction(mod, &code, aa->type, 0);
+            break;
+        }
 
-            if (instr != WI_NOP) {
-                WID(instr, ((WasmInstructionData) { alignment, 0 }));
-            } else {
-                DEBUG_HERE;
-            }
+        case Ast_Kind_Field_Access: {
+            AstFieldAccess* field = (AstFieldAccess* ) expr;
 
+            u64 offset = 0;
+            compile_field_access_location(mod, &code, field, &offset);
+            compile_load_instruction(mod, &code, field->type, offset);
             break;
         }
 
@@ -1225,10 +1258,9 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ProgramInfo* program) {
     module->next_func_idx   = program->foreign_func_count;
     module->next_global_idx = program->foreign_global_count;
 
-    // NOTE: First, assign indicies to all functions / globals
     bh_arr_each(Entity, entity, program->entities) {
         switch (entity->type) {
-            case Entity_Type_Function: {
+            case Entity_Type_Function_Header: {
                 if (entity->function->flags & Ast_Flag_Intrinsic) break;
 
                 u64 func_idx;
@@ -1241,7 +1273,7 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ProgramInfo* program) {
                 break;
             }
 
-            case Entity_Type_Global: {
+            case Entity_Type_Global_Header: {
                 u64 global_idx;
                 if ((entity->global->flags & Ast_Flag_Foreign) != 0)
                     global_idx = module->next_foreign_global_idx++;
@@ -1254,23 +1286,19 @@ void onyx_wasm_module_compile(OnyxWasmModule* module, ProgramInfo* program) {
 
             case Entity_Type_String_Literal: {
                 compile_string_literal(module, (AstStrLit *) entity->strlit);
-            }
 
-            default: break;
-        }
-    }
-
-    // NOTE: Round up to the nearest multiple of 16
-    builtin_heap_start.value.i =
-        (module->next_datum_offset & 15)
-        ? ((module->next_datum_offset >> 4) + 1) << 4
-        : module->next_datum_offset;
+                // HACK: To put this here
+                // NOTE: Round up to the nearest multiple of 16
+                builtin_heap_start.value.i =
+                    (module->next_datum_offset & 15)
+                    ? ((module->next_datum_offset >> 4) + 1) << 4
+                    : module->next_datum_offset;
+                break;
+            }
 
-    // NOTE: Then, compile everything
-    bh_arr_each(Entity, entity, program->entities) {
-        switch (entity->type) {
             case Entity_Type_Function: compile_function(module, entity->function); break;
             case Entity_Type_Global:   compile_global(module,   entity->global); break;
+
             default: break;
         }
     }