Migrate command_for_plain_statement to tnode_t

This commit is contained in:
ridiculousfish 2018-01-13 15:36:14 -08:00
parent f16ae9348f
commit 18a120d00e
8 changed files with 59 additions and 65 deletions

View File

@ -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);

View File

@ -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");
}
}

View File

@ -1915,11 +1915,11 @@ 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") {
impending_exit = true;
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;
}
}
}
}

View File

@ -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 {

View File

@ -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).

View File

@ -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();
}

View File

@ -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,

View File

@ -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,