aboutsummaryrefslogtreecommitdiff
path: root/etc/utils/hierarchy-diagram
blob: e30bb8762c26741d25eab20464614a6b94f3190b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/bin/bash

usage(){
cat <<EOT >&2
Usage : hierarchy-diagram [OPTIONS]

Description:
    hierarchy-diagram is a small utility to draw hierarchy diagrams of
    mathematical structures.  This utility uses the coercion paths and the
    canonical projections between <module>.type types (typically in the MathComp
    library) to draw the diagram.  Indirect edges which can be composed of other
    edges by transitivity are eliminated automatically for each kind of edges.
    A diagram appears on the standard output in the DOT format which can be
    converted to several image formats by Graphviz.

Options:
    -h, -help:
        Output a usage message and exit.

    -canonicals (off|on|color):
        Configure output of edges of canonical projections.  The default value
        is "on".

    -coercions (off|on|color):
        Configure output of edges of coercions.  The default value is "off".
        The value given by this option must be different from that by
        -canonical soption.

    -R dir coqdir:
        This option is passed to coqtop: "recursively map physical dir to
        logical coqdir".

    -lib library:
        Specify a Coq library used to draw a diagram.  This option can appear
        repetitively.  If not specified, all.all will be used.
EOT
}

raw_coercions=$(tempfile -s .out | sed s/\.out$//)
raw_canonicals=$(tempfile -s .out | sed s/\.out$//)
parsed_coercions=$(tempfile)
parsed_canonicals=$(tempfile)
opt_raw_inheritances=off
opt_canonicals=on
opt_coercions=off
opt_libs=()
opt_coqtopargs=()

while [[ $# -gt 0 ]]
do

case "$1" in
  -raw-inheritances)
    opt_raw_inheritances=on
    shift;
  ;;
  -canonicals)
    opt_canonicals="$2"
    shift; shift
  ;;
  -coercions)
    opt_coercions="$2"
    shift; shift
  ;;
  -R)
    opt_coqtopargs+=("-R" "$2" "$3")
    shift; shift; shift
  ;;
  -lib)
    opt_libs+=("$2")
    shift; shift
  ;;
  -h|-help)
    usage; exit
  ;;
  *)
    echo "ERROR: invalid option -- $1" >&2
    echo >&2
    usage; exit 1
  ;;
esac

done

[[ ${#opt_libs[@]} -eq 0 ]] && opt_libs=(all.all)

coqtop -w none ${opt_coqtopargs[@]} >/dev/null 2>&1 <<EOT
Set Printing Width 4611686018427387903.
Require Import ${opt_libs[@]}.
Redirect "$raw_coercions" Print Graph.
Redirect "$raw_canonicals" Print Canonical Projections.
EOT

cat $raw_canonicals.out \
| sed -n 's/^\([a-zA-Z_\.]*\)\.sort <- \([a-zA-Z_\.]*\)\.sort ( \([a-zA-Z_\.]*\)\.\([a-zA-Z_]*\) )$/\1 \2 \3 \4/p' \
| while read -r from_module to_module proj_module projection; do
  if [[ $from_module = $proj_module ]] || [[ $to_module = $proj_module ]]; then
    echo $from_module $to_module $proj_module $projection
  fi
done > $parsed_canonicals

cat $raw_coercions.out \
| sed -n 's/^\[\([^]]*\)\] : \([a-zA-Z_\.]*\)\.type >-> \([a-zA-Z_\.]*\)\.type$/\2 \3 \1/p' > $parsed_coercions

if [[ $opt_raw_inheritances != "off" ]]; then

  cat $parsed_canonicals | sed 's/^\([^ ]*\) \([^ ]*\) .*$/\1\n\2/g' | sort | uniq \
  | while read -r module; do
    echo -n "$module "
    sed -n "s/^\([^ ]*\) $module .*$/\1/p" $parsed_canonicals | sort | xargs
  done

else

echo "digraph structures {"

if [[ $opt_canonicals != "off" ]]; then

  cat $parsed_canonicals | while read -r from_module to_module proj_module projection; do
    grep "^$from_module " $parsed_canonicals | ( while read -r _ middle_module _ _; do
      if grep -q "^$middle_module $to_module " $parsed_canonicals; then
        exit 1
      fi
    done )
    if [[ "$?" = "0" ]]; then
      echo -n "\"$to_module\" -> \"$from_module\""
      [[ $opt_canonicals == "on" ]] || echo -n "[color=$opt_canonicals]"
      echo ";"
    fi
  done

fi

if [[ $opt_coercions != "off" ]]; then

  cat $parsed_coercions | while read -r from_module to_module coercion; do
    grep "^$from_module " $parsed_coercions | ( while read -r _ middle_module _; do
      if grep -q "^$middle_module $to_module " $parsed_coercions; then
        exit 1
      fi
    done )
    if [[ "$?" = "0" ]]; then
      echo -n "\"$to_module\" -> \"$from_module\""
      [[ $opt_coercions == "on" ]] || echo -n "[color=$opt_coercions]"
      echo ";"
    fi
  done
fi

echo "}"

fi

rm $raw_coercions.out $raw_canonicals.out $parsed_coercions $parsed_canonicals