packages feed

hydra-0.1.0: src/main/haskell/Hydra/Impl/Haskell/Dsl/Terms.hs

module Hydra.Impl.Haskell.Dsl.Terms where

import Hydra.All
import qualified Hydra.Impl.Haskell.Dsl.Literals as Literals

import Prelude hiding (map)
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 Control.Monad as CM
import Data.Int
import Data.String(IsString(..))


instance IsString (Term m) where fromString = string

annot :: m -> Term m -> Term m
annot ann t = TermAnnotated $ Annotated t ann

apply :: Term m -> Term m -> Term m
apply func arg = TermApplication $ Application func arg

bigfloat :: Double -> Term m
bigfloat = literal . Literals.bigfloat

bigint :: Integer -> Term m
bigint = literal . Literals.bigint

binary :: String -> Term m
binary = literal . Literals.binary

boolean :: Bool -> Term m
boolean = literal . Literals.boolean

cases :: Name -> [Field m] -> Term m
cases n fields = TermFunction $ FunctionElimination $ EliminationUnion $ CaseStatement n fields

compareTo :: Term m -> Term m
compareTo = TermFunction . FunctionCompareTo

constFunction :: Term m -> Term m
constFunction = lambda "_"

delta :: Term m
delta = TermFunction $ FunctionElimination EliminationElement

element :: Name -> Term m
element = TermElement

elementRef :: Element a -> Term m
elementRef = apply delta . TermElement . elementName

elementRefByName :: Name -> Term m
elementRefByName = apply delta . TermElement

eliminateNominal :: Name -> Term m
eliminateNominal = TermFunction . FunctionElimination . EliminationNominal

elimination :: Elimination m -> Term m
elimination = TermFunction . FunctionElimination

expectBinary :: Show m => Term m -> Flow s String
expectBinary = expectLiteral Literals.expectBinary

expectBoolean :: Show m => Term m -> Flow s Bool
expectBoolean = expectLiteral Literals.expectBoolean

expectFloat32 :: Show m => Term m -> Flow s Float
expectFloat32 = expectLiteral Literals.expectFloat32

expectFloat64 :: Show m => Term m -> Flow s Double
expectFloat64 = expectLiteral Literals.expectFloat64

expectInt32 :: Show m => Term m -> Flow s Int
expectInt32 = expectLiteral Literals.expectInt32

expectInt64 :: Show m => Term m -> Flow s Integer
expectInt64 = expectLiteral Literals.expectInt64

expectList :: Show m => (Term m -> Flow s a) -> Term m -> Flow s [a]
expectList f term = case stripTerm term of
  TermList l -> CM.mapM f l
  _ -> unexpected "list" term

expectLiteral :: Show m => (Literal -> Flow s a) -> Term m -> Flow s a
expectLiteral expect term = case stripTerm term of
  TermLiteral lit -> expect lit
  _ -> unexpected "literal" term

expectMap :: (Ord k, Show m) => (Term m -> Flow s k) -> (Term m -> Flow s v) -> Term m -> Flow s (M.Map k v)
expectMap fk fv term = case stripTerm term of
  TermMap m -> M.fromList <$> CM.mapM expectPair (M.toList m)
    where
      expectPair (kterm, vterm) = do
        kval <- fk kterm
        vval <- fv vterm
        return (kval, vval)
  _ -> unexpected "map" term

expectNArgs :: Int -> [Term m] -> Flow s ()
expectNArgs n args = if L.length args /= n
  then unexpected (show n ++ " arguments") (L.length args)
  else pure ()

expectOptional :: Show m => (Term m -> Flow s a) -> Term m -> Flow s (Y.Maybe a)
expectOptional f term = case stripTerm term of
  TermOptional mt -> case mt of
    Nothing -> pure Nothing
    Just t -> Just <$> f t
  _ -> unexpected "optional value" term

expectRecord :: Show m => Term m -> Flow s [Field m]
expectRecord term = case stripTerm term of
  TermRecord (Record _ fields) -> pure fields
  _ -> unexpected "record" term

expectSet :: (Ord a, Show m) => (Term m -> Flow s a) -> Term m -> Flow s (S.Set a)
expectSet f term = case stripTerm term of
  TermSet s -> S.fromList <$> CM.mapM f (S.toList s)
  _ -> unexpected "set" term

expectString :: Show m => Term m -> Flow s String
expectString = expectLiteral Literals.expectString

