Migrate tnode implementation to tnode.cpp

Also improve commenting.
This commit is contained in:
ridiculousfish 2018-01-20 13:14:29 -08:00
parent 094e853a20
commit 0f8e8d1cea
7 changed files with 171 additions and 147 deletions

View file

@ -50,7 +50,7 @@ SET(FISH_SRCS
src/parse_execution.cpp src/parse_productions.cpp src/parse_tree.cpp
src/parse_util.cpp src/parser.cpp src/parser_keywords.cpp src/path.cpp
src/postfork.cpp src/proc.cpp src/reader.cpp src/sanity.cpp src/screen.cpp
src/signal.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp
src/signal.cpp src/tnode.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp
src/wcstringutil.cpp src/wgetopt.cpp src/wildcard.cpp src/wutil.cpp
)

View file

@ -124,8 +124,8 @@ FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bg.o obj/builtin_bind.o ob
obj/iothread.o obj/kill.o obj/output.o obj/pager.o obj/parse_execution.o \
obj/parse_productions.o obj/parse_tree.o obj/parse_util.o obj/parser.o \
obj/parser_keywords.o obj/path.o obj/postfork.o obj/proc.o obj/reader.o \
obj/sanity.o obj/screen.o obj/signal.o obj/tokenizer.o obj/utf8.o obj/util.o \
obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o
obj/sanity.o obj/screen.o obj/signal.o obj/tokenizer.o obj/tnode.o obj/utf8.o \
obj/util.o obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o
FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS)

View file

@ -67,6 +67,9 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
4F2D55CF2013ECDD00822920 /* tnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F2D55CE2013ECDD00822920 /* tnode.cpp */; };
4F2D55D02013ECDD00822920 /* tnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F2D55CE2013ECDD00822920 /* tnode.cpp */; };
4F2D55D12013ED0100822920 /* tnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F2D55CE2013ECDD00822920 /* tnode.cpp */; };
63A2C0E91CC60F3B00973404 /* pcre2_find_bracket.c in Sources */ = {isa = PBXBuildFile; fileRef = 63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */; };
9C7A55271DCD651F0049C25D /* fallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853E13B3ACEE0099B651 /* fallback.cpp */; };
9C7A552F1DCD65820049C25D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855E13B3ACEE0099B651 /* util.cpp */; };
@ -690,6 +693,8 @@
/* Begin PBXFileReference section */
4E142D731B56B5D7008783C8 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = ../osx/config.h; sourceTree = "<group>"; };
4F2D55CD2013ECDD00822920 /* tnode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tnode.h; sourceTree = "<group>"; };
4F2D55CE2013ECDD00822920 /* tnode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tnode.cpp; sourceTree = "<group>"; };
63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_find_bracket.c; sourceTree = "<group>"; };
9C7A55721DCD71330049C25D /* fish_key_reader */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish_key_reader; sourceTree = BUILT_PRODUCTS_DIR; };
9C7A557C1DCD717C0049C25D /* fish_key_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fish_key_reader.cpp; sourceTree = "<group>"; };
@ -1256,6 +1261,8 @@
D0A0855C13B3ACEE0099B651 /* signal.cpp */,
D0A0852513B3ACEE0099B651 /* tokenizer.h */,
D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */,
4F2D55CD2013ECDD00822920 /* tnode.h */,
4F2D55CE2013ECDD00822920 /* tnode.cpp */,
D0C9733A18DE5451002D7C81 /* utf8.h */,
D0C9733718DE5449002D7C81 /* utf8.cpp */,
D0A0852613B3ACEE0099B651 /* util.h */,
@ -1755,6 +1762,7 @@
9C7A55491DCD71330049C25D /* postfork.cpp in Sources */,
9C7A554A1DCD71330049C25D /* screen.cpp in Sources */,
9C7A554B1DCD71330049C25D /* signal.cpp in Sources */,
4F2D55D12013ED0100822920 /* tnode.cpp in Sources */,
9C7A554C1DCD71330049C25D /* utf8.cpp in Sources */,
9C7A554E1DCD71330049C25D /* function.cpp in Sources */,
9C7A554F1DCD71330049C25D /* complete.cpp in Sources */,
@ -1974,6 +1982,7 @@
D05F59B41F041AE4003EE978 /* builtin_contains.cpp in Sources */,
D030FC081A4A38F300F7ADA0 /* pager.cpp in Sources */,
D030FC091A4A38F300F7ADA0 /* parse_util.cpp in Sources */,
4F2D55D02013ECDD00822920 /* tnode.cpp in Sources */,
D0D02AD9159864A6008E62BD /* parser_keywords.cpp in Sources */,
D02960E71FBD726200CA3985 /* builtin_wait.cpp in Sources */,
D05F59A51F041AE4003EE978 /* builtin_fg.cpp in Sources */,
@ -2057,6 +2066,7 @@
D0D02A6E15983838008E62BD /* kill.cpp in Sources */,
D0D02A6F1598383E008E62BD /* parser.cpp in Sources */,
D05F59771F041AE4003EE978 /* builtin_string.cpp in Sources */,
4F2D55CF2013ECDD00822920 /* tnode.cpp in Sources */,
D05F597A1F041AE4003EE978 /* builtin_status.cpp in Sources */,
D0D02A8F15983D8F008E62BD /* parser_keywords.cpp in Sources */,
D0D02A7015983842008E62BD /* proc.cpp in Sources */,

