Jlm
Loading...
Searching...
No Matches
GraphWriter.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2024 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
7#include <jlm/util/strfmt.hpp>
8
9#include <ostream>
10#include <string_view>
11
12namespace jlm::util::graph
13{
14// All GraphElements with an associated ProgramObject get this attribute added
15static const char * const DOT_TOOLTIP_ATTRIBUTE = "tooltip";
16// Edges are not named in dot, so use an attribute to assign id instead
17static const char * const DOT_EDGE_ID_ATTRIBUTE = "id";
18// For setting the background color of cells in dot html tables
19static const char * const DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE = "BGCOLOR";
20
21// The json field containing the label of a graph element
22static const char * const JSON_LABEL_FIELD = "label";
23// The map of attributes in a graph element json object
24static const char * const JSON_ATTRIBUTE_FIELD = "attr";
25// The address of the program object represented by a graph element json object
26static const char * const JSON_OBJECT_POINTER_FIELD = "obj";
27// Field specifying the type of node, for special nodes like InOutNodes
28static const char * const JSON_NODE_TYPE_FIELD = "type";
29
30// Fields in json objects representing InOutNodes
31static const char * const JSON_IN_PORTS_FIELD = "ins";
32static const char * const JSON_OUT_PORTS_FIELD = "outs";
33static const char * const JSON_SUBGRAPHS_FIELD = "subgraphs";
34static const char * const JSON_HTML_TABLE_ATTRIBUTES_FIELD = "htmlTableAttr";
35
36// Fields in Graph objects
37static const char * const JSON_PARENT_NODE_FIELD = "parentNode";
38static const char * const JSON_PARENT_GRAPH_FIELD = "parentGraph";
39static const char * const JSON_ARGUMENTS_FIELD = "arguments";
40static const char * const JSON_NODES_FIELD = "nodes";
41static const char * const JSON_RESULTS_FIELD = "results";
42static const char * const JSON_EDGES_FIELD = "edges";
43
49static bool
50looksLikeIdentifier(std::string_view string)
51{
52 if (string.empty())
53 return false;
54
55 // We avoid C's isalpha, as it is locale dependent
56 auto isAlpha = [](char c)
57 {
58 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
59 };
60 auto isDigit = [](char c)
61 {
62 return (c >= '0' && c <= '9');
63 };
64
65 char firstChar = string[0];
66 if (!isAlpha(firstChar) && firstChar != '_')
67 return false;
68
69 for (char c : string)
70 if (!isAlpha(c) && !isDigit(c) && c != '_')
71 return false;
72
73 return true;
74}
75
80static void
81printIdentifierSafe(std::ostream & out, std::string_view string)
82{
83 bool quoted = !looksLikeIdentifier(string);
84
85 if (quoted)
86 out << '"';
87 for (char c : string)
88 {
89 if (c == '"')
90 out << "\\\"";
91 else if (c == '\\')
92 out << "\\\\";
93 else if (c == '\n')
94 out << "\\n";
95 else if (c == '\r')
96 out << "\\r";
97 else if (c == '\t')
98 out << "\\t";
99 else if (c < ' ' || c >= 127)
100 {
101 // Print all other special chars as \x escaped hex.
102 char tmpStr[3];
103 snprintf(tmpStr, sizeof(tmpStr), "%02X", c);
104 out << "\\x" << tmpStr;
105 }
106 else
107 out << c;
108 }
109 if (quoted)
110 out << '"';
111}
112
118static void
119printStringAsHtmlText(std::ostream & out, std::string_view string, bool replaceNewlines)
120{
121 for (char c : string)
122 {
123 if (c == '&')
124 out << "&amp;";
125 else if (c == '"')
126 out << "&quot;";
127 else if (c == '<')
128 out << "&lt;";
129 else if (c == '>')
130 out << "&gt;";
131 else if (c == '\n' && replaceNewlines)
132 out << "<BR/>";
133 else
134 out << c;
135 }
136}
137
142static void
143printStringAsHtmlAttributeName(std::ostream & out, std::string_view string)
144{
145 for (char c : string)
146 {
147 if (c <= ' ' || c >= 127 || c == '<' || c == '>' || c == '"' || c == '\'' || c == '/'
148 || c == '=')
149 out << '-';
150 else
151 out << c;
152 }
153}
154
159static void
160printJsonString(std::ostream & out, std::string_view string)
161{
162 out << '"';
163 for (char c : string)
164 {
165 if (c == '"')
166 out << "\\\"";
167 else if (c == '\\')
168 out << "\\\\";
169 else if (c == '\n')
170 out << "\\n";
171 else if (c == '\t')
172 out << "\\t";
173 else
174 out << c;
175 }
176 out << '"';
177}
178
183std::ostream &
184withIndent(std::ostream & out, size_t indent)
185{
186 for (size_t i = 0; i < indent; i++)
187 out << ' ';
188 return out;
189}
190
192 : Label_(),
193 UniqueIdSuffix_(std::nullopt),
194 ProgramObject_(0),
195 AttributeMap_()
196{}
197
198void
199GraphElement::PrintFullId(std::ostream & out) const
200{
202 out << GetIdPrefix() << GetUniqueIdSuffix();
203}
204
205std::string
207{
208 std::ostringstream ss;
209 PrintFullId(ss);
210 return ss.str();
211}
212
213const Graph &
215{
216 return const_cast<GraphElement *>(this)->GetGraph();
217}
218
219void
220GraphElement::SetLabel(std::string label)
221{
222 Label_ = std::move(label);
223}
224
225void
226GraphElement::AppendToLabel(std::string_view text, std::string_view sep)
227{
228 if (HasLabel())
229 {
230 Label_.append(sep).append(text);
231 }
232 else
233 {
234 Label_ = text;
235 }
236}
237
238bool
240{
241 return !Label_.empty();
242}
243
244const std::string &
246{
247 return Label_;
248}
249
250std::string_view
251GraphElement::GetLabelOr(std::string_view otherwise) const
252{
253 if (HasLabel())
254 return Label_;
255 return otherwise;
256}
257
258size_t
264
265void
267{
268 JLM_ASSERT(object);
269 // If the GraphElement was already associated with a program object, remove the old mapping first
270 if (ProgramObject_ != 0)
272 ProgramObject_ = object;
273 if (ProgramObject_ != 0)
275}
276
277void
282
283bool
285{
286 return ProgramObject_ != 0;
287}
288
289uintptr_t
291{
292 return ProgramObject_;
293}
294
295void
296GraphElement::SetAttribute(const std::string & attribute, std::string value)
297{
298 AttributeMap_[attribute] = std::move(value);
299}
300
301void
302GraphElement::SetAttributeObject(const std::string & attribute, uintptr_t object)
303{
304 JLM_ASSERT(object);
305 AttributeMap_[attribute] = object;
306}
307
308void
309GraphElement::SetAttributeGraphElement(const std::string & attribute, const GraphElement & element)
310{
311 JLM_ASSERT(&GetGraph().GetWriter() == &element.GetGraph().GetWriter());
312 AttributeMap_[attribute] = &element;
313}
314
315bool
316GraphElement::HasAttribute(const std::string & attribute) const
317{
318 return AttributeMap_.find(attribute) != AttributeMap_.end();
319}
320
321std::optional<std::string_view>
322GraphElement::GetAttributeString(const std::string & attribute) const
323{
324 if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
325 {
326 if (auto stringValue = std::get_if<std::string>(&it->second))
327 {
328 return *stringValue;
329 }
330 }
331 return std::nullopt;
332}
333
334std::optional<uintptr_t>
335GraphElement::GetAttributeObject(const std::string & attribute) const
336{
337 if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
338 {
339 if (auto uintptrValue = std::get_if<uintptr_t>(&it->second))
340 {
341 return *uintptrValue;
342 }
343 }
344 return std::nullopt;
345}
346
347const GraphElement *
348GraphElement::GetAttributeGraphElement(const std::string & attribute) const
349{
350 if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
351 {
352 if (auto graphElementValue = std::get_if<const GraphElement *>(&it->second))
353 {
354 return *graphElementValue;
355 }
356
357 // Otherwise, check if this attribute holds a program object that is represented by a
358 // GraphElement in this graph, or in any graph in the GraphWriter.
359 if (auto ptr = std::get_if<uintptr_t>(&it->second))
360 {
361 if (auto gElement = GetGraph().GetElementFromProgramObject(*ptr))
362 {
363 return gElement;
364 }
365 if (auto gwElement = GetGraph().GetWriter().GetElementFromProgramObject(*ptr))
366 {
367 return gwElement;
368 }
369 }
370 }
371 return nullptr;
372}
373
374bool
375GraphElement::RemoveAttribute(const std::string & attribute)
376{
377 return AttributeMap_.erase(attribute);
378}
379
380void
382{
383 if (IsFinalized())
384 return;
385
386 auto & writer = GetGraph().GetWriter();
388}
389
390bool
392{
393 return UniqueIdSuffix_.has_value();
394}
395
399static void
401 std::ostream & out,
402 std::string_view name,
403 std::string_view value,
405{
407 {
408 printIdentifierSafe(out, name);
409 out << "=";
410 printIdentifierSafe(out, value);
411 out << " "; // space separation
412 }
413 else if (format == AttributeOutputFormat::HTMLAttributes)
414 {
416 out << "=\""; // HTML attributes must be quoted
417 printStringAsHtmlText(out, value, false);
418 out << "\" "; // Closing quote and separating space
419 }
420 else if (format == AttributeOutputFormat::JSON)
421 {
422 printJsonString(out, name);
423 out << ':';
424 printJsonString(out, value);
425 }
426 else
427 {
428 JLM_UNREACHABLE("Unknown AttributeOutputFormat");
429 }
430}
431
432void
434 std::ostream & out,
435 const std::string & name,
436 AttributeOutputFormat format) const
437{
438 if (auto string = GetAttributeString(name))
439 {
440 outputKeyValuePair(out, name, *string, format);
441 }
442 else if (auto graphElement = GetAttributeGraphElement(name))
443 {
444 outputKeyValuePair(out, name, graphElement->GetFullId(), format);
445 }
446 else if (auto object = GetAttributeObject(name))
447 {
448 outputKeyValuePair(out, name, strfmt("ptr", reinterpret_cast<void *>(*object)), format);
449 }
450 else
451 {
452 JLM_UNREACHABLE("Unknown attribute type");
453 }
454}
455
456void
458{
459 bool first = true;
460 const auto next = [&]()
461 {
462 if (first)
463 first = false;
464 else if (format == AttributeOutputFormat::JSON)
465 out << ", ";
466 else
467 out << ' ';
468 };
469
470 for (const auto & [name, _] : AttributeMap_)
471 {
472 next();
473 OutputAttribute(out, name, format);
474 }
475
476 // If no tooltip attribute is specified, use the program object pointer.
477 // This is not done in JSON, as the program object is included in a separate field.
479 && HasProgramObject())
480 {
481 next();
483 out,
485 strfmt(reinterpret_cast<void *>(GetProgramObject())),
486 format);
487 }
488}
489
495static std::ostream &
496printNextJsonField(std::ostream & out, std::string_view name, size_t indent, bool & firstField)
497{
498 if (firstField)
499 firstField = false;
500 else
501 out << ',';
502 out << std::endl;
503 withIndent(out, indent) << '"' << name << "\": ";
504
505 return out;
506};
507
508template<typename T>
509static void
510printJsonElementMap(std::ostream & out, size_t indent, const T & elements)
511{
512 out << "{";
513 bool first = true;
514 for (auto & element : elements)
515 {
516 if (first)
517 first = false;
518 else
519 out << ",";
520 out << std::endl;
521 element->outputJson(out, indent + 1);
522 }
523
524 if (first)
525 out << "}";
526 else
527 {
528 out << std::endl;
529 withIndent(out, indent) << "}";
530 }
531};
532
533void
534GraphElement::outputJsonObjectOpening(std::ostream & out, size_t indent, bool & firstField) const
535{
536 withIndent(out, indent) << "\"";
537 PrintFullId(out); // The full id does not include quotes or special characters
538 out << "\": {";
539
540 indent++;
541 if (HasLabel())
542 {
543 printNextJsonField(out, JSON_LABEL_FIELD, indent, firstField);
545 }
546
547 if (HasProgramObject())
548 {
549 printNextJsonField(out, JSON_OBJECT_POINTER_FIELD, indent, firstField);
550 out << '"' << reinterpret_cast<void *>(GetProgramObject()) << '"';
551 }
552
553 if (!AttributeMap_.empty())
554 {
555 printNextJsonField(out, JSON_ATTRIBUTE_FIELD, indent, firstField);
556 out << '{';
558 out << '}';
559 }
560}
561
562static void
563outputJsonObjectClosing(std::ostream & out, size_t indent, bool firstField)
564{
565 // The object contains at least one field, close on the next line
566 if (!firstField)
567 {
568 out << std::endl;
569 withIndent(out, indent);
570 }
571 out << "}";
572}
573
575 : GraphElement()
576{}
577
578Graph &
580{
581 return GetNode().GetGraph();
582}
583
584bool
586{
587 return true;
588}
589
590bool
592{
593 return true;
594}
595
596const std::vector<Edge *> &
598{
599 return Connections_;
600}
601
602void
604{
605 if (this == &edge.GetFrom())
606 JLM_ASSERT(CanBeEdgeTail() || !edge.IsDirected());
607 else if (this == &edge.GetTo())
608 JLM_ASSERT(CanBeEdgeHead() || !edge.IsDirected());
609 else
610 JLM_UNREACHABLE("Port was informed about unrelated edge");
611
612 Connections_.push_back(&edge);
613}
614
615bool
617{
618 for (auto & edge : Connections_)
619 {
620 if (&edge->GetFrom() == this || !edge->IsDirected())
621 return true;
622 }
623 return false;
624}
625
626bool
628{
629 for (auto & edge : Connections_)
630 {
631 if (&edge->GetTo() == this || !edge->IsDirected())
632 return true;
633 }
634 return false;
635}
636
637void
638Port::OutputIncomingEdgesASCII(std::ostream & out) const
639{
640 std::ostringstream text;
641 size_t numIncomingEdges = 0;
642
643 for (auto & edge : Connections_)
644 {
645 if (&edge->GetTo() != this && edge->IsDirected())
646 continue;
647
648 Port & otherEnd = edge->GetOtherEnd(*this);
649 if (numIncomingEdges == 0)
650 text << otherEnd.GetFullId();
651 else
652 text << ", " << otherEnd.GetFullId();
653
654 numIncomingEdges++;
655 }
656
657 if (numIncomingEdges == 1)
658 out << text.str();
659 else
660 out << "[" << text.str() << "]";
661}
662
664 : Port(),
665 Graph_(graph)
666{}
667
668const char *
670{
671 return "node";
672}
673
674Node &
676{
677 return *this;
678}
679
680Graph &
682{
683 return Graph_;
684}
685
686void
687Node::SetShape(std::string shape)
688{
689 SetAttribute("shape", std::move(shape));
690}
691
692void
693Node::SetFillColor(std::string color)
694{
695 // The dot output gives all nodes style=filled by default, so we only need to set the color
696 SetAttribute("fillcolor", std::move(color));
697}
698
699void
700Node::OutputDotPortId(std::ostream & out) const
701{
702 out << GetFullId();
703}
704
705void
706Node::OutputASCII(std::ostream & out, size_t indent) const
707{
708 withIndent(out, indent);
709 if (HasOutgoingEdges())
710 {
711 out << GetFullId() << ":";
712 }
713 printIdentifierSafe(out, GetLabelOr("NODE"));
714 if (HasIncomingEdges())
715 {
716 out << "<-";
718 }
719 out << std::endl;
720}
721
722void
723Node::OutputDot(std::ostream & out, size_t indent) const
724{
725 withIndent(out, indent) << GetFullId() << " [";
726 out << "label=";
727 if (HasLabel())
728 {
730 }
731 else
732 {
734 }
735 out << " ";
737 out << "];" << std::endl;
738}
739
740void
741Node::outputJson(std::ostream & out, size_t indent) const
742{
743 bool firstField = true;
744 outputJsonObjectOpening(out, indent, firstField);
745 outputJsonObjectClosing(out, indent, firstField);
746}
747
748void
749Node::OutputSubgraphs(std::ostream & out, OutputFormat format, size_t indent) const
750{
751 // Regular nodes do not have sub graphs
752}
753
755 : Node_(node)
756{}
757
758const char *
760{
761 return "in";
762}
763
764Node &
766{
767 return Node_;
768}
769
770bool
772{
773 return false;
774}
775
776void
777InputPort::SetFillColor(std::string color)
778{
779 // Attribute on the <TD> tag used by the dot output
781}
782
783void
784InputPort::OutputDotPortId(std::ostream & out) const
785{
786 out << Node_.GetFullId() << ":" << GetFullId() << ":n";
787}
788
789void
790InputPort::outputJson(std::ostream & out, size_t indent) const
791{
792 bool firstField = true;
793 outputJsonObjectOpening(out, indent, firstField);
794 outputJsonObjectClosing(out, indent, firstField);
795}
796
798 : Node_(node)
799{}
800
801const char *
803{
804 return "out";
805}
806
807Node &
809{
810 return Node_;
811}
812
813bool
815{
816 return false;
817}
818
819void
820OutputPort::SetFillColor(std::string color)
821{
822 // Attribute on the <TD> tag used by the dot output
824}
825
826void
827OutputPort::OutputDotPortId(std::ostream & out) const
828{
829 out << Node_.GetFullId() << ":" << GetFullId() << ":s";
830}
831
832void
833OutputPort::outputJson(std::ostream & out, size_t indent) const
834{
835 bool firstField = true;
836 outputJsonObjectOpening(out, indent, firstField);
837 outputJsonObjectClosing(out, indent, firstField);
838}
839
840InOutNode::InOutNode(Graph & graph, size_t inputPorts, size_t outputPorts)
841 : Node(graph)
842{
843 for (size_t i = 0; i < inputPorts; i++)
845
846 for (size_t i = 0; i < outputPorts; i++)
848}
849
850void
852{
853 throw Error("InOutNodes can not have custom shapes set");
854}
855
856InputPort &
858{
859 auto inputPort = new InputPort(*this);
860 InputPorts_.emplace_back(inputPort);
861 return *inputPort;
862}
863
864size_t
866{
867 return InputPorts_.size();
868}
869
870InputPort &
872{
873 JLM_ASSERT(index < InputPorts_.size());
874 return *InputPorts_[index];
875}
876
879{
880 auto outputPort = new OutputPort(*this);
881 OutputPorts_.emplace_back(outputPort);
882 return *outputPort;
883}
884
885size_t
887{
888 return OutputPorts_.size();
889}
890
893{
894 JLM_ASSERT(index < OutputPorts_.size());
895 return *OutputPorts_[index];
896}
897
898Graph &
900{
901 auto & graph = GetGraph().GetWriter().CreateSubGraph(*this);
902 SubGraphs_.push_back(&graph);
903 return graph;
904}
905
906size_t
908{
909 return SubGraphs_.size();
910}
911
912Graph &
914{
915 JLM_ASSERT(index < SubGraphs_.size());
916 return *SubGraphs_[index];
917}
918
919void
920InOutNode::SetHtmlTableAttribute(std::string name, std::string value)
921{
922 HtmlTableAttributes_[name] = std::move(value);
923}
924
925void
926InOutNode::SetFillColor(std::string color)
927{
929}
930
931void
933{
935
936 for (auto & inputPort : InputPorts_)
937 inputPort->Finalize();
938 for (auto & outputPort : OutputPorts_)
939 outputPort->Finalize();
940 for (auto & graph : SubGraphs_)
941 graph->Finalize();
942}
943
944void
945InOutNode::OutputSubgraphs(std::ostream & out, OutputFormat format, size_t indent) const
946{
947 // Only the ASCII format prints subgraphs inside the nodes themselves
949
950 for (auto & graph : SubGraphs_)
951 graph->Output(out, format, indent);
952}
953
954void
955InOutNode::OutputASCII(std::ostream & out, size_t indent) const
956{
957 withIndent(out, indent);
958
959 // output the names of all output ports
960 for (size_t i = 0; i < NumOutputPorts(); i++)
961 {
962 if (i != 0)
963 out << ", ";
964 out << OutputPorts_[i]->GetFullId();
965 }
966 if (NumOutputPorts() != 0)
967 out << " := ";
968
969 // If the node itself is used as a tail port, we must include its name
971 {
972 out << GetFullId() << ":";
973 }
974 printIdentifierSafe(out, GetLabelOr("NODE"));
976 {
977 out << "<-";
979 }
980 out << " ";
981
982 // Now output the origins of all input ports
983 for (size_t i = 0; i < NumInputPorts(); i++)
984 {
985 if (i != 0)
986 out << ", ";
987 InputPorts_[i]->OutputIncomingEdgesASCII(out);
988 }
989 out << std::endl;
990
991 // Output all sub graphs, if we have any
992 OutputSubgraphs(out, OutputFormat::ASCII, indent + 1);
993}
994
995void
996InOutNode::OutputDot(std::ostream & out, size_t indent) const
997{
998 withIndent(out, indent) << GetFullId() << " [shape=plain style=solid ";
999 out << "label=<" << std::endl;
1000
1001 // InOutNodes are printed as html tables
1002 out << "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">" << std::endl;
1003
1004 // Used to create rows of boxes above and below the node
1005 auto PrintPortList = [&out](auto & ports)
1006 {
1007 if (ports.empty())
1008 return;
1009
1010 out << "\t<TR><TD>" << std::endl;
1011 out << "\t\t<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\"><TR>" << std::endl;
1012 out << "\t\t\t<TD WIDTH=\"20\"></TD>" << std::endl;
1013 for (size_t i = 0; i < ports.size(); i++)
1014 {
1015 // Spacing
1016 if (i != 0)
1017 out << "\t\t\t<TD WIDTH=\"10\"></TD>" << std::endl;
1018
1019 auto & port = *ports[i];
1020 out << "\t\t\t<TD BORDER=\"1\" CELLPADDING=\"1\" ";
1021 out << "PORT=\"" << port.GetFullId() << "\" ";
1022 port.OutputAttributes(out, AttributeOutputFormat::HTMLAttributes);
1023 // Unless a different color is specified, fill the port cell with white
1024 if (!port.HasAttribute(DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE))
1025 {
1026 out << DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE << "=\"white\" ";
1027 }
1028 if (port.HasLabel())
1029 {
1030 out << "><FONT POINT-SIZE=\"10\">";
1031 printStringAsHtmlText(out, port.GetLabel(), true);
1032 out << "</FONT>";
1033 }
1034 else
1035 {
1036 // ports without labels have a fixed size
1037 out << " WIDTH=\"8\" HEIGHT=\"5\" FIXEDSIZE=\"true\">";
1038 }
1039 out << "</TD>" << std::endl;
1040 }
1041 out << "\t\t\t<TD WIDTH=\"20\"></TD>" << std::endl;
1042 out << "\t\t</TR></TABLE>" << std::endl;
1043 out << "\t</TD></TR>" << std::endl;
1044 };
1045
1046 // Inputs
1047 PrintPortList(InputPorts_);
1048
1049 // The main body of the node: a rounded rectangle
1050 out << "\t<TR><TD>" << std::endl;
1051 out << "\t\t<TABLE BORDER=\"1\" STYLE=\"ROUNDED\" CELLBORDER=\"0\" ";
1052 out << "CELLSPACING=\"0\" CELLPADDING=\"0\" ";
1053 for (auto & [name, value] : HtmlTableAttributes_)
1054 {
1056 out << "=\"";
1057 printStringAsHtmlText(out, value, false);
1058 out << "\" ";
1059 }
1060 // Unless a different color is specified, fill the table cell with white
1062 {
1063 out << DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE << "=\"white\" ";
1064 }
1065 out << ">" << std::endl;
1066 out << "\t\t\t<TR><TD CELLPADDING=\"1\">";
1068 out << "</TD></TR>" << std::endl;
1069
1070 // Subgraphs
1071 if (!SubGraphs_.empty())
1072 {
1073 out << "\t\t\t<TR><TD>" << std::endl;
1074 out << "\t\t\t\t<TABLE BORDER=\"0\" CELLSPACING=\"4\" CELLPADDING=\"2\"><TR>" << std::endl;
1075 for (auto & graph : SubGraphs_)
1076 {
1077 out << "\t\t\t\t\t<TD BORDER=\"1\" STYLE=\"ROUNDED\" WIDTH=\"40\" BGCOLOR=\"white\" ";
1078 out << "_SUBGRAPH=\"" << graph->GetFullId() << "\">";
1079 printStringAsHtmlText(out, graph->GetFullId(), true);
1080 out << "</TD>" << std::endl;
1081 }
1082 out << "\t\t\t\t</TR></TABLE>" << std::endl;
1083 out << "\t\t\t</TD></TR>" << std::endl;
1084 }
1085
1086 // End of the rounded rectangle
1087 out << "\t\t</TABLE>" << std::endl;
1088 out << "\t</TD></TR>" << std::endl;
1089
1090 PrintPortList(OutputPorts_);
1091
1092 out << "</TABLE>" << std::endl;
1093 withIndent(out, indent) << "> ";
1095 out << "];" << std::endl;
1096}
1097
1098void
1099InOutNode::outputJson(std::ostream & out, size_t indent) const
1100{
1101 bool firstField = true;
1102 outputJsonObjectOpening(out, indent, firstField);
1103 indent++;
1104
1105 printNextJsonField(out, JSON_NODE_TYPE_FIELD, indent, firstField) << "\"inout\"";
1106
1107 // Input ports
1108 if (NumInputPorts())
1109 {
1110 printNextJsonField(out, JSON_IN_PORTS_FIELD, indent, firstField);
1111 printJsonElementMap(out, indent + 1, InputPorts_);
1112 }
1113
1114 // Output ports
1115 if (NumOutputPorts())
1116 {
1117 printNextJsonField(out, JSON_OUT_PORTS_FIELD, indent, firstField);
1118 printJsonElementMap(out, indent + 1, OutputPorts_);
1119 }
1120
1121 // Subgraphs
1122 if (NumSubgraphs())
1123 {
1124 printNextJsonField(out, JSON_SUBGRAPHS_FIELD, indent, firstField) << "[";
1125 bool first = true;
1126 for (const auto & subgraph : SubGraphs_)
1127 {
1128 if (first)
1129 first = false;
1130 else
1131 out << ", ";
1132 out << '"';
1133 subgraph->PrintFullId(out);
1134 out << '"';
1135 }
1136 out << "]";
1137 }
1138
1139 // HTML Table attributes
1140 if (!HtmlTableAttributes_.empty())
1141 {
1142 printNextJsonField(out, JSON_HTML_TABLE_ATTRIBUTES_FIELD, indent, firstField) << "{";
1143 bool first = true;
1144 for (const auto & [key, value] : HtmlTableAttributes_)
1145 {
1146 if (first)
1147 first = false;
1148 else
1149 out << ", ";
1151 }
1152 out << "}";
1153 }
1154
1155 indent--;
1156 outputJsonObjectClosing(out, indent, firstField);
1157}
1158
1160 : Node(graph),
1161 OutsideSource_(nullptr)
1162{}
1163
1164const char *
1166{
1167 return "arg";
1168}
1169
1170bool
1172{
1173 return false;
1174}
1175
1176void
1178{
1179 OutsideSource_ = &outsideSource;
1180 SetAttributeGraphElement("outsideSource", outsideSource);
1181}
1182
1183void
1184ArgumentNode::OutputASCII(std::ostream & out, size_t) const
1185{
1186 // In ASCII the argument is printed as part of an ARG line
1187 out << GetFullId();
1188 if (HasLabel())
1189 {
1190 out << ":";
1192 }
1193 if (OutsideSource_ != nullptr)
1194 {
1195 out << " <= ";
1197 }
1198}
1199
1201 : Node(graph),
1202 OutsideDestination_(nullptr)
1203{}
1204
1205const char *
1207{
1208 return "res";
1209}
1210
1211bool
1213{
1214 return false;
1215}
1216
1217void
1218ResultNode::SetOutsideDestination(const Port & outsideDestination)
1219{
1220 OutsideDestination_ = &outsideDestination;
1221 SetAttributeGraphElement("outsideDest", outsideDestination);
1222}
1223
1224void
1225ResultNode::OutputASCII(std::ostream & out, size_t) const
1226{
1227 // In ASCII the result is printed as part of an RES line
1229 if (HasLabel())
1230 {
1231 out << ":";
1233 }
1234 if (OutsideDestination_ != nullptr)
1235 out << " => " << OutsideDestination_->GetFullId();
1236}
1237
1238Edge::Edge(Port & from, Port & to, bool directed)
1239 : From_(from),
1240 To_(to),
1241 Directed_(directed)
1242{
1243 from.OnEdgeAdded(*this);
1244 to.OnEdgeAdded(*this);
1245}
1246
1247const char *
1249{
1250 return "edge";
1251}
1252
1253Graph &
1255{
1256 // from and to have the same graph, return either
1257 return From_.GetGraph();
1258}
1259
1260Port &
1262{
1263 return From_;
1264}
1265
1266Port &
1268{
1269 return To_;
1270}
1271
1272bool
1274{
1275 return Directed_;
1276}
1277
1278Port &
1280{
1281 if (&end == &From_)
1282 return To_;
1283 else if (&end == &To_)
1284 return From_;
1285
1286 JLM_UNREACHABLE("GetOtherEnd called with neither end");
1287}
1288
1289void
1290Edge::SetStyle(std::string style)
1291{
1292 SetAttribute("style", std::move(style));
1293}
1294
1295void
1296Edge::SetArrowHead(std::string arrow)
1297{
1298 SetAttribute("arrowhead", std::move(arrow));
1299}
1300
1301void
1302Edge::SetArrowTail(std::string arrow)
1303{
1304 // When outputting dot, the "dir" attribute will be automatically changed to make the tail visible
1305 SetAttribute("arrowtail", std::move(arrow));
1306}
1307
1308std::string_view
1310{
1311 const bool hasHeadArrow = HasAttribute("arrowhead") || Directed_;
1312 const bool hasTailArrow = HasAttribute("arrowtail");
1313 if (hasHeadArrow && hasTailArrow)
1314 return "both";
1315 else if (hasHeadArrow)
1316 return "forward";
1317 else if (hasTailArrow)
1318 return "back";
1319 else
1320 return "none";
1321}
1322
1323void
1324Edge::OutputDot(std::ostream & out, size_t indent) const
1325{
1326 withIndent(out, indent);
1328 out << " -> ";
1329 To_.OutputDotPortId(out);
1330 out << "[";
1331
1332 out << "dir=" << getDirection() << " ";
1333
1334 if (HasLabel())
1335 {
1336 out << "label=";
1338 out << " ";
1339 }
1340
1341 // Edges are not normally named, so use the id attribute to include the edge's id
1343 {
1344 out << DOT_EDGE_ID_ATTRIBUTE << "=";
1346 out << " ";
1347 }
1348
1350 out << "];" << std::endl;
1351}
1352
1353void
1354Edge::outputJson(std::ostream & out, size_t indent) const
1355{
1356 bool firstField = true;
1357 outputJsonObjectOpening(out, indent, firstField);
1358 indent++;
1359
1360 printNextJsonField(out, "from", indent, firstField);
1361 out << '"';
1363 out << '"';
1364
1365 printNextJsonField(out, "to", indent, firstField);
1366 out << '"';
1367 To_.OutputDotPortId(out);
1368 out << '"';
1369
1370 // Direction
1371 printNextJsonField(out, "dir", indent, firstField);
1372 out << '"' << getDirection() << '"';
1373
1374 indent--;
1375 outputJsonObjectClosing(out, indent, firstField);
1376}
1377
1379 : GraphElement(),
1380 Writer_(writer),
1381 ParentNode_(nullptr)
1382{}
1383
1384Graph::Graph(Writer & writer, Node & parentNode)
1385 : GraphElement(),
1386 Writer_(writer),
1387 ParentNode_(&parentNode)
1388{}
1389
1390const char *
1392{
1393 return "graph";
1394}
1395
1396Graph &
1398{
1399 return *this;
1400}
1401
1402Writer &
1404{
1405 return Writer_;
1406}
1407
1408const Writer &
1410{
1411 return Writer_;
1412}
1413
1414bool
1416{
1417 return ParentNode_ != nullptr;
1418}
1419
1420Node &
1422{
1423 auto node = new Node(*this);
1424 Nodes_.emplace_back(node);
1425 return *node;
1426}
1427
1428InOutNode &
1429Graph::CreateInOutNode(size_t inputPorts, size_t outputPorts)
1430{
1431 auto node = new InOutNode(*this, inputPorts, outputPorts);
1432 Nodes_.emplace_back(node);
1433 return *node;
1434}
1435
1436size_t
1437Graph::NumNodes() const noexcept
1438{
1439 return Nodes_.size();
1440}
1441
1442Node &
1443Graph::GetNode(size_t index)
1444{
1445 JLM_ASSERT(index < NumNodes());
1446 return *Nodes_[index];
1447}
1448
1451{
1452 auto node = new ArgumentNode(*this);
1453 ArgumentNodes_.emplace_back(node);
1454 return *node;
1455}
1456
1457size_t
1459{
1460 return ArgumentNodes_.size();
1461}
1462
1463Node &
1465{
1466 JLM_ASSERT(index < NumArgumentNodes());
1467 return *ArgumentNodes_[index];
1468}
1469
1470ResultNode &
1472{
1473 auto node = new ResultNode(*this);
1474 ResultNodes_.emplace_back(node);
1475 return *node;
1476}
1477
1478size_t
1480{
1481 return ResultNodes_.size();
1482}
1483
1484Node &
1486{
1487 JLM_ASSERT(index < NumResultNodes());
1488 return *ResultNodes_[index];
1489}
1490
1491Edge &
1492Graph::CreateEdge(Port & from, Port & to, bool directed)
1493{
1494 // Edges must be between ports in this graph
1495 JLM_ASSERT(&from.GetGraph() == this);
1496 JLM_ASSERT(&to.GetGraph() == this);
1497
1498 // Edge's constructor informs the ports about the edge
1499 auto edge = new Edge(from, to, directed);
1500 Edges_.emplace_back(edge);
1501 return *edge;
1502}
1503
1504size_t
1505Graph::NumEdges() const noexcept
1506{
1507 return Edges_.size();
1508}
1509
1510Edge &
1511Graph::GetEdge(size_t index)
1512{
1513 JLM_ASSERT(index < NumEdges());
1514 return *Edges_[index];
1515}
1516
1517Edge *
1519{
1520 for (auto edge : a.GetConnections())
1521 {
1522 if (edge->IsDirected() && &edge->GetFrom() != &a)
1523 continue;
1524 if (&edge->GetOtherEnd(a) == &b)
1525 return edge;
1526 }
1527 return nullptr;
1528}
1529
1532{
1533 if (auto it = ProgramObjectMapping_.find(object); it != ProgramObjectMapping_.end())
1534 return it->second;
1535 return nullptr;
1536}
1537
1538void
1540{
1541 JLM_ASSERT(&element.GetGraph() == this);
1542
1543 uintptr_t object = element.GetProgramObject();
1544 JLM_ASSERT(object != 0);
1545
1546 auto & slot = ProgramObjectMapping_[object];
1547 JLM_ASSERT(slot == nullptr && "Trying to map a GraphElement to an already mapped program object");
1548 slot = &element;
1549
1550 // In addition to adding the element to the Graph's mapping,
1551 // the GraphWriter has its own mapping containing elements from all graphs
1553}
1554
1555void
1557{
1558 JLM_ASSERT(&element.GetGraph() == this);
1559
1560 auto object = element.GetProgramObject();
1561 JLM_ASSERT(object != 0);
1562
1563 size_t erased = ProgramObjectMapping_.erase(object);
1564 JLM_ASSERT(erased == 1);
1565
1567}
1568
1569void
1571{
1573
1574 for (auto & arg : ArgumentNodes_)
1575 arg->Finalize();
1576 // Nodes with sub graphs also finalize them
1577 for (auto & node : Nodes_)
1578 node->Finalize();
1579 for (auto & res : ResultNodes_)
1580 res->Finalize();
1581 for (auto & edge : Edges_)
1582 edge->Finalize();
1583}
1584
1585void
1586Graph::OutputASCII(std::ostream & out, size_t indent) const
1587{
1588 withIndent(out, indent) << "{" << std::endl;
1589 indent++;
1590
1591 // Use a single ARG line for all graph arguments
1592 bool anyArguments = false;
1593 for (auto & arg : ArgumentNodes_)
1594 {
1595 if (!anyArguments)
1596 withIndent(out, indent) << "ARG ";
1597 else
1598 out << ", ";
1599 anyArguments = true;
1600 arg->OutputASCII(out, indent);
1601 }
1602 if (anyArguments)
1603 out << std::endl;
1604
1605 // Print all other nodes in order
1606 for (auto & node : Nodes_)
1607 {
1608 // Will also print sub graphs recursively
1609 node->OutputASCII(out, indent);
1610 }
1611
1612 // Use a single RES line for all graph results
1613 bool anyResults = false;
1614 for (auto & res : ResultNodes_)
1615 {
1616 if (!anyResults)
1617 withIndent(out, indent) << "RES ";
1618 else
1619 out << ", ";
1620 anyResults = true;
1621 res->OutputASCII(out, indent);
1622 }
1623 if (anyResults)
1624 out << std::endl;
1625
1626 indent--;
1627 withIndent(out, indent) << "}" << std::endl;
1628}
1629
1630void
1631Graph::OutputDot(std::ostream & out, size_t indent) const
1632{
1633 withIndent(out, indent) << "digraph " << GetFullId() << " {" << std::endl;
1634 indent++;
1635
1636 // Default node attributes. Filling nodes by default makes them easier to click
1637 withIndent(out, indent)
1638 << "node[shape=box style=filled fillcolor=white width=0.1 height=0.1 margin=0.05];"
1639 << std::endl;
1640 withIndent(out, indent) << "penwidth=6;" << std::endl;
1641 if (HasLabel())
1642 {
1643 withIndent(out, indent) << "label=";
1645 out << std::endl;
1646 }
1647 withIndent(out, indent);
1649 out << std::endl;
1650
1651 // Helper function used to print argument nodes and result nodes
1652 auto PrintOrderedSubgraph = [&out](auto & nodes, const char * rank, size_t indent)
1653 {
1654 if (nodes.empty())
1655 return;
1656 withIndent(out, indent++) << "{" << std::endl;
1657 withIndent(out, indent) << "rank=" << rank << ";" << std::endl;
1658 for (size_t i = 0; i < nodes.size(); i++)
1659 {
1660 nodes[i]->OutputDot(out, indent);
1661
1662 // Use invisible edges to order nodes in the subgraph
1663 if (i != 0)
1664 withIndent(out, indent) << nodes[i - 1]->GetFullId() << " -> " << nodes[i]->GetFullId()
1665 << "[style=invis];" << std::endl;
1666 }
1667 withIndent(out, --indent) << "}" << std::endl;
1668 };
1669
1670 PrintOrderedSubgraph(ArgumentNodes_, "source", indent);
1671
1672 for (auto & node : Nodes_)
1673 {
1674 node->OutputDot(out, indent);
1675 }
1676
1677 PrintOrderedSubgraph(ResultNodes_, "sink", indent);
1678
1679 for (auto & edge : Edges_)
1680 {
1681 edge->OutputDot(out, indent);
1682 }
1683
1684 indent--;
1685 withIndent(out, indent) << "}" << std::endl;
1686}
1687
1688void
1689Graph::outputJson(std::ostream & out, size_t indent) const
1690{
1691 bool firstField = true;
1692 outputJsonObjectOpening(out, indent, firstField);
1693 indent++;
1694
1695 // If we are a subgraph, list both the node and its parent graph
1696 if (IsSubgraph())
1697 {
1698 printNextJsonField(out, JSON_PARENT_NODE_FIELD, indent, firstField);
1699 out << '"';
1701 out << '"';
1702
1703 printNextJsonField(out, JSON_PARENT_GRAPH_FIELD, indent, firstField);
1704 out << '"';
1706 out << '"';
1707 }
1708
1709 // Arguments
1710 if (!ArgumentNodes_.empty())
1711 {
1712 printNextJsonField(out, JSON_ARGUMENTS_FIELD, indent, firstField);
1713 printJsonElementMap(out, indent, ArgumentNodes_);
1714 }
1715
1716 // Nodes
1717 if (!Nodes_.empty())
1718 {
1719 printNextJsonField(out, JSON_NODES_FIELD, indent, firstField);
1720 printJsonElementMap(out, indent, Nodes_);
1721 }
1722
1723 // Results
1724 if (!ResultNodes_.empty())
1725 {
1726 printNextJsonField(out, JSON_RESULTS_FIELD, indent, firstField);
1727 printJsonElementMap(out, indent, ResultNodes_);
1728 }
1729
1730 // Edges
1731 if (!Edges_.empty())
1732 {
1733 printNextJsonField(out, JSON_EDGES_FIELD, indent, firstField);
1734 printJsonElementMap(out, indent, Edges_);
1735 }
1736
1737 indent--;
1738 outputJsonObjectClosing(out, indent, firstField);
1739}
1740
1741void
1742Graph::Output(std::ostream & out, OutputFormat format, size_t indent) const
1743{
1745
1746 switch (format)
1747 {
1749 OutputASCII(out, indent);
1750 break;
1751 case OutputFormat::Dot:
1752 OutputDot(out, indent);
1753 break;
1754 case OutputFormat::Json:
1755 outputJson(out, indent);
1756 break;
1757 default:
1758 JLM_UNREACHABLE("Unknown output format");
1759 }
1760}
1761
1762Graph &
1764{
1765 auto graph = new Graph(*this);
1766 Graphs_.emplace_back(graph);
1767 return *graph;
1768}
1769
1770size_t
1771Writer::NumGraphs() const noexcept
1772{
1773 return Graphs_.size();
1774}
1775
1776Graph &
1777Writer::GetGraph(size_t index)
1778{
1779 JLM_ASSERT(index < NumGraphs());
1780 return *Graphs_[index];
1781}
1782
1783Graph &
1785{
1786 auto graph = new Graph(*this, parentNode);
1787 Graphs_.emplace_back(graph);
1788 return *graph;
1789}
1790
1793{
1794 if (auto it = ProgramObjectMapping_.find(object); it != ProgramObjectMapping_.end())
1795 return it->second;
1796 return nullptr;
1797}
1798
1799void
1801{
1802 JLM_ASSERT(&element.GetGraph().GetWriter() == this);
1803
1804 uintptr_t object = element.GetProgramObject();
1805 JLM_ASSERT(object != 0);
1806
1807 // This may override an existing mapping,
1808 // if multiple graphs contain elements representing the same program object
1809 ProgramObjectMapping_[object] = &element;
1810}
1811
1812void
1814{
1815 JLM_ASSERT(&element.GetGraph().GetWriter() == this);
1816
1817 uintptr_t object = element.GetProgramObject();
1818 JLM_ASSERT(object != 0);
1819
1820 // If the pair (object, &element) exists in the writer's mapping, remove it
1821 if (auto it = ProgramObjectMapping_.find(object); it != ProgramObjectMapping_.end())
1822 {
1823 // Check that there is not some other GraphElement currently mapped to the object
1824 if (it->second == &element)
1825 ProgramObjectMapping_.erase(it);
1826 }
1827}
1828
1829size_t
1831{
1832 size_t & nextValue = NextUniqueIdStubSuffix_[idStub];
1833 return nextValue++;
1834}
1835
1836void
1838{
1839 for (auto & graph : Graphs_)
1840 if (!graph->IsSubgraph())
1841 graph->Finalize();
1842}
1843
1844void
1845Writer::outputAllGraphs(std::ostream & out, OutputFormat format)
1846{
1847 Finalize();
1848
1849 switch (format)
1850 {
1852 for (auto & graph : Graphs_)
1853 {
1854 // In ASCII printing, subgraphs are printed inside their nodes,
1855 // so we only output root graphs in this loop
1856 if (!graph->IsSubgraph())
1857 graph->Output(out, format, 0);
1858 }
1859 break;
1860
1861 case OutputFormat::Dot:
1862 for (auto & graph : Graphs_)
1863 {
1864 graph->Output(out, format, 0);
1865 }
1866 break;
1867
1868 case OutputFormat::Json:
1869 {
1870 out << "{" << std::endl;
1871 bool first = true;
1872 for (auto & graph : Graphs_)
1873 {
1874 if (first)
1875 first = false;
1876 else
1877 out << "," << std::endl;
1878 graph->Output(out, format, 1);
1879 }
1880 out << std::endl << "}" << std::endl;
1881 break;
1882 }
1883
1884 default:
1885 JLM_UNREACHABLE("Unknown OutputFormat");
1886 }
1887}
1888}
std::vector< rvsdg::Node * > nodes
bool CanBeEdgeHead() const override
void SetOutsideSource(const Port &outsideSource)
const char * GetIdPrefix() const override
void OutputASCII(std::ostream &out, size_t indent) const override
void SetStyle(std::string style)
void SetArrowTail(std::string arrow)
Port & GetOtherEnd(const Port &end)
const char * GetIdPrefix() const override
Graph & GetGraph() override
std::string_view getDirection() const
Edge(Port &from, Port &to, bool directed)
void OutputDot(std::ostream &out, size_t indent) const
void SetArrowHead(std::string arrow)
void outputJson(std::ostream &out, size_t indent) const
void outputJsonObjectOpening(std::ostream &out, size_t indent, bool &firstField) const
void SetLabel(std::string label)
uintptr_t GetProgramObject() const noexcept
std::string GetFullId() const
void SetAttributeObject(const std::string &attribute, uintptr_t object)
virtual Graph & GetGraph()=0
void AppendToLabel(std::string_view text, std::string_view sep="\n")
void SetAttribute(const std::string &attribute, std::string value)
std::string_view GetLabelOr(std::string_view otherwise) const
const std::string & GetLabel() const
void PrintFullId(std::ostream &out) const
bool RemoveAttribute(const std::string &attribute)
virtual const char * GetIdPrefix() const =0
std::optional< uintptr_t > GetAttributeObject(const std::string &attribute) const
std::unordered_map< std::string, AttributeValue > AttributeMap_
void OutputAttribute(std::ostream &out, const std::string &name, AttributeOutputFormat format) const
void OutputAttributes(std::ostream &out, AttributeOutputFormat format) const
bool HasAttribute(const std::string &attribute) const
const GraphElement * GetAttributeGraphElement(const std::string &attribute) const
std::optional< size_t > UniqueIdSuffix_
std::optional< std::string_view > GetAttributeString(const std::string &attribute) const
void SetProgramObjectUintptr(uintptr_t object)
void SetAttributeGraphElement(const std::string &attribute, const GraphElement &element)
bool HasProgramObject() const noexcept
std::vector< std::unique_ptr< ArgumentNode > > ArgumentNodes_
ArgumentNode & CreateArgumentNode()
Node & GetNode(size_t index)
void Output(std::ostream &out, OutputFormat format, size_t indent) const
GraphElement * GetElementFromProgramObject(uintptr_t object) const
std::unordered_map< uintptr_t, GraphElement * > ProgramObjectMapping_
Node & GetResultNode(size_t index)
Edge & GetEdge(size_t index)
Edge * GetEdgeBetween(Port &a, Port &b)
void OutputASCII(std::ostream &out, size_t indent) const
void RemoveProgramObjectMapping(GraphElement &element)
const char * GetIdPrefix() const override
std::vector< std::unique_ptr< Edge > > Edges_
Graph & GetGraph() override
ResultNode & CreateResultNode()
size_t NumResultNodes() const noexcept
std::vector< std::unique_ptr< ResultNode > > ResultNodes_
void OutputDot(std::ostream &out, size_t indent) const
size_t NumEdges() const noexcept
size_t NumNodes() const noexcept
Node & GetArgumentNode(size_t index)
InOutNode & CreateInOutNode(size_t inputPorts, size_t outputPorts)
size_t NumArgumentNodes() const noexcept
void outputJson(std::ostream &out, size_t indent) const
std::vector< std::unique_ptr< Node > > Nodes_
Edge & CreateEdge(Port &from, Port &to, bool directed)
void MapProgramObjectToElement(GraphElement &element)
void OutputSubgraphs(std::ostream &out, OutputFormat format, size_t indent) const override
std::unordered_map< std::string, std::string > HtmlTableAttributes_
std::vector< std::unique_ptr< OutputPort > > OutputPorts_
void OutputDot(std::ostream &out, size_t indent) const override
InOutNode(Graph &graph, size_t inputPorts, size_t outputPorts)
void outputJson(std::ostream &out, size_t indent) const override
void SetFillColor(std::string color) override
OutputPort & GetOutputPort(size_t index)
std::vector< Graph * > SubGraphs_
std::vector< std::unique_ptr< InputPort > > InputPorts_
void SetShape(std::string) override
InputPort & GetInputPort(size_t index)
Graph & GetSubgraph(size_t index)
void OutputASCII(std::ostream &out, size_t indent) const override
void SetHtmlTableAttribute(std::string name, std::string value)
const char * GetIdPrefix() const override
bool CanBeEdgeTail() const override
void SetFillColor(std::string color) override
void outputJson(std::ostream &out, size_t indent) const
void OutputDotPortId(std::ostream &out) const override
Node & GetNode() override
const char * GetIdPrefix() const override
Graph & GetGraph() override
virtual void OutputDot(std::ostream &out, size_t indent) const
virtual void OutputSubgraphs(std::ostream &out, OutputFormat format, size_t indent) const
void OutputDotPortId(std::ostream &out) const override
virtual void OutputASCII(std::ostream &out, size_t indent) const
virtual void outputJson(std::ostream &out, size_t indent) const
virtual void SetShape(std::string shape)
void SetFillColor(std::string color) override
void SetFillColor(std::string color) override
void outputJson(std::ostream &out, size_t indent) const
bool CanBeEdgeHead() const override
void OutputDotPortId(std::ostream &out) const override
const char * GetIdPrefix() const override
const std::vector< Edge * > & GetConnections() const
virtual bool CanBeEdgeTail() const
virtual bool CanBeEdgeHead() const
virtual Node & GetNode()=0
std::vector< Edge * > Connections_
bool HasIncomingEdges() const
void OutputIncomingEdgesASCII(std::ostream &out) const
Graph & GetGraph() override
void OnEdgeAdded(Edge &edge)
bool HasOutgoingEdges() const
virtual void OutputDotPortId(std::ostream &out) const =0
void SetOutsideDestination(const Port &outsideSource)
void OutputASCII(std::ostream &out, size_t indent) const override
const char * GetIdPrefix() const override
bool CanBeEdgeTail() const override
GraphElement * GetElementFromProgramObject(uintptr_t object) const
size_t GetNextUniqueIdStubSuffix(const char *idStub)
std::unordered_map< std::string, size_t > NextUniqueIdStubSuffix_
std::unordered_map< uintptr_t, GraphElement * > ProgramObjectMapping_
void removeProgramObjectMapping(GraphElement &element)
size_t NumGraphs() const noexcept
Graph & CreateSubGraph(Node &parentNode)
std::vector< std::unique_ptr< Graph > > Graphs_
void outputAllGraphs(std::ostream &out, OutputFormat format)
Graph & GetGraph(size_t index)
void mapProgramObjectToElement(GraphElement &element)
#define JLM_ASSERT(x)
Definition common.hpp:16
#define JLM_UNREACHABLE(msg)
Definition common.hpp:43
static void printJsonString(std::ostream &out, std::string_view string)
static const char *const JSON_PARENT_NODE_FIELD
std::ostream & withIndent(std::ostream &out, size_t indent)
static void printStringAsHtmlAttributeName(std::ostream &out, std::string_view string)
static void outputJsonObjectClosing(std::ostream &out, size_t indent, bool firstField)
static const char *const JSON_NODES_FIELD
static const char *const DOT_EDGE_ID_ATTRIBUTE
static const char *const JSON_LABEL_FIELD
static std::ostream & printNextJsonField(std::ostream &out, std::string_view name, size_t indent, bool &firstField)
static const char *const JSON_SUBGRAPHS_FIELD
static void printIdentifierSafe(std::ostream &out, std::string_view string)
static const char *const DOT_TOOLTIP_ATTRIBUTE
static void printJsonElementMap(std::ostream &out, size_t indent, const T &elements)
static const char *const DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE
static const char *const JSON_HTML_TABLE_ATTRIBUTES_FIELD
static bool looksLikeIdentifier(std::string_view string)
static const char *const JSON_ATTRIBUTE_FIELD
static const char *const JSON_NODE_TYPE_FIELD
static const char *const JSON_OBJECT_POINTER_FIELD
static const char *const JSON_IN_PORTS_FIELD
static const char *const JSON_OUT_PORTS_FIELD
static const char *const JSON_PARENT_GRAPH_FIELD
static const char *const JSON_ARGUMENTS_FIELD
static void outputKeyValuePair(std::ostream &out, std::string_view name, std::string_view value, AttributeOutputFormat format)
static void printStringAsHtmlText(std::ostream &out, std::string_view string, bool replaceNewlines)
static const char *const JSON_EDGES_FIELD
static const char *const JSON_RESULTS_FIELD
static std::string strfmt(Args... args)
Definition strfmt.hpp:35