expectUnion :: Show m => Term m -> Flow s (Field m)
expectUnion term = case stripTerm term of
  TermUnion (Union _ field) -> pure field
  _ -> unexpected "union" term

field :: String -> Term m -> Field m
field n = Field (FieldName n)

fieldsToMap :: [Field m] -> M.Map FieldName (Term m)
fieldsToMap fields = M.fromList $ (\(Field name term) -> (name, term)) <$> fields

float32 :: Float -> Term m
float32 = literal . Literals.float32

float64 :: Double -> Term m
float64 = literal . Literals.float64

float :: FloatValue -> Term m
float = literal . Literals.float

fold :: Term m -> Term m
fold = TermFunction . FunctionElimination . EliminationList

int16 :: Int16 -> Term m
int16 = literal . Literals.int16

int32 :: Int -> Term m
int32 = literal . Literals.int32

int64 :: Int64 -> Term m
int64 = literal . Literals.int64

int8 :: Int8 -> Term m
int8 = literal . Literals.int8

integer :: IntegerValue -> Term m
integer = literal . Literals.integer

isUnit :: Eq m => Term m -> Bool
isUnit t = stripTerm t == TermRecord (Record unitTypeName [])

lambda :: String -> Term m -> Term m
lambda param body = TermFunction $ FunctionLambda $ Lambda (Variable param) body

letTerm :: Variable -> Term m -> Term m -> Term m
letTerm v t1 t2 = TermLet $ Let v t1 t2

list :: [Term m] -> Term m
list = TermList

literal :: Literal -> Term m
literal = TermLiteral

map :: M.Map (Term m) (Term m) -> Term m
map = TermMap

mapTerm :: M.Map (Term m) (Term m) -> Term m
mapTerm = TermMap

match :: Name -> [(FieldName, Term m)] -> Term m
match n = cases n . fmap toField
  where
    toField (name, term) = Field name term

matchOptional :: Term m -> Term m -> Term m
matchOptional n j = TermFunction $ FunctionElimination $ EliminationOptional $ OptionalCases n j

matchWithVariants :: Name -> [(FieldName, FieldName)] -> Term m
matchWithVariants n = cases n . fmap toField
  where
    toField (from, to) = Field from $ constFunction $ unitVariant n to

nominal :: Name -> Term m -> Term m
nominal name term = TermNominal $ Named name term

optional :: Y.Maybe (Term m) -> Term m
optional = TermOptional

primitive :: Name -> Term m
primitive = TermFunction . FunctionPrimitive

product :: [Term m] -> Term m
product = TermProduct

projection :: Name -> FieldName -> Term m
projection n fname = TermFunction $ FunctionElimination $ EliminationRecord $ Projection n fname

record :: Name -> [Field m] -> Term m
record n fields = TermRecord $ Record n fields

requireField :: M.Map FieldName (Term m) -> FieldName -> GraphFlow m (Term m)
requireField fields fname = Y.maybe err pure $ M.lookup fname fields
  where
    err = fail $ "no such field: " ++ unFieldName fname

set :: S.Set (Term m) -> Term m
set = TermSet

stringList :: [String] -> Term m
stringList l = list (string <$> l)

stringSet :: Ord m => S.Set String -> Term m
stringSet strings = set $ S.fromList $ string <$> S.toList strings

string :: String -> Term m
string = TermLiteral . LiteralString

sum :: Int -> Int -> Term m -> Term m
sum i s term = TermSum $ Sum i s term

uint16 :: Integer -> Term m
uint16 = literal . Literals.uint16

uint32 :: Integer -> Term m
uint32 = literal . Literals.uint32

uint64 :: Integer -> Term m
uint64 = literal . Literals.uint64

uint8 :: Integer -> Term m
uint8 = literal . Literals.uint8

union :: Name -> Field m -> Term m
union n = TermUnion . Union n

unit :: Term m
unit = TermRecord $ Record (Name "hydra/core.UnitType") []

unitVariant :: Name -> FieldName -> Term m
unitVariant n fname = variant n fname unit

variable :: String -> Term m
variable = TermVariable . Variable

variant :: Name -> FieldName -> Term m -> Term m
variant n fname term = TermUnion $ Union n $ Field fname term

withVariant :: Name -> FieldName -> Term m
withVariant n = constFunction . unitVariant n