hydra-0.12.0: src/main/haskell/Hydra/Sources/Kernel/Terms/Annotations.hs
{-# LANGUAGE OverloadedStrings #-}
module Hydra.Sources.Kernel.Terms.Annotations where
-- Standard imports for term-level kernel modules
import Hydra.Kernel
import Hydra.Sources.Libraries
import qualified Hydra.Dsl.Accessors as Accessors
import qualified Hydra.Dsl.Ast as Ast
import qualified Hydra.Dsl.Coders as Coders
import qualified Hydra.Dsl.Compute as Compute
import qualified Hydra.Dsl.Core as Core
import qualified Hydra.Dsl.Grammar as Grammar
import qualified Hydra.Dsl.Graph as Graph
import qualified Hydra.Dsl.Json as Json
import qualified Hydra.Dsl.Lib.Chars as Chars
import qualified Hydra.Dsl.Lib.Equality as Equality
import qualified Hydra.Dsl.Lib.Flows as Flows
import qualified Hydra.Dsl.Lib.Lists as Lists
import qualified Hydra.Dsl.Lib.Literals as Literals
import qualified Hydra.Dsl.Lib.Logic as Logic
import qualified Hydra.Dsl.Lib.Maps as Maps
import qualified Hydra.Dsl.Lib.Math as Math
import qualified Hydra.Dsl.Lib.Optionals as Optionals
import Hydra.Dsl.Phantoms as Phantoms
import qualified Hydra.Dsl.Lib.Sets as Sets
import Hydra.Dsl.Lib.Strings as Strings
import qualified Hydra.Dsl.Mantle as Mantle
import qualified Hydra.Dsl.Module as Module
import qualified Hydra.Dsl.TTerms as TTerms
import qualified Hydra.Dsl.TTypes as TTypes
import qualified Hydra.Dsl.Terms as Terms
import qualified Hydra.Dsl.Topology as Topology
import qualified Hydra.Dsl.Types as Types
import qualified Hydra.Dsl.Typing as Typing
import Hydra.Sources.Kernel.Types.All
import Prelude hiding ((++))
import qualified Data.Int as I
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Maybe as Y
import qualified Hydra.Sources.Kernel.Terms.Constants as Constants
import qualified Hydra.Sources.Kernel.Terms.Decode.Core as DecodeCore
import qualified Hydra.Sources.Kernel.Terms.Decoding as Decoding
import qualified Hydra.Sources.Kernel.Terms.Encode.Core as EncodeCore
import qualified Hydra.Sources.Kernel.Terms.Extract.Core as ExtractCore
import qualified Hydra.Sources.Kernel.Terms.Lexical as Lexical
import qualified Hydra.Sources.Kernel.Terms.Monads as Monads
import qualified Hydra.Sources.Kernel.Terms.Rewriting as Rewriting
import qualified Hydra.Sources.Kernel.Terms.Show.Core as ShowCore
import qualified Hydra.Sources.Kernel.Terms.Variants as Variants
module_ :: Module
module_ = Module (Namespace "hydra.annotations") elements
[Decoding.module_, DecodeCore.module_, EncodeCore.module_, ExtractCore.module_, Lexical.module_, ShowCore.module_,
Variants.module_, Monads.module_]
kernelTypesModules $
Just "Utilities for reading and writing type and term annotations"
where
elements = [
el aggregateAnnotationsDef,
el debugIfDef,
el failOnFlagDef,
el getDebugIdDef,
el getAttrDef,
el getAttrWithDefaultDef,
el getCountDef,
el getDescriptionDef,
el getTermAnnotationDef,
el getTermDescriptionDef,
el getTypeDef,
el getTypeAnnotationDef,
el getTypeClassesDef,
el getTypeDescriptionDef,
el isNativeTypeDef,
el hasDescriptionDef,
el hasFlagDef,
el hasTypeDescriptionDef,
el nextCountDef,
el normalizeTermAnnotationsDef,
el normalizeTypeAnnotationsDef,
el putAttrDef,
el putCountDef,
el resetCountDef,
el setAnnotationDef,
el setDescriptionDef,
el setTermAnnotationDef,
el setTermDescriptionDef,
el setTypeDef,
el setTypeAnnotationDef,
el setTypeClassesDef,
el setTypeDescriptionDef,
el termAnnotationInternalDef,
el typeAnnotationInternalDef,
el typeElementDef,
el whenFlagDef,
el unshadowVariablesDef,
el withDepthDef]
define :: String -> TTerm a -> TBinding a
define = definitionInModule module_
aggregateAnnotationsDef :: TBinding ((x -> Maybe y) -> (y -> x) -> (y -> M.Map Name Term) -> x -> M.Map Name Term)
aggregateAnnotationsDef = define "aggregateAnnotations" $
doc "Aggregate annotations from nested structures" $
lambdas ["getValue", "getX", "getAnns", "t"] $ lets [
"toPairs">: lambdas ["rest", "t"] $ Optionals.maybe (var "rest")
(lambda "yy" $ var "toPairs"
@@ Lists.cons (Maps.toList $ var "getAnns" @@ var "yy") (var "rest")
@@ (var "getX" @@ var "yy"))
(var "getValue" @@ var "t")] $
Maps.fromList $ Lists.concat $ var "toPairs" @@ list [] @@ var "t"
debugIfDef :: TBinding (String -> String -> Flow s ())
debugIfDef = define "debugIf" $
doc "Debug if the debug ID matches" $
lambdas ["debugId", "message"] $ lets [
"checkAndFail">: lambda "desc" $ Logic.ifElse
(Equality.equal (var "desc") (just $ string "debugId"))
(Flows.fail $ var "message")
(produce unit)] $
Flows.bind (ref getDebugIdDef) (var "checkAndFail")
failOnFlagDef :: TBinding (Name -> String -> Flow s ())
failOnFlagDef = define "failOnFlag" $
doc "Fail if the given flag is set" $
lambdas ["flag", "msg"] $ binds [
"val">: ref hasFlagDef @@ var "flag"] $
Logic.ifElse (var "val")
(Flows.fail $ var "msg")
(produce unit)
getAttrDef :: TBinding (Name -> Flow s (Maybe Term))
getAttrDef = define "getAttr" $
doc "Get an attribute from the trace" $
lambda "key" $ Compute.flow $
lambdas ["s0", "t0"] $ Compute.flowState
(just $ Maps.lookup (var "key") (Compute.traceOther $ var "t0"))
(var "s0")
(var "t0")
getAttrWithDefaultDef :: TBinding (Name -> Term -> Flow s Term)
getAttrWithDefaultDef = define "getAttrWithDefault" $
doc "Get an attribute with a default value" $
lambdas ["key", "def"] $ Flows.map
(lambda "mval" $ Optionals.fromMaybe (var "def") (var "mval"))
(ref getAttrDef @@ var "key")
getCountDef :: TBinding (Name -> Flow s Int)
getCountDef = define "getCount" $
doc "Get a counter value" $
lambda "key" $ ref Lexical.withEmptyGraphDef @@ (Flows.bind
(ref getAttrWithDefaultDef @@ var "key" @@ (Core.int32 0))
(ref ExtractCore.int32Def))
getDebugIdDef :: TBinding (Flow s (Maybe String))
getDebugIdDef = define "getDebugId" $
doc "Get the debug ID from flow state" $
ref Lexical.withEmptyGraphDef @@ (Flows.bind
(ref getAttrDef @@ ref Constants.key_debugIdDef)
(lambda "desc" $ Flows.mapOptional (ref ExtractCore.stringDef) (var "desc")))
getDescriptionDef :: TBinding (M.Map Name Term -> Flow Graph (Maybe String))
getDescriptionDef = define "getDescription" $
doc "Get description from annotations map" $
lambda "anns" $ Optionals.maybe
(produce nothing)
(lambda "term" $ Flows.map (unaryFunction just) $ ref ExtractCore.stringDef @@ var "term")
(Maps.lookup (Core.nameLift key_description) (var "anns"))
getTermAnnotationDef :: TBinding (Name -> Term -> Maybe Term)
getTermAnnotationDef = define "getTermAnnotation" $
doc "Get a term annotation" $
lambdas ["key", "term"] $ Maps.lookup (var "key") (ref termAnnotationInternalDef @@ var "term")
getTermDescriptionDef :: TBinding (Term -> Flow Graph (Maybe String))
getTermDescriptionDef = define "getTermDescription" $
doc "Get term description" $
lambda "term" $ ref getDescriptionDef @@ (ref termAnnotationInternalDef @@ var "term")
getTypeDef :: TBinding (M.Map Name Term -> Flow Graph (Maybe Type))
getTypeDef = define "getType" $
doc "Get type from annotations" $
lambda "anns" $ Optionals.maybe
(produce nothing)
(lambda "dat" $ Flows.map (unaryFunction just) (ref DecodeCore.typeDef @@ var "dat"))
(Maps.lookup (ref Constants.key_typeDef) (var "anns"))
getTypeAnnotationDef :: TBinding (Name -> Type -> Maybe Term)
getTypeAnnotationDef = define "getTypeAnnotation" $
doc "Get a type annotation" $
lambdas ["key", "typ"] $ Maps.lookup (var "key") (ref typeAnnotationInternalDef @@ var "typ")
getTypeClassesDef :: TBinding (Term -> Flow Graph (M.Map Name (S.Set TypeClass)))
getTypeClassesDef = define "getTypeClasses" $
doc "Get type classes from term" $
lambda "term" $ lets [
"decodeClass">: lambda "term" $ lets [
"byName">: Maps.fromList $ list [
pair (Core.nameLift _TypeClass_equality) Graph.typeClassEquality,
pair (Core.nameLift _TypeClass_ordering) Graph.typeClassOrdering]] $ binds [
"fn">: ref ExtractCore.unitVariantDef @@ Core.nameLift _TypeClass @@ var "term"] $
Optionals.maybe
(ref Monads.unexpectedDef @@ string "type class" @@ (ref ShowCore.termDef @@ var "term"))
(unaryFunction produce)
(Maps.lookup (var "fn") (var "byName"))] $
Optionals.maybe
(produce Maps.empty)
(lambda "term" $ ref ExtractCore.mapDef
@@ (ref DecodeCore.nameDef)
@@ (ref ExtractCore.setOfDef @@ var "decodeClass")
@@ (var "term"))
(ref getTermAnnotationDef @@ ref Constants.key_classesDef @@ var "term")
getTypeDescriptionDef :: TBinding (Type -> Flow Graph (Maybe String))
getTypeDescriptionDef = define "getTypeDescription" $
doc "Get type description" $
lambda "typ" $ ref getDescriptionDef @@ (ref typeAnnotationInternalDef @@ var "typ")
isNativeTypeDef :: TBinding (Binding -> Bool)
isNativeTypeDef = define "isNativeType" $
doc ("For a typed term, decide whether a coder should encode it as a native type expression,"
<> " or as a Hydra type expression.") $
lambda "el" $ lets [
"isFlaggedAsFirstClassType">: Optionals.fromMaybe false $
Optionals.bind
(ref getTermAnnotationDef @@ ref Constants.key_firstClassTypeDef @@ (Core.bindingTerm $ var "el"))
(ref Decoding.booleanDef)] $
Optionals.maybe false
(lambda "ts" $ Logic.and
(Equality.equal (var "ts") (Core.typeScheme (list []) (Core.typeVariable $ Core.nameLift _Type)))
(Logic.not $ var "isFlaggedAsFirstClassType"))
(Core.bindingType $ var "el")
hasDescriptionDef :: TBinding (M.Map Name Term -> Bool)
hasDescriptionDef = define "hasDescription" $
doc "Check if annotations contain description" $
lambda "anns" $ Optionals.isJust $ Maps.lookup (ref Constants.key_descriptionDef) (var "anns")
hasFlagDef :: TBinding (Name -> Flow s Bool)
hasFlagDef = define "hasFlag" $
doc "Check if flag is set" $
lambda "flag" $ ref Lexical.withEmptyGraphDef
@@ (bind "term" (ref getAttrWithDefaultDef @@ var "flag" @@ Core.false) $
ref ExtractCore.booleanDef @@ var "term")
hasTypeDescriptionDef :: TBinding (Type -> Bool)
hasTypeDescriptionDef = define "hasTypeDescription" $
doc "Check if type has description" $
lambda "typ" $ ref hasDescriptionDef @@ (ref typeAnnotationInternalDef @@ var "typ")
nextCountDef :: TBinding (Name -> Flow s Int)
nextCountDef = define "nextCount" $
doc "Return a zero-indexed counter for the given key: 0, 1, 2, ..." $
lambda "key" $ binds [
"count">: ref getCountDef @@ var "key"] $
Flows.map
(constant $ var "count")
(ref putCountDef @@ var "key" @@ Math.add (var "count") (int32 1))
-- TODO: move into hydra.rewriting
normalizeTermAnnotationsDef :: TBinding (Term -> Term)
normalizeTermAnnotationsDef = define "normalizeTermAnnotations" $
doc "Normalize term annotations" $
lambda "term" $ lets [
"anns">: ref termAnnotationInternalDef @@ var "term",
"stripped">: ref Rewriting.deannotateTermDef @@ var "term"] $
Logic.ifElse (Maps.null $ var "anns")
(var "stripped")
(Core.termAnnotated $ Core.annotatedTerm (var "stripped") (var "anns"))
-- TODO: move into hydra.rewriting
normalizeTypeAnnotationsDef :: TBinding (Type -> Type)
normalizeTypeAnnotationsDef = define "normalizeTypeAnnotations" $
doc "Normalize type annotations" $
lambda "typ" $ lets [
"anns">: ref typeAnnotationInternalDef @@ var "typ",
"stripped">: ref Rewriting.deannotateTypeDef @@ var "typ"] $
Logic.ifElse (Maps.null $ var "anns")
(var "stripped")
(Core.typeAnnotated $ Core.annotatedType (var "stripped") (var "anns"))
putAttrDef :: TBinding (Name -> Term -> Flow s ())
putAttrDef = define "putAttr" $
doc "Set an attribute in the trace" $
lambdas ["key", "val"] $ Compute.flow $ lambdas ["s0", "t0"] $
Compute.flowState
(just unit)
(var "s0")
(Compute.traceWithOther (var "t0") (Maps.insert (var "key") (var "val") (Compute.traceOther $ var "t0")))
putCountDef :: TBinding (Name -> Int -> Flow s ())
putCountDef = define "putCount" $
doc "Set counter value" $
lambdas ["key", "count"] $
ref putAttrDef @@ var "key" @@ (Core.termLiteral $ Core.literalInteger $ Core.integerValueInt32 $ var "count")
resetCountDef :: TBinding (Name -> Flow s ())
resetCountDef = define "resetCount" $
doc "Reset counter to zero" $
lambda "key" $ ref putAttrDef @@ var "key" @@ TTerms.int32 0
setAnnotationDef :: TBinding (Name -> Maybe Term -> M.Map Name Term -> M.Map Name Term)
setAnnotationDef = define "setAnnotation" $
doc "Set annotation in map" $
lambdas ["key", "val", "m"] $ Maps.alter (constant $ var "val") (var "key") (var "m")
setDescriptionDef :: TBinding (Maybe String -> M.Map Name Term -> M.Map Name Term)
setDescriptionDef = define "setDescription" $
doc "Set description in annotations" $
lambda "d" $ ref setAnnotationDef
@@ ref Constants.key_descriptionDef
@@ Optionals.map (unaryFunction Core.termLiteral <.> unaryFunction Core.literalString) (var "d")
setTermAnnotationDef :: TBinding (Name -> Maybe Term -> Term -> Term)
setTermAnnotationDef = define "setTermAnnotation" $
doc "Set term annotation" $
lambdas ["key", "val", "term"] $ lets [
"term'">: ref Rewriting.deannotateTermDef @@ var "term",
"anns">: ref setAnnotationDef @@ var "key" @@ var "val" @@ (ref termAnnotationInternalDef @@ var "term")] $
Logic.ifElse (Maps.null $ var "anns")
(var "term'")
(Core.termAnnotated $ Core.annotatedTerm (var "term'") (var "anns"))
setTermDescriptionDef :: TBinding (Maybe String -> Term -> Term)
setTermDescriptionDef = define "setTermDescription" $
doc "Set term description" $
lambda "d" $ ref setTermAnnotationDef
@@ ref Constants.key_descriptionDef
@@ Optionals.map (lambda "s" $ Core.termLiteral $ Core.literalString $ var "s") (var "d")
setTypeDef :: TBinding (Maybe Type -> M.Map Name Term -> M.Map Name Term)
setTypeDef = define "setType" $
doc "Set type in annotations" $
lambda "mt" $ ref setAnnotationDef @@ ref Constants.key_typeDef @@ Optionals.map (ref EncodeCore.typeDef) (var "mt")
setTypeAnnotationDef :: TBinding (Name -> Maybe Term -> Type -> Type)
setTypeAnnotationDef = define "setTypeAnnotation" $
doc "Set type annotation" $
lambdas ["key", "val", "typ"] $ lets [
"typ'">: ref Rewriting.deannotateTypeDef @@ var "typ",
"anns">: ref setAnnotationDef @@ var "key" @@ var "val" @@ (ref typeAnnotationInternalDef @@ var "typ")] $
Logic.ifElse (Maps.null (var "anns"))
(var "typ'")
(Core.typeAnnotated $ Core.annotatedType (var "typ'") (var "anns"))
setTypeClassesDef :: TBinding (M.Map Name (S.Set TypeClass) -> Term -> Term)
setTypeClassesDef = define "setTypeClasses" $
doc "Set type classes on term" $
lambda "m" $ lets [
"encodeClass">: lambda "tc" $ cases _TypeClass (var "tc") Nothing [
_TypeClass_equality>>: constant $ TTerms.unitVariantPhantom _TypeClass _TypeClass_equality,
_TypeClass_ordering>>: constant $ TTerms.unitVariantPhantom _TypeClass _TypeClass_ordering],
"encodePair">: lambda "nameClasses" $ lets [
"name">: first $ var "nameClasses",
"classes">: second $ var "nameClasses"] $
pair
(ref EncodeCore.nameDef @@ var "name")
(Core.termSet $ Sets.fromList $ Lists.map (var "encodeClass") $ Sets.toList $ var "classes"),
"encoded">: Logic.ifElse (Maps.null $ var "m")
nothing
(just $ Core.termMap $ Maps.fromList $ Lists.map (var "encodePair") $ Maps.toList $ var "m")]
$ ref setTermAnnotationDef @@ ref Constants.key_classesDef @@ var "encoded"
setTypeDescriptionDef :: TBinding (Maybe String -> Type -> Type)
setTypeDescriptionDef = define "setTypeDescription" $
doc "Set type description" $
lambda "d" $ ref setTypeAnnotationDef
@@ ref Constants.key_descriptionDef
@@ Optionals.map (unaryFunction Core.termLiteral <.> unaryFunction Core.literalString) (var "d")
termAnnotationInternalDef :: TBinding (Term -> M.Map Name Term)
termAnnotationInternalDef = define "termAnnotationInternal" $
doc "Get internal term annotations" $
lets [
"getAnn">: lambda "t" $ cases _Term (var "t")
(Just nothing) [
_Term_annotated>>: lambda "a" $ just $ var "a"]] $
ref aggregateAnnotationsDef @@ var "getAnn" @@ (unaryFunction Core.annotatedTermSubject) @@ (unaryFunction Core.annotatedTermAnnotation)
typeAnnotationInternalDef :: TBinding (Type -> M.Map Name Term)
typeAnnotationInternalDef = define "typeAnnotationInternal" $
doc "Get internal type annotations" $ lets [
"getAnn">: lambda "t" $ cases _Type (var "t")
(Just nothing) [
_Type_annotated>>: lambda "a" $ just $ var "a"]] $
ref aggregateAnnotationsDef @@ var "getAnn" @@ (unaryFunction Core.annotatedTypeSubject) @@ (unaryFunction Core.annotatedTypeAnnotation)
-- TODO: deprecate
typeElementDef :: TBinding (Name -> Type -> Binding)
typeElementDef = define "typeElement" $
doc "Create a type element with proper annotations" $
lambdas ["name", "typ"] $ lets [
"schemaTerm">: Core.termVariable (Core.nameLift _Type),
"dataTerm">: ref normalizeTermAnnotationsDef @@ (Core.termAnnotated $ Core.annotatedTerm
(ref EncodeCore.typeDef @@ var "typ")
(Maps.fromList $ list [pair (ref Constants.key_typeDef) (var "schemaTerm")]))] $
Core.binding (var "name") (var "dataTerm") (just $ Core.typeScheme (list []) (var "typ"))
whenFlagDef :: TBinding (Name -> Flow s a -> Flow s a -> Flow s a)
whenFlagDef = define "whenFlag" $
doc "Execute different flows based on flag" $
lambdas ["flag", "fthen", "felse"] $ binds [
"b">: ref hasFlagDef @@ var "flag"] $
Logic.ifElse (var "b") (var "fthen") (var "felse")
-- TODO: move into hydra.rewriting
unshadowVariablesDef :: TBinding (Term -> Term)
unshadowVariablesDef = define "unshadowVariables" $
doc "Unshadow variables in term" $
lambda "term" $ lets [
"freshName">: Flows.map (lambda "n" $ Core.name $ Strings.cat2 (string "s") (Literals.showInt32 $ var "n")) $
ref nextCountDef @@ Core.name (string "unshadow"),
"rewrite">: lambdas ["recurse", "term"] $ lets [
"handleOther">: var "recurse" @@ var "term"] $ binds [
"state">: ref Monads.getStateDef] $ lets [
"reserved">: first $ var "state",
"subst">: second $ var "state"] $
cases _Term (var "term")
(Just $ var "handleOther") [
_Term_variable>>: lambda "v" $ produce $ Core.termVariable $
Optionals.fromMaybe (var "v") (Maps.lookup (var "v") (var "subst")),
_Term_function>>: lambda "f" $
cases _Function (var "f")
(Just $ var "handleOther") [
_Function_lambda>>: lambda "l" $ lets [
"v">: Core.lambdaParameter $ var "l",
"d">: Core.lambdaDomain $ var "l",
"body">: Core.lambdaBody $ var "l"] $
Logic.ifElse (Sets.member (var "v")(var "reserved"))
( bind "v'" (var "freshName") $
exec (ref Monads.putStateDef @@ pair
(Sets.insert (var "v'") (var "reserved"))
(Maps.insert (var "v") (var "v'") (var "subst"))) $
bind "body'" (var "recurse" @@ var "body") $
exec (ref Monads.putStateDef @@ var "state") $
produce $ Core.termFunction $ Core.functionLambda $ Core.lambda (var "v'") (var "d") (var "body'"))
( exec (ref Monads.putStateDef @@ pair (Sets.insert (var "v") (var "reserved")) (var "subst")) $
Flows.map
(lambda "body'" $ Core.termFunction $ Core.functionLambda $ Core.lambda (var "v") (var "d") (var "body'"))
(var "recurse" @@ var "body"))]]] $
Optionals.fromJust $ Compute.flowStateValue $ Compute.unFlow
(ref Rewriting.rewriteTermMDef @@ var "rewrite" @@ var "term")
(pair Sets.empty Maps.empty)
(ref Monads.emptyTraceDef)
withDepthDef :: TBinding (Name -> (Int -> Flow s a) -> Flow s a)
withDepthDef = define "withDepth" $
doc ("Provide an one-indexed, integer-valued 'depth' to a flow, where the depth is the number of nested calls."
<> " This is useful for generating variable names while avoiding conflicts between the variables of parents and children."
<> " E.g. a variable in an outer case/match statement might be \"v1\", whereas the variable of another case/match statement"
<> " inside of the first one becomes \"v2\". See also nextCount.") $
lambdas ["key", "f"] $ binds [
"count">: ref getCountDef @@ var "key"] $ lets [
"inc">: Math.add (var "count") (int32 1)] $
exec (ref putCountDef @@ var "key" @@ var "inc") $ binds [
"r">: var "f" @@ var "inc"] $
exec (ref putCountDef @@ var "key" @@ var "count") $
produce $ var "r"