/* Helper code for graphviz output. Copyright (C) 2019-2025 Free Software Foundation, Inc. Contributed by David Malcolm . This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #ifndef GCC_GRAPHVIZ_H #define GCC_GRAPHVIZ_H #include "pretty-print.h" /* for ATTRIBUTE_GCC_PPDIAG. */ namespace xml { class node; } namespace dot { /* A class for writing .dot output to a pretty_printer with indentation to show nesting. */ class writer { public: writer (pretty_printer &pp); void indent () { m_indent++; } void outdent () { m_indent--; } void write_indent (); void write_character (char ch) { pp_character (&m_pp, ch); } void write_string (const char *str) { pp_string (&m_pp, str); } void write_newline () { pp_newline (&m_pp); } pretty_printer *get_pp () const { return &m_pp; } private: pretty_printer &m_pp; int m_indent; }; // An AST for the dot language // See https://graphviz.org/doc/info/lang.html // Forward decls struct id; struct node_id; struct port; struct kv_pair; struct graph; struct attr_list; struct stmt_list; struct stmt; struct node_stmt; struct attr_stmt; struct kv_stmt; struct edge_stmt; struct subgraph; // Decls struct ast_node { virtual ~ast_node () {} virtual void print (writer &w) const = 0; void dump () const; }; struct id : public ast_node { enum class kind { identifier, quoted, html }; id (std::string str); /* For HTML labels: see https://graphviz.org/doc/info/shapes.html#html */ id (const xml::node &n); void print (writer &w) const final override; static bool is_identifier_p (const char *); std::string m_str; enum kind m_kind; }; /* ID '=' ID */ struct kv_pair : ast_node { kv_pair (id key, id value) : m_key (std::move (key)), m_value (std::move (value)) { } void print (writer &w) const final override; id m_key; id m_value; }; /* attr_list: '[' [ a_list ] ']' [ attr_list ] */ struct attr_list : public ast_node { void print (writer &w) const; void add (id key, id value) { m_kvs.push_back ({std::move (key), std::move (value)}); } std::vector m_kvs; }; /* stmt_list : [ stmt [ ';' ] stmt_list ] */ struct stmt_list : public ast_node { void print (writer &w) const final override; void add_stmt (std::unique_ptr s) { m_stmts.push_back (std::move (s)); } void add_edge (node_id src_id, node_id dst_id); void add_attr (id key, id value); std::vector> m_stmts; }; /* graph : [ strict ] (graph | digraph) [ ID ] '{' stmt_list '}' */ struct graph : public ast_node { graph () : m_id (nullptr) { } graph (id id_) : m_id (std::make_unique (std::move (id_))) { } void print (writer &w) const final override; void add_stmt (std::unique_ptr s) { m_stmt_list.add_stmt (std::move (s)); } std::unique_ptr m_id; // optional stmt_list m_stmt_list; }; /* Abstract base class. stmt : node_stmt | edge_stmt | attr_stmt | ID '=' ID ("kv_stmt") | subgraph */ struct stmt { virtual ~stmt () {} virtual void print (writer &w) const = 0; }; struct stmt_with_attr_list : public stmt { void set_attr (id key, id value) { m_attrs.add (std::move (key), std::move (value)); } void set_label (dot::id label); attr_list m_attrs; }; struct node_stmt : public stmt_with_attr_list { node_stmt (id id_) : m_id (id_) { } void print (writer &w) const final override; id m_id; }; struct attr_stmt : public stmt_with_attr_list { enum class kind { graph, node, edge }; attr_stmt (enum kind kind_) : m_kind (kind_) { } void print (writer &w) const final override; enum kind m_kind; }; /* "ID '=' ID" as a stmt. */ struct kv_stmt : public stmt { kv_stmt (kv_pair kv) : m_kv (std::move (kv)) {} void print (writer &w) const final override; kv_pair m_kv; }; /* node_id : ID [ port ] */ enum class compass_pt { n, ne, e, se, s, sw, w, nw, c /* "_" clashes with intl macro */ }; /* port : ':' ID [ ':' compass_pt ] | ':' compass_pt */ struct port : public ast_node { port (id id_) : m_id (std::make_unique (std::move (id_))), m_compass_pt (nullptr) { } port (enum compass_pt compass_pt_) : m_id (nullptr), m_compass_pt (std::make_unique (compass_pt_)) { } port (id id_, enum compass_pt compass_pt_) : m_id (std::make_unique (std::move (id_))), m_compass_pt (std::make_unique (compass_pt_)) { } port (const port &other) : m_id (nullptr), m_compass_pt (nullptr) { if (other.m_id) m_id = std::make_unique (*other.m_id); if (other.m_compass_pt) m_compass_pt = std::make_unique (*other.m_compass_pt); } void print (writer &w) const final override; std::unique_ptr m_id; // would be std::optional std::unique_ptr m_compass_pt; // would be std::optional }; struct node_id : public ast_node { node_id (id id_) : m_id (id_), m_port (nullptr) { } node_id (id id_, port port_) : m_id (id_), m_port (std::make_unique (std::move (port_))) { } node_id (const node_id &other) : m_id (other.m_id), m_port (nullptr) { if (other.m_port) m_port = std::make_unique (*other.m_port); } node_id &operator= (const node_id &other) { m_id = other.m_id; if (other.m_port) m_port = std::make_unique (*other.m_port); else m_port = nullptr; return *this; } void print (writer &w) const final override; id m_id; std::unique_ptr m_port; // would be std::optional }; /* The full grammar for edge_stmt is: edge_stmt : (node_id | subgraph) edgeRHS [ attr_list ] edgeRHS : edgeop (node_id | subgraph) [ edgeRHS ] This class support the subsets where all are "node_id", rather than "subgraph", and doesn't yet support "port" giving effectively: node_id (edgeop node_id)+ [ attr_list] */ struct edge_stmt : public stmt_with_attr_list { edge_stmt (node_id src_id, node_id dst_id) { m_node_ids.push_back (std::move (src_id)); m_node_ids.push_back (std::move (dst_id)); } void print (writer &w) const final override; std::vector m_node_ids; // should have 2 or more elements }; /* [ subgraph [ ID ] ] '{' stmt_list '}' */ struct subgraph : public stmt { subgraph (id id_) : m_id (id_) { } void print (writer &w) const final override; void add_stmt (std::unique_ptr s) { m_stmt_list.add_stmt (std::move (s)); } void add_attr (id key, id value) { m_stmt_list.add_stmt (std::make_unique (kv_pair (std::move (key), std::move (value)))); } id m_id; stmt_list m_stmt_list; }; extern std::unique_ptr make_svg_from_graph (const graph &g); } // namespace dot /* A class for writing .dot output to a pretty_printer with indentation to show nesting. */ class graphviz_out : public dot::writer { public: graphviz_out (pretty_printer *pp); void print (const char *fmt, ...) ATTRIBUTE_GCC_PPDIAG(2,3); void println (const char *fmt, ...) ATTRIBUTE_GCC_PPDIAG(2,3); void begin_tr (); void end_tr (); void begin_td (); void end_td (); void begin_trtd (); void end_tdtr (); }; #endif /* GCC_GRAPHVIZ_H */