dotparse-0.0.3: readme.org
#+TITLE: dotparse
[[https://hackage.haskell.org/package/dotparse][file:https://img.shields.io/hackage/v/dotparse.svg]]
* Introduction
Parsing and printing for the dot language of graphviz.
- A close rendition of the [[http://www.graphviz.org/doc/info/lang.html][dot language specification]]
- Supports inexact printing . parsing round trip; forgetting comments, separator choice and whitespace.
- Treats attribute keys and values as ByteStrings. No Type safety of Attributes is attempted.
- Uses command-line graphviz programs `dot` and `neato` to augment dot graph specifications
- Supports conversion from and to [[https://hackage.haskell.org/package/algebraic-graphs][algebraic-graphs]].
- Support rendering dot graphs using [[https://hackage.haskell.org/package/chart-svg][chart-svg]].
- Uses [[https://hackage.haskell.org/package/flatparse][flatparse]] for speedy parsing.
** Reference
Graphviz documentation:
[[https://www.graphviz.org/][Graphviz Main Page]]
[[http://magjac.com/graphviz-visual-editor/][Graphviz Visual Editor]]
[[http://www.graphviz.org/doc/info/attrs.html][Attributes | Graphviz]]
[[http://www.graphviz.org/pdf/dot.1.pdf][Dot Grammar]]
Graphviz practical examples:
https://renenyffenegger.ch/notes/tools/Graphviz/examples/index
https://renenyffenegger.ch/notes/tools/Graphviz/attributes/label/HTML-like/index
** Similar projects
The [[https://hackage.haskell.org/package/graphviz][graphviz]] library aims for comprehensive typing of graphviz attributes and syntax. As a result, it is quite large and somewhat incomplete. In contrast, dotparse parsing is simpler, more robust and faster. It is also somewhat tied to fgl and I wanted to try a different graph library.
[[https://hackage.haskell.org/package/dotgen][dotgen]] is a dot graph printer but not a parser. It supports a monadic style of printing. Specifically, it supports generation of unique names if that is an important feature of the problem domain.
** development wish list
Target [[https://hackage.haskell.org/package/calligraphy][calligraphy]] for enhanced source code visualization.
Broaden support to include fgl and containers.
Support parsing of library graphs from cabal.
Explore [[https://kowainik.github.io/posts/2019-01-14-tomland#tagged-partial-bidirectional-isomorphism][tagged partial birectional isomorphism]] style.
Steal design ideas from [[https://hackage.haskell.org/package/jordan][jordan]].
Case-insensitive parsing blocked on [[https://github.com/AndrasKovacs/flatparse/issues/10][AndrasKovacs/flatparse#10 Case-insensitive keyword matching]].
Exact printing of comments and whitespace.
* process pipelines
** algebraic-graphs
Starting with a Graph from algebraic-graphs:
#+begin_src haskell
import qualified Algebra.Graph as G
:{
exAGraph :: G.Graph Int
exAGraph =
G.edges $
[(v, (v + 1) `mod` 6) | v <- [0 .. 5]]
<> [(v, v + k) | v <- [0 .. 5], k <- [6, 12]]
<> [(2, 18), (2, 19), (15, 18), (15, 19), (18, 3), (19, 3)]
:}
#+end_src
#+RESULTS:
: ghci| ghci| ghci| ghci| ghci| ghci| ghci|
#+begin_src haskell :results output :exports both
exAGraph
#+end_src
#+RESULTS:
: Overlay (Connect (Vertex 0) (Vertex 1)) (Overlay (Connect (Vertex 1) (Vertex 2)) (Overlay (Connect (Vertex 2) (Vertex 3)) (Overlay (Connect (Vertex 3) (Vertex 4)) (Overlay (Connect (Vertex 4) (Vertex 5)) (Overlay (Connect (Vertex 5) (Vertex 0)) (Overlay (Connect (Vertex 0) (Vertex 6)) (Overlay (Connect (Vertex 0) (Vertex 12)) (Overlay (Connect (Vertex 1) (Vertex 7)) (Overlay (Connect (Vertex 1) (Vertex 13)) (Overlay (Connect (Vertex 2) (Vertex 8)) (Overlay (Connect (Vertex 2) (Vertex 14)) (Overlay (Connect (Vertex 3) (Vertex 9)) (Overlay (Connect (Vertex 3) (Vertex 15)) (Overlay (Connect (Vertex 4) (Vertex 10)) (Overlay (Connect (Vertex 4) (Vertex 16)) (Overlay (Connect (Vertex 5) (Vertex 11)) (Overlay (Connect (Vertex 5) (Vertex 17)) (Overlay (Connect (Vertex 2) (Vertex 18)) (Overlay (Connect (Vertex 2) (Vertex 19)) (Overlay (Connect (Vertex 15) (Vertex 18)) (Overlay (Connect (Vertex 15) (Vertex 19)) (Overlay (Connect (Vertex 18) (Vertex 3)) (Connect (Vertex 19) (Vertex 3))))))))))))))))))))))))
** dotparse Graph
Convert to a dotparse Graph
#+begin_src haskell
exGraph = defaultGraph & addStatements (toStatements Directed (Char8.pack . show <$> exAGraph))
#+end_src
** dotPrint
Encode graph as a ByteString (prior to processing via graphviz)
#+begin_src haskell :results output :exports both
BS.putStr (dotPrint defaultDotConfig exGraph)
#+end_src
#+RESULTS:
#+begin_example
digraph {
node [height=0.5;shape=circle]
graph [overlap=false;size="1!";splines=spline]
edge [arrowsize=0.5]
"9"
"8"
"7"
"6"
"5"
"4"
"3"
"2"
"19"
"18"
"17"
"16"
"15"
"14"
"13"
"12"
"11"
"10"
"1"
"0"
"5" -> "17"
"5" -> "11"
"5" -> "0"
"4" -> "5"
"4" -> "16"
"4" -> "10"
"3" -> "9"
"3" -> "4"
"3" -> "15"
"2" -> "8"
"2" -> "3"
"2" -> "19"
"2" -> "18"
"2" -> "14"
"19" -> "3"
"18" -> "3"
"15" -> "19"
"15" -> "18"
"1" -> "7"
"1" -> "2"
"1" -> "13"
"0" -> "6"
"0" -> "12"
"0" -> "1"
}
#+end_example
** processDotWith
Directly create an SVG from the dotparse Graph
#+begin_src haskell :file other/exdirect.svg :results output graphics file :exports both
(\b f -> processDotWith Directed ["-Tsvg", "-o", "other/" <> f <> ".svg"] b) (dotPrint defaultDotConfig exGraph) "exdirect"
#+end_src
#+RESULTS:
[[file:other/exdirect.svg]]
** processDot
ByteString of the processed Graph
#+begin_src haskell :results output
BS.putStr =<< processDot Directed (dotPrint defaultDotConfig exInt)
#+end_src
** processGraph
Graph augmented by graphviz
#+begin_src haskell
exGraphAugmented <- processGraph exGraph
:t exGraphAugmented
#+end_src
#+RESULTS:
: exGraphAugmented :: Graph
** graphToChartWith
SVG production via chart-svg
#+begin_src haskell :file other/exga.svg :results output graphics file :exports both
import Chart (writeChartOptions)
writeChartOptions "other/exga.svg" (graphToChartWith defaultChartConfig exGraphAugmented)
#+end_src
#+RESULTS:
[[file:other/exga.svg]]
* Development
** imports
#+begin_src haskell :results output
:reload
:set prompt "> "
:set -XOverloadedLabels
:set -XOverloadedStrings
:set -Wno-type-defaults
:set -XImportQualifiedPost
import Chart
import Optics.Core
import FlatParse.Basic qualified as FP
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as Char8
import GHC.Exts
import DotParse
import DotParse.Examples
import DotParse.Examples.NumHask
import Data.Proxy
print "ok"
#+end_src
#+RESULTS:
: [3 of 6] Compiling DotParse.Types ( src/DotParse/Types.hs, interpreted ) [Source file changed]
: [5 of 6] Compiling DotParse.Examples.NumHask ( src/DotParse/Examples/NumHask.hs, interpreted ) [Source file changed]
: Ok, six modules loaded.
: >
: ok
** write examples
Round-trip test
#+begin_src haskell :results output
testAll
#+end_src
#+RESULTS:
#+begin_example
ex0
ex1
ex2
ex3
ex4
ex5
ex6
ex7
ex8
ex9
ex10
ex11
ex12
ex13
ex14
ex15
#+end_example
Render examples
#+begin_src haskell
svgAll
#+end_src
#+RESULTS:
#+begin_example
ex0
ex1
ex2
ex3
ex4
ex5
ex6
ex7
ex8
ex9
ex10
ex11
ex12
ex13
ex14
ex15
#+end_example
** numhask example
#+begin_src haskell
writeNHChart
#+end_src
This is bugged downstream as chart-svg uses the link string (which is very long compared with the displayed text) to estimate the bounding box and results in a very wide chart with a lot of white space either side.