packages feed

hydra-0.8.0: src/main/haskell/Hydra/Annotations.hs

-- | Functions for working with term and type annotations

module Hydra.Annotations where

import Hydra.Basics
import Hydra.Strip
import Hydra.Core
import Hydra.Compute
import Hydra.Extras
import Hydra.Graph
import Hydra.CoreDecoding
import Hydra.CoreEncoding
import Hydra.Mantle
import Hydra.Rewriting
import Hydra.Tier1
import Hydra.Tier2
import qualified Hydra.Dsl.Expect as Expect
import qualified Hydra.Dsl.Terms as Terms

import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Maybe as Y


key_classes = Name "classes"
key_description = Name "description"
key_type = Name "type"


aggregateAnnotations :: (x -> Maybe y) -> (y -> x) -> (y -> M.Map Name Term) -> x -> M.Map Name Term
aggregateAnnotations getValue getX getAnns t = M.fromList $ L.concat $ toPairs [] t
  where
    toPairs rest t = case getValue t of
      Nothing -> rest
      Just yy -> toPairs ((M.toList (getAnns yy)):rest) (getX yy)

failOnFlag :: Name -> String -> Flow s ()
failOnFlag flag msg = do
  val <- hasFlag flag
  if val
    then fail msg
    else pure ()

getAttr :: Name -> Flow s (Maybe Term)
getAttr key = Flow q
  where
    q s0 t0 = FlowState (Just $ M.lookup key $ traceOther t0) s0 t0

getAttrWithDefault :: Name -> Term -> Flow s Term
getAttrWithDefault key def = Y.fromMaybe def <$> getAttr key

getDescription :: M.Map Name Term -> Flow Graph (Y.Maybe String)
getDescription anns = case getAnnotation key_description anns of
  Nothing -> pure Nothing
  Just term -> case term of
    TermLiteral (LiteralString s) -> pure $ Just s
    _ -> fail $ "unexpected value for " ++ show key_description ++ ": " ++ show term

getTermAnnotation :: Name -> Term -> Y.Maybe Term
getTermAnnotation key = getAnnotation key . termAnnotationInternal

getTermDescription :: Term -> Flow Graph (Y.Maybe String)
getTermDescription = getDescription . termAnnotationInternal

getType :: M.Map Name Term -> Flow Graph (Y.Maybe Type)
getType anns = case getAnnotation key_type anns of
  Nothing -> pure Nothing
  Just dat -> Just <$> coreDecodeType dat

getTypeAnnotation :: Name -> Type -> Y.Maybe Term
getTypeAnnotation key = getAnnotation key . typeAnnotationInternal

getTypeClasses :: Type -> Flow Graph (M.Map Name (S.Set TypeClass))
getTypeClasses typ = case getTypeAnnotation key_classes typ of
    Nothing -> pure M.empty
    Just term -> Expect.map coreDecodeName (Expect.set decodeClass) term
  where
    decodeClass term = Expect.unitVariant _TypeClass term >>= \fn -> Y.maybe
      (unexpected "type class" $ show term) pure $ M.lookup fn byName
    byName = M.fromList [(_TypeClass_equality, TypeClassEquality), (_TypeClass_ordering, TypeClassOrdering)]

getTypeDescription :: Type -> Flow Graph (Y.Maybe String)
getTypeDescription = getDescription . typeAnnotationInternal

hasFlag :: Name -> Flow s Bool
hasFlag flag = getAttrWithDefault flag (Terms.boolean False) >>= Expect.boolean

-- | Return a zero-indexed counter for the given key: 0, 1, 2, ...
nextCount :: Name -> Flow s Int
nextCount attrName = do
  count <- getAttrWithDefault attrName (Terms.int32 0) >>= Expect.int32
  putAttr attrName (Terms.int32 $ count + 1)
  return count

resetCount :: Name -> Flow s ()
resetCount attrName = do
  putAttr attrName (Terms.int32 0)
  return ()

normalizeTermAnnotations :: Term -> Term
normalizeTermAnnotations term = if M.null anns
    then stripped
    else TermAnnotated $ AnnotatedTerm stripped anns
  where
    anns = termAnnotationInternal term
    stripped = stripTerm term

normalizeTypeAnnotations :: Type -> Type
normalizeTypeAnnotations typ = if M.null anns
    then stripped
    else TypeAnnotated $ AnnotatedType stripped anns
  where
    anns = typeAnnotationInternal typ
    stripped = stripType typ

