packages feed

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

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

module Moonlight.Pale.Ghc.Expr.Render.Module
  ( ModuleRenderContext (..),
    RenderTarget (..),
    renderSourceWith,
    renderRewriteModuleSource,
    renderModuleDeclaration,
    renderConvertedModuleWith
  )
where

import Data.Foldable qualified as Foldable
import Data.Kind (Type)
import Moonlight.Core (Pattern (..))
import Moonlight.Pale.Ghc.Expr.Convert.Coalgebra
  ( Binding (..),
    ConvertedInstanceDeclaration (..),
    ConvertedModule (..),
    ModuleDeclaration (..),
    convertedBindingValue,
    convertedModuleBindingSites,
    tlbBinding,
  )
import Moonlight.Pale.Ghc.Expr.Render.Analysis
import Moonlight.Pale.Ghc.Expr.Render.Binding
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.Refusal
import Moonlight.Pale.Ghc.Expr.Syntax

type ModuleRenderContext :: Type
data ModuleRenderContext = ModuleRenderContext
  { moduleHeaderPrefix :: !String,
    moduleRenderedName :: !(Maybe String)
  }
  deriving stock (Eq, Ord, Show)

type RenderTarget :: Type
data RenderTarget
  = RenderAnnotatedExpression !Expr
  | RenderRewriteExpression !(Pattern HsExprF)
  | RenderNamedRewriteBinding !String !(Pattern HsExprF)
  | RenderSourceBinding !Binding
  | RenderRewriteModule !ModuleRenderContext ![(String, Pattern HsExprF)]
  | RenderConvertedModule !ModuleRenderContext !ConvertedModule

renderSourceWith ::
  RenderDocument document =>
  RenderMode ->
  RenderTarget ->
  Either RenderRefusal document
renderSourceWith renderMode = \case
  RenderAnnotatedExpression expressionValue -> do
    expressionRenderSource <-
      prepareRenderSource (Right . exprNode) [] [expressionValue]
    renderExprWith
      expressionRenderSource
      renderMode
      0
      expressionValue
  RenderRewriteExpression expressionValue -> do
    expressionRenderSource <- patternRenderSource expressionValue
    renderExprWith
      expressionRenderSource
      renderMode
      0
      expressionValue
  RenderNamedRewriteBinding bindingName bindingTerm -> do
    expressionRenderSource <- patternRenderSource bindingTerm
    renderTopLevelBindingWith
      expressionRenderSource
      renderMode
      bindingName
      bindingTerm
  RenderSourceBinding bindingValue ->
    renderBindingWith renderMode bindingValue
  RenderRewriteModule moduleContext renderedBindings ->
    renderRewriteModuleSource renderMode moduleContext renderedBindings
  RenderConvertedModule moduleContext convertedModule ->
    renderConvertedModuleWith renderMode moduleContext convertedModule

renderRewriteModuleSource ::
  RenderDocument document =>
  RenderMode ->
  ModuleRenderContext ->
  [(String, Pattern HsExprF)] ->
  Either RenderRefusal document
renderRewriteModuleSource renderMode moduleContext renderedBindings = do
  bindingDocuments <-
    traverse
      ( \(bindingName, bindingTerm) -> do
          expressionRenderSource <- patternRenderSource bindingTerm
          renderTopLevelBindingWith
            expressionRenderSource
            renderMode
            bindingName
            bindingTerm
      )
      renderedBindings
  let headerPrefix = moduleHeaderPrefix moduleContext
  let prefixValue =
        if null headerPrefix
          then
            requiredLanguageHeader (fmap snd renderedBindings)
              <> maybe
                ""
                (\moduleNameValue -> "module " <> moduleNameValue <> " where\n\n")
                (moduleRenderedName moduleContext)
          else headerPrefix
  Right
    ( text prefixValue
        <> intercalateDoc (text "\n\n") bindingDocuments
        <> text "\n"
    )

renderModuleDeclaration ::
  RenderDocument document =>
  RenderMode ->
  ModuleDeclaration ->
  Either RenderRefusal document
renderModuleDeclaration renderMode = \case
  ValueDeclaration bindingValue ->
    renderBindingWith renderMode (tlbBinding bindingValue)
  TypeSignatureDeclaration signature ->
    Right (text (renderTypeSignature signature))
  FixityDeclarationNode declaration ->
    Right (text (renderFixityDeclaration declaration))
  InstanceDeclarationNode instanceDeclaration ->
    Right (text (convertedInstanceSource instanceDeclaration))
  OpaqueDeclaration _ _ declarationSource ->
    Right (text declarationSource)

renderConvertedModuleWith ::
  RenderDocument document =>
  RenderMode ->
  ModuleRenderContext ->
  ConvertedModule ->
  Either RenderRefusal document
renderConvertedModuleWith renderMode moduleContext convertedModule = do
  declarationDocuments <-
    traverse
      (renderModuleDeclaration renderMode)
      (Foldable.toList (cmDeclarations convertedModule))
  let bindings =
        fmap convertedBindingValue (convertedModuleBindingSites convertedModule)
      headerPrefix =
        moduleHeaderPrefix moduleContext
  let prefixValue =
        if null headerPrefix
          then
            requiredConvertedLanguageHeader bindings
              <> maybe
                ""
                (\moduleNameValue -> "module " <> moduleNameValue <> " where\n\n")
                (moduleRenderedName moduleContext)
          else headerPrefix
  Right
    ( text prefixValue
        <> intercalateDoc (text "\n\n") declarationDocuments
        <> text "\n"
    )