Migrate loop status from blocks into libdata

Blocks will soon need to be shared across parsers. Migrate the loop status
(like break or continue) from the block into the libdata. It turns out we
only ever need one, we don't need to track this per-block.

Make it an enum class.
This commit is contained in:
ridiculousfish 2019-05-18 23:12:34 -07:00
parent ff3fe961f4
commit cd7e8f4103
4 changed files with 25 additions and 34 deletions

View file

@ -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;
}

View file

@ -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<while_block_t>();
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;
}
}

View file

@ -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);

View file

@ -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<parser_t> {