putAttr :: Name -> Term -> Flow s ()
putAttr key val = Flow q
  where
    q s0 t0 = FlowState (Just ()) s0 (t0 {traceOther = M.insert key val $ traceOther t0})

setAnnotation :: Name -> Y.Maybe Term -> M.Map Name Term -> M.Map Name Term
setAnnotation key val m = M.alter (const val) key m

setDescription :: Y.Maybe String -> M.Map Name Term -> M.Map Name Term
setDescription d = setAnnotation key_description (Terms.string <$> d)

setTermAnnotation :: Name -> Y.Maybe Term -> Term -> Term
setTermAnnotation key val term = if anns == M.empty
    then term'
    else TermAnnotated $ AnnotatedTerm term' anns
  where
    term' = stripTerm term
    anns = setAnnotation key val $ termAnnotationInternal term

setTermDescription :: Y.Maybe String -> Term -> Term
setTermDescription d = setTermAnnotation key_description (Terms.string <$> d)

-- TODO: temporary. Move this function out of Annotations
setTermType :: Y.Maybe Type -> Term -> Term
setTermType mtyp term = case mtyp of
    Nothing -> withoutType term
    Just typ -> TermTyped $ TypedTerm (withoutType term) typ
  where
    withoutType term = case term of
      TermAnnotated (AnnotatedTerm term1 ann) -> TermAnnotated $ AnnotatedTerm (withoutType term1) ann
      TermTyped (TypedTerm term1 _) -> term1
      _ -> term

setType mt = setAnnotation key_type (coreEncodeType <$> mt)

setTypeAnnotation :: Name -> Y.Maybe Term -> Type -> Type
setTypeAnnotation key val typ = if anns == M.empty
    then typ'
    else TypeAnnotated $ AnnotatedType typ' anns
  where
    typ' = stripType typ
    anns = setAnnotation key val $ typeAnnotationInternal typ

setTypeClasses :: M.Map Name (S.Set TypeClass) -> Type -> Type
setTypeClasses m = setTypeAnnotation key_classes encoded
  where
    encoded = if M.null m
      then Nothing
      else Just $ Terms.map $ M.fromList (encodePair <$> M.toList m)
    encodePair (name, classes) = (coreEncodeName name, Terms.set $ S.fromList (encodeClass <$> (S.toList classes)))
    encodeClass tc = Terms.unitVariant _TypeClass $ case tc of
      TypeClassEquality -> _TypeClass_equality
      TypeClassOrdering -> _TypeClass_ordering

setTypeDescription :: Y.Maybe String -> Type -> Type
setTypeDescription d = setTypeAnnotation key_description (Terms.string <$> d)

termAnnotationInternal :: Term -> M.Map Name Term
termAnnotationInternal = aggregateAnnotations getAnn annotatedTermSubject annotatedTermAnnotation
  where
    getAnn t = case t of
      TermAnnotated a -> Just a
      TermTyped (TypedTerm t1 _) -> getAnn t1
      _ -> Nothing

typeAnnotationInternal :: Type -> M.Map Name Term
typeAnnotationInternal = aggregateAnnotations getAnn annotatedTypeSubject annotatedTypeAnnotation
  where
    getAnn t = case t of
      TypeAnnotated a -> Just a
      _ -> Nothing

whenFlag :: Name -> Flow s a -> Flow s a -> Flow s a
whenFlag flag fthen felse = do
  b <- hasFlag flag
  if b
    then fthen
    else felse

-- TODO: move out of Annotations and into Rewriting
unshadowVariables :: Term -> Term
unshadowVariables term = Y.fromJust $ flowStateValue $ unFlow (rewriteTermM rewrite term) (S.empty, M.empty) emptyTrace
  where
    rewrite recurse term = do
      (reserved, subst) <- getState
      case term of
        TermVariable v -> pure $ TermVariable $ Y.fromMaybe v $ M.lookup v subst
        TermFunction (FunctionLambda (Lambda v d body)) -> if S.member v reserved
          then do
            v' <- freshName
            putState (S.insert v' reserved, M.insert v v' subst)
            body' <- recurse body
            putState (reserved, subst)
            pure $ TermFunction $ FunctionLambda $ Lambda v' d body'
          else do
            putState (S.insert v reserved, subst)
            body' <- recurse body
            return $ TermFunction $ FunctionLambda $ Lambda v d body'
        _ -> recurse term
    freshName = (\n -> Name $ "s" ++ show n) <$> nextCount (Name "unshadow")