2023-03-10 10:30:54 -05:00
|
|
|
# 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,
|
|
|
|
# }
|
|
|
|
# }
|
|
|
|
#
|
tamer: asg::air::object::tpl: Reject multi-expression shape
This enforces the new constraint that templates expanding into an `Expr`
context must only inline a single `Expr`.
Perhaps in the future we'll support explicit splicing, like `,@` in
Lisp. But this new restriction is intended for two purposes:
- To make templates more predictable (if you have a list of expressions
inlined then they will act differently depending on the type of
expression that they are inlined into, which means that more defensive
programming would otherwise be required); and
- To make expansion easier, since we're going to have to set aside an
expansion workspace ahead of time to ensure ordering (Petgraph can't
replace edges in-place). If we support multi-expansion, we'd have to
handle associativity in all expression contexts.
This'll become more clear in future commits.
It's nice to see all this hard work coming together now, though; it's easy
now to perform static analysis on the system, and any part of the graph
construction can throw errors with rich diagnostic information and still
recover properly. And, importantly, the system enforces its own state, and
the compiler helps us with that (the previous commits).
DEV-13163
2023-07-26 03:43:24 -04:00
|
|
|
# But edges also support arbitrary code definitions:
|
|
|
|
#
|
|
|
|
# object_rel! {
|
|
|
|
# Source -> {
|
|
|
|
# tree TargetA {
|
|
|
|
# fn pre_add_edge(...) {
|
|
|
|
# // ...
|
|
|
|
# }
|
|
|
|
# },
|
|
|
|
# tree TargetB,
|
|
|
|
# cross TargetC,
|
|
|
|
# }
|
|
|
|
# }
|
|
|
|
#
|
|
|
|
# And because of that,
|
|
|
|
# this script hard-codes the expected level of nesting just to make life
|
|
|
|
# easier.
|
|
|
|
#
|
2023-03-10 10:30:54 -05:00
|
|
|
# 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
|
|
|
|
}
|
|
|
|
|
|
|
|
# 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.
|
tamer: asg::air::object::tpl: Reject multi-expression shape
This enforces the new constraint that templates expanding into an `Expr`
context must only inline a single `Expr`.
Perhaps in the future we'll support explicit splicing, like `,@` in
Lisp. But this new restriction is intended for two purposes:
- To make templates more predictable (if you have a list of expressions
inlined then they will act differently depending on the type of
expression that they are inlined into, which means that more defensive
programming would otherwise be required); and
- To make expansion easier, since we're going to have to set aside an
expansion workspace ahead of time to ensure ordering (Petgraph can't
replace edges in-place). If we support multi-expansion, we'd have to
handle associativity in all expression contexts.
This'll become more clear in future commits.
It's nice to see all this hard work coming together now, though; it's easy
now to perform static analysis on the system, and any part of the graph
construction can throw errors with rich diagnostic information and still
recover properly. And, importantly, the system enforces its own state, and
the compiler helps us with that (the previous commits).
DEV-13163
2023-07-26 03:43:24 -04:00
|
|
|
# We hard-code the expected depth to simplify parsing.
|
|
|
|
in_block && /^ \w+ -> \{$/ {
|
2023-03-10 10:30:54 -05:00
|
|
|
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.
|
tamer: asg::air::object::tpl: Reject multi-expression shape
This enforces the new constraint that templates expanding into an `Expr`
context must only inline a single `Expr`.
Perhaps in the future we'll support explicit splicing, like `,@` in
Lisp. But this new restriction is intended for two purposes:
- To make templates more predictable (if you have a list of expressions
inlined then they will act differently depending on the type of
expression that they are inlined into, which means that more defensive
programming would otherwise be required); and
- To make expansion easier, since we're going to have to set aside an
expansion workspace ahead of time to ensure ordering (Petgraph can't
replace edges in-place). If we support multi-expansion, we'd have to
handle associativity in all expression contexts.
This'll become more clear in future commits.
It's nice to see all this hard work coming together now, though; it's easy
now to perform static analysis on the system, and any part of the graph
construction can throw errors with rich diagnostic information and still
recover properly. And, importantly, the system enforces its own state, and
the compiler helps us with that (the previous commits).
DEV-13163
2023-07-26 03:43:24 -04:00
|
|
|
block_src && /^ }$/ {
|
2023-03-10 10:30:54 -05:00
|
|
|
block_src = ""
|
|
|
|
print ""
|
|
|
|
}
|
|
|
|
|
2023-04-10 12:16:21 -04:00
|
|
|
# "// empty" means that the lack of edges is intentional.
|
|
|
|
block_src && /^ *\/\/ empty$/ {
|
|
|
|
# Suppress error from the edge check below.
|
|
|
|
found_rels++
|
|
|
|
}
|
|
|
|
|
|
|
|
# Skip comments.
|
|
|
|
/^ *\/+/ { next }
|
|
|
|
|
2023-03-10 10:30:54 -05:00
|
|
|
# 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.
|
tamer: asg::air::object::tpl: Reject multi-expression shape
This enforces the new constraint that templates expanding into an `Expr`
context must only inline a single `Expr`.
Perhaps in the future we'll support explicit splicing, like `,@` in
Lisp. But this new restriction is intended for two purposes:
- To make templates more predictable (if you have a list of expressions
inlined then they will act differently depending on the type of
expression that they are inlined into, which means that more defensive
programming would otherwise be required); and
- To make expansion easier, since we're going to have to set aside an
expansion workspace ahead of time to ensure ordering (Petgraph can't
replace edges in-place). If we support multi-expansion, we'd have to
handle associativity in all expression contexts.
This'll become more clear in future commits.
It's nice to see all this hard work coming together now, though; it's easy
now to perform static analysis on the system, and any part of the graph
construction can throw errors with rich diagnostic information and still
recover properly. And, importantly, the system enforces its own state, and
the compiler helps us with that (the previous commits).
DEV-13163
2023-07-26 03:43:24 -04:00
|
|
|
block_src && /^ \w+ \w+(,| \{)$/ {
|
|
|
|
# Clean up the end of the string _before_ pulling information out of
|
|
|
|
# fields,
|
|
|
|
# since the number of fields can vary.
|
|
|
|
gsub(/(,| \{)$/, "")
|
|
|
|
|
2023-03-10 10:30:54 -05:00
|
|
|
# Edge type (cross, tree)
|
|
|
|
ty = $(NF-1)
|
|
|
|
|
2023-03-17 12:53:53 -04:00
|
|
|
# Dashed is visually in-between solid and dotted,
|
|
|
|
# and `dyn` is either `tree` or `cross`,
|
|
|
|
# determined at runtime.
|
|
|
|
# But we may need some other representation if `dotted` is too visually
|
|
|
|
# sparse and difficult/annoying to see;
|
|
|
|
# let's see where the ASG visualization ends up first.
|
2023-03-10 10:30:54 -05:00
|
|
|
attrs = ""
|
2023-03-17 12:53:53 -04:00
|
|
|
switch (ty) {
|
|
|
|
case "tree":
|
2023-03-17 13:37:36 -04:00
|
|
|
attrs="[style=solid,arrowhead=normal]";
|
2023-03-17 12:53:53 -04:00
|
|
|
break;
|
|
|
|
case "cross":
|
2023-03-17 13:37:36 -04:00
|
|
|
attrs="[style=dotted,arrowhead=open]";
|
2023-03-17 12:53:53 -04:00
|
|
|
break;
|
|
|
|
case "dyn":
|
2023-03-17 13:37:36 -04:00
|
|
|
attrs="[style=dashed,arrowhead=normal]";
|
2023-03-17 12:53:53 -04:00
|
|
|
break;
|
2023-03-10 10:30:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
# 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.
|
2023-03-17 13:37:36 -04:00
|
|
|
printf " %5s -> %-5s %32s; # %s edge\n", block_src, $NF, attrs, ty
|
2023-03-10 10:30:54 -05:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|