packages feed

maxsharing-1.0: Spanning.hs

{-# LANGUAGE UnicodeSyntax, ScopedTypeVariables, TypeFamilies #-}
module Spanning where

import Prelude.Unicode
import Lambda (Λ (..), Symbol (..), Params (..), V, combinations)
import Language.HaLex.Dfa (Dfa (..))
import Data.Map (Map)
import qualified Data.Map as Map
import Data.List ((\\),sort)
import Control.Monad.State
import Control.Applicative


-- | Turns a λ-DFA into a λ-spanning-tree. The I-symbol is used for indirection nodes
spanning ∷ ∀ state. Ord state => Params → Dfa state Symbol -> Λ
spanning params (Dfa symbols states start _ trans) = evalState (spanningΛ start) funcVarsVisited where

	-- Stateful computation that remembers which shared states have already been dispatched
	spanningΛ ∷ state → State (Map state (V,Bool)) Λ
	spanningΛ s = let descend = spanningLoc in do
		sharedStates ← get
		case Map.lookup s sharedStates of -- we check the status of s
			Nothing → descend s           -- s is not a shared state
			Just (f,True) → pure $ V f        -- s is a shared state that has already been dispatched
			Just (f,False) → do               -- s is a shared state that we will dispatch right now
				modify $ Map.insert s (f,True) -- we mark s as dispatched
				I f <$> descend s          -- we create an indirection node and descend below s

	spanningLoc ∷ state → State (Map state (V,Bool)) Λ
	spanningLoc s = let descend = spanningΛ in case outEdges s of
	   [(S_Λ,e)] → Λ (lookupMap s varMap) <$> descend e
	   [(S_A0,f), (S_A1,x)] → A <$> descend f <*> descend x
	   [(S_V,x)] → pure $ V (lookupMap x varMap)
	   [(S_S0,e),(S_S1,abs)] → S (lookupMap abs varMap) <$> descend e
	   [(S_F v, _)] → pure $ V v
	   [] → pure $ I "bh" (V "bh") -- blackhole
	   _ → error "This seems not to be a λ-DFA"

	varMap ∷ Map state V
	varMap = Map.fromList $ filter isAbstraction states `zip` vs where
		vs ∷ [V]
		vs = combinations "xyzabcderstuvw" \\ freeVariables
		isAbstraction ∷ state → Bool
		isAbstraction node = trans node S_Λ ≢ dummy

	dummy ∷ state -- the state whose outgoing edges all point to itself
	dummy = case [s | s ← states, all (\sym → trans s sym ≡ s) symbols] of
		[x] → x
		___ → error $ "couldn't identify dummy state"

	freeVariables ∷ [V]
	freeVariables = [v | S_F v ← symbols]

	fs ∷ [V]
	fs = combinations ['F'..'U'] \\ freeVariables

	-- same as funcVars only with the additional information wether we have already visited a node
	funcVarsVisited ∷ Map state (V,Bool)
	funcVarsVisited = fmap (\v → (v,False)) funcVars

	funcVars ∷ Map state V -- maps shared states to a unique function variable
	funcVars = Map.fromList $ {-filter (not . isVarOcc)-} sharedStates `zip` fs where
		isVarOcc s = any (\l → trans s l ≢ dummy) (filter isVarSymbol symbols) where
			isVarSymbol (S_V) = True
			isVarSymbol (S_F _) = True
			isVarSymbol _ = False

	-- states with multiple incoming (non-backlink) edges (shared subgraphs)
	sharedStates ∷ [state]
	sharedStates = Map.keys $ Map.filter (≥2) $ Map.fromListWith (+) (succList `zip` repeat 1) where
		succList = start : [target | (sym,target) ← concatMap outEdges states, not $ isBacklink sym]
		isBacklink (S_V)   = True
		isBacklink (S_F _) = True
		isBacklink (S_S1)  = True
		isBacklink _______ = False

	outEdges ∷ state → [(Symbol, state)]
	outEdges source = sort [(sym, target) | sym ← symbols, let target = trans source sym, target ≢ dummy]

lookupMap ∷ Ord k ⇒ k → Map k a → a
lookupMap = Map.findWithDefault (error "lookupMap: key not found")