packages feed

hs-bindgen-1.0.0.0: src-internal/HsBindgen/Macro/Interface.hs

-- | Pluggable macro-language interface
--
-- This module defines the macro language 'Lang' record type, providing the
-- macro-language implementation (parsing, name resolution, typechecking,
-- translation). Values of the macro language 'Lang' can be provided by separate
-- packages. The default value uses @c-expr-dsl@, and is defined in the
-- user-facing @hs-bindgen@ library as 'HsBindgen.Macro.cExpr'.
--
-- Intended for qualified import.
--
-- @
-- import HsBindgen.Macro.Interface qualified as Macro
-- @
module HsBindgen.Macro.Interface (
    -- * Record
    Lang(..)
    -- * Name resolution
  , Unresolved(..)
  , Resolved(..)
    -- * Typecheck result
  , TypecheckResult(..)
  ) where

import Clang.HighLevel.Types

import HsBindgen.Runtime.Macro qualified as Runtime.Macro

import HsBindgen.Backend.Hs.Haddock.Documentation qualified as HsDoc
import HsBindgen.Backend.Hs.Name qualified as Hs
import HsBindgen.Backend.SHs.AST.Expr
import HsBindgen.Frontend.Analysis
import HsBindgen.Imports
import HsBindgen.IR.C qualified as C
import HsBindgen.IR.Hs qualified as Hs
import HsBindgen.Language.Haskell qualified as Hs
import HsBindgen.Macro.Error
import HsBindgen.Macro.Type

{-------------------------------------------------------------------------------
  Record
-------------------------------------------------------------------------------}

-- | A macro language: provides parsing, name resolution, typechecking and
-- translation of C macros.
--
-- The C standard (and any other configuration) is fixed when the macro 'Lang'
-- is constructed; the interface itself is configuration-free.
--
-- Note how the macro language resembles the stages of compilers: parse,
-- resolve, typecheck, translate.
data Lang (l :: Star) = Lang {
    -- | Parse a single macro.
    parse ::
         Runtime.Macro.Raw (Token SourcePath TokenSpelling)
      -> Either MacroParseError (Unresolved l)

  , resolve ::
         Set C.DeclId
         -- ^ Set of declaration IDs in scope.
      -> Unresolved l
      -> Either MacroResolutionError (Resolved l)

    -- | Batch-typecheck a sequence of macros.
    --
    -- The resulting map must have keys for all resolved macros.
  , typecheck ::
         [Resolved l]
      -> Map Text (TypecheckResult l)

    -- | Translate a checked type-like macro to an 'HsType'.
    --
    -- The caller supplies a function to translate variables to 'HsType'.
  , translateType ::
         TypecheckedType l Hs.Type
      -> Hs.Type

    -- | Translate a checked value-macro to a 'Binding'.
  , translateValue ::
         Hs.Name Hs.NsVar
         -- ^ Exported binding name
      -> TypecheckedValue l Hs.TermName
      -> Maybe HsDoc.Comment
      -> Binding
  }

{-------------------------------------------------------------------------------
  Name resolution
-------------------------------------------------------------------------------}

newtype Unresolved l = Unresolved { unwrap :: Parsed l () }

deriving stock instance (HasTypes l) => Eq   (Unresolved l)
deriving stock instance (HasTypes l) => Show (Unresolved l)

data Resolved l = Resolved {
    -- | The macro, with references resolved to 'C.DeclId's.
    macro :: Parsed l C.DeclId
    -- | Dependencies of the macro, paired with what the dependent needs to know
    -- about each dependency (full shape vs. name only).
  , deps  :: [(C.DeclId, Dependency)]
  }
  deriving stock (Generic)

deriving stock instance (HasTypes l) => Eq   (Resolved l)
deriving stock instance (HasTypes l) => Show (Resolved l)

{-------------------------------------------------------------------------------
  Typecheck result
-------------------------------------------------------------------------------}

-- | Result of typechecking a single macro.
--
-- @l@ is the macro-language tag.
data TypecheckResult l
  = TypecheckType  (TypecheckedType l C.DeclId)
  | TypecheckValue (TypecheckedValue l C.DeclId)
  | TypecheckError MacroTypecheckError

deriving stock instance HasTypes l => Show (TypecheckResult l)
deriving stock instance HasTypes l => Eq   (TypecheckResult l)