packages feed

hs-bindgen-1.0.0.0: src/HsBindgen/Internal/Macro/CExpr/Parse.hs

module HsBindgen.Internal.Macro.CExpr.Parse (
    parseMacro
  ) where

import Data.Vec.Lazy qualified as Vec

import C.Expr.Parse qualified as CExpr
import C.Expr.Syntax qualified as CExpr

import Clang.CStandard
import Clang.HighLevel.Types
import Clang.Paths

import HsBindgen.Runtime.Macro qualified as Runtime.Macro

import HsBindgen.Internal.Macro.CExpr.Type
import HsBindgen.Macro.Error
import HsBindgen.Macro.Interface qualified as Macro

-- | Parse the body of a macro definition as a C expression
--
-- The definition has already been split by
-- 'HsBindgen.Macro.Syntax.splitMacro'; we assemble the 'CExpr.Macro' from the
-- split and the parsed body.
parseMacro ::
     ClangCStandard
  -> Runtime.Macro.Raw (Token SourcePath TokenSpelling)
  -> Either MacroParseError (Macro.Unresolved CExpr)
parseMacro cStd macro =
    case macro.params of
      Runtime.Macro.NoParams                               -> parseBody []
      Runtime.Macro.Params params Runtime.Macro.NotVariadic     -> parseBody params
      Runtime.Macro.Params _      Runtime.Macro.Ellipsis        -> Left unsupportedVariadic
      Runtime.Macro.Params _      Runtime.Macro.NamedEllipsis{} -> Left unsupportedVariadic
  where
    sourcePath :: FilePath
    sourcePath =
          getSourcePath $ singleLocPath start
            where
              start :: SingleLoc SourcePath
              start = rangeStart $ multiLocExpansion <$> tokenExtent macro.name

    parseBody ::
         [Token SourcePath TokenSpelling]
      -> Either MacroParseError (Macro.Unresolved CExpr)
    parseBody params =
        Vec.reifyList (map identifier params) $ \macroParams ->
          case CExpr.runParser sourcePath (CExpr.parseMacroBody cStd macroParams) macro.body of
            Right body -> Right . Macro.Unresolved $ CExpr.Macro{
                macroLoc    = macro.name.tokenExtent.rangeStart
              , macroName   = identifier macro.name
              , macroParams = macroParams
              , macroExpr   = body
              }
            Left err -> Left $ MacroParseError err.parseError

    identifier :: Token SourcePath TokenSpelling -> CExpr.Identifier
    identifier = CExpr.Identifier . getTokenSpelling . tokenSpelling

    -- 'CExpr.Macro' does not support a variadic parameter list; without this
    -- check @__VA_ARGS__@, or the GNU name standing for it, would silently
    -- parse as a free variable.
    unsupportedVariadic :: MacroParseError
    unsupportedVariadic = MacroParseError {
        macroParseError = "variadic macros are not supported"
      }