mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 21:03:12 +00:00
Add && and || to fish grammar
This teaches the parser about && and ||, implemented via a new production "job_conjunction". These do not yet have execution support.
This commit is contained in:
parent
8ded041352
commit
23d4f93556
6 changed files with 96 additions and 22 deletions
|
@ -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++) {
|
||||
|
|
|
@ -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<parse_token_type_t> 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<parse_token_type_t> 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"},
|
||||
|
|
|
@ -35,6 +35,8 @@ using tok_string = primitive<parse_token_type_string>;
|
|||
using tok_pipe = primitive<parse_token_type_pipe>;
|
||||
using tok_background = primitive<parse_token_type_background>;
|
||||
using tok_redirection = primitive<parse_token_type_redirection>;
|
||||
using tok_andand = primitive<parse_token_type_andand>;
|
||||
using tok_oror = primitive<parse_token_type_oror>;
|
||||
|
||||
// Define keyword types.
|
||||
template <parse_keyword_t Keyword>
|
||||
|
@ -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<job, job_list>;
|
||||
using normal = seq<job, job_conjunction, job_list>;
|
||||
using empty_line = seq<tok_end, job_list>;
|
||||
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<tok_andand, optional_newlines, job, job_conjunction>;
|
||||
using orors = seq<tok_oror, optional_newlines, job, job_conjunction>;
|
||||
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<keyword<parse_keyword_function>, argument, argument_list, tok_
|
|||
|
||||
// A boolean statement is AND or OR or NOT
|
||||
DEF_ALT(boolean_statement) {
|
||||
using ands = seq<keyword<parse_keyword_and>, statement>;
|
||||
using ors = seq<keyword<parse_keyword_or>, statement>;
|
||||
using nots = seq<keyword<parse_keyword_not>, statement>;
|
||||
using ands = seq<keyword<parse_keyword_and>, statement>; // foo ; and bar
|
||||
using ors = seq<keyword<parse_keyword_or>, statement>; // foo ; or bar
|
||||
using nots = seq<keyword<parse_keyword_not>, statement>; // not foo
|
||||
ALT_BODY(boolean_statement, ands, ors, nots);
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<andands>();
|
||||
case parse_token_type_oror:
|
||||
return production_for<orors>();
|
||||
default:
|
||||
return production_for<empty>();
|
||||
}
|
||||
}
|
||||
|
||||
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<boolean>();
|
||||
|
||||
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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue