blob: 638181aabb05b9486191a6af9947a5e1a1381058 [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.
#include "common/formatting/tree_annotator.h"
#include <vector>
#include "common/text/concrete_syntax_leaf.h"
#include "common/text/token_stream_view.h"
#include "common/text/tree_context_visitor.h"
namespace verible {
namespace { // implementation detail
// TreeAnnotator traverses a syntax tree and filtered token stream view,
// using the syntax tree to maintain context.
// TODO(fangism): The class bears some semblance to TreeUnwrapper in its
// simultaneous traversal of a token stream and syntax tree, and may be worth
// refactoring as a common pattern.
class TreeAnnotator : public TreeContextVisitor {
public:
TreeAnnotator(const Symbol* syntax_tree_root, const TokenInfo& eof_token,
std::vector<PreFormatToken>::iterator tokens_begin,
std::vector<PreFormatToken>::iterator tokens_end,
const ContextTokenAnnotatorFunction& annotator)
: eof_token_(eof_token),
syntax_tree_root_(syntax_tree_root),
token_annotator_(annotator),
next_filtered_token_(tokens_begin),
end_filtered_token_(tokens_end) {}
void Annotate();
private: // methods
using TreeContextVisitor::Visit; // for SyntaxTreeNode
void Visit(const SyntaxTreeLeaf& leaf) final {
CatchUpToCurrentLeaf(leaf.get());
}
void CatchUpToCurrentLeaf(const TokenInfo& leaf_token);
// TODO(fangism): This exists solely to facilitate CatchUpToCurrentLeaf().
// Consider using position in text_buffer as a terminator, and eliminating
// this.
const TokenInfo& EOFToken() const { return eof_token_; }
private: // fields
// Saved copy of the EOF token from the original token stream.
// This is used to mark the end of token processing.
const TokenInfo eof_token_;
// Syntax tree that will be traversed, and will provide context
// during leaf visits.
const Symbol* syntax_tree_root_ = nullptr;
// Function used to annotate the PreFormatTokens.
ContextTokenAnnotatorFunction token_annotator_;
// Pointer to last token that was visited.
// This gets passed to the first parameter (left token) of the
// token_annotator_ function.
std::vector<PreFormatToken>::iterator next_filtered_token_;
// Pointer to end() of preformatted_tokens.
const std::vector<PreFormatToken>::iterator end_filtered_token_;
// Copy of current_context_ that is saved for use as a left-token's context
// passed into the token_annotator_ function.
SyntaxTreeContext saved_left_context_;
};
void TreeAnnotator::Annotate() {
if (next_filtered_token_ == end_filtered_token_) return;
// Visit the tokens from the beginning of the token stream through
// the last syntax tree node.
if (syntax_tree_root_ != nullptr) {
syntax_tree_root_->Accept(this);
}
// Else without a syntax tree, the following code will still annotate
// over the sequence of format tokens with an empty context, which is
// suitable for context-insensitive annotations.
// Visit the tokens between the last syntax tree node and EOF.
// For example, there could be comments.
CatchUpToCurrentLeaf(EOFToken());
}
void TreeAnnotator::CatchUpToCurrentLeaf(const TokenInfo& leaf_token) {
// "Catch up" next_filtered_token_ to the current leaf.
// Recall that SyntaxTreeLeaf has its own copy of TokenInfo,
// so we need to compare a unique property instead of address.
// The very last token (before end_filtered_token) is an EOF token,
// which doesn't need to be annotated.
// TODO(fangism): efficiently compute greatest-common-ancestor between
// left- and right- context. Ideally this should be memo-ized as the
// traversal occurs, to avoid any unnecessary (linear) searches.
while (std::distance(next_filtered_token_, end_filtered_token_) > 1 &&
// compare const char* addresses:
next_filtered_token_->token->text().begin() !=
leaf_token.text().begin()) {
const auto& left_token = *next_filtered_token_;
++next_filtered_token_;
auto& right_token = *next_filtered_token_;
token_annotator_(left_token, &right_token, saved_left_context_, Context());
}
// next_filtered_token_ now points to leaf_token, now caught up.
// TODO(fangism): This costs an entire vector/stack-copy for every leaf token.
// May need to choose a different structure for SyntaxTreeContext.
saved_left_context_ = Context();
}
} // namespace
void AnnotateFormatTokensUsingSyntaxContext(
const Symbol* syntax_tree_root, const TokenInfo& eof_token,
std::vector<PreFormatToken>::iterator tokens_begin,
std::vector<PreFormatToken>::iterator tokens_end,
const ContextTokenAnnotatorFunction& annotator) {
TreeAnnotator t(syntax_tree_root, eof_token, tokens_begin, tokens_end,
annotator);
t.Annotate();
}
} // namespace verible