mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 13:23:09 +00:00
Eliminate parse_node_tree::find_nodes
This commit is contained in:
parent
242512f0df
commit
194f7f34d9
8 changed files with 80 additions and 111 deletions
|
@ -1367,7 +1367,7 @@ void complete(const wcstring &cmd_with_subcmds, std::vector<completion_t> *out_c
|
|||
use_implicit_cd);
|
||||
} else {
|
||||
// Get all the arguments.
|
||||
auto all_arguments = tree.find_nodes<grammar::argument>(plain_statement);
|
||||
auto all_arguments = plain_statement.descendants<grammar::argument>();
|
||||
|
||||
// See whether we are in an argument. We may also be in a redirection, or nothing at
|
||||
// all.
|
||||
|
|
|
@ -132,12 +132,11 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre
|
|||
|
||||
if (node.has_comments()) // handle comments, which come before the text
|
||||
{
|
||||
const parse_node_tree_t::parse_node_list_t comment_nodes =
|
||||
(tree.comment_nodes_for_node(node));
|
||||
for (size_t i = 0; i < comment_nodes.size(); i++) {
|
||||
const parse_node_t &comment_node = *comment_nodes.at(i);
|
||||
auto comment_nodes = tree.comment_nodes_for_node(node);
|
||||
for (const auto &comment : comment_nodes) {
|
||||
append_whitespace(node_indent, do_indent, *has_new_line, out_result);
|
||||
out_result->append(source, comment_node.source_start, comment_node.source_length);
|
||||
auto source_range = comment.source_range();
|
||||
out_result->append(source, source_range->start, source_range->length);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2418,7 +2418,6 @@ static void test_autosuggest_suggest_special() {
|
|||
}
|
||||
|
||||
const wcstring wd = L"test/autosuggest_test";
|
||||
const env_vars_snapshot_t &vars = env_vars_snapshot_t::current();
|
||||
|
||||
perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/0", L"foobar/", __LINE__);
|
||||
perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/0", L"foobar/", __LINE__);
|
||||
|
@ -3394,7 +3393,8 @@ static bool test_1_parse_ll2(const wcstring &src, wcstring *out_cmd, wcstring *o
|
|||
}
|
||||
|
||||
// Get the statement. Should only have one.
|
||||
auto stmts = tree.find_nodes<grammar::plain_statement>(tree.at(0));
|
||||
tnode_t<grammar::job_list> job_list{&tree, &tree.at(0)};
|
||||
auto stmts = job_list.descendants<grammar::plain_statement>();
|
||||
if (stmts.size() != 1) {
|
||||
say(L"Unexpected number of statements (%lu) found in '%ls'", stmts.size(), src.c_str());
|
||||
return false;
|
||||
|
@ -3406,15 +3406,34 @@ static bool test_1_parse_ll2(const wcstring &src, wcstring *out_cmd, wcstring *o
|
|||
*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);
|
||||
for (size_t i = 0; i < arg_nodes.size(); i++) {
|
||||
if (i > 0) out_joined_args->push_back(L' ');
|
||||
out_joined_args->append(arg_nodes.at(i)->get_source(src));
|
||||
bool first = true;
|
||||
for (auto arg_node : stmt.descendants<grammar::argument>()) {
|
||||
if (!first) out_joined_args->push_back(L' ');
|
||||
out_joined_args->append(arg_node.get_source(src));
|
||||
first = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Verify that 'function -h' and 'function --help' are plain statements but 'function --foo' is
|
||||
// not (issue #1240).
|
||||
template <typename Type>
|
||||
static void check_function_help(const wchar_t *src) {
|
||||
parse_node_tree_t tree;
|
||||
if (!parse_tree_from_string(src, parse_flag_none, &tree, NULL)) {
|
||||
err(L"Failed to parse '%ls'", src);
|
||||
}
|
||||
|
||||
tnode_t<grammar::job_list> node{&tree, &tree.at(0)};
|
||||
auto node_list = node.descendants<Type>();
|
||||
if (node_list.size() == 0) {
|
||||
err(L"Failed to find node of type '%ls'", token_type_description(Type::token));
|
||||
} else if (node_list.size() > 1) {
|
||||
err(L"Found too many nodes of type '%ls'", token_type_description(Type::token));
|
||||
}
|
||||
}
|
||||
|
||||
// Test the LL2 (two token lookahead) nature of the parser by exercising the special builtin and
|
||||
// command handling. In particular, 'command foo' should be a decorated statement 'foo' but 'command
|
||||
// -help' should be an undecorated statement 'command' with argument '--help', and NOT attempt to
|
||||
|
@ -3459,31 +3478,10 @@ static void test_new_parser_ll2(void) {
|
|||
tests[i].src.c_str(), (int)tests[i].deco, (int)deco, (long)__LINE__);
|
||||
}
|
||||
|
||||
// Verify that 'function -h' and 'function --help' are plain statements but 'function --foo' is
|
||||
// not (issue #1240).
|
||||
const struct {
|
||||
wcstring src;
|
||||
parse_token_type_t type;
|
||||
} tests2[] = {
|
||||
{L"function -h", symbol_plain_statement},
|
||||
{L"function --help", symbol_plain_statement},
|
||||
{L"function --foo ; end", symbol_function_header},
|
||||
{L"function foo ; end", symbol_function_header},
|
||||
};
|
||||
for (size_t i = 0; i < sizeof tests2 / sizeof *tests2; i++) {
|
||||
parse_node_tree_t tree;
|
||||
if (!parse_tree_from_string(tests2[i].src, parse_flag_none, &tree, NULL)) {
|
||||
err(L"Failed to parse '%ls'", tests2[i].src.c_str());
|
||||
}
|
||||
|
||||
const parse_node_tree_t::parse_node_list_t node_list =
|
||||
tree.find_nodes(tree.at(0), tests2[i].type);
|
||||
if (node_list.size() == 0) {
|
||||
err(L"Failed to find node of type '%ls'", token_type_description(tests2[i].type));
|
||||
} else if (node_list.size() > 1) {
|
||||
err(L"Found too many nodes of type '%ls'", token_type_description(tests2[i].type));
|
||||
}
|
||||
}
|
||||
check_function_help<grammar::plain_statement>(L"function -h");
|
||||
check_function_help<grammar::plain_statement>(L"function --help");
|
||||
check_function_help<grammar::function_header>(L"function --foo; end");
|
||||
check_function_help<grammar::function_header>(L"function foo; end");
|
||||
}
|
||||
|
||||
static void test_new_parser_ad_hoc() {
|
||||
|
@ -3500,9 +3498,8 @@ static void test_new_parser_ad_hoc() {
|
|||
|
||||
// Expect three case_item_lists: one for each case, and a terminal one. The bug was that we'd
|
||||
// try to run a command 'case'.
|
||||
const parse_node_t &root = parse_tree.at(0);
|
||||
const parse_node_tree_t::parse_node_list_t node_list =
|
||||
parse_tree.find_nodes(root, symbol_case_item_list);
|
||||
tnode_t<grammar::job_list> root{&parse_tree, &parse_tree.at(0)};
|
||||
auto node_list = root.descendants<grammar::case_item_list>();
|
||||
if (node_list.size() != 3) {
|
||||
err(L"Expected 3 case item nodes, found %lu", node_list.size());
|
||||
}
|
||||
|
|
|
@ -66,13 +66,9 @@ static wcstring profiling_cmd_name_for_redirectable_block(const parse_node_t &no
|
|||
const size_t src_start = node.source_start;
|
||||
size_t src_len = node.source_length;
|
||||
|
||||
const parse_node_tree_t::parse_node_list_t statement_terminator_nodes =
|
||||
tree.find_nodes(node, parse_token_type_end, 1);
|
||||
if (!statement_terminator_nodes.empty()) {
|
||||
const parse_node_t *term = statement_terminator_nodes.at(0);
|
||||
assert(term->source_start >= src_start);
|
||||
src_len = term->source_start - src_start;
|
||||
}
|
||||
auto term = tree.find_child<g::end_command>(node);
|
||||
assert(term.has_source() && term.source_range()->start >= src_start);
|
||||
src_len = term.source_range()->start - src_start;
|
||||
|
||||
wcstring result = wcstring(src, src_start, src_len);
|
||||
result.append(L"...");
|
||||
|
|
|
@ -42,6 +42,13 @@ struct keyword {
|
|||
}
|
||||
};
|
||||
|
||||
// Define special types.
|
||||
// Comments are not emitted as part of productions, but specially by the parser.
|
||||
struct comment {
|
||||
using type_tuple = std::tuple<>;
|
||||
static constexpr parse_token_type_t token = parse_special_type_comment;
|
||||
};
|
||||
|
||||
// Forward declare all the symbol types.
|
||||
#define ELEM(T) struct T;
|
||||
#include "parse_grammar_elements.inc"
|
||||
|
|
|
@ -1231,27 +1231,6 @@ const parse_node_t *parse_node_tree_t::get_parent(const parse_node_t &node,
|
|||
return result;
|
||||
}
|
||||
|
||||
static void find_nodes_recursive(const parse_node_tree_t &tree, const parse_node_t &parent,
|
||||
parse_token_type_t type,
|
||||
parse_node_tree_t::parse_node_list_t *result, size_t max_count) {
|
||||
if (result->size() < max_count) {
|
||||
if (parent.type == type) result->push_back(&parent);
|
||||
for (node_offset_t i = 0; i < parent.child_count; i++) {
|
||||
const parse_node_t *child = tree.get_child(parent, i);
|
||||
assert(child != NULL);
|
||||
find_nodes_recursive(tree, *child, type, result, max_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parse_node_tree_t::parse_node_list_t parse_node_tree_t::find_nodes(const parse_node_t &parent,
|
||||
parse_token_type_t type,
|
||||
size_t max_count) const {
|
||||
parse_node_list_t result;
|
||||
find_nodes_recursive(*this, parent, type, &result, max_count);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Return true if the given node has the proposed ancestor as an ancestor (or is itself that
|
||||
/// ancestor).
|
||||
static bool node_has_ancestor(const parse_node_tree_t &tree, const parse_node_t &node,
|
||||
|
@ -1365,16 +1344,16 @@ bool parse_node_tree_t::statement_is_in_pipeline(const parse_node_t &node,
|
|||
return result;
|
||||
}
|
||||
|
||||
parse_node_tree_t::parse_node_list_t parse_node_tree_t::comment_nodes_for_node(
|
||||
std::vector<tnode_t<grammar::comment>> parse_node_tree_t::comment_nodes_for_node(
|
||||
const parse_node_t &parent) const {
|
||||
parse_node_list_t result;
|
||||
std::vector<tnode_t<grammar::comment>> result;
|
||||
if (parent.has_comments()) {
|
||||
// Walk all our nodes, looking for comment nodes that have the given node as a parent.
|
||||
for (size_t i = 0; i < this->size(); i++) {
|
||||
const parse_node_t &potential_comment = this->at(i);
|
||||
if (potential_comment.type == parse_special_type_comment &&
|
||||
this->get_parent(potential_comment) == &parent) {
|
||||
result.push_back(&potential_comment);
|
||||
result.emplace_back(this, &potential_comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1428,12 +1407,13 @@ maybe_t<wcstring> command_for_plain_statement(tnode_t<grammar::plain_statement>
|
|||
return none();
|
||||
}
|
||||
|
||||
arguments_node_list_t get_argument_nodes(tnode_t<grammar::argument_list> list) {
|
||||
return list.descendants<grammar::argument>();
|
||||
arguments_node_list_t get_argument_nodes(tnode_t<grammar::argument_list> list, size_t max) {
|
||||
return list.descendants<grammar::argument>(max);
|
||||
}
|
||||
|
||||
arguments_node_list_t get_argument_nodes(tnode_t<grammar::arguments_or_redirections_list> list) {
|
||||
return list.descendants<grammar::argument>();
|
||||
arguments_node_list_t get_argument_nodes(tnode_t<grammar::arguments_or_redirections_list> list,
|
||||
size_t max) {
|
||||
return list.descendants<grammar::argument>(max);
|
||||
}
|
||||
|
||||
bool job_node_is_background(tnode_t<grammar::job> job) {
|
||||
|
|
|
@ -169,15 +169,6 @@ class parse_node_tree_t : public std::vector<parse_node_t> {
|
|||
const parse_node_t *get_parent(const parse_node_t &node,
|
||||
parse_token_type_t expected_type = token_type_invalid) const;
|
||||
|
||||
// Find all the nodes of a given type underneath a given node, up to max_count of them.
|
||||
typedef std::vector<const parse_node_t *> parse_node_list_t;
|
||||
parse_node_list_t find_nodes(const parse_node_t &parent, parse_token_type_t type,
|
||||
size_t max_count = size_t(-1)) const;
|
||||
|
||||
// Find all the nodes of a given type underneath a given node, up to max_count of them.
|
||||
template <typename Type>
|
||||
std::vector<tnode_t<Type>> find_nodes(const parse_node_t &parent, size_t max_count = -1) const;
|
||||
|
||||
// Finds the last node of a given type, or empty if it could not be found. If parent is NULL,
|
||||
// this finds the last node in the tree of that type.
|
||||
template <typename Type>
|
||||
|
@ -196,7 +187,7 @@ class parse_node_tree_t : public std::vector<parse_node_t> {
|
|||
bool statement_is_in_pipeline(const parse_node_t &node, bool include_first) const;
|
||||
|
||||
/// Given a node, return all of its comment nodes.
|
||||
parse_node_list_t comment_nodes_for_node(const parse_node_t &node) const;
|
||||
std::vector<tnode_t<grammar::comment>> comment_nodes_for_node(const parse_node_t &node) const;
|
||||
|
||||
private:
|
||||
template <typename Type>
|
||||
|
@ -377,7 +368,18 @@ class tnode_t {
|
|||
template <typename DescendantType>
|
||||
std::vector<tnode_t<DescendantType>> descendants(size_t max_count = -1) const {
|
||||
if (!nodeptr) return {};
|
||||
return tree->find_nodes<DescendantType>(*nodeptr);
|
||||
std::vector<tnode_t<DescendantType>> result;
|
||||
std::vector<const parse_node_t *> stack{nodeptr};
|
||||
while (!stack.empty() && result.size() < max_count) {
|
||||
const parse_node_t *node = stack.back();
|
||||
if (node->type == DescendantType::token) result.emplace_back(tree, node);
|
||||
stack.pop_back();
|
||||
node_offset_t index = node->child_count;
|
||||
while (index--) {
|
||||
stack.push_back(tree->get_child(*node, index));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Given that we are a list type, \return the next node of some Item in some node list,
|
||||
|
@ -402,18 +404,6 @@ tnode_t<Type> parse_node_tree_t::find_last_node(const parse_node_t *parent) cons
|
|||
return tnode_t<Type>(this, this->find_last_node_of_type(Type::token, parent));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
std::vector<tnode_t<Type>> parse_node_tree_t::find_nodes(const parse_node_t &parent,
|
||||
size_t max_count) const {
|
||||
auto ptrs = this->find_nodes(parent, Type::token, max_count);
|
||||
std::vector<tnode_t<Type>> result;
|
||||
result.reserve(ptrs.size());
|
||||
for (const parse_node_t *np : ptrs) {
|
||||
result.emplace_back(this, np);
|
||||
}
|
||||
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,
|
||||
|
@ -430,9 +420,11 @@ enum token_type redirection_type(tnode_t<grammar::redirection> redirection, cons
|
|||
int *out_fd, wcstring *out_target);
|
||||
|
||||
/// Return the arguments under an arguments_list or arguments_or_redirection_list
|
||||
/// Do not return more than max.
|
||||
using arguments_node_list_t = std::vector<tnode_t<grammar::argument>>;
|
||||
arguments_node_list_t get_argument_nodes(tnode_t<grammar::argument_list>);
|
||||
arguments_node_list_t get_argument_nodes(tnode_t<grammar::arguments_or_redirections_list>);
|
||||
arguments_node_list_t get_argument_nodes(tnode_t<grammar::argument_list>, size_t max = -1);
|
||||
arguments_node_list_t get_argument_nodes(tnode_t<grammar::arguments_or_redirections_list>,
|
||||
size_t max = -1);
|
||||
|
||||
/// Return whether the given job is background because it has a & symbol.
|
||||
bool job_node_is_background(tnode_t<grammar::job>);
|
||||
|
|
|
@ -760,15 +760,13 @@ bool parse_util_argument_is_help(const wchar_t *s) {
|
|||
}
|
||||
|
||||
/// Check if the first argument under the given node is --help.
|
||||
static bool first_argument_is_help(const parse_node_tree_t &node_tree, const parse_node_t &node,
|
||||
static bool first_argument_is_help(tnode_t<grammar::plain_statement> statement,
|
||||
const wcstring &src) {
|
||||
bool is_help = false;
|
||||
const parse_node_tree_t::parse_node_list_t arg_nodes =
|
||||
node_tree.find_nodes(node, symbol_argument, 1);
|
||||
auto arg_nodes = get_argument_nodes(statement.child<1>());
|
||||
if (!arg_nodes.empty()) {
|
||||
// Check the first argument only.
|
||||
const parse_node_t &arg = *arg_nodes.at(0);
|
||||
const wcstring first_arg_src = arg.get_source(src);
|
||||
wcstring first_arg_src = arg_nodes.front().get_source(src);
|
||||
is_help = parse_util_argument_is_help(first_arg_src.c_str());
|
||||
}
|
||||
return is_help;
|
||||
|
@ -1167,13 +1165,14 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src,
|
|||
errored |= detect_errors_in_backgrounded_job(node_tree, job, &parse_errors);
|
||||
}
|
||||
} else if (node.type == symbol_plain_statement) {
|
||||
using namespace grammar;
|
||||
tnode_t<plain_statement> statement{&node_tree, &node};
|
||||
// In a few places below, we want to know if we are in a pipeline.
|
||||
const bool is_in_pipeline =
|
||||
node_tree.statement_is_in_pipeline(node, true /* count first */);
|
||||
node_tree.statement_is_in_pipeline(statement, true /* count first */);
|
||||
|
||||
// We need to know the decoration.
|
||||
const enum parse_statement_decoration_t decoration =
|
||||
get_decoration({&node_tree, &node});
|
||||
const enum parse_statement_decoration_t decoration = get_decoration(statement);
|
||||
|
||||
// Check that we don't try to pipe through exec.
|
||||
if (is_in_pipeline && decoration == parse_statement_decoration_exec) {
|
||||
|
@ -1183,7 +1182,6 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src,
|
|||
|
||||
if (maybe_t<wcstring> mcommand =
|
||||
command_for_plain_statement({&node_tree, &node}, buff_src)) {
|
||||
using namespace grammar;
|
||||
wcstring command = std::move(*mcommand);
|
||||
// Check that we can expand the command.
|
||||
if (!expand_one(command,
|
||||
|
@ -1214,7 +1212,7 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src,
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!found_function && !first_argument_is_help(node_tree, node, buff_src)) {
|
||||
if (!found_function && !first_argument_is_help(statement, buff_src)) {
|
||||
errored = append_syntax_error(&parse_errors, node.source_start,
|
||||
INVALID_RETURN_ERR_MSG);
|
||||
}
|
||||
|
@ -1246,7 +1244,7 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src,
|
|||
}
|
||||
}
|
||||
|
||||
if (!found_loop && !first_argument_is_help(node_tree, node, buff_src)) {
|
||||
if (!found_loop && !first_argument_is_help(statement, buff_src)) {
|
||||
errored = append_syntax_error(
|
||||
&parse_errors, node.source_start,
|
||||
(command == L"break" ? INVALID_BREAK_ERR_MSG
|
||||
|
|
Loading…
Reference in a new issue