added: parsing, symres, checking of capture blocks
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 19 Apr 2023 02:13:49 +0000 (21:13 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 19 Apr 2023 02:13:49 +0000 (21:13 -0500)
compiler/include/astnodes.h
compiler/src/astnodes.c
compiler/src/checker.c
compiler/src/clone.c
compiler/src/parser.c
compiler/src/symres.c

index 74c6b48a7efca511a6c5b656b8705fbfd98a25ef..c3ffdc3bf9f649a7e0cbcc94cd131d45bd4ff654 100644 (file)
     NODE(DirectiveInsert)      \
     NODE(Macro)                \
                                \
+    NODE(CaptureBlock)         \
+    NODE(CaptureLocal)         \
+                               \
     NODE(ForeignBlock)         \
                                \
     NODE(Package)              \
@@ -231,6 +234,9 @@ typedef enum AstKind {
     Ast_Kind_Macro,
     Ast_Kind_Do_Block,
 
+    Ast_Kind_Capture_Block,
+    Ast_Kind_Capture_Local,
+
     Ast_Kind_Foreign_Block,
 
     Ast_Kind_Zero_Value,
@@ -1332,12 +1338,30 @@ struct AstFunction {
 
     AstBinding *original_binding_to_node;
 
+    AstCaptureBlock *captures;
+
     b32 is_exported        : 1;
     b32 is_foreign         : 1;
     b32 is_foreign_dyncall : 1;
     b32 is_intrinsic       : 1;
 };
 
+struct AstCaptureBlock {
+    AstNode_base;
+
+    bh_arr(AstCaptureLocal *) captures;
+
+    u32 total_size_in_bytes;
+
+    b32 is_legal : 1;
+};
+
+struct AstCaptureLocal {
+    AstTyped_base;
+
+    u32 offset;
+};
+
 struct AstPolyQuery {
     AstNode_base;
 
index 04bf9f646f93297401339083b7192255b0b63119..d97b793ca694e4919d023e2bc4dc4acaf38c7a39 100644 (file)
@@ -108,6 +108,9 @@ static const char* ast_node_names[] = {
     "MACRO",
     "DO BLOCK",
 
+    "CAPTURE BLOCK",
+    "CAPTURE LOCAL",
+
     "FOREIGN BLOCK",
     "ZERO VALUE",
     
index ca4ceb898d41f52cbdd5d143b636750e224cc154..abafdefe3d67201024319b912e3c4a96f1788fcb 100644 (file)
@@ -2251,6 +2251,7 @@ CheckStatus check_expression(AstTyped** pexpr) {
         case Ast_Kind_Switch_Case: break;
         case Ast_Kind_Foreign_Block: break;
         case Ast_Kind_Zero_Value: break;
+        case Ast_Kind_Capture_Local: break;
 
         default:
             retval = Check_Error;
@@ -2419,6 +2420,21 @@ CheckStatus check_directive_export_name(AstDirectiveExportName *ename) {
     return Check_Success;
 }
 
+CheckStatus check_capture_block(AstCaptureBlock *block) {
+    block->total_size_in_bytes = 0;
+
+    bh_arr_each(AstCaptureLocal *, capture, block->captures) {
+        CHECK(expression, (AstTyped **) capture);
+
+        assert((*capture)->type);
+
+        (*capture)->offset = block->total_size_in_bytes;
+        block->total_size_in_bytes += type_size_of((*capture)->type);
+    }
+
+    return Check_Success;
+}
+
 CheckStatus check_statement(AstNode** pstmt) {
     AstNode* stmt = *pstmt;
 
@@ -2549,7 +2565,15 @@ CheckStatus check_function(AstFunction* func) {
     if (for_node_stack) bh_arr_clear(for_node_stack);
 
     if (func->body) {
-        CheckStatus status = check_block(func->body);
+        CheckStatus status = Check_Success;
+        if (func->captures) {
+            status = check_capture_block(func->captures);
+        }
+
+        if (status == Check_Success) {
+            status = check_block(func->body);
+        }
+
         if (status == Check_Error && func->generated_from && context.cycle_detected == 0)
             ERROR(func->generated_from->pos, "Error in polymorphic procedure generated from this location.");
 
index 4070c0b66bdb33418592f81304adc3176383c1a7..d21f0e182ce033c483a4d63ff3f0e8bcb7e8369e 100644 (file)
@@ -117,6 +117,8 @@ static inline i32 ast_kind_to_size(AstNode* node) {
         case Ast_Kind_Directive_First: return sizeof(AstDirectiveFirst);
         case Ast_Kind_Directive_Export_Name: return sizeof(AstDirectiveExportName);
         case Ast_Kind_Import: return sizeof(AstImport);
+        case Ast_Kind_Capture_Block: return sizeof(AstCaptureBlock);
+        case Ast_Kind_Capture_Local: return sizeof(AstCaptureLocal);
         case Ast_Kind_Count: return 0;
     }
 
@@ -594,6 +596,25 @@ AstNode* ast_clone(bh_allocator a, void* n) {
         case Ast_Kind_Directive_Export_Name:
             C(AstDirectiveExportName, func);
             break;
+
+        case Ast_Kind_Capture_Block: {
+            AstCaptureBlock* cd = (AstCaptureBlock *) nn;
+            AstCaptureBlock* cs = (AstCaptureBlock *) node;
+
+            cd->captures = NULL;
+            bh_arr_new(global_heap_allocator, cd->captures, bh_arr_length(cs->captures));
+
+            bh_arr_each(AstCaptureLocal *, expr, cs->captures) {
+                bh_arr_push(cd->captures, (AstCaptureLocal *) ast_clone(a, (AstNode *) *expr));
+            }
+
+            cd->is_legal = 0;
+            break;
+        }
+
+        case Ast_Kind_Capture_Local:
+            C(AstCaptureLocal, type_node);
+            break;
     }
 
     clone_depth--;
index 423f0256188a1aefa70455065bcbda0aeae90352..7aa9c6b18c858d5c3e2c93790d658033eebfdfb3 100644 (file)
@@ -524,6 +524,7 @@ static AstTyped* parse_factor(OnyxParser* parser) {
 
         case Token_Type_Symbol: {
             if (parse_possible_quick_function_definition(parser, &retval)) {
+                retval->flags |= Ast_Flag_Function_Is_Lambda;
                 ENTITY_SUBMIT(retval);
                 break;
             }
@@ -1708,6 +1709,34 @@ static AstNode* parse_statement(OnyxParser* parser) {
                 break;
             }
 
+            if (parse_possible_directive(parser, "capture")) {
+                // :LinearTokenDependent
+                AstCaptureBlock *captures = make_node(AstCaptureBlock, Ast_Kind_Capture_Block);
+                captures->token = parser->curr - 2;
+
+                bh_arr_new(global_heap_allocator, captures->captures, 2);
+
+                expect_token(parser, '{');
+                while (!consume_token_if_next(parser, '}')) {
+                    if (parser->hit_unexpected_token) break;
+
+                    AstCaptureLocal *capture = make_node(AstCaptureLocal, Ast_Kind_Capture_Local);
+                    capture->token = expect_token(parser, Token_Type_Symbol);
+
+                    expect_token(parser, ':');
+                    capture->type_node = parse_type(parser);
+
+                    bh_arr_push(captures->captures, capture);
+
+                    if (peek_token(0)->type != '}')
+                        expect_token(parser, ',');
+                }
+
+                retval = (AstNode *) captures;
+                needs_semicolon = 0;
+                break;
+            }
+
             if (next_tokens_are(parser, 2, '#', Token_Type_Symbol)) {
                 retval = (AstNode *) parse_factor(parser);
                 break;
index 0920ec77ffadc79c31bd3deeb484fa9cee60a256..5cc9cab2923eee5ebffa14c4df47ec903333465b 100644 (file)
@@ -837,6 +837,23 @@ static SymresStatus symres_directive_insert(AstDirectiveInsert* insert) {
     return Symres_Success;
 }
 
+static SymresStatus symres_capture_block(AstCaptureBlock *block) {
+    if (!block->is_legal) {
+        onyx_report_error(block->token->pos, Error_Critical, "#capture block is not allowed here.");
+        return Symres_Error;
+    }
+
+    bh_arr_each(AstCaptureLocal *, capture, block->captures) {
+        SYMRES(type, &(*capture)->type_node);
+    }
+
+    bh_arr_each(AstCaptureLocal *, capture, block->captures) {
+        symbol_introduce(current_scope, (*capture)->token, (AstNode *) *capture);
+    }
+
+    return Symres_Success;
+}
+
 static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
     if (remove) *remove = 0;
 
@@ -864,6 +881,12 @@ static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
             if (remove) *remove = 1;
             break;
 
+        case Ast_Kind_Capture_Block:
+            SYMRES(capture_block, (AstCaptureBlock *) *stmt);
+
+            if (remove) *remove = 1;
+            break;
+
         default: SYMRES(expression, (AstTyped **) stmt); break;
     }
 
@@ -1091,6 +1114,14 @@ SymresStatus symres_function(AstFunction* func) {
         func->flags |= Ast_Flag_Has_Been_Symres;
     }
 
+    if (func->body && func->body->body && func->body->body->kind == Ast_Kind_Capture_Block) {
+        func->captures = (void *) func->body->body;
+
+        if (func->flags & Ast_Flag_Function_Is_Lambda) {
+            ((AstCaptureBlock *) func->body->body)->is_legal = 1;
+        }
+    }
+
     SYMRES(block, func->body);
 
     scope_leave();