packages feed

hs-bindgen-1.0.0.0: src-internal/HsBindgen/Frontend/Pass/PrepareReparse/AST.hs

-- | A simplified intermediate AST for a header file containing only
-- declarations that are intended to be run through the reparser
--
-- This module is intended to be imported unqualified. It is also intended to
-- only be imported from within the "HsBindgen.Frontend.Pass.PrepareReparse"
-- module hierarchy.
--
-- > import HsBindgen.Frontend.Pass.PrepareReparse.AST
--
module HsBindgen.Frontend.Pass.PrepareReparse.AST (
    -- * Before preprocessing
    PreHeader (..)
  , Include (..)
  , Undef (..)
  , MacroName (..)
    -- * After preprocessing
  , PostHeader (..)
    -- * Common
  , Target (..)
  , Decl (..)
  , Tag (..)
  , TagType (..)
  , TagName (..)
  ) where

import Prelude hiding (print)

{-------------------------------------------------------------------------------
  Before preprocessing
-------------------------------------------------------------------------------}

-- | A header file for reparse targets before macro prepocessing
--
-- The goal of the @PrepareReparse@ pass is to expand macro invocations in
-- reparse targets that would make the reparser fail. In general, all macro
-- definitions that were not parsed as types have to be expanded. To achieve
-- this expanding of only select macro definitions, we include the original
-- header file, we undefine all macro definitions that were parsed as types, and
-- then we include all reparse targets. This 'PreHeader' can then be printed to
-- a file, and that file can preprocessed using the @clang@ executable.
--
-- Example:
--
-- > PreHeader {
-- >    include = Include "foo/bar/baz.h"
-- >  , undefs = [ Undef (MacroName "A") ]
-- >  , targets = [
-- >        Target (Tag Function (TagName "myFunction")) (Decl "void myFunction (A x);")
-- >      , Target (Tag Field (TagName "MyStruct.myStructField")) (Decl "B myStructField;")
-- >      ]
-- >  }
--
-- In the above example, @A@ was parsed as a type, and @B@ was not.
data PreHeader = PreHeader {
    include :: Include
  , undefs :: [Undef]
  , targets :: [Target]
  }

-- | An include directive
newtype Include = Include FilePath
  deriving stock (Show, Eq)

-- | A macro undefinition
data Undef = Undef MacroName
  deriving stock (Show, Eq)

-- | A macro name
newtype MacroName = MacroName String
  deriving stock (Show, Eq)

{-------------------------------------------------------------------------------
  After preprocessing
-------------------------------------------------------------------------------}

-- | A header file for reparse targets after macro prepocessing
--
-- After running the @clang@ executable on a printed 'PreHeader' (and cutting
-- out all code from the included original header file), we are left with a file
-- that contains all reparse targets but with all the relevant macro invocations
-- expanded. These textual reparse targets can then be parsed as a 'PostHeader'.
--
-- Example:
--
-- > PostHeader {
-- >    targets = [
-- >        Target (Tag Function (TagName "myFunction")) (Decl "void myFunction (A x);")
-- >      , Target (Tag Field (TagName "MyStruct.myStructField")) (Decl "void myStructField;")
-- >      ]
-- >  }
--
-- In the above example, @A@ was parsed as a type, and @B@ was not so it was
-- expanded.
data PostHeader = PostHeader {
    targets :: [Target]
  }

{-------------------------------------------------------------------------------
  Common
-------------------------------------------------------------------------------}

-- | A /reparse/ target is a declaration that should be run through the reparser
data Target = Target Tag Decl
  deriving stock (Show, Eq)

-- | The raw text of a declaration
newtype Decl = Decl String
  deriving stock (Show, Eq)

-- | A tag records the origins of a reparser target: the kind of declaration,
-- and a unique name for the declaration
--
-- Tags are used in the printer to signal the start and end of a reparse target,
-- which makes it easier to parse the printer output later. Moreover, the tag
-- name is used to assign unique tag names to fields, because field names are
-- not unique in general.
--
-- Examples:
--
-- > Tag Function (TagName "myFunction")
-- > Tag Field (TagName "MyStruct.myStructField")
--
data Tag = Tag TagType TagName
  deriving stock (Show, Eq, Ord)

-- | Reparse targets can be declarations of these kinds:
data TagType =
    -- | A struct or union field
    Field
  | Function
  | Typedef
    -- | A global variable
  | Variable
  deriving stock (Show, Eq, Ord, Enum, Bounded)

-- | A unique name for a reparse target
--
-- This is generally just the name of the declaration itself, but for fields we
-- pair the name of the field with the name of the enclosing struct or union.
-- Field names are not unique in /general/. They are only unique within a single
-- struct or union.
--
-- Examples:
--
-- > TagName "myFunction"
-- > TagName "MyStruct.myStructField"
--
newtype TagName = TagName String
  deriving stock (Show, Eq, Ord)