From 1fe9dd040c04ecb9714b2b65e6f3de823589f65f Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 8 Sep 2022 18:51:34 -0500 Subject: [PATCH] added '#first' for for-loops --- compiler/include/astnodes.h | 12 ++++++++ compiler/src/astnodes.c | 1 + compiler/src/checker.c | 28 ++++++++++++++++++- compiler/src/clone.c | 1 + compiler/src/parser.c | 15 ++++++++++ compiler/src/wasm_emit.c | 56 +++++++++++++++++++++++++++++++++++++ core/io/stream.onyx | 1 + 7 files changed, 113 insertions(+), 1 deletion(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index 0052d233..0ad33558 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -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; }; diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index f2c6faf2..fc6e7085 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -98,6 +98,7 @@ static const char* ast_node_names[] = { "INIT", "LIBRARY", "REMOVE", + "FIRST", "CALL SITE", "CODE BLOCK", diff --git a/compiler/src/checker.c b/compiler/src/checker.c index f5c4a978..f32be5d2 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -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) diff --git a/compiler/src/clone.c b/compiler/src/clone.c index 828f6613..2c3a49fc 100644 --- a/compiler/src/clone.c +++ b/compiler/src/clone.c @@ -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; } diff --git a/compiler/src/parser.c b/compiler/src/parser.c index e62459dc..e8d74118 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -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); diff --git a/compiler/src/wasm_emit.c b/compiler/src/wasm_emit.c index 0dc1c4a6..f380970f 100644 --- a/compiler/src/wasm_emit.c +++ b/compiler/src/wasm_emit.c @@ -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; diff --git a/core/io/stream.onyx b/core/io/stream.onyx index bff7bfbe..ca8d5398 100644 --- a/core/io/stream.onyx +++ b/core/io/stream.onyx @@ -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; -- 2.25.1