hydra-0.15.0: src/main/haskell/Hydra/Graphviz/Coder.hs
-- Note: this is an automatically generated file. Do not edit.
-- | Functions for converting Hydra terms to Graphviz DOT graphs
module Hydra.Graphviz.Coder where
import qualified Hydra.Core as Core
import qualified Hydra.Graphviz.Dot as Dot
import qualified Hydra.Lib.Equality as Equality
import qualified Hydra.Lib.Lists as Lists
import qualified Hydra.Lib.Literals as Literals
import qualified Hydra.Lib.Logic as Logic
import qualified Hydra.Lib.Maps as Maps
import qualified Hydra.Lib.Maybes as Maybes
import qualified Hydra.Lib.Pairs as Pairs
import qualified Hydra.Lib.Sets as Sets
import qualified Hydra.Lib.Strings as Strings
import qualified Hydra.Names as Names
import qualified Hydra.Packaging as Packaging
import qualified Hydra.Paths as Paths
import qualified Hydra.Rewriting as Rewriting
import qualified Hydra.Show.Paths as ShowPaths
import Prelude hiding (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci
import qualified Data.Map as M
-- | Create a DOT label attribute
labelAttr :: String -> Dot.EqualityPair
labelAttr lab =
Dot.EqualityPair {
Dot.equalityPairLeft = (Dot.Id "label"),
Dot.equalityPairRight = (Dot.Id lab)}
-- | Create DOT label attributes with a node style
labelAttrs :: String -> String -> Dot.AttrList
labelAttrs style lab =
let styleAttrs =
Logic.ifElse (Equality.equal style nodeStyleSimple) [] (Logic.ifElse (Equality.equal style nodeStyleElement) [
Dot.EqualityPair {
Dot.equalityPairLeft = (Dot.Id "style"),
Dot.equalityPairRight = (Dot.Id "filled")},
Dot.EqualityPair {
Dot.equalityPairLeft = (Dot.Id "fillcolor"),
Dot.equalityPairRight = (Dot.Id "lightyellow")}] (Logic.ifElse (Equality.equal style nodeStyleVariable) [
Dot.EqualityPair {
Dot.equalityPairLeft = (Dot.Id "style"),
Dot.equalityPairRight = (Dot.Id "filled")},
Dot.EqualityPair {
Dot.equalityPairLeft = (Dot.Id "fillcolor"),
Dot.equalityPairRight = (Dot.Id "lightcyan")}] [
Dot.EqualityPair {
Dot.equalityPairLeft = (Dot.Id "style"),
Dot.equalityPairRight = (Dot.Id "filled")},
Dot.EqualityPair {
Dot.equalityPairLeft = (Dot.Id "fillcolor"),
Dot.equalityPairRight = (Dot.Id "linen")}]))
in (Dot.AttrList [
Lists.concat2 [
labelAttr lab] styleAttrs])
-- | The 'element' node style
nodeStyleElement :: String
nodeStyleElement = "element"
-- | The 'primitive' node style
nodeStylePrimitive :: String
nodeStylePrimitive = "primitive"
-- | The 'simple' node style
nodeStyleSimple :: String
nodeStyleSimple = "simple"
-- | The 'variable' node style
nodeStyleVariable :: String
nodeStyleVariable = "variable"
-- | Construct a map from namespace to prefix for all standard libraries
standardNamespaces :: M.Map Packaging.Namespace String
standardNamespaces =
M.fromList [
(Packaging.Namespace "hydra.lib.chars", "chars"),
(Packaging.Namespace "hydra.lib.eithers", "eithers"),
(Packaging.Namespace "hydra.lib.equality", "equality"),
(Packaging.Namespace "hydra.lib.lists", "lists"),
(Packaging.Namespace "hydra.lib.literals", "literals"),
(Packaging.Namespace "hydra.lib.logic", "logic"),
(Packaging.Namespace "hydra.lib.maps", "maps"),
(Packaging.Namespace "hydra.lib.math", "math"),
(Packaging.Namespace "hydra.lib.maybes", "maybes"),
(Packaging.Namespace "hydra.lib.pairs", "pairs"),
(Packaging.Namespace "hydra.lib.regex", "regex"),
(Packaging.Namespace "hydra.lib.sets", "sets"),
(Packaging.Namespace "hydra.lib.strings", "strings")]
-- | Compute a label and node style for a term
termLabel :: Bool -> M.Map Packaging.Namespace String -> Core.Term -> (String, String)
termLabel compact namespaces term =
let simpleLabel = \lab -> (lab, nodeStyleSimple)
in case term of
Core.TermAnnotated _ -> simpleLabel "@{}"
Core.TermApplication _ -> simpleLabel (Logic.ifElse compact "$" "apply")
Core.TermLambda _ -> simpleLabel (Logic.ifElse compact "\955" "lambda")
Core.TermProject v0 -> simpleLabel (Strings.cat [
"{",
(Names.compactName namespaces (Core.projectionTypeName v0)),
"}.",
(Core.unName (Core.projectionField v0))])
Core.TermCases v0 -> simpleLabel (Strings.cat [
"cases_{",
(Names.compactName namespaces (Core.caseStatementTypeName v0)),
"}"])
Core.TermUnwrap v0 -> simpleLabel (Strings.cat [
"unwrap_{",
(Names.compactName namespaces v0),
"}"])
Core.TermLet _ -> simpleLabel "let"
Core.TermList _ -> simpleLabel (Logic.ifElse compact "[]" "list")
Core.TermLiteral v0 -> simpleLabel (case v0 of
Core.LiteralBinary v1 -> Literals.binaryToString v1
Core.LiteralBoolean v1 -> Literals.showBoolean v1
Core.LiteralInteger v1 -> case v1 of
Core.IntegerValueBigint v2 -> Literals.showBigint v2
Core.IntegerValueInt8 v2 -> Literals.showInt8 v2
Core.IntegerValueInt16 v2 -> Literals.showInt16 v2
Core.IntegerValueInt32 v2 -> Literals.showInt32 v2
Core.IntegerValueInt64 v2 -> Literals.showInt64 v2
Core.IntegerValueUint8 v2 -> Literals.showUint8 v2
Core.IntegerValueUint16 v2 -> Literals.showUint16 v2
Core.IntegerValueUint32 v2 -> Literals.showUint32 v2
Core.IntegerValueUint64 v2 -> Literals.showUint64 v2
_ -> "?"
Core.LiteralFloat v1 -> case v1 of
Core.FloatValueBigfloat v2 -> Literals.showBigfloat v2
Core.FloatValueFloat32 v2 -> Literals.showFloat32 v2
Core.FloatValueFloat64 v2 -> Literals.showFloat64 v2
_ -> "?"
Core.LiteralString v1 -> v1
_ -> "?")
Core.TermMap _ -> simpleLabel (Logic.ifElse compact "<,>" "map")
Core.TermMaybe _ -> simpleLabel (Logic.ifElse compact "opt" "optional")
Core.TermRecord v0 -> simpleLabel (Strings.cat2 "\8743" (Names.compactName namespaces (Core.recordTypeName v0)))
Core.TermTypeLambda _ -> simpleLabel "tyabs"
Core.TermTypeApplication _ -> simpleLabel "tyapp"
Core.TermInject v0 -> simpleLabel (Strings.cat2 "\8891" (Names.compactName namespaces (Core.injectionTypeName v0)))
Core.TermVariable v0 -> simpleLabel (Names.compactName namespaces v0)
Core.TermWrap v0 -> simpleLabel (Strings.cat [
"(",
(Names.compactName namespaces (Core.wrappedTermTypeName v0)),
")"])
_ -> simpleLabel "?"
-- | Convert a term to a full DOT graph
termToDotGraph :: Core.Term -> Dot.Graph
termToDotGraph term =
Dot.Graph {
Dot.graphStrict = False,
Dot.graphDirected = True,
Dot.graphId = Nothing,
Dot.graphStatements = (termToDotStmts standardNamespaces term)}
-- | Convert a term to full DOT statements showing term structure
termToDotStmts :: M.Map Packaging.Namespace String -> Core.Term -> [Dot.Stmt]
termToDotStmts namespaces term =
let encode =
\mlabstyle -> \isElement -> \ids -> \mparent -> \stmtsVisited -> \accessorTerm ->
let accessor = Pairs.first accessorTerm
currentTerm = Pairs.second accessorTerm
stmts = Pairs.first stmtsVisited
visited = Pairs.second stmtsVisited
termLS = termLabel True namespaces currentTerm
rawLabel = Pairs.first termLS
termNodeStyle = Pairs.second termLS
labelOf =
\vis -> \t ->
let tls = termLabel True namespaces t
l = Pairs.first tls
s = Pairs.second tls
in (Names.uniqueLabel vis l, s)
labstyle = Maybes.maybe (labelOf visited currentTerm) (\ls -> ls) mlabstyle
label = Pairs.first labstyle
style = Pairs.second labstyle
nodeStyle = Logic.ifElse isElement nodeStyleElement termNodeStyle
selfId = Dot.Id label
selfVisited = Sets.insert label visited
nodeStmt =
Dot.StmtNode (Dot.NodeStmt {
Dot.nodeStmtId = (toNodeId selfId),
Dot.nodeStmtAttributes = (Just (labelAttrs nodeStyle rawLabel))})
toAccessorEdgeStmt =
\acc -> \sty -> \i1 -> \i2 -> toEdgeStmt i1 i2 (Maybes.map (\s -> labelAttrs sty s) (ShowPaths.subtermStep acc))
edgeAttrs =
\lab -> Dot.AttrList [
[
Dot.EqualityPair {
Dot.equalityPairLeft = (Dot.Id "label"),
Dot.equalityPairRight = (Dot.Id lab)}]]
parentStmt = Maybes.maybe [] (\parent -> [
toAccessorEdgeStmt accessor style parent selfId]) mparent
selfStmts =
Lists.concat [
stmts,
[
nodeStmt],
parentStmt]
dflt = Lists.foldl (encode Nothing False ids (Just selfId)) (selfStmts, selfVisited) (Rewriting.subtermsWithSteps currentTerm)
in case currentTerm of
Core.TermLambda v0 ->
let v = Core.lambdaParameter v0
body = Core.lambdaBody v0
vstr = Core.unName v
varLabel = Names.uniqueLabel selfVisited vstr
varId = Dot.Id varLabel
visited1 = Sets.insert varLabel selfVisited
ids1 = Maps.insert v varId ids
varNodeStmt =
Dot.StmtNode (Dot.NodeStmt {
Dot.nodeStmtId = (toNodeId varId),
Dot.nodeStmtAttributes = (Just (labelAttrs nodeStyleVariable vstr))})
varEdgeStmt =
Dot.StmtEdge (Dot.EdgeStmt {
Dot.edgeStmtLeft = (toNodeOrSubgraph selfId),
Dot.edgeStmtRight = [
toNodeOrSubgraph varId],
Dot.edgeStmtAttributes = (Just (edgeAttrs "var"))})
in (encode Nothing False ids1 (Just selfId) (Lists.concat [
selfStmts,
[
varNodeStmt,
varEdgeStmt]], visited1) (Paths.SubtermStepLambdaBody, body))
Core.TermLet v0 ->
let bindings = Core.letBindings v0
env = Core.letBody v0
addBindingIds =
\idsVis -> \binding ->
let curIds = Pairs.first idsVis
curVis = Pairs.second idsVis
bname = Core.bindingName binding
bterm = Core.bindingTerm binding
bls = labelOf curVis bterm
blab = Pairs.first bls
in (Maps.insert bname (Dot.Id blab) curIds, (Sets.insert blab curVis))
idsVis1 = Lists.foldl addBindingIds (ids, visited) bindings
ids1 = Pairs.first idsVis1
addBindingTerm =
\stVis -> \binding ->
let bname = Core.bindingName binding
bterm = Core.bindingTerm binding
blab = Dot.unId (Maybes.fromMaybe (Dot.Id "?") (Maps.lookup bname ids1))
in (encode (Just (blab, nodeStyleElement)) True ids1 (Just selfId) stVis (Paths.SubtermStepLetBinding bname, bterm))
stmts1 = Lists.foldl addBindingTerm (selfStmts, selfVisited) bindings
in (encode Nothing False ids1 (Just selfId) stmts1 (Paths.SubtermStepLetBody, env))
Core.TermVariable v0 -> Maybes.maybe dflt (\i -> (Lists.concat2 stmts [
toAccessorEdgeStmt accessor style (Maybes.fromMaybe selfId mparent) i], visited)) (Maps.lookup v0 ids)
_ -> dflt
in (Pairs.first (encode Nothing False Maps.empty Nothing ([], Sets.empty) (Paths.SubtermStepAnnotatedBody, term)))
-- | Convert a term to an subterm-style DOT graph
termToSubtermDotGraph :: Core.Term -> Dot.Graph
termToSubtermDotGraph term =
Dot.Graph {
Dot.graphStrict = False,
Dot.graphDirected = True,
Dot.graphId = Nothing,
Dot.graphStatements = (termToSubtermDotStmts standardNamespaces term)}
-- | Convert a term to subterm-style DOT statements
termToSubtermDotStmts :: M.Map Packaging.Namespace String -> Core.Term -> [Dot.Stmt]
termToSubtermDotStmts namespaces term =
let accessorGraph = ShowPaths.termToSubtermGraph namespaces term
nodes = Paths.subtermGraphNodes accessorGraph
edges = Paths.subtermGraphEdges accessorGraph
nodeStmt =
\node -> Dot.StmtNode (Dot.NodeStmt {
Dot.nodeStmtId = (toNodeId (Dot.Id (Paths.subtermNodeId node))),
Dot.nodeStmtAttributes = (Just (Dot.AttrList [
[
labelAttr (Paths.subtermNodeLabel node)]]))})
edgeStmt =
\edge ->
let lab1 = Paths.subtermNodeId (Paths.subtermEdgeSource edge)
lab2 = Paths.subtermNodeId (Paths.subtermEdgeTarget edge)
pathAccessors = Paths.unSubtermPath (Paths.subtermEdgePath edge)
showPath = Strings.intercalate "/" (Maybes.cat (Lists.map ShowPaths.subtermStep pathAccessors))
in (toEdgeStmt (Dot.Id lab1) (Dot.Id lab2) (Just (Dot.AttrList [
[
labelAttr showPath]])))
in (Lists.concat2 (Lists.map nodeStmt nodes) (Lists.map edgeStmt edges))
-- | Create a DOT edge statement
toEdgeStmt :: Dot.Id -> Dot.Id -> Maybe Dot.AttrList -> Dot.Stmt
toEdgeStmt i1 i2 attrs =
Dot.StmtEdge (Dot.EdgeStmt {
Dot.edgeStmtLeft = (toNodeOrSubgraph i1),
Dot.edgeStmtRight = [
toNodeOrSubgraph i2],
Dot.edgeStmtAttributes = attrs})
-- | Create a DOT NodeId from an Id
toNodeId :: Dot.Id -> Dot.NodeId
toNodeId i =
Dot.NodeId {
Dot.nodeIdId = i,
Dot.nodeIdPort = Nothing}
-- | Create a DOT NodeOrSubgraph from an Id
toNodeOrSubgraph :: Dot.Id -> Dot.NodeOrSubgraph
toNodeOrSubgraph i = Dot.NodeOrSubgraphNode (toNodeId i)