added '#first' for for-loops
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 8 Sep 2022 23:51:34 +0000 (18:51 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 8 Sep 2022 23:51:34 +0000 (18:51 -0500)
compiler/include/astnodes.h
compiler/src/astnodes.c
compiler/src/checker.c
compiler/src/clone.c
compiler/src/parser.c
compiler/src/wasm_emit.c
core/io/stream.onyx

index 0052d233c4b424f8e53befaec5126b7ff75f2327..0ad33558f3ac64dc14aabfb0efbc62708bf8fd4b 100644 (file)
@@ -42,6 +42,7 @@
     NODE(DirectiveInit)        \
     NODE(DirectiveLibrary)     \
     NODE(DirectiveRemove)      \
+    NODE(DirectiveFirst)       \
                                \
     NODE(Return)               \
     NODE(Jump)                 \
@@ -217,6 +218,7 @@ typedef enum AstKind {
     Ast_Kind_Directive_Init,
     Ast_Kind_Directive_Library,
     Ast_Kind_Directive_Remove,
+    Ast_Kind_Directive_First,
     Ast_Kind_Call_Site,
 
     Ast_Kind_Code_Block,
@@ -794,6 +796,11 @@ struct AstFor           {
     // But for now, this will do.
     b32 by_pointer : 1;
     b32 no_close   : 1;
+    b32 has_first  : 1;
+
+    // NOTE: This is used by the AstDirectiveFirst node for this
+    // for node to know which local variable to use.
+    u64 first_local;
 };
 struct AstIfWhile {
     AstNode_base;
@@ -1312,6 +1319,11 @@ struct AstDirectiveRemove {
     AstNode_base;
 };
 
+struct AstDirectiveFirst {
+    AstTyped_base;
+    AstFor *for_node;
+};
+
 struct AstNote {
     AstNode_base;
 };
index f2c6faf2bec8cb48bb894fef819ba65dea42eb70..fc6e708572e34a28aeffab7c1423f85ae5813b4b 100644 (file)
@@ -98,6 +98,7 @@ static const char* ast_node_names[] = {
     "INIT",
     "LIBRARY",
     "REMOVE",
+    "FIRST",
     "CALL SITE",
 
     "CODE BLOCK",
index f5c4a978d408008cf56d6de51a7cd54ce94ed5b6..f32be5d2dc14dd3efc00026484e930d4d3bf4ad2 100644 (file)
@@ -108,11 +108,13 @@ CheckStatus check_do_block(AstDoBlock** pdoblock);
 CheckStatus check_constraint(AstConstraint *constraint);
 CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFilePos pos);
 CheckStatus check_polyquery(AstPolyQuery *query);
+CheckStatus check_directive_first(AstDirectiveFirst *first);
 
 // HACK HACK HACK
 b32 expression_types_must_be_known = 0;
 b32 all_checks_are_final           = 1;
 b32 inside_for_iterator            = 0;
+bh_arr(AstFor *) for_node_stack    = NULL;
 
 #define STATEMENT_LEVEL 1
 #define EXPRESSION_LEVEL 2
@@ -324,7 +326,10 @@ CheckStatus check_for(AstFor* fornode) {
 
     fornode->flags |= Ast_Flag_Has_Been_Checked;
 
+
 fornode_expr_checked:
+    bh_arr_push(for_node_stack, fornode);
+
     old_inside_for_iterator = inside_for_iterator;
     inside_for_iterator = 0;
     iter_type = fornode->iter->type;
@@ -338,6 +343,7 @@ fornode_expr_checked:
         if (cs > Check_Errors_Start) return cs; 
     } while(0);
 
+    bh_arr_pop(for_node_stack);
     return Check_Success;
 }
 
@@ -1956,6 +1962,10 @@ CheckStatus check_expression(AstTyped** pexpr) {
             if (expr->type == NULL) YIELD(expr->token->pos, "Waiting to know globals type.");
             break;
 
+        case Ast_Kind_Directive_First:
+            CHECK(directive_first, (AstDirectiveFirst *) expr);
+            break;
+
         case Ast_Kind_StrLit: break;
         case Ast_Kind_File_Contents: break;
         case Ast_Kind_Overloaded_Function: break;
@@ -2058,6 +2068,19 @@ CheckStatus check_remove_directive(AstDirectiveRemove *remove) {
     return Check_Success;
 }
 
+CheckStatus check_directive_first(AstDirectiveFirst *first) {
+    if (bh_arr_length(for_node_stack) == 0) {
+        ERROR(first->token->pos, "#first is only allowed in the body of a for-loop.");
+    }
+
+    first->for_node = bh_arr_last(for_node_stack);
+    assert(first->for_node);
+
+    first->for_node->has_first = 1;
+
+    return Check_Success;
+}
+
 CheckStatus check_statement(AstNode** pstmt) {
     AstNode* stmt = *pstmt;
 
@@ -2171,8 +2194,11 @@ CheckStatus check_function(AstFunction* func) {
         }
     }
 
-    inside_for_iterator = 0;
     expected_return_type = &func->type->Function.return_type;
+
+    inside_for_iterator = 0;
+    if (for_node_stack) bh_arr_clear(for_node_stack);
+
     if (func->body) {
         CheckStatus status = check_block(func->body);
         if (status == Check_Error && func->generated_from && context.cycle_detected == 0)
index 828f6613ecd1a901f08e05643b7655187210889c..2c3a49fc729e6f533561957c7d76ad3381e6b2a3 100644 (file)
@@ -113,6 +113,7 @@ static inline i32 ast_kind_to_size(AstNode* node) {
         case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
         case Ast_Kind_Constraint: return sizeof(AstConstraint);
         case Ast_Kind_Directive_Remove: return sizeof(AstDirectiveRemove);
+        case Ast_Kind_Directive_First: return sizeof(AstDirectiveFirst);
         case Ast_Kind_Count: return 0;
     }
 
index e62459dc160f730d608c65e2d67693b82d914649..e8d74118f326ed67bd969eff14179ff506565253 100644 (file)
@@ -756,6 +756,14 @@ static AstTyped* parse_factor(OnyxParser* parser) {
                 retval = (AstTyped *) str_node;
                 break;
             }
+            else if (parse_possible_directive(parser, "first")) {
+                AstDirectiveFirst *first = make_node(AstDirectiveFirst, Ast_Kind_Directive_First);
+                first->token = parser->curr - 1;
+                first->type_node = (AstType *) &basic_type_bool;
+
+                retval = (AstTyped *) first;
+                break;
+            }
 
             onyx_report_error(parser->curr->pos, Error_Critical, "Invalid directive in expression.");
             return NULL;
@@ -3299,6 +3307,13 @@ static void parse_top_level_statement(OnyxParser* parser) {
                 parser->tag_depth -= 1;
                 return;
             }
+            else if (parse_possible_directive(parser, "doc")) {
+                // This is a future feature I want to add to the language, proper docstrings.
+                // For now (and so I start documenting thing...), #doc can be used anywhere
+                // at top level, followed by a string to add a doc string.
+                expect_token(parser, Token_Type_Literal_String);
+                return;
+            }
             else {
                 OnyxToken* directive_token = expect_token(parser, '#');
                 OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
index 0dc1c4a6b769c18d58170822c68fa25a32ce5c6a..f380970fb31c51a8128b9b5060295b26cd4ecca9 100644 (file)
@@ -1145,6 +1145,12 @@ EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local) {
     WIL(for_node->token, WI_LOCAL_TEE, low_local);
     WIL(for_node->token, WI_LOCAL_SET, iter_local);
 
+    if (for_node->has_first) {
+        for_node->first_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+        WIL(for_node->token, WI_I32_CONST, 1);
+        WIL(for_node->token, WI_LOCAL_SET, for_node->first_local);
+    }
+
     emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
     emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
     emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
@@ -1191,6 +1197,11 @@ EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local) {
     WIL(for_node->token, WI_LOCAL_GET, step_local);
     WI(for_node->token, WI_I32_ADD);
     WIL(for_node->token, WI_LOCAL_SET, iter_local);
+    
+    if (for_node->has_first) {
+        WIL(for_node->token, WI_I32_CONST, 0);
+        WIL(for_node->token, WI_LOCAL_SET, for_node->first_local);
+    }
 
     if (bh_arr_last(code).type != WI_JUMP)
         WID(for_node->token, WI_JUMP, 0x00);
@@ -1198,6 +1209,7 @@ EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local) {
     emit_leave_structured_block(mod, &code);
     emit_leave_structured_block(mod, &code);
 
+    if (for_node->has_first) local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
     local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
     local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
     local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
@@ -1230,6 +1242,12 @@ EMIT_FUNC(for_array, AstFor* for_node, u64 iter_local) {
     WI(for_node->token, WI_PTR_ADD);
     WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
 
+    if (for_node->has_first) {
+        for_node->first_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+        WIL(for_node->token, WI_I32_CONST, 1);
+        WIL(for_node->token, WI_LOCAL_SET, for_node->first_local);
+    }
+
     emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
     emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
     emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
@@ -1258,12 +1276,19 @@ EMIT_FUNC(for_array, AstFor* for_node, u64 iter_local) {
     WI(for_node->token, WI_PTR_ADD);
     WIL(for_node->token, WI_LOCAL_SET, ptr_local);
 
+    if (for_node->has_first) {
+        WIL(NULL, WI_I32_CONST, 0);
+        WIL(NULL, WI_LOCAL_SET, for_node->first_local);
+    }
+
     if (bh_arr_last(code).type != WI_JUMP)
         WID(for_node->token, WI_JUMP, 0x00);
 
     emit_leave_structured_block(mod, &code);
     emit_leave_structured_block(mod, &code);
 
+    if (for_node->has_first) local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+
     local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
     if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
 
@@ -1300,6 +1325,12 @@ EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
     WI(for_node->token, WI_PTR_ADD);
     WIL(for_node->token, WI_LOCAL_SET, end_ptr_local);
 
+    if (for_node->has_first) {
+        for_node->first_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+        WIL(for_node->token, WI_I32_CONST, 1);
+        WIL(for_node->token, WI_LOCAL_SET, for_node->first_local);
+    }
+
     emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token);
     emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token);
     emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token);
@@ -1328,12 +1359,18 @@ EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
     WI(for_node->token, WI_PTR_ADD);
     WIL(for_node->token, WI_LOCAL_SET, ptr_local);
 
+    if (for_node->has_first) {
+        WIL(NULL, WI_I32_CONST, 0);
+        WIL(NULL, WI_LOCAL_SET, for_node->first_local);
+    }
+
     if (bh_arr_last(code).type != WI_JUMP)
         WID(for_node->token, WI_JUMP, 0x00);
 
     emit_leave_structured_block(mod, &code);
     emit_leave_structured_block(mod, &code);
 
+    if (for_node->has_first) local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
     local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
     if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
 
@@ -1370,6 +1407,12 @@ EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
         bh_arr_push(mod->for_remove_info, remove_info);
     }
 
+    if (for_node->has_first) {
+        for_node->first_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+        WIL(for_node->token, WI_I32_CONST, 1);
+        WIL(for_node->token, WI_LOCAL_SET, for_node->first_local);
+    }
+
     AstLocal* var = for_node->var;
     b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
     u64 offset = 0;
@@ -1445,6 +1488,12 @@ EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
     WID(for_node->token, WI_COND_JUMP, 0x01);
 
     emit_block(mod, &code, for_node->stmt, 0);
+
+    if (for_node->has_first) {
+        WIL(NULL, WI_I32_CONST, 0);
+        WIL(NULL, WI_LOCAL_SET, for_node->first_local);
+    }
+
     WID(for_node->token, WI_JUMP, 0x00);
 
     emit_leave_structured_block(mod, &code);
@@ -1455,6 +1504,7 @@ EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
 
     bh_arr_pop(mod->for_remove_info);
 
+    if (for_node->has_first) local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
     local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
     local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
     local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
@@ -3319,6 +3369,12 @@ EMIT_FUNC(expression, AstTyped* expr) {
             break;
         }
 
+        case Ast_Kind_Directive_First: {
+            AstDirectiveFirst *first = (AstDirectiveFirst *) expr;
+            WIL(first->token, WI_LOCAL_GET, first->for_node->first_local);
+            break;
+        }
+
         default:
             bh_printf("Unhandled case: %d\n", expr->kind);
             DEBUG_HERE;
index bff7bfbe15aeed5c967eda2a8ed5030ce9786e97..ca8d5398872ca820a16f3934f77f4b9a7c16f348 100644 (file)
@@ -205,6 +205,7 @@ buffer_stream_free :: (use bs: ^BufferStream) {
 
 #match builtin.delete buffer_stream_free
 
+#match core.string.as_str buffer_stream_to_str
 buffer_stream_to_str :: (use bs: ^BufferStream) -> str {
     if !write_enabled do return null_str;