packages feed

chr-lang-0.1.0.1: src/CHR/Language/Generic/AST.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

-------------------------------------------------------------------------------------------
--- Generic terms describing constraints, providing interpretation to AST of your choice
-------------------------------------------------------------------------------------------

module CHR.Language.Generic.AST
  ( TmOp
  , TmMk(..)
  , TmIs(..)
  , TmValMk(..)
  
  , GTm(..)
  
  , GTermAs(..)
  , GTermAsTm(..)
  
  , GTermAsM
  
  , GTermAsState(..)
  , emptyGTermAsState
  
  , gtermasVar
  
  , gtermasFail
  )
  where

import           Data.Char
import           Data.Typeable
import           Data.Proxy
import           GHC.Generics
import           Control.Monad.Except
import           Control.Monad.State
-- import qualified Data.Map                   as Map
import qualified Data.HashMap.Strict        as MapH

import           CHR.Pretty                 as PP
import           CHR.Utils
import           CHR.Data.Lens
import           CHR.Types
import qualified CHR.Data.Lookup            as Lk
import qualified Data.List                  as List
-- import           CHR.Types.Core

-------------------------------------------------------------------------------------------
--- Supporting types
-------------------------------------------------------------------------------------------

data GTermAsState
  = GTermAsState
      { _gtermasNmToVarMp       :: NmToVarMp
      }

emptyGTermAsState :: GTermAsState
emptyGTermAsState = GTermAsState emptyNmToVarMp
      
-------------------------------------------------------------------------------------------
--- Lens
-------------------------------------------------------------------------------------------

mkLabel ''GTermAsState

-------------------------------------------------------------------------------------------
--- Term language/AST
-------------------------------------------------------------------------------------------

-- | Terms
data GTm
  = GTm_Var     String                  -- ^ variable (to be substituted)
  | GTm_Int     Integer                 -- ^ int value (for arithmetic)
  | GTm_Str     String                  -- ^ string value
  | GTm_Con     String [GTm]            -- ^ general term structure
  | GTm_Nil                             -- ^ special case: list nil
  | GTm_Cns     GTm GTm                 -- ^ special case: list cons
  deriving (Show, Eq, Ord, Typeable, Generic)

instance PP GTm where
  pp (GTm_Var v        ) = pp v -- "v" >|< v
  pp (GTm_Con c []     ) = pp c
  pp (GTm_Con c@(h:_) [a1,a2])
    | not (isAlpha h)    = ppParens $ a1 >#< c >#< a2
  pp (GTm_Con c as     ) = ppParens $ c >#< ppSpaces as
  pp (GTm_Nil          ) = pp "[]"
  pp (GTm_Cns h t      ) = "[" >|< h >#< ":" >#< t >|< "]"
  pp (GTm_Int i        ) = pp i
  pp (GTm_Str s        ) = pp $ show s

-------------------------------------------------------------------------------------------
--- Term interpretation in context of CHR
-------------------------------------------------------------------------------------------

type family TmOp tm :: *

class TmMk tm where
  tmUnaryOps  :: Proxy tm -> [(String, TmOp tm)]
  tmBinaryOps :: Proxy tm -> [(String, TmOp tm)]
  
  mkTmBool :: Bool -> tm
  mkTmVar  :: String -> tm
  mkTmStr  :: String -> tm
  mkTmInt  :: Integer -> tm
  mkTmCon  :: String -> [tm] -> tm
  mkTmLst  :: [tm] -> Maybe tm -> tm
  
  mkTmUnaryOp   :: TmOp tm -> tm -> tm
  mkTmBinaryOp  :: TmOp tm -> tm -> tm -> tm

class TmIs tm where
  isTmInt :: tm -> Maybe Int
  isTmBool :: tm -> Maybe Bool
  -- tmValMkInt :: Int -> tm

class TmValMk tm where
  valTmMkInt :: Int -> tm

-- | Interpretation monad, which is partial
type GTermAsM = StateT GTermAsState (Either PP_Doc)

class TmMk tm => GTermAsTm tm where
  asTm :: GTm -> GTermAsM tm
  asTm t = case t of
    GTm_Con "True" [] -> return $ mkTmBool True
    GTm_Con "False" [] -> return $ mkTmBool False
    GTm_Con o [a]
      | Just o' <- List.lookup o (tmUnaryOps (Proxy :: Proxy tm))-> do
        a <- asTm a
        return $ mkTmUnaryOp o' a
    GTm_Con o [a,b]
      | Just o' <- List.lookup o (tmBinaryOps (Proxy :: Proxy tm)) -> do
        a <- asTm a
        b <- asTm b
        return $ mkTmBinaryOp o' a b
    GTm_Con c a -> forM a asTm >>= (return . mkTmCon c)
    GTm_Var v -> -- Tm_Var <$> gtermasVar v
                 return $ mkTmVar v
    GTm_Str v -> return $ mkTmStr v
    GTm_Int i -> return $ mkTmInt i
    GTm_Nil   -> return $ mkTmLst [] Nothing
    t@(GTm_Cns _ _) -> asTmList t >>= (return . uncurry mkTmLst)
    -- t -> gtermasFail t "not a term"

  -- | as list, if matches/possible. Only to be invoked for GTm_Cns 
  asTmList :: GTm -> GTermAsM ([tm], Maybe tm)
  asTmList (GTm_Cns h    GTm_Nil     ) = asTm h >>= \h -> return ([h], Nothing)
  asTmList (GTm_Cns h t@(GTm_Cns _ _)) = asTm h >>= \h -> asTmList t >>= \(t,mt) -> return ((h:t),mt)
  asTmList (GTm_Cns h t              ) = asTm h >>= \h -> asTm     t >>= \t -> return ([h], Just t)
  asTmList _                           = panic "GTermAs.asTmList: should not happen, not intended to be called with non GTm_Cns"

instance {-# OVERLAPPABLE #-} TmMk tm => GTermAsTm tm where

-- | Term interpretation in context of CHR
class GTermAs cnstr guard bprio prio
  | cnstr -> guard bprio prio
  , guard -> cnstr bprio prio 
  , bprio -> cnstr guard prio 
  , prio -> cnstr guard bprio 
  where
  --
  asHeadConstraint :: GTm -> GTermAsM cnstr
  --
  asBodyConstraint :: GTm -> GTermAsM cnstr
  --
  asGuard :: GTm -> GTermAsM guard
  --
  asHeadBacktrackPrio :: GTm -> GTermAsM bprio
  --
  asAltBacktrackPrio :: GTm -> GTermAsM bprio
  --
  asRulePrio :: GTm -> GTermAsM prio

--
gtermasVar :: String -> GTermAsM IVar
gtermasVar s = modL gtermasNmToVarMp $ \m -> maybe (let i = Lk.size m in (i, Lk.insert s i m)) (,m) $ Lk.lookup s m
  
  -- insertLookupWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> (Maybe a, Map k a)

-- | Fail the interpretation
gtermasFail :: GTm -> String -> GTermAsM a
gtermasFail t m = throwError $ "GTerm interpretation failure" >-< indent 2 ("why :" >#< m >-< "term:" >#< t)