packages feed

hydra-0.13.0: 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 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)
(@@@) :: TTerm Term -> TTerm Term -> TTerm Term
fun @@@ arg = apply fun arg

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

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

cases :: Name -> TTerm Term -> TTerm (Maybe Term) -> [TTerm Field] -> TTerm Term
cases tname arg dflt fields = match tname dflt fields @@@ arg

-- | Create a union case statement with optional default case
match :: Name -> TTerm (Maybe Term) -> [TTerm Field] -> TTerm Term
match tname dflt fields = Core.termFunction $ Core.functionElimination $ Core.eliminationUnion $
  Core.caseStatement
    (Core.nameLift tname)
    dflt
    (P.list fields)

-- | Create a field for case matching
field :: Name -> TTerm Term -> TTerm Field
field fname body = Core.field (Core.nameLift fname) body

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

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

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

-- | Create a reference to a primitive function
primitive :: Name -> TTerm Term
primitive name = Core.termFunction $ Core.functionPrimitive $ Core.nameLift name

-- TODO: this is probably equivalent to `primitive`.
-- | Create a reference to a primitive function using encodedName
-- (for primitives that need namespace encoding)
primitiveEncoded :: Name -> TTerm Term
primitiveEncoded name = Core.termFunction $ Core.functionPrimitive $ P.encodedName name

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

-- | Create a record field projection
project :: Name -> Name -> TTerm Term
project tname fname = Core.termFunction $ Core.functionElimination $ Core.eliminationRecord $
  Core.projection (Core.nameLift tname) (Core.nameLift fname)

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

-- | Create an unwrap elimination for a wrapped type
unwrapDynamic :: TTerm Name -> TTerm Term
unwrapDynamic tname = Core.termFunction $ Core.functionElimination $ Core.eliminationWrap tname

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

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

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

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

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

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

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

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

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

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

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

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

-- | Create a let expression with bindings and a body
-- Each binding is a (name, term) pair
lets :: [(String, TTerm Term)] -> TTerm Term -> TTerm 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 single let binding
letn :: String -> TTerm Term -> TTerm Term -> TTerm Term
letn name value body = lets [(name, value)] body

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

-- | Create a Nothing term
nothing :: TTerm Term
nothing = TTerm $ TermMaybe Nothing

-- | Create a Just term
just :: TTerm Term -> TTerm Term
just (TTerm t) = TTerm $ TermMaybe $ Just t

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

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

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