hydra-kernel-0.17.0: src/main/haskell/Hydra/Serialization.hs
-- Note: this is an automatically generated file. Do not edit.
-- | Utilities for constructing generic program code ASTs, used for the serialization phase of source code generation.
module Hydra.Serialization where
import qualified Hydra.Ast as Ast
import qualified Hydra.Coders as Coders
import qualified Hydra.Core as Core
import qualified Hydra.Docs as Docs
import qualified Hydra.Error.Checking as Checking
import qualified Hydra.Error.Core as ErrorCore
import qualified Hydra.Error.File as ErrorFile
import qualified Hydra.Error.Packaging as ErrorPackaging
import qualified Hydra.Error.System as ErrorSystem
import qualified Hydra.Errors as Errors
import qualified Hydra.File as File
import qualified Hydra.Graph as Graph
import qualified Hydra.Json.Model as Model
import qualified Hydra.Overlay.Haskell.Lib.Equality as Equality
import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
import qualified Hydra.Overlay.Haskell.Lib.Literals as Literals
import qualified Hydra.Overlay.Haskell.Lib.Logic as Logic
import qualified Hydra.Overlay.Haskell.Lib.Math as Math
import qualified Hydra.Overlay.Haskell.Lib.Optionals as Optionals
import qualified Hydra.Overlay.Haskell.Lib.Pairs as Pairs
import qualified Hydra.Overlay.Haskell.Lib.Strings as Strings
import qualified Hydra.Packaging as Packaging
import qualified Hydra.Parsing as Parsing
import qualified Hydra.Paths as Paths
import qualified Hydra.Query as Query
import qualified Hydra.Relational as Relational
import qualified Hydra.System as System
import qualified Hydra.Tabular as Tabular
import qualified Hydra.Testing as Testing
import qualified Hydra.Time as Time
import qualified Hydra.Topology as Topology
import qualified Hydra.Typed as Typed
import qualified Hydra.Typing as Typing
import qualified Hydra.Util as Util
import qualified Hydra.Validation as Validation
import qualified Hydra.Variants as Variants
import Prelude hiding (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci
-- | Angle bracket pair `<` `>` for use with `brackets`
angleBraces :: Ast.Brackets
angleBraces =
Ast.Brackets {
Ast.bracketsOpen = (Ast.Symbol "<"),
Ast.bracketsClose = (Ast.Symbol ">")}
-- | Comma-separate the elements inside angle brackets in the given block style; renders as `<>` when empty
angleBracesList :: Ast.BlockStyle -> [Ast.Expr] -> Ast.Expr
angleBracesList style els = Logic.ifElse (Lists.null els) (cst "<>") (brackets angleBraces style (commaSep style els))
-- | Produce a bracketed list which separates elements by spaces or newlines depending on the estimated width of the expression.
bracesListAdaptive :: [Ast.Expr] -> Ast.Expr
bracesListAdaptive els =
chooseLayout maxLineWidth (curlyBracesList Nothing inlineStyle els) (curlyBracesList Nothing halfBlockStyle els)
-- | Comma-separate the elements inside square brackets in the given block style; renders as `[]` when empty
bracketList :: Ast.BlockStyle -> [Ast.Expr] -> Ast.Expr
bracketList style els = Logic.ifElse (Lists.null els) (cst "[]") (brackets squareBrackets style (commaSep style els))
-- | Produce a bracketed list which separates elements by spaces or newlines depending on the estimated width of the expression.
bracketListAdaptive :: [Ast.Expr] -> Ast.Expr
bracketListAdaptive els = chooseLayout maxLineWidth (bracketList inlineStyle els) (bracketList halfBlockStyle els)
-- | Wrap an expression in the given bracket pair using the given block style
brackets :: Ast.Brackets -> Ast.BlockStyle -> Ast.Expr -> Ast.Expr
brackets br style e =
Ast.ExprBrackets (Ast.BracketExpr {
Ast.bracketExprBrackets = br,
Ast.bracketExprEnclosed = e,
Ast.bracketExprStyle = style})
-- | Pick between an inline and a multi-line layout for the same logical expression. Returns the inline form if its estimated width does not exceed the threshold, otherwise the block form. Note: the threshold is measured against the inline expression in isolation; surrounding indentation may push content beyond the threshold at print time. Callers that know they are nested can pass a smaller threshold to compensate.
chooseLayout :: Int -> Ast.Expr -> Ast.Expr -> Ast.Expr
chooseLayout threshold inlineExpr blockExpr =
Logic.ifElse (Equality.gt (expressionLength inlineExpr) threshold) blockExpr inlineExpr
-- | Separate elements with a comma, using the given block style
commaSep :: Ast.BlockStyle -> [Ast.Expr] -> Ast.Expr
commaSep = symbolSep ","
-- | Comma-separate elements inline if the joined width fits, otherwise break onto separate lines.
commaSepAdaptive :: [Ast.Expr] -> Ast.Expr
commaSepAdaptive els = chooseLayout maxLineWidth (commaSep inlineStyle els) (commaSep halfBlockStyle els)
-- | Construct a constant expression from a literal string
cst :: String -> Ast.Expr
cst s = Ast.ExprConst (sym s)
-- | Wrap a single expression in curly braces using the given block style
curlyBlock :: Ast.BlockStyle -> Ast.Expr -> Ast.Expr
curlyBlock style e = curlyBracesList Nothing style [
e]
-- | Curly brace pair `{` `}` for use with `brackets`
curlyBraces :: Ast.Brackets
curlyBraces =
Ast.Brackets {
Ast.bracketsOpen = (Ast.Symbol "{"),
Ast.bracketsClose = (Ast.Symbol "}")}
-- | Separate the elements inside curly braces using the given symbol (default `,`) in the given block style; renders as `{}` when empty
curlyBracesList :: Maybe String -> Ast.BlockStyle -> [Ast.Expr] -> Ast.Expr
curlyBracesList msymb style els =
Logic.ifElse (Lists.null els) (cst "{}") (brackets curlyBraces style (symbolSep (Optionals.fromOptional "," msymb) style els))
-- | Indent every non-empty line of `s` by `idt`. Empty lines stay empty (no trailing whitespace) so downstream byte-identity checks don't care about indent depth.
customIndent :: String -> String -> String
customIndent idt s =
Strings.cat (Lists.intersperse "\n" (Lists.map (\line -> Logic.ifElse (Equality.equal line "") line (Strings.cat2 idt line)) (Strings.lines s)))
-- | Render a list of expressions as a block whose first element starts where the surrounding context placed it and subsequent elements break onto fresh lines indented by the given prefix
customIndentBlock :: String -> [Ast.Expr] -> Ast.Expr
customIndentBlock idt els =
let idtOp =
Ast.Op {
Ast.opSymbol = (sym ""),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsSpace,
Ast.paddingRight = (Ast.WsBreakAndIndent idt)},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone}
in (Optionals.cases (Lists.maybeHead els) (cst "") (\head -> Logic.ifElse (Equality.equal (Lists.length els) 1) head (ifx idtOp head (newlineSep (Lists.drop 1 els)))))
-- | Separate elements with a dot and no surrounding whitespace
dotSep :: [Ast.Expr] -> Ast.Expr
dotSep =
sep (Ast.Op {
Ast.opSymbol = (sym "."),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsNone,
Ast.paddingRight = Ast.WsNone},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone})
-- | Separate elements with a blank line between them
doubleNewlineSep :: [Ast.Expr] -> Ast.Expr
doubleNewlineSep =
sep (Ast.Op {
Ast.opSymbol = (sym ""),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsBreak,
Ast.paddingRight = Ast.WsBreak},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone})
-- | The two-space string used as the canonical indent unit
doubleSpace :: String
doubleSpace = " "
-- | Find the approximate length (number of characters, including spaces and newlines) of an expression without actually printing it.
expressionLength :: Ast.Expr -> Int
expressionLength e =
let symbolLength = \s -> Strings.length (Ast.unSymbol s)
wsLength =
\ws -> case ws of
Ast.WsNone -> 0
Ast.WsSpace -> 1
Ast.WsBreak -> 10000
Ast.WsBreakAndIndent _ -> 10000
Ast.WsDoubleBreak -> 10000
blockStyleLength =
\style ->
let mindentLen = Optionals.cases (Ast.blockStyleIndent style) 0 Strings.length
nlBeforeLen = Logic.ifElse (Ast.blockStyleNewlineBeforeContent style) 1 0
nlAfterLen = Logic.ifElse (Ast.blockStyleNewlineAfterContent style) 1 0
in (Math.add mindentLen (Math.add nlBeforeLen nlAfterLen))
bracketsLength =
\brackets -> Math.add (symbolLength (Ast.bracketsOpen brackets)) (symbolLength (Ast.bracketsClose brackets))
bracketExprLength =
\be -> Math.add (bracketsLength (Ast.bracketExprBrackets be)) (Math.add (expressionLength (Ast.bracketExprEnclosed be)) (blockStyleLength (Ast.bracketExprStyle be)))
indentedExpressionLength =
\ie ->
let baseLen = expressionLength (Ast.indentedExpressionExpr ie)
indentLen =
case (Ast.indentedExpressionStyle ie) of
Ast.IndentStyleAllLines v0 -> Strings.length v0
Ast.IndentStyleSubsequentLines v0 -> Strings.length v0
in (Math.add baseLen indentLen)
opLength =
\op ->
let symLen = symbolLength (Ast.opSymbol op)
padding = Ast.opPadding op
leftLen = wsLength (Ast.paddingLeft padding)
rightLen = wsLength (Ast.paddingRight padding)
in (Math.add symLen (Math.add leftLen rightLen))
opExprLength =
\oe ->
let opLen = opLength (Ast.opExprOp oe)
leftLen = expressionLength (Ast.opExprLhs oe)
rightLen = expressionLength (Ast.opExprRhs oe)
in (Math.add opLen (Math.add leftLen rightLen))
seqExprLength =
\se ->
let sopLen = opLength (Ast.seqExprOp se)
elementLens = Lists.map expressionLength (Ast.seqExprElements se)
totalElLen = Lists.foldl Math.add 0 elementLens
numSeps = Math.sub (Lists.length (Ast.seqExprElements se)) 1
in (Math.add totalElLen (Math.mul sopLen (Logic.ifElse (Equality.gt numSeps 0) numSeps 0)))
in case e of
Ast.ExprConst v0 -> symbolLength v0
Ast.ExprIndent v0 -> indentedExpressionLength v0
Ast.ExprOp v0 -> opExprLength v0
Ast.ExprBrackets v0 -> bracketExprLength v0
Ast.ExprSeq v0 -> seqExprLength v0
-- | Block style with double-space indent and newlines both before and after the bracketed content
fullBlockStyle :: Ast.BlockStyle
fullBlockStyle =
Ast.BlockStyle {
Ast.blockStyleIndent = (Just doubleSpace),
Ast.blockStyleNewlineBeforeContent = True,
Ast.blockStyleNewlineAfterContent = True}
-- | Block style with double-space indent, a newline before the content but not after
halfBlockStyle :: Ast.BlockStyle
halfBlockStyle =
Ast.BlockStyle {
Ast.blockStyleIndent = (Just doubleSpace),
Ast.blockStyleNewlineBeforeContent = True,
Ast.blockStyleNewlineAfterContent = False}
-- | Build an infix expression with the given operator and two operands
ifx :: Ast.Op -> Ast.Expr -> Ast.Expr -> Ast.Expr
ifx op lhs rhs =
Ast.ExprOp (Ast.OpExpr {
Ast.opExprOp = op,
Ast.opExprLhs = lhs,
Ast.opExprRhs = rhs})
-- | Indent every non-empty line of a string by `doubleSpace`
indent :: String -> String
indent = customIndent doubleSpace
-- | Like `customIndentBlock`, but using `doubleSpace` as the indent prefix
indentBlock :: [Ast.Expr] -> Ast.Expr
indentBlock = customIndentBlock doubleSpace
-- | Indent every line of an expression except the first by the given prefix
indentSubsequentLines :: String -> Ast.Expr -> Ast.Expr
indentSubsequentLines idt e =
Ast.ExprIndent (Ast.IndentedExpression {
Ast.indentedExpressionStyle = (Ast.IndentStyleSubsequentLines idt),
Ast.indentedExpressionExpr = e})
-- | Space-separate three elements: left operand, operator string, right operand
infixWs :: String -> Ast.Expr -> Ast.Expr -> Ast.Expr
infixWs op l r =
spaceSep [
l,
(cst op),
r]
-- | Space-separate operands interleaved with the given operator string
infixWsList :: String -> [Ast.Expr] -> Ast.Expr
infixWsList op opers =
let opExpr = cst op
foldFun = \e -> \r -> Logic.ifElse (Lists.null e) [
r] (Lists.cons r (Lists.cons opExpr e))
in (spaceSep (Lists.foldl foldFun [] (Lists.reverse opers)))
-- | Block style with no indent and no surrounding newlines: contents stay on a single line
inlineStyle :: Ast.BlockStyle
inlineStyle =
Ast.BlockStyle {
Ast.blockStyleIndent = Nothing,
Ast.blockStyleNewlineBeforeContent = False,
Ast.blockStyleNewlineAfterContent = False}
-- | The canonical maximum line width used by Hydra writers. Adaptive helpers compare estimated expression widths against this threshold to decide whether to render inline or break across lines. Set to 120 to match the project-wide line-length convention.
maxLineWidth :: Int
maxLineWidth = 120
-- | Separate elements by a newline (no leading whitespace)
newlineSep :: [Ast.Expr] -> Ast.Expr
newlineSep =
sep (Ast.Op {
Ast.opSymbol = (sym ""),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsNone,
Ast.paddingRight = Ast.WsBreak},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone})
-- | Operator padding with no whitespace on either side
noPadding :: Ast.Padding
noPadding =
Ast.Padding {
Ast.paddingLeft = Ast.WsNone,
Ast.paddingRight = Ast.WsNone}
-- | Concatenate elements with no separator and no whitespace
noSep :: [Ast.Expr] -> Ast.Expr
noSep =
sep (Ast.Op {
Ast.opSymbol = (sym ""),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsNone,
Ast.paddingRight = Ast.WsNone},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone})
-- | Construct a constant expression from an int32
num :: Int -> Ast.Expr
num i = cst (Literals.showInt32 i)
-- | Build an op with single-space padding from a symbol, precedence, and associativity
op :: String -> Int -> Ast.Associativity -> Ast.Op
op s p assoc =
Ast.Op {
Ast.opSymbol = (sym s),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsSpace,
Ast.paddingRight = Ast.WsSpace},
Ast.opPrecedence = (Ast.Precedence p),
Ast.opAssociativity = assoc}
-- | Build the `|` alternative-separator operator. The flag selects whether the right side breaks onto a new line.
orOp :: Bool -> Ast.Op
orOp newlines =
Ast.Op {
Ast.opSymbol = (sym "|"),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsSpace,
Ast.paddingRight = (Logic.ifElse newlines Ast.WsBreak Ast.WsSpace)},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone}
-- | Separate alternatives with `|`, breaking onto separate lines when the block style requests a newline before content
orSep :: Ast.BlockStyle -> [Ast.Expr] -> Ast.Expr
orSep style l =
let newlines = Ast.blockStyleNewlineBeforeContent style
in (Optionals.cases (Lists.maybeHead l) (cst "") (\h -> Lists.foldl (\acc -> \el -> ifx (orOp newlines) acc el) h (Lists.drop 1 l)))
-- | Comma-separate the elements inside parentheses; switches to a half-block style when newlines are requested and there is more than one element. Renders as `()` when empty.
parenList :: Bool -> [Ast.Expr] -> Ast.Expr
parenList newlines els =
let style = Logic.ifElse (Logic.and newlines (Equality.gt (Lists.length els) 1)) halfBlockStyle inlineStyle
in (Logic.ifElse (Lists.null els) (cst "()") (brackets parentheses style (commaSep style els)))
-- | Produce a parenthesized list which separates elements by spaces or newlines depending on the estimated width of the expression.
parenListAdaptive :: [Ast.Expr] -> Ast.Expr
parenListAdaptive els = chooseLayout maxLineWidth (parenList False els) (parenList True els)
-- | Wrap an expression in inline parentheses
parens :: Ast.Expr -> Ast.Expr
parens = brackets parentheses inlineStyle
-- | Round parenthesis pair `(` `)` for use with `brackets`
parentheses :: Ast.Brackets
parentheses =
Ast.Brackets {
Ast.bracketsOpen = (Ast.Symbol "("),
Ast.bracketsClose = (Ast.Symbol ")")}
-- | Recursively insert parentheses around subexpressions where required by operator precedence and associativity. The traversal descends into bracket, indent, sequence, and operator expressions and parenthesizes left or right operands whose binding is weaker than the surrounding operator.
parenthesize :: Ast.Expr -> Ast.Expr
parenthesize exp =
let assocLeft =
\a -> case a of
Ast.AssociativityRight -> False
_ -> True
assocRight =
\a -> case a of
Ast.AssociativityLeft -> False
_ -> True
in case exp of
Ast.ExprBrackets v0 -> Ast.ExprBrackets (Ast.BracketExpr {
Ast.bracketExprBrackets = (Ast.bracketExprBrackets v0),
Ast.bracketExprEnclosed = (parenthesize (Ast.bracketExprEnclosed v0)),
Ast.bracketExprStyle = (Ast.bracketExprStyle v0)})
Ast.ExprConst _ -> exp
Ast.ExprIndent v0 -> Ast.ExprIndent (Ast.IndentedExpression {
Ast.indentedExpressionStyle = (Ast.indentedExpressionStyle v0),
Ast.indentedExpressionExpr = (parenthesize (Ast.indentedExpressionExpr v0))})
Ast.ExprSeq v0 -> Ast.ExprSeq (Ast.SeqExpr {
Ast.seqExprOp = (Ast.seqExprOp v0),
Ast.seqExprElements = (Lists.map parenthesize (Ast.seqExprElements v0))})
Ast.ExprOp v0 ->
let op = Ast.opExprOp v0
prec = Ast.unPrecedence (Ast.opPrecedence op)
assoc = Ast.opAssociativity op
lhs = Ast.opExprLhs v0
rhs = Ast.opExprRhs v0
lhs_ = parenthesize lhs
rhs_ = parenthesize rhs
lhs2 =
case lhs_ of
Ast.ExprOp v1 ->
let lop = Ast.opExprOp v1
lprec = Ast.unPrecedence (Ast.opPrecedence lop)
lassoc = Ast.opAssociativity lop
comparison = Equality.compare prec lprec
in case comparison of
Util.ComparisonLessThan -> lhs_
Util.ComparisonGreaterThan -> parens lhs_
Util.ComparisonEqualTo -> Logic.ifElse (Logic.and (assocLeft assoc) (assocLeft lassoc)) lhs_ (parens lhs_)
_ -> lhs_
rhs2 =
case rhs_ of
Ast.ExprOp v1 ->
let rop = Ast.opExprOp v1
rprec = Ast.unPrecedence (Ast.opPrecedence rop)
rassoc = Ast.opAssociativity rop
comparison = Equality.compare prec rprec
in case comparison of
Util.ComparisonLessThan -> rhs_
Util.ComparisonGreaterThan -> parens rhs_
Util.ComparisonEqualTo -> Logic.ifElse (Logic.and (assocRight assoc) (assocRight rassoc)) rhs_ (parens rhs_)
_ -> rhs_
in (Ast.ExprOp (Ast.OpExpr {
Ast.opExprOp = op,
Ast.opExprLhs = lhs2,
Ast.opExprRhs = rhs2}))
-- | Prepend a prefix string to an expression with no surrounding whitespace
prefix :: String -> Ast.Expr -> Ast.Expr
prefix p expr =
let preOp =
Ast.Op {
Ast.opSymbol = (sym p),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsNone,
Ast.paddingRight = Ast.WsNone},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone}
in (ifx preOp (cst "") expr)
-- | Render an expression to a string, expanding bracket pairs, block styles, indents, and operator chains
printExpr :: Ast.Expr -> String
printExpr e =
let pad =
\ws -> case ws of
Ast.WsNone -> ""
Ast.WsSpace -> " "
Ast.WsBreak -> "\n"
Ast.WsBreakAndIndent _ -> "\n"
Ast.WsDoubleBreak -> "\n\n"
idt =
\ws -> \s -> case ws of
Ast.WsBreakAndIndent v0 -> customIndent v0 s
_ -> s
in case e of
Ast.ExprConst v0 -> Ast.unSymbol v0
Ast.ExprIndent v0 ->
let style = Ast.indentedExpressionStyle v0
expr = Ast.indentedExpressionExpr v0
lns = Strings.lines (printExpr expr)
indentLine = \pre -> \line -> Logic.ifElse (Equality.equal line "") line (Strings.cat2 pre line)
ilns =
case style of
Ast.IndentStyleAllLines v1 -> Lists.map (indentLine v1) lns
Ast.IndentStyleSubsequentLines v1 -> Logic.ifElse (Equality.equal (Lists.length lns) 1) lns (Optionals.fromOptional lns (Optionals.map (\uc -> Lists.cons (Pairs.first uc) (Lists.map (indentLine v1) (Pairs.second uc))) (Lists.uncons lns)))
in (Strings.intercalate "\n" ilns)
Ast.ExprSeq v0 ->
let sop = Ast.seqExprOp v0
ssym = Ast.unSymbol (Ast.opSymbol sop)
spadding = Ast.opPadding sop
spadl = Ast.paddingLeft spadding
spadr = Ast.paddingRight spadding
selements = Ast.seqExprElements v0
printedElements = Lists.map (\el -> idt spadr (printExpr el)) selements
isNewlineWs =
\ws -> case ws of
Ast.WsBreak -> True
Ast.WsBreakAndIndent _ -> True
Ast.WsDoubleBreak -> True
_ -> False
spadlIsNewline = isNewlineWs spadl
spadrIsNewline = isNewlineWs spadr
joinElements =
\acc -> \el ->
let elStartsWithNewline = Optionals.cases (Strings.maybeCharAt 0 el) False (\c -> Equality.equal c 10)
elIsEmpty = Equality.equal el ""
padlEff =
Logic.ifElse (Logic.or (Logic.and elStartsWithNewline (Logic.not spadlIsNewline)) (Logic.and elIsEmpty (Equality.equal ssym ""))) "" (pad spadl)
padrEff =
Logic.ifElse (Logic.or (Logic.and elStartsWithNewline (Logic.not spadrIsNewline)) (Logic.and elIsEmpty (Equality.equal ssym ""))) "" (pad spadr)
in (Strings.cat [
acc,
padlEff,
ssym,
padrEff,
el])
in (Optionals.cases (Lists.maybeHead printedElements) "" (\h -> Lists.foldl joinElements h (Lists.drop 1 printedElements)))
Ast.ExprOp v0 ->
let op = Ast.opExprOp v0
sym = Ast.unSymbol (Ast.opSymbol op)
padding = Ast.opPadding op
padl = Ast.paddingLeft padding
padr = Ast.paddingRight padding
l = Ast.opExprLhs v0
r = Ast.opExprRhs v0
lhs = idt padl (printExpr l)
rhs = idt padr (printExpr r)
padrIsNewline =
case padr of
Ast.WsBreak -> True
Ast.WsBreakAndIndent _ -> True
Ast.WsDoubleBreak -> True
_ -> False
padlIsNewline =
case padl of
Ast.WsBreak -> True
Ast.WsDoubleBreak -> True
_ -> False
padlEffective =
Logic.ifElse (Logic.and (Logic.not padlIsNewline) (Logic.and (Equality.equal sym "") (Logic.or padrIsNewline (Equality.equal rhs "")))) "" (pad padl)
padrPad = pad padr
rhsStartsWithNewline = Optionals.cases (Strings.maybeCharAt 0 rhs) False (\c -> Equality.equal c 10)
padrEffective = Logic.ifElse (Logic.and rhsStartsWithNewline (Logic.not padrIsNewline)) "" padrPad
in (Strings.cat2 (Strings.cat2 (Strings.cat2 (Strings.cat2 lhs padlEffective) sym) padrEffective) rhs)
Ast.ExprBrackets v0 ->
let brs = Ast.bracketExprBrackets v0
l = Ast.unSymbol (Ast.bracketsOpen brs)
r = Ast.unSymbol (Ast.bracketsClose brs)
e = Ast.bracketExprEnclosed v0
style = Ast.bracketExprStyle v0
body = printExpr e
doIndent = Ast.blockStyleIndent style
nlBefore = Ast.blockStyleNewlineBeforeContent style
nlAfter = Ast.blockStyleNewlineAfterContent style
ibody = Optionals.cases doIndent body (\pre -> customIndent pre body)
pre = Logic.ifElse nlBefore "\n" ""
suf = Logic.ifElse nlAfter "\n" ""
in (Strings.cat2 (Strings.cat2 (Strings.cat2 (Strings.cat2 l pre) ibody) suf) r)
-- | Separate elements with `;` inline
semicolonSep :: [Ast.Expr] -> Ast.Expr
semicolonSep = symbolSep ";" inlineStyle
-- | Combine a list of expressions into a single OpExpr chain using the given operator. Returns the empty constant for an empty list.
sep :: Ast.Op -> [Ast.Expr] -> Ast.Expr
sep op els =
Optionals.cases (Lists.maybeHead els) (cst "") (\h -> Lists.foldl (\acc -> \el -> ifx op acc el) h (Lists.drop 1 els))
-- | Space-separate elements (no break between them)
spaceSep :: [Ast.Expr] -> Ast.Expr
spaceSep =
sep (Ast.Op {
Ast.opSymbol = (sym ""),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsSpace,
Ast.paddingRight = Ast.WsNone},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone})
-- | Space-separate elements inline if the joined width fits, otherwise newline-separate them. Useful for long signatures, type chains, and other space-joined sequences.
spaceSepAdaptive :: [Ast.Expr] -> Ast.Expr
spaceSepAdaptive els = chooseLayout maxLineWidth (spaceSep els) (newlineSep els)
-- | Square bracket pair `[` `]` for use with `brackets`
squareBrackets :: Ast.Brackets
squareBrackets =
Ast.Brackets {
Ast.bracketsOpen = (Ast.Symbol "["),
Ast.bracketsClose = (Ast.Symbol "]")}
-- | Like sep, but produces a SeqExpr instead of an OpExpr chain. SeqExpr is treated as structural layout and is not subject to parenthesization.
structuralSep :: Ast.Op -> [Ast.Expr] -> Ast.Expr
structuralSep op els =
Logic.ifElse (Lists.null els) (cst "") (Logic.ifElse (Equality.equal (Lists.length els) 1) (Optionals.fromOptional (cst "") (Lists.maybeHead els)) (Ast.ExprSeq (Ast.SeqExpr {
Ast.seqExprOp = op,
Ast.seqExprElements = els})))
-- | Like spaceSep, but produces a SeqExpr. Use for structural layout that should not trigger parenthesization of children.
structuralSpaceSep :: [Ast.Expr] -> Ast.Expr
structuralSpaceSep =
structuralSep (Ast.Op {
Ast.opSymbol = (sym ""),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsSpace,
Ast.paddingRight = Ast.WsNone},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone})
-- | Append a suffix string to an expression
suffix :: String -> Ast.Expr -> Ast.Expr
suffix s expr =
let sufOp =
Ast.Op {
Ast.opSymbol = (sym s),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsNone,
Ast.paddingRight = Ast.WsNone},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone}
in (ifx sufOp expr (cst ""))
-- | Construct a Symbol from a string
sym :: String -> Ast.Symbol
sym s = Ast.Symbol s
-- | Separate elements with the given symbol; trailing whitespace per element is determined by the block style's newline-before/after flags (space, single break, or double break)
symbolSep :: String -> Ast.BlockStyle -> [Ast.Expr] -> Ast.Expr
symbolSep symb style l =
let breakCount =
Lists.length (Lists.filter (\x_ -> x_) [
Ast.blockStyleNewlineBeforeContent style,
(Ast.blockStyleNewlineAfterContent style)])
break =
Logic.ifElse (Equality.equal breakCount 0) Ast.WsSpace (Logic.ifElse (Equality.equal breakCount 1) Ast.WsBreak Ast.WsDoubleBreak)
commaOp =
Ast.Op {
Ast.opSymbol = (sym symb),
Ast.opPadding = Ast.Padding {
Ast.paddingLeft = Ast.WsNone,
Ast.paddingRight = break},
Ast.opPrecedence = (Ast.Precedence 0),
Ast.opAssociativity = Ast.AssociativityNone}
in (Optionals.cases (Lists.maybeHead l) (cst "") (\h -> Lists.foldl (\acc -> \el -> ifx commaOp acc el) h (Lists.drop 1 l)))
-- | Indent every line of an expression by four spaces
tabIndent :: Ast.Expr -> Ast.Expr
tabIndent e =
Ast.ExprIndent (Ast.IndentedExpression {
Ast.indentedExpressionStyle = (Ast.IndentStyleAllLines " "),
Ast.indentedExpressionExpr = e})
-- | Tab-indent a list of expressions, separated by blank lines
tabIndentDoubleSpace :: [Ast.Expr] -> Ast.Expr
tabIndentDoubleSpace exprs = tabIndent (doubleNewlineSep exprs)
-- | Tab-indent a list of expressions, separated by single newlines
tabIndentSingleSpace :: [Ast.Expr] -> Ast.Expr
tabIndentSingleSpace exprs = tabIndent (newlineSep exprs)
-- | Render a placeholder for a type the writer does not yet handle, e.g. `[bigint]`
unsupportedType :: String -> Ast.Expr
unsupportedType label = cst (Strings.cat2 (Strings.cat2 "[" label) "]")
-- | Render a placeholder for an unsupported variant, including a string representation of the offending value
unsupportedVariant :: String -> String -> Ast.Expr
unsupportedVariant label obj =
cst (Strings.cat2 (Strings.cat2 (Strings.cat2 (Strings.cat2 "[unsupported " label) ": ") (Literals.showString obj)) "]")
-- | Append a trailing comma to an expression
withComma :: Ast.Expr -> Ast.Expr
withComma e =
noSep [
e,
(cst ",")]
-- | Append a trailing semicolon to an expression
withSemi :: Ast.Expr -> Ast.Expr
withSemi e =
noSep [
e,
(cst ";")]