Add support for ! as an analog to 'not'

! and not are effectively interchangeable now.
Mark them both as operators for syntax highlighting.
This commit is contained in:
ridiculousfish 2018-03-04 16:06:32 -08:00
parent f83742d579
commit c7f16439bf
10 changed files with 70 additions and 16 deletions

View file

@ -3961,6 +3961,11 @@ static void test_highlighting() {
{L"||", highlight_spec_operator},
{L"true", highlight_spec_command},
{L";", highlight_spec_statement_terminator},
{L"and", highlight_spec_operator},
{L"not", highlight_spec_operator},
{L"!", highlight_spec_operator},
{L"true", highlight_spec_command},
{L";", highlight_spec_statement_terminator},
{L"end", highlight_spec_command},
{NULL, -1}};

View file

@ -1033,7 +1033,6 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
case symbol_if_clause:
case symbol_else_clause:
case symbol_case_item:
case symbol_not_statement:
case symbol_decorated_statement:
case symbol_if_statement: {
this->color_children(node, parse_token_type_string, highlight_spec_command);
@ -1065,6 +1064,10 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
this->color_node(node, highlight_spec_operator);
break;
case symbol_not_statement:
this->color_children(node, parse_token_type_string, highlight_spec_operator);
break;
case symbol_job_decorator:
this->color_node(node, highlight_spec_operator);
break;

View file

@ -111,21 +111,30 @@ enum parse_keyword_t {
parse_keyword_if,
parse_keyword_in,
parse_keyword_not,
parse_keyword_exclam,
parse_keyword_or,
parse_keyword_switch,
parse_keyword_while,
} __packed;
const enum_map<parse_keyword_t> keyword_enum_map[] = {
{parse_keyword_and, L"and"}, {parse_keyword_begin, L"begin"},
{parse_keyword_builtin, L"builtin"}, {parse_keyword_case, L"case"},
{parse_keyword_command, L"command"}, {parse_keyword_else, L"else"},
{parse_keyword_end, L"end"}, {parse_keyword_exec, L"exec"},
{parse_keyword_for, L"for"}, {parse_keyword_function, L"function"},
{parse_keyword_if, L"if"}, {parse_keyword_in, L"in"},
{parse_keyword_not, L"not"}, {parse_keyword_or, L"or"},
{parse_keyword_switch, L"switch"}, {parse_keyword_while, L"while"},
{parse_keyword_none, NULL}};
const enum_map<parse_keyword_t> keyword_enum_map[] = {{parse_keyword_exclam, L"!"},
{parse_keyword_and, L"and"},
{parse_keyword_begin, L"begin"},
{parse_keyword_builtin, L"builtin"},
{parse_keyword_case, L"case"},
{parse_keyword_command, L"command"},
{parse_keyword_else, L"else"},
{parse_keyword_end, L"end"},
{parse_keyword_exec, L"exec"},
{parse_keyword_for, L"for"},
{parse_keyword_function, L"function"},
{parse_keyword_if, L"if"},
{parse_keyword_in, L"in"},
{parse_keyword_not, L"not"},
{parse_keyword_or, L"or"},
{parse_keyword_switch, L"switch"},
{parse_keyword_while, L"while"},
{parse_keyword_none, NULL}};
#define keyword_enum_map_len (sizeof keyword_enum_map / sizeof *keyword_enum_map)
// Node tag values.

View file

@ -924,7 +924,8 @@ bool parse_execution_context_t::determine_io_chain(tnode_t<g::arguments_or_redir
parse_execution_result_t parse_execution_context_t::populate_not_process(
job_t *job, process_t *proc, tnode_t<g::not_statement> not_statement) {
job->set_flag(JOB_NEGATE, !job->get_flag(JOB_NEGATE));
return this->populate_job_process(job, proc, not_statement.child<1>());
return this->populate_job_process(job, proc,
not_statement.require_get_child<g::statement, 1>());
}
template <typename Type>

View file

@ -312,8 +312,10 @@ DEF(function_header)
produces_sequence<keyword<parse_keyword_function>, argument, argument_list, tok_end>{
BODY(function_header)};
DEF(not_statement) produces_sequence<keyword<parse_keyword_not>, statement> {
BODY(not_statement);
DEF_ALT(not_statement) {
using nots = seq<keyword<parse_keyword_not>, statement>;
using exclams = seq<keyword<parse_keyword_exclam>, statement>;
ALT_BODY(not_statement, nots, exclams);
};
// An andor_job_list is zero or more job lists, where each starts with an `and` or `or` boolean

View file

@ -143,7 +143,8 @@ RESOLVE(statement) {
switch (token1.type) {
case parse_token_type_string: {
switch (token1.keyword) {
case parse_keyword_not: {
case parse_keyword_not:
case parse_keyword_exclam: {
return production_for<nots>();
}
case parse_keyword_for:
@ -215,6 +216,19 @@ RESOLVE(case_item_list) {
return production_for<empty>();
}
RESOLVE(not_statement) {
UNUSED(token2);
UNUSED(out_tag);
switch (token1.keyword) {
case parse_keyword_not:
return production_for<nots>();
case parse_keyword_exclam:
return production_for<exclams>();
default:
return NO_PRODUCTION;
}
}
RESOLVE(andor_job_list) {
UNUSED(out_tag);

View file

@ -960,7 +960,7 @@ static inline parse_keyword_t keyword_with_name(const wchar_t *name) {
static bool is_keyword_char(wchar_t c) {
return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z') || (c >= L'0' && c <= L'9') ||
c == L'\'' || c == L'"' || c == L'\\' || c == '\n';
c == L'\'' || c == L'"' || c == L'\\' || c == '\n' || c == L'!';
}
/// Given a token, returns the keyword it matches, or parse_keyword_none.

View file

@ -8,5 +8,8 @@
####################
# && and || in while statements
####################
# Nots
####################
# Complex scenarios

View file

@ -28,6 +28,14 @@ while [ $alpha -lt 2 ] && [ $beta -lt 3 ]
set delta ( math $delta + 1 )
end
logmsg "Nots"
true && ! false ; echo $status
not true && ! false ; echo $status
not not not true ; echo $status
not ! ! not true ; echo $status
not ! echo not ! ; echo $status
logmsg "Complex scenarios"
begin; echo 1 ; false ; end || begin ; echo 2 && echo 3 ; end

View file

@ -23,6 +23,15 @@ if test 4 ok
3 3 3
4 4 4
####################
# Nots
0
1
1
0
not !
0
####################
# Complex scenarios
1