Joos1W Compiler Framework
All Classes Functions Typedefs Pages
DotPrinter.h
1 #pragma once
2 
3 #include <iostream>
4 #include <string_view>
5 #include <unordered_map>
6 
7 namespace utils {
8 
9 /// @brief A class to help print DOT graphs!
10 class DotPrinter {
11  using string_view = std::string_view;
12  using ostream = std::ostream;
13  using string_list = std::initializer_list<string_view>;
14 
15  struct Sanitize {
16  string_view str;
17  bool left_align;
18  Sanitize(string_view str, bool left_align = false)
19  : str(str), left_align(left_align) {}
20  };
21 
22  friend ostream& operator<<(ostream& os, Sanitize s);
23 
24 public:
25  /// @brief Constructor takes in the output stream to print into
26  DotPrinter(ostream& os) : os(os), min_height_{"0"}, min_width_{"70"} {}
27 
28  /// @brief Constructor takes in the output stream to print into
29  DotPrinter(ostream& os, string_view min_height)
30  : os(os), min_height_{min_height}, min_width_{min_height} {}
31 
32  /// @brief Prints a table row with a single column
33  void printTableSingleRow(string_view cell_text, string_list cell_attrs = {},
34  bool left_align = false) {
35  print_html_start("tr");
36  print_html_start("td", {"colspan", "3", "height", min_height_}, cell_attrs);
37  os << Sanitize{cell_text, left_align};
38  print_html_end("td");
39  print_html_end("tr");
40  }
41 
42  /// @brief Prints a table row with 2 columns
43  void printTableDoubleRow(string_view cell1_text, string_view cell2_text,
44  string_list cell1_attrs = {},
45  string_list cell2_attrs = {}) {
46  print_html_start("tr");
47  print_html_start(
48  "td", {"height", min_height_, "width", min_width_}, cell1_attrs);
49  os << Sanitize{cell1_text};
50  print_html_end("td");
51  print_html_start(
52  "td",
53  {"height", min_height_, "width", min_width_, "colspan", "2"},
54  cell2_attrs);
55  os << Sanitize{cell2_text};
56  print_html_end("td");
57  print_html_end("tr");
58  }
59 
60  /// @brief Prints a table row with 3 columns
61  void printTableTripleRow(string_view cell1_text, string_view cell2_text,
62  string_view cell3_text, string_list cell1_attrs = {},
63  string_list cell2_attrs = {},
64  string_list cell3_attrs = {}) {
65  print_html_start("tr");
66  print_html_start("td", {"width", min_width_}, cell1_attrs);
67  os << Sanitize{cell1_text};
68  print_html_end("td");
69  print_html_start("td", {"width", min_width_}, cell2_attrs);
70  os << Sanitize{cell2_text};
71  print_html_end("td");
72  print_html_start("td", {"width", min_width_}, cell3_attrs);
73  os << Sanitize{cell3_text};
74  print_html_end("td");
75  print_html_end("tr");
76  }
77 
78  /// @brief Starts a DOT label that is also an HTML table
79  void startTLabel(int id, string_list attrs = {},
80  string_view cellpadding = "1") {
81  indent() << "node" << id << " [shape=none margin=0.01";
82  print_attr_list(attrs, false);
83  os << " label=<\n";
84  indent_++;
85  // clang-format off
86  print_html_start(
87  "table",
88  {
89  "border", "0",
90  "cellborder", "1",
91  "cellspacing", "0",
92  "cellpadding", cellpadding,
93  "margin", "0"
94  }
95  );
96  // clang-format on
97  }
98 
99  /// @brief Ends a DOT label that is also an HTML table
100  void endTLabel() {
101  print_html_end("table");
102  indent_--;
103  indent() << ">];\n";
104  }
105 
106  /// @brief Starts a DOT label
107  void startLabel(int id, string_list attrs = {}) {
108  indent() << "node" << id << " [shape=rect";
109  print_attr_list(attrs, false);
110  os << " label=<";
111  }
112 
113  /// @brief Ends a DOT label
114  void endLabel() { os << ">];\n"; }
115 
116  /// @brief Prints a DOT label
117  void printLabel(int id, string_view label, string_list attrs = {}) {
118  startLabel(id, attrs);
119  sanitize(label);
120  endLabel();
121  }
122 
123  /// @brief Prints the DOT subgraph { given id
124  void startSubgraph(int id, string_view label = "") {
125  indent() << "subgraph cluster_" << id << " {\n";
126  indent_++;
127  if(!label.empty()) {
128  indent() << "label=<" << Sanitize{label} << ">;\n";
129  }
130  }
131 
132  /// @brief Prints the remainder of the DOT subgraph
133  void endSubgraph() {
134  indent_--;
135  indent() << "}\n";
136  }
137 
138  /// @brief Prints the DOT graph {
139  void startGraph() {
140  os << "digraph G {\n";
141  indent_++;
142  }
143 
144  /// @brief Prints the remainder of the DOT graph
145  void endGraph() {
146  indent_--;
147  os << "}\n";
148  }
149 
150  /// @brief Prints a DOT connection between 2 nodes
151  void printConnection(int from, int to) {
152  indent() << "node" << from << " -> node" << to << ";\n";
153  }
154 
155  /// @brief Prints a DOT connection between 2 nodes but it goes against the
156  /// hierarchy and is dotted red arrow.
157  void printBackedge(int from, int to) {
158  indent() << "node" << from << " -> node" << to
159  << " [weight=0, style=dashed, color=red];\n";
160  }
161 
162  /// @brief Prints a DOT connection between 2 nodes
163  /// @param from The node from which the connection starts
164  /// @param port1 The port of the from node
165  /// @param to The node to which the connection ends
166  /// @param lhead_cluster If we should connect to a subgraph, the id of it
167  void printConnection(int from, string_view port, int to,
168  int lhead_cluster = -1) {
169  indent() << "node" << from << port << " -> node" << to;
170  if(lhead_cluster != -1) {
171  os << " [lhead=cluster_" << lhead_cluster << "]";
172  }
173  os << "\n";
174  }
175 
176  /// @brief Allocates a new unique id
177  int id() { return id_++; }
178 
179  template <typename T>
180  int id(T const* ptr) {
181  auto key = reinterpret_cast<void const*>(ptr);
182  return ptr_to_id[key] = id();
183  }
184 
185  template <typename T>
186  int getId(T const* ptr) {
187  auto key = reinterpret_cast<void const*>(ptr);
188  // Get the id if it exists or return -1
189  return ptr_to_id.contains(key) ? ptr_to_id[key] : -1;
190  }
191 
192  /// @brief Print a santized string as a label value
193  void sanitize(string_view str) { os << Sanitize{str}; }
194 
195  /// @brief Print a single line of indented text
196  void print(string_view str) { indent() << str << "\n"; }
197 
198 private:
199  ostream& indent() {
200  for(int i = 0; i < indent_; i++) os << " ";
201  return os;
202  }
203 
204  void print_attr_list(string_list attrs, bool quote) {
205  bool isKey = true;
206  for(auto attr : attrs) {
207  if(isKey) {
208  os << " " << attr << "=";
209  } else {
210  if(quote) os << "\"";
211  os << attr;
212  if(quote) os << "\"";
213  }
214  isKey = !isKey;
215  }
216  }
217 
218  void print_html_start(string_view tag, string_list attrs = {},
219  string_list attrs2 = {}) {
220  bool newline = true;
221  if(tag == "td") newline = false;
222  indent() << "<" << tag;
223  print_attr_list(attrs, true);
224  print_attr_list(attrs2, true);
225  os << ">";
226  if(newline) {
227  os << "\n";
228  indent_++;
229  }
230  }
231 
232  void print_html_end(string_view tag) {
233  bool newline = true;
234  if(tag == "td") newline = false;
235  if(newline) {
236  indent_--;
237  indent();
238  }
239  os << "</" << tag << ">\n";
240  }
241 
242 private:
243  ostream& os;
244  int indent_ = 0;
245  int id_ = 0;
246  string_view min_height_;
247  string_view min_width_;
248  std::unordered_map<void const*, int> ptr_to_id;
249 };
250 
251 } // namespace utils