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
275
276void
281
282bool
284{
285 return ProgramObject_ != 0;
286}
287
288uintptr_t
290{
291 return ProgramObject_;
292}
293
294void
295GraphElement::SetAttribute(const std::string & attribute, std::string value)
296{
297 AttributeMap_[attribute] = std::move(value);
298}
299
300void
301GraphElement::SetAttributeObject(const std::string & attribute, uintptr_t object)
302{
303 JLM_ASSERT(object);
304 AttributeMap_[attribute] = object;
305}
306
307void
308GraphElement::SetAttributeGraphElement(const std::string & attribute, const GraphElement & element)
309{
310 JLM_ASSERT(&GetGraph().GetWriter() == &element.GetGraph().GetWriter());
311 AttributeMap_[attribute] = &element;
312}
313
314bool
315GraphElement::HasAttribute(const std::string & attribute) const
316{
317 return AttributeMap_.find(attribute) != AttributeMap_.end();
318}
319
320std::optional<std::string_view>
321GraphElement::GetAttributeString(const std::string & attribute) const
322{
323 if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
324 {
325 if (auto stringValue = std::get_if<std::string>(&it->second))
326 {
327 return *stringValue;
328 }
329 }
330 return std::nullopt;
331}
332
333std::optional<uintptr_t>
334GraphElement::GetAttributeObject(const std::string & attribute) const
335{
336 if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
337 {
338 if (auto uintptrValue = std::get_if<uintptr_t>(&it->second))
339 {
340 return *uintptrValue;
341 }
342 }
343 return std::nullopt;
344}
345
346const GraphElement *
347GraphElement::GetAttributeGraphElement(const std::string & attribute) const
348{
349 if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
350 {
351 if (auto graphElementValue = std::get_if<const GraphElement *>(&it->second))
352 {
353 return *graphElementValue;
354 }
355
356 // Otherwise, check if this attribute holds a program object that is represented by a
357 // GraphElement in this graph, or in any graph in the GraphWriter.
358 if (auto ptr = std::get_if<uintptr_t>(&it->second))
359 {
360 if (auto gElement = GetGraph().GetElementFromProgramObject(*ptr))
361 {
362 return gElement;
363 }
364 if (auto gwElement = GetGraph().GetWriter().GetElementFromProgramObject(*ptr))
365 {
366 return gwElement;
367 }
368 }
369 }
370 return nullptr;
371}
372
373bool
374GraphElement::RemoveAttribute(const std::string & attribute)
375{
376 return AttributeMap_.erase(attribute);
377}
378
379void
381{
382 if (IsFinalized())
383 return;
384
385 auto & writer = GetGraph().GetWriter();
387}
388
389bool
391{
392 return UniqueIdSuffix_.has_value();
393}
394
398static void
400 std::ostream & out,
401 std::string_view name,
402 std::string_view value,
404{
406 {
407 printIdentifierSafe(out, name);
408 out << "=";
409 printIdentifierSafe(out, value);
410 out << " "; // space separation
411 }
412 else if (format == AttributeOutputFormat::HTMLAttributes)
413 {
415 out << "=\""; // HTML attributes must be quoted
416 printStringAsHtmlText(out, value, false);
417 out << "\" "; // Closing quote and separating space
418 }
419 else if (format == AttributeOutputFormat::JSON)
420 {
421 printJsonString(out, name);
422 out << ':';
423 printJsonString(out, value);
424 }
425 else
426 {
427 JLM_UNREACHABLE("Unknown AttributeOutputFormat");
428 }
429}
430
431void
433 std::ostream & out,
434 const std::string & name,
435 AttributeOutputFormat format) const
436{
437 if (auto string = GetAttributeString(name))
438 {
439 outputKeyValuePair(out, name, *string, format);
440 }
441 else if (auto graphElement = GetAttributeGraphElement(name))
442 {
443 outputKeyValuePair(out, name, graphElement->GetFullId(), format);
444 }
445 else if (auto object = GetAttributeObject(name))
446 {
447 outputKeyValuePair(out, name, strfmt("ptr", reinterpret_cast<void *>(*object)), format);
448 }
449 else
450 {
451 JLM_UNREACHABLE("Unknown attribute type");
452 }
453}
454
455void
457{
458 bool first = true;
459 const auto next = [&]()
460 {
461 if (first)
462 first = false;
463 else if (format == AttributeOutputFormat::JSON)
464 out << ", ";
465 else
466 out << ' ';
467 };
468
469 for (const auto & [name, _] : AttributeMap_)
470 {
471 next();
472 OutputAttribute(out, name, format);
473 }
474
475 // If no tooltip attribute is specified, use the program object pointer.
476 // This is not done in JSON, as the program object is included in a separate field.
478 && HasProgramObject())
479 {
480 next();
482 out,
484 strfmt(reinterpret_cast<void *>(GetProgramObject())),
485 format);
486 }
487}
488
494static std::ostream &
495printNextJsonField(std::ostream & out, std::string_view name, size_t indent, bool & firstField)
496{
497 if (firstField)
498 firstField = false;
499 else
500 out << ',';
501 out << std::endl;
502 withIndent(out, indent) << '"' << name << "\": ";
503
504 return out;
505};
506
507template<typename T>
508static void
509printJsonElementMap(std::ostream & out, size_t indent, const T & elements)
510{
511 out << "{";
512 bool first = true;
513 for (auto & element : elements)
514 {
515 if (first)
516 first = false;
517 else
518 out << ",";
519 out << std::endl;
520 element->outputJson(out, indent + 1);
521 }
522
523 if (first)
524 out << "}";
525 else
526 {
527 out << std::endl;
528 withIndent(out, indent) << "}";
529 }
530};
531
532void
533GraphElement::outputJsonObjectOpening(std::ostream & out, size_t indent, bool & firstField) const
534{
535 withIndent(out, indent) << "\"";
536 PrintFullId(out); // The full id does not include quotes or special characters
537 out << "\": {";
538
539 indent++;
540 if (HasLabel())
541 {
542 printNextJsonField(out, JSON_LABEL_FIELD, indent, firstField);
544 }
545
546 if (HasProgramObject())
547 {
548 printNextJsonField(out, JSON_OBJECT_POINTER_FIELD, indent, firstField);
549 out << '"' << reinterpret_cast<void *>(GetProgramObject()) << '"';
550 }
551
552 if (!AttributeMap_.empty())
553 {
554 printNextJsonField(out, JSON_ATTRIBUTE_FIELD, indent, firstField);
555 out << '{';
557 out << '}';
558 }
559}
560
561static void
562outputJsonObjectClosing(std::ostream & out, size_t indent, bool firstField)
563{
564 // The object contains at least one field, close on the next line
565 if (!firstField)
566 {
567 out << std::endl;
568 withIndent(out, indent);
569 }
570 out << "}";
571}
572
574 : GraphElement()
575{}
576
577Graph &
579{
580 return GetNode().GetGraph();
581}
582
583bool
585{
586 return true;
587}
588
589bool
591{
592 return true;
593}
594
595const std::vector<Edge *> &
597{
598 return Connections_;
599}
600
601void
603{
604 if (this == &edge.GetFrom())
605 JLM_ASSERT(CanBeEdgeTail() || !edge.IsDirected());
606 else if (this == &edge.GetTo())
607 JLM_ASSERT(CanBeEdgeHead() || !edge.IsDirected());
608 else
609 JLM_UNREACHABLE("Port was informed about unrelated edge");
610
611 Connections_.push_back(&edge);
612}
613
614bool
616{
617 for (auto & edge : Connections_)
618 {
619 if (&edge->GetFrom() == this || !edge->IsDirected())
620 return true;
621 }
622 return false;
623}
624
625bool
627{
628 for (auto & edge : Connections_)
629 {
630 if (&edge->GetTo() == this || !edge->IsDirected())
631 return true;
632 }
633 return false;
634}
635
636void
637Port::OutputIncomingEdgesASCII(std::ostream & out) const
638{
639 std::ostringstream text;
640 size_t numIncomingEdges = 0;
641
642 for (auto & edge : Connections_)
643 {
644 if (&edge->GetTo() != this && edge->IsDirected())
645 continue;
646
647 Port & otherEnd = edge->GetOtherEnd(*this);
648 if (numIncomingEdges == 0)
649 text << otherEnd.GetFullId();
650 else
651 text << ", " << otherEnd.GetFullId();
652
653 numIncomingEdges++;
654 }
655
656 if (numIncomingEdges == 1)
657 out << text.str();
658 else
659 out << "[" << text.str() << "]";
660}
661
663 : Port(),
664 Graph_(graph)
665{}
666
667const char *
669{
670 return "node";
671}
672
673Node &
675{
676 return *this;
677}
678
679Graph &
681{
682 return Graph_;
683}
684
685void
686Node::SetShape(std::string shape)
687{
688 SetAttribute("shape", std::move(shape));
689}
690
691void
692Node::SetFillColor(std::string color)
693{
694 // The dot output gives all nodes style=filled by default, so we only need to set the color
695 SetAttribute("fillcolor", std::move(color));
696}
697
698void
699Node::OutputDotPortId(std::ostream & out) const
700{
701 out << GetFullId();
702}
703
704void
705Node::OutputASCII(std::ostream & out, size_t indent) const
706{
707 withIndent(out, indent);
708 if (HasOutgoingEdges())
709 {
710 out << GetFullId() << ":";
711 }
712 printIdentifierSafe(out, GetLabelOr("NODE"));
713 if (HasIncomingEdges())
714 {
715 out << "<-";
717 }
718 out << std::endl;
719}
720
721void
722Node::OutputDot(std::ostream & out, size_t indent) const
723{
724 withIndent(out, indent) << GetFullId() << " [";
725 out << "label=";
726 if (HasLabel())
727 {
729 }
730 else
731 {
733 }
734 out << " ";
736 out << "];" << std::endl;
737}
738
739void
740Node::outputJson(std::ostream & out, size_t indent) const
741{
742 bool firstField = true;
743 outputJsonObjectOpening(out, indent, firstField);
744 outputJsonObjectClosing(out, indent, firstField);
745}
746
747void
748Node::OutputSubgraphs(std::ostream & out, OutputFormat format, size_t indent) const
749{
750 // Regular nodes do not have sub graphs
751}
752
754 : Node_(node)
755{}
756
757const char *
759{
760 return "in";
761}
762
763Node &
765{
766 return Node_;
767}
768
769bool
771{
772 return false;
773}
774
775void
776InputPort::SetFillColor(std::string color)
777{
778 // Attribute on the <TD> tag used by the dot output
780}
781
782void
783InputPort::OutputDotPortId(std::ostream & out) const
784{
785 out << Node_.GetFullId() << ":" << GetFullId() << ":n";
786}
787
788void
789InputPort::outputJson(std::ostream & out, size_t indent) const
790{
791 bool firstField = true;
792 outputJsonObjectOpening(out, indent, firstField);
793 outputJsonObjectClosing(out, indent, firstField);
794}
795
797 : Node_(node)
798{}
799
800const char *
802{
803 return "out";
804}
805
806Node &
808{
809 return Node_;
810}
811
812bool
814{
815 return false;
816}
817
818void
819OutputPort::SetFillColor(std::string color)
820{
821 // Attribute on the <TD> tag used by the dot output
823}
824
825void
826OutputPort::OutputDotPortId(std::ostream & out) const
827{
828 out << Node_.GetFullId() << ":" << GetFullId() << ":s";
829}
830
831void
832OutputPort::outputJson(std::ostream & out, size_t indent) const
833{
834 bool firstField = true;
835 outputJsonObjectOpening(out, indent, firstField);
836 outputJsonObjectClosing(out, indent, firstField);
837}
838
839InOutNode::InOutNode(Graph & graph, size_t inputPorts, size_t outputPorts)
840 : Node(graph)
841{
842 for (size_t i = 0; i < inputPorts; i++)
844
845 for (size_t i = 0; i < outputPorts; i++)
847}
848
849void
851{
852 throw Error("InOutNodes can not have custom shapes set");
853}
854
855InputPort &
857{
858 auto inputPort = new InputPort(*this);
859 InputPorts_.emplace_back(inputPort);
860 return *inputPort;
861}
862
863size_t
865{
866 return InputPorts_.size();
867}
868
869InputPort &
871{
872 JLM_ASSERT(index < InputPorts_.size());
873 return *InputPorts_[index];
874}
875
878{
879 auto outputPort = new OutputPort(*this);
880 OutputPorts_.emplace_back(outputPort);
881 return *outputPort;
882}
883
884size_t
886{
887 return OutputPorts_.size();
888}
889
892{
893 JLM_ASSERT(index < OutputPorts_.size());
894 return *OutputPorts_[index];
895}
896
897Graph &
899{
900 auto & graph = GetGraph().GetWriter().CreateSubGraph(*this);
901 SubGraphs_.push_back(&graph);
902 return graph;
903}
904
905size_t
907{
908 return SubGraphs_.size();
909}
910
911Graph &
913{
914 JLM_ASSERT(index < SubGraphs_.size());
915 return *SubGraphs_[index];
916}
917
918void
919InOutNode::SetHtmlTableAttribute(std::string name, std::string value)
920{
921 HtmlTableAttributes_[name] = std::move(value);
922}
923
924void
925InOutNode::SetFillColor(std::string color)
926{
928}
929
930void
932{
934
935 for (auto & inputPort : InputPorts_)
936 inputPort->Finalize();
937 for (auto & outputPort : OutputPorts_)
938 outputPort->Finalize();
939 for (auto & graph : SubGraphs_)
940 graph->Finalize();
941}
942
943void
944InOutNode::OutputSubgraphs(std::ostream & out, OutputFormat format, size_t indent) const
945{
946 // Only the ASCII format prints subgraphs inside the nodes themselves
948
949 for (auto & graph : SubGraphs_)
950 graph->Output(out, format, indent);
951}
952
953void
954InOutNode::OutputASCII(std::ostream & out, size_t indent) const
955{
956 withIndent(out, indent);
957
958 // output the names of all output ports
959 for (size_t i = 0; i < NumOutputPorts(); i++)
960 {
961 if (i != 0)
962 out << ", ";
963 out << OutputPorts_[i]->GetFullId();
964 }
965 if (NumOutputPorts() != 0)
966 out << " := ";
967
968 // If the node itself is used as a tail port, we must include its name
970 {
971 out << GetFullId() << ":";
972 }
973 printIdentifierSafe(out, GetLabelOr("NODE"));
975 {
976 out << "<-";
978 }
979 out << " ";
980
981 // Now output the origins of all input ports
982 for (size_t i = 0; i < NumInputPorts(); i++)
983 {
984 if (i != 0)
985 out << ", ";
986 InputPorts_[i]->OutputIncomingEdgesASCII(out);
987 }
988 out << std::endl;
989
990 // Output all sub graphs, if we have any
991 OutputSubgraphs(out, OutputFormat::ASCII, indent + 1);
992}
993
994void
995InOutNode::OutputDot(std::ostream & out, size_t indent) const
996{
997 withIndent(out, indent) << GetFullId() << " [shape=plain style=solid ";
998 out << "label=<" << std::endl;
999
1000 // InOutNodes are printed as html tables
1001 out << "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">" << std::endl;
1002
1003 // Used to create rows of boxes above and below the node
1004 auto PrintPortList = [&out](auto & ports)
1005 {
1006 if (ports.empty())
1007 return;
1008
1009 out << "\t<TR><TD>" << std::endl;
1010 out << "\t\t<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\"><TR>" << std::endl;
1011 out << "\t\t\t<TD WIDTH=\"20\"></TD>" << std::endl;
1012 for (size_t i = 0; i < ports.size(); i++)
1013 {
1014 // Spacing
1015 if (i != 0)
1016 out << "\t\t\t<TD WIDTH=\"10\"></TD>" << std::endl;
1017
1018 auto & port = *ports[i];
1019 out << "\t\t\t<TD BORDER=\"1\" CELLPADDING=\"1\" ";
1020 out << "PORT=\"" << port.GetFullId() << "\" ";
1021 port.OutputAttributes(out, AttributeOutputFormat::HTMLAttributes);
1022 // Unless a different color is specified, fill the port cell with white
1023 if (!port.HasAttribute(DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE))
1024 {
1025 out << DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE << "=\"white\" ";
1026 }
1027 if (port.HasLabel())
1028 {
1029 out << "><FONT POINT-SIZE=\"10\">";
1030 printStringAsHtmlText(out, port.GetLabel(), true);
1031 out << "</FONT>";
1032 }
1033 else
1034 {
1035 // ports without labels have a fixed size
1036 out << " WIDTH=\"8\" HEIGHT=\"5\" FIXEDSIZE=\"true\">";
1037 }
1038 out << "</TD>" << std::endl;
1039 }
1040 out << "\t\t\t<TD WIDTH=\"20\"></TD>" << std::endl;
1041 out << "\t\t</TR></TABLE>" << std::endl;
1042 out << "\t</TD></TR>" << std::endl;
1043 };
1044
1045 // Inputs
1046 PrintPortList(InputPorts_);
1047
1048 // The main body of the node: a rounded rectangle
1049 out << "\t<TR><TD>" << std::endl;
1050 out << "\t\t<TABLE BORDER=\"1\" STYLE=\"ROUNDED\" CELLBORDER=\"0\" ";
1051 out << "CELLSPACING=\"0\" CELLPADDING=\"0\" ";
1052 for (auto & [name, value] : HtmlTableAttributes_)
1053 {
1055 out << "=\"";
1056 printStringAsHtmlText(out, value, false);
1057 out << "\" ";
1058 }
1059 // Unless a different color is specified, fill the table cell with white
1061 {
1062 out << DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE << "=\"white\" ";
1063 }
1064 out << ">" << std::endl;
1065 out << "\t\t\t<TR><TD CELLPADDING=\"1\">";
1067 out << "</TD></TR>" << std::endl;
1068
1069 // Subgraphs
1070 if (!SubGraphs_.empty())
1071 {
1072 out << "\t\t\t<TR><TD>" << std::endl;
1073 out << "\t\t\t\t<TABLE BORDER=\"0\" CELLSPACING=\"4\" CELLPADDING=\"2\"><TR>" << std::endl;
1074 for (auto & graph : SubGraphs_)
1075 {
1076 out << "\t\t\t\t\t<TD BORDER=\"1\" STYLE=\"ROUNDED\" WIDTH=\"40\" BGCOLOR=\"white\" ";
1077 out << "_SUBGRAPH=\"" << graph->GetFullId() << "\">";
1078 printStringAsHtmlText(out, graph->GetFullId(), true);
1079 out << "</TD>" << std::endl;
1080 }
1081 out << "\t\t\t\t</TR></TABLE>" << std::endl;
1082 out << "\t\t\t</TD></TR>" << std::endl;
1083 }
1084
1085 // End of the rounded rectangle
1086 out << "\t\t</TABLE>" << std::endl;
1087 out << "\t</TD></TR>" << std::endl;
1088
1089 PrintPortList(OutputPorts_);
1090
1091 out << "</TABLE>" << std::endl;
1092 withIndent(out, indent) << "> ";
1094 out << "];" << std::endl;
1095}
1096
1097void
1098InOutNode::outputJson(std::ostream & out, size_t indent) const
1099{
1100 bool firstField = true;
1101 outputJsonObjectOpening(out, indent, firstField);
1102 indent++;
1103
1104 printNextJsonField(out, JSON_NODE_TYPE_FIELD, indent, firstField) << "\"inout\"";
1105
1106 // Input ports
1107 if (NumInputPorts())
1108 {
1109 printNextJsonField(out, JSON_IN_PORTS_FIELD, indent, firstField);
1110 printJsonElementMap(out, indent + 1, InputPorts_);
1111 }
1112
1113 // Output ports
1114 if (NumOutputPorts())
1115 {
1116 printNextJsonField(out, JSON_OUT_PORTS_FIELD, indent, firstField);
1117 printJsonElementMap(out, indent + 1, OutputPorts_);
1118 }
1119
1120 // Subgraphs
1121 if (NumSubgraphs())
1122 {
1123 printNextJsonField(out, JSON_SUBGRAPHS_FIELD, indent, firstField) << "[";
1124 bool first = true;
1125 for (const auto & subgraph : SubGraphs_)
1126 {
1127 if (first)
1128 first = false;
1129 else
1130 out << ", ";
1131 out << '"';
1132 subgraph->PrintFullId(out);
1133 out << '"';
1134 }
1135 out << "]";
1136 }
1137
1138 // HTML Table attributes
1139 if (!HtmlTableAttributes_.empty())
1140 {
1141 printNextJsonField(out, JSON_HTML_TABLE_ATTRIBUTES_FIELD, indent, firstField) << "{";
1142 bool first = true;
1143 for (const auto & [key, value] : HtmlTableAttributes_)
1144 {
1145 if (first)
1146 first = false;
1147 else
1148 out << ", ";
1150 }
1151 out << "}";
1152 }
1153
1154 indent--;
1155 outputJsonObjectClosing(out, indent, firstField);
1156}
1157
1159 : Node(graph),
1160 OutsideSource_(nullptr)
1161{}
1162
1163const char *
1165{
1166 return "arg";
1167}
1168
1169bool
1171{
1172 return false;
1173}
1174
1175void
1177{
1178 OutsideSource_ = &outsideSource;
1179 SetAttributeGraphElement("outsideSource", outsideSource);
1180}
1181
1182void
1183ArgumentNode::OutputASCII(std::ostream & out, size_t) const
1184{
1185 // In ASCII the argument is printed as part of an ARG line
1186 out << GetFullId();
1187 if (HasLabel())
1188 {
1189 out << ":";
1191 }
1192 if (OutsideSource_ != nullptr)
1193 {
1194 out << " <= ";
1196 }
1197}
1198
1200 : Node(graph),
1201 OutsideDestination_(nullptr)
1202{}
1203
1204const char *
1206{
1207 return "res";
1208}
1209
1210bool
1212{
1213 return false;
1214}
1215
1216void
1217ResultNode::SetOutsideDestination(const Port & outsideDestination)
1218{
1219 OutsideDestination_ = &outsideDestination;
1220 SetAttributeGraphElement("outsideDest", outsideDestination);
1221}
1222
1223void
1224ResultNode::OutputASCII(std::ostream & out, size_t) const
1225{
1226 // In ASCII the result is printed as part of an RES line
1228 if (HasLabel())
1229 {
1230 out << ":";
1232 }
1233 if (OutsideDestination_ != nullptr)
1234 out << " => " << OutsideDestination_->GetFullId();
1235}
1236
1237Edge::Edge(Port & from, Port & to, bool directed)
1238 : From_(from),
1239 To_(to),
1240 Directed_(directed)
1241{
1242 from.OnEdgeAdded(*this);
1243 to.OnEdgeAdded(*this);
1244}
1245
1246const char *
1248{
1249 return "edge";
1250}
1251
1252Graph &
1254{
1255 // from and to have the same graph, return either
1256 return From_.GetGraph();
1257}
1258
1259Port &
1261{
1262 return From_;
1263}
1264
1265Port &
1267{
1268 return To_;
1269}
1270
1271bool
1273{
1274 return Directed_;
1275}
1276
1277Port &
1279{
1280 if (&end == &From_)
1281 return To_;
1282 else if (&end == &To_)
1283 return From_;
1284
1285 JLM_UNREACHABLE("GetOtherEnd called with neither end");
1286}
1287
1288void
1289Edge::SetStyle(std::string style)
1290{
1291 SetAttribute("style", std::move(style));
1292}
1293
1294void
1295Edge::SetArrowHead(std::string arrow)
1296{
1297 SetAttribute("arrowhead", std::move(arrow));
1298}
1299
1300void
1301Edge::SetArrowTail(std::string arrow)
1302{
1303 // When outputting dot, the "dir" attribute will be automatically changed to make the tail visible
1304 SetAttribute("arrowtail", std::move(arrow));
1305}
1306
1307std::string_view
1309{
1310 const bool hasHeadArrow = HasAttribute("arrowhead") || Directed_;
1311 const bool hasTailArrow = HasAttribute("arrowtail");
1312 if (hasHeadArrow && hasTailArrow)
1313 return "both";
1314 else if (hasHeadArrow)
1315 return "forward";
1316 else if (hasTailArrow)
1317 return "back";
1318 else
1319 return "none";
1320}
1321
1322void
1323Edge::OutputDot(std::ostream & out, size_t indent) const
1324{
1325 withIndent(out, indent);
1327 out << " -> ";
1328 To_.OutputDotPortId(out);
1329 out << "[";
1330
1331 out << "dir=" << getDirection() << " ";
1332
1333 if (HasLabel())
1334 {
1335 out << "label=";
1337 out << " ";
1338 }
1339
1340 // Edges are not normally named, so use the id attribute to include the edge's id
1342 {
1343 out << DOT_EDGE_ID_ATTRIBUTE << "=";
1345 out << " ";
1346 }
1347
1349 out << "];" << std::endl;
1350}
1351
1352void
1353Edge::outputJson(std::ostream & out, size_t indent) const
1354{
1355 bool firstField = true;
1356 outputJsonObjectOpening(out, indent, firstField);
1357 indent++;
1358
1359 printNextJsonField(out, "from", indent, firstField);
1360 out << '"';
1362 out << '"';
1363
1364 printNextJsonField(out, "to", indent, firstField);
1365 out << '"';
1366 To_.OutputDotPortId(out);
1367 out << '"';
1368
1369 // Direction
1370 printNextJsonField(out, "dir", indent, firstField);
1371 out << '"' << getDirection() << '"';
1372
1373 indent--;
1374 outputJsonObjectClosing(out, indent, firstField);
1375}
1376
1378 : GraphElement(),
1379 Writer_(writer),
1380 ParentNode_(nullptr)
1381{}
1382
1383Graph::Graph(Writer & writer, Node & parentNode)
1384 : GraphElement(),
1385 Writer_(writer),
1386 ParentNode_(&parentNode)
1387{}
1388
1389const char *
1391{
1392 return "graph";
1393}
1394
1395Graph &
1397{
1398 return *this;
1399}
1400
1401Writer &
1403{
1404 return Writer_;
1405}
1406
1407const Writer &
1409{
1410 return Writer_;
1411}
1412
1413bool
1415{
1416 return ParentNode_ != nullptr;
1417}
1418
1419Node &
1421{
1422 auto node = new Node(*this);
1423 Nodes_.emplace_back(node);
1424 return *node;
1425}
1426
1427InOutNode &
1428Graph::CreateInOutNode(size_t inputPorts, size_t outputPorts)
1429{
1430 auto node = new InOutNode(*this, inputPorts, outputPorts);
1431 Nodes_.emplace_back(node);
1432 return *node;
1433}
1434
1435size_t
1436Graph::NumNodes() const noexcept
1437{
1438 return Nodes_.size();
1439}
1440
1441Node &
1442Graph::GetNode(size_t index)
1443{
1444 JLM_ASSERT(index < NumNodes());
1445 return *Nodes_[index];
1446}
1447
1450{
1451 auto node = new ArgumentNode(*this);
1452 ArgumentNodes_.emplace_back(node);
1453 return *node;
1454}
1455
1456size_t
1458{
1459 return ArgumentNodes_.size();
1460}
1461
1462Node &
1464{
1465 JLM_ASSERT(index < NumArgumentNodes());
1466 return *ArgumentNodes_[index];
1467}
1468
1469ResultNode &
1471{
1472 auto node = new ResultNode(*this);
1473 ResultNodes_.emplace_back(node);
1474 return *node;
1475}
1476
1477size_t
1479{
1480 return ResultNodes_.size();
1481}
1482
1483Node &
1485{
1486 JLM_ASSERT(index < NumResultNodes());
1487 return *ResultNodes_[index];
1488}
1489
1490Edge &
1491Graph::CreateEdge(Port & from, Port & to, bool directed)
1492{
1493 // Edges must be between ports in this graph
1494 JLM_ASSERT(&from.GetGraph() == this);
1495 JLM_ASSERT(&to.GetGraph() == this);
1496
1497 // Edge's constructor informs the ports about the edge
1498 auto edge = new Edge(from, to, directed);
1499 Edges_.emplace_back(edge);
1500 return *edge;
1501}
1502
1503size_t
1504Graph::NumEdges() const noexcept
1505{
1506 return Edges_.size();
1507}
1508
1509Edge &
1510Graph::GetEdge(size_t index)
1511{
1512 JLM_ASSERT(index < NumEdges());
1513 return *Edges_[index];
1514}
1515
1516Edge *
1518{
1519 for (auto edge : a.GetConnections())
1520 {
1521 if (edge->IsDirected() && &edge->GetFrom() != &a)
1522 continue;
1523 if (&edge->GetOtherEnd(a) == &b)
1524 return edge;
1525 }
1526 return nullptr;
1527}
1528
1531{
1532 if (auto it = ProgramObjectMapping_.find(object); it != ProgramObjectMapping_.end())
1533 return it->second;
1534 return nullptr;
1535}
1536
1537void
1539{
1540 JLM_ASSERT(&element.GetGraph() == this);
1541
1542 uintptr_t object = element.GetProgramObject();
1543 JLM_ASSERT(object != 0);
1544
1545 auto & slot = ProgramObjectMapping_[object];
1546 JLM_ASSERT(slot == nullptr && "Trying to map a GraphElement to an already mapped program object");
1547 slot = &element;
1548}
1549
1550void
1552{
1553 size_t erased = ProgramObjectMapping_.erase(object);
1554 JLM_ASSERT(erased == 1);
1555}
1556
1557void
1559{
1561
1562 for (auto & arg : ArgumentNodes_)
1563 arg->Finalize();
1564 // Nodes with sub graphs also finalize them
1565 for (auto & node : Nodes_)
1566 node->Finalize();
1567 for (auto & res : ResultNodes_)
1568 res->Finalize();
1569 for (auto & edge : Edges_)
1570 edge->Finalize();
1571}
1572
1573void
1574Graph::OutputASCII(std::ostream & out, size_t indent) const
1575{
1576 withIndent(out, indent) << "{" << std::endl;
1577 indent++;
1578
1579 // Use a single ARG line for all graph arguments
1580 bool anyArguments = false;
1581 for (auto & arg : ArgumentNodes_)
1582 {
1583 if (!anyArguments)
1584 withIndent(out, indent) << "ARG ";
1585 else
1586 out << ", ";
1587 anyArguments = true;
1588 arg->OutputASCII(out, indent);
1589 }
1590 if (anyArguments)
1591 out << std::endl;
1592
1593 // Print all other nodes in order
1594 for (auto & node : Nodes_)
1595 {
1596 // Will also print sub graphs recursively
1597 node->OutputASCII(out, indent);
1598 }
1599
1600 // Use a single RES line for all graph results
1601 bool anyResults = false;
1602 for (auto & res : ResultNodes_)
1603 {
1604 if (!anyResults)
1605 withIndent(out, indent) << "RES ";
1606 else
1607 out << ", ";
1608 anyResults = true;
1609 res->OutputASCII(out, indent);
1610 }
1611 if (anyResults)
1612 out << std::endl;
1613
1614 indent--;
1615 withIndent(out, indent) << "}" << std::endl;
1616}
1617
1618void
1619Graph::OutputDot(std::ostream & out, size_t indent) const
1620{
1621 withIndent(out, indent) << "digraph " << GetFullId() << " {" << std::endl;
1622 indent++;
1623
1624 // Default node attributes. Filling nodes by default makes them easier to click
1625 withIndent(out, indent)
1626 << "node[shape=box style=filled fillcolor=white width=0.1 height=0.1 margin=0.05];"
1627 << std::endl;
1628 withIndent(out, indent) << "penwidth=6;" << std::endl;
1629 if (HasLabel())
1630 {
1631 withIndent(out, indent) << "label=";
1633 out << std::endl;
1634 }
1635 withIndent(out, indent);
1637 out << std::endl;
1638
1639 // Helper function used to print argument nodes and result nodes
1640 auto PrintOrderedSubgraph = [&out](auto & nodes, const char * rank, size_t indent)
1641 {
1642 if (nodes.empty())
1643 return;
1644 withIndent(out, indent++) << "{" << std::endl;
1645 withIndent(out, indent) << "rank=" << rank << ";" << std::endl;
1646 for (size_t i = 0; i < nodes.size(); i++)
1647 {
1648 nodes[i]->OutputDot(out, indent);
1649
1650 // Use invisible edges to order nodes in the subgraph
1651 if (i != 0)
1652 withIndent(out, indent) << nodes[i - 1]->GetFullId() << " -> " << nodes[i]->GetFullId()
1653 << "[style=invis];" << std::endl;
1654 }
1655 withIndent(out, --indent) << "}" << std::endl;
1656 };
1657
1658 PrintOrderedSubgraph(ArgumentNodes_, "source", indent);
1659
1660 for (auto & node : Nodes_)
1661 {
1662 node->OutputDot(out, indent);
1663 }
1664
1665 PrintOrderedSubgraph(ResultNodes_, "sink", indent);
1666
1667 for (auto & edge : Edges_)
1668 {
1669 edge->OutputDot(out, indent);
1670 }
1671
1672 indent--;
1673 withIndent(out, indent) << "}" << std::endl;
1674}
1675
1676void
1677Graph::outputJson(std::ostream & out, size_t indent) const
1678{
1679 bool firstField = true;
1680 outputJsonObjectOpening(out, indent, firstField);
1681 indent++;
1682
1683 // If we are a subgraph, list both the node and its parent graph
1684 if (IsSubgraph())
1685 {
1686 printNextJsonField(out, JSON_PARENT_NODE_FIELD, indent, firstField);
1687 out << '"';
1689 out << '"';
1690
1691 printNextJsonField(out, JSON_PARENT_GRAPH_FIELD, indent, firstField);
1692 out << '"';
1694 out << '"';
1695 }
1696
1697 // Arguments
1698 if (!ArgumentNodes_.empty())
1699 {
1700 printNextJsonField(out, JSON_ARGUMENTS_FIELD, indent, firstField);
1701 printJsonElementMap(out, indent, ArgumentNodes_);
1702 }
1703
1704 // Nodes
1705 if (!Nodes_.empty())
1706 {
1707 printNextJsonField(out, JSON_NODES_FIELD, indent, firstField);
1708 printJsonElementMap(out, indent, Nodes_);
1709 }
1710
1711 // Results
1712 if (!ResultNodes_.empty())
1713 {
1714 printNextJsonField(out, JSON_RESULTS_FIELD, indent, firstField);
1715 printJsonElementMap(out, indent, ResultNodes_);
1716 }
1717
1718 // Edges
1719 if (!Edges_.empty())
1720 {
1721 printNextJsonField(out, JSON_EDGES_FIELD, indent, firstField);
1722 printJsonElementMap(out, indent, Edges_);
1723 }
1724
1725 indent--;
1726 outputJsonObjectClosing(out, indent, firstField);
1727}
1728
1729void
1730Graph::Output(std::ostream & out, OutputFormat format, size_t indent) const
1731{
1733
1734 switch (format)
1735 {
1737 OutputASCII(out, indent);
1738 break;
1739 case OutputFormat::Dot:
1740 OutputDot(out, indent);
1741 break;
1742 case OutputFormat::Json:
1743 outputJson(out, indent);
1744 break;
1745 default:
1746 JLM_UNREACHABLE("Unknown output format");
1747 }
1748}
1749
1750Graph &
1752{
1753 auto graph = new Graph(*this);
1754 Graphs_.emplace_back(graph);
1755 return *graph;
1756}
1757
1758size_t
1759Writer::NumGraphs() const noexcept
1760{
1761 return Graphs_.size();
1762}
1763
1764Graph &
1765Writer::GetGraph(size_t index)
1766{
1767 JLM_ASSERT(index < NumGraphs());
1768 return *Graphs_[index];
1769}
1770
1771Graph &
1773{
1774 auto graph = new Graph(*this, parentNode);
1775 Graphs_.emplace_back(graph);
1776 return *graph;
1777}
1778
1781{
1782 for (auto & graph : Graphs_)
1783 if (auto found = graph->GetElementFromProgramObject(object))
1784 return found;
1785
1786 return nullptr;
1787}
1788
1789size_t
1791{
1792 size_t & nextValue = NextUniqueIdStubSuffix_[idStub];
1793 return nextValue++;
1794}
1795
1796void
1798{
1799 for (auto & graph : Graphs_)
1800 if (!graph->IsSubgraph())
1801 graph->Finalize();
1802}
1803
1804void
1805Writer::outputAllGraphs(std::ostream & out, OutputFormat format)
1806{
1807 Finalize();
1808
1809 switch (format)
1810 {
1812 for (auto & graph : Graphs_)
1813 {
1814 // In ASCII printing, subgraphs are printed inside their nodes,
1815 // so we only output root graphs in this loop
1816 if (!graph->IsSubgraph())
1817 graph->Output(out, format, 0);
1818 }
1819 break;
1820
1821 case OutputFormat::Dot:
1822 for (auto & graph : Graphs_)
1823 {
1824 graph->Output(out, format, 0);
1825 }
1826 break;
1827
1828 case OutputFormat::Json:
1829 {
1830 out << "{" << std::endl;
1831 bool first = true;
1832 for (auto & graph : Graphs_)
1833 {
1834 if (first)
1835 first = false;
1836 else
1837 out << "," << std::endl;
1838 graph->Output(out, format, 1);
1839 }
1840 out << std::endl << "}" << std::endl;
1841 break;
1842 }
1843
1844 default:
1845 JLM_UNREACHABLE("Unknown OutputFormat");
1846 }
1847}
1848}
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
const char * GetIdPrefix() const override
void RemoveProgramObjectMapping(uintptr_t object)
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_
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)
#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