Skip to content

Commit

Permalink
TreePathVisitor keeps track of paths used to reach each node in a CST.
Browse files Browse the repository at this point in the history
This is useful for analyses that care more about the shape and structure of a
tree than its contents.

This will be used for tabular alignment for handling optional and repeated
constructs in the syntax tree.

PiperOrigin-RevId: 309828656
  • Loading branch information
fangism authored and hzeller committed May 4, 2020
1 parent 0defe52 commit a152898
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 0 deletions.
20 changes: 20 additions & 0 deletions common/text/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,26 @@ cc_test(
],
)

cc_library(
name = "tree_path_visitor",
srcs = ["tree_path_visitor.cc"],
hdrs = ["tree_path_visitor.h"],
deps = [
":concrete_syntax_tree",
":visitors",
],
)

cc_test(
name = "tree_path_visitor_test",
srcs = ["tree_path_visitor_test.cc"],
deps = [
":tree_builder_test_util",
":tree_path_visitor",
"@com_google_googletest//:gtest_main",
],
)

cc_library(
name = "tree_utils",
srcs = ["tree_utils.cc"],
Expand Down
41 changes: 41 additions & 0 deletions common/text/tree_path_visitor.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "common/text/tree_path_visitor.h"

#include "common/text/concrete_syntax_tree.h"

namespace verible {

namespace {
template <class V>
class AutoPopBack {
public:
explicit AutoPopBack(V* v) : vec_(v) { vec_->push_back(0); }
~AutoPopBack() { vec_->pop_back(); }

private:
V* vec_;
};
} // namespace

void TreePathVisitor::Visit(const SyntaxTreeNode& node) {
const AutoPopBack<SyntaxTreePath> p(&current_path_);
for (const auto& child : node.children()) {
if (child) child->Accept(this);
++current_path_.back();
}
}

} // namespace verible
56 changes: 56 additions & 0 deletions common/text/tree_path_visitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef VERIBLE_COMMON_TEXT_TREE_PATH_VISITOR_H_
#define VERIBLE_COMMON_TEXT_TREE_PATH_VISITOR_H_

#include <cstddef>
#include <vector>

#include "common/text/visitors.h"

namespace verible {

// Type that is used to keep track of positions descended from a root
// node to reach a particular node.
// Path types should be lexicographically comparable.
// This is very similar in spirit to VectorTree<>::Path(), but
// needs to be tracked in a stack-like manner during visitation
// because SyntaxTreeNode and Leaf do not maintain upward pointers
// to their parent nodes.
// TODO(fangism): consider replacing with hybrid "small" vector to
// minimize heap allocations, because these are expected to be small.
using SyntaxTreePath = std::vector<size_t>;

// This visitor traverses a tree and maintains a stack of offsets
// that represents the positional path taken from the root to
// reach each node.
// This is useful for applications where the shape and positions of nodes
// within the tree are more meaningful than the contents.
class TreePathVisitor : public SymbolVisitor {
public:
TreePathVisitor() = default;

protected:
void Visit(const SyntaxTreeNode& node) override;

const SyntaxTreePath& Path() const { return current_path_; }

// Keeps track of path of descent from root node.
SyntaxTreePath current_path_;
};

} // namespace verible

#endif // VERIBLE_COMMON_TEXT_TREE_PATH_VISITOR_H_
197 changes: 197 additions & 0 deletions common/text/tree_path_visitor_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "common/text/tree_path_visitor.h"

#include <vector>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "common/text/tree_builder_test_util.h"

namespace verible {
namespace {

using ::testing::ElementsAreArray;

// Test class demonstrating visitation and path tracking
class RecordingVisitor : public TreePathVisitor {
public:
void Visit(const SyntaxTreeLeaf& leaf) override {
path_history_.push_back(Path());
}

void Visit(const SyntaxTreeNode& node) override {
path_history_.push_back(Path());
TreePathVisitor::Visit(node);
}

const std::vector<SyntaxTreePath>& PathTagHistory() const {
return path_history_;
}

private:
std::vector<SyntaxTreePath> path_history_;
};

TEST(TreePathVisitorTest, LoneNode) {
auto tree = Node();
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{}, // the one-and-only node has no parent
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, LoneLeaf) {
auto tree = XLeaf(0);
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{}, // the one-and-only leaf has no parent
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, NodeWithOnlyNullptrs) {
auto tree = TNode(1, nullptr, nullptr);
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{},
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, NodeWithSomeNullptrs) {
auto tree = TNode(1, nullptr, Node(), nullptr, Node());
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{},
{1},
{3},
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, NodeWithSomeNullptrs2) {
auto tree = TNode(1, Node(), Node(), nullptr, Node(), nullptr);
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{},
{0},
{1},
{3},
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, ThinTree) {
auto tree = TNode(3, TNode(4, TNode(5)));
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{},
{0},
{0, 0},
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, ThinTreeWithLeaf) {
auto tree = TNode(3, TNode(4, TNode(5, XLeaf(1))));
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{},
{0},
{0, 0},
{0, 0, 0},
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, FlatTree) {
auto tree = TNode(3, TNode(4), XLeaf(5), TNode(6));
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{},
{0},
{1},
{2},
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, FullTree) {
auto tree = TNode(3, //
TNode(4, //
XLeaf(99), //
TNode(1, //
XLeaf(99), //
XLeaf(0))), //
XLeaf(5), //
TNode(6, //
TNode(2, //
TNode(7, //
XLeaf(99)), //
TNode(8))));
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{}, {0}, {0, 0}, {0, 1}, {0, 1, 0}, {0, 1, 1},
{1}, {2}, {2, 0}, {2, 0, 0}, {2, 0, 0, 0}, {2, 0, 1},
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

TEST(TreePathVisitorTest, FullTreeWithNullptrs) {
auto tree = TNode(3, //
nullptr, //
TNode(4, //
nullptr, //
XLeaf(99), //
nullptr, //
TNode(1, //
XLeaf(99), //
nullptr, //
XLeaf(0))), //
nullptr, //
XLeaf(5), //
nullptr, //
nullptr, //
TNode(6, //
nullptr, //
TNode(2, //
nullptr, //
TNode(7, //
nullptr, //
XLeaf(99)), //
TNode(8))), //
nullptr);
RecordingVisitor r;
tree->Accept(&r);
const std::vector<SyntaxTreePath> expect = {
{}, {1}, {1, 1}, {1, 3}, {1, 3, 0}, {1, 3, 2},
{3}, {6}, {6, 1}, {6, 1, 1}, {6, 1, 1, 1}, {6, 1, 2},
};
EXPECT_THAT(r.PathTagHistory(), ElementsAreArray(expect));
}

} // namespace
} // namespace verible

0 comments on commit a152898

Please sign in to comment.