mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 05:13:10 +00:00
Migrate tnode_t into new header tnode.h
This commit is contained in:
parent
1c2943bd8b
commit
094e853a20
12 changed files with 256 additions and 245 deletions
|
@ -34,11 +34,11 @@
|
||||||
#include "function.h"
|
#include "function.h"
|
||||||
#include "iothread.h"
|
#include "iothread.h"
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
#include "parse_tree.h"
|
|
||||||
#include "parse_util.h"
|
#include "parse_util.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "wildcard.h"
|
#include "wildcard.h"
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
|
@ -38,8 +38,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
#include "highlight.h"
|
#include "highlight.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
#include "parse_tree.h"
|
|
||||||
#include "print_help.h"
|
#include "print_help.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
||||||
#define SPACES_PER_INDENT 4
|
#define SPACES_PER_INDENT 4
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "signal.h"
|
#include "signal.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "tokenizer.h"
|
#include "tokenizer.h"
|
||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
|
@ -27,9 +27,9 @@
|
||||||
#include "history.h"
|
#include "history.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
#include "parse_tree.h"
|
|
||||||
#include "parse_util.h"
|
#include "parse_util.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "tokenizer.h"
|
#include "tokenizer.h"
|
||||||
#include "wildcard.h"
|
#include "wildcard.h"
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
|
@ -36,12 +36,12 @@
|
||||||
#include "iothread.h"
|
#include "iothread.h"
|
||||||
#include "lru.h"
|
#include "lru.h"
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
#include "parse_tree.h"
|
|
||||||
#include "parse_util.h"
|
#include "parse_util.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "wildcard.h" // IWYU pragma: keep
|
#include "wildcard.h" // IWYU pragma: keep
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
||||||
// Our history format is intended to be valid YAML. Here it is:
|
// Our history format is intended to be valid YAML. Here it is:
|
||||||
//
|
//
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
#include "tokenizer.h"
|
#include "tokenizer.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "wildcard.h"
|
#include "wildcard.h"
|
||||||
#include "wutil.h"
|
#include "wutil.h"
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "parse_productions.h"
|
#include "parse_productions.h"
|
||||||
#include "parse_tree.h"
|
#include "parse_tree.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "tokenizer.h"
|
#include "tokenizer.h"
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
|
237
src/parse_tree.h
237
src/parse_tree.h
|
@ -200,243 +200,6 @@ class parse_node_tree_t : public std::vector<parse_node_t> {
|
||||||
const parse_node_t *parent) const;
|
const parse_node_t *parent) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct source_range_t {
|
|
||||||
uint32_t start;
|
|
||||||
uint32_t length;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check if a child type is possible for a parent type at a given index.
|
|
||||||
template <typename Parent, typename Child, size_t Index>
|
|
||||||
constexpr bool child_type_possible_at_index() {
|
|
||||||
return Parent::template type_possible<Child, Index>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a child type is possible for a parent type at any index.
|
|
||||||
// 5 is arbitrary and represents the longest production we have.
|
|
||||||
template <typename Parent, typename Child>
|
|
||||||
constexpr bool child_type_possible() {
|
|
||||||
return child_type_possible_at_index<Parent, Child, 0>() ||
|
|
||||||
child_type_possible_at_index<Parent, Child, 1>() ||
|
|
||||||
child_type_possible_at_index<Parent, Child, 2>() ||
|
|
||||||
child_type_possible_at_index<Parent, Child, 3>() ||
|
|
||||||
child_type_possible_at_index<Parent, Child, 4>() ||
|
|
||||||
child_type_possible_at_index<Parent, Child, 5>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper for type-safe manipulation of parse nodes.
|
|
||||||
/// This is a lightweight value-type class.
|
|
||||||
template <typename Type>
|
|
||||||
class tnode_t {
|
|
||||||
/// The tree containing our node.
|
|
||||||
const parse_node_tree_t *tree = nullptr;
|
|
||||||
|
|
||||||
/// The node in the tree
|
|
||||||
const parse_node_t *nodeptr = nullptr;
|
|
||||||
|
|
||||||
// Helper to get a child type at a given index.
|
|
||||||
template <class Element, uint32_t Index>
|
|
||||||
using child_at = typename std::tuple_element<Index, typename Element::type_tuple>::type;
|
|
||||||
|
|
||||||
public:
|
|
||||||
tnode_t() = default;
|
|
||||||
|
|
||||||
tnode_t(const parse_node_tree_t *t, const parse_node_t *n) : tree(t), nodeptr(n) {
|
|
||||||
assert(t && "tree cannot be null in this constructor");
|
|
||||||
assert((!n || n->type == Type::token) && "node has wrong type");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to create a tnode from the given tree and parse node.
|
|
||||||
// Returns an empty node if the parse node is null, or has the wrong type.
|
|
||||||
static tnode_t try_create(const parse_node_tree_t *tree, const parse_node_t *node) {
|
|
||||||
assert(tree && "tree cannot be null");
|
|
||||||
return tnode_t(tree, node && node->type == Type::token ? node : nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Temporary conversion to parse_node_t to assist in migration.
|
|
||||||
/* implicit */ operator const parse_node_t &() const {
|
|
||||||
assert(nodeptr && "Empty tnode_t");
|
|
||||||
return *nodeptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* implicit */ operator const parse_node_t *() const { return nodeptr; }
|
|
||||||
|
|
||||||
/// Return the underlying (type-erased) node.
|
|
||||||
const parse_node_t *node() const { return nodeptr; }
|
|
||||||
|
|
||||||
/// Check whether we're populated.
|
|
||||||
explicit operator bool() const { return nodeptr != nullptr; }
|
|
||||||
|
|
||||||
bool operator==(const tnode_t &rhs) const { return tree == rhs.tree && nodeptr == rhs.nodeptr; }
|
|
||||||
|
|
||||||
bool operator!=(const tnode_t &rhs) const { return !(*this == rhs); }
|
|
||||||
|
|
||||||
bool has_source() const { return nodeptr && nodeptr->has_source(); }
|
|
||||||
|
|
||||||
// return the tag, or 0 if missing.
|
|
||||||
parse_node_tag_t tag() const { return nodeptr ? nodeptr->tag : 0; }
|
|
||||||
|
|
||||||
// return the number of children, or 0 if missing.
|
|
||||||
uint8_t child_count() const { return nodeptr ? nodeptr->child_count : 0; }
|
|
||||||
|
|
||||||
maybe_t<source_range_t> source_range() const {
|
|
||||||
if (!has_source()) return none();
|
|
||||||
return source_range_t{nodeptr->source_start, nodeptr->source_length};
|
|
||||||
}
|
|
||||||
|
|
||||||
wcstring get_source(const wcstring &str) const {
|
|
||||||
assert(has_source() && "Source missing");
|
|
||||||
return nodeptr->get_source(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool location_in_or_at_end_of_source_range(size_t loc) const {
|
|
||||||
return nodeptr && nodeptr->location_in_or_at_end_of_source_range(loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static tnode_t find_node_matching_source_location(const parse_node_tree_t *tree,
|
|
||||||
size_t source_loc,
|
|
||||||
const parse_node_t *parent) {
|
|
||||||
assert(tree && "null tree");
|
|
||||||
return tnode_t{tree,
|
|
||||||
tree->find_node_matching_source_location(Type::token, source_loc, parent)};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type-safe access to a child at the given index.
|
|
||||||
template <node_offset_t Index>
|
|
||||||
tnode_t<child_at<Type, Index>> child() const {
|
|
||||||
using child_type = child_at<Type, Index>;
|
|
||||||
const parse_node_t *child = nullptr;
|
|
||||||
if (nodeptr) child = tree->get_child(*nodeptr, Index, child_type::token);
|
|
||||||
return tnode_t<child_type>{tree, child};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a parse_node_t for a child.
|
|
||||||
/// This is used to disambiguate alts.
|
|
||||||
template <node_offset_t Index>
|
|
||||||
const parse_node_t &get_child_node() const {
|
|
||||||
assert(nodeptr && "receiver is missing in get_child_node");
|
|
||||||
return *tree->get_child(*nodeptr, Index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If the child at the given index has the given type, return it; otherwise return an empty
|
|
||||||
/// child. Note this will refuse to compile if the child type is not possible.
|
|
||||||
/// This is used for e.g. alternations.
|
|
||||||
template <class ChildType, node_offset_t Index>
|
|
||||||
tnode_t<ChildType> try_get_child() const {
|
|
||||||
static_assert(child_type_possible_at_index<Type, ChildType, Index>(),
|
|
||||||
"Cannot contain a child of this type");
|
|
||||||
const parse_node_t *child = nullptr;
|
|
||||||
if (nodeptr) child = &get_child_node<Index>();
|
|
||||||
if (child && child->type == ChildType::token) return {tree, child};
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// assert that this is not empty and that the child at index Index has the given type, then
|
|
||||||
/// return that child. Note this will refuse to compile if the child type is not possible.
|
|
||||||
template <class ChildType, node_offset_t Index>
|
|
||||||
tnode_t<ChildType> require_get_child() const {
|
|
||||||
assert(nodeptr && "receiver is missing in require_get_child()");
|
|
||||||
auto result = try_get_child<ChildType, Index>();
|
|
||||||
assert(result && "require_get_child(): wrong child type");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find the first direct child of the given node of the given type. asserts on failure.
|
|
||||||
template <class ChildType>
|
|
||||||
tnode_t<ChildType> find_child() const {
|
|
||||||
assert(nodeptr && "receiver is missing in find_child()");
|
|
||||||
tnode_t<ChildType> result{tree, &tree->find_child(*nodeptr, ChildType::token)};
|
|
||||||
assert(result && "cannot find child");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type-safe access to a node's parent.
|
|
||||||
/// If the parent exists and has type ParentType, return it.
|
|
||||||
/// Otherwise return a missing tnode.
|
|
||||||
template <class ParentType>
|
|
||||||
tnode_t<ParentType> try_get_parent() const {
|
|
||||||
static_assert(child_type_possible<ParentType, Type>(), "Parent cannot have us as a child");
|
|
||||||
if (!nodeptr) return {};
|
|
||||||
return {tree, tree->get_parent(*nodeptr, ParentType::token)};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds all descendants (up to max_count) under this node of the given type.
|
|
||||||
template <typename DescendantType>
|
|
||||||
std::vector<tnode_t<DescendantType>> descendants(size_t max_count = -1) const {
|
|
||||||
if (!nodeptr) return {};
|
|
||||||
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,
|
|
||||||
/// adjusting 'this' to be the remainder of the list.
|
|
||||||
/// Returns an empty item on failure.
|
|
||||||
template <class ItemType>
|
|
||||||
tnode_t<ItemType> next_in_list() {
|
|
||||||
if (!nodeptr) return {};
|
|
||||||
const parse_node_t *next =
|
|
||||||
tree->next_node_in_node_list(*nodeptr, ItemType::token, &nodeptr);
|
|
||||||
return {tree, next};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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);
|
|
||||||
|
|
||||||
/// Return the decoration for a plain statement.
|
|
||||||
parse_statement_decoration_t get_decoration(tnode_t<grammar::plain_statement> stmt);
|
|
||||||
|
|
||||||
/// Return the type for a boolean statement.
|
|
||||||
enum parse_bool_statement_type_t bool_statement_type(tnode_t<grammar::boolean_statement> stmt);
|
|
||||||
|
|
||||||
/// Given a redirection, get the redirection type (or TOK_NONE) and target (file path, or fd).
|
|
||||||
enum token_type redirection_type(tnode_t<grammar::redirection> redirection, const wcstring &src,
|
|
||||||
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>, 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>);
|
|
||||||
|
|
||||||
/// Return whether 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.
|
|
||||||
bool statement_is_in_pipeline(tnode_t<grammar::statement> st, bool include_first);
|
|
||||||
|
|
||||||
/// Check whether an argument_list is a root list.
|
|
||||||
inline bool argument_list_is_root(tnode_t<grammar::argument_list> list) {
|
|
||||||
return !list.try_get_parent<grammar::argument_list>();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool argument_list_is_root(tnode_t<grammar::arguments_or_redirections_list> list) {
|
|
||||||
return !list.try_get_parent<grammar::arguments_or_redirections_list>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The big entry point. Parse a string, attempting to produce a tree for the given goal type.
|
/// 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,
|
bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags,
|
||||||
parse_node_tree_t *output, parse_error_list_t *errors,
|
parse_node_tree_t *output, parse_error_list_t *errors,
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
#include "expand.h"
|
#include "expand.h"
|
||||||
#include "fallback.h" // IWYU pragma: keep
|
#include "fallback.h" // IWYU pragma: keep
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
#include "parse_tree.h"
|
|
||||||
#include "parse_util.h"
|
#include "parse_util.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "tokenizer.h"
|
#include "tokenizer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "wildcard.h"
|
#include "wildcard.h"
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
#include "intern.h"
|
#include "intern.h"
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
#include "parse_execution.h"
|
#include "parse_execution.h"
|
||||||
#include "parse_tree.h"
|
|
||||||
#include "parse_util.h"
|
#include "parse_util.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
#include "sanity.h"
|
#include "sanity.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
||||||
class io_chain_t;
|
class io_chain_t;
|
||||||
|
|
|
@ -62,7 +62,6 @@
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "parse_constants.h"
|
#include "parse_constants.h"
|
||||||
#include "parse_tree.h"
|
|
||||||
#include "parse_util.h"
|
#include "parse_util.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "proc.h"
|
#include "proc.h"
|
||||||
|
@ -70,6 +69,7 @@
|
||||||
#include "sanity.h"
|
#include "sanity.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "signal.h"
|
#include "signal.h"
|
||||||
|
#include "tnode.h"
|
||||||
#include "tokenizer.h"
|
#include "tokenizer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "wutil.h" // IWYU pragma: keep
|
#include "wutil.h" // IWYU pragma: keep
|
||||||
|
|
245
src/tnode.h
Normal file
245
src/tnode.h
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
// Type-safe access to fish parse trees.
|
||||||
|
#ifndef FISH_TNODE_H
|
||||||
|
#define FISH_TNODE_H
|
||||||
|
|
||||||
|
#include "parse_grammar.h"
|
||||||
|
#include "parse_tree.h"
|
||||||
|
|
||||||
|
struct source_range_t {
|
||||||
|
uint32_t start;
|
||||||
|
uint32_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if a child type is possible for a parent type at a given index.
|
||||||
|
template <typename Parent, typename Child, size_t Index>
|
||||||
|
constexpr bool child_type_possible_at_index() {
|
||||||
|
return Parent::template type_possible<Child, Index>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a child type is possible for a parent type at any index.
|
||||||
|
// 5 is arbitrary and represents the longest production we have.
|
||||||
|
template <typename Parent, typename Child>
|
||||||
|
constexpr bool child_type_possible() {
|
||||||
|
return child_type_possible_at_index<Parent, Child, 0>() ||
|
||||||
|
child_type_possible_at_index<Parent, Child, 1>() ||
|
||||||
|
child_type_possible_at_index<Parent, Child, 2>() ||
|
||||||
|
child_type_possible_at_index<Parent, Child, 3>() ||
|
||||||
|
child_type_possible_at_index<Parent, Child, 4>() ||
|
||||||
|
child_type_possible_at_index<Parent, Child, 5>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A helper for type-safe manipulation of parse nodes.
|
||||||
|
/// This is a lightweight value-type class.
|
||||||
|
template <typename Type>
|
||||||
|
class tnode_t {
|
||||||
|
/// The tree containing our node.
|
||||||
|
const parse_node_tree_t *tree = nullptr;
|
||||||
|
|
||||||
|
/// The node in the tree
|
||||||
|
const parse_node_t *nodeptr = nullptr;
|
||||||
|
|
||||||
|
// Helper to get a child type at a given index.
|
||||||
|
template <class Element, uint32_t Index>
|
||||||
|
using child_at = typename std::tuple_element<Index, typename Element::type_tuple>::type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
tnode_t() = default;
|
||||||
|
|
||||||
|
tnode_t(const parse_node_tree_t *t, const parse_node_t *n) : tree(t), nodeptr(n) {
|
||||||
|
assert(t && "tree cannot be null in this constructor");
|
||||||
|
assert((!n || n->type == Type::token) && "node has wrong type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create a tnode from the given tree and parse node.
|
||||||
|
// Returns an empty node if the parse node is null, or has the wrong type.
|
||||||
|
static tnode_t try_create(const parse_node_tree_t *tree, const parse_node_t *node) {
|
||||||
|
assert(tree && "tree cannot be null");
|
||||||
|
return tnode_t(tree, node && node->type == Type::token ? node : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Temporary conversion to parse_node_t to assist in migration.
|
||||||
|
/* implicit */ operator const parse_node_t &() const {
|
||||||
|
assert(nodeptr && "Empty tnode_t");
|
||||||
|
return *nodeptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* implicit */ operator const parse_node_t *() const { return nodeptr; }
|
||||||
|
|
||||||
|
/// Return the underlying (type-erased) node.
|
||||||
|
const parse_node_t *node() const { return nodeptr; }
|
||||||
|
|
||||||
|
/// Check whether we're populated.
|
||||||
|
explicit operator bool() const { return nodeptr != nullptr; }
|
||||||
|
|
||||||
|
bool operator==(const tnode_t &rhs) const { return tree == rhs.tree && nodeptr == rhs.nodeptr; }
|
||||||
|
|
||||||
|
bool operator!=(const tnode_t &rhs) const { return !(*this == rhs); }
|
||||||
|
|
||||||
|
bool has_source() const { return nodeptr && nodeptr->has_source(); }
|
||||||
|
|
||||||
|
// return the tag, or 0 if missing.
|
||||||
|
parse_node_tag_t tag() const { return nodeptr ? nodeptr->tag : 0; }
|
||||||
|
|
||||||
|
// return the number of children, or 0 if missing.
|
||||||
|
uint8_t child_count() const { return nodeptr ? nodeptr->child_count : 0; }
|
||||||
|
|
||||||
|
maybe_t<source_range_t> source_range() const {
|
||||||
|
if (!has_source()) return none();
|
||||||
|
return source_range_t{nodeptr->source_start, nodeptr->source_length};
|
||||||
|
}
|
||||||
|
|
||||||
|
wcstring get_source(const wcstring &str) const {
|
||||||
|
assert(has_source() && "Source missing");
|
||||||
|
return nodeptr->get_source(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool location_in_or_at_end_of_source_range(size_t loc) const {
|
||||||
|
return nodeptr && nodeptr->location_in_or_at_end_of_source_range(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static tnode_t find_node_matching_source_location(const parse_node_tree_t *tree,
|
||||||
|
size_t source_loc,
|
||||||
|
const parse_node_t *parent) {
|
||||||
|
assert(tree && "null tree");
|
||||||
|
return tnode_t{tree,
|
||||||
|
tree->find_node_matching_source_location(Type::token, source_loc, parent)};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type-safe access to a child at the given index.
|
||||||
|
template <node_offset_t Index>
|
||||||
|
tnode_t<child_at<Type, Index>> child() const {
|
||||||
|
using child_type = child_at<Type, Index>;
|
||||||
|
const parse_node_t *child = nullptr;
|
||||||
|
if (nodeptr) child = tree->get_child(*nodeptr, Index, child_type::token);
|
||||||
|
return tnode_t<child_type>{tree, child};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a parse_node_t for a child.
|
||||||
|
/// This is used to disambiguate alts.
|
||||||
|
template <node_offset_t Index>
|
||||||
|
const parse_node_t &get_child_node() const {
|
||||||
|
assert(nodeptr && "receiver is missing in get_child_node");
|
||||||
|
return *tree->get_child(*nodeptr, Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the child at the given index has the given type, return it; otherwise return an empty
|
||||||
|
/// child. Note this will refuse to compile if the child type is not possible.
|
||||||
|
/// This is used for e.g. alternations.
|
||||||
|
template <class ChildType, node_offset_t Index>
|
||||||
|
tnode_t<ChildType> try_get_child() const {
|
||||||
|
static_assert(child_type_possible_at_index<Type, ChildType, Index>(),
|
||||||
|
"Cannot contain a child of this type");
|
||||||
|
const parse_node_t *child = nullptr;
|
||||||
|
if (nodeptr) child = &get_child_node<Index>();
|
||||||
|
if (child && child->type == ChildType::token) return {tree, child};
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// assert that this is not empty and that the child at index Index has the given type, then
|
||||||
|
/// return that child. Note this will refuse to compile if the child type is not possible.
|
||||||
|
template <class ChildType, node_offset_t Index>
|
||||||
|
tnode_t<ChildType> require_get_child() const {
|
||||||
|
assert(nodeptr && "receiver is missing in require_get_child()");
|
||||||
|
auto result = try_get_child<ChildType, Index>();
|
||||||
|
assert(result && "require_get_child(): wrong child type");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the first direct child of the given node of the given type. asserts on failure.
|
||||||
|
template <class ChildType>
|
||||||
|
tnode_t<ChildType> find_child() const {
|
||||||
|
assert(nodeptr && "receiver is missing in find_child()");
|
||||||
|
tnode_t<ChildType> result{tree, &tree->find_child(*nodeptr, ChildType::token)};
|
||||||
|
assert(result && "cannot find child");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type-safe access to a node's parent.
|
||||||
|
/// If the parent exists and has type ParentType, return it.
|
||||||
|
/// Otherwise return a missing tnode.
|
||||||
|
template <class ParentType>
|
||||||
|
tnode_t<ParentType> try_get_parent() const {
|
||||||
|
static_assert(child_type_possible<ParentType, Type>(), "Parent cannot have us as a child");
|
||||||
|
if (!nodeptr) return {};
|
||||||
|
return {tree, tree->get_parent(*nodeptr, ParentType::token)};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds all descendants (up to max_count) under this node of the given type.
|
||||||
|
template <typename DescendantType>
|
||||||
|
std::vector<tnode_t<DescendantType>> descendants(size_t max_count = -1) const {
|
||||||
|
if (!nodeptr) return {};
|
||||||
|
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,
|
||||||
|
/// adjusting 'this' to be the remainder of the list.
|
||||||
|
/// Returns an empty item on failure.
|
||||||
|
template <class ItemType>
|
||||||
|
tnode_t<ItemType> next_in_list() {
|
||||||
|
if (!nodeptr) return {};
|
||||||
|
const parse_node_t *next =
|
||||||
|
tree->next_node_in_node_list(*nodeptr, ItemType::token, &nodeptr);
|
||||||
|
return {tree, next};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
/// Return the decoration for a plain statement.
|
||||||
|
parse_statement_decoration_t get_decoration(tnode_t<grammar::plain_statement> stmt);
|
||||||
|
|
||||||
|
/// Return the type for a boolean statement.
|
||||||
|
enum parse_bool_statement_type_t bool_statement_type(tnode_t<grammar::boolean_statement> stmt);
|
||||||
|
|
||||||
|
/// Given a redirection, get the redirection type (or TOK_NONE) and target (file path, or fd).
|
||||||
|
enum token_type redirection_type(tnode_t<grammar::redirection> redirection, const wcstring &src,
|
||||||
|
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>, 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>);
|
||||||
|
|
||||||
|
/// Return whether 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.
|
||||||
|
bool statement_is_in_pipeline(tnode_t<grammar::statement> st, bool include_first);
|
||||||
|
|
||||||
|
/// Check whether an argument_list is a root list.
|
||||||
|
inline bool argument_list_is_root(tnode_t<grammar::argument_list> list) {
|
||||||
|
return !list.try_get_parent<grammar::argument_list>();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool argument_list_is_root(tnode_t<grammar::arguments_or_redirections_list> list) {
|
||||||
|
return !list.try_get_parent<grammar::arguments_or_redirections_list>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue