packages feed

moonlight-pale-0.1.0.0: src-ghc-surface/Moonlight/Pale/Ghc/Expr/Render/Binding.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Moonlight.Pale.Ghc.Expr.Render.Binding
  ( renderBindingWith,
    renderBindingWithSource,
    renderTopLevelBindingWith,
    renderBindingLhs,
    renderBindingClause,
    renderBindingRhs,
    appendWhereGroup,
    renderWhereGroup,
    renderInlineWhereGroup,
    renderGuardedTopLevelAlts,
    renderGuardedTopLevelAlt
  )
where

import Data.List.NonEmpty qualified as NonEmpty
import Moonlight.Pale.Ghc.Expr.Convert.Coalgebra
  ( Binding (..),
    BindingGroup,
    bindingGroupBindings,
    Clause (..),
    Rhs (..),
  )
import Moonlight.Pale.Ghc.Expr.Render.Carrier
import Moonlight.Pale.Ghc.Expr.Render.Document
import Moonlight.Pale.Ghc.Expr.Render.Expression
import Moonlight.Pale.Ghc.Expr.Render.Name
import Moonlight.Pale.Ghc.Expr.Render.Pattern
import Moonlight.Pale.Ghc.Expr.Render.Refusal
import Moonlight.Pale.Ghc.Expr.Syntax

renderBindingWith ::
  RenderDocument document =>
  RenderMode ->
  Binding ->
  Either RenderRefusal document
renderBindingWith renderMode bindingValue = do
  renderContext <- bindingRenderSource bindingValue
  renderBindingWithSource renderContext renderMode bindingValue

renderBindingWithSource ::
  RenderDocument document =>
  RenderSource Expr ->
  RenderMode ->
  Binding ->
  Either RenderRefusal document
renderBindingWithSource renderContext renderMode = \case
  FunctionBinding binderAnn clauses ->
    vcat
      <$> traverse
        (renderBindingClause renderContext renderMode (renderDefinitionName (renderBinderSpelling renderContext binderAnn)))
        (NonEmpty.toList clauses)
  PatternBinding patternValue rhsValue -> do
    patternDoc <- renderPat renderContext False patternValue
    renderBindingRhs renderContext renderMode patternDoc rhsValue

renderTopLevelBindingWith ::
  RenderDocument document =>
  RenderSource recursive ->
  RenderMode ->
  String ->
  recursive ->
  Either RenderRefusal document
renderTopLevelBindingWith renderContext renderMode bindingName bindingTerm
  | null bindingName =
      Left RenderEmptyBindingName
  | otherwise = do
      bodyDoc <- renderExprWith renderContext renderMode 0 bindingTerm
      Right
        ( renderDelimitedExpression
            renderMode
            (renderDefinitionName bindingName)
            "="
            bodyDoc
        )

renderBindingLhs ::
  RenderDocument document =>
  RenderSource recursive ->
  document ->
  [HsPatF] ->
  Either RenderRefusal document
renderBindingLhs renderContext headDoc patternValues =
  case patternValues of
    [] ->
      Right headDoc
    _ ->
      renderClauseLhs renderContext headDoc patternValues

renderBindingClause ::
  RenderDocument document =>
  RenderSource Expr ->
  RenderMode ->
  document ->
  Clause ->
  Either RenderRefusal document
renderBindingClause renderContext renderMode bindingHead clauseValue = do
  lhsDoc <-
    renderBindingLhs renderContext bindingHead (clausePatterns clauseValue)
  renderBindingRhs renderContext renderMode lhsDoc (clauseRhs clauseValue)

renderBindingRhs ::
  RenderDocument document =>
  RenderSource Expr ->
  RenderMode ->
  document ->
  Rhs ->
  Either RenderRefusal document
renderBindingRhs renderContext renderMode lhsDoc = \case
  UnguardedRhs bodyExpression maybeWhereGroup -> do
    bodyDoc <-
      renderExprWith
        renderContext
        renderMode
        0
        bodyExpression
    appendWhereGroup
      renderContext
      renderMode
      maybeWhereGroup
      (renderDelimitedExpression renderMode lhsDoc "=" bodyDoc)
  GuardedRhs guardedAlternatives maybeWhereGroup -> do
    equationDoc <-
      renderGuardedTopLevelAlts
        renderContext
        renderMode
        lhsDoc
        (NonEmpty.toList guardedAlternatives)
    appendWhereGroup renderContext renderMode maybeWhereGroup equationDoc

appendWhereGroup ::
  RenderDocument document =>
  RenderSource Expr ->
  RenderMode ->
  Maybe BindingGroup ->
  document ->
  Either RenderRefusal document
appendWhereGroup renderContext renderMode maybeBindingGroup equationDoc =
  case maybeBindingGroup of
    Nothing ->
      Right equationDoc
    Just bindingGroup -> do
      case renderMode of
        CompactRender -> do
          whereSuffix <-
            renderInlineWhereGroup renderContext CompactRender bindingGroup
          Right (equationDoc <> whereSuffix)
        GeneratedRender -> do
          whereDoc <-
            renderWhereGroup renderContext GeneratedRender bindingGroup
          Right (vcat [equationDoc, whereDoc])

renderWhereGroup ::
  RenderDocument document =>
  RenderSource Expr ->
  RenderMode ->
  BindingGroup ->
  Either RenderRefusal document
renderWhereGroup renderContext renderMode bindingGroup = do
  bindingDocs <-
    traverse
      (renderBindingWithSource renderContext renderMode)
      (NonEmpty.toList (bindingGroupBindings bindingGroup))
  Right (vcat [nest 2 (text "where"), nest 4 (vcat bindingDocs)])

renderInlineWhereGroup ::
  RenderDocument document =>
  RenderSource Expr ->
  RenderMode ->
  BindingGroup ->
  Either RenderRefusal document
renderInlineWhereGroup renderContext renderMode bindingGroup = do
  bindingDocs <-
    traverse
      (renderBindingWithSource renderContext renderMode)
      (NonEmpty.toList (bindingGroupBindings bindingGroup))
  Right (renderBlock renderMode " where" bindingDocs)

renderGuardedTopLevelAlts ::
  RenderDocument document =>
  RenderSource recursive ->
  RenderMode ->
  document ->
  [GuardedAltF recursive] ->
  Either RenderRefusal document
renderGuardedTopLevelAlts renderContext renderMode lhsDoc guardedAlts =
  case guardedAlts of
    [] ->
      Left RenderGuardedExpression
    [GuardedAltF [] bodyValue] -> do
      bodyDoc <- renderExprWith renderContext renderMode 0 bodyValue
      Right (lhsDoc <> text " = " <> bodyDoc)
    _ -> do
      altDocs <- traverse (renderGuardedTopLevelAlt renderContext renderMode) guardedAlts
      Right
        ( case renderMode of
            CompactRender ->
              lhsDoc <+> intercalateDoc (text " ") altDocs
            GeneratedRender ->
              vcat (lhsDoc : fmap (nest 2) altDocs)
        )

renderGuardedTopLevelAlt ::
  RenderDocument document =>
  RenderSource recursive ->
  RenderMode ->
  GuardedAltF recursive ->
  Either RenderRefusal document
renderGuardedTopLevelAlt renderContext renderMode =
  renderGuardedAlt renderContext renderMode (text "| ") " = "