Yume
dot_visitor.cpp
Go to the documentation of this file.
1#include "dot_visitor.hpp"
2#include "ast/ast.hpp"
3#include "token.hpp"
4#include "util.hpp"
5#include "llvm/Support/raw_ostream.h"
6#include <algorithm>
7#include <memory>
8#include <string_view>
9
10namespace yume::diagnostic {
11static auto xml_escape(std::string_view data) -> std::string {
12 std::string str{};
13 llvm::raw_string_ostream stream(str);
14
15 for (char i : data) {
16 switch (i) {
17 case '&': stream << "&amp;"; break;
18 case '\'': stream << "&apos;"; break;
19 case '\"': stream << "&quot;"; break;
20 case '<': stream << "&lt;"; break;
21 case '>': stream << "&gt;"; break;
22 case '\0': stream << "\\\\0"; break;
23 default: stream << i; break;
24 }
25 }
26
27 return str;
28}
29
30inline static auto opt_str(const char* ptr) -> optional<string> {
31 if (ptr == nullptr)
32 return {};
33 return string(ptr);
34}
35
36auto DotVisitor::add_node(const string& content, const char* label) -> DotNode& {
37 return add_node(DotNode(m_index, std::nullopt, std::nullopt, content), label);
38}
39
40auto DotVisitor::add_node(DotNode&& node, const char* label) -> DotNode& {
41 m_index++;
42 if (m_parent == nullptr) {
43 m_root = std::make_unique<DotNode>(node);
44 return *m_root;
45 }
46 return m_parent->children.emplace_back(opt_str(label), node).child;
47}
48
49void DotVisitor::DotNode::write(llvm::raw_ostream& stream) const {
50 stream << AST_KEY << index << " [label=<";
51 if (simple()) {
52 stream << "<I>" << content << "</I>";
53 } else {
54 stream << "<FONT POINT-SIZE=\"9\">" << location->to_string() << "</FONT><BR/>";
55 if (type.has_value())
56 stream << "<U>" << *type << "</U><BR/>";
57 stream << "<B>" << content << "</B>";
58 }
59
60 bool skip_first_child = false;
61 if (!children.empty()) {
62 const auto& first = children.at(0);
63 if (!first.line_label.has_value() && first.child.simple()) {
64 skip_first_child = true;
65 stream << "<BR/><I>" << first.child.content << "</I>";
66 }
67 }
68
69 stream << ">];\n";
70
71 for (const auto& [line_label, child] : children) {
72 if (skip_first_child) {
73 skip_first_child = false;
74 continue;
75 }
76 child.write(stream);
77 stream << AST_KEY << index << " -> " << AST_KEY << child.index;
78 if (line_label.has_value())
79 stream << " [label=\"" << *line_label << "\"]";
80 stream << ";\n";
81 }
82}
83
84auto DotVisitor::visit(const ast::AST& expr, string_view label) -> DotVisitor& {
85 Loc location = expr.location();
86 optional<string> type = {};
87 if (auto val_ty = expr.val_ty(); val_ty)
88 type = xml_escape(val_ty->name());
89 auto kind_label = xml_escape(expr.kind_name());
90
91 auto& node = add_node(DotNode(m_index, location, type, kind_label), label.data());
92
93 auto* restore_parent = std::exchange(m_parent, &node);
94 expr.visit(*this);
95 m_parent = restore_parent;
96
97 // This node had no parent, which means it's a root node.
98 if (restore_parent == nullptr) {
99 node.write(m_stream);
100 m_root.reset();
101 }
102
103 return *this;
104}
105
106auto DotVisitor::visit(const string& str, string_view label) -> DotVisitor& {
107 add_node(xml_escape(str), label.data());
108
109 return *this;
110}
111
112auto DotVisitor::visit(std::nullptr_t, string_view label) -> DotVisitor& {
113 add_node("<FONT COLOR=\"RED\">NULL</FONT>", label.data());
114
115 return *this;
116}
117} // namespace yume::diagnostic
All nodes in the AST tree of the program inherit from this class.
Definition: ast.hpp:224
auto visit(const ast::AST &expr, string_view label) -> DotVisitor &override
Definition: dot_visitor.cpp:84
static auto xml_escape(std::string_view data) -> std::string
Definition: dot_visitor.cpp:11
static auto opt_str(const char *ptr) -> optional< string >
Definition: dot_visitor.cpp:30
Represents a location in source code, as a range starting at a line and column and ending at some oth...
Definition: token.hpp:26