-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgraphvizfn.sml
138 lines (119 loc) · 5.22 KB
/
graphvizfn.sml
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
(* $Id$
*
* Copyright (c) 2008 Timothy Bourke (University of NSW and NICTA)
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the "BSD License" which is distributed with the
* software in the file LICENSE.
*
* 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 BSD
* License for more details.
*)
functor Graphviz (
type t
val output : TextIO.outstream * t -> unit
val warn : string list -> unit
val statusToString : OS.Process.status -> string
structure Plain : PLAIN
structure OpSys :
sig
type ('a, 'b) proc
val execute : string * string list -> ('a, 'b) proc
val textInstreamOf : (TextIO.instream, 'a) proc
-> TextIO.instream
val textOutstreamOf : ('a, TextIO.outstream) proc
-> TextIO.outstream
val reap : ('a, 'b) proc -> OS.Process.status
end
) : GRAPHVIZ
=
struct
type t = t
type plain_graph = Plain.graph
datatype output = PS | SVG
datatype graph = Dot | Neato | Fdp | Twopi | Circo
fun langToOption PS = "-Tps"
| langToOption SVG = "-Tsvg"
fun stringToGraph "dot" = SOME Dot
| stringToGraph "neato" = SOME Neato
| stringToGraph "fdp" = SOME Fdp
| stringToGraph "twopi" = SOME Twopi
| stringToGraph "circo" = SOME Circo
| stringToGraph _ = NONE
fun graphToString Dot = "dot"
| graphToString Neato = "neato"
| graphToString Fdp = "fdp"
| graphToString Twopi = "twopi"
| graphToString Circo = "circo"
fun exePath exe = let
val d = OS.Path.concat (Settings.graphvizPath (), "bin")
handle Path => OS.Path.currentArc
in OS.Path.joinDirFile {dir=d, file=Settings.adjustExe exe} end
fun graphToPath Dot = exePath "dot"
| graphToPath Neato = exePath "neato"
| graphToPath Fdp = exePath "fdp"
| graphToPath Twopi = exePath "twopi"
| graphToPath Circo = exePath "circo"
(* Relies on the fact that the graphviz utilities read an entire dot file
* on stdin before writing any output to stdout (otherwise deadlock is
* possible). *)
fun makePlain g v = let
val _ = Util.debugOutline (fn ()=>["executing ", graphToPath g,
" -Tplain"])
val proc = OpSys.execute (graphToPath g, ["-Tplain"])
val ostrm = OpSys.textOutstreamOf proc
val _ = output (ostrm, v)
val _ = TextIO.closeOut ostrm
val _ = Util.debugDetailed (fn ()=>["--output written."])
val istrm = TextIO.getInstream (OpSys.textInstreamOf proc)
(*
fun dscan istrm = let (* for debugging: *)
val r = TextIO.StreamIO.input1 istrm
val _ = case r of
NONE => TextIO.print "[DONE]"
| SOME (c, _) => TextIO.print ("·" ^ Char.toString c)
in r end
val (plain, istrm') = case Plain.scan dscan istrm of
*)
val (plain, istrm') = case Plain.scan TextIO.StreamIO.input1 istrm of
NONE => (NONE, istrm)
| SOME (plain, istrm') => (SOME plain, istrm')
val _ = Util.debugDetailed (fn ()=>["--reaping..."])
val st = OpSys.reap proc
val _ = Util.debugDetailed (fn ()=>["--done (",
if OS.Process.isSuccess st
then "success" else "failure", ")"])
val _ = if not (OS.Process.isSuccess st)
then warn ["reap failed: ", statusToString st]
else ()
val _ = TextIO.StreamIO.closeIn istrm
in if OS.Process.isSuccess st then plain else NONE end
handle IO.Io {cause,...} => NONE before
warn ["failed invoking ",graphToPath g," (", General.exnMessage cause, ")"]
| e => NONE before
warn ["failed invoking ",graphToPath g," (", General.exnMessage e, ")"]
fun copyFile (istrm, ostrm) = let
fun loop data = if CharVector.length data = 0 then ()
else (TextIO.output (ostrm, data);
loop (TextIO.input istrm))
in loop (TextIO.input istrm) end
fun makeFile (g, lang) (fstrm, v) = let
val _ = Util.debugOutline (fn ()=>["executing ", graphToPath g,
" ", langToOption lang])
val proc = OpSys.execute (graphToPath g, [langToOption lang])
val ostrm = OpSys.textOutstreamOf proc
val istrm = OpSys.textInstreamOf proc
val _ = Util.debugDetailed (fn ()=>["--writing output..."])
val _ = output (ostrm, v)
val _ = TextIO.closeOut ostrm
val _ = Util.debugDetailed (fn ()=>["--done."])
val _ = copyFile (istrm, fstrm) before TextIO.closeIn istrm
in OpSys.reap proc end
handle IO.Io {cause,...} => OS.Process.failure before
warn ["failed invoking ",graphToPath g," (", General.exnMessage cause, ")"]
| e => OS.Process.failure before
warn ["failed invoking ",graphToPath g," (", General.exnMessage e, ")"]
end