SIMPLE_DOT=$(cat <<'EOF' BEGIN { edge = "->" properties_separator = ";" open_blocks = "{" close_blocks = "}" hyperedge_property = "_he" uuid_command = "uuidgen" blocks = 0 processed_line = 0 # Print the graph header. #print "digraph {" } # Firstly, we process blocks. NF == 1 && $1 == open_blocks { # open block blocks++ } NF == 1 && $1 == "}" { # close block blocks-- } # Secondly, we process "special rules". ## Nodes. NF > 1 && $2 != edge { printf ($1 "\t") label = get_label($0) get_properties($0, properties) properties_str = generate_properties(label, properties) if (length(properties_str)) { print properties_str } processed_line = 1 } ## Edges. NF > 3 && $2 == edge { printf ($1 FS $2 FS $3) label = get_label($0) get_properties($0, properties) properties_str = generate_properties(label, properties) if (length(properties_str)) { printf "\t" print properties_str } processed_line = 1 } # Thirdly, we do general treatment for every line. { if (!processed_line) { print $0 } processed_line = 0 } END { # Print the closing curly brace. print "}" } function get_label(_line, _field, _out, _split, _len) { _field = 2 if ($2 == edge) { _field = 4 } # Special case: there isn't label, but there are properties. _len = split($(_field), _split, properties_separator) if ((_len > 1) && !_split[1]) { # There is a properties_separator in the field, and no label. return } for (_field; _field <= NF; _field++) { _len = split($(_field), _split, properties_separator) if (_len > 1) { _out = !(_out) ? _split[1] : (_out FS _split[1]) return _out } else { _out = !(_out) ? $(_field) : (_out FS $(_field)) } } return _out } function get_properties(_line, _split, _field, _len, _uuid) { delete _split _len = split(_line, _split, properties_separator) if (!_len) { return } for (_field = 2; _field <= _len; _field++) { if (_split[_field] == hyperedge_property) { uuid_command | getline _uuid _split[_field] = hyperedge_property "=\"" _uuid "\"" close(uuid_command) } _split[_field - 1] = _split[_field] } delete _split[_len] return } function generate_properties(_label, _properties, _out, _len, _type_label) { _type_label = "label" for (p in _properties) { if (match(hyperedge_property, _properties[p])) { _type_label = "xlabel" } } if (_label) { _out = (_type_label "=\"" _label "\"") } _len = length(_properties) for (i = 1; i <= _len; i++) { _out = !(_out) ? _properties[1] : (_out "," _properties[i]) } return (!(_out) ? "" : ("[" _out "]")) } EOF ) HYPERGRAPH_PROCESSOR=$(cat <<'EOF' // An attempt to reproduce what was reported in https://gitlab.com/graphviz/graphviz/-/issues/1911#note_473279838 E [ _he ] { // Creates the node representing the hyper-edge, if it already doesn't exist. node_t hyperedge = isNode($G, $._he); if (!hyperedge) { hyperedge = node($G, $._he); hyperedge.shape = "point"; hyperedge.fillcolor = "red"; if ($.label) { /* hyperedge.label = $.label; hyperedge.shape = "none"; */ hyperedge.xlabel = $.label; } } // Creates the edges to and from hyperedge, if it already doesn't exist. if (!isEdge_sg($G, $.tail, hyperedge, "")) { edge_t new_out = edge_sg($G, $.tail, hyperedge, ""); new_out.arrowhead = "none"; } if(!isEdge_sg($G, hyperedge, $.head, "")) { edge_t new_in = edge_sg($G, hyperedge, $.head, ""); } // Removes the current edge (with attribute "_he") because edges to "hyperedge" are added. delete($G, $); } EOF ) # Process the input files and produce a SVG. cat <(echo "digraph {") ~/src/scripts/concept-style.dot <(awk -f <(echo "${SIMPLE_DOT}") $*) | gvpr -c -f <(echo "${HYPERGRAPH_PROCESSOR}") | dot -Tsvg ## DEAD-CODE ## awk -f ~/tmp/graphviz/concept.awk $* | ## gvpr -c -f ~/tmp/graphviz/hypergraph.gvpr | ## xsel -ib