diff --git a/parse_exec.cpp b/parse_exec.cpp index f12f757a7..c5c15a076 100644 --- a/parse_exec.cpp +++ b/parse_exec.cpp @@ -228,30 +228,7 @@ class parse_exec_t // Always pop pop(); } - - void assemble_if_header(exec_node_t &exec_node, const parse_node_t &header) - { - PARSE_ASSERT(header.type == symbol_if_header); - PARSE_ASSERT(&header == &parse_tree.at(exec_node.parse_node_idx)); - PARSE_ASSERT(exec_node.body_parse_node_idx != NODE_OFFSET_INVALID); - if_header_t if_header; - if_header.body = exec_node.body_parse_node_idx; - // We may hit this on enter or exit - if (! exec_node.visited) - { - // Entry. Don't pop the header - just push the job. We'll pop it on exit. - exec_node.visited = true; - visitor->enter_if_header(if_header); - push(header.child_offset(1)); - } - else - { - // Exit. Pop it. - visitor->exit_if_header(if_header); - pop(); - } - } void enter_parse_node(size_t idx); void run_top_node(void); @@ -367,11 +344,11 @@ void parse_exec_t::run_top_node() break; } - case symbol_if_header: + case symbol_if_statement: { - PARSE_ASSERT(parse_node.child_count == 2); - assemble_if_header(exec_node, parse_node); - break; + PARSE_ASSERT(parse_node.child_count == 3); + + } case symbol_decorated_statement: diff --git a/parse_tree.cpp b/parse_tree.cpp index 6f6982e19..bdf77dc55 100644 --- a/parse_tree.cpp +++ b/parse_tree.cpp @@ -51,11 +51,16 @@ wcstring token_type_description(parse_token_type_t type) case symbol_statement: return L"statement"; case symbol_block_statement: return L"block_statement"; case symbol_block_header: return L"block_header"; - case symbol_if_header: return L"if_header"; case symbol_for_header: return L"for_header"; case symbol_while_header: return L"while_header"; case symbol_begin_header: return L"begin_header"; case symbol_function_header: return L"function_header"; + + case symbol_if_statement: return L"if_statement"; + case symbol_if_clause: return L"if_clause"; + case symbol_else_clause: return L"else_clause"; + case symbol_else_continuation: return L"else_continuation"; + case symbol_boolean_statement: return L"boolean_statement"; case symbol_decorated_statement: return L"decorated_statement"; case symbol_plain_statement: return L"plain_statement"; @@ -213,6 +218,17 @@ struct parse_stack_element_t parse_stack_element_t(parse_keyword_t k) : type(parse_token_type_string), keyword(k), node_idx(-1) { } + + wcstring describe(void) const + { + wcstring result = token_type_description(type); + if (keyword != parse_keyword_none) + { + append_format(result, L" <%ls>", keyword_description(keyword).c_str()); + } + return result; + } + }; class parse_ll_t @@ -244,7 +260,8 @@ class parse_ll_t void accept_token_job_continuation(parse_token_t token); void accept_token_statement(parse_token_t token); void accept_token_block_header(parse_token_t token); - void accept_token_if_header(parse_token_t token); + void accept_token_else_clause(parse_token_t token); + void accept_token_else_continuation(parse_token_t token); void accept_token_boolean_statement(parse_token_t token); void accept_token_decorated_statement(parse_token_t token); void accept_token_plain_statement(parse_token_t token); @@ -257,6 +274,8 @@ class parse_ll_t void parse_error(const wchar_t *expected, parse_token_t token); void append_error_callout(wcstring &error_message, parse_token_t token); + void dump_stack(void) const; + // Get the node corresponding to the top element of the stack parse_node_t &node_for_top_symbol() { @@ -294,11 +313,11 @@ class parse_ll_t if (1) { fprintf(stderr, "Pop %ls\n", token_type_description(symbol_stack.back().type).c_str()); - if (tok5.type != token_type_invalid) fprintf(stderr, "Push %ls\n", token_type_description(tok5.type).c_str()); - if (tok4.type != token_type_invalid) fprintf(stderr, "Push %ls\n", token_type_description(tok4.type).c_str()); - if (tok3.type != token_type_invalid) fprintf(stderr, "Push %ls\n", token_type_description(tok3.type).c_str()); - if (tok2.type != token_type_invalid) fprintf(stderr, "Push %ls\n", token_type_description(tok2.type).c_str()); - if (tok1.type != token_type_invalid) fprintf(stderr, "Push %ls\n", token_type_description(tok1.type).c_str()); + if (tok5.type != token_type_invalid) fprintf(stderr, "Push %ls\n", tok5.describe().c_str()); + if (tok4.type != token_type_invalid) fprintf(stderr, "Push %ls\n", tok4.describe().c_str()); + if (tok3.type != token_type_invalid) fprintf(stderr, "Push %ls\n", tok3.describe().c_str()); + if (tok2.type != token_type_invalid) fprintf(stderr, "Push %ls\n", tok2.describe().c_str()); + if (tok1.type != token_type_invalid) fprintf(stderr, "Push %ls\n", tok1.describe().c_str()); } // Get the node for the top symbol and tell it about its children @@ -329,9 +348,41 @@ class parse_ll_t } }; +void parse_ll_t::dump_stack(void) const +{ + // Walk backwards from the top, looking for parents + wcstring_list_t lines; + if (symbol_stack.empty()) + { + lines.push_back(L"(empty)"); + } + else + { + node_offset_t child = symbol_stack.back().node_idx; + node_offset_t cursor = child; + lines.push_back(nodes.at(cursor).describe()); + while (cursor--) + { + const parse_node_t &node = nodes.at(cursor); + if (node.child_start <= child && node.child_start + node.child_count > child) + { + lines.push_back(node.describe()); + child = cursor; + } + } + } + + fprintf(stderr, "Stack dump (%lu elements):\n", symbol_stack.size()); + for (size_t idx = 0; idx < lines.size(); idx++) + { + fprintf(stderr, " %ls\n", lines.at(idx).c_str()); + } +} + void parse_ll_t::token_unhandled(parse_token_t token, const char *function) { fprintf(stderr, "Unhandled token with type %ls in function %s\n", token_type_description(token.type).c_str(), function); + this->dump_stack(); PARSER_DIE(); } @@ -424,13 +475,16 @@ void parse_ll_t::accept_token_statement(parse_token_t token) symbol_stack_pop_push(symbol_boolean_statement); break; - case parse_keyword_if: case parse_keyword_for: case parse_keyword_while: case parse_keyword_function: case parse_keyword_begin: symbol_stack_pop_push(symbol_block_statement); break; + + case parse_keyword_if: + symbol_stack_pop_push(symbol_if_statement); + break; case parse_keyword_else: case parse_keyword_switch: @@ -475,10 +529,6 @@ void parse_ll_t::accept_token_block_header(parse_token_t token) case parse_token_type_string: switch (token.keyword) { - case parse_keyword_if: - symbol_stack_pop_push(symbol_if_header); - break; - case parse_keyword_else: PARSER_DIE(); //todo break; @@ -513,6 +563,36 @@ void parse_ll_t::accept_token_block_header(parse_token_t token) } } +void parse_ll_t::accept_token_else_clause(parse_token_t token) +{ + PARSE_ASSERT(stack_top_type() == symbol_else_clause); + switch (token.keyword) + { + case parse_keyword_else: + symbol_stack_pop_push(parse_keyword_else, symbol_else_continuation); + break; + + default: + symbol_stack_pop_push(); + break; + } +} + +void parse_ll_t::accept_token_else_continuation(parse_token_t token) +{ + PARSE_ASSERT(stack_top_type() == symbol_else_continuation); + switch (token.keyword) + { + case parse_keyword_if: + symbol_stack_pop_push(symbol_if_clause, symbol_else_clause); + break; + + default: + symbol_stack_pop_push(parse_token_type_end, symbol_job_list); + break; + } +} + void parse_ll_t::accept_token_boolean_statement(parse_token_t token) { PARSE_ASSERT(stack_top_type() == symbol_boolean_statement); @@ -705,6 +785,22 @@ void parse_ll_t::accept_token(parse_token_t token, const wcstring &src) accept_token_statement(token); break; + case symbol_if_statement: + symbol_stack_pop_push(symbol_if_clause, symbol_else_clause, parse_keyword_end); + break; + + case symbol_if_clause: + symbol_stack_pop_push(parse_keyword_if, symbol_job, parse_token_type_end, symbol_job_list); + break; + + case symbol_else_clause: + accept_token_else_clause(token); + break; + + case symbol_else_continuation: + accept_token_else_continuation(token); + break; + case symbol_block_statement: symbol_stack_pop_push(symbol_block_header, parse_token_type_end, symbol_job_list, parse_keyword_end, symbol_arguments_or_redirections_list); break; @@ -713,10 +809,6 @@ void parse_ll_t::accept_token(parse_token_t token, const wcstring &src) accept_token_block_header(token); break; - case symbol_if_header: - symbol_stack_pop_push(parse_keyword_if, symbol_job); - break; - case symbol_for_header: symbol_stack_pop_push(parse_keyword_for, parse_token_type_string, parse_keyword_in, symbol_arguments_or_redirections_list, parse_token_type_end); break; diff --git a/parse_tree.h b/parse_tree.h index 29365209c..4530a6326 100644 --- a/parse_tree.h +++ b/parse_tree.h @@ -15,7 +15,7 @@ #include #define PARSE_ASSERT(a) assert(a) -#define PARSER_DIE() assert(0) +#define PARSER_DIE() exit_without_destructors(-1) class parse_node_t; class parse_node_tree_t; @@ -58,11 +58,16 @@ enum parse_token_type_t symbol_statement, symbol_block_statement, symbol_block_header, - symbol_if_header, symbol_for_header, symbol_while_header, symbol_begin_header, symbol_function_header, + + symbol_if_statement, + symbol_if_clause, + symbol_else_clause, + symbol_else_continuation, + symbol_boolean_statement, symbol_decorated_statement, symbol_plain_statement, @@ -159,17 +164,24 @@ class parse_node_tree_t : public std::vector # A statement is a normal command, or an if / while / and etc - statement = boolean_statement | block_statement | decorated_statement + statement = boolean_statement | block_statement | if_statement | decorated_statement # A block is a conditional, loop, or begin/end + if_statement = if_clause else_clause + if_clause = job STATEMENT_TERMINATOR job_list + else_clause = | + else_continuation + else_continuation = if_clause else_clause | + STATEMENT_TERMINATOR job_list + block_statement = block_header STATEMENT_TERMINATOR job_list arguments_or_redirections_list - block_header = if_header | for_header | while_header | function_header | begin_header - if_header = IF job - for_header = FOR var_name IN arguments_or_redirections_list + block_header = for_header | while_header | function_header | begin_header + for_header = FOR var_name IN arguments_or_redirections_list while_header = WHILE statement begin_header = BEGIN function_header = FUNCTION function_name arguments_or_redirections_list + #(TODO: functions should not support taking redirections in their arguments)