packages feed

hdiff-0.0.1: executables/Languages/Interface.hs

{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeApplications #-}
module Languages.Interface where

import Data.List (isSuffixOf)
import Data.Type.Equality

import Control.Applicative
import Control.Monad.Except

import System.IO
import System.Exit

import Generics.MRSOP.Util
import Generics.MRSOP.Base hiding (Infix)
import Generics.MRSOP.HDiff.Renderer
import Generics.MRSOP.HDiff.Digest

data LangParser :: * where
  LangParser :: (LangCnstr ki fam codes ix, EqHO ki , ShowHO ki)
             -- |Language extension
             => String
             -- |Parser that
             -> (FilePath -> IO (Fix ki codes ix))
             -> LangParser

data VectorOf (a :: *) (n :: Nat) :: * where
  V0 :: VectorOf a 'Z
  VS :: a -> VectorOf a n -> VectorOf a ('S n)

vecMapM :: (Monad m) => (a -> m b) -> VectorOf a n -> m (VectorOf b n)
vecMapM _ V0 = return V0
vecMapM f (VS x xs) = VS <$> f x <*> vecMapM f xs

type LangCnstr ki fam codes ix
  = (HasDatatypeInfo ki fam codes, RendererHO ki, IsNat ix, DigestibleHO ki, TestEquality ki)

-- |Given a list of languages, parses a number of files
withParsedEl :: LangParser
             -> VectorOf FilePath ('S n)
             -> (forall kon (ki :: kon -> *) fam codes ix
                 . (LangCnstr ki fam codes ix , EqHO ki , ShowHO ki)
                => VectorOf (Fix ki codes ix) ('S n)
                -> IO res)
             -> ExceptT String IO res
withParsedEl (LangParser ext parser) vec f
  = do fs <- vecMapM (parseWithExt ext parser) vec
       liftIO $ f fs
  where
    parseWithExt :: (HasDatatypeInfo ki fam codes)
                 => String
                 -> (FilePath -> IO (Fix ki codes ix))
                 -> FilePath
                 -> ExceptT String IO (Fix ki codes ix)
    parseWithExt e p file
      | ("." ++ ext) `isSuffixOf` file = liftIO $ p file
      | otherwise
      = throwError ("Wrong Extension; expecting: " ++ show e ++ "\n")

-- |Tries a variety of parsers on a number of
--  files.
withParsedEls :: [LangParser]
              -> VectorOf FilePath ('S n)
              -> (forall kon (ki :: kon -> *) fam codes ix
                  . (LangCnstr ki fam codes ix , EqHO ki , ShowHO ki)
                 => VectorOf (Fix ki codes ix) ('S n)
                 -> IO res)
              -> ExceptT String IO res
withParsedEls []     _     _ = throwError "No parser succeeded\n"
withParsedEls (p:ps) files f = withParsedEl  p  files f
                           <|> withParsedEls ps files f


-- * Fixed interface for one, two and three files

redirectErr :: ExceptT String IO a -> IO a
redirectErr f = runExceptT f >>= either myerr return
  where
    myerr str = hPutStrLn stderr str
             >> exitWith (ExitFailure 10)
         
withParsed1 :: [LangParser]
            -> FilePath
            -> (forall kon (ki :: kon -> *) fam codes ix
                 . (LangCnstr ki fam codes ix , EqHO ki , ShowHO ki)
                => Fix ki codes ix
                -> IO res)
            -> IO res
withParsed1 parsers file f
  = redirectErr
  $ withParsedEls parsers (VS file V0)
  $ \(VS x V0) -> f x

withParsed2 :: [LangParser]
            -> FilePath -> FilePath
            -> (forall kon (ki :: kon -> *) fam codes ix
                 . (LangCnstr ki fam codes ix , EqHO ki , ShowHO ki)
                => Fix ki codes ix -> Fix ki codes ix
                -> IO res)
            -> IO res
withParsed2 parsers a b f
  = redirectErr
  $ withParsedEls parsers (VS a (VS b V0))
  $ \(VS x (VS y V0)) -> f x y
         
withParsed3 :: [LangParser]
            -> FilePath -> FilePath -> FilePath
            -> (forall kon (ki :: kon -> *) fam codes ix
                 . (LangCnstr ki fam codes ix , EqHO ki , ShowHO ki)
                => Fix ki codes ix -> Fix ki codes ix -> Fix ki codes ix
                -> IO res)
            -> IO res
withParsed3 parsers a b c f
  = redirectErr
  $ withParsedEls parsers (VS a (VS b (VS c V0)))
  $ \(VS x (VS y (VS z V0))) -> f x y z