diff --git a/builtin.cpp b/builtin.cpp index d77d6361b..331f96308 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -3983,11 +3983,11 @@ struct parse_execution_simulator_t : public parse_execution_visitor_t { } - virtual void enter_if_header(const if_header_t &statement) + virtual void enter_if_clause(const exec_if_clause_t &statement) { } - virtual void exit_if_header(const if_header_t &statement) + virtual void exit_if_clause(const exec_if_clause_t &statement) { append_format(back(), L"\nIF successful jump to %lu", (unsigned long)statement.body); } @@ -4041,6 +4041,12 @@ struct parse_execution_simulator_t : public parse_execution_visitor_t int builtin_parse(parser_t &parser, wchar_t **argv) { + struct sigaction act; + sigemptyset(& act.sa_mask); + act.sa_flags=0; + act.sa_handler=SIG_DFL; + sigaction(SIGINT, &act, 0); + std::vector txt; for (;;) { diff --git a/parse_exec.cpp b/parse_exec.cpp index c5c15a076..0028d530c 100644 --- a/parse_exec.cpp +++ b/parse_exec.cpp @@ -36,7 +36,7 @@ class parse_exec_t /* Current visitor (very transient) */ struct parse_execution_visitor_t * visitor; - const parse_node_t &get_child(parse_node_t &parent, node_offset_t which) const + const parse_node_t &get_child(const parse_node_t &parent, node_offset_t which) const { return parse_tree.at(parent.child_offset(which)); } @@ -214,6 +214,118 @@ class parse_exec_t push(exec_node_t(header_idx, body_idx)); } + /* which: 0 -> if, 1 -> else if, 2 -> else */ + void assemble_if_else_clause(exec_node_t &exec_node, const parse_node_t &node, int which) + { + if (which == 0) + { + PARSE_ASSERT(node.type == symbol_if_clause); + PARSE_ASSERT(node.child_count == 4); + } + else if (which == 2) + { + PARSE_ASSERT(node.type == symbol_else_continuation); + PARSE_ASSERT(node.child_count == 2); + } + + struct exec_if_clause_t clause; + if (which == 0) + { + clause.body = node.child_offset(3); + } + else + { + clause.body = node.child_offset(1); + } + if (! exec_node.visited) + { + visitor->enter_if_clause(clause); + exec_node.visited = true; + if (which == 0) + { + push(node.child_offset(1)); + } + } + else + { + visitor->exit_if_clause(clause); + pop(); + } + } + + void assemble_arguments(node_offset_t start_idx, exec_argument_list_t *output) const + { + node_offset_t idx = start_idx; + for (;;) + { + const parse_node_t &node = parse_tree.at(idx); + PARSE_ASSERT(node.type == symbol_argument_list || node.type == symbol_argument_list_nonempty); + if (node.type == symbol_argument_list) + { + // argument list, may be empty + PARSE_ASSERT(node.child_count == 0 || node.child_count == 1); + if (node.child_count == 0) + { + break; + } + else + { + idx = node.child_offset(0); + } + } + else + { + // nonempty argument list + PARSE_ASSERT(node.child_count == 2); + output->push_back(exec_argument_t(node.child_offset(0))); + idx = node.child_offset(1); + } + } + } + + void assemble_1_case_item(exec_switch_statement_t *statement, node_offset_t node_idx) + { + const parse_node_t &node = parse_tree.at(node_idx); + PARSE_ASSERT(node.type == symbol_case_item); + + // add a new case + size_t len = statement->cases.size(); + statement->cases.resize(len + 1); + exec_switch_case_t &new_case = statement->cases.back(); + + // assemble it + new_case.body = node.child_offset(3); + assemble_arguments(node.child_offset(1), &new_case.arguments); + + + } + + void assemble_case_item_list(exec_switch_statement_t *statement, node_offset_t node_idx) + { + const parse_node_t &node = parse_tree.at(node_idx); + PARSE_ASSERT(node.type == symbol_case_item_list); + PARSE_ASSERT(node.child_count == 0 || node.child_count == 2); + if (node.child_count == 2) + { + assemble_1_case_item(statement, node.child_offset(0)); + assemble_case_item_list(statement, node.child_offset(1)); + } + } + + void assemble_switch_statement(const exec_node_t &exec_node, const parse_node_t &parse_node) + { + PARSE_ASSERT(parse_node.type == symbol_switch_statement); + exec_switch_statement_t statement; + + statement.argument.parse_node_idx = parse_node.child_offset(1); + assemble_case_item_list(&statement, parse_node.child_offset(3)); + + visitor->visit_switch_statement(statement); + + // pop off the switch + pop(); + } + void assemble_function_header(const exec_node_t &exec_node, const parse_node_t &header) { PARSE_ASSERT(header.type == symbol_function_header); @@ -222,7 +334,7 @@ class parse_exec_t exec_function_header_t function_info; function_info.name_idx = header.child_offset(1); function_info.body_idx = exec_node.body_parse_node_idx; - assemble_arguments_and_redirections(header.child_offset(2), &function_info.arguments_and_redirections); + assemble_arguments(header.child_offset(2), &function_info.arguments); visitor->visit_function(function_info); // Always pop @@ -347,10 +459,59 @@ void parse_exec_t::run_top_node() case symbol_if_statement: { PARSE_ASSERT(parse_node.child_count == 3); - - + pop_push(0, 2); + break; } - + + case symbol_if_clause: + { + PARSE_ASSERT(parse_node.child_count == 4); + assemble_if_else_clause(exec_node, parse_node, 0); + pop(); + break; + } + + case symbol_else_clause: + { + PARSE_ASSERT(parse_node.child_count == 0 || parse_node.child_count == 2); + if (parse_node.child_count == 0) + { + // No else + pop(); + } + else + { + // We have an else + pop_push(1); + } + break; + } + + case symbol_else_continuation: + { + // Figure out if this is an else if or a terminating else + PARSE_ASSERT(parse_node.child_count == 2); + const parse_node_t &first_child = get_child(parse_node, 1); + PARSE_ASSERT(first_child.type == symbol_if_clause || first_child.type == parse_token_type_end); + if (first_child.type == symbol_if_clause) + { + pop_push_all(); + } + else + { + // else + assemble_if_else_clause(exec_node, parse_node, 2); + pop(); + } + break; + } + + case symbol_switch_statement: + { + assemble_switch_statement(exec_node, parse_node); + break; + } + case symbol_decorated_statement: { PARSE_ASSERT(parse_node.child_count == 1 || parse_node.child_count == 2); @@ -363,6 +524,7 @@ void parse_exec_t::run_top_node() } // The following symbols should be handled by their parents, i.e. never pushed on our stack + case symbol_case_item_list: case symbol_plain_statement: case symbol_arguments_or_redirections_list: case symbol_argument_or_redirection: diff --git a/parse_exec.h b/parse_exec.h index 533051993..197f656d3 100644 --- a/parse_exec.h +++ b/parse_exec.h @@ -28,6 +28,12 @@ class parse_execution_context_t struct exec_argument_t { node_offset_t parse_node_idx; + exec_argument_t(node_offset_t p) : parse_node_idx(p) + { + } + exec_argument_t() + { + } }; typedef std::vector exec_argument_list_t; @@ -101,7 +107,7 @@ struct exec_function_header_t node_offset_t body_idx; // Arguments - exec_arguments_and_redirections_t arguments_and_redirections; + exec_argument_list_t arguments; }; struct exec_block_statement_t @@ -110,12 +116,24 @@ struct exec_block_statement_t exec_arguments_and_redirections_t arguments_and_redirections; }; -struct if_header_t +struct exec_if_clause_t { // Node containing the body of the if statement node_offset_t body; }; +struct exec_switch_case_t +{ + exec_argument_list_t arguments; + node_offset_t body; +}; + +struct exec_switch_statement_t +{ + exec_argument_t argument; + std::vector cases; +}; + struct parse_execution_visitor_t { node_offset_t node_idx; @@ -131,8 +149,11 @@ struct parse_execution_visitor_t virtual void visit_function(const exec_function_header_t &function) { } virtual bool enter_block_statement(const exec_block_statement_t &statement) { return true; } - virtual void enter_if_header(const if_header_t &statement) { } - virtual void exit_if_header(const if_header_t &statement) { } + virtual void enter_if_clause(const exec_if_clause_t &statement) { } + virtual void exit_if_clause(const exec_if_clause_t &statement) { } + + virtual void visit_switch_statement(const exec_switch_statement_t &header) { } + virtual void visit_boolean_statement(void) { } virtual void visit_basic_statement(const exec_basic_statement_t &statement) { } diff --git a/parse_tree.cpp b/parse_tree.cpp index bdf77dc55..0f5395c78 100644 --- a/parse_tree.cpp +++ b/parse_tree.cpp @@ -61,6 +61,13 @@ wcstring token_type_description(parse_token_type_t type) case symbol_else_clause: return L"else_clause"; case symbol_else_continuation: return L"else_continuation"; + case symbol_switch_statement: return L"switch_statement"; + case symbol_case_item_list: return L"case_item_list"; + case symbol_case_item: return L"case_item"; + + case symbol_argument_list_nonempty: return L"argument_list_nonempty"; + case symbol_argument_list: return L"argument_list"; + case symbol_boolean_statement: return L"boolean_statement"; case symbol_decorated_statement: return L"decorated_statement"; case symbol_plain_statement: return L"plain_statement"; @@ -263,8 +270,10 @@ class parse_ll_t 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_case_item_list(parse_token_t token); void accept_token_decorated_statement(parse_token_t token); void accept_token_plain_statement(parse_token_t token); + void accept_token_argument_list(parse_token_t token); void accept_token_arguments_or_redirections_list(parse_token_t token); void accept_token_argument_or_redirection(parse_token_t token); bool accept_token_string(parse_token_t token); @@ -312,7 +321,7 @@ class parse_ll_t // Logging? if (1) { - fprintf(stderr, "Pop %ls\n", token_type_description(symbol_stack.back().type).c_str()); + fprintf(stderr, "Pop %ls (%lu)\n", token_type_description(symbol_stack.back().type).c_str(), symbol_stack.size()); 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()); @@ -404,15 +413,18 @@ void parse_ll_t::accept_token_job_list(parse_token_t token) { case parse_token_type_string: // 'end' is special - if (token.keyword == parse_keyword_end) + switch (token.keyword) { - // End this job list - symbol_stack_pop_push(); - } - else - { - // Normal string - symbol_stack_pop_push(symbol_job, symbol_job_list); + case parse_keyword_end: + case parse_keyword_else: + // End this job list + symbol_stack_pop_push(); + break; + + default: + // Normal string + symbol_stack_pop_push(symbol_job, symbol_job_list); + break; } break; @@ -487,10 +499,11 @@ void parse_ll_t::accept_token_statement(parse_token_t token) break; case parse_keyword_else: + symbol_stack_pop_push(); + break; + case parse_keyword_switch: - symbol_stack_pop_push(symbol_block_statement); - fprintf(stderr, "Unimplemented type\n"); - PARSER_DIE(); + symbol_stack_pop_push(symbol_switch_statement); break; case parse_keyword_end: @@ -502,6 +515,7 @@ void parse_ll_t::accept_token_statement(parse_token_t token) case parse_keyword_none: case parse_keyword_command: case parse_keyword_builtin: + case parse_keyword_case: symbol_stack_pop_push(symbol_decorated_statement); break; @@ -612,6 +626,7 @@ void parse_ll_t::accept_token_boolean_statement(parse_token_t token) token_unhandled(token, __FUNCTION__); break; } + break; default: token_unhandled(token, __FUNCTION__); @@ -619,6 +634,22 @@ void parse_ll_t::accept_token_boolean_statement(parse_token_t token) } } +void parse_ll_t::accept_token_case_item_list(parse_token_t token) +{ + PARSE_ASSERT(stack_top_type() == symbol_case_item_list); + switch (token.keyword) + { + case parse_keyword_case: + symbol_stack_pop_push(symbol_case_item, symbol_case_item_list); + break; + + default: + // empty list + symbol_stack_pop_push(); + break; + } +} + void parse_ll_t::accept_token_decorated_statement(parse_token_t token) { PARSE_ASSERT(stack_top_type() == symbol_decorated_statement); @@ -656,6 +687,20 @@ void parse_ll_t::accept_token_plain_statement(parse_token_t token) symbol_stack_pop_push(parse_token_type_string, symbol_arguments_or_redirections_list); } +void parse_ll_t::accept_token_argument_list(parse_token_t token) +{ + PARSE_ASSERT(stack_top_type() == symbol_argument_list); + if (token.type == parse_token_type_string) + { + symbol_stack_pop_push(symbol_argument_list_nonempty); + } + else + { + symbol_stack_pop_push(); + } +} + + void parse_ll_t::accept_token_arguments_or_redirections_list(parse_token_t token) { PARSE_ASSERT(stack_top_type() == symbol_arguments_or_redirections_list); @@ -822,7 +867,19 @@ void parse_ll_t::accept_token(parse_token_t token, const wcstring &src) break; case symbol_function_header: - symbol_stack_pop_push(parse_keyword_function, parse_token_type_string, symbol_arguments_or_redirections_list); + symbol_stack_pop_push(parse_keyword_function, parse_token_type_string, symbol_argument_list); + break; + + case symbol_switch_statement: + symbol_stack_pop_push(parse_keyword_switch, parse_token_type_string, parse_token_type_end, symbol_case_item_list, parse_keyword_end); + break; + + case symbol_case_item_list: + accept_token_case_item_list(token); + break; + + case symbol_case_item: + symbol_stack_pop_push(parse_keyword_case, symbol_argument_list, parse_token_type_end, symbol_job_list); break; case symbol_boolean_statement: @@ -837,6 +894,14 @@ void parse_ll_t::accept_token(parse_token_t token, const wcstring &src) accept_token_plain_statement(token); break; + case symbol_argument_list_nonempty: + symbol_stack_pop_push(parse_token_type_string, symbol_argument_list); + break; + + case symbol_argument_list: + accept_token_argument_list(token); + break; + case symbol_arguments_or_redirections_list: accept_token_arguments_or_redirections_list(token); break; @@ -844,7 +909,7 @@ void parse_ll_t::accept_token(parse_token_t token, const wcstring &src) case symbol_argument_or_redirection: accept_token_argument_or_redirection(token); break; - + /* Tokens */ case parse_token_type_string: consumed = accept_token_string(token); @@ -880,6 +945,7 @@ static parse_keyword_t keyword_for_token(token_type tok, const wchar_t *tok_txt) {L"begin", parse_keyword_begin}, {L"function", parse_keyword_function}, {L"switch", parse_keyword_switch}, + {L"case", parse_keyword_case}, {L"end", parse_keyword_end}, {L"and", parse_keyword_and}, {L"or", parse_keyword_or}, diff --git a/parse_tree.h b/parse_tree.h index 4530a6326..525480f3a 100644 --- a/parse_tree.h +++ b/parse_tree.h @@ -68,11 +68,18 @@ enum parse_token_type_t symbol_else_clause, symbol_else_continuation, + symbol_switch_statement, + symbol_case_item_list, + symbol_case_item, + symbol_boolean_statement, symbol_decorated_statement, symbol_plain_statement, symbol_arguments_or_redirections_list, symbol_argument_or_redirection, + + symbol_argument_list_nonempty, + symbol_argument_list, // Terminal types parse_token_type_string, @@ -96,6 +103,7 @@ enum parse_keyword_t parse_keyword_begin, parse_keyword_function, parse_keyword_switch, + parse_keyword_case, parse_keyword_end, parse_keyword_and, parse_keyword_or, @@ -164,7 +172,7 @@ 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 | if_statement | decorated_statement + statement = boolean_statement | block_statement | if_statement | switch_statement | decorated_statement # A block is a conditional, loop, or begin/end @@ -174,16 +182,21 @@ class parse_node_tree_t : public std::vector else_continuation else_continuation = if_clause else_clause | STATEMENT_TERMINATOR job_list + + switch_statement = SWITCH STATEMENT_TERMINATOR case_item_list + case_item_list = | + case_item case_item_list + case_item = CASE argument_list STATEMENT_TERMINATOR job_list + + argument_list_nonempty = argument_list + argument_list = | argument_list_nonempty block_statement = block_header STATEMENT_TERMINATOR job_list 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) + function_header = FUNCTION function_name argument_list # A boolean statement is AND or OR or NOT