mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-17 19:21:15 -03:00
Rework 'and' and 'or' to be "job decorators"
This promotes "and" and "or" from a type of statement to "job decorators," as a possible prefix on a job. The point is to rationalize how they interact with && and ||. In the new world 'and' and 'or' apply to a entire job conjunction, i.e. they have "lower precedence." Example: if [ $age -ge 0 ] && [ $age -le 18 ] or [ $age -ge 75 ] && [ $age -le 100 ] echo "Child or senior" end
This commit is contained in:
@@ -1072,17 +1072,15 @@ static bool detect_errors_in_backgrounded_job(tnode_t<grammar::job> job,
|
||||
"Expected first job to be the node we found");
|
||||
(void)first_jconj;
|
||||
|
||||
// Try getting the next job as a boolean statement.
|
||||
tnode_t<g::job> next_job = jlist.next_in_list<g::job_conjunction>().child<0>();
|
||||
tnode_t<g::statement> next_stmt = next_job.child<0>();
|
||||
if (auto bool_stmt = next_stmt.try_get_child<g::boolean_statement, 0>()) {
|
||||
// Try getting the next job's decorator.
|
||||
if (auto next_job_dec = jlist.next_in_list<g::job_decorator>()) {
|
||||
// The next job is indeed a boolean statement.
|
||||
parse_bool_statement_type_t bool_type = bool_statement_type(bool_stmt);
|
||||
if (bool_type == parse_bool_and) { // this is not allowed
|
||||
errored = append_syntax_error(parse_errors, bool_stmt.source_range()->start,
|
||||
parse_bool_statement_type_t bool_type = bool_statement_type(next_job_dec);
|
||||
if (bool_type == parse_bool_and) {
|
||||
errored = append_syntax_error(parse_errors, next_job_dec.source_range()->start,
|
||||
BOOL_AFTER_BACKGROUND_ERROR_MSG, L"and");
|
||||
} else if (bool_type == parse_bool_or) { // this is not allowed
|
||||
errored = append_syntax_error(parse_errors, bool_stmt.source_range()->start,
|
||||
} else if (bool_type == parse_bool_or) {
|
||||
errored = append_syntax_error(parse_errors, next_job_dec.source_range()->start,
|
||||
BOOL_AFTER_BACKGROUND_ERROR_MSG, L"or");
|
||||
}
|
||||
}
|
||||
@@ -1100,7 +1098,8 @@ static bool detect_errors_in_plain_statement(const wcstring &buff_src,
|
||||
|
||||
// In a few places below, we want to know if we are in a pipeline.
|
||||
tnode_t<statement> st = pst.try_get_parent<decorated_statement>().try_get_parent<statement>();
|
||||
const bool is_in_pipeline = statement_is_in_pipeline(st, true /* count first */);
|
||||
pipeline_position_t pipe_pos = get_pipeline_position(st);
|
||||
bool is_in_pipeline = (pipe_pos != pipeline_position_t::none);
|
||||
|
||||
// We need to know the decoration.
|
||||
const enum parse_statement_decoration_t decoration = get_decoration(pst);
|
||||
@@ -1110,6 +1109,19 @@ static bool detect_errors_in_plain_statement(const wcstring &buff_src,
|
||||
errored = append_syntax_error(parse_errors, source_start, EXEC_ERR_MSG, L"exec");
|
||||
}
|
||||
|
||||
// This is a somewhat stale check that 'and' and 'or' are not in pipelines, except at the
|
||||
// beginning. We can't disallow them as commands entirely because we need to support 'and
|
||||
// --help', etc.
|
||||
if (pipe_pos == pipeline_position_t::subsequent) {
|
||||
// check if our command is 'and' or 'or'. This is very clumsy; we don't catch e.g. quoted
|
||||
// commands.
|
||||
wcstring command = pst.child<0>().get_source(buff_src);
|
||||
if (command == L"and" || command == L"or") {
|
||||
errored =
|
||||
append_syntax_error(parse_errors, source_start, EXEC_ERR_MSG, command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (maybe_t<wcstring> mcommand = command_for_plain_statement(pst, buff_src)) {
|
||||
wcstring command = std::move(*mcommand);
|
||||
// Check that we can expand the command.
|
||||
@@ -1254,16 +1266,9 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src,
|
||||
has_unclosed_block = true;
|
||||
} else if (node.type == symbol_statement && !node.has_source()) {
|
||||
// Check for a statement without source in a pipeline, i.e. unterminated pipeline.
|
||||
has_unclosed_pipe |= statement_is_in_pipeline({&node_tree, &node}, false);
|
||||
} else if (node.type == symbol_boolean_statement) {
|
||||
// 'or' and 'and' can be in a pipeline, as long as they're first.
|
||||
tnode_t<g::boolean_statement> gbs{&node_tree, &node};
|
||||
parse_bool_statement_type_t type = bool_statement_type(gbs);
|
||||
if ((type == parse_bool_and || type == parse_bool_or) &&
|
||||
statement_is_in_pipeline(gbs.try_get_parent<g::statement>(),
|
||||
false /* don't count first */)) {
|
||||
errored = append_syntax_error(&parse_errors, node.source_start, EXEC_ERR_MSG,
|
||||
(type == parse_bool_and) ? L"and" : L"or");
|
||||
auto pipe_pos = get_pipeline_position({&node_tree, &node});
|
||||
if (pipe_pos != pipeline_position_t::none) {
|
||||
has_unclosed_pipe = true;
|
||||
}
|
||||
} else if (node.type == symbol_argument) {
|
||||
tnode_t<g::argument> arg{&node_tree, &node};
|
||||
|
||||
Reference in New Issue
Block a user