packages feed

hydra-kernel-0.16.1: src/main/haskell/Hydra/Dsl/Meta/DeepCore.hs

-- | A domain-specific language for constructing Hydra terms in Haskell.
--
-- This DSL provides helpers for constructing "deep" terms - i.e., terms that
-- represent Hydra terms as data (Term values), as opposed to the shallow DSL
-- that works at the Haskell level.
module Hydra.Dsl.Meta.DeepCore where

import Hydra.Kernel
import Hydra.Dsl.Terms (ToPrimName(..))
import qualified Hydra.Dsl.Meta.Core as Core
import qualified Hydra.Dsl.Meta.Phantoms as P

import qualified Data.Char as C
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S


--------------------------------------------------------------------------------
-- Application operators
--------------------------------------------------------------------------------

-- | Apply a function term to an argument term (infix operator, alias)
(@@@) :: TypedTerm Term -> TypedTerm Term -> TypedTerm Term
fun @@@ arg = apply fun arg

-- | Apply a function term to an argument term
apply :: TypedTerm Term -> TypedTerm Term -> TypedTerm Term
apply lhs rhs = Core.termApplication $ Core.application lhs rhs

--------------------------------------------------------------------------------
-- Case statements and pattern matching
--------------------------------------------------------------------------------

cases :: Name -> TypedTerm Term -> TypedTerm (Maybe Term) -> [TypedTerm CaseAlternative] -> TypedTerm Term
cases tname arg dflt alts = match tname dflt alts @@@ arg

-- | Create a field (e.g. for injection payloads)
field :: Name -> TypedTerm Term -> TypedTerm Field
field fname body = Core.field (Core.nameLift fname) body

-- | Create a case alternative (a variant tag together with its handler) for case matching
caseAlternative :: Name -> TypedTerm Term -> TypedTerm CaseAlternative
caseAlternative aname handler = Core.caseAlternative (Core.nameLift aname) handler

-- | Create a union case statement with optional default case
match :: Name -> TypedTerm (Maybe Term) -> [TypedTerm CaseAlternative] -> TypedTerm Term
match tname dflt alts = Core.termCases $
  Core.caseStatement
    (Core.nameLift tname)
    dflt
    (P.list alts)

--------------------------------------------------------------------------------
-- Functions and lambdas
--------------------------------------------------------------------------------

constant :: TypedTerm Term -> TypedTerm Term
constant = lambda ignoredVariable

-- | Create a lambda term with a variable name and body
lambda :: String -> TypedTerm Term -> TypedTerm Term
lambda v body = Core.termLambda $ Core.lambda (Core.name (P.string v)) P.nothing $ body

-- | Create a lambda term with a dynamic parameter name, optional domain, and body
lambdaTyped :: TypedTerm Name -> TypedTerm (Maybe Type) -> TypedTerm Term -> TypedTerm Term
lambdaTyped name domain body = Core.termLambda $ Core.lambda name domain body

-- | Create a reference to a primitive function.
-- Uses termVariable; the name resolves via graphPrimitives fallthrough.
primitive :: ToPrimName n => n -> TypedTerm Term
primitive = Core.termVariable . Core.nameLift . toPrimName

-- TODO: this is probably equivalent to `primitive`.
-- | Create a reference to a primitive function using encodedName
-- (for primitives that need namespace encoding)
primitiveEncoded :: ToPrimName n => n -> TypedTerm Term
primitiveEncoded = Core.termVariable . P.encodedName . toPrimName

--------------------------------------------------------------------------------
-- Projections and eliminations
--------------------------------------------------------------------------------

-- | Create a record field projection
project :: Name -> Name -> TypedTerm Term
project tname fname = Core.termProject $
  Core.projection (Core.nameLift tname) (Core.nameLift fname)

unwrap :: Name -> TypedTerm Term
unwrap name = unwrapDynamic (Core.nameLift name)

-- | Create an unwrap elimination for a wrapped type
unwrapDynamic :: TypedTerm Name -> TypedTerm Term
unwrapDynamic tname = Core.termUnwrap tname

--------------------------------------------------------------------------------
-- Literals and basic terms
--------------------------------------------------------------------------------

-- | Create a string literal term
string :: TypedTerm String -> TypedTerm Term
string s = Core.termLiteral $ Core.literalString s

-- | Create a unit term
unit :: TypedTerm Term
unit = Core.termUnit

-- | Create a variable reference term
var :: String -> TypedTerm Term
var s = Core.termVariable $ Core.name $ P.string s

--------------------------------------------------------------------------------
-- Compound terms (records, unions, wraps, lists)
--------------------------------------------------------------------------------

-- | Create an Either term (left or right)
either :: TypedTerm (Either Term Term) -> TypedTerm Term
either e = Core.termEither e

-- | Create a union injection term
injection :: Name -> TypedTerm Field -> TypedTerm Term
injection tname fld = Core.termInject $ Core.injection (Core.nameLift tname) fld

-- | Create a Just term
just :: TypedTerm Term -> TypedTerm Term
just (TypedTerm t) = TypedTerm $ TermOptional $ Just t

left :: TypedTerm Term -> TypedTerm Term
left (TypedTerm t) = Core.termEither $ TypedTerm $ TermEither $ Left t

-- | Create a single let binding
letn :: String -> TypedTerm Term -> TypedTerm Term -> TypedTerm Term
letn name value body = lets [(name, value)] body

-- | Create a let expression with bindings and a body
-- Each binding is a (name, term) pair
lets :: [(String, TypedTerm Term)] -> TypedTerm Term -> TypedTerm Term
lets bindings body = Core.termLet $ Core.let_
  (P.list [Core.binding (Core.name (P.string n)) t P.nothing | (n, t) <- bindings])
  body

-- | Create a list term from a list of terms
list :: TypedTerm [Term] -> TypedTerm Term
list terms = Core.termList terms

map :: TypedTerm (M.Map Term Term) -> TypedTerm Term
map terms = Core.termMap terms

-- | Create a Nothing term
nothing :: TypedTerm Term
nothing = TypedTerm $ TermOptional Nothing

pair :: TypedTerm Term -> TypedTerm Term -> TypedTerm Term
pair (TypedTerm first) (TypedTerm second) = Core.termPair $ TypedTerm $ TermPair (first, second)

-- | Create a record term with a type name and fields
record :: Name -> [TypedTerm Field] -> TypedTerm Term
record tname fields = Core.termRecord $ Core.record (Core.nameLift tname) (P.list fields)

ref :: TypedTermDefinition a -> TypedTerm Term
ref (TypedTermDefinition name _) = Core.termVariable $ Core.nameLift name

right :: TypedTerm Term -> TypedTerm Term
right (TypedTerm t) = Core.termEither $ TypedTerm $ TermEither $ Right t

-- | Create a wrap term with a type name and body
wrap :: Name -> TypedTerm Term -> TypedTerm Term
wrap tname body = Core.termWrap $ Core.wrappedTerm (Core.nameLift tname) body

-- | Create a wrap term with a dynamic type name (TypedTerm Name)
wrapDynamic :: TypedTerm Name -> TypedTerm Term -> TypedTerm Term
wrapDynamic tname body = Core.termWrap $ Core.wrappedTerm tname body