diff --git a/src/builtin.cpp b/src/builtin.cpp index ffc2be4d9..92a1e5b15 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -276,29 +276,21 @@ static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar return STATUS_INVALID_ARGS; } - // Find the index of the enclosing for or while loop. Recall that incrementing loop_idx goes - // 'up' to outer blocks. - size_t loop_idx; - for (loop_idx = 0; loop_idx < parser.block_count(); loop_idx++) { + // Paranoia: ensure we have a real loop. + bool has_loop = false; + for (size_t loop_idx = 0; loop_idx < parser.block_count() && !has_loop; loop_idx++) { const block_t *b = parser.block_at_index(loop_idx); - if (b->type() == WHILE || b->type() == FOR) break; + if (b->type() == WHILE || b->type() == FOR) has_loop = true; + if (b->type() == FUNCTION_CALL) break; } - - if (loop_idx >= parser.block_count()) { + if (!has_loop) { streams.err.append_format(_(L"%ls: Not inside of loop\n"), argv[0]); builtin_print_help(parser, streams, argv[0], streams.err); return STATUS_CMD_ERROR; } - // Skip blocks interior to the loop (but not the loop itself) - size_t block_idx = loop_idx; - while (block_idx--) { - parser.block_at_index(block_idx)->skip = true; - } - - // Mark the loop's status - block_t *loop_block = parser.block_at_index(loop_idx); - loop_block->loop_status = is_break ? LOOP_BREAK : LOOP_CONTINUE; + // Mark the status in the libdata. + parser.libdata().loop_status = is_break ? loop_status_t::breaks : loop_status_t::continues; return STATUS_CMD_OK; } diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index fc6ab1b71..20d240772 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -192,7 +192,7 @@ parse_execution_context_t::cancellation_reason(const block_t *block) const { if (block && block->skip) { return execution_cancellation_skip; } - if (block && block->loop_status != LOOP_NORMAL) { + if (parser->libdata().loop_status != loop_status_t::normals) { return execution_cancellation_loop_control; } return execution_cancellation_none; @@ -421,16 +421,15 @@ parse_execution_result_t parse_execution_context_t::run_for_statement( assert(retval == ENV_OK && "for loop variable should have been successfully set"); (void)retval; - fb->loop_status = LOOP_NORMAL; + auto &ld = parser->libdata(); + ld.loop_status = loop_status_t::normals; this->run_job_list(block_contents, fb); if (this->cancellation_reason(fb) == execution_cancellation_loop_control) { // Handle break or continue. - if (fb->loop_status == LOOP_CONTINUE) { - // Reset the loop state. - fb->loop_status = LOOP_NORMAL; - continue; - } else if (fb->loop_status == LOOP_BREAK) { + bool do_break = (ld.loop_status == loop_status_t::breaks); + ld.loop_status = loop_status_t::normals; + if (do_break) { break; } } @@ -584,17 +583,18 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( } // Push a while block and then check its cancellation reason. + auto &ld = parser->libdata(); + ld.loop_status = loop_status_t::normals; while_block_t *wb = parser->push_block(); this->run_job_list(contents, wb); - auto loop_status = wb->loop_status; auto cancel_reason = this->cancellation_reason(wb); parser->pop_block(wb); if (cancel_reason == execution_cancellation_loop_control) { // Handle break or continue. - if (loop_status == LOOP_CONTINUE) { + if (ld.loop_status == loop_status_t::continues) { continue; - } else if (loop_status == LOOP_BREAK) { + } else if (ld.loop_status == loop_status_t::breaks) { break; } } diff --git a/src/parser.cpp b/src/parser.cpp index 84e58a8b2..871e8f789 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -138,8 +138,6 @@ void parser_t::push_block_int(block_t *new_current) { new_current->skip = false; } - new_current->loop_status = LOOP_NORMAL; - // Push it onto our stack. This acquires ownership because of unique_ptr. this->block_stack.emplace_back(new_current); diff --git a/src/parser.h b/src/parser.h index 6e5bad8f3..8c4b34048 100644 --- a/src/parser.h +++ b/src/parser.h @@ -46,10 +46,10 @@ enum block_type_t { }; /// Possible states for a loop. -enum loop_status_t { - LOOP_NORMAL, /// current loop block executed as normal - LOOP_BREAK, /// current loop block should be removed - LOOP_CONTINUE, /// current loop block should be skipped +enum class loop_status_t { + normals, /// current loop block executed as normal + breaks, /// current loop block should be removed + continues, /// current loop block should be skipped }; /// block_t represents a block of commands. @@ -65,8 +65,6 @@ struct block_t { public: /// Whether execution of the commands in this block should be skipped. bool skip{false}; - /// Status for the current loop block. Can be any of the values from the loop_status enum. - enum loop_status_t loop_status { LOOP_NORMAL }; /// Name of file that created this block. This string is intern'd. const wchar_t *src_filename{nullptr}; /// Line number where this block was created. @@ -174,6 +172,9 @@ struct library_data_t { /// Whether we are running an event handler. This is not a bool because we keep count of the /// event nesting level. int is_event{0}; + + /// Whether we should break or continue the current loop. + enum loop_status_t loop_status { loop_status_t::normals }; }; class parser_t : public std::enable_shared_from_this {