mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
2bf702b74b
Add EdgeRef and ChildEdgeIteratorType to GraphTraits<DotNode *> and to GraphTraits<const DotNode *>. This allows iterating on edges of DotGraphs, which will be useful for some testing on edge properties.
189 lines
5.1 KiB
C++
189 lines
5.1 KiB
C++
/// \file DotGraphObject.cpp
|
|
/// \brief
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
// Standard includes
|
|
#include <fstream>
|
|
#include <map>
|
|
#include <set>
|
|
|
|
// revng includes
|
|
#include "revng/Support/Debug.h"
|
|
|
|
// Local libraries includes
|
|
#include "revng/UnitTestHelpers/DotGraphObject.h"
|
|
|
|
using namespace llvm;
|
|
|
|
Logger<> DotLogger("parsedot");
|
|
|
|
void DotNode::addSuccessor(DotNode *NewSuccessor) {
|
|
|
|
// Assert that we are not double inserting.
|
|
bool Found = false;
|
|
for (DotNode *Successor : Successors) {
|
|
if (NewSuccessor == Successor) {
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
revng_assert(not Found);
|
|
|
|
Successors.push_back(NewSuccessor);
|
|
SuccEdges.push_back(std::make_pair(this, NewSuccessor));
|
|
}
|
|
|
|
void DotGraph::parseDotImpl(std::ifstream &F, llvm::StringRef EntryName) {
|
|
|
|
// Keep a map between the node identifiers of the nodes defined in the dot
|
|
// file and the corresponding `DotNode` object.
|
|
std::map<llvm::SmallString<8>, DotNode *> DotNodeMap;
|
|
|
|
// Container for the current line.
|
|
std::string CurrentLine;
|
|
|
|
// Edge delimiter.
|
|
llvm::StringRef Delimiter("->");
|
|
size_t DelimiterSize = Delimiter.size();
|
|
|
|
bool ParsedHeader = false;
|
|
bool ParsedFooter = false;
|
|
|
|
while (getline(F, CurrentLine)) {
|
|
|
|
// Drop comments, i.e., everything after #
|
|
size_t SharpPosition = CurrentLine.find("#");
|
|
if (SharpPosition != std::string::npos) {
|
|
CurrentLine.resize(SharpPosition);
|
|
}
|
|
|
|
// Trim away all the spaces from the current line.
|
|
CurrentLine.erase(remove_if(CurrentLine.begin(),
|
|
CurrentLine.end(),
|
|
isspace),
|
|
CurrentLine.end());
|
|
|
|
// Obtain a StringRef which is more easily to handle.
|
|
llvm::StringRef CurrentLineRef = CurrentLine;
|
|
|
|
// Always skip over empty lines.
|
|
if (CurrentLineRef == "") {
|
|
continue;
|
|
} else {
|
|
if (not ParsedHeader) {
|
|
if (CurrentLineRef.startswith("digraph")
|
|
and CurrentLineRef.endswith("{")) {
|
|
ParsedHeader = true;
|
|
continue;
|
|
} else {
|
|
revng_abort("GraphViz header malformed");
|
|
}
|
|
} else if (not ParsedFooter) {
|
|
|
|
if (CurrentLineRef == "}") {
|
|
ParsedFooter = true;
|
|
continue;
|
|
}
|
|
|
|
// The body of the dot file should contain the edge declaration.
|
|
revng_log(DotLogger, "Parsed line:\n" << CurrentLineRef << "\n");
|
|
size_t pos = CurrentLineRef.find(Delimiter);
|
|
revng_assert(pos != llvm::StringRef::npos);
|
|
|
|
// Collect the source node (everything that comes before `->`).
|
|
llvm::StringRef SourceID = CurrentLineRef.substr(0, pos);
|
|
revng_log(DotLogger, "Source is: " << SourceID << "\n");
|
|
|
|
// Collect the target node.
|
|
llvm::StringRef TargetID = CurrentLineRef.substr(pos + DelimiterSize);
|
|
|
|
// Remove the last `;`.
|
|
size_t TargetSize = TargetID.size();
|
|
revng_assert(TargetID.substr(TargetSize - 1, TargetSize) == ";");
|
|
TargetID = TargetID.substr(0, TargetSize - 1);
|
|
revng_log(DotLogger, "Target is: " << TargetID << "\n");
|
|
|
|
DotNode *Source = nullptr;
|
|
DotNode *Target = nullptr;
|
|
|
|
// Lazily creates the nodes that have not been created before and
|
|
// retrieve all the necessary pointers to the nodes.
|
|
auto It = DotNodeMap.find(SourceID);
|
|
if (It == DotNodeMap.end()) {
|
|
Source = addNode(SourceID);
|
|
DotNodeMap[SourceID] = Source;
|
|
} else {
|
|
Source = It->second;
|
|
}
|
|
|
|
It = DotNodeMap.find(TargetID);
|
|
if (It == DotNodeMap.end()) {
|
|
Target = addNode(TargetID);
|
|
DotNodeMap[TargetID] = Target;
|
|
} else {
|
|
Target = It->second;
|
|
}
|
|
|
|
revng_assert(Source != nullptr and Target != nullptr);
|
|
|
|
// Add to the successors of `Source` the node `Target`.
|
|
Source->addSuccessor(Target);
|
|
} else {
|
|
revng_abort("Content after the footer.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the entry node searching for the entry name.
|
|
if (not EntryName.empty()) {
|
|
EntryNode = getNodeByName(EntryName);
|
|
} else {
|
|
EntryNode = nullptr;
|
|
}
|
|
}
|
|
|
|
void DotGraph::parseDotFromFile(llvm::StringRef FileName,
|
|
llvm::StringRef EntryName) {
|
|
std::ifstream DotFile;
|
|
DotFile.open(FileName);
|
|
|
|
// Check that the file has been opened.
|
|
if (DotFile.is_open()) {
|
|
parseDotImpl(DotFile, EntryName);
|
|
|
|
// Close the input file.
|
|
DotFile.close();
|
|
} else {
|
|
revng_abort("Input dot file not opened");
|
|
}
|
|
}
|
|
|
|
DotNode *DotGraph::addNode(llvm::StringRef Name) {
|
|
Nodes.emplace_back(std::make_unique<DotNode>(Name));
|
|
DotNode *NewNode = Nodes.back().get();
|
|
return NewNode;
|
|
}
|
|
|
|
DotNode *DotGraph::getNodeByName(llvm::StringRef Name) {
|
|
|
|
// Verify that we have a single node matching the name.
|
|
DotNode *Candidate = nullptr;
|
|
|
|
for (DotNode *Node : nodes()) {
|
|
if (Node->getName() == Name) {
|
|
if (Candidate == nullptr) {
|
|
Candidate = Node;
|
|
} else {
|
|
revng_abort("Two nodes with the same name found.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check that we found the node we are searching for.
|
|
revng_assert(Candidate != nullptr);
|
|
return Candidate;
|
|
}
|