2020-07-03 05:51:45 +08:00
|
|
|
// Provides the "linkage" between an ast and actual execution structures (job_t, etc.).
|
2013-12-25 05:17:24 +08:00
|
|
|
#ifndef FISH_PARSE_EXECUTION_H
|
|
|
|
#define FISH_PARSE_EXECUTION_H
|
|
|
|
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <stddef.h>
|
2016-04-21 14:00:54 +08:00
|
|
|
|
2020-07-04 02:16:51 +08:00
|
|
|
#include "ast.h"
|
2015-07-25 23:14:25 +08:00
|
|
|
#include "common.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "parse_constants.h"
|
2013-12-25 05:17:24 +08:00
|
|
|
#include "parse_tree.h"
|
|
|
|
#include "proc.h"
|
|
|
|
|
2019-05-20 05:44:17 +08:00
|
|
|
class block_t;
|
Implement cancel groups
This concerns how "internal job groups" know to stop executing when an
external command receives a "cancel signal" (SIGINT or SIGQUIT). For
example:
while true
sleep 1
end
The intent is that if any 'sleep' exits from a cancel signal, then so would
the while loop. This is why you can hit control-C to end the loop even
if the SIGINT is delivered to sleep and not fish.
Here the 'while' loop is considered an "internal job group" (no separate
pgid, bash would not fork) while each 'sleep' is a separate external
command with its own job group, pgroup, etc. Prior to this change, after
running each 'sleep', parse_execution_context_t would check to see if its
exit status was a cancel signal, and if so, stash it into an int that the
cancel checker would check. But this became unwieldy: now there were three
sources of cancellation signals (that int, the job group, and fish itself).
Introduce the notion of a "cancellation group" which is a set of job
groups that should cancel together. Even though the while loop and sleep
are in different job groups, they are in the same cancellation group. When
any job gets a SIGINT or SIGQUIT, it marks that signal in its cancellation
group, which prevents running new jobs in that group.
This reduces the number of signals to check from 3 to 2; eventually we can
teach cancellation groups how to check fish's own signals and then it will
just be 1.
2020-09-03 06:06:05 +08:00
|
|
|
class cancellation_group_t;
|
2020-01-16 09:14:47 +08:00
|
|
|
class operation_context_t;
|
2015-07-25 23:14:25 +08:00
|
|
|
class parser_t;
|
2013-12-25 05:17:24 +08:00
|
|
|
|
2019-12-18 10:10:29 +08:00
|
|
|
/// An eval_result represents evaluation errors including wildcards which failed to match, syntax
|
|
|
|
/// errors, or other expansion errors. It also tracks when evaluation was skipped due to signal
|
|
|
|
/// cancellation. Note it does not track the exit status of commands.
|
2020-01-24 08:45:00 +08:00
|
|
|
enum class end_execution_reason_t {
|
2019-12-18 10:10:29 +08:00
|
|
|
/// Evaluation was successfull.
|
|
|
|
ok,
|
|
|
|
|
|
|
|
/// Evaluation was skipped due to control flow (break or return).
|
|
|
|
control_flow,
|
|
|
|
|
|
|
|
/// Evaluation was cancelled, e.g. because of a signal or exit.
|
|
|
|
cancelled,
|
|
|
|
|
|
|
|
/// A parse error or failed expansion (but not an error exit status from a command).
|
|
|
|
error,
|
2014-01-01 06:37:37 +08:00
|
|
|
};
|
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
class parse_execution_context_t {
|
|
|
|
private:
|
2017-12-23 06:40:15 +08:00
|
|
|
parsed_source_ref_t pstree;
|
2016-05-03 03:31:33 +08:00
|
|
|
parser_t *const parser;
|
2020-01-16 09:14:47 +08:00
|
|
|
const operation_context_t &ctx;
|
Implement cancel groups
This concerns how "internal job groups" know to stop executing when an
external command receives a "cancel signal" (SIGINT or SIGQUIT). For
example:
while true
sleep 1
end
The intent is that if any 'sleep' exits from a cancel signal, then so would
the while loop. This is why you can hit control-C to end the loop even
if the SIGINT is delivered to sleep and not fish.
Here the 'while' loop is considered an "internal job group" (no separate
pgid, bash would not fork) while each 'sleep' is a separate external
command with its own job group, pgroup, etc. Prior to this change, after
running each 'sleep', parse_execution_context_t would check to see if its
exit status was a cancel signal, and if so, stash it into an int that the
cancel checker would check. But this became unwieldy: now there were three
sources of cancellation signals (that int, the job group, and fish itself).
Introduce the notion of a "cancellation group" which is a set of job
groups that should cancel together. Even though the while loop and sleep
are in different job groups, they are in the same cancellation group. When
any job gets a SIGINT or SIGQUIT, it marks that signal in its cancellation
group, which prevents running new jobs in that group.
This reduces the number of signals to check from 3 to 2; eventually we can
teach cancellation groups how to check fish's own signals and then it will
just be 1.
2020-09-03 06:06:05 +08:00
|
|
|
const std::shared_ptr<cancellation_group_t> cancel_group;
|
2020-05-30 03:10:41 +08:00
|
|
|
|
2018-02-12 12:08:40 +08:00
|
|
|
// The currently executing job node, used to indicate the line number.
|
2020-07-04 02:16:51 +08:00
|
|
|
const ast::job_t *executing_job_node{};
|
2020-05-30 03:10:41 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
// Cached line number information.
|
2017-12-23 06:40:15 +08:00
|
|
|
size_t cached_lineno_offset = 0;
|
|
|
|
int cached_lineno_count = 0;
|
2020-05-30 03:10:41 +08:00
|
|
|
|
|
|
|
/// The block IO chain.
|
|
|
|
/// For example, in `begin; foo ; end < file.txt` this would have the 'file.txt' IO.
|
|
|
|
io_chain_t block_io{};
|
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
// No copying allowed.
|
2018-11-04 15:58:44 +08:00
|
|
|
parse_execution_context_t(const parse_execution_context_t &) = delete;
|
|
|
|
parse_execution_context_t &operator=(const parse_execution_context_t &) = delete;
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2019-12-18 10:10:29 +08:00
|
|
|
// Check to see if we should end execution.
|
|
|
|
// \return the eval result to end with, or none() to continue on.
|
2020-01-24 08:45:00 +08:00
|
|
|
// This will never return end_execution_reason_t::ok.
|
|
|
|
maybe_t<end_execution_reason_t> check_end_execution() const;
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2020-01-24 09:34:46 +08:00
|
|
|
// Report an error, setting $status to \p status. Always returns
|
|
|
|
// 'end_execution_reason_t::error'.
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t report_error(int status, const ast::node_t &node, const wchar_t *fmt,
|
2020-01-24 09:34:46 +08:00
|
|
|
...) const;
|
|
|
|
end_execution_reason_t report_errors(int status, const parse_error_list_t &error_list) const;
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
/// Command not found support.
|
2020-01-24 08:45:00 +08:00
|
|
|
end_execution_reason_t handle_command_not_found(const wcstring &cmd,
|
2020-07-04 02:16:51 +08:00
|
|
|
const ast::decorated_statement_t &statement,
|
2020-01-24 08:45:00 +08:00
|
|
|
int err_code);
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
// Utilities
|
2020-07-04 02:16:51 +08:00
|
|
|
wcstring get_source(const ast::node_t &node) const;
|
|
|
|
const ast::decorated_statement_t *infinite_recursive_statement_in_job_list(
|
|
|
|
const ast::job_list_t &job_list, wcstring *out_func_name) const;
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2018-08-26 16:41:45 +08:00
|
|
|
// Expand a command which may contain variables, producing an expand command and possibly
|
|
|
|
// arguments. Prints an error message on error.
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t expand_command(const ast::decorated_statement_t &statement,
|
2020-01-24 08:45:00 +08:00
|
|
|
wcstring *out_cmd, wcstring_list_t *out_args) const;
|
2018-08-26 16:41:45 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
/// Indicates whether a job is a simple block (one block, no redirections).
|
2020-07-04 02:16:51 +08:00
|
|
|
bool job_is_simple_block(const ast::job_t &job) const;
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2020-07-04 02:16:51 +08:00
|
|
|
enum process_type_t process_type_for_command(const ast::decorated_statement_t &statement,
|
2016-05-03 03:31:33 +08:00
|
|
|
const wcstring &cmd) const;
|
2020-01-24 08:45:00 +08:00
|
|
|
end_execution_reason_t apply_variable_assignments(
|
2020-07-04 02:16:51 +08:00
|
|
|
process_t *proc, const ast::variable_assignment_list_t &variable_assignments,
|
Support FOO=bar syntax for passing variables to individual commands
This adds initial support for statements with prefixed variable assignments.
Statments like this are supported:
a=1 b=$a echo $b # outputs 1
Just like in other shells, the left-hand side of each assignment must
be a valid variable identifier (no quoting/escaping). Array indexing
(PATH[1]=/bin ls $PATH) is *not* yet supported, but can be added fairly
easily.
The right hand side may be any valid string token, like a command
substitution, or a brace expansion.
Since `a=* foo` is equivalent to `begin set -lx a *; foo; end`,
the assignment, like `set`, uses nullglob behavior, e.g. below command
can safely be used to check if a directory is empty.
x=/nothing/{,.}* test (count $x) -eq 0
Generic file completion is done after the equal sign, so for example
pressing tab after something like `HOME=/` completes files in the
root directory
Subcommand completion works, so something like
`GIT_DIR=repo.git and command git ` correctly calls git completions
(but the git completion does not use the variable as of now).
The variable assignment is highlighted like an argument.
Closes #6048
2019-10-23 09:13:29 +08:00
|
|
|
const block_t **block);
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
// These create process_t structures from statements.
|
2020-01-24 08:45:00 +08:00
|
|
|
end_execution_reason_t populate_job_process(
|
2020-07-04 02:16:51 +08:00
|
|
|
job_t *job, process_t *proc, const ast::statement_t &statement,
|
|
|
|
const ast::variable_assignment_list_t &variable_assignments_list_t);
|
2020-01-24 08:45:00 +08:00
|
|
|
end_execution_reason_t populate_not_process(job_t *job, process_t *proc,
|
2020-07-04 02:16:51 +08:00
|
|
|
const ast::not_statement_t ¬_statement);
|
2020-01-24 08:45:00 +08:00
|
|
|
end_execution_reason_t populate_plain_process(job_t *job, process_t *proc,
|
2020-07-04 02:16:51 +08:00
|
|
|
const ast::decorated_statement_t &statement);
|
2018-01-16 07:37:13 +08:00
|
|
|
|
|
|
|
template <typename Type>
|
2020-01-24 08:45:00 +08:00
|
|
|
end_execution_reason_t populate_block_process(job_t *job, process_t *proc,
|
2020-07-04 02:16:51 +08:00
|
|
|
const ast::statement_t &statement,
|
|
|
|
const Type &specific_statement);
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
// These encapsulate the actual logic of various (block) statements.
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t run_block_statement(const ast::block_statement_t &statement,
|
2020-01-24 08:45:00 +08:00
|
|
|
const block_t *associated_block);
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t run_for_statement(const ast::for_header_t &header,
|
|
|
|
const ast::job_list_t &contents);
|
|
|
|
end_execution_reason_t run_if_statement(const ast::if_statement_t &statement,
|
2020-01-24 08:45:00 +08:00
|
|
|
const block_t *associated_block);
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t run_switch_statement(const ast::switch_statement_t &statement);
|
|
|
|
end_execution_reason_t run_while_statement(const ast::while_header_t &header,
|
|
|
|
const ast::job_list_t &contents,
|
2020-01-24 08:45:00 +08:00
|
|
|
const block_t *associated_block);
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t run_function_statement(const ast::block_statement_t &statement,
|
|
|
|
const ast::function_header_t &header);
|
|
|
|
end_execution_reason_t run_begin_statement(const ast::job_list_t &contents);
|
2016-05-03 03:31:33 +08:00
|
|
|
|
|
|
|
enum globspec_t { failglob, nullglob };
|
2020-07-04 02:16:51 +08:00
|
|
|
using ast_args_list_t = std::vector<const ast::argument_t *>;
|
|
|
|
|
|
|
|
static ast_args_list_t get_argument_nodes(const ast::argument_list_t &args);
|
|
|
|
static ast_args_list_t get_argument_nodes(const ast::argument_or_redirection_list_t &args);
|
|
|
|
|
|
|
|
end_execution_reason_t expand_arguments_from_nodes(const ast_args_list_t &argument_nodes,
|
2020-01-24 08:45:00 +08:00
|
|
|
wcstring_list_t *out_arguments,
|
|
|
|
globspec_t glob_behavior);
|
2016-05-03 03:31:33 +08:00
|
|
|
|
2020-01-24 09:34:46 +08:00
|
|
|
// Determines the list of redirections for a node.
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t determine_redirections(const ast::argument_or_redirection_list_t &list,
|
|
|
|
redirection_spec_list_t *out_redirections);
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t run_1_job(const ast::job_t &job, const block_t *associated_block);
|
|
|
|
end_execution_reason_t test_and_run_1_job_conjunction(const ast::job_conjunction_t &jc,
|
|
|
|
const block_t *associated_block);
|
|
|
|
end_execution_reason_t run_job_conjunction(const ast::job_conjunction_t &job_expr,
|
2020-01-24 08:45:00 +08:00
|
|
|
const block_t *associated_block);
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t run_job_list(const ast::job_list_t &job_list_node,
|
|
|
|
const block_t *associated_block);
|
|
|
|
end_execution_reason_t run_job_list(const ast::andor_job_list_t &job_list_node,
|
2020-01-24 08:45:00 +08:00
|
|
|
const block_t *associated_block);
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t populate_job_from_job_node(job_t *j, const ast::job_t &job_node,
|
2020-01-24 08:45:00 +08:00
|
|
|
const block_t *associated_block);
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2018-02-12 12:08:40 +08:00
|
|
|
// Returns the line number of the node. Not const since it touches cached_lineno_offset.
|
2020-07-04 02:16:51 +08:00
|
|
|
int line_offset_of_node(const ast::job_t *node);
|
2019-11-19 08:54:36 +08:00
|
|
|
int line_offset_of_character_at_offset(size_t offset);
|
2014-03-17 07:45:00 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
public:
|
2020-05-30 03:10:41 +08:00
|
|
|
/// Construct a context in preparation for evaluating a node in a tree, with the given block_io.
|
Implement cancel groups
This concerns how "internal job groups" know to stop executing when an
external command receives a "cancel signal" (SIGINT or SIGQUIT). For
example:
while true
sleep 1
end
The intent is that if any 'sleep' exits from a cancel signal, then so would
the while loop. This is why you can hit control-C to end the loop even
if the SIGINT is delivered to sleep and not fish.
Here the 'while' loop is considered an "internal job group" (no separate
pgid, bash would not fork) while each 'sleep' is a separate external
command with its own job group, pgroup, etc. Prior to this change, after
running each 'sleep', parse_execution_context_t would check to see if its
exit status was a cancel signal, and if so, stash it into an int that the
cancel checker would check. But this became unwieldy: now there were three
sources of cancellation signals (that int, the job group, and fish itself).
Introduce the notion of a "cancellation group" which is a set of job
groups that should cancel together. Even though the while loop and sleep
are in different job groups, they are in the same cancellation group. When
any job gets a SIGINT or SIGQUIT, it marks that signal in its cancellation
group, which prevents running new jobs in that group.
This reduces the number of signals to check from 3 to 2; eventually we can
teach cancellation groups how to check fish's own signals and then it will
just be 1.
2020-09-03 06:06:05 +08:00
|
|
|
/// The cancel group is never null and should be provided when resolving job groups.
|
|
|
|
/// The execution context may access the parser and parent job group (if any) through ctx.
|
2020-05-30 03:10:41 +08:00
|
|
|
parse_execution_context_t(parsed_source_ref_t pstree, const operation_context_t &ctx,
|
Implement cancel groups
This concerns how "internal job groups" know to stop executing when an
external command receives a "cancel signal" (SIGINT or SIGQUIT). For
example:
while true
sleep 1
end
The intent is that if any 'sleep' exits from a cancel signal, then so would
the while loop. This is why you can hit control-C to end the loop even
if the SIGINT is delivered to sleep and not fish.
Here the 'while' loop is considered an "internal job group" (no separate
pgid, bash would not fork) while each 'sleep' is a separate external
command with its own job group, pgroup, etc. Prior to this change, after
running each 'sleep', parse_execution_context_t would check to see if its
exit status was a cancel signal, and if so, stash it into an int that the
cancel checker would check. But this became unwieldy: now there were three
sources of cancellation signals (that int, the job group, and fish itself).
Introduce the notion of a "cancellation group" which is a set of job
groups that should cancel together. Even though the while loop and sleep
are in different job groups, they are in the same cancellation group. When
any job gets a SIGINT or SIGQUIT, it marks that signal in its cancellation
group, which prevents running new jobs in that group.
This reduces the number of signals to check from 3 to 2; eventually we can
teach cancellation groups how to check fish's own signals and then it will
just be 1.
2020-09-03 06:06:05 +08:00
|
|
|
std::shared_ptr<cancellation_group_t> cancel_group,
|
2020-05-30 03:10:41 +08:00
|
|
|
io_chain_t block_io);
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
/// Returns the current line number, indexed from 1. Not const since it touches
|
|
|
|
/// cached_lineno_offset.
|
2014-03-02 08:04:13 +08:00
|
|
|
int get_current_line_number();
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
/// Returns the source offset, or -1.
|
2014-03-17 13:06:32 +08:00
|
|
|
int get_current_source_offset() const;
|
|
|
|
|
2016-05-03 03:31:33 +08:00
|
|
|
/// Returns the source string.
|
2017-12-23 06:40:15 +08:00
|
|
|
const wcstring &get_source() const { return pstree->src; }
|
|
|
|
|
2020-07-04 02:16:51 +08:00
|
|
|
/// Return the parsed ast.
|
2020-07-08 07:16:45 +08:00
|
|
|
const ast::ast_t &ast() const { return pstree->ast; }
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2018-02-11 11:16:35 +08:00
|
|
|
/// Start executing at the given node. Returns 0 if there was no error, 1 if there was an
|
2016-05-03 03:31:33 +08:00
|
|
|
/// error.
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t eval_node(const ast::statement_t &statement,
|
2020-01-24 08:45:00 +08:00
|
|
|
const block_t *associated_block);
|
2020-07-04 02:16:51 +08:00
|
|
|
end_execution_reason_t eval_node(const ast::job_list_t &job_list,
|
2020-01-24 08:45:00 +08:00
|
|
|
const block_t *associated_block);
|
2013-12-25 05:17:24 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|