mirror of
https://github.com/rgleichman/glance.git
synced 2024-11-29 21:40:48 +03:00
205 lines
9.5 KiB
Haskell
205 lines
9.5 KiB
Haskell
module GraphAlgorithms(
|
|
collapseNodes,
|
|
findTreeRoots,
|
|
childCanBeEmbedded
|
|
) where
|
|
|
|
import qualified Data.Graph.Inductive.PatriciaTree as FGR
|
|
import qualified Data.Graph.Inductive.Graph as ING
|
|
import Types(SgNamedNode, Edge(..), SyntaxNode(..), sgNamedNodeToSyntaxNode, EdgeEnd(..), NameAndPort(..), IngSyntaxGraph)
|
|
import Data.Maybe(listToMaybe, catMaybes)
|
|
import Data.List(foldl', find)
|
|
import Diagrams.Prelude(toName)
|
|
import qualified Debug.Trace
|
|
|
|
import Util(printSelf, maybeBoolToBool)
|
|
|
|
-- See graph_algs.txt for pseudocode
|
|
|
|
type LabelledGraphEdge = ING.LEdge Edge
|
|
|
|
-- START collapseNodes helper functions --
|
|
|
|
-- | A syntaxNodeIsEmbeddable if it can be collapsed into another node
|
|
syntaxNodeIsEmbeddable :: SyntaxNode -> Bool
|
|
syntaxNodeIsEmbeddable n = case n of
|
|
ApplyNode _ -> True
|
|
-- TODO make PatternApplyNode embeddable
|
|
PatternApplyNode _ _ -> False
|
|
NameNode _ -> False
|
|
LiteralNode _ -> True
|
|
FunctionDefNode _ -> False
|
|
GuardNode _ -> False
|
|
CaseNode _ -> False
|
|
BranchNode -> False
|
|
CaseResultNode -> False
|
|
-- Don't use a catch all (i.e. irrefutable) pattern here so that if other
|
|
-- SyntaxNodes are added we will get a warning here.
|
|
|
|
-- | A syntaxNodeCanEmbed if it can contain other nodes
|
|
syntaxNodeCanEmbed :: SyntaxNode -> Bool
|
|
syntaxNodeCanEmbed n = case n of
|
|
ApplyNode _ -> True
|
|
x@(NestedApplyNode _ _) -> True -- This case should not happen
|
|
-- TODO make PatternApplyNode embed
|
|
PatternApplyNode _ _ -> False
|
|
NameNode _ -> False
|
|
LiteralNode _ -> False
|
|
FunctionDefNode _ -> False
|
|
GuardNode _ -> False
|
|
CaseNode _ -> False
|
|
BranchNode -> False
|
|
CaseResultNode -> False
|
|
-- Don't use a catch all (i.e. irrefutable) pattern here so that if other
|
|
-- SyntaxNodes are added we will get a warning here.
|
|
|
|
extractSyntaxNode :: ING.LNode SgNamedNode -> SyntaxNode
|
|
extractSyntaxNode = snd . snd
|
|
|
|
findParents :: ING.Graph gr => gr a b -> ING.Node -> [ING.Node]
|
|
-- TODO, may need to use ING.pre or ING.neighbors instead of ING.suc'
|
|
findParents = ING.suc
|
|
|
|
findChildren :: ING.Graph gr => gr a b -> ING.Node -> [ING.Node]
|
|
findChildren = ING.pre
|
|
|
|
-- | graphNodeCanEmbed returns true if the label (SyntaxNode) associated with the
|
|
-- node can be embedded in other SyntaxNodes (i.e. nodeCanEmbed is True)
|
|
graphNodeCanEmbed :: ING.Graph gr => IngSyntaxGraph gr -> ING.Node -> Bool
|
|
graphNodeCanEmbed graph node = maybeBoolToBool $ fmap syntaxNodeCanEmbed (lookupSyntaxNode graph node)
|
|
|
|
graphNodeIsEmbeddable :: ING.Graph gr => IngSyntaxGraph gr -> ING.Node -> Bool
|
|
graphNodeIsEmbeddable graph node = maybeBoolToBool $ fmap syntaxNodeIsEmbeddable (lookupSyntaxNode graph node)
|
|
|
|
lookupSyntaxNode :: ING.Graph gr => IngSyntaxGraph gr -> ING.Node -> Maybe SyntaxNode
|
|
lookupSyntaxNode gr node = fmap sgNamedNodeToSyntaxNode $ ING.lab gr node
|
|
|
|
-- | filterNodes returns a list of the nodes in the graph
|
|
-- where the filter function is true.
|
|
filterNodes :: ING.DynGraph gr => (ING.Node -> Bool) -> gr a b -> [ING.Node]
|
|
filterNodes pred gr = ING.nodes $ ING.nfilter pred gr
|
|
|
|
-- | Replace the a node's label
|
|
changeNodeLabel :: ING.DynGraph gr => gr a b -> ING.Node -> a -> gr a b
|
|
changeNodeLabel graph node newLabel = case ING.match node graph of
|
|
(Just (inEdges, _, _, outEdges), restOfTheGraph) -> (inEdges, node, newLabel, outEdges) ING.& restOfTheGraph
|
|
(Nothing, _) -> graph
|
|
|
|
findEdgeLabel :: ING.Graph gr => gr a b -> ING.Node -> ING.Node -> Maybe b
|
|
findEdgeLabel graph node1 node2 = fmap fst matchingEdges where
|
|
labelledEdges = ING.lneighbors graph node1
|
|
matchingEdges = find ((== node2) . snd) labelledEdges
|
|
|
|
-- END helper functions --
|
|
|
|
collapseNodes :: (ING.DynGraph gr) => IngSyntaxGraph gr -> IngSyntaxGraph gr
|
|
collapseNodes originalGraph = finalGraph where
|
|
-- findTreeRoots returns a list of nodes that will embed other nodes, but are not embedded themselves.
|
|
-- These nodes are thus each a root of a collapsed node tree.
|
|
treeRoots = findTreeRoots originalGraph
|
|
-- Now collapse each tree of nodes
|
|
finalGraph = collapseRoots treeRoots originalGraph originalGraph treeRoots
|
|
|
|
-- START findTreeRoots functions --
|
|
|
|
-- |findTreeRoots returns a list of nodes that might embed other nodes, but are not embedded themselves.
|
|
-- These nodes are thus each a root of a collapsed node tree.
|
|
-- A node is a treeRoot if all of these conditions are true:
|
|
-- 1. The SyntaxNode can embed other nodes (i.e. syntaxNodeCanEmbed is true)
|
|
-- 2. The node has no parents that can embed it
|
|
-- TODO These rules should be revised to allow cycles to be embedded.
|
|
-- Condition 2. should be revised such that if there is a parent that is a bind, it's a root even if other nodes can embed it.
|
|
-- Note: A treeRoot may not actually have any embeddable children, since collapseTree will do nothing in that case.
|
|
findTreeRoots :: ING.DynGraph gr => IngSyntaxGraph gr -> [ING.Node]
|
|
findTreeRoots graph = filterNodes (isTreeRoot graph) graph
|
|
|
|
isTreeRoot :: ING.Graph gr => IngSyntaxGraph gr -> ING.Node -> Bool
|
|
isTreeRoot graph node = graphNodeCanEmbed graph node && noParentsCanEmbed where
|
|
noParentsCanEmbed = null parentsThatCanEmbed
|
|
parentsThatCanEmbed = filter (graphNodeCanEmbed graph) parents
|
|
parents = findParents graph node
|
|
|
|
-- END findTreeRoots functions
|
|
-- START collapseRoots functions
|
|
|
|
collapseRoots :: ING.DynGraph gr => [ING.Node] -> IngSyntaxGraph gr -> IngSyntaxGraph gr -> [ING.Node] -> IngSyntaxGraph gr
|
|
collapseRoots treeRoots originalGraph = foldl' (collapseTree treeRoots originalGraph)
|
|
|
|
collapseTree :: ING.DynGraph gr => [ING.Node] -> IngSyntaxGraph gr -> IngSyntaxGraph gr -> ING.Node -> IngSyntaxGraph gr
|
|
collapseTree treeRoots originalGraph oldGraph rootNode = case childrenToEmbed of
|
|
[] -> oldGraph
|
|
_ -> finalGraph
|
|
where
|
|
-- Need to use the original graph for finding children, otherwise a node can be embedded when it is used twice in
|
|
-- what will be a single embedding node. Examples:
|
|
-- "y = foo (3 + bazOf2) bazOf2 where bazOf2 = baz 2",
|
|
-- "y = foo (3 + bazOf2) (8 * bazOf2) where bazOf2 = baz 2"
|
|
childrenToEmbed = findChildrenToEmbed treeRoots rootNode originalGraph
|
|
-- Recursively collapse the children nodes
|
|
graphWithCollapsedChildren = collapseRoots treeRoots originalGraph oldGraph childrenToEmbed
|
|
-- Modify the rootNode label (i.e. SyntaxNode) to incorporate the children it is embedding
|
|
graphWithEmbeddedChildren = embedChildSyntaxNodes rootNode childrenToEmbed graphWithCollapsedChildren
|
|
-- Transfer the edges of the children to rootNode
|
|
childEdgesToTransfer = findChildEdgesToTransfer rootNode childrenToEmbed graphWithEmbeddedChildren
|
|
graphWithEdgesTransferred = ING.insEdges childEdgesToTransfer graphWithEmbeddedChildren
|
|
-- Delete the children that have been embedded (and any or their remaining edges)
|
|
finalGraph = deleteChildren childrenToEmbed graphWithEdgesTransferred
|
|
|
|
-- | findChildrenToEmbed returns a list of the node's children that can be embedded
|
|
-- A child can be embedded iff all of these conditions are true:
|
|
-- 1. The node is not a treeRoot (otherwise a cycle of embedding could occur)
|
|
-- 2. The SyntaxNode is embeddable (i.e. syntaxNodeIsEmbeddable is True)
|
|
-- 3. The node has exactly one parent that can embed (i.e. nodeCanEmbed is True for one parent)
|
|
findChildrenToEmbed :: ING.Graph gr => [ING.Node] -> ING.Node -> IngSyntaxGraph gr -> [ING.Node]
|
|
findChildrenToEmbed treeRoots node graph = if graphNodeCanEmbed graph node
|
|
then childrenToEmbed
|
|
else []
|
|
where
|
|
childrenToEmbed = filter (childCanBeEmbedded treeRoots graph) (findChildren graph node)
|
|
|
|
childCanBeEmbedded :: ING.Graph gr => [ING.Node] -> IngSyntaxGraph gr -> ING.Node -> Bool
|
|
childCanBeEmbedded treeRoots graph child = notTreeRoot && isEmbeddable && oneParentCanEmbed where
|
|
notTreeRoot = notElem child treeRoots
|
|
isEmbeddable = graphNodeIsEmbeddable graph child
|
|
oneParentCanEmbed = case parentsThatCanEmbed of
|
|
[_] -> True
|
|
_ -> False
|
|
parentsThatCanEmbed = filter (graphNodeCanEmbed graph) (findParents graph child)
|
|
|
|
-- TODO findChildEdgesToTransfer might add too many edges
|
|
findChildEdgesToTransfer :: ING.Graph gr => ING.Node -> [ING.Node] -> gr a b-> [ING.LEdge b]
|
|
findChildEdgesToTransfer parentNode nodes graph = concatMap makeLabelledGraphEdges nodes where
|
|
makeLabelledGraphEdges childNode = fmap (changeEdgeToParent parentNode childNode) $ ING.inn graph childNode
|
|
|
|
changeEdgeToParent :: ING.Node -> ING.Node -> ING.LEdge b -> ING.LEdge b
|
|
changeEdgeToParent parentNode childNode (fromNode, toNode, edgeLabel)
|
|
| childNode == fromNode = (parentNode, toNode, edgeLabel)
|
|
| childNode == toNode = (fromNode, parentNode, edgeLabel)
|
|
|
|
-- | Change the node label of the parent to be nested.
|
|
embedChildSyntaxNodes :: ING.DynGraph gr => ING.Node -> [ING.Node] -> IngSyntaxGraph gr -> IngSyntaxGraph gr
|
|
embedChildSyntaxNodes parentNode childrenNodes oldGraph = case childrenNodes of
|
|
[] -> oldGraph
|
|
_ -> newGraph
|
|
where
|
|
maybeOldNodeLabel = ING.lab oldGraph parentNode
|
|
newGraph = case maybeOldNodeLabel of
|
|
Nothing -> oldGraph
|
|
Just oldNodeLabel -> changeNodeLabel oldGraph parentNode newNodeLabel
|
|
where
|
|
(nodeName, oldSyntaxNode) = oldNodeLabel
|
|
newNodeLabel = (nodeName, newSyntaxNode)
|
|
newSyntaxNode = case oldSyntaxNode of
|
|
-- TODO Add PatternApplyNode, and NestedApplyNode
|
|
ApplyNode x -> NestedApplyNode x childrenAndEdgesToParent
|
|
_ -> oldSyntaxNode
|
|
childrenAndEdgesToParent = catMaybes $ fmap findChildAndEdge childrenNodes
|
|
findChildAndEdge childNode =
|
|
(,) <$> ING.lab oldGraph childNode <*> findEdgeLabel oldGraph parentNode childNode
|
|
|
|
|
|
deleteChildren :: ING.Graph gr => [ING.Node] -> IngSyntaxGraph gr -> IngSyntaxGraph gr
|
|
deleteChildren = ING.delNodes
|
|
|
|
-- END collapseRoots functions
|