From 094e853a20b3812e0ba1af6f75741f69c53609db Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 20 Jan 2018 11:58:57 -0800 Subject: [PATCH] Migrate tnode_t into new header tnode.h --- src/complete.cpp | 2 +- src/fish_indent.cpp | 2 +- src/fish_tests.cpp | 1 + src/highlight.cpp | 2 +- src/history.cpp | 4 +- src/parse_execution.cpp | 1 + src/parse_tree.cpp | 1 + src/parse_tree.h | 237 -------------------------------------- src/parse_util.cpp | 2 +- src/parser.cpp | 2 +- src/reader.cpp | 2 +- src/tnode.h | 245 ++++++++++++++++++++++++++++++++++++++++ 12 files changed, 256 insertions(+), 245 deletions(-) create mode 100644 src/tnode.h diff --git a/src/complete.cpp b/src/complete.cpp index b61d6ba1f..fbb4e4799 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -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 diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index ca1ce7c47..801b835ca 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -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 diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index e9946e756..808e07ba7 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -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" diff --git a/src/highlight.cpp b/src/highlight.cpp index c7e41548c..e419230ad 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -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 diff --git a/src/history.cpp b/src/history.cpp index 37af02161..8023a496a 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -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: // diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 50f692503..551b0fdc7 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -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" diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 8f74f575c..e049b229b 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -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 diff --git a/src/parse_tree.h b/src/parse_tree.h index 59a2250a8..6f48fcfb6 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -200,243 +200,6 @@ class parse_node_tree_t : public std::vector { 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 -constexpr bool child_type_possible_at_index() { - return Parent::template type_possible(); -} - -// 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 -constexpr bool child_type_possible() { - return child_type_possible_at_index() || - child_type_possible_at_index() || - child_type_possible_at_index() || - child_type_possible_at_index() || - child_type_possible_at_index() || - child_type_possible_at_index(); -} - -/// A helper for type-safe manipulation of parse nodes. -/// This is a lightweight value-type class. -template -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 - using child_at = typename std::tuple_element::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() 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 - tnode_t> child() const { - using child_type = child_at; - const parse_node_t *child = nullptr; - if (nodeptr) child = tree->get_child(*nodeptr, Index, child_type::token); - return tnode_t{tree, child}; - } - - /// Return a parse_node_t for a child. - /// This is used to disambiguate alts. - template - 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 - tnode_t try_get_child() const { - static_assert(child_type_possible_at_index(), - "Cannot contain a child of this type"); - const parse_node_t *child = nullptr; - if (nodeptr) child = &get_child_node(); - 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 - tnode_t require_get_child() const { - assert(nodeptr && "receiver is missing in require_get_child()"); - auto result = try_get_child(); - 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 - tnode_t find_child() const { - assert(nodeptr && "receiver is missing in find_child()"); - tnode_t 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 - tnode_t try_get_parent() const { - static_assert(child_type_possible(), "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 - std::vector> descendants(size_t max_count = -1) const { - if (!nodeptr) return {}; - std::vector> result; - std::vector 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 - tnode_t 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 -tnode_t parse_node_tree_t::find_child(const parse_node_t &parent) const { - return tnode_t(this, &this->find_child(parent, Type::token)); -} - -template -tnode_t parse_node_tree_t::find_last_node(const parse_node_t *parent) const { - return tnode_t(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 command_for_plain_statement(tnode_t stmt, - const wcstring &src); - -/// Return the decoration for a plain statement. -parse_statement_decoration_t get_decoration(tnode_t stmt); - -/// Return the type for a boolean statement. -enum parse_bool_statement_type_t bool_statement_type(tnode_t stmt); - -/// Given a redirection, get the redirection type (or TOK_NONE) and target (file path, or fd). -enum token_type redirection_type(tnode_t 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>; -arguments_node_list_t get_argument_nodes(tnode_t, size_t max = -1); -arguments_node_list_t get_argument_nodes(tnode_t, - size_t max = -1); - -/// Return whether the given job is background because it has a & symbol. -bool job_node_is_background(tnode_t); - -/// 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 st, bool include_first); - -/// Check whether an argument_list is a root list. -inline bool argument_list_is_root(tnode_t list) { - return !list.try_get_parent(); -} - -inline bool argument_list_is_root(tnode_t list) { - return !list.try_get_parent(); -} - /// 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, diff --git a/src/parse_util.cpp b/src/parse_util.cpp index ae54380ae..86c49735d 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -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" diff --git a/src/parser.cpp b/src/parser.cpp index 4ee773271..4b91d5642 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -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; diff --git a/src/reader.cpp b/src/reader.cpp index cc77f5ada..ae22a64f5 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -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 diff --git a/src/tnode.h b/src/tnode.h new file mode 100644 index 000000000..9dccdde3b --- /dev/null +++ b/src/tnode.h @@ -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 +constexpr bool child_type_possible_at_index() { + return Parent::template type_possible(); +} + +// 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 +constexpr bool child_type_possible() { + return child_type_possible_at_index() || + child_type_possible_at_index() || + child_type_possible_at_index() || + child_type_possible_at_index() || + child_type_possible_at_index() || + child_type_possible_at_index(); +} + +/// A helper for type-safe manipulation of parse nodes. +/// This is a lightweight value-type class. +template +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 + using child_at = typename std::tuple_element::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() 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 + tnode_t> child() const { + using child_type = child_at; + const parse_node_t *child = nullptr; + if (nodeptr) child = tree->get_child(*nodeptr, Index, child_type::token); + return tnode_t{tree, child}; + } + + /// Return a parse_node_t for a child. + /// This is used to disambiguate alts. + template + 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 + tnode_t try_get_child() const { + static_assert(child_type_possible_at_index(), + "Cannot contain a child of this type"); + const parse_node_t *child = nullptr; + if (nodeptr) child = &get_child_node(); + 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 + tnode_t require_get_child() const { + assert(nodeptr && "receiver is missing in require_get_child()"); + auto result = try_get_child(); + 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 + tnode_t find_child() const { + assert(nodeptr && "receiver is missing in find_child()"); + tnode_t 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 + tnode_t try_get_parent() const { + static_assert(child_type_possible(), "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 + std::vector> descendants(size_t max_count = -1) const { + if (!nodeptr) return {}; + std::vector> result; + std::vector 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 + tnode_t 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 +tnode_t parse_node_tree_t::find_child(const parse_node_t &parent) const { + return tnode_t(this, &this->find_child(parent, Type::token)); +} + +template +tnode_t parse_node_tree_t::find_last_node(const parse_node_t *parent) const { + return tnode_t(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 command_for_plain_statement(tnode_t stmt, + const wcstring &src); + +/// Return the decoration for a plain statement. +parse_statement_decoration_t get_decoration(tnode_t stmt); + +/// Return the type for a boolean statement. +enum parse_bool_statement_type_t bool_statement_type(tnode_t stmt); + +/// Given a redirection, get the redirection type (or TOK_NONE) and target (file path, or fd). +enum token_type redirection_type(tnode_t 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>; +arguments_node_list_t get_argument_nodes(tnode_t, size_t max = -1); +arguments_node_list_t get_argument_nodes(tnode_t, + size_t max = -1); + +/// Return whether the given job is background because it has a & symbol. +bool job_node_is_background(tnode_t); + +/// 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 st, bool include_first); + +/// Check whether an argument_list is a root list. +inline bool argument_list_is_root(tnode_t list) { + return !list.try_get_parent(); +} + +inline bool argument_list_is_root(tnode_t list) { + return !list.try_get_parent(); +} + +#endif