View file

@ -36,14 +36,13 @@
#include "maybe.h"
#include "parse_constants.h"
#include "parse_execution.h"
#include "parse_tree.h"
#include "parse_util.h"
#include "parser.h"
#include "path.h"
#include "proc.h"
#include "reader.h"
#include "tokenizer.h"
#include "tnode.h"
#include "tokenizer.h"
#include "util.h"
#include "wildcard.h"
#include "wutil.h"

View file

@ -1246,23 +1246,6 @@ static bool node_has_ancestor(const parse_node_tree_t &tree, const parse_node_t
return node_has_ancestor(tree, tree.at(node.parent), proposed_ancestor);
}
const parse_node_t *parse_node_tree_t::find_last_node_of_type(parse_token_type_t type,
const parse_node_t *parent) const {
const parse_node_t *result = NULL;
// Find nodes of the given type in the tree, working backwards.
size_t idx = this->size();
while (idx--) {
const parse_node_t &node = this->at(idx);
bool expected_type = (node.type == type);
if (expected_type && (parent == NULL || node_has_ancestor(*this, node, *parent))) {
// The types match and it has the right parent.
result = &node;
break;
}
}
return result;
}
const parse_node_t *parse_node_tree_t::find_node_matching_source_location(
parse_token_type_t type, size_t source_loc, const parse_node_t *parent) const {
const parse_node_t *result = NULL;
@ -1287,130 +1270,20 @@ const parse_node_t *parse_node_tree_t::find_node_matching_source_location(
return result;
}
enum parse_statement_decoration_t get_decoration(tnode_t<grammar::plain_statement> stmt) {
parse_statement_decoration_t decoration = parse_statement_decoration_none;
if (auto decorated_statement = stmt.try_get_parent<grammar::decorated_statement>()) {
decoration = static_cast<parse_statement_decoration_t>(decorated_statement.tag());
}
return decoration;
}
enum parse_bool_statement_type_t bool_statement_type(tnode_t<grammar::boolean_statement> stmt) {
return static_cast<parse_bool_statement_type_t>(stmt.tag());
}
enum token_type redirection_type(tnode_t<grammar::redirection> redirection, const wcstring &src,
int *out_fd, wcstring *out_target) {
assert(redirection && "redirection is missing");
enum token_type result = TOK_NONE;
tnode_t<grammar::tok_redirection> prim = redirection.child<0>(); // like 2>
assert(prim && "expected to have primitive");
if (prim.has_source()) {
result = redirection_type_for_string(prim.get_source(src), out_fd);
}
if (out_target != NULL) {
tnode_t<grammar::tok_string> target = redirection.child<1>(); // like &1 or file path
*out_target = target ? target.get_source(src) : wcstring();
}
return result;
}
std::vector<tnode_t<grammar::comment>> parse_node_tree_t::comment_nodes_for_node(
const parse_node_t &parent) const {
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.emplace_back(this, &potential_comment);
}
const parse_node_t *parse_node_tree_t::find_last_node_of_type(parse_token_type_t type,
const parse_node_t *parent) const {
const parse_node_t *result = NULL;
// Find nodes of the given type in the tree, working backwards.
size_t idx = this->size();
while (idx--) {
const parse_node_t &node = this->at(idx);
bool expected_type = (node.type == type);
if (expected_type && (parent == NULL || node_has_ancestor(*this, node, *parent))) {
// The types match and it has the right parent.
result = &node;
break;
}
}
return result;
}
const parse_node_t *parse_node_tree_t::next_node_in_node_list(
const parse_node_t &node_list, parse_token_type_t entry_type,
const parse_node_t **out_list_tail) const {
parse_token_type_t list_type = node_list.type;
// Paranoia - it doesn't make sense for a list type to contain itself.
assert(list_type != entry_type);
const parse_node_t *list_cursor = &node_list;
const parse_node_t *list_entry = NULL;
// Loop while we don't have an item but do have a list. Note that some nodes may contain
// nothing; e.g. job_list contains blank lines as a production.
while (list_entry == NULL && list_cursor != NULL) {
const parse_node_t *next_cursor = NULL;
// Walk through the children.
for (node_offset_t i = 0; i < list_cursor->child_count; i++) {
const parse_node_t *child = this->get_child(*list_cursor, i);
if (child->type == entry_type) {
// This is the list entry.
list_entry = child;
} else if (child->type == list_type) {
// This is the next in the list.
next_cursor = child;
}
}
// Go to the next entry, even if it's NULL.
list_cursor = next_cursor;
}
// Return what we got.
assert(list_cursor == NULL || list_cursor->type == list_type);
assert(list_entry == NULL || list_entry->type == entry_type);
if (out_list_tail != NULL) *out_list_tail = list_cursor;
return list_entry;
}
maybe_t<wcstring> command_for_plain_statement(tnode_t<grammar::plain_statement> stmt,
const wcstring &src) {
tnode_t<grammar::tok_string> cmd = stmt.child<0>();
if (cmd && cmd.has_source()) {
return cmd.get_source(src);
}
return none();
}
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,
size_t max) {
return list.descendants<grammar::argument>(max);
}
bool job_node_is_background(tnode_t<grammar::job> job) {
tnode_t<grammar::optional_background> bg = job.child<2>();
return bg.tag() == parse_background;
}
bool statement_is_in_pipeline(tnode_t<grammar::statement> st, bool include_first) {
using namespace grammar;
if (!st) {
return false;
}
// If we're part of a job continuation, we're definitely in a pipeline.
if (st.try_get_parent<job_continuation>()) {
return true;
}
// If include_first is set, check if we're the beginning of a job, and if so, whether that job
// has a non-empty continuation.
if (include_first) {
tnode_t<job_continuation> jc = st.try_get_parent<job>().child<1>();
if (jc.try_get_child<statement, 1>()) {
return true;
}
}
return false;
}

