mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 05:13:10 +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");
|
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");
|
say(L"Testing basic evaluation");
|
||||||
|
|
||||||
// Ensure that we don't crash on infinite self recursion and mutual recursion. These must use
|
// 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; end", true},
|
||||||
{L"begin if true; end; end;", true},
|
{L"begin if true; end; end;", true},
|
||||||
{L"begin if true ; echo hi ; 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++) {
|
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,
|
token_type_invalid = 1,
|
||||||
// Non-terminal tokens
|
// Non-terminal tokens
|
||||||
symbol_job_list,
|
symbol_job_list,
|
||||||
|
symbol_job_conjunction,
|
||||||
symbol_job,
|
symbol_job,
|
||||||
symbol_job_continuation,
|
symbol_job_continuation,
|
||||||
symbol_statement,
|
symbol_statement,
|
||||||
|
@ -52,6 +53,8 @@ enum parse_token_type_t {
|
||||||
parse_token_type_pipe,
|
parse_token_type_pipe,
|
||||||
parse_token_type_redirection,
|
parse_token_type_redirection,
|
||||||
parse_token_type_background,
|
parse_token_type_background,
|
||||||
|
parse_token_type_andand,
|
||||||
|
parse_token_type_oror,
|
||||||
parse_token_type_end,
|
parse_token_type_end,
|
||||||
// Special terminal type that means no more tokens forthcoming.
|
// Special terminal type that means no more tokens forthcoming.
|
||||||
parse_token_type_terminate,
|
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_pipe, L"parse_token_type_pipe"},
|
||||||
{parse_token_type_redirection, L"parse_token_type_redirection"},
|
{parse_token_type_redirection, L"parse_token_type_redirection"},
|
||||||
{parse_token_type_string, L"parse_token_type_string"},
|
{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"},
|
{parse_token_type_terminate, L"parse_token_type_terminate"},
|
||||||
{symbol_andor_job_list, L"symbol_andor_job_list"},
|
{symbol_andor_job_list, L"symbol_andor_job_list"},
|
||||||
{symbol_argument, L"symbol_argument"},
|
{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_clause, L"symbol_if_clause"},
|
||||||
{symbol_if_statement, L"symbol_if_statement"},
|
{symbol_if_statement, L"symbol_if_statement"},
|
||||||
{symbol_job, L"symbol_job"},
|
{symbol_job, L"symbol_job"},
|
||||||
|
{symbol_job_conjunction, L"symbol_job_conjunction"},
|
||||||
{symbol_job_continuation, L"symbol_job_continuation"},
|
{symbol_job_continuation, L"symbol_job_continuation"},
|
||||||
{symbol_job_list, L"symbol_job_list"},
|
{symbol_job_list, L"symbol_job_list"},
|
||||||
{symbol_optional_newlines, L"symbol_optional_newlines"},
|
{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_pipe = primitive<parse_token_type_pipe>;
|
||||||
using tok_background = primitive<parse_token_type_background>;
|
using tok_background = primitive<parse_token_type_background>;
|
||||||
using tok_redirection = primitive<parse_token_type_redirection>;
|
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.
|
// Define keyword types.
|
||||||
template <parse_keyword_t Keyword>
|
template <parse_keyword_t Keyword>
|
||||||
|
@ -197,12 +199,20 @@ struct alternative {};
|
||||||
|
|
||||||
// A job_list is a list of jobs, separated by semicolons or newlines
|
// A job_list is a list of jobs, separated by semicolons or newlines
|
||||||
DEF_ALT(job_list) {
|
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_line = seq<tok_end, job_list>;
|
||||||
using empty = grammar::empty;
|
using empty = grammar::empty;
|
||||||
ALT_BODY(job_list, normal, empty_line, 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
|
// 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
|
// 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
|
// 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
|
// A boolean statement is AND or OR or NOT
|
||||||
DEF_ALT(boolean_statement) {
|
DEF_ALT(boolean_statement) {
|
||||||
using ands = seq<keyword<parse_keyword_and>, statement>;
|
using ands = seq<keyword<parse_keyword_and>, statement>; // foo ; and bar
|
||||||
using ors = seq<keyword<parse_keyword_or>, statement>;
|
using ors = seq<keyword<parse_keyword_or>, statement>; // foo ; or bar
|
||||||
using nots = seq<keyword<parse_keyword_not>, statement>;
|
using nots = seq<keyword<parse_keyword_not>, statement>; // not foo
|
||||||
ALT_BODY(boolean_statement, ands, ors, nots);
|
ALT_BODY(boolean_statement, ands, ors, nots);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Define ELEM before including this file.
|
// Define ELEM before including this file.
|
||||||
ELEM(job_list)
|
ELEM(job_list)
|
||||||
ELEM(job)
|
ELEM(job)
|
||||||
|
ELEM(job_conjunction)
|
||||||
ELEM(job_continuation)
|
ELEM(job_continuation)
|
||||||
ELEM(statement)
|
ELEM(statement)
|
||||||
ELEM(if_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) {
|
RESOLVE(job_continuation) {
|
||||||
UNUSED(token2);
|
UNUSED(token2);
|
||||||
UNUSED(out_tag);
|
UNUSED(out_tag);
|
||||||
|
@ -106,6 +119,10 @@ RESOLVE(statement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (token1.type) {
|
switch (token1.type) {
|
||||||
|
case parse_token_type_andand:
|
||||||
|
case parse_token_type_oror:
|
||||||
|
return production_for<boolean>();
|
||||||
|
|
||||||
case parse_token_type_string: {
|
case parse_token_type_string: {
|
||||||
switch (token1.keyword) {
|
switch (token1.keyword) {
|
||||||
case parse_keyword_and:
|
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_pipe:
|
||||||
case parse_token_type_redirection:
|
case parse_token_type_redirection:
|
||||||
case parse_token_type_background:
|
case parse_token_type_background:
|
||||||
|
case parse_token_type_andand:
|
||||||
|
case parse_token_type_oror:
|
||||||
case parse_token_type_end:
|
case parse_token_type_end:
|
||||||
case parse_token_type_terminate: {
|
case parse_token_type_terminate: {
|
||||||
debug(0, "Terminal token type %ls passed to %s", token_type_description(node_type),
|
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) {
|
switch (type) {
|
||||||
// Hackish. We only support the following types.
|
// Hackish. We only support the following types.
|
||||||
case symbol_statement: {
|
case symbol_statement:
|
||||||
return L"a command";
|
return L"a command";
|
||||||
}
|
case symbol_argument:
|
||||||
case symbol_argument: {
|
|
||||||
return L"an argument";
|
return L"an argument";
|
||||||
}
|
case symbol_job:
|
||||||
case parse_token_type_string: {
|
case symbol_job_list:
|
||||||
|
return L"a job";
|
||||||
|
case parse_token_type_string:
|
||||||
return L"a string";
|
return L"a string";
|
||||||
}
|
case parse_token_type_pipe:
|
||||||
case parse_token_type_pipe: {
|
|
||||||
return L"a pipe";
|
return L"a pipe";
|
||||||
}
|
case parse_token_type_redirection:
|
||||||
case parse_token_type_redirection: {
|
|
||||||
return L"a redirection";
|
return L"a redirection";
|
||||||
}
|
case parse_token_type_background:
|
||||||
case parse_token_type_background: {
|
|
||||||
return L"a '&'";
|
return L"a '&'";
|
||||||
}
|
case parse_token_type_andand:
|
||||||
case parse_token_type_end: {
|
return L"'&&'";
|
||||||
|
case parse_token_type_oror:
|
||||||
|
return L"'||'";
|
||||||
|
case parse_token_type_end:
|
||||||
return L"end of the statement";
|
return L"end of the statement";
|
||||||
}
|
case parse_token_type_terminate:
|
||||||
case parse_token_type_terminate: {
|
|
||||||
return L"end of the input";
|
return L"end of the input";
|
||||||
}
|
|
||||||
default: { return format_string(L"a %ls", token_type_description(type)); }
|
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:
|
case TOK_PIPE:
|
||||||
return parse_token_type_pipe;
|
return parse_token_type_pipe;
|
||||||
case TOK_ANDAND:
|
case TOK_ANDAND:
|
||||||
|
return parse_token_type_andand;
|
||||||
case TOK_OROR:
|
case TOK_OROR:
|
||||||
// Temporary while && and || support is brought up.
|
return parse_token_type_oror;
|
||||||
return parse_special_type_comment;
|
|
||||||
case TOK_END:
|
case TOK_END:
|
||||||
return parse_token_type_end;
|
return parse_token_type_end;
|
||||||
case TOK_BACKGROUND:
|
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_redirection:
|
||||||
case parse_token_type_background:
|
case parse_token_type_background:
|
||||||
case parse_token_type_end:
|
case parse_token_type_end:
|
||||||
|
case parse_token_type_andand:
|
||||||
|
case parse_token_type_oror:
|
||||||
case parse_token_type_terminate: {
|
case parse_token_type_terminate: {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue