blob: a28abbf6ca3e393204c264b262048df2f7b6a1b8 [file] [log] [blame]
// 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.
// ConcreteSyntaxTree represents the structure of a body of text.
//
// This header also provides the following (owernship-transferring) functions
// for constructing syntax trees in semantic action blocks:
//
// $$ = MakeNode($1, $2, ...);
// $$ = MakeTaggedNode(kTag, $1, $2, ...);
// $$ = ExtendNode($1, $2, ...);
// $$ = MakeNode($1, ForwardChildren($2), $3, ...);
//
// As ownership is transferred exclusively, the pointers left behind are
// null as a result.
//
// These functions are intended for use only in <language>.yc semantic actions.
//
// The std::move is automated for the sake of easy tree building.
// Without the automation, the user would have to write:
// $$ = MakeNode(std::move($1), std::move($2), ...);
// which would be less readable than the above.
#ifndef VERIBLE_COMMON_TEXT_CONCRETE_SYNTAX_TREE_H_
#define VERIBLE_COMMON_TEXT_CONCRETE_SYNTAX_TREE_H_
#include <algorithm>
#include <cstddef>
#include <initializer_list>
#include <memory>
#include <utility>
#include <vector>
#include "common/text/constants.h"
#include "common/text/symbol.h"
#include "common/text/tree_compare.h"
#include "common/text/visitors.h"
#include "common/util/casts.h"
#include "common/util/logging.h"
namespace verible {
// Using unique_ptr in the symbol stack requires careful moving.
using SymbolPtr = std::unique_ptr<Symbol>;
// Currently, a tree *is* a tree-node, but this may change in the future.
// Treat this as an opaque type.
using ConcreteSyntaxTree = SymbolPtr;
// Helper class for transferring ownership of children, which is
// used as an overload to SyntaxTreeNode::AppendChild.
// This takes over ownership of the symbol pointer.
// $$ = MakeNode($1, $2, ForwardChildren($3), $4);
struct ForwardChildren {
explicit ForwardChildren(SymbolPtr& symbol) // NOLINT
: node(std::move(symbol)) {}
SymbolPtr node;
};
// SyntaxTreeNode is a language-agnostic node structure, supporting an
// arbitrary number of children. The 'tag' field is a node type enumeration
// used by various language front-ends.
class SyntaxTreeNode final : public Symbol {
public:
explicit SyntaxTreeNode(const int tag = kUntagged) : tag_(tag) {}
const std::vector<SymbolPtr>& children() const { return children_; }
std::vector<SymbolPtr>& mutable_children() { return children_; }
// Transfer ownership of argument to this object.
// Call MakeNode or ExtendNode instead of calling this directly.
void AppendChild(SymbolPtr child) { children_.push_back(std::move(child)); }
// Transfer ownership of argument's children to this object.
// Call MakeNode or ExtendNode instead of calling this directly.
// If node is actually a leaf, just append the leaf.
void AppendChild(ForwardChildren forwarded_children) {
if (forwarded_children.node == nullptr) return;
auto* node = dynamic_cast<SyntaxTreeNode*>(forwarded_children.node.get());
if (node == nullptr) {
// Could be a SyntaxTreeLeaf, for instance.
children_.push_back(std::move(forwarded_children.node));
return;
}
const auto new_size = children_.size() + node->children_.size();
children_.reserve(new_size);
for (auto& child : node->children_) {
children_.push_back(std::move(child));
}
// Remove all the vacated children slots left in the parent.
node->children_.clear();
}
// This no-op case is the base case for the variadic Append.
void Append() const {}
// Ownership of all arguments is transferred to this object.
// Call MakeNode or ExtendNode instead of calling Append directly.
template <typename T, typename... Args>
void Append(T&& t, Args&&... args) {
AppendChild(std::move(t)); // Append the first.
Append(std::forward<Args>(args)...); // Append the rest.
}
// Children accessor (mutable).
SymbolPtr& operator[](size_t i);
// Children accessor (const).
const SymbolPtr& operator[](size_t i) const;
// Compares this node to an arbitrary symbol using the compare_tokens
// function.
bool equals(const Symbol* symbol,
const TokenComparator& compare_tokens) const final;
// Compares this node to another node.
// Checks for recursive equality among all children of both nodes.
bool equals(const SyntaxTreeNode* node,
const TokenComparator& compare_tokens) const;
// Uses passed TreeVisitorRecursive to visit all children recursively,
// then visit itself.
void Accept(TreeVisitorRecursive* visitor) const final;
void Accept(MutableTreeVisitorRecursive* visitor,
SymbolPtr* this_owned) final;
// Accepting a symbol visitor does not recursively visit children.
void Accept(SymbolVisitor* visitor) const final;
// Method override that returns the Kind of Symbol
SymbolKind Kind() const final { return SymbolKind::kNode; }
SymbolTag Tag() const final { return NodeTag(tag_); }
// MatchesTag returns true if the tag value matches the argument.
// This is designed to work with any enumeration type.
template <typename EnumType>
bool MatchesTag(EnumType e) const {
return tag_ == static_cast<int>(e);
}
template <typename EnumType>
bool MatchesTagAnyOf(std::initializer_list<EnumType> enums) const {
auto it = enums.begin();
// Unroll OR expression. Right now, we never have more than 4.
switch (enums.size()) {
case 4:
if (*it++ == EnumType(tag_)) return true;
ABSL_FALLTHROUGH_INTENDED;
case 3:
if (*it++ == EnumType(tag_)) return true;
ABSL_FALLTHROUGH_INTENDED;
case 2:
if (*it++ == EnumType(tag_)) return true;
ABSL_FALLTHROUGH_INTENDED;
case 1:
if (*it++ == EnumType(tag_)) return true;
ABSL_FALLTHROUGH_INTENDED;
case 0:
return false;
default:
// TODO(hzeller): with constexpr magic, can we static_assert() this ?
LOG(FATAL) << "need more choice " << enums.size();
return false;
}
}
private:
// This tag would really prefer to be a language-specific node enumeration
// type, but that would (IMHO) create unecessary templating.
// Decision: Keep this a generic int.
int tag_;
// Sequence of pointers to subtrees and nodes.
std::vector<SymbolPtr> children_;
};
// The following functions are intended for use in semantic action blocks
// in yacc/bison grammar files (.yc).
// Construct a syntax tree node with a tag.
// Ownership of all args is transferred, and consumed by the new node.
// Sample usage: $$ = MakeNode(TAG, $1, $2, $3);
template <typename... Args>
SymbolPtr MakeNode(Args&&... args) {
std::unique_ptr<SyntaxTreeNode> node_pointer(new SyntaxTreeNode);
node_pointer->Append(std::forward<Args>(args)...);
return std::move(node_pointer);
}
// Construct a syntax tree node with a tag.
// Ownership of all args is transferred, and consumed by the new node.
// Sample usage:
// $$ = MakeTaggedNode(TAG); // empty, no children
// $$ = MakeTaggedNode(TAG, $1, $2, $3);
template <typename Enum, typename... Args>
SymbolPtr MakeTaggedNode(const Enum tag, Args&&... args) {
std::unique_ptr<SyntaxTreeNode> node_pointer(
new SyntaxTreeNode(static_cast<int>(tag)));
node_pointer->Append(std::forward<Args>(args)...);
return std::move(node_pointer);
}
// Extend the children of an existing node.
// Equivalent to: $$ = std::move(ExtendNode($1, $2, $3));
// Ownership of all args is transferred, and consumed by the existing node.
// $1 is transferred to $$.
// Sample usage: $$ = ExtendNode($1, $2, $3);
template <typename T, typename... Args>
SymbolPtr ExtendNode(T&& list_ptr, Args&&... args) {
return _ExtendNodeMoved(std::move(list_ptr), std::forward<Args>(args)...);
}
// Implementation detail, call ExtendNode instead for automatic std::move.
template <typename... Args>
SymbolPtr _ExtendNodeMoved(SymbolPtr list_ptr, Args&&... args) {
CHECK(list_ptr->Kind() == SymbolKind::kNode);
SyntaxTreeNode* node_pointer = down_cast<SyntaxTreeNode*>(list_ptr.get());
node_pointer->Append(std::forward<Args>(args)...);
return list_ptr;
}
// Helper function to deal with move semantics and argument forwarding
void SetChild_(const SymbolPtr& parent, int child_index, SymbolPtr new_child);
// Sets the child at child_index of parent to new_child.
// SetChild will crash when:
// Child_index is out of range
// Parent either a leaf or a nullptr
// Preexisting data at target index is not a nullptr
// Equivalent to: parent->children[child_index] = std::move(new_child);
template <typename T>
void SetChild(const SymbolPtr& parent, int child_index, T&& new_child) {
SetChild_(parent, child_index, std::move(new_child));
}
} // namespace verible
#endif // VERIBLE_COMMON_TEXT_CONCRETE_SYNTAX_TREE_H_