129
src/tnode.cpp Normal file
View file

@ -0,0 +1,129 @@
#include "tnode.h"
const parse_node_t *parse_node_tree_t::next_node_in_node_list(
const parse_node_t &node_list, parse_token_type_t entry_type,
const parse_node_t **out_list_tail) const {
parse_token_type_t list_type = node_list.type;
// Paranoia - it doesn't make sense for a list type to contain itself.
assert(list_type != entry_type);
const parse_node_t *list_cursor = &node_list;
const parse_node_t *list_entry = NULL;
// Loop while we don't have an item but do have a list. Note that some nodes may contain
// nothing; e.g. job_list contains blank lines as a production.
while (list_entry == NULL && list_cursor != NULL) {
const parse_node_t *next_cursor = NULL;
// Walk through the children.
for (node_offset_t i = 0; i < list_cursor->child_count; i++) {
const parse_node_t *child = this->get_child(*list_cursor, i);
if (child->type == entry_type) {
// This is the list entry.
list_entry = child;
} else if (child->type == list_type) {
// This is the next in the list.
next_cursor = child;
}
}
// Go to the next entry, even if it's NULL.
list_cursor = next_cursor;
}
// Return what we got.
assert(list_cursor == NULL || list_cursor->type == list_type);
assert(list_entry == NULL || list_entry->type == entry_type);
if (out_list_tail != NULL) *out_list_tail = list_cursor;
return list_entry;
}
enum parse_statement_decoration_t get_decoration(tnode_t<grammar::plain_statement> stmt) {
parse_statement_decoration_t decoration = parse_statement_decoration_none;
if (auto decorated_statement = stmt.try_get_parent<grammar::decorated_statement>()) {
decoration = static_cast<parse_statement_decoration_t>(decorated_statement.tag());
}
return decoration;
}
enum parse_bool_statement_type_t bool_statement_type(tnode_t<grammar::boolean_statement> stmt) {
return static_cast<parse_bool_statement_type_t>(stmt.tag());
}
enum token_type redirection_type(tnode_t<grammar::redirection> redirection, const wcstring &src,
int *out_fd, wcstring *out_target) {
assert(redirection && "redirection is missing");
enum token_type result = TOK_NONE;
tnode_t<grammar::tok_redirection> prim = redirection.child<0>(); // like 2>
assert(prim && "expected to have primitive");
if (prim.has_source()) {
result = redirection_type_for_string(prim.get_source(src), out_fd);
}
if (out_target != NULL) {
tnode_t<grammar::tok_string> target = redirection.child<1>(); // like &1 or file path
*out_target = target ? target.get_source(src) : wcstring();
}
return result;
}
std::vector<tnode_t<grammar::comment>> parse_node_tree_t::comment_nodes_for_node(
const parse_node_t &parent) const {
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.emplace_back(this, &potential_comment);
}
}
}
return result;
}
maybe_t<wcstring> command_for_plain_statement(tnode_t<grammar::plain_statement> stmt,
const wcstring &src) {
tnode_t<grammar::tok_string> cmd = stmt.child<0>();
if (cmd && cmd.has_source()) {
return cmd.get_source(src);
}
return none();
}
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,
size_t max) {
return list.descendants<grammar::argument>(max);
}
bool job_node_is_background(tnode_t<grammar::job> job) {
tnode_t<grammar::optional_background> bg = job.child<2>();
return bg.tag() == parse_background;
}
bool statement_is_in_pipeline(tnode_t<grammar::statement> st, bool include_first) {
using namespace grammar;
if (!st) {
return false;
}
// If we're part of a job continuation, we're definitely in a pipeline.
if (st.try_get_parent<job_continuation>()) {
return true;
}
// If include_first is set, check if we're the beginning of a job, and if so, whether that job
// has a non-empty continuation.
if (include_first) {
tnode_t<job_continuation> jc = st.try_get_parent<job>().child<1>();
if (jc.try_get_child<statement, 1>()) {
return true;
}
}
return false;
}

