118 lines
3.6 KiB
Haskell
118 lines
3.6 KiB
Haskell
|
|
{-# LANGUAGE BlockArguments #-}
|
||
|
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
||
|
|
{-# LANGUAGE OverloadedRecordDot #-}
|
||
|
|
{-# HLINT ignore "Unused LANGUAGE pragma" #-}
|
||
|
|
{-# HLINT ignore "Use concatMap" #-}
|
||
|
|
{-# LANGUAGE RecordWildCards #-}
|
||
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||
|
|
{-# LANGUAGE TypeApplications #-}
|
||
|
|
{-# HLINT ignore "Move catMaybes" #-}
|
||
|
|
{-# HLINT ignore "Use mapMaybe" #-}
|
||
|
|
{-# HLINT ignore "Use list comprehension" #-}
|
||
|
|
{-# LANGUAGE TypeOperators #-}
|
||
|
|
{-# LANGUAGE NoFieldSelectors #-}
|
||
|
|
{-# LANGUAGE NoMonomorphismRestriction #-}
|
||
|
|
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
|
||
|
|
|
||
|
|
module Ologs
|
||
|
|
( Arc,
|
||
|
|
Identity,
|
||
|
|
Olog,
|
||
|
|
makeOlog,
|
||
|
|
MakeOlogError (..),
|
||
|
|
)
|
||
|
|
where
|
||
|
|
|
||
|
|
import Control.Monad
|
||
|
|
import Data.List.NonEmpty (NonEmpty, nonEmpty)
|
||
|
|
import qualified Data.List.NonEmpty as NE
|
||
|
|
import qualified Data.Map as Map
|
||
|
|
import Data.Traversable
|
||
|
|
|
||
|
|
data Arc dot = Arc
|
||
|
|
{ name :: String,
|
||
|
|
source :: dot,
|
||
|
|
target :: dot
|
||
|
|
}
|
||
|
|
deriving (Show, Eq)
|
||
|
|
|
||
|
|
data Identity = Identity
|
||
|
|
{ lhs :: [String],
|
||
|
|
rhs :: [String]
|
||
|
|
}
|
||
|
|
deriving (Show, Eq)
|
||
|
|
|
||
|
|
data Olog dot = Olog
|
||
|
|
{ dots :: [dot],
|
||
|
|
arcs :: [Arc dot],
|
||
|
|
identities :: [Identity]
|
||
|
|
}
|
||
|
|
deriving (Show, Eq)
|
||
|
|
|
||
|
|
-- type f $ x = f x
|
||
|
|
-- type ($) f x = f x
|
||
|
|
|
||
|
|
data MakeOlogError dot
|
||
|
|
= UnknownSource String dot
|
||
|
|
| UnknownTarget String dot
|
||
|
|
| ForbiddenTrivialIdentity
|
||
|
|
| UnknownArc String
|
||
|
|
| NonJoiningExpressionLhs [String]
|
||
|
|
| NonJoiningExpressionRhs [String]
|
||
|
|
| IdentityMismatch [String] [String] (dot, dot) (dot, dot)
|
||
|
|
| NotALoop [String]
|
||
|
|
deriving (Show, Eq)
|
||
|
|
|
||
|
|
makeOlog ::
|
||
|
|
forall dot.
|
||
|
|
(Eq dot) =>
|
||
|
|
[dot] ->
|
||
|
|
[(String, dot, dot)] ->
|
||
|
|
[([String], [String])] ->
|
||
|
|
Either (MakeOlogError dot) (Olog dot)
|
||
|
|
makeOlog dots preArcs preIdentities = do
|
||
|
|
arcs <- for preArcs \(name, source, target) -> do
|
||
|
|
-- TODO reuse `namesToArcs`?
|
||
|
|
unless (source `elem` dots) $ Left $ UnknownSource name source
|
||
|
|
unless (target `elem` dots) $ Left $ UnknownTarget name target
|
||
|
|
pure Arc{name, source, target}
|
||
|
|
identities <- for preIdentities \(lhs, rhs) -> do
|
||
|
|
lhs' :: [(String, (dot, dot))] <- for lhs \arcName -> case Map.lookup arcName namesToArcs of
|
||
|
|
Nothing -> Left $ UnknownArc arcName
|
||
|
|
Just srcAndTgt -> pure (arcName, srcAndTgt)
|
||
|
|
rhs' :: [(String, (dot, dot))] <- for rhs \arcName -> case Map.lookup arcName namesToArcs of
|
||
|
|
Nothing -> Left $ UnknownArc arcName
|
||
|
|
Just srcAndTgt -> pure (arcName, srcAndTgt)
|
||
|
|
case (nonEmpty lhs', nonEmpty rhs') of
|
||
|
|
(Nothing, Nothing) ->
|
||
|
|
Left ForbiddenTrivialIdentity
|
||
|
|
(Just l, Nothing) -> do
|
||
|
|
checkTerm NonJoiningExpressionLhs l
|
||
|
|
let (_, (_, tgt)) = NE.head l
|
||
|
|
let (_, (src, _)) = NE.last l
|
||
|
|
errorWhen (src /= tgt) $ NotALoop lhs
|
||
|
|
(Nothing, Just r) -> do
|
||
|
|
checkTerm NonJoiningExpressionRhs r
|
||
|
|
let (_, (_, tgt)) = NE.head r
|
||
|
|
let (_, (src, _)) = NE.last r
|
||
|
|
errorWhen (src /= tgt) $ NotALoop rhs
|
||
|
|
(Just l, Just r) -> do
|
||
|
|
checkTerm NonJoiningExpressionLhs l
|
||
|
|
checkTerm NonJoiningExpressionRhs r
|
||
|
|
let combinedSrcTgt xhs = (fst $ snd $ NE.last xhs, snd $ snd $ NE.head xhs)
|
||
|
|
l' = combinedSrcTgt l
|
||
|
|
r' = combinedSrcTgt r
|
||
|
|
errorWhen (l' /= r') $ IdentityMismatch lhs rhs l' r'
|
||
|
|
pure Identity {lhs, rhs}
|
||
|
|
pure Olog {dots, arcs, identities}
|
||
|
|
where
|
||
|
|
checkTerm errorFactory arcs = do
|
||
|
|
let
|
||
|
|
targets = NE.tail $ snd . snd <$> (arcs :: (NonEmpty (String, (dot, dot))))
|
||
|
|
sources = NE.init $ fst . snd <$> arcs
|
||
|
|
errorWhen (sources /= targets) $ errorFactory $ map fst $ NE.toList arcs
|
||
|
|
errorWhen b e = if b then Left e else Right ()
|
||
|
|
namesToArcs = Map.fromList $ (\(name, src, tgt) -> (name, (src, tgt))) <$> preArcs
|
||
|
|
|