added compound declarations for struct members
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 3 Feb 2021 04:26:27 +0000 (22:26 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 3 Feb 2021 04:26:27 +0000 (22:26 -0600)
bin/onyx
onyx.exe
src/onyxparser.c

index 247034e8fa5186074a36282a8b1521dfb725ee63..820da07d1b8a3aa3ece73cacf38c35c291d8a40b 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 4a8203e58d698d8f936efbd94baefc3dbb60e132..2493bbfd15ade518240630170fa367dbcbda4dec 100644 (file)
Binary files a/onyx.exe and b/onyx.exe differ
index d6fc334aa1d4336dde7a40fcb58c54badafe69f4..bb8899c9bf97f7f745b7aa2443e42ea5b285ae60 100644 (file)
@@ -129,6 +129,27 @@ static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type) {
     }
 }
 
+static void consume_tokens(OnyxParser* parser, i32 n) {
+    fori (i, 0, n) consume_token(parser);
+}
+
+static b32 next_tokens_are(OnyxParser* parser, i32 n, ...) {
+    va_list va;
+    va_start(va, n);
+
+    i32 matched = 1;
+
+    fori (i, 0, n) {
+        TokenType expected_type = va_arg(va, TokenType);
+        if (peek_token(i)->type != expected_type) {
+            matched = 0;
+            break;
+        }
+    }
+
+    va_end(va);
+    return matched;
+}
 
 
 // TODO: Make parsing numeric literals not rely on the C standard libary.
@@ -1669,51 +1690,79 @@ static AstStructType* parse_struct(OnyxParser* parser) {
     expect_token(parser, '{');
 
     b32 member_is_used = 0;
+    bh_arr(OnyxToken *) member_list_temp = NULL;
+    bh_arr_new(global_heap_allocator, member_list_temp, 4);
+
     while (!consume_token_if_next(parser, '}')) {
         if (parser->hit_unexpected_token) return s_node;
         
-        OnyxToken* member_name;
-
-        if (consume_token_if_next(parser, Token_Type_Keyword_Use)) member_is_used = 1;
-
-        member_name = expect_token(parser, Token_Type_Symbol);
+        member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
 
-        expect_token(parser, ':');
-        
-        if (parser->curr->type == ':' && !member_is_used) {
+        if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
             if (!s_node->scope) {
                 s_node->scope = scope_create(context.ast_alloc, bh_arr_last(parser->scope_stack), s_node->token->pos);
                 bh_arr_push(parser->scope_stack, s_node->scope);
             }
-            
-            AstBinding* binding = parse_top_level_binding(parser, member_name);
+
+            OnyxToken* binding_name = expect_token(parser, Token_Type_Symbol);
+            consume_token(parser);
+
+            AstBinding* binding = parse_top_level_binding(parser, binding_name);
             symbol_introduce(s_node->scope, binding->token, binding->node);
             
             consume_token_if_next(parser, ';');
-            
+
         } else {
-            AstStructMember* mem = make_node(AstStructMember, Ast_Kind_Struct_Member);    
-            mem->token = member_name;
-            
-            if (member_is_used) {
-                mem->flags |= Ast_Flag_Struct_Mem_Used;
-                member_is_used = 0;
-            }
-            
-            if (parser->curr->type != '=') {
-                mem->type_node = parse_type(parser);
+            bh_arr_clear(member_list_temp);
+            while (!consume_token_if_next(parser, ':')) {
+                bh_arr_push(member_list_temp, expect_token(parser, Token_Type_Symbol));
+
+                if (parser->curr->type != ':')
+                    expect_token(parser, ',');
             }
 
+            AstType* member_type = NULL;
+            if (parser->curr->type != '=')
+                member_type = parse_type(parser);
+
+            AstTyped* initial_value = NULL;
             if (consume_token_if_next(parser, '='))
-                mem->initial_value = parse_expression(parser, 0);
-            
+                initial_value = parse_expression(parser, 0);
+
+            // RECONSIDER: There are seamingly arbitrary limitations put in place here which do two things:
+            //   1. Prevent multiple struct members being used in the same declaration.
+            //      This makes sense because the members will be of the same type, which means
+            //      they have the same members. Using both of the members would immediately result
+            //      in name collisions.
+            //
+            //   2. Prevent multiple struct members having an initializer set for them.
+            //      I think the semantics could be confusing either way, so I'm deciding to leave
+            //      them out of discussion for now. Initialized members should be treated special and
+            //      deserve their own line.
+            if (bh_arr_length(member_list_temp) > 1) {
+                if (member_is_used) onyx_report_error((member_list_temp[0] - 1)->pos, "'use' is only allowed for a single struct member declaration. Try splitting this compound declaration into multiple lines.");
+                if (initial_value)  onyx_report_error(initial_value->token->pos, "Intialized values are only allowed on single struct member declarations. Try splitting this compound initializer into multiple lines.");
+            }
+
+            bh_arr_each(OnyxToken *, member_name, member_list_temp) {
+                AstStructMember* mem = make_node(AstStructMember, Ast_Kind_Struct_Member);    
+                mem->token = *member_name;
+                mem->type_node = member_type;
+                mem->initial_value = initial_value;
+
+                if (member_is_used) mem->flags |= Ast_Flag_Struct_Mem_Used;
+
+                bh_arr_push(s_node->members, mem);
+            }
+
             expect_token(parser, ';');
-            bh_arr_push(s_node->members, mem);
         }
     }
 
     if (s_node->scope) bh_arr_pop(parser->scope_stack);
 
+    bh_arr_free(member_list_temp);
+
     if (poly_struct != NULL) {
         // NOTE: Not a StructType
         return (AstStructType *) poly_struct;