blob: a499a68e766ab479655f5a671e392b03079610ab [file] [log] [blame]
// Copyright 2017-2022 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_VERILOG_FLOW_TREE_H_
#define VERIBLE_VERILOG_FLOW_TREE_H_
#include <bitset>
#include <map>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "common/text/token_stream_view.h"
namespace verilog {
// FlowTree class builds the control flow graph of a tokenized System-Verilog
// source code. Furthermore, enabling doing the following queries on the graph:
// - Generating all the possible variants (provided via a callback function).
class FlowTree {
private:
// 'kMaxDistinctMacros' shows the maximum number of distinct macros that can
// be considered in conditonal directives.
static constexpr int kMaxDistinctMacros = 128;
public:
using BitSet = std::bitset<kMaxDistinctMacros>;
using TokenSequenceConstIterator = verible::TokenSequence::const_iterator;
// "ConditionalBlock" saves locations of conditionals in a "TokenSequence".
// All locations should point inside this specific "TokenSequence".
// Since it is only used in conjunction with a "TokenSequence",
// It should be initialized to last location in this "TokenSequence",
// For example in "GenerateControlFlowTree" is initialized to
// 'source_sequence_.end()'.
struct Variant {
// Contains the token sequence of the variant.
verible::TokenSequence sequence;
// The i-th bit in "macros_mask" is 1 when the macro (with ID = i) is
// assumed to be defined, otherwise it is assumed to be undefined.
BitSet macros_mask;
// The i-th bit in "visited" is 1 when the macro (with ID = i) is visited or
// assumed (either defined or not), otherwise it is not visited (its value
// doesn't affect this variant).
//
// e.g.:
// `ifdef A
// `ifdef B
// ...
// `endif
// `endif
//
// Consider the variant in which A is undefined,
// we notice that B doesn't affect the variant.
// Then the bit corresponding to B in "visited" is 0.
BitSet visited;
};
// Receive a complete token sequence of one variant.
// variant_sequence: the generated variant token sequence.
using VariantReceiver = std::function<bool(const Variant &variant)>;
explicit FlowTree(verible::TokenSequence source_sequence)
: source_sequence_(std::move(source_sequence)){};
// Generates all possible variants.
absl::Status GenerateVariants(const VariantReceiver &receiver);
// Returns all the used macros in conditionals, ordered with the same ID as
// used in BitSets.
const std::vector<TokenSequenceConstIterator> &GetUsedMacros() {
return conditional_macros_;
}
private:
struct ConditionalBlock {
ConditionalBlock(TokenSequenceConstIterator if_location,
TokenSequenceConstIterator non_location)
: if_location(if_location),
else_location(non_location),
endif_location(non_location) {}
// "if_location" points to `ifdef or `ifndef.
TokenSequenceConstIterator if_location;
std::vector<TokenSequenceConstIterator> elsif_locations;
TokenSequenceConstIterator else_location;
TokenSequenceConstIterator endif_location;
};
// Constructs the control flow tree by adding the tree edges in edges_.
absl::Status GenerateControlFlowTree();
// Traveses the tree in a depth first manner.
absl::Status DepthFirstSearch(const VariantReceiver &receiver,
TokenSequenceConstIterator current_node);
// Checks if the iterator points to a conditonal directive (`ifdef/ifndef...).
static bool IsConditional(TokenSequenceConstIterator iterator);
// Adds all edges withing a conditional block.
absl::Status AddBlockEdges(const ConditionalBlock &block);
// The tree edges which defines the possible next childs of each token in
// source_sequence_.
std::map<TokenSequenceConstIterator, std::vector<TokenSequenceConstIterator>>
edges_;
// Extracts the conditional macro checked.
static absl::Status MacroFollows(
TokenSequenceConstIterator conditional_iterator);
// Adds macro to conditional_macros_ vector, and save its ID in
// conditional_macro_id_ map.
absl::Status AddMacroOfConditional(
TokenSequenceConstIterator conditional_iterator);
int GetMacroIDOfConditional(TokenSequenceConstIterator conditional_iterator);
// Holds all of the conditional blocks.
std::vector<ConditionalBlock> if_blocks_;
// The original source code lexed token seqouence.
const verible::TokenSequence source_sequence_;
// Current variant being generated by DepthFirstSearch.
Variant current_variant_;
// A flag that determines if the VariantReceiver returned 'false'.
// By default: it assumes VariantReceiver wants more variants.
bool wants_more_ = 1;
// Mapping each conditional macro to an integer ID,
// to use it later as a bit offset.
std::map<absl::string_view, int> conditional_macro_id_;
// A vector containing all the macros used placed by their given ID.
std::vector<TokenSequenceConstIterator> conditional_macros_;
// Number of macros appeared in `ifdef/`ifndef/`elsif.
int conditional_macros_counter_ = 0;
};
} // namespace verilog
#endif // VERIBLE_VERILOG_FLOW_TREE_H_