hydra-0.14.0: src/gen-main/haskell/Hydra/Ext/Haskell/Utils.hs
-- Note: this is an automatically generated file. Do not edit.
-- | Utilities for working with Haskell syntax trees
module Hydra.Ext.Haskell.Utils where
import qualified Hydra.Context as Context
import qualified Hydra.Core as Core
import qualified Hydra.Errors as Errors
import qualified Hydra.Ext.Haskell.Language as Language
import qualified Hydra.Ext.Haskell.Syntax as Syntax
import qualified Hydra.Formatting as Formatting
import qualified Hydra.Graph as Graph
import qualified Hydra.Lib.Eithers as Eithers
import qualified Hydra.Lib.Equality as Equality
import qualified Hydra.Lib.Lists as Lists
import qualified Hydra.Lib.Logic as Logic
import qualified Hydra.Lib.Maps as Maps
import qualified Hydra.Lib.Maybes as Maybes
import qualified Hydra.Lib.Pairs as Pairs
import qualified Hydra.Lib.Sets as Sets
import qualified Hydra.Lib.Strings as Strings
import qualified Hydra.Module as Module
import qualified Hydra.Names as Names
import qualified Hydra.Rewriting as Rewriting
import qualified Hydra.Schemas as Schemas
import Prelude hiding (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.ByteString as B
import qualified Data.Int as I
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
-- | Create an application pattern from a name and argument patterns
applicationPattern :: Syntax.Name -> [Syntax.Pattern] -> Syntax.Pattern
applicationPattern name args =
Syntax.PatternApplication (Syntax.ApplicationPattern {
Syntax.applicationPatternName = name,
Syntax.applicationPatternArgs = args})
-- | Generate a Haskell name reference for a Hydra element
elementReference :: Module.Namespaces Syntax.ModuleName -> Core.Name -> Syntax.Name
elementReference namespaces name =
let namespacePair = Module.namespacesFocus namespaces
gname = Pairs.first namespacePair
gmod = Syntax.unModuleName (Pairs.second namespacePair)
namespacesMap = Module.namespacesMapping namespaces
qname = Names.qualifyName name
local = Module.qualifiedNameLocal qname
escLocal = sanitizeHaskellName local
mns = Module.qualifiedNameNamespace qname
in (Maybes.cases (Module.qualifiedNameNamespace qname) (simpleName local) (\ns -> Maybes.cases (Maps.lookup ns namespacesMap) (simpleName local) (\mn ->
let aliasStr = Syntax.unModuleName mn
in (Logic.ifElse (Equality.equal ns gname) (simpleName escLocal) (rawName (Strings.cat [
aliasStr,
".",
(sanitizeHaskellName local)]))))))
-- | Create a Haskell function application expression
hsapp :: Syntax.Expression -> Syntax.Expression -> Syntax.Expression
hsapp l r =
Syntax.ExpressionApplication (Syntax.ApplicationExpression {
Syntax.applicationExpressionFunction = l,
Syntax.applicationExpressionArgument = r})
-- | Create a Haskell lambda expression
hslambda :: Syntax.Name -> Syntax.Expression -> Syntax.Expression
hslambda name rhs =
Syntax.ExpressionLambda (Syntax.LambdaExpression {
Syntax.lambdaExpressionBindings = [
Syntax.PatternName name],
Syntax.lambdaExpressionInner = rhs})
-- | Create a Haskell literal expression
hslit :: Syntax.Literal -> Syntax.Expression
hslit lit = Syntax.ExpressionLiteral lit
-- | Create a Haskell variable expression from a string
hsvar :: String -> Syntax.Expression
hsvar s = Syntax.ExpressionVariable (rawName s)
-- | Compute the Haskell module namespaces for a Hydra module
namespacesForModule :: Module.Module -> Context.Context -> Graph.Graph -> Either (Context.InContext Errors.Error) (Module.Namespaces Syntax.ModuleName)
namespacesForModule mod cx g =
Eithers.bind (Schemas.moduleDependencyNamespaces cx g True True True True mod) (\nss ->
let ns = Module.moduleNamespace mod
toModuleName =
\namespace ->
let namespaceStr = Module.unNamespace namespace
parts = Strings.splitOn "." namespaceStr
lastPart = Lists.last parts
capitalized = Formatting.capitalize lastPart
in (Syntax.ModuleName capitalized)
toPair = \name -> (name, (toModuleName name))
addPair =
\state -> \namePair ->
let currentMap = Pairs.first state
currentSet = Pairs.second state
name = Pairs.first namePair
alias = Pairs.second namePair
aliasStr = Syntax.unModuleName alias
in (Logic.ifElse (Sets.member alias currentSet) (addPair state (name, (Syntax.ModuleName (Strings.cat2 aliasStr "_")))) (Maps.insert name alias currentMap, (Sets.insert alias currentSet)))
focusPair = toPair ns
nssAsList = Sets.toList nss
nssPairs = Lists.map toPair nssAsList
emptyState = (Maps.empty, Sets.empty)
finalState = Lists.foldl addPair emptyState nssPairs
resultMap = Pairs.first finalState
in (Right (Module.Namespaces {
Module.namespacesFocus = focusPair,
Module.namespacesMapping = resultMap})))
-- | Generate an accessor name for a newtype wrapper (e.g., 'unFoo' for Foo)
newtypeAccessorName :: Core.Name -> String
newtypeAccessorName name = Strings.cat2 "un" (Names.localNameOf name)
-- | Create a raw Haskell name from a string without sanitization
rawName :: String -> Syntax.Name
rawName n =
Syntax.NameNormal (Syntax.QualifiedName {
Syntax.qualifiedNameQualifiers = [],
Syntax.qualifiedNameUnqualified = (Syntax.NamePart n)})
-- | Generate a Haskell name for a record field accessor
recordFieldReference :: Module.Namespaces Syntax.ModuleName -> Core.Name -> Core.Name -> Syntax.Name
recordFieldReference namespaces sname fname =
let fnameStr = Core.unName fname
qname = Names.qualifyName sname
ns = Module.qualifiedNameNamespace qname
typeNameStr = typeNameForRecord sname
decapitalized = Formatting.decapitalize typeNameStr
capitalized = Formatting.capitalize fnameStr
nm = Strings.cat2 decapitalized capitalized
qualName =
Module.QualifiedName {
Module.qualifiedNameNamespace = ns,
Module.qualifiedNameLocal = nm}
unqualName = Names.unqualifyName qualName
in (elementReference namespaces unqualName)
-- | Sanitize a string to be a valid Haskell identifier, escaping reserved words
sanitizeHaskellName :: String -> String
sanitizeHaskellName = Formatting.sanitizeWithUnderscores Language.reservedWords
-- | Create a sanitized Haskell name from a string
simpleName :: String -> Syntax.Name
simpleName arg_ = rawName (sanitizeHaskellName arg_)
-- | Create a simple value binding (e.g., 'foo = expr' or 'foo = expr where ...')
simpleValueBinding :: Syntax.Name -> Syntax.Expression -> Maybe Syntax.LocalBindings -> Syntax.ValueBinding
simpleValueBinding hname rhs bindings =
let pat =
Syntax.PatternApplication (Syntax.ApplicationPattern {
Syntax.applicationPatternName = hname,
Syntax.applicationPatternArgs = []})
rightHandSide = Syntax.RightHandSide rhs
in (Syntax.ValueBindingSimple (Syntax.SimpleValueBinding {
Syntax.simpleValueBindingPattern = pat,
Syntax.simpleValueBindingRhs = rightHandSide,
Syntax.simpleValueBindingLocalBindings = bindings}))
-- | Convert a list of types into a nested type application
toTypeApplication :: [Syntax.Type] -> Syntax.Type
toTypeApplication types =
let app =
\l -> Logic.ifElse (Equality.gt (Lists.length l) 1) (Syntax.TypeApplication (Syntax.ApplicationType {
Syntax.applicationTypeContext = (app (Lists.tail l)),
Syntax.applicationTypeArgument = (Lists.head l)})) (Lists.head l)
in (app (Lists.reverse types))
-- | Extract the local type name from a fully qualified record type name
typeNameForRecord :: Core.Name -> String
typeNameForRecord sname =
let snameStr = Core.unName sname
parts = Strings.splitOn "." snameStr
in (Lists.last parts)
-- | Generate a Haskell name for a union variant constructor, with disambiguation
unionFieldReference :: S.Set Core.Name -> Module.Namespaces Syntax.ModuleName -> Core.Name -> Core.Name -> Syntax.Name
unionFieldReference boundNames namespaces sname fname =
let fnameStr = Core.unName fname
qname = Names.qualifyName sname
ns = Module.qualifiedNameNamespace qname
typeNameStr = typeNameForRecord sname
capitalizedTypeName = Formatting.capitalize typeNameStr
capitalizedFieldName = Formatting.capitalize fnameStr
deconflict =
\name ->
let tname =
Names.unqualifyName (Module.QualifiedName {
Module.qualifiedNameNamespace = ns,
Module.qualifiedNameLocal = name})
in (Logic.ifElse (Sets.member tname boundNames) (deconflict (Strings.cat2 name "_")) name)
nm = deconflict (Strings.cat2 capitalizedTypeName capitalizedFieldName)
qualName =
Module.QualifiedName {
Module.qualifiedNameNamespace = ns,
Module.qualifiedNameLocal = nm}
unqualName = Names.unqualifyName qualName
in (elementReference namespaces unqualName)
-- | Unpack nested forall types into a list of type variables and the inner type
unpackForallType :: Core.Type -> ([Core.Name], Core.Type)
unpackForallType t =
case (Rewriting.deannotateType t) of
Core.TypeForall v0 ->
let v = Core.forallTypeParameter v0
tbody = Core.forallTypeBody v0
recursiveResult = unpackForallType tbody
vars = Pairs.first recursiveResult
finalType = Pairs.second recursiveResult
in (Lists.cons v vars, finalType)
_ -> ([], t)