diff --git a/parse_execution.cpp b/parse_execution.cpp index 8ca50d565..e5a9b9f3d 100644 --- a/parse_execution.cpp +++ b/parse_execution.cpp @@ -73,7 +73,7 @@ const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_j const wcstring &forbidden_function_name = parser->forbidden_function.back(); /* Get the first job in the job list. */ - const parse_node_t *first_job = tree.next_job_in_job_list(job_list, NULL); + const parse_node_t *first_job = tree.next_node_in_node_list(job_list, symbol_job, NULL); if (first_job == NULL) { return NULL; @@ -474,26 +474,26 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(const p { /* Expand case statements */ const parse_node_t *case_item_list = get_child(statement, 3, symbol_case_item_list); - while (matching_case_item == NULL && case_item_list->child_count > 0) + + /* Loop while we don't have a match but do have more of the list */ + while (matching_case_item == NULL && case_item_list != NULL) { if (should_cancel_execution(sb)) { result = parse_execution_cancelled; break; } - - if (case_item_list->production_idx == 2) + + /* Get the next item and the remainder of the list */ + const parse_node_t *case_item = tree.next_node_in_node_list(*case_item_list, symbol_case_item, &case_item_list); + if (case_item == NULL) { - /* Hackish: blank line */ - case_item_list = get_child(*case_item_list, 1, symbol_case_item_list); - continue; + /* No more items */ + break; } - /* Pull out this case item and the rest of the list */ - const parse_node_t &case_item = *get_child(*case_item_list, 0, symbol_case_item); - /* Pull out the argument list */ - const parse_node_t &arg_list = *get_child(case_item, 1, symbol_argument_list); + const parse_node_t &arg_list = *get_child(*case_item, 1, symbol_argument_list); /* Expand arguments. We explicitly ignore unmatched_wildcard. That is, a case item list may have a wildcard that fails to expand to anything. */ const wcstring_list_t case_args = this->determine_arguments(arg_list, NULL); @@ -510,17 +510,14 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(const p /* If this matched, we're done */ if (match) { - matching_case_item = &case_item; + matching_case_item = case_item; break; } } - - /* Remainder of the list */ - case_item_list = get_child(*case_item_list, 1, symbol_case_item_list); } } - if (result == parse_execution_success && matching_case_item) + if (result == parse_execution_success && matching_case_item != NULL) { /* Success, evaluate the job list */ const parse_node_t *job_list = get_child(*matching_case_item, 3, symbol_job_list); @@ -1319,30 +1316,9 @@ parse_execution_result_t parse_execution_context_t::run_job_list(const parse_nod while (job_list != NULL && ! should_cancel_execution(associated_block)) { assert(job_list->type == symbol_job_list); - - // These correspond to the three productions of job_list + // Try pulling out a job - const parse_node_t *job = NULL; - switch (job_list->production_idx) - { - case 0: // empty - job_list = NULL; - break; - - case 1: //job, job_list - job = get_child(*job_list, 0, symbol_job); - job_list = get_child(*job_list, 1, symbol_job_list); - break; - - case 2: //blank line, job_list - job = NULL; - job_list = get_child(*job_list, 1, symbol_job_list); - break; - - default: //if we get here, it means more productions have been added to job_list, which is bad - fprintf(stderr, "Unexpected production in job_list: %lu\n", (unsigned long)job_list->production_idx); - PARSER_DIE(); - } + const parse_node_t *job = tree.next_node_in_node_list(*job_list, symbol_job, &job_list); if (job != NULL) { diff --git a/parse_tree.cpp b/parse_tree.cpp index d3953ca26..5134379d5 100644 --- a/parse_tree.cpp +++ b/parse_tree.cpp @@ -1465,35 +1465,44 @@ parse_node_tree_t::parse_node_list_t parse_node_tree_t::specific_statements_for_ return result; } -const parse_node_t *parse_node_tree_t::next_job_in_job_list(const parse_node_t &top_job_list, const parse_node_t **out_list_tail) const +const parse_node_t *parse_node_tree_t::next_node_in_node_list(const parse_node_t &node_list, parse_token_type_t entry_type, const parse_node_t **out_list_tail) const { - assert(top_job_list.type == symbol_job_list); + parse_token_type_t list_type = node_list.type; - /* Our cursor variable */ - const parse_node_t *job_list = &top_job_list; + /* Paranoia - it doesn't make sense for a list type to contain itself */ + assert(list_type != entry_type); - /* Skip over a run of empty jobs */ - assert(job_list->type == symbol_job_list); - while (job_list->production_idx == 2) + const parse_node_t *list_cursor = &node_list; + const parse_node_t *list_entry = NULL; + + /* Loop while we don't have an item but do have a list. Note that not every node in the list may contain an in item that we care about - e.g. job_list contains blank lines as a production */ + while (list_entry == NULL && list_cursor != NULL) { - job_list = this->get_child(*job_list, 1, symbol_job_list); + const parse_node_t *next_cursor = NULL; + + /* Walk through the children */ + for (size_t i=0; i < list_cursor->child_count; i++) + { + const parse_node_t *child = this->get_child(*list_cursor, i); + if (child->type == entry_type) + { + /* This is the list entry */ + list_entry = child; + } + else if (child->type == list_type) + { + /* This is the next in the list */ + next_cursor = child; + } + } + /* Go to the next entry, even if it's NULL */ + list_cursor = next_cursor; } - /* Should now be at production 0 or 1 */ - assert(job_list->type == symbol_job_list); - assert(job_list->production_idx == 0 || job_list->production_idx == 1); - - /* Pull out the job */ - const parse_node_t *job = NULL; - const parse_node_t *list_tail = NULL; - if (job_list->production_idx == 1) - { - job = this->get_child(*job_list, 0, symbol_job); - list_tail = this->get_child(*job_list, 1, symbol_job_list); - } - - /* Return them */ + /* Return what we got */ + assert(list_cursor == NULL || list_cursor->type == list_type); + assert(list_entry == NULL || list_entry->type == entry_type); if (out_list_tail != NULL) - *out_list_tail = list_tail; - return job; + *out_list_tail = list_cursor; + return list_entry; } diff --git a/parse_tree.h b/parse_tree.h index 4f26674ea..76cd80ec4 100644 --- a/parse_tree.h +++ b/parse_tree.h @@ -211,8 +211,8 @@ public: /* If the given node is a block statement, returns the header node (for_header, while_header, begin_header, or function_header). Otherwise returns NULL */ const parse_node_t *header_node_for_block_statement(const parse_node_t &node) const; - /* Given a job list, returns the next job (or NULL), and the tail of the job list. */ - const parse_node_t *next_job_in_job_list(const parse_node_t &job_list, const parse_node_t **list_tail) const; + /* Given a node list (e.g. of type symbol_job_list) and a node type (e.g. symbol_job), return the next element of the given type in that list, and the tail (by reference). Returns NULL if we've exhausted the list. */ + const parse_node_t *next_node_in_node_list(const parse_node_t &node_list, parse_token_type_t item_type, const parse_node_t **list_tail) const; /* Given a job, return all of its statements. These are 'specific statements' (e.g. symbol_decorated_statement, not symbol_statement) */ parse_node_list_t specific_statements_for_job(const parse_node_t &job) const;