diff --git a/parse_execution.cpp b/parse_execution.cpp index 4201c288b..137b7e001 100644 --- a/parse_execution.cpp +++ b/parse_execution.cpp @@ -47,7 +47,7 @@ node_offset_t parse_execution_context_t::get_offset(const parse_node_t &node) co bool parse_execution_context_t::should_cancel_execution(const block_t *block) const { - return block && block->skip; + return block && (block->skip || block->loop_status != LOOP_NORMAL); } int parse_execution_context_t::run_if_statement(const parse_node_t &statement) @@ -228,6 +228,19 @@ int parse_execution_context_t::run_for_statement(const parse_node_t &header, con fb->skip = 0; this->run_job_list(block_contents, fb); + + /* Handle break or continue */ + if (fb->loop_status == LOOP_CONTINUE) + { + /* Reset the loop state */ + fb->loop_status = LOOP_NORMAL; + fb->skip = false; + continue; + } + else if (fb->loop_status == LOOP_BREAK) + { + break; + } } return proc_get_last_status(); @@ -358,7 +371,21 @@ int parse_execution_context_t::run_while_statement(const parse_node_t &header, c /* A while loop is a while loop! */ while (! this->should_cancel_execution(wb) && this->run_1_job(while_condition, wb) == EXIT_SUCCESS) { + /* The block ought to go inside the loop (see #1212) */ this->run_job_list(block_contents, wb); + + /* Handle break or continue */ + if (wb->loop_status == LOOP_CONTINUE) + { + /* Reset the loop state */ + wb->loop_status = LOOP_NORMAL; + wb->skip = false; + continue; + } + else if (wb->loop_status == LOOP_BREAK) + { + break; + } } /* Done */ @@ -562,7 +589,7 @@ wcstring_list_t parse_execution_context_t::determine_arguments(const parse_node_ } /* Return if we had a wildcard problem */ - if (unmatched_wildcard && ! matched_wildcard) + if (out_unmatched_wildcard_node != NULL && unmatched_wildcard && ! matched_wildcard) { *out_unmatched_wildcard_node = unmatched_wildcard_node; } diff --git a/parser.cpp b/parser.cpp index c60527814..bd0471df0 100644 --- a/parser.cpp +++ b/parser.cpp @@ -2602,6 +2602,13 @@ int parser_t::eval_new_parser(const wcstring &cmd, const io_chain_t &io, enum bl { CHECK_BLOCK(1); + if (block_type != TOP && block_type != SUBST) + { + debug(1, INVALID_SCOPE_ERR_MSG, parser_t::get_block_desc(block_type)); + bugreport(); + return 1; + } + /* Parse the source into a tree, if we can */ parse_node_tree_t tree; if (! parse_t::parse(cmd, parse_flag_none, &tree, NULL)) diff --git a/tests/test9.in b/tests/test9.in index a38fbc7c1..a16281f10 100644 --- a/tests/test9.in +++ b/tests/test9.in @@ -35,3 +35,37 @@ emit test3 foo bar # test empty argument emit + +echo "Test break and continue" +# This should output Ping once +for i in a b c + if not contains $i c ; continue ; end + echo Ping +end + +# This should output Pong not at all +for i in a b c + if not contains $i c ; break ; end + echo Pong +end + +# This should output Foop three times, and Boop not at all +set i a a a +while contains $i a + set -e i[-1] + echo Foop + continue + echo Boop +end + +# This should output Doop once +set i a a a +while contains $i a + set -e i[-1] + echo Doop + break + echo Darp +end + + +false diff --git a/tests/test9.out b/tests/test9.out index 8e19365cd..cf9054f8c 100644 --- a/tests/test9.out +++ b/tests/test9.out @@ -2,3 +2,9 @@ Testing that builtins can truncate files abc before:test1 received event test3 with args: foo bar +Test break and continue +Ping +Foop +Foop +Foop +Doop