hydra-0.15.0: src/main/haskell/Hydra/Haskell/Serde.hs
-- Note: this is an automatically generated file. Do not edit.
-- | Haskell operator precendence and associativity are drawn from:
-- | https://self-learning-java-tutorial.blogspot.com/2016/04/haskell-operator-precedence.html
-- | Other operators were investigated using GHCi, e.g. ":info (->)"
-- | Operator names are drawn (loosely) from:
-- | https://stackoverflow.com/questions/7746894/are-there-pronounceable-names-for-common-haskell-operators
module Hydra.Haskell.Serde where
import qualified Hydra.Ast as Ast
import qualified Hydra.Constants as Constants
import qualified Hydra.Haskell.Operators as Operators
import qualified Hydra.Haskell.Syntax as Syntax
import qualified Hydra.Lib.Equality as Equality
import qualified Hydra.Lib.Lists as Lists
import qualified Hydra.Lib.Literals as Literals
import qualified Hydra.Lib.Logic as Logic
import qualified Hydra.Lib.Maybes as Maybes
import qualified Hydra.Lib.Strings as Strings
import qualified Hydra.Serialization as Serialization
import Prelude hiding (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci
-- | Convert a pattern-matching alternative to an AST expression
alternativeToExpr :: Syntax.Alternative -> Ast.Expr
alternativeToExpr alt =
Serialization.structuralSpaceSep [
patternToExpr (Syntax.alternativePattern alt),
(Serialization.cst "->"),
(caseRhsToExpr (Syntax.alternativeRhs alt))]
-- | Convert a function application expression to an AST expression
applicationExpressionToExpr :: Syntax.ApplicationExpression -> Ast.Expr
applicationExpressionToExpr app =
Serialization.ifx Operators.appOp (expressionToExpr (Syntax.applicationExpressionFunction app)) (expressionToExpr (Syntax.applicationExpressionArgument app))
-- | Convert an application pattern to an AST expression
applicationPatternToExpr :: Syntax.ApplicationPattern -> Ast.Expr
applicationPatternToExpr appPat =
let name = Syntax.applicationPatternName appPat
pats = Syntax.applicationPatternArgs appPat
in (Serialization.spaceSep (Lists.cons (nameToExpr name) (Lists.map patternToExpr pats)))
-- | Convert a type class assertion to an AST expression
assertionToExpr :: Syntax.Assertion -> Ast.Expr
assertionToExpr sert =
case sert of
Syntax.AssertionClass v0 -> classAssertionToExpr v0
Syntax.AssertionTuple v0 -> Serialization.parenList False (Lists.map assertionToExpr v0)
-- | Convert a case expression to an AST expression
caseExpressionToExpr :: Syntax.CaseExpression -> Ast.Expr
caseExpressionToExpr caseExpr =
let cs = Syntax.caseExpressionCase caseExpr
alts = Syntax.caseExpressionAlternatives caseExpr
ofOp =
Ast.Op {
Ast.opSymbol = (Ast.Symbol "of"),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsSpace,
Ast.paddingRight = (Ast.WsBreakAndIndent " ")},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone}
lhs =
Serialization.spaceSep [
Serialization.cst "case",
(expressionToExpr cs)]
rhs = Serialization.newlineSep (Lists.map alternativeToExpr alts)
in (Serialization.ifx ofOp lhs rhs)
-- | Convert a case right-hand side to an AST expression
caseRhsToExpr :: Syntax.CaseRhs -> Ast.Expr
caseRhsToExpr rhs = expressionToExpr (Syntax.unCaseRhs rhs)
-- | Convert a class assertion to an AST expression
classAssertionToExpr :: Syntax.ClassAssertion -> Ast.Expr
classAssertionToExpr clsAsrt =
let name = Syntax.classAssertionName clsAsrt
types = Syntax.classAssertionTypes clsAsrt
in (Serialization.spaceSep (Lists.cons (nameToExpr name) [
Serialization.commaSep Serialization.halfBlockStyle (Lists.map typeToExpr types)]))
-- | Convert a record construction expression to an AST expression
constructRecordExpressionToExpr :: Syntax.ConstructRecordExpression -> Ast.Expr
constructRecordExpressionToExpr constructRecord =
let name = Syntax.constructRecordExpressionName constructRecord
updates = Syntax.constructRecordExpressionFields constructRecord
fromUpdate =
\update ->
let fn = Syntax.fieldUpdateName update
val = Syntax.fieldUpdateValue update
in (Serialization.ifx Operators.defineOp (nameToExpr fn) (expressionToExpr val))
body = Serialization.commaSep Serialization.halfBlockStyle (Lists.map fromUpdate updates)
in (Serialization.spaceSep (Lists.cons (nameToExpr name) [
Serialization.brackets Serialization.curlyBraces Serialization.halfBlockStyle body]))
-- | Convert a data constructor to an AST expression
constructorToExpr :: Syntax.Constructor -> Ast.Expr
constructorToExpr cons =
case cons of
Syntax.ConstructorOrdinary v0 ->
let name = Syntax.ordinaryConstructorName v0
types = Syntax.ordinaryConstructorFields v0
in (Serialization.spaceSep (Lists.cons (nameToExpr name) [
Serialization.spaceSep (Lists.map typeToExpr types)]))
Syntax.ConstructorRecord v0 ->
let name = Syntax.recordConstructorName v0
fields = Syntax.recordConstructorFields v0
in (Serialization.spaceSep (Lists.cons (nameToExpr name) [
Serialization.curlyBracesList Nothing Serialization.halfBlockStyle (Lists.map fieldWithCommentsToExpr fields)]))
-- | Convert a data constructor with comments to an AST expression
constructorWithCommentsToExpr :: Syntax.ConstructorWithComments -> Ast.Expr
constructorWithCommentsToExpr consWithComments =
let body = Syntax.constructorWithCommentsBody consWithComments
mc = Syntax.constructorWithCommentsComments consWithComments
in (Maybes.maybe (constructorToExpr body) (\c -> Serialization.newlineSep (Lists.cons (Serialization.cst (toHaskellComments c)) [
constructorToExpr body])) mc)
-- | Convert a data/newtype keyword to an AST expression
dataOrNewtypeToExpr :: Syntax.DataOrNewtype -> Ast.Expr
dataOrNewtypeToExpr kw =
case kw of
Syntax.DataOrNewtypeData -> Serialization.cst "data"
Syntax.DataOrNewtypeNewtype -> Serialization.cst "newtype"
-- | Convert a declaration head to an AST expression
declarationHeadToExpr :: Syntax.DeclarationHead -> Ast.Expr
declarationHeadToExpr hd =
case hd of
Syntax.DeclarationHeadApplication v0 ->
let fun = Syntax.applicationDeclarationHeadFunction v0
op = Syntax.applicationDeclarationHeadOperand v0
in (Serialization.spaceSep (Lists.cons (declarationHeadToExpr fun) [
variableToExpr op]))
Syntax.DeclarationHeadSimple v0 -> nameToExpr v0
-- | Convert a declaration to an AST expression
declarationToExpr :: Syntax.Declaration -> Ast.Expr
declarationToExpr decl =
case decl of
Syntax.DeclarationData v0 ->
let kw = Syntax.dataDeclarationKeyword v0
hd = Syntax.dataDeclarationHead v0
cons = Syntax.dataDeclarationConstructors v0
deriv = Syntax.dataDeclarationDeriving v0
derivCat = Lists.concat (Lists.map Syntax.unDeriving deriv)
constructors = Serialization.orSep Serialization.halfBlockStyle (Lists.map constructorWithCommentsToExpr cons)
derivingClause =
Logic.ifElse (Lists.null derivCat) [] [
Serialization.spaceSep (Lists.cons (Serialization.cst "deriving") [
Serialization.parenList False (Lists.map nameToExpr derivCat)])]
mainParts =
[
Serialization.spaceSep (Lists.cons (dataOrNewtypeToExpr kw) (Lists.cons (declarationHeadToExpr hd) [
Serialization.cst "="])),
constructors]
in (Serialization.indentBlock (Lists.concat2 mainParts derivingClause))
Syntax.DeclarationType v0 ->
let hd = Syntax.typeDeclarationName v0
typ = Syntax.typeDeclarationType v0
in (Serialization.spaceSep (Lists.cons (Serialization.cst "type") (Lists.cons (declarationHeadToExpr hd) (Lists.cons (Serialization.cst "=") [
typeToExpr typ]))))
Syntax.DeclarationValueBinding v0 -> valueBindingToExpr v0
Syntax.DeclarationTypedBinding v0 ->
let typeSig = Syntax.typedBindingTypeSignature v0
vb = Syntax.typedBindingValueBinding v0
name = Syntax.typeSignatureName typeSig
htype = Syntax.typeSignatureType typeSig
in (Serialization.newlineSep (Lists.cons (Serialization.structuralSpaceSep [
nameToExpr name,
(Serialization.cst "::"),
(typeToExpr htype)]) [
valueBindingToExpr vb]))
-- | Convert a declaration with comments to an AST expression
declarationWithCommentsToExpr :: Syntax.DeclarationWithComments -> Ast.Expr
declarationWithCommentsToExpr declWithComments =
let body = Syntax.declarationWithCommentsBody declWithComments
mc = Syntax.declarationWithCommentsComments declWithComments
in (Maybes.maybe (declarationToExpr body) (\c -> Serialization.newlineSep (Lists.cons (Serialization.cst (toHaskellComments c)) [
declarationToExpr body])) mc)
-- | Convert a Haskell expression to an AST expression
expressionToExpr :: Syntax.Expression -> Ast.Expr
expressionToExpr expr =
case expr of
Syntax.ExpressionApplication v0 -> applicationExpressionToExpr v0
Syntax.ExpressionCase v0 -> caseExpressionToExpr v0
Syntax.ExpressionConstructRecord v0 -> constructRecordExpressionToExpr v0
Syntax.ExpressionDo v0 -> Serialization.indentBlock (Lists.cons (Serialization.cst "do") (Lists.map statementToExpr v0))
Syntax.ExpressionIf v0 -> ifExpressionToExpr v0
Syntax.ExpressionLiteral v0 -> literalToExpr v0
Syntax.ExpressionLambda v0 -> Serialization.parenthesize (lambdaExpressionToExpr v0)
Syntax.ExpressionLet v0 ->
let bindings = Syntax.letExpressionBindings v0
inner = Syntax.letExpressionInner v0
encodeBinding = \binding -> Serialization.indentSubsequentLines " " (localBindingToExpr binding)
in (Serialization.indentBlock (Lists.cons (Serialization.cst "") (Lists.cons (Serialization.spaceSep (Lists.cons (Serialization.cst "let") [
Serialization.customIndentBlock " " (Lists.map encodeBinding bindings)])) [
Serialization.spaceSep (Lists.cons (Serialization.cst "in") [
expressionToExpr inner])])))
Syntax.ExpressionList v0 -> Serialization.bracketList Serialization.halfBlockStyle (Lists.map expressionToExpr v0)
Syntax.ExpressionParens v0 -> Serialization.parenthesize (expressionToExpr v0)
Syntax.ExpressionTuple v0 -> Serialization.parenList False (Lists.map expressionToExpr v0)
Syntax.ExpressionVariable v0 -> nameToExpr v0
-- | Convert a field declaration to an AST expression
fieldToExpr :: Syntax.Field -> Ast.Expr
fieldToExpr field =
let name = Syntax.fieldName field
typ = Syntax.fieldType field
in (Serialization.spaceSep (Lists.cons (nameToExpr name) (Lists.cons (Serialization.cst "::") [
typeToExpr typ])))
-- | Convert a field with comments to an AST expression
fieldWithCommentsToExpr :: Syntax.FieldWithComments -> Ast.Expr
fieldWithCommentsToExpr fieldWithComments =
let field = Syntax.fieldWithCommentsField fieldWithComments
mc = Syntax.fieldWithCommentsComments fieldWithComments
in (Maybes.maybe (fieldToExpr field) (\c -> Serialization.newlineSep (Lists.cons (Serialization.cst (toHaskellComments c)) [
fieldToExpr field])) mc)
-- | Convert an if-then-else expression to an AST expression
ifExpressionToExpr :: Syntax.IfExpression -> Ast.Expr
ifExpressionToExpr ifExpr =
let eif = Syntax.ifExpressionCondition ifExpr
ethen = Syntax.ifExpressionThen ifExpr
eelse = Syntax.ifExpressionElse ifExpr
ifOp =
Ast.Op {
Ast.opSymbol = (Ast.Symbol ""),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsNone,
Ast.paddingRight = (Ast.WsBreakAndIndent " ")},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone}
body =
Serialization.newlineSep (Lists.cons (Serialization.spaceSep (Lists.cons (Serialization.cst "then") [
expressionToExpr ethen])) [
Serialization.spaceSep (Lists.cons (Serialization.cst "else") [
expressionToExpr eelse])])
in (Serialization.ifx ifOp (Serialization.spaceSep (Lists.cons (Serialization.cst "if") [
expressionToExpr eif])) body)
-- | Convert an import/export specification to an AST expression
importExportSpecToExpr :: Syntax.ImportExportSpec -> Ast.Expr
importExportSpecToExpr spec = nameToExpr (Syntax.importExportSpecName spec)
-- | Convert an import statement to an AST expression
importToExpr :: Syntax.Import -> Ast.Expr
importToExpr import_ =
let qual = Syntax.importQualified import_
modName = Syntax.importModule import_
mod = Syntax.importAs import_
mspec = Syntax.importSpec import_
name = Syntax.unModuleName modName
hidingSec =
\spec -> case spec of
Syntax.SpecImportHiding v0 -> Serialization.spaceSep (Lists.cons (Serialization.cst "hiding ") [
Serialization.parens (Serialization.commaSep Serialization.inlineStyle (Lists.map importExportSpecToExpr v0))])
parts =
Maybes.cat [
Just (Serialization.cst "import"),
(Logic.ifElse qual (Just (Serialization.cst "qualified")) Nothing),
(Just (Serialization.cst name)),
(Maybes.map (\m -> Serialization.cst (Strings.cat2 "as " (Syntax.unModuleName m))) mod),
(Maybes.map hidingSec mspec)]
in (Serialization.spaceSep parts)
-- | Convert a lambda expression to an AST expression
lambdaExpressionToExpr :: Syntax.LambdaExpression -> Ast.Expr
lambdaExpressionToExpr lambdaExpr =
let bindings = Syntax.lambdaExpressionBindings lambdaExpr
inner = Syntax.lambdaExpressionInner lambdaExpr
head = Serialization.spaceSep (Lists.map patternToExpr bindings)
body = expressionToExpr inner
in (Serialization.ifx Operators.lambdaOp (Serialization.prefix "\\" head) body)
-- | Convert a literal value to an AST expression
literalToExpr :: Syntax.Literal -> Ast.Expr
literalToExpr lit =
let parensIfNeg =
\b -> \e -> Logic.ifElse b (Strings.cat [
"(",
e,
")"]) e
showFloat =
\showFn -> \v ->
let raw = showFn v
in (Logic.ifElse (Equality.equal raw "NaN") "(0/0)" (Logic.ifElse (Equality.equal raw "Infinity") "(1/0)" (Logic.ifElse (Equality.equal raw "-Infinity") "(-(1/0))" (parensIfNeg (Equality.equal (Maybes.fromMaybe 0 (Strings.maybeCharAt 0 raw)) 45) raw))))
in (Serialization.cst (case lit of
Syntax.LiteralChar v0 -> Literals.showString (Literals.showUint16 v0)
Syntax.LiteralDouble v0 -> showFloat (\v -> Literals.showFloat64 v) v0
Syntax.LiteralFloat v0 -> showFloat (\v -> Literals.showFloat32 v) v0
Syntax.LiteralInt v0 -> parensIfNeg (Equality.lt v0 0) (Literals.showInt32 v0)
Syntax.LiteralInteger v0 -> parensIfNeg (Equality.lt v0 0) (Literals.showBigint v0)
Syntax.LiteralString v0 -> Literals.showString v0))
-- | Convert a local binding to an AST expression
localBindingToExpr :: Syntax.LocalBinding -> Ast.Expr
localBindingToExpr binding =
case binding of
Syntax.LocalBindingSignature v0 -> typeSignatureToExpr v0
Syntax.LocalBindingValue v0 -> valueBindingToExpr v0
-- | Convert a module head to an AST expression
moduleHeadToExpr :: Syntax.ModuleHead -> Ast.Expr
moduleHeadToExpr moduleHead =
let mc = Syntax.moduleHeadComments moduleHead
modName = Syntax.moduleHeadName moduleHead
mname = Syntax.unModuleName modName
head =
Serialization.spaceSep (Lists.cons (Serialization.cst "module") (Lists.cons (Serialization.cst mname) [
Serialization.cst "where"]))
in (Maybes.maybe head (\c -> Serialization.newlineSep (Lists.cons (Serialization.cst (toHaskellComments c)) (Lists.cons (Serialization.cst "") [
head]))) mc)
-- | Convert a Haskell module to an AST expression
moduleToExpr :: Syntax.Module -> Ast.Expr
moduleToExpr module_ =
let mh = Syntax.moduleHead module_
imports = Syntax.moduleImports module_
decls = Syntax.moduleDeclarations module_
warning = [
Serialization.cst (toSimpleComments Constants.warningAutoGeneratedFile)]
headerLine = Maybes.maybe [] (\h -> [
moduleHeadToExpr h]) mh
declLines = Lists.map declarationWithCommentsToExpr decls
importLines = Logic.ifElse (Lists.null imports) [] [
Serialization.newlineSep (Lists.map importToExpr imports)]
in (Serialization.doubleNewlineSep (Lists.concat [
warning,
headerLine,
importLines,
declLines]))
-- | Convert a Haskell name to an AST expression
nameToExpr :: Syntax.Name -> Ast.Expr
nameToExpr name =
Serialization.cst (case name of
Syntax.NameImplicit v0 -> Strings.cat2 "?" (writeQualifiedName v0)
Syntax.NameNormal v0 -> writeQualifiedName v0
Syntax.NameParens v0 -> Strings.cat [
"(",
(writeQualifiedName v0),
")"])
-- | Convert a pattern to an AST expression
patternToExpr :: Syntax.Pattern -> Ast.Expr
patternToExpr pat =
case pat of
Syntax.PatternApplication v0 -> applicationPatternToExpr v0
Syntax.PatternList v0 -> Serialization.bracketList Serialization.halfBlockStyle (Lists.map patternToExpr v0)
Syntax.PatternLiteral v0 -> literalToExpr v0
Syntax.PatternName v0 -> nameToExpr v0
Syntax.PatternParens v0 -> Serialization.parenthesize (patternToExpr v0)
Syntax.PatternTuple v0 -> Serialization.parenList False (Lists.map patternToExpr v0)
Syntax.PatternWildcard -> Serialization.cst "_"
-- | Convert a right-hand side to an AST expression
rightHandSideToExpr :: Syntax.RightHandSide -> Ast.Expr
rightHandSideToExpr rhs = expressionToExpr (Syntax.unRightHandSide rhs)
-- | Convert a statement to an AST expression
statementToExpr :: Syntax.Statement -> Ast.Expr
statementToExpr stmt = expressionToExpr (Syntax.unStatement stmt)
-- | Convert a string to Haddock documentation comments. Empty source lines emit `-- |` (no trailing space) so blank doc lines don't carry trailing whitespace into the generated file.
toHaskellComments :: String -> String
toHaskellComments c =
Strings.intercalate "\n" (Lists.map (\s -> Logic.ifElse (Equality.equal s "") "-- |" (Strings.cat2 "-- | " s)) (Strings.lines c))
-- | Convert a string to simple line comments. Empty source lines emit `--` (no trailing space) for the same reason as toHaskellComments.
toSimpleComments :: String -> String
toSimpleComments c =
Strings.intercalate "\n" (Lists.map (\s -> Logic.ifElse (Equality.equal s "") "--" (Strings.cat2 "-- " s)) (Strings.lines c))
-- | Convert a type signature to an AST expression
typeSignatureToExpr :: Syntax.TypeSignature -> Ast.Expr
typeSignatureToExpr typeSig =
let name = Syntax.typeSignatureName typeSig
typ = Syntax.typeSignatureType typeSig
nameExpr = nameToExpr name
typeExpr = typeToExpr typ
inlineSig =
Serialization.structuralSpaceSep [
nameExpr,
(Serialization.cst "::"),
typeExpr]
in (Logic.ifElse (Equality.gt (Serialization.expressionLength inlineSig) 120) (Serialization.newlineSep [
Serialization.spaceSep [
nameExpr,
(Serialization.cst "::")],
(Serialization.tabIndent typeExpr)]) inlineSig)
-- | Convert a Haskell type to an AST expression
typeToExpr :: Syntax.Type -> Ast.Expr
typeToExpr htype =
case htype of
Syntax.TypeApplication v0 ->
let lhs = Syntax.applicationTypeContext v0
rhs = Syntax.applicationTypeArgument v0
in (Serialization.ifx Operators.appOp (typeToExpr lhs) (typeToExpr rhs))
Syntax.TypeCtx v0 ->
let ctx = Syntax.contextTypeCtx v0
typ = Syntax.contextTypeType v0
in (Serialization.ifx Operators.assertOp (assertionToExpr ctx) (typeToExpr typ))
Syntax.TypeFunction v0 ->
let dom = Syntax.functionTypeDomain v0
cod = Syntax.functionTypeCodomain v0
in (Serialization.ifx Operators.arrowOp (typeToExpr dom) (typeToExpr cod))
Syntax.TypeList v0 -> Serialization.bracketList Serialization.inlineStyle [
typeToExpr v0]
Syntax.TypeTuple v0 -> Serialization.parenList False (Lists.map typeToExpr v0)
Syntax.TypeVariable v0 -> nameToExpr v0
-- | Convert a value binding to an AST expression
valueBindingToExpr :: Syntax.ValueBinding -> Ast.Expr
valueBindingToExpr vb =
case vb of
Syntax.ValueBindingSimple v0 ->
let pat = Syntax.simpleValueBindingPattern v0
rhs = Syntax.simpleValueBindingRhs v0
local = Syntax.simpleValueBindingLocalBindings v0
lhsExpr = patternToExpr pat
rhsExpr = rightHandSideToExpr rhs
inlineBody =
Serialization.structuralSpaceSep [
lhsExpr,
(Serialization.cst "="),
rhsExpr]
body =
Logic.ifElse (Equality.gt (Serialization.expressionLength inlineBody) 120) (Serialization.newlineSep [
Serialization.spaceSep [
lhsExpr,
(Serialization.cst "=")],
(Serialization.tabIndent rhsExpr)]) inlineBody
in (Maybes.maybe body (\localBindings ->
let bindings = Syntax.unLocalBindings localBindings
in (Serialization.indentBlock (Lists.cons body [
Serialization.indentBlock (Lists.cons (Serialization.cst "where") (Lists.map localBindingToExpr bindings))]))) local)
-- | Convert a type variable to an AST expression
variableToExpr :: Syntax.Variable -> Ast.Expr
variableToExpr variable = nameToExpr (Syntax.unVariable variable)
-- | Write a qualified name as a string
writeQualifiedName :: Syntax.QualifiedName -> String
writeQualifiedName qname =
let qualifiers = Syntax.qualifiedNameQualifiers qname
unqual = Syntax.qualifiedNameUnqualified qname
h = \namePart -> Syntax.unNamePart namePart
allParts = Lists.concat2 (Lists.map h qualifiers) [
h unqual]
in (Strings.intercalate "." allParts)