packages feed

moonlight-pale-0.1.0.0: src-ghc-surface/Moonlight/Pale/Ghc/Expr/Convert/Declaration.hs

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

module Moonlight.Pale.Ghc.Expr.Convert.Declaration
  ( fixityAssociativityFromGhc,
    convertLocatedDecl,
    convertDecl,
    convertSignatureDeclaration,
    opaqueDeclaration,
    convertValueBinding
  )
where

import Data.Foldable (toList)
import Data.List.NonEmpty qualified as NonEmpty
import Data.Map.Strict qualified as Map
import GHC.Hs
  ( ClsInstDecl (..),
    GhcPs,
    HsBind,
    HsDecl (..),
    InstDecl (..),
    LHsBind,
    LHsDecl,
    FixitySig (..),
    Sig (..),
  )
import GHC.Parser.Annotation (getLocA)
import Language.Haskell.Syntax.Basic (Fixity (..), FixityDirection (..))
import GHC.Types.SrcLoc (unLoc)
import Moonlight.Pale.Ghc.Expr.Convert.Expression
import Moonlight.Pale.Ghc.Expr.Convert.Obstruction
import Moonlight.Pale.Ghc.Expr.Convert.Pattern
import Moonlight.Pale.Ghc.Expr.Convert.Row
import Moonlight.Pale.Ghc.Expr.Convert.Source
import Moonlight.Pale.Ghc.Expr.Convert.State
import Moonlight.Pale.Ghc.Expr.Syntax

fixityAssociativityFromGhc :: FixityDirection -> FixityAssociativity
fixityAssociativityFromGhc = \case
  InfixL -> FixityLeft
  InfixR -> FixityRight
  InfixN -> FixityNone

convertLocatedDecl :: SourceSliceIndex -> LHsDecl GhcPs -> ConvM ModuleDeclaration
convertLocatedDecl moduleSourceIndex locatedDecl =
  convertDecl
    moduleSourceIndex
    (sourceRegionFromSrcSpan (getLocA locatedDecl))
    (unLoc locatedDecl)

convertDecl :: SourceSliceIndex -> Maybe SourceRegion -> HsDecl GhcPs -> ConvM ModuleDeclaration
convertDecl moduleSourceIndex declRegion = \case
  ValD _ bindValue ->
    ValueDeclaration <$> convertValueBinding declRegion bindValue
  SigD _ signature ->
    convertSignatureDeclaration moduleSourceIndex declRegion signature
  TyClD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedTypeOrClassDeclaration
  InstD _ instanceDeclaration ->
    convertInstanceDeclaration moduleSourceIndex declRegion instanceDeclaration
  DerivD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedDerivingDeclaration
  KindSigD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedKindSignatureDeclaration
  DefD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedDefaultDeclaration
  ForD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedForeignDeclaration
  WarningD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedWarningDeclaration
  AnnD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedAnnotationDeclaration
  RuleD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedRuleDeclaration
  SpliceD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedSpliceDeclaration
  DocD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedDocumentationDeclaration
  RoleAnnotD {} ->
    opaqueDeclaration moduleSourceIndex declRegion UnsupportedRoleAnnotationDeclaration

convertSignatureDeclaration ::
  SourceSliceIndex ->
  Maybe SourceRegion ->
  Sig GhcPs ->
  ConvM ModuleDeclaration
