packages feed

rest-rewrite-0.1: src/Language/REST/RPO.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ImplicitParams #-}

module Language.REST.RPO (rpo, rpoTerm, rpoGTE, rpoGTE', synGTE) where

import Prelude hiding (EQ, GT)
import Debug.Trace (trace)
import Text.Printf

import Control.Monad.Identity
import Control.Monad.State.Strict
import GHC.Generics
import Data.Hashable
import qualified Data.List as L
import qualified Data.HashSet as S
import qualified Data.HashMap.Strict as M

import qualified Language.REST.MultiSet as MS
import           Language.REST.Op
import           Language.REST.OpOrdering as OpOrdering
import           Language.REST.OrderingConstraints as OC
import qualified Language.REST.MetaTerm as MT
import           Language.REST.Types
import qualified Language.REST.RuntimeTerm as RT
import           Language.REST.MultisetOrder

type MultiSet = MS.MultiSet

data RuntimeTerm = App Op (MultiSet RuntimeTerm) deriving (Generic, Eq, Hashable, Ord)

instance Show RuntimeTerm where
  show (App op trms) =
      if MS.null trms
      then show op
      else show op ++ show trms

instance MT.ToMetaTerm RuntimeTerm where
  toMetaTerm (App op xs) = MT.RWApp op (map MT.toMetaTerm $ MS.toList xs)

ops :: RuntimeTerm -> S.HashSet Op
ops (App f ts) = S.insert f (S.unions $ map ops (MS.distinctElems ts))

rpoTerm :: RT.RuntimeTerm -> RuntimeTerm
rpoTerm (RT.App f xs) = App f $ MS.fromList (map rpoTerm xs)

isSubtermOf :: RuntimeTerm -> RuntimeTerm -> Bool
isSubtermOf t u@(App _ us) = t == u || any (t `isSubtermOf`) (MS.distinctElems us)

trace' :: String -> a -> a
-- trace' = trace
trace' _ x = x

type CacheKey oc = ((oc Op), Relation, RuntimeTerm, RuntimeTerm)

type Cache oc = M.HashMap (CacheKey oc) (oc Op)

data RPOState oc =
  RPOState
    { rpoCache :: Cache oc
    , rpoDepth :: Int
    }

type RMonad oc = State (RPOState oc)

incDepth :: RMonad oc (oc Op) -> RMonad oc (oc Op)
incDepth action = do
  modify (\st -> st{rpoDepth = rpoDepth st + 1})
  result <- action
  modify (\st -> st{rpoDepth = rpoDepth st - 1})
  return result


cached :: (Eq (oc Op), Hashable (oc Op)) => CacheKey oc -> RMonad oc (oc Op) -> RMonad oc (oc Op)
cached key@(_,_,t1,t2) thunk = do
  cache <- gets rpoCache
  case M.lookup key cache of
    Just result -> trace' ("Cache hit" ++ show (t1, t2)) $ return result
    Nothing     -> trace' ("Cache miss" ++ show (t1, t2)) $ do
      result <- trace' "Do thunk" thunk
      trace' "Done" $ modify (\st -> st{ rpoCache = M.insert key result (rpoCache st)})
      return result
 where
   trace' _  x = x
   -- trace' = trace


rpo :: (Show (oc Op), Eq (oc Op), Hashable (oc Op)) => ConstraintGen oc Op RT.RuntimeTerm Identity
rpo = runStateConstraints (cmapConstraints rpoTerm rpo') (RPOState M.empty 0)

rpoMul :: (Show (oc Op), Eq (oc Op), Hashable (oc Op)) => ConstraintGen oc Op (MultiSet RuntimeTerm) (RMonad oc)
rpoMul = multisetOrder rpo'

rpo' :: (Show (oc Op), Eq (oc Op), Hashable (oc Op)) => ConstraintGen oc Op RuntimeTerm (RMonad oc)
rpo' impl _      oc _  _ | oc == unsatisfiable impl           = return $ unsatisfiable impl
rpo' OC{unsatisfiable} r oc      t u      | t == u            = return $ if r == GT then unsatisfiable else oc
rpo' OC{unsatisfiable} _ _       t u      | t `isSubtermOf` u = return unsatisfiable
rpo' OC{unsatisfiable} r oc      t u      | u `isSubtermOf` t = return $ if r == EQ then unsatisfiable else oc
rpo' oc r cs t@(App f ts) u@(App g us)    | f == g            = rpoMul oc r cs ts us

rpo' oc r cs t@(App f ts) u@(App g us) = incDepth result
  where
    traceString depth = printf "%s %s %s %s" (take depth $ repeat '.') (show t) (show r) (show u)
    cs'    = noConstraints oc -- relevantConstraints oc cs (ops t) (ops u)
    result = cached (cs, r, t, u) $ (intersect oc cs <$> result')
    result' = cached (cs', r, t, u) $ do
      depth <- gets rpoDepth
      trace' (traceString depth) $
        if r == EQ
        then rpoMul oc r (addConstraint oc (f =. g) cs') ts us
        else
          unionAll oc <$> sequence [
            rpoMul oc GT  (addConstraint oc (f >. g) cs') (MS.singleton t) us
          , rpoMul oc r   (addConstraint oc (f =. g) cs') ts               us
          , rpoMul oc GTE cs'                             ts               (MS.singleton u)
          ]



rpoGTE t u = runIdentity $ rpoGTE' ?impl (noConstraints ?impl) t u

rpoGTE' impl oc t u = rpo impl GTE oc t u













-- Non symbolic version

synEQ :: OpOrdering -> RuntimeTerm -> RuntimeTerm -> Bool
synEQ o l r = synGTE' o l r && synGTE' o r l

removeSynEQs :: OpOrdering -> [RuntimeTerm] -> [RuntimeTerm] -> ([RuntimeTerm], [RuntimeTerm])
removeSynEQs _ [] ys = ([], ys)
removeSynEQs ordering (x : xs) ys
  | Just y <- L.find (synEQ ordering x) ys
  = removeSynEQs ordering xs $ L.delete y ys
  | otherwise
  = let (xs', ys') = removeSynEQs ordering xs ys in (x : xs', ys')

synGTE :: OpOrdering -> RT.RuntimeTerm -> RT.RuntimeTerm -> Bool
synGTE o t u = synGTE' o (rpoTerm t) (rpoTerm u)

synGTE' :: OpOrdering -> RuntimeTerm -> RuntimeTerm -> Bool
synGTE' ordering t@(App f ts) u@(App g us)
  | opGT ordering f g
  = synGTM ordering (MS.singleton t) us
synGTE' ordering (App f ts) (App g us)
  | opEQ ordering f g
  = synGTEM ordering ts us
synGTE' ordering (App _ ts) u = synGTEM ordering ts (MS.singleton u)

rpoT :: OpOrdering -> RuntimeTerm -> RuntimeTerm -> Bool
rpoT o t1 t2 = synGTE' o t1 t2 && not (synGTE' o t2 t1)

synGTEM :: OpOrdering -> MultiSet RuntimeTerm -> MultiSet RuntimeTerm -> Bool
synGTEM ordering xs ys = case removeSynEQs ordering (MS.toList xs) (MS.toList ys) of
  (xs', ys') -> all (\y -> any (\x -> rpoT ordering x y) xs') ys'

synGTM :: OpOrdering -> MultiSet RuntimeTerm -> MultiSet RuntimeTerm -> Bool
synGTM ordering xs ys = case removeSynEQs ordering (MS.toList xs) (MS.toList ys) of
  ([] , [] ) -> False
  (xs', ys') -> all (\y -> any (\x -> rpoT ordering x y) xs') ys'