View file

@ -28,8 +28,21 @@ constexpr bool child_type_possible() {
child_type_possible_at_index<Parent, Child, 5>();
}
/// A helper for type-safe manipulation of parse nodes.
/// This is a lightweight value-type class.
/// tnode_t ("typed node") is type-safe access to a parse_tree. A tnode_t holds both a pointer to a
/// parse_node_tree_t and a pointer to a parse_node_t. (Note that the parse_node_tree_t is unowned;
/// the caller must ensure that the tnode does not outlive the tree.
///
/// tnode_t is a lightweight value-type class. It ought to be passed by value. A tnode_t may also be
/// "missing", associated with a null parse_node_t pointer. operator bool() may be used to check if
/// a tnode_t is misisng.
///
/// A tnode_t is parametrized by a grammar element, and uses the fish grammar to statically
/// type-check accesses to children and parents. Any particular tnode either corresponds to a
/// sequence (a single child) or an alternation (multiple possible children). A sequence may have
/// its children accessed directly via child(), which is templated on the index (and returns a
/// tnode of the proper type). Alternations may be disambiguated via try_get_child(), which returns
/// an empty child if the child has the wrong type, or require_get_child() which aborts if the child
/// has the wrong type.
template <typename Type>
class tnode_t {
/// The tree containing our node.
@ -65,7 +78,7 @@ class tnode_t {
/* implicit */ operator const parse_node_t *() const { return nodeptr; }
/// Return the underlying (type-erased) node.
/// \return the underlying (type-erased) node.
const parse_node_t *node() const { return nodeptr; }
/// Check whether we're populated.