convertSignatureDeclaration moduleSourceIndex declarationRegion = \case
  TypeSig _ names signatureType ->
    case NonEmpty.nonEmpty (fmap unLoc names) of
      Nothing ->
        throwConvert (ConvertEmptyTypeSignature declarationRegion)
      Just signatureNames ->
        pure
          ( TypeSignatureDeclaration
              TypeSignature
                { typeSignatureNames = signatureNames,
                  typeSignatureType = normalizedTypeText signatureType
                }
          )
  FixSig _ (FixitySig _ operatorNames (Fixity precedence direction)) ->
    case NonEmpty.nonEmpty (fmap unLoc operatorNames) of
      Nothing ->
        throwConvert (ConvertEmptyFixityDeclaration declarationRegion)
      Just fixityOperatorNames ->
        pure
          ( FixityDeclarationNode
              FixityDeclaration
                { fixityAssociativity = fixityAssociativityFromGhc direction,
                  fixityPrecedence = precedence,
                  fixityOperators = fixityOperatorNames
                }
          )
  PatSynSig {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedPatternSynonymSignature
  ClassOpSig {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedClassOperationSignature
  InlineSig {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedInlineSignature
  SpecSig {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedSpecializationSignature
  SpecSigE {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedExpressionSpecializationSignature
  SpecInstSig {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedInstanceSpecializationSignature
  MinimalSig {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedMinimalSignature
  SCCFunSig {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedCostCentreSignature
  CompleteMatchSig {} ->
    opaqueDeclaration moduleSourceIndex declarationRegion UnsupportedCompleteMatchSignature

opaqueDeclaration ::
  SourceSliceIndex ->
  Maybe SourceRegion ->
  UnsupportedDeclarationTag ->
  ConvM ModuleDeclaration
opaqueDeclaration moduleSourceIndex declarationRegion declarationTag =
  uncurry (OpaqueDeclaration declarationTag)
    <$> requireDeclarationSource
      moduleSourceIndex
      declarationRegion
      (ConvertDeclarationSourceUnavailable declarationRegion declarationTag)

convertInstanceDeclaration ::
  SourceSliceIndex ->
  Maybe SourceRegion ->
  InstDecl GhcPs ->
  ConvM ModuleDeclaration
convertInstanceDeclaration moduleSourceIndex declarationRegion = \case
  ClsInstD {cid_inst = ClsInstDecl {cid_binds = methodBindings}} -> do
    (instanceRegion, instanceSource) <-
      requireDeclarationSource
        moduleSourceIndex
        declarationRegion
        (ConvertInstanceDeclarationSourceUnavailable declarationRegion)
    methodSections <-
      traverse convertLocatedInstanceMethod (toList methodBindings)
    pure
      ( InstanceDeclarationNode
          ConvertedInstanceDeclaration
            { convertedInstanceRegion = instanceRegion,
              convertedInstanceSource = instanceSource,
              convertedInstanceMethods = methodSections
            }
      )
  DataFamInstD {} ->
    opaqueDeclaration
      moduleSourceIndex
      declarationRegion
      UnsupportedDataFamilyInstanceDeclaration
  TyFamInstD {} ->
    opaqueDeclaration
      moduleSourceIndex
      declarationRegion
      UnsupportedTypeFamilyInstanceDeclaration

convertLocatedInstanceMethod ::
  LHsBind GhcPs ->
  ConvM InstanceMethodSection
convertLocatedInstanceMethod locatedBinding = do
  let methodRegion =
        sourceRegionFromSrcSpan (getLocA locatedBinding)
  methodResult <-
    runInstanceMethodSection
      methodRegion
      (convertValueBinding methodRegion (unLoc locatedBinding))
  pure
    ( case methodResult of
        Left methodObstruction ->
          ObstructedInstanceMethod methodObstruction
        Right convertedBinding ->
          TraversableInstanceMethod convertedBinding
    )

requireDeclarationSource ::
  SourceSliceIndex ->
  Maybe SourceRegion ->
  ConvertObstruction ->
  ConvM (SourceRegion, String)
requireDeclarationSource moduleSourceIndex declarationRegion unavailableObstruction =
  case declarationRegion >>= sourceSliceForRegion moduleSourceIndex of
    Just declarationSection ->
      pure declarationSection
    Nothing ->
      throwConvert unavailableObstruction

convertValueBinding :: Maybe SourceRegion -> HsBind GhcPs -> ConvM ConvertedValueBinding
convertValueBinding declRegion bindValue = do
  bindingScope <- freshChildScope
  convertedBinding <-
    withScope bindingScope $ do
      headPattern <- bindingHeadPatternFromBind declRegion bindValue
      convertBindingWithPattern Map.empty headPattern bindValue
  let !bindingMetrics =
        convertedExpressionMetrics (cbExpression convertedBinding)
  pure
    ConvertedValueBinding
      { convertedValueBindingValue = cbBinding convertedBinding,
        convertedValueBindingScope = bindingScope,
        convertedValueBindingRegion = declRegion,
        convertedValueBindingMetricSection = bindingMetrics
      }