diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 6cb3c24dd..74aa08b0d 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -838,6 +838,38 @@ static void test_parser() { err(L"backgrounded 'while' conditional not reported as error"); } + if (!parse_util_detect_errors(L"true | || false")) { + err(L"bogus boolean statement error not detected on line %d", __LINE__); + } + + if (!parse_util_detect_errors(L"|| false")) { + err(L"bogus boolean statement error not detected on line %d", __LINE__); + } + + if (!parse_util_detect_errors(L"&& false")) { + err(L"bogus boolean statement error not detected on line %d", __LINE__); + } + + if (!parse_util_detect_errors(L"true ; && false")) { + err(L"bogus boolean statement error not detected on line %d", __LINE__); + } + + if (!parse_util_detect_errors(L"true ; || false")) { + err(L"bogus boolean statement error not detected on line %d", __LINE__); + } + + if (!parse_util_detect_errors(L"true || && false")) { + err(L"bogus boolean statement error not detected on line %d", __LINE__); + } + + if (!parse_util_detect_errors(L"true && || false")) { + err(L"bogus boolean statement error not detected on line %d", __LINE__); + } + + if (!parse_util_detect_errors(L"true && && false")) { + err(L"bogus boolean statement error not detected on line %d", __LINE__); + } + say(L"Testing basic evaluation"); // Ensure that we don't crash on infinite self recursion and mutual recursion. These must use @@ -3414,6 +3446,11 @@ static void test_new_parser_correctness() { {L"begin; end", true}, {L"begin if true; end; end;", true}, {L"begin if true ; echo hi ; end; end", true}, + {L"true && false || false", true}, + {L"true || false; and true", true}, + {L"true || ||", false}, + {L"|| true", false}, + {L"true || \n\n false", true}, }; for (size_t i = 0; i < sizeof parser_tests / sizeof *parser_tests; i++) { diff --git a/src/parse_constants.h b/src/parse_constants.h index 044f549e0..ff6dbec0a 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -17,6 +17,7 @@ enum parse_token_type_t { token_type_invalid = 1, // Non-terminal tokens symbol_job_list, + symbol_job_conjunction, symbol_job, symbol_job_continuation, symbol_statement, @@ -52,6 +53,8 @@ enum parse_token_type_t { parse_token_type_pipe, parse_token_type_redirection, parse_token_type_background, + parse_token_type_andand, + parse_token_type_oror, parse_token_type_end, // Special terminal type that means no more tokens forthcoming. parse_token_type_terminate, @@ -77,6 +80,8 @@ const enum_map token_enum_map[] = { {parse_token_type_pipe, L"parse_token_type_pipe"}, {parse_token_type_redirection, L"parse_token_type_redirection"}, {parse_token_type_string, L"parse_token_type_string"}, + {parse_token_type_andand, L"parse_token_type_andand"}, + {parse_token_type_oror, L"parse_token_type_oror"}, {parse_token_type_terminate, L"parse_token_type_terminate"}, {symbol_andor_job_list, L"symbol_andor_job_list"}, {symbol_argument, L"symbol_argument"}, @@ -98,6 +103,7 @@ const enum_map token_enum_map[] = { {symbol_if_clause, L"symbol_if_clause"}, {symbol_if_statement, L"symbol_if_statement"}, {symbol_job, L"symbol_job"}, + {symbol_job_conjunction, L"symbol_job_conjunction"}, {symbol_job_continuation, L"symbol_job_continuation"}, {symbol_job_list, L"symbol_job_list"}, {symbol_optional_newlines, L"symbol_optional_newlines"}, diff --git a/src/parse_grammar.h b/src/parse_grammar.h index 0526ef055..abdb56508 100644 --- a/src/parse_grammar.h +++ b/src/parse_grammar.h @@ -35,6 +35,8 @@ using tok_string = primitive; using tok_pipe = primitive; using tok_background = primitive; using tok_redirection = primitive; +using tok_andand = primitive; +using tok_oror = primitive; // Define keyword types. template @@ -197,12 +199,20 @@ struct alternative {}; // A job_list is a list of jobs, separated by semicolons or newlines DEF_ALT(job_list) { - using normal = seq; + using normal = seq; using empty_line = seq; using empty = grammar::empty; ALT_BODY(job_list, normal, empty_line, empty); }; +// A job_conjunction is a || or && continuation of a job +DEF_ALT(job_conjunction) { + using andands = seq; + using orors = seq; + using empty = grammar::empty; + ALT_BODY(job_conjunction, andands, orors, empty); +}; + // 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 @@ -291,9 +301,9 @@ produces_sequence, argument, argument_list, tok_ // A boolean statement is AND or OR or NOT DEF_ALT(boolean_statement) { - using ands = seq, statement>; - using ors = seq, statement>; - using nots = seq, statement>; + using ands = seq, statement>; // foo ; and bar + using ors = seq, statement>; // foo ; or bar + using nots = seq, statement>; // not foo ALT_BODY(boolean_statement, ands, ors, nots); }; diff --git a/src/parse_grammar_elements.inc b/src/parse_grammar_elements.inc index ff087eeb7..e2e99e2f3 100644 --- a/src/parse_grammar_elements.inc +++ b/src/parse_grammar_elements.inc @@ -1,6 +1,7 @@ // Define ELEM before including this file. ELEM(job_list) ELEM(job) +ELEM(job_conjunction) ELEM(job_continuation) ELEM(statement) ELEM(if_statement) diff --git a/src/parse_productions.cpp b/src/parse_productions.cpp index e97b27208..c1acf5dd1 100644 --- a/src/parse_productions.cpp +++ b/src/parse_productions.cpp @@ -61,6 +61,19 @@ RESOLVE(job_list) { } } +RESOLVE(job_conjunction) { + UNUSED(token2); + UNUSED(out_tag); + switch (token1.type) { + case parse_token_type_andand: + return production_for(); + case parse_token_type_oror: + return production_for(); + default: + return production_for(); + } +} + RESOLVE(job_continuation) { UNUSED(token2); UNUSED(out_tag); @@ -106,6 +119,10 @@ RESOLVE(statement) { } switch (token1.type) { + case parse_token_type_andand: + case parse_token_type_oror: + return production_for(); + case parse_token_type_string: { switch (token1.keyword) { case parse_keyword_and: @@ -356,6 +373,8 @@ const production_element_t *parse_productions::production_for_token(parse_token_ case parse_token_type_pipe: case parse_token_type_redirection: case parse_token_type_background: + case parse_token_type_andand: + case parse_token_type_oror: case parse_token_type_end: case parse_token_type_terminate: { debug(0, "Terminal token type %ls passed to %s", token_type_description(node_type), diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index f99fc7951..f1f92fa5e 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -138,30 +138,29 @@ static wcstring token_type_user_presentable_description( switch (type) { // Hackish. We only support the following types. - case symbol_statement: { + case symbol_statement: return L"a command"; - } - case symbol_argument: { + case symbol_argument: return L"an argument"; - } - case parse_token_type_string: { + case symbol_job: + case symbol_job_list: + return L"a job"; + case parse_token_type_string: return L"a string"; - } - case parse_token_type_pipe: { + case parse_token_type_pipe: return L"a pipe"; - } - case parse_token_type_redirection: { + case parse_token_type_redirection: return L"a redirection"; - } - case parse_token_type_background: { + case parse_token_type_background: return L"a '&'"; - } - case parse_token_type_end: { + case parse_token_type_andand: + return L"'&&'"; + case parse_token_type_oror: + return L"'||'"; + case parse_token_type_end: return L"end of the statement"; - } - case parse_token_type_terminate: { + case parse_token_type_terminate: return L"end of the input"; - } default: { return format_string(L"a %ls", token_type_description(type)); } } } @@ -222,9 +221,9 @@ static inline parse_token_type_t parse_token_type_from_tokenizer_token( case TOK_PIPE: return parse_token_type_pipe; case TOK_ANDAND: + return parse_token_type_andand; case TOK_OROR: - // Temporary while && and || support is brought up. - return parse_special_type_comment; + return parse_token_type_oror; case TOK_END: return parse_token_type_end; case TOK_BACKGROUND: @@ -731,6 +730,8 @@ static bool type_is_terminal_type(parse_token_type_t type) { case parse_token_type_redirection: case parse_token_type_background: case parse_token_type_end: + case parse_token_type_andand: + case parse_token_type_oror: case parse_token_type_terminate: { return true; }