packages feed

hydra-0.12.0: src/main/haskell/Hydra/Sources/Kernel/Terms/Adapt/Simple.hs

{-# LANGUAGE OverloadedStrings #-}

module Hydra.Sources.Kernel.Terms.Adapt.Simple 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.Decode.Core as DecodeCore
import qualified Hydra.Sources.Kernel.Terms.Inference as Inference
import qualified Hydra.Sources.Kernel.Terms.Literals as Lits
import qualified Hydra.Sources.Kernel.Terms.Monads as Monads
import qualified Hydra.Sources.Kernel.Terms.Reduction as Reduction
import qualified Hydra.Sources.Kernel.Terms.Rewriting as Rewriting
import qualified Hydra.Sources.Kernel.Terms.Schemas as Schemas
import qualified Hydra.Sources.Kernel.Terms.Show.Core as ShowCore
import qualified Hydra.Sources.Kernel.Terms.Show.Graph as ShowGraph
import qualified Hydra.Sources.Kernel.Terms.Variants as Variants


module_ :: Module
module_ = Module (Namespace "hydra.adapt.simple") elements
    [DecodeCore.module_, Inference.module_, Lits.module_, Monads.module_, Reduction.module_, Rewriting.module_, Schemas.module_,
      ShowCore.module_, ShowGraph.module_, Variants.module_]
    kernelTypesModules $
    Just "Simple, one-way adapters for types and terms"
  where
    elements = [
      el adaptFloatTypeDef,
      el adaptDataGraphDef,
      el adaptGraphSchemaDef,
      el adaptIntegerTypeDef,
      el adaptLiteralDef,
      el adaptLiteralTypeDef,
      el adaptLiteralTypesMapDef,
      el adaptLiteralValueDef,
      el adaptPrimitiveDef,
      el adaptTermDef,
      el adaptTypeDef,
      el adaptTypeSchemeDef,
      el dataGraphToDefinitionsDef,
      el literalTypeSupportedDef,
      el schemaGraphToDefinitionsDef,
      el termAlternativesDef,
      el typeAlternativesDef]

define :: String -> TTerm a -> TBinding a
define = definitionInModule module_

adaptFloatTypeDef :: TBinding (LanguageConstraints -> FloatType -> Maybe FloatType)
adaptFloatTypeDef = define "adaptFloatType" $
  doc "Attempt to adapt a floating-point type using the given language constraints" $
  "constraints" ~> "ft" ~>
  "supported" <~ Sets.member (var "ft") (Coders.languageConstraintsFloatTypes $ var "constraints") $
  "alt" <~ (ref adaptFloatTypeDef @@ var "constraints") $
  Logic.ifElse (var "supported")
    (just $ var "ft")
    (cases _FloatType (var "ft")
      Nothing [
      _FloatType_bigfloat>>: constant nothing,
      _FloatType_float32>>: constant $ var "alt" @@ Core.floatTypeFloat64,
      _FloatType_float64>>: constant $ var "alt" @@ Core.floatTypeBigfloat])

adaptDataGraphDef :: TBinding (LanguageConstraints -> Bool -> Graph -> Flow s Graph)
adaptDataGraphDef = define "adaptDataGraph" $
  doc "Adapt a graph and its schema to the given language constraints, prior to inference" $
  "constraints" ~> "doExpand" ~> "graph0" ~>
  "litmap" <~ ref adaptLiteralTypesMapDef @@ var "constraints" $
  "els0" <~ Graph.graphElements (var "graph0") $
  "env0" <~ Graph.graphEnvironment (var "graph0") $
  "body0" <~ Graph.graphBody (var "graph0") $
  "prims0" <~ Graph.graphPrimitives (var "graph0") $
  "schema0" <~ Graph.graphSchema (var "graph0") $
  "schema1" <<~ optCases (var "schema0")
    (produce nothing)
--    ("sg" ~> Flows.fail $ "schema graph: " ++ (ref ShowGraph.graphDef @@ var "sg")) $
    ( "sg" ~>
      "tmap0" <<~ ref Schemas.graphAsTypesDef @@ var "sg" $
      "tmap1" <<~ ref adaptGraphSchemaDef @@ var "constraints" @@ var "litmap" @@ var "tmap0" $
      "emap" <~ ref Schemas.typesToElementsDef @@ var "tmap1" $
      produce $ just $ Graph.graphWithElements (var "sg") (var "emap")) $
  "gterm0" <~ ref Schemas.graphAsTermDef @@ var "graph0" $
  "gterm1" <~ Logic.ifElse (var "doExpand")
    (ref Reduction.expandLambdasDef @@ var "graph0" @@ var "gterm0")
    (var "gterm0") $
  "gterm2" <<~ ref adaptTermDef @@ var "constraints" @@ var "litmap" @@ var "gterm1" $
  "els1" <~ ref Schemas.termAsGraphDef @@ var "gterm2" $
  "prims1" <<~ Flows.mapElems (ref adaptPrimitiveDef @@ var "constraints" @@ var "litmap") (var "prims0") $
--  Flows.fail $ "adapted data graph: " ++ (ref ShowCore.termDef @@ var "gterm2")
--  Flows.fail $ "schema graph: " ++ (optCases (var "schema1")
--    ("none")
--    ("sg" ~> ref ShowGraph.graphDef @@ var "sg"))
  produce $ Graph.graph
    (var "els1")
    (var "env0")
    Maps.empty
    Core.termUnit
    (var "prims1")
    (var "schema1")

adaptGraphSchemaDef :: TBinding (LanguageConstraints -> M.Map LiteralType LiteralType -> M.Map Name Type -> Flow s (M.Map Name Type))
adaptGraphSchemaDef = define "adaptGraphSchema" $
  doc "Adapt a schema graph to the given language constraints" $
  "constraints" ~> "litmap" ~> "types0" ~>
  "mapPair" <~ ("pair" ~>
    "name" <~ first (var "pair") $
    "typ" <~ second (var "pair") $
    "typ1" <<~ ref adaptTypeDef @@ var "constraints" @@ var "litmap" @@ var "typ" $
    produce $ pair (var "name") (var "typ1")) $
  "pairs" <<~ Flows.mapList (var "mapPair") (Maps.toList $ var "types0") $
  produce $ Maps.fromList (var "pairs")

adaptIntegerTypeDef :: TBinding (LanguageConstraints -> IntegerType -> Maybe IntegerType)
adaptIntegerTypeDef = define "adaptIntegerType" $
  doc "Attempt to adapt an integer type using the given language constraints" $
  "constraints" ~> "it" ~>
  "supported" <~ Sets.member (var "it") (Coders.languageConstraintsIntegerTypes $ var "constraints") $
  "alt" <~ (ref adaptIntegerTypeDef @@ var "constraints") $
  Logic.ifElse (var "supported")
    (just $ var "it")
    (cases _IntegerType (var "it")
      Nothing [
      _IntegerType_bigint>>: constant nothing,
      _IntegerType_int8>>: constant $ var "alt" @@ Core.integerTypeUint16,
      _IntegerType_int16>>: constant $ var "alt" @@ Core.integerTypeUint32,
      _IntegerType_int32>>: constant $ var "alt" @@ Core.integerTypeUint64,
      _IntegerType_int64>>: constant $ var "alt" @@ Core.integerTypeBigint,
      _IntegerType_uint8>>: constant $ var "alt" @@ Core.integerTypeInt16,
      _IntegerType_uint16>>: constant $ var "alt" @@ Core.integerTypeInt32,
      _IntegerType_uint32>>: constant $ var "alt" @@ Core.integerTypeInt64,
      _IntegerType_uint64>>: constant $ var "alt" @@ Core.integerTypeBigint])

adaptLiteralDef :: TBinding (LiteralType -> Literal -> Literal)
adaptLiteralDef = define "adaptLiteral" $
  doc "Convert a literal to a different type" $
  "lt" ~> "l" ~>
  cases _Literal (var "l")
    Nothing [
    _Literal_binary>>: "b" ~> cases _LiteralType (var "lt")
      Nothing [
      _LiteralType_string>>: constant $ Core.literalString $ Literals.binaryToString $ var "b"],
    _Literal_boolean>>: "b" ~> cases _LiteralType (var "lt")
      Nothing [
      _LiteralType_integer>>: "it" ~> Core.literalInteger $
        ref Lits.bigintToIntegerValueDef @@ var "it" @@ Logic.ifElse (var "b") (bigint 1) (bigint 0)],
    _Literal_float>>: "f" ~> cases _LiteralType (var "lt")
      Nothing [
      _LiteralType_float>>: "ft" ~> Core.literalFloat $
        ref Lits.bigfloatToFloatValueDef @@ var "ft" @@ (ref Lits.floatValueToBigfloatDef @@ var "f")],
    _Literal_integer>>: "i" ~> cases _LiteralType (var "lt")
      Nothing [
      _LiteralType_integer>>: "it" ~> Core.literalInteger $
        ref Lits.bigintToIntegerValueDef @@ var "it" @@ (ref Lits.integerValueToBigintDef @@ var "i")]]

adaptLiteralTypeDef :: TBinding (LanguageConstraints -> LiteralType -> Maybe LiteralType)
adaptLiteralTypeDef = define "adaptLiteralType" $
  doc "Attempt to adapt a literal type using the given language constraints" $
  "constraints" ~> "lt" ~>
  Logic.ifElse (ref literalTypeSupportedDef @@ var "constraints" @@ var "lt")
    nothing
    (cases _LiteralType (var "lt")
      (Just nothing) [
      _LiteralType_binary>>: constant $ just Core.literalTypeString,
      _LiteralType_boolean>>: constant $ Optionals.map (unaryFunction Core.literalTypeInteger) $
          ref adaptIntegerTypeDef @@ var "constraints" @@ Core.integerTypeInt8,
      _LiteralType_float>>: "ft" ~> Optionals.map (unaryFunction Core.literalTypeFloat) $
        ref adaptFloatTypeDef @@ var "constraints" @@ var "ft",
      _LiteralType_integer>>: "it" ~> Optionals.map (unaryFunction Core.literalTypeInteger) $
        ref adaptIntegerTypeDef @@ var "constraints" @@ var "it"])

adaptLiteralTypesMapDef :: TBinding (LanguageConstraints -> M.Map LiteralType LiteralType)
adaptLiteralTypesMapDef = define "adaptLiteralTypesMap" $
  doc "Derive a map of adapted literal types for the given language constraints" $
  "constraints" ~>
  "tryType" <~ ("lt" ~> optCases (ref adaptLiteralTypeDef @@ var "constraints" @@ var "lt")
    nothing
    ("lt2" ~> just $ pair (var "lt") (var "lt2"))) $
  Maps.fromList $ Optionals.cat $ Lists.map (var "tryType") (ref Variants.literalTypesDef)

adaptLiteralValueDef :: TBinding (M.Map LiteralType LiteralType -> LiteralType -> Literal -> Literal)
adaptLiteralValueDef = define "adaptLiteralValue" $
  doc "Adapt a literal value using the given language constraints" $
  "litmap" ~> "lt" ~> "l" ~> optCases (Maps.lookup (var "lt") (var "litmap"))
    (Core.literalString $ ref ShowCore.literalDef @@ var "l")
    ("lt2" ~> ref adaptLiteralDef @@ var "lt2" @@ var "l")

adaptPrimitiveDef :: TBinding (LanguageConstraints -> M.Map LiteralType LiteralType -> Primitive -> Flow s Primitive)
adaptPrimitiveDef = define "adaptPrimitive" $
  doc "Adapt a primitive to the given language constraints, prior to inference" $
  "constraints" ~> "litmap" ~> "prim0" ~>
  "ts0" <~ Graph.primitiveType (var "prim0") $
  "ts1" <<~ ref adaptTypeSchemeDef @@ var "constraints" @@ var "litmap" @@ var "ts0" $
  produce $ Graph.primitiveWithType (var "prim0") (var "ts1")

-- Note: this function could be made more efficient through precomputation of alternatives,
--       similar to what is done for literals.
adaptTermDef :: TBinding (LanguageConstraints -> M.Map LiteralType LiteralType -> Term -> Flow Graph Term)
adaptTermDef = define "adaptTerm" $
  doc "Adapt a term using the given language constraints" $
  "constraints" ~> "litmap" ~> "term0" ~>
  "rewrite" <~ ("recurse" ~> "term0" ~>
    "term1" <<~ var "recurse" @@ var "term0" $
    "tryTerm" <~ ("term" ~>
      "supportedVariant" <~ Sets.member
        (ref Variants.termVariantDef @@ var "term")
        (Coders.languageConstraintsTermVariants $ var "constraints") $
      Logic.ifElse (var "supportedVariant")
        (cases _Term (var "term")
          (Just $ produce $ just $ var "term") [
          _Term_literal>>: "l" ~>
            "lt" <~ ref Variants.literalTypeDef @@ var "l" $
            produce $ just $ Logic.ifElse (ref literalTypeSupportedDef @@ var "constraints" @@ var "lt")
              (var "term")
              (Core.termLiteral $ ref adaptLiteralValueDef @@ var "litmap" @@ var "lt" @@ var "l")])
        ("tryAlts" <~ ("alts" ~> Logic.ifElse (Lists.null $ var "alts")
          (produce nothing)
          ( "mterm" <<~ var "tryTerm" @@ Lists.head (var "alts") $
            optCases (var "mterm")
              (var "tryAlts" @@ Lists.tail (var "alts"))
              ("t" ~> produce $ just $ var "t"))) $
           "alts" <<~ ref termAlternativesDef @@ var "term1" $
           var "tryAlts" @@ var "alts")) $
    "mterm" <<~ var "tryTerm" @@ var "term1" $
    optCases (var "mterm")
      (Flows.fail $ "no alternatives for term: " ++ (ref ShowCore.termDef @@ var "term1"))
      ("term2" ~> produce $ var "term2")) $
  ref Rewriting.rewriteTermMDef @@ var "rewrite" @@ var "term0"

adaptTypeDef :: TBinding (LanguageConstraints -> M.Map LiteralType LiteralType -> Type -> Flow s Type)
adaptTypeDef = define "adaptType" $
  doc "Adapt a type using the given language constraints" $
  "constraints" ~> "litmap" ~> "type0" ~>
  "rewrite" <~ ("recurse" ~> "typ" ~>
    "type1" <<~ var "recurse" @@ var "typ" $
    "tryType" <~ ("typ" ~>
      "supportedVariant" <~ Sets.member
        (ref Variants.typeVariantDef @@ var "typ")
        (Coders.languageConstraintsTypeVariants $ var "constraints") $
      Logic.ifElse (var "supportedVariant")
        (cases _Type (var "typ")
          (Just $ just $ var "typ") [
          _Type_literal>>: "lt" ~> Logic.ifElse (ref literalTypeSupportedDef @@ var "constraints" @@ var "lt")
            (just $ var "typ")
            (optCases (Maps.lookup (var "lt") (var "litmap"))
              (just $ Core.typeLiteral Core.literalTypeString)
              ("lt2" ~> just $ Core.typeLiteral $ var "lt2"))])
        ("tryAlts" <~ ("alts" ~> Logic.ifElse (Lists.null $ var "alts")
          nothing
          ( optCases (var "tryType" @@ Lists.head (var "alts"))
              (var "tryAlts" @@ Lists.tail (var "alts"))
              ("t" ~> just $ var "t"))) $
           "alts" <~ ref typeAlternativesDef @@ var "type1" $
           var "tryAlts" @@ var "alts")) $
    optCases (var "tryType" @@ var "type1")
      (Flows.fail $ "no alternatives for type: " ++ (ref ShowCore.typeDef @@ var "typ"))
      ("type2" ~> produce $ var "type2")) $
  ref Rewriting.rewriteTypeMDef @@ var "rewrite" @@ var "type0"

adaptTypeSchemeDef :: TBinding (LanguageConstraints -> M.Map LiteralType LiteralType -> TypeScheme -> Flow s TypeScheme)
adaptTypeSchemeDef = define "adaptTypeScheme" $
  doc "Adapt a type scheme to the given language constraints, prior to inference" $
  "constraints" ~> "litmap" ~> "ts0" ~>
  "vars0" <~ Core.typeSchemeVariables (var "ts0") $
  "t0" <~ Core.typeSchemeType (var "ts0") $
  "t1" <<~ ref adaptTypeDef @@ var "constraints" @@ var "litmap" @@ var "t0" $
  produce $ Core.typeScheme (var "vars0") (var "t1")

dataGraphToDefinitionsDef :: TBinding (LanguageConstraints -> Bool -> Graph -> [[Name]] -> Flow s (Graph, [[TermDefinition]]))
dataGraphToDefinitionsDef = define "dataGraphToDefinitions" $
  doc ("Given a data graph along with language constraints and a designated list of element names,"
    <> " adapt the graph to the language constraints, perform inference,"
    <> " then return a corresponding term definition for each element name.") $
  "constraints" ~> "doExpand" ~> "graph" ~> "nameLists" ~>
  "graph1" <<~ ref adaptDataGraphDef @@ var "constraints" @@ var "doExpand" @@ var "graph" $
--  Flows.fail ("adapted graph: " ++ (ref ShowGraph.graphDef @@ var "graph1"))
  "graph2" <<~ ref Inference.inferGraphTypesDef @@ var "graph1" $
  "toDef" <~ ("el" ~>
    "ts" <~ Optionals.fromJust (Core.bindingType $ var "el") $
    Module.termDefinition
      (Core.bindingName $ var "el")
      (Core.bindingTerm $ var "el")
      (ref Schemas.typeSchemeToFTypeDef @@ var "ts")) $
  produce $ pair
    (var "graph2")
    (Lists.map
      ("names" ~> Lists.map (var "toDef") $
        Lists.map ("n" ~> Optionals.fromJust $ Maps.lookup (var"n") (Graph.graphElements $ var "graph2")) (var "names"))
      (var "nameLists"))

literalTypeSupportedDef :: TBinding (LanguageConstraints -> LiteralType -> Bool)
literalTypeSupportedDef = define "literalTypeSupported" $
  doc "Check if a literal type is supported by the given language constraints" $
  "constraints" ~> "lt" ~>
  Logic.ifElse
    (Sets.member
      (ref Variants.literalTypeVariantDef @@ var "lt")
      (Coders.languageConstraintsLiteralVariants $ var "constraints"))
    (cases _LiteralType (var "lt")
      (Just true) [
        _LiteralType_float>>: "ft" ~> Sets.member (var "ft") (Coders.languageConstraintsFloatTypes $ var "constraints"),
        _LiteralType_integer>>: "it" ~> Sets.member (var "it") (Coders.languageConstraintsIntegerTypes $ var "constraints")])
    false

schemaGraphToDefinitionsDef :: TBinding (LanguageConstraints -> Graph -> [[Name]] -> Flow s (M.Map Name Type, [[TypeDefinition]]))
schemaGraphToDefinitionsDef = define "schemaGraphToDefinitions" $
  doc ("Given a schema graph along with language constraints and a designated list of element names,"
    <> " adapt the graph to the language constraints,"
    <> " then return a corresponding type definition for each element name.") $
  "constraints" ~> "graph" ~> "nameLists" ~>
  "litmap" <~ ref adaptLiteralTypesMapDef @@ var "constraints" $
  "tmap0" <<~ ref Schemas.graphAsTypesDef @@ var "graph" $
  "tmap1" <<~ ref adaptGraphSchemaDef @@ var "constraints" @@ var "litmap" @@ var "tmap0" $
  "toDef" <~ ("pair" ~> Module.typeDefinition (first $ var "pair") (second $ var "pair")) $
  produce $ pair
    (var "tmap1")
    (Lists.map
      ("names" ~> Lists.map (var "toDef") $
        Lists.map ("n" ~> pair (var "n") (Optionals.fromJust $ Maps.lookup (var "n") (var "tmap1"))) (var "names"))
      (var "nameLists"))

termAlternativesDef :: TBinding (Term -> Flow Graph [Term])
termAlternativesDef = define "termAlternatives" $
  doc "Find a list of alternatives for a given term, if any" $
  "term" ~> cases _Term (var "term")
    (Just $ produce $ list []) [
    _Term_annotated>>: "at" ~>
      "term2" <~ Core.annotatedTermSubject (var "at") $
      produce $ list [
        var "term2"], -- TODO: lossy
    _Term_optional>>: "ot" ~> produce $ list [
      Core.termList $ optCases (var "ot")
        (list [])
        ("term2" ~> list [var "term2"])],
    -- Note: no type abstractions or type applications, as we are not expecting System F terms here
    _Term_union>>: "inj" ~>
      "tname" <~ Core.injectionTypeName (var "inj") $
      "field" <~ Core.injectionField (var "inj") $
      "fname" <~ Core.fieldName (var "field") $
      "fterm" <~ Core.fieldTerm (var "field") $
      "rt" <<~ ref Schemas.requireUnionTypeDef @@ var "tname" $
      produce $ list [
        "forFieldType" <~ ("ft" ~>
          "ftname" <~ Core.fieldTypeName (var "ft") $
          Core.field (var "fname") $ Core.termOptional $ Logic.ifElse (Equality.equal (var "ftname") (var "fname"))
            (just $ var "fterm")
            (nothing)) $
        "fields" <~ Lists.map (var "forFieldType") (Core.rowTypeFields $ var "rt") $
        Core.termRecord $ Core.record (var "tname") (var "fields")],
    _Term_unit>>: constant $ produce $ list [
      Core.termLiteral $ Core.literalBoolean true],
    _Term_wrap>>: "wt" ~>
      "term2" <~ Core.wrappedTermObject (var "wt") $
      produce $ list [
         var "term2"]]

typeAlternativesDef :: TBinding (Type -> [Type])
typeAlternativesDef = define "typeAlternatives" $
  doc "Find a list of alternatives for a given type, if any" $
  "type" ~> cases _Type (var "type")
    (Just $ list []) [
    _Type_annotated>>: "at" ~>
      "type2" <~ Core.annotatedTypeSubject (var "at") $
       list [var "type2"], -- TODO: lossy
    _Type_optional>>: "ot" ~> list [
      Core.typeList $ var "ot"],
    _Type_union>>: "rt" ~>
      "tname" <~ Core.rowTypeTypeName (var "rt") $
      "fields" <~ Core.rowTypeFields (var "rt") $
      list [
        Core.typeRecord $ Core.rowType (var "tname") (var "fields")],
    _Type_unit>>: constant $ list [
      Core.typeLiteral $ Core.literalTypeBoolean]]