Add && and || support to the conditions of if and while

This updates the "boolean tail" feature of the if and while conditions
to know about job_conjunction, thereby respecting && and ||
This commit is contained in:
ridiculousfish 2018-03-02 10:23:57 -08:00
parent 8e9670ccd5
commit e1dafeab01
7 changed files with 75 additions and 12 deletions

View file

@ -121,7 +121,7 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre
const bool is_root_case_list = const bool is_root_case_list =
node_type == symbol_case_item_list && parent_type != symbol_case_item_list; node_type == symbol_case_item_list && parent_type != symbol_case_item_list;
const bool is_if_while_header = const bool is_if_while_header =
(node_type == symbol_job || node_type == symbol_andor_job_list) && (node_type == symbol_job_conjunction || node_type == symbol_andor_job_list) &&
(parent_type == symbol_if_clause || parent_type == symbol_while_header); (parent_type == symbol_if_clause || parent_type == symbol_while_header);
if (is_root_job_list || is_root_case_list || is_if_while_header) { if (is_root_job_list || is_root_case_list || is_if_while_header) {

View file

@ -244,12 +244,12 @@ parse_execution_result_t parse_execution_context_t::run_if_statement(
} }
// An if condition has a job and a "tail" of andor jobs, e.g. "foo ; and bar; or baz". // An if condition has a job and a "tail" of andor jobs, e.g. "foo ; and bar; or baz".
tnode_t<g::job> condition_head = if_clause.child<1>(); tnode_t<g::job_conjunction> condition_head = if_clause.child<1>();
tnode_t<g::andor_job_list> condition_boolean_tail = if_clause.child<3>(); tnode_t<g::andor_job_list> condition_boolean_tail = if_clause.child<3>();
// Check the condition and the tail. We treat parse_execution_errored here as failure, in // Check the condition and the tail. We treat parse_execution_errored here as failure, in
// accordance with historic behavior. // accordance with historic behavior.
parse_execution_result_t cond_ret = run_1_job(condition_head, ib); parse_execution_result_t cond_ret = run_job_conjunction(condition_head, ib);
if (cond_ret == parse_execution_success) { if (cond_ret == parse_execution_success) {
cond_ret = run_job_list(condition_boolean_tail, ib); cond_ret = run_job_list(condition_boolean_tail, ib);
} }
@ -527,13 +527,13 @@ parse_execution_result_t parse_execution_context_t::run_while_statement(
parse_execution_result_t ret = parse_execution_success; parse_execution_result_t ret = parse_execution_success;
// The conditions of the while loop. // The conditions of the while loop.
tnode_t<g::job> condition_head = header.child<1>(); tnode_t<g::job_conjunction> condition_head = header.child<1>();
tnode_t<g::andor_job_list> condition_boolean_tail = header.child<3>(); tnode_t<g::andor_job_list> condition_boolean_tail = header.child<3>();
// Run while the condition is true. // Run while the condition is true.
for (;;) { for (;;) {
// Check the condition. // Check the condition.
parse_execution_result_t cond_ret = this->run_1_job(condition_head, wb); parse_execution_result_t cond_ret = this->run_job_conjunction(condition_head, wb);
if (cond_ret == parse_execution_success) { if (cond_ret == parse_execution_success) {
cond_ret = run_job_list(condition_boolean_tail, wb); cond_ret = run_job_list(condition_boolean_tail, wb);
} }

View file

@ -245,7 +245,7 @@ produces_sequence<if_clause, else_clause, end_command, arguments_or_redirections
BODY(if_statement)}; BODY(if_statement)};
DEF(if_clause) DEF(if_clause)
produces_sequence<keyword<parse_keyword_if>, job, tok_end, andor_job_list, job_list>{ produces_sequence<keyword<parse_keyword_if>, job_conjunction, tok_end, andor_job_list, job_list>{
BODY(if_clause)}; BODY(if_clause)};
DEF_ALT(else_clause) { DEF_ALT(else_clause) {
@ -294,7 +294,8 @@ produces_sequence<keyword<parse_keyword_for>, tok_string, keyword<parse_keyword_
}; };
DEF(while_header) DEF(while_header)
produces_sequence<keyword<parse_keyword_while>, job, tok_end, andor_job_list>{BODY(while_header)}; produces_sequence<keyword<parse_keyword_while>, job_conjunction, tok_end, andor_job_list>{
BODY(while_header)};
DEF(begin_header) produces_single<keyword<parse_keyword_begin>>{BODY(begin_header)}; DEF(begin_header) produces_single<keyword<parse_keyword_begin>>{BODY(begin_header)};

View file

@ -1057,14 +1057,14 @@ static bool detect_errors_in_backgrounded_job(tnode_t<grammar::job> job,
// foo & ; or bar // foo & ; or bar
// if foo & ; end // if foo & ; end
// while foo & ; end // while foo & ; end
if (job.try_get_parent<g::if_clause>()) { auto job_conj = job.try_get_parent<g::job_conjunction>();
if (job_conj.try_get_parent<g::if_clause>()) {
errored = append_syntax_error(parse_errors, source_range->start, errored = append_syntax_error(parse_errors, source_range->start,
BACKGROUND_IN_CONDITIONAL_ERROR_MSG); BACKGROUND_IN_CONDITIONAL_ERROR_MSG);
} else if (job.try_get_parent<g::while_header>()) { } else if (job_conj.try_get_parent<g::while_header>()) {
errored = append_syntax_error(parse_errors, source_range->start, errored = append_syntax_error(parse_errors, source_range->start,
BACKGROUND_IN_CONDITIONAL_ERROR_MSG); BACKGROUND_IN_CONDITIONAL_ERROR_MSG);
} else if (auto jlist = } else if (auto jlist = job_conj.try_get_parent<g::job_list>()) {
job.try_get_parent<g::job_conjunction>().try_get_parent<g::job_list>()) {
// This isn't very complete, e.g. we don't catch 'foo & ; not and bar'. // This isn't very complete, e.g. we don't catch 'foo & ; not and bar'.
// Fetch the job list and then advance it by one. // Fetch the job list and then advance it by one.
auto first_jconj = jlist.next_in_list<g::job_conjunction>(); auto first_jconj = jlist.next_in_list<g::job_conjunction>();

View file

@ -0,0 +1,12 @@
####################
# Basic && and || support
####################
# && and || in if statements
####################
# && and || in while statements
####################
# Complex scenarios

View file

@ -1,7 +1,33 @@
# Test && and || support logmsg "Basic && and || support"
echo first && echo second echo first && echo second
echo third || echo fourth echo third || echo fourth
true && false ; echo "true && false: $status" true && false ; echo "true && false: $status"
true || false ; echo "true || false: $status" true || false ; echo "true || false: $status"
true && false || true ; echo "true && false || true: $status" true && false || true ; echo "true && false || true: $status"
logmsg "&& and || in if statements"
if true || false ; echo "if test 1 ok" ; end
if true && false ; else; echo "if test 2 ok" ; end
if true && false ; or true ; echo "if test 3 ok" ; end
if [ 0 = 1 ] || [ 5 -ge 3 ] ; echo "if test 4 ok"; end
logmsg "&& and || in while statements"
set alpha 0
set beta 0
set gamma 0
set delta 0
while [ $alpha -lt 2 ] && [ $beta -lt 3 ]
and [ $gamma -lt 4 ] || [ $delta -lt 5 ]
echo $alpha $beta $gamma
set alpha ( math $alpha + 1 )
set beta ( math $beta + 1 )
set gamma ( math $gamma + 1 )
set delta ( math $delta + 1 )
end
logmsg "Complex scenarios"
begin; echo 1 ; false ; end || begin ; echo 2 && echo 3 ; end

View file

@ -1,6 +1,30 @@
####################
# Basic && and || support
first first
second second
third third
true && false: 1 true && false: 1
true || false: 0 true || false: 0
true && false || true: 0 true && false || true: 0
####################
# && and || in if statements
if test 1 ok
if test 2 ok
if test 3 ok
if test 4 ok
####################
# && and || in while statements
0 0 0
1 1 1
2 2 2
3 3 3
4 4 4
####################
# Complex scenarios
1
2
3