tamer: tools/asg-ontviz: ASG ontology visualization

This parses the declarative `object_rel!` definitions from the Rust sources
and produces a DOT representation of the ontology of the graph, which can
then be rendered using Graphviz.

This does not yet introduce it into the build; it ought to be run as part of
`make check` (without rendering with Graphviz) to ensure that we catch
breaking changes, and `make html` ought to integrate it into the
documentation, perhaps as part of `asg::graph` or `asg::graph::object`.

DEV-13708
main
Mike Gerwitz 2023-03-10 10:30:54 -05:00
parent 0aa69c079d
commit f733a85597
2 changed files with 170 additions and 0 deletions

View File

@ -0,0 +1,20 @@
#!/bin/bash
# Generate DOT representation of TAMER's ASG ontology
#
# See sibling `asg-ontviz.awk` for more information;
# this is just a thin wrapper around it that invokes it with a list of
# source files.
cd "$(dirname "$0")"
# We explicitly filter files in this directory to ensure that the expected
# ontological definitions (`object_rel!`) are actually present,
# because otherwise they were moved and we need to update the path.
# This is enforced by the script.
#
# So, to be clear:
# do _not_ do something like `grep -rl 'object_rel!' ../src/asg`.
find ../src/asg/graph/object/ -name '*.rs' \
-a \! -name 'rel.rs' \
-a \! -name 'test.rs' \
| xargs awk -f asg-ontviz.awk

View File

@ -0,0 +1,150 @@
# Generate ontology visualization for the ASG
#
# Copyright (C) 2014-2023 Ryan Specialty, LLC.
#
# This file is part of TAME.
#
# This program 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 of the License, or
# (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
# Use the sibling `asg-ontvis` script to invoke this for you with the
# necessary files.
#
# The relationships between objects on the ASG are defined declaratively
# using the `object_rel!` macro.
# They follow this syntax:
#
# object_rel! {
# Source -> {
# tree TargetA,
# tree TargetB,
# cross TargetC,
# }
# }
#
# This script expects to receive a list of files containing such
# definitions.
# It will output,
# to stdout,
# a DOT definition representing the ASG's ontology,
# which can be used to render a visualization.
#
# To render the output of this script,
# pipe it to e.g. `dot -Tsvg > out.svg`.
#
# This tool is supposed to generate human-readable DOT output;
# please retain that quality in any changes you make.
# This is helpful not only to help the user figure out what output they're
# seeing,
# but is also useful because the DOT output is quite understandable by
# itself without the need to generate a graphical representation,
# depending on the information the user is looking for.
BEGIN {
print "# Representation of the TAMER ASG ontology generated by asg-ontviz."
print "# Generate visualization using e.g. `dot -Tsvg > out.svg`."
print "digraph {"
# Whether we're parsing a `object_rel!` block.
in_block = 0
# The current source relation, if `in_block_subexpr`.
block_src = ""
# The final exit status (0 = success)
exit_code = 0
}
BEGINFILE {
# Whether any object relationships were found in the file.
# This is used in `ENDFILE` to notify the user that something may be
# wrong.
found_rels = 0
}
# Skip comments.
/^ *\/+/ { next }
# Predicates will be reset for each line,
# allowing the remainder of the script to be read more declaratively.
{ in_block = in_block_subexpr = 0 }
# Block predicates for each line of text.
/^object_rel! {$/, /^}$/ { in_block = 1 }
# `Foo -> {` line declares the source of the relation.
in_block && /->/ {
block_src = $1
printf " # `%s` from `%s:%d`\n", block_src, FILENAME, FNR
next
}
# A closing curly brace always means that we've finished with the current
# source relation,
# since we're at the innermost level of nesting.
block_src && /}/ {
block_src = ""
print ""
}
# For each target object,
# output a relation.
#
# The DOT format is pretty rigid;
# because we are styling,
# we cannot use curly braces similar to how the relation is defined in
# Rust using the `object_rel!` macro;
# we must independently define each one.
# But that's okay;
# the output is quite legible.
block_src && $NF ~ /\w+,$/ {
# Edge type (cross, tree)
ty = $(NF-1)
attrs = ""
if (ty == "cross") {
attrs="[style=dashed]"
}
gsub(/,$/, "")
# This may need updating over time as object names in Rust sources
# exceed the fixed-width definition here.
# This output is intended to form a table that is easy to read and
# visually scan.
# For this reason,
# the source object is right-aligned and target is left-aligned,
# so that `Src -> Target` can be easily read regardless of the width
# of the objects involved.
printf " %5s -> %-5s %14s; # %s edge\n", block_src, $NF, attrs, ty
found_rels++
}
ENDFILE {
if (found_rels == 0) {
# Note that this can happen if the macro is changed or the calling
# `asg-ontvis` shell script is not providing the correct
# filenames.
printf "error: no relations found in `%s`\n", FILENAME >"/dev/stderr"
exit_code = 1
}
}
END {
print "}"
exit exit_code
}