mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-24 13:03:13 +08:00
Migrate command_for_plain_statement to tnode_t
This commit is contained in:
parent
f16ae9348f
commit
18a120d00e
|
@ -3401,11 +3401,9 @@ static bool test_1_parse_ll2(const wcstring &src, wcstring *out_cmd, wcstring *o
|
|||
}
|
||||
tnode_t<grammar::plain_statement> stmt = stmts.at(0);
|
||||
|
||||
// Return its decoration.
|
||||
// Return its decoration and command.
|
||||
*out_deco = tree.decoration_for_plain_statement(stmt);
|
||||
|
||||
// Return its command.
|
||||
tree.command_for_plain_statement(stmt, src, out_cmd);
|
||||
*out_cmd = *command_for_plain_statement(stmt, src);
|
||||
|
||||
// Return arguments separated by spaces.
|
||||
const parse_node_tree_t::parse_node_list_t arg_nodes = tree.find_nodes(stmt, symbol_argument);
|
||||
|
|
|
@ -240,16 +240,14 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d
|
|||
|
||||
// Given a plain statement node in a parse tree, get the command and return it, expanded
|
||||
// appropriately for commands. If we succeed, return true.
|
||||
bool plain_statement_get_expanded_command(const wcstring &src, const parse_node_tree_t &tree,
|
||||
const parse_node_t &plain_statement, wcstring *out_cmd) {
|
||||
assert(plain_statement.type == symbol_plain_statement);
|
||||
|
||||
static bool plain_statement_get_expanded_command(const wcstring &src,
|
||||
tnode_t<grammar::plain_statement> stmt,
|
||||
wcstring *out_cmd) {
|
||||
// Get the command. Try expanding it. If we cannot, it's an error.
|
||||
wcstring cmd;
|
||||
if (tree.command_for_plain_statement(plain_statement, src, &cmd) &&
|
||||
expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) {
|
||||
maybe_t<wcstring> cmd = command_for_plain_statement(stmt, src);
|
||||
if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) {
|
||||
// Success, return the expanded string by reference.
|
||||
*out_cmd = std::move(cmd);
|
||||
*out_cmd = std::move(*cmd);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -324,8 +322,8 @@ static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expand
|
|||
|
||||
// Find the last statement.
|
||||
auto last_statement = parse_tree.find_last_node<grammar::plain_statement>();
|
||||
if (last_statement && plain_statement_get_expanded_command(buff, parse_tree, *last_statement,
|
||||
out_expanded_command)) {
|
||||
if (last_statement &&
|
||||
plain_statement_get_expanded_command(buff, last_statement, out_expanded_command)) {
|
||||
// Find the last argument. If we don't get one, return an invalid node.
|
||||
if (auto last_arg = parse_tree.find_last_node<grammar::argument>(last_statement)) {
|
||||
*out_last_arg = last_arg.get_source(buff);
|
||||
|
@ -803,8 +801,7 @@ void highlighter_t::color_arguments(const parse_node_t &list_node) {
|
|||
const parse_node_t *parent = this->parse_tree.get_parent(list_node, symbol_plain_statement);
|
||||
if (parent != NULL) {
|
||||
wcstring cmd_str;
|
||||
if (plain_statement_get_expanded_command(this->buff, this->parse_tree, *parent,
|
||||
&cmd_str)) {
|
||||
if (plain_statement_get_expanded_command(this->buff, {&parse_tree, parent}, &cmd_str)) {
|
||||
cmd_is_cd = (cmd_str == L"cd");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1915,14 +1915,14 @@ void history_t::add_pending_with_file_detection(const wcstring &str) {
|
|||
impending_exit = true;
|
||||
}
|
||||
|
||||
wcstring command;
|
||||
tree.command_for_plain_statement(node, str, &command);
|
||||
unescape_string_in_place(&command, UNESCAPE_DEFAULT);
|
||||
if (command == L"exit" || command == L"reboot") {
|
||||
if (maybe_t<wcstring> command = command_for_plain_statement({&tree, &node}, str)) {
|
||||
unescape_string_in_place(&*command, UNESCAPE_DEFAULT);
|
||||
if (*command == L"exit" || *command == L"reboot") {
|
||||
impending_exit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we got a path, we'll perform file detection for autosuggestion hinting.
|
||||
history_identifier_t identifier = 0;
|
||||
|
|
|
@ -104,8 +104,9 @@ node_offset_t parse_execution_context_t::get_offset(const parse_node_t &node) co
|
|||
return offset;
|
||||
}
|
||||
|
||||
const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_job_list(
|
||||
const parse_node_t &job_list, wcstring *out_func_name) const {
|
||||
tnode_t<grammar::plain_statement>
|
||||
parse_execution_context_t::infinite_recursive_statement_in_job_list(const parse_node_t &job_list,
|
||||
wcstring *out_func_name) const {
|
||||
assert(job_list.type == symbol_job_list);
|
||||
// This is a bit fragile. It is a test to see if we are inside of function call, but not inside
|
||||
// a block in that function call. If, in the future, the rules for what block scopes are pushed
|
||||
|
@ -114,23 +115,23 @@ const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_j
|
|||
bool is_within_function_call =
|
||||
(current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL);
|
||||
if (!is_within_function_call) {
|
||||
return NULL;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Check to see which function call is forbidden.
|
||||
if (parser->forbidden_function.empty()) {
|
||||
return NULL;
|
||||
return {};
|
||||
}
|
||||
const wcstring &forbidden_function_name = parser->forbidden_function.back();
|
||||
|
||||
// Get the first job in the job list.
|
||||
const parse_node_t *first_job = tree().next_node_in_node_list(job_list, symbol_job, NULL);
|
||||
if (first_job == NULL) {
|
||||
return NULL;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Here's the statement node we find that's infinite recursive.
|
||||
const parse_node_t *infinite_recursive_statement = NULL;
|
||||
tnode_t<grammar::plain_statement> infinite_recursive_statement;
|
||||
|
||||
// Get the list of statements.
|
||||
const parse_node_tree_t::parse_node_list_t statements =
|
||||
|
@ -144,8 +145,9 @@ const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_j
|
|||
if (statement.type != symbol_decorated_statement) {
|
||||
continue;
|
||||
}
|
||||
tnode_t<grammar::decorated_statement> dec_statement(&tree(), &statement);
|
||||
|
||||
const parse_node_t &plain_statement = tree().find_child(statement, symbol_plain_statement);
|
||||
auto plain_statement = tree().find_child<grammar::plain_statement>(dec_statement);
|
||||
if (tree().decoration_for_plain_statement(plain_statement) !=
|
||||
parse_statement_decoration_none) {
|
||||
// This statement has a decoration like 'builtin' or 'command', and therefore is not
|
||||
|
@ -154,22 +156,17 @@ const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_j
|
|||
}
|
||||
|
||||
// Ok, this is an undecorated plain statement. Get and expand its command.
|
||||
wcstring cmd;
|
||||
tree().command_for_plain_statement(plain_statement, pstree->src, &cmd);
|
||||
|
||||
if (expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL) &&
|
||||
maybe_t<wcstring> cmd = command_for_plain_statement(plain_statement, pstree->src);
|
||||
if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL) &&
|
||||
cmd == forbidden_function_name) {
|
||||
// This is it.
|
||||
infinite_recursive_statement = &statement;
|
||||
infinite_recursive_statement = plain_statement;
|
||||
if (out_func_name != NULL) {
|
||||
*out_func_name = forbidden_function_name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(infinite_recursive_statement == NULL ||
|
||||
infinite_recursive_statement->type == symbol_decorated_statement);
|
||||
return infinite_recursive_statement;
|
||||
}
|
||||
|
||||
|
@ -829,9 +826,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process(
|
|||
bool use_implicit_cd = false;
|
||||
|
||||
// Get the command. We expect to always get it here.
|
||||
wcstring cmd;
|
||||
bool got_cmd = tree().command_for_plain_statement(statement, pstree->src, &cmd);
|
||||
assert(got_cmd);
|
||||
wcstring cmd = *command_for_plain_statement({&tree(), &statement}, pstree->src);
|
||||
|
||||
// Expand it as a command. Return an error on failure.
|
||||
bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL);
|
||||
|
@ -1389,11 +1384,11 @@ parse_execution_result_t parse_execution_context_t::eval_node_at_offset(
|
|||
// execution (which does block statements, but never job lists).
|
||||
assert(offset == 0);
|
||||
wcstring func_name;
|
||||
const parse_node_t *infinite_recursive_node =
|
||||
auto infinite_recursive_node =
|
||||
this->infinite_recursive_statement_in_job_list(node, &func_name);
|
||||
if (infinite_recursive_node != NULL) {
|
||||
if (infinite_recursive_node) {
|
||||
// We have an infinite recursion.
|
||||
this->report_error(*infinite_recursive_node, INFINITE_FUNC_RECURSION_ERR_MSG,
|
||||
this->report_error(infinite_recursive_node, INFINITE_FUNC_RECURSION_ERR_MSG,
|
||||
func_name.c_str());
|
||||
status = parse_execution_errored;
|
||||
} else {
|
||||
|
|
|
@ -73,8 +73,8 @@ class parse_execution_context_t {
|
|||
const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which,
|
||||
parse_token_type_t expected_type = token_type_invalid) const;
|
||||
node_offset_t get_offset(const parse_node_t &node) const;
|
||||
const parse_node_t *infinite_recursive_statement_in_job_list(const parse_node_t &job_list,
|
||||
wcstring *out_func_name) const;
|
||||
tnode_t<grammar::plain_statement> infinite_recursive_statement_in_job_list(
|
||||
const parse_node_t &job_list, wcstring *out_func_name) const;
|
||||
bool is_function_context() const;
|
||||
|
||||
/// Indicates whether a job is a simple block (one block, no redirections).
|
||||
|
|
|
@ -1330,20 +1330,6 @@ enum parse_statement_decoration_t parse_node_tree_t::decoration_for_plain_statem
|
|||
return decoration;
|
||||
}
|
||||
|
||||
bool parse_node_tree_t::command_for_plain_statement(const parse_node_t &node, const wcstring &src,
|
||||
wcstring *out_cmd) const {
|
||||
bool result = false;
|
||||
assert(node.type == symbol_plain_statement);
|
||||
const parse_node_t *cmd_node = this->get_child(node, 0, parse_token_type_string);
|
||||
if (cmd_node != NULL && cmd_node->has_source()) {
|
||||
out_cmd->assign(src, cmd_node->source_start, cmd_node->source_length);
|
||||
result = true;
|
||||
} else {
|
||||
out_cmd->clear();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool parse_node_tree_t::statement_is_in_pipeline(const parse_node_t &node,
|
||||
bool include_first) const {
|
||||
// Moderately nasty hack! Walk up our ancestor chain and see if we are in a job_continuation.
|
||||
|
@ -1495,3 +1481,12 @@ const parse_node_t *parse_node_tree_t::next_node_in_node_list(
|
|||
if (out_list_tail != NULL) *out_list_tail = list_cursor;
|
||||
return list_entry;
|
||||
}
|
||||
|
||||
maybe_t<wcstring> command_for_plain_statement(tnode_t<grammar::plain_statement> stmt,
|
||||
const wcstring &src) {
|
||||
tnode_t<grammar::tok_string> cmd = stmt.child<0>();
|
||||
if (cmd && cmd.has_source()) {
|
||||
return cmd.get_source(src);
|
||||
}
|
||||
return none();
|
||||
}
|
||||
|
|
|
@ -159,6 +159,9 @@ class parse_node_tree_t : public std::vector<parse_node_t> {
|
|||
// Find the first direct child of the given node of the given type. asserts on failure.
|
||||
const parse_node_t &find_child(const parse_node_t &parent, parse_token_type_t type) const;
|
||||
|
||||
template <typename Type>
|
||||
tnode_t<Type> find_child(const parse_node_t &parent) const;
|
||||
|
||||
// Get the node corresponding to the parent of the given node, or NULL if there is no such
|
||||
// child. If expected_type is provided, only returns the parent if it is of that type. Note the
|
||||
// asymmetry: get_child asserts since the children are known, but get_parent does not, since the
|
||||
|
@ -197,11 +200,6 @@ class parse_node_tree_t : public std::vector<parse_node_t> {
|
|||
enum parse_statement_decoration_t decoration_for_plain_statement(
|
||||
const parse_node_t &node) const;
|
||||
|
||||
/// Given a plain statement, get the command by reference (from the child node). Returns true if
|
||||
/// successful. Clears the command on failure.
|
||||
bool command_for_plain_statement(const parse_node_t &node, const wcstring &src,
|
||||
wcstring *out_cmd) const;
|
||||
|
||||
/// Given a plain statement, return true if the statement is part of a pipeline. If
|
||||
/// include_first is set, the first command in a pipeline is considered part of it; otherwise
|
||||
/// only the second or additional commands are.
|
||||
|
@ -349,6 +347,11 @@ class tnode_t {
|
|||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
tnode_t<Type> parse_node_tree_t::find_child(const parse_node_t &parent) const {
|
||||
return tnode_t<Type>(this, &this->find_child(parent, Type::token));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
tnode_t<Type> parse_node_tree_t::find_last_node(const parse_node_t *parent) const {
|
||||
return tnode_t<Type>(this, this->find_last_node_of_type(Type::token, parent));
|
||||
|
@ -366,6 +369,11 @@ std::vector<tnode_t<Type>> parse_node_tree_t::find_nodes(const parse_node_t &par
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Given a plain statement, get the command from the child node. Returns the command string on
|
||||
/// success, none on failure.
|
||||
maybe_t<wcstring> command_for_plain_statement(tnode_t<grammar::plain_statement> stmt,
|
||||
const wcstring &src);
|
||||
|
||||
/// The big entry point. Parse a string, attempting to produce a tree for the given goal type.
|
||||
bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags,
|
||||
parse_node_tree_t *output, parse_error_list_t *errors,
|
||||
|
|
|
@ -1188,8 +1188,9 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src,
|
|||
L"exec");
|
||||
}
|
||||
|
||||
wcstring command;
|
||||
if (node_tree.command_for_plain_statement(node, buff_src, &command)) {
|
||||
if (maybe_t<wcstring> mcommand =
|
||||
command_for_plain_statement({&node_tree, &node}, buff_src)) {
|
||||
wcstring command = std::move(*mcommand);
|
||||
// Check that we can expand the command.
|
||||
if (!expand_one(command,
|
||||
EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS,
|
||||
|
|
Loading…
Reference in New Issue
Block a user