Migrate tnode_t into new header tnode.h

This commit is contained in:
ridiculousfish 2018-01-20 11:58:57 -08:00
parent 1c2943bd8b
commit 094e853a20
12 changed files with 256 additions and 245 deletions

View file

@ -34,11 +34,11 @@
#include "function.h"
#include "iothread.h"
#include "parse_constants.h"
#include "parse_tree.h"
#include "parse_util.h"
#include "parser.h"
#include "path.h"
#include "proc.h"
#include "tnode.h"
#include "util.h"
#include "wildcard.h"
#include "wutil.h" // IWYU pragma: keep

View file

@ -38,8 +38,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "highlight.h"
#include "output.h"
#include "parse_constants.h"
#include "parse_tree.h"
#include "print_help.h"
#include "tnode.h"
#include "wutil.h" // IWYU pragma: keep
#define SPACES_PER_INDENT 4

View file

@ -63,6 +63,7 @@
#include "reader.h"
#include "screen.h"
#include "signal.h"
#include "tnode.h"
#include "tokenizer.h"
#include "utf8.h"
#include "util.h"

View file

@ -27,9 +27,9 @@
#include "history.h"
#include "output.h"
#include "parse_constants.h"
#include "parse_tree.h"
#include "parse_util.h"
#include "path.h"
#include "tnode.h"
#include "tokenizer.h"
#include "wildcard.h"
#include "wutil.h" // IWYU pragma: keep

View file

@ -36,12 +36,12 @@
#include "iothread.h"
#include "lru.h"
#include "parse_constants.h"
#include "parse_tree.h"
#include "parse_util.h"
#include "path.h"
#include "reader.h"
#include "tnode.h"
#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:
//

View file

@ -43,6 +43,7 @@
#include "proc.h"
#include "reader.h"
#include "tokenizer.h"
#include "tnode.h"
#include "util.h"
#include "wildcard.h"
#include "wutil.h"

View file

@ -17,6 +17,7 @@
#include "parse_productions.h"
#include "parse_tree.h"
#include "proc.h"
#include "tnode.h"
#include "tokenizer.h"
#include "wutil.h" // IWYU pragma: keep

View file

@ -200,243 +200,6 @@ class parse_node_tree_t : public std::vector<parse_node_t> {
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.
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

@ -17,8 +17,8 @@
#include "expand.h"
#include "fallback.h" // IWYU pragma: keep
#include "parse_constants.h"
#include "parse_tree.h"
#include "parse_util.h"
#include "tnode.h"
#include "tokenizer.h"
#include "util.h"
#include "wildcard.h"

View file

@ -16,12 +16,12 @@
#include "intern.h"
#include "parse_constants.h"
#include "parse_execution.h"
#include "parse_tree.h"
#include "parse_util.h"
#include "parser.h"
#include "proc.h"
#include "reader.h"
#include "sanity.h"
#include "tnode.h"
#include "wutil.h" // IWYU pragma: keep
class io_chain_t;

View file

@ -62,7 +62,6 @@
#include "output.h"
#include "pager.h"
#include "parse_constants.h"
#include "parse_tree.h"
#include "parse_util.h"
#include "parser.h"
#include "proc.h"
@ -70,6 +69,7 @@
#include "sanity.h"
#include "screen.h"
#include "signal.h"
#include "tnode.h"
#include "tokenizer.h"
#include "util.h"
#include "wutil.h" // IWYU pragma: keep

245
src/tnode.h Normal file
View 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