mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-26 19:03:38 +08:00
Allow and/or statements to attach to the if/while header
For example: if false; or true; echo hello; end will output 'hello' now. Fixes #1428
This commit is contained in:
parent
0a6f62358b
commit
594b460ba2
|
@ -92,7 +92,8 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre
|
|||
/* Increment the indent if we are either a root job_list, or root case_item_list, or in an if or while header (#1665) */
|
||||
const bool is_root_job_list = (node_type == symbol_job_list && parent_type != symbol_job_list);
|
||||
const bool is_root_case_item_list = (node_type == symbol_case_item_list && parent_type != symbol_case_item_list);
|
||||
const bool is_if_while_header = (node_type == symbol_job && (parent_type == symbol_if_clause || parent_type == symbol_while_header));
|
||||
const bool is_if_while_header = ((node_type == symbol_job || node_type == symbol_andor_job_list) &&
|
||||
(parent_type == symbol_if_clause || parent_type == symbol_while_header));
|
||||
if (is_root_job_list || is_root_case_item_list || is_if_while_header)
|
||||
{
|
||||
node_indent += 1;
|
||||
|
|
|
@ -42,6 +42,8 @@ enum parse_token_type_t
|
|||
symbol_arguments_or_redirections_list,
|
||||
symbol_argument_or_redirection,
|
||||
|
||||
symbol_andor_job_list,
|
||||
|
||||
symbol_argument_list,
|
||||
|
||||
// "freestanding" argument lists are parsed from the argument list supplied to 'complete -a'
|
||||
|
|
|
@ -296,17 +296,23 @@ parse_execution_result_t parse_execution_context_t::run_if_statement(const parse
|
|||
break;
|
||||
}
|
||||
|
||||
/* An if condition has a job and a "tail" of andor jobs, e.g. "foo ; and bar; or baz" */
|
||||
assert(if_clause != NULL && else_clause != NULL);
|
||||
const parse_node_t &condition = *get_child(*if_clause, 1, symbol_job);
|
||||
const parse_node_t &condition_head = *get_child(*if_clause, 1, symbol_job);
|
||||
const parse_node_t &condition_boolean_tail = *get_child(*if_clause, 3, symbol_andor_job_list);
|
||||
|
||||
/* Check the condition. We treat parse_execution_errored here as failure, in accordance with historic behavior */
|
||||
parse_execution_result_t cond_ret = run_1_job(condition, ib);
|
||||
bool take_branch = (cond_ret == parse_execution_success) && proc_get_last_status() == EXIT_SUCCESS;
|
||||
/* Check the condition and the tail. We treat parse_execution_errored here as failure, in accordance with historic behavior */
|
||||
parse_execution_result_t cond_ret = run_1_job(condition_head, ib);
|
||||
if (cond_ret == parse_execution_success)
|
||||
{
|
||||
cond_ret = run_job_list(condition_boolean_tail, ib);
|
||||
}
|
||||
const bool take_branch = (cond_ret == parse_execution_success) && proc_get_last_status() == EXIT_SUCCESS;
|
||||
|
||||
if (take_branch)
|
||||
{
|
||||
/* condition succeeded */
|
||||
job_list_to_execute = get_child(*if_clause, 3, symbol_job_list);
|
||||
job_list_to_execute = get_child(*if_clause, 4, symbol_job_list);
|
||||
break;
|
||||
}
|
||||
else if (else_clause->child_count == 0)
|
||||
|
@ -651,17 +657,22 @@ parse_execution_result_t parse_execution_context_t::run_while_statement(const pa
|
|||
|
||||
parse_execution_result_t ret = parse_execution_success;
|
||||
|
||||
/* The condition and contents of the while loop, as a job and job list respectively */
|
||||
const parse_node_t &while_condition = *get_child(header, 1, symbol_job);
|
||||
/* The conditions of the while loop */
|
||||
const parse_node_t &condition_head = *get_child(header, 1, symbol_job);
|
||||
const parse_node_t &condition_boolean_tail = *get_child(header, 3, symbol_andor_job_list);
|
||||
|
||||
/* Run while the condition is true */
|
||||
for (;;)
|
||||
{
|
||||
/* Check the condition */
|
||||
parse_execution_result_t cond_result = this->run_1_job(while_condition, wb);
|
||||
parse_execution_result_t cond_ret = this->run_1_job(condition_head, wb);
|
||||
if (cond_ret == parse_execution_success)
|
||||
{
|
||||
cond_ret = run_job_list(condition_boolean_tail, wb);
|
||||
}
|
||||
|
||||
/* We only continue on successful execution and EXIT_SUCCESS */
|
||||
if (cond_result != parse_execution_success || proc_get_last_status() != EXIT_SUCCESS)
|
||||
if (cond_ret != parse_execution_success || proc_get_last_status() != EXIT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -1460,13 +1471,13 @@ parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t
|
|||
|
||||
parse_execution_result_t parse_execution_context_t::run_job_list(const parse_node_t &job_list_node, const block_t *associated_block)
|
||||
{
|
||||
assert(job_list_node.type == symbol_job_list);
|
||||
assert(job_list_node.type == symbol_job_list || job_list_node.type == symbol_andor_job_list);
|
||||
|
||||
parse_execution_result_t result = parse_execution_success;
|
||||
const parse_node_t *job_list = &job_list_node;
|
||||
while (job_list != NULL && ! should_cancel_execution(associated_block))
|
||||
{
|
||||
assert(job_list->type == symbol_job_list);
|
||||
assert(job_list->type == symbol_job_list || job_list_node.type == symbol_andor_job_list);
|
||||
|
||||
// Try pulling out a job
|
||||
const parse_node_t *job = tree.next_node_in_node_list(*job_list, symbol_job, &job_list);
|
||||
|
|
|
@ -173,7 +173,7 @@ RESOLVE(statement)
|
|||
}
|
||||
|
||||
RESOLVE_ONLY(if_statement) = {symbol_if_clause, symbol_else_clause, symbol_end_command, symbol_arguments_or_redirections_list};
|
||||
RESOLVE_ONLY(if_clause) = { KEYWORD(parse_keyword_if), symbol_job, parse_token_type_end, symbol_job_list };
|
||||
RESOLVE_ONLY(if_clause) = { KEYWORD(parse_keyword_if), symbol_job, parse_token_type_end, symbol_andor_job_list, symbol_job_list };
|
||||
|
||||
RESOLVE(else_clause)
|
||||
{
|
||||
|
@ -216,6 +216,29 @@ RESOLVE(case_item_list)
|
|||
|
||||
RESOLVE_ONLY(case_item) = {KEYWORD(parse_keyword_case), symbol_argument_list, parse_token_type_end, symbol_job_list};
|
||||
|
||||
RESOLVE(andor_job_list)
|
||||
{
|
||||
P list_end = {};
|
||||
P andor_job = {symbol_job, symbol_andor_job_list};
|
||||
P empty_line = {parse_token_type_end, symbol_andor_job_list};
|
||||
|
||||
if (token1.type == parse_token_type_end)
|
||||
{
|
||||
return &empty_line;
|
||||
}
|
||||
else if (token1.keyword == parse_keyword_and || token1.keyword == parse_keyword_or)
|
||||
{
|
||||
// Check that the argument to and/or is a string that's not help
|
||||
// Otherwise it's either 'and --help' or a naked 'and', and not part of this list
|
||||
if (token2.type == parse_token_type_string && !token2.is_help_argument)
|
||||
{
|
||||
return &andor_job;
|
||||
}
|
||||
}
|
||||
// All other cases end the list
|
||||
return &list_end;
|
||||
}
|
||||
|
||||
RESOLVE(argument_list)
|
||||
{
|
||||
P empty = {};
|
||||
|
@ -272,7 +295,7 @@ RESOLVE(block_header)
|
|||
|
||||
RESOLVE_ONLY(for_header) = {KEYWORD(parse_keyword_for), parse_token_type_string, KEYWORD
|
||||
(parse_keyword_in), symbol_argument_list, parse_token_type_end};
|
||||
RESOLVE_ONLY(while_header) = {KEYWORD(parse_keyword_while), symbol_job, parse_token_type_end};
|
||||
RESOLVE_ONLY(while_header) = {KEYWORD(parse_keyword_while), symbol_job, parse_token_type_end, symbol_andor_job_list};
|
||||
RESOLVE_ONLY(begin_header) = {KEYWORD(parse_keyword_begin)};
|
||||
RESOLVE_ONLY(function_header) = {KEYWORD(parse_keyword_function), symbol_argument, symbol_argument_list, parse_token_type_end};
|
||||
|
||||
|
@ -415,6 +438,7 @@ const production_t *parse_productions::production_for_token(parse_token_type_t n
|
|||
TEST(begin_header)
|
||||
TEST(function_header)
|
||||
TEST(plain_statement)
|
||||
TEST(andor_job_list)
|
||||
TEST(arguments_or_redirections_list)
|
||||
TEST(argument_or_redirection)
|
||||
TEST(argument)
|
||||
|
|
|
@ -168,6 +168,8 @@ wcstring token_type_description(parse_token_type_t type)
|
|||
case symbol_case_item:
|
||||
return L"case_item";
|
||||
|
||||
case symbol_andor_job_list:
|
||||
return L"andor_job_list";
|
||||
case symbol_argument_list:
|
||||
return L"argument_list";
|
||||
case symbol_freestanding_argument_list:
|
||||
|
|
|
@ -222,7 +222,7 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, parse
|
|||
# A job_list is a list of jobs, separated by semicolons or newlines
|
||||
|
||||
job_list = <empty> |
|
||||
job job_list
|
||||
job job_list |
|
||||
<TOK_END> job_list
|
||||
|
||||
# A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases like if statements, where we require a command). To represent "non-empty", we require a statement, followed by a possibly empty job_continuation, and then optionally a background specifier '&'
|
||||
|
@ -238,7 +238,7 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, parse
|
|||
# A block is a conditional, loop, or begin/end
|
||||
|
||||
if_statement = if_clause else_clause end_command arguments_or_redirections_list
|
||||
if_clause = <IF> job <TOK_END> job_list
|
||||
if_clause = <IF> job <TOK_END> andor_job_list job_list
|
||||
else_clause = <empty> |
|
||||
<ELSE> else_continuation
|
||||
else_continuation = if_clause else_clause |
|
||||
|
@ -254,16 +254,20 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, parse
|
|||
block_statement = block_header job_list end_command arguments_or_redirections_list
|
||||
block_header = for_header | while_header | function_header | begin_header
|
||||
for_header = FOR var_name IN argument_list <TOK_END>
|
||||
while_header = WHILE job <TOK_END>
|
||||
while_header = WHILE job <TOK_END> andor_job_list
|
||||
begin_header = BEGIN
|
||||
|
||||
# Functions take arguments, and require at least one (the name). No redirections allowed.
|
||||
function_header = FUNCTION argument argument_list <TOK_END>
|
||||
|
||||
# A boolean statement is AND or OR or NOT
|
||||
|
||||
boolean_statement = AND statement | OR statement | NOT statement
|
||||
|
||||
# An andor_job_list is zero or more job lists, where each starts with an `and` or `or` boolean statement
|
||||
andor_job_list = <empty> |
|
||||
job andor_job_list |
|
||||
<TOK_END> andor_job_list
|
||||
|
||||
# A decorated_statement is a command with a list of arguments_or_redirections, possibly with "builtin" or "command" or "exec"
|
||||
|
||||
decorated_statement = plain_statement | COMMAND plain_statement | BUILTIN plain_statement | EXEC plain_statement
|
||||
|
|
|
@ -759,13 +759,13 @@ static void compute_indents_recursive(const parse_node_tree_t &tree, node_offset
|
|||
if (node_idx > *max_visited_node_idx)
|
||||
*max_visited_node_idx = node_idx;
|
||||
|
||||
/* We could implement this by utilizing the fish grammar. But there's an easy trick instead: almost everything that wraps a job list should be indented by 1. So just find all of the job lists. One exception is switch, which wraps a case_item_list instead of a job_list. The other exception is job_list itself: a job_list is a job and a job_list, and we want that child list to be indented the same as the parent. So just find all job_lists whose parent is not a job_list, and increment their indent by 1. */
|
||||
/* We could implement this by utilizing the fish grammar. But there's an easy trick instead: almost everything that wraps a job list should be indented by 1. So just find all of the job lists. One exception is switch, which wraps a case_item_list instead of a job_list. The other exception is job_list itself: a job_list is a job and a job_list, and we want that child list to be indented the same as the parent. So just find all job_lists whose parent is not a job_list, and increment their indent by 1. We also want to treat andor_job_list like job_lists */
|
||||
|
||||
const parse_node_t &node = tree.at(node_idx);
|
||||
const parse_token_type_t node_type = node.type;
|
||||
|
||||
/* Increment the indent if we are either a root job_list, or root case_item_list */
|
||||
const bool is_root_job_list = (node_type == symbol_job_list && parent_type != symbol_job_list);
|
||||
const bool is_root_job_list = node_type != parent_type && (node_type == symbol_job_list || node_type == symbol_andor_job_list);
|
||||
const bool is_root_case_item_list = (node_type == symbol_case_item_list && parent_type != symbol_case_item_list);
|
||||
if (is_root_job_list || is_root_case_item_list)
|
||||
{
|
||||
|
|
|
@ -32,7 +32,7 @@ if false ; end ; echo $status
|
|||
if false ; or true ; echo "success1" ; end
|
||||
if false ; and false ; echo "failure1" ; end
|
||||
while false ; and false ; or true ; echo "success2"; break ; end
|
||||
while false; or begin ; false; or true; end; echo "success3"; end
|
||||
while false; or begin ; false; or true; end; echo "success3"; break ; end
|
||||
if false ; else if false ; and true ; else if false ; and false ; else if false; or true; echo "success4"; end
|
||||
if false ; else if false ; and true ; else if false ; or false ; else if false; echo "failure 4"; end
|
||||
if false ; or true | false ; echo "failure5" ; end
|
||||
|
|
Loading…
Reference in New Issue
Block a user