{-# LANGUAGE
  TemplateHaskell,
  CPP
  #-}
module LLVM.Internal.InstructionDefs (
  astInstructionRecs,
  astConstantRecs,
  instructionDefs,
  ID.InstructionKind(..),
  ID.InstructionDef(..),
  instrP,
  innerJoin,
  outerJoin
  ) where

import LLVM.Prelude

import qualified Language.Haskell.TH as TH
import qualified Language.Haskell.TH.Quote as TH

import Data.Map (Map)
import qualified Data.Map as Map

import qualified LLVM.Internal.FFI.InstructionDefs as ID

import qualified LLVM.AST.Instruction as A
import qualified LLVM.AST.Constant as A.C

$(do
   let ctorRecs t = do
#if __GLASGOW_HASKELL__ < 800
         TH.TyConI (TH.DataD _ _ _ cons _) <- TH.reify t
#else
         TH.TyConI (TH.DataD _ _ _ _ cons _) <- TH.reify t
#endif
         TH.dataToExpQ (const Nothing) $ [ (TH.nameBase n, rec) | rec@(TH.RecC n _) <- cons ]

   [d|
      astInstructionRecs :: Map String TH.Con
      astInstructionRecs = Map.fromList $(ctorRecs ''A.Instruction)
      astConstantRecs :: Map String TH.Con
      astConstantRecs = Map.fromList $(ctorRecs ''A.C.Constant)
    |]
 )

instructionDefs :: Map String ID.InstructionDef
instructionDefs = Map.fromList [ ((refName . ID.cAPIName $ i), i) | i <- ID.instructionDefs ]
  where
    refName "AtomicCmpXchg" = "CmpXchg"
    refName "PHI" = "Phi"
    refName x = x

innerJoin :: Ord k => Map k a -> Map k b -> Map k (a,b)
innerJoin = Map.intersectionWith (,)

outerJoin :: Ord k => Map k a -> Map k b -> Map k (Maybe a, Maybe b)
outerJoin xs ys = Map.unionWith combine
                  (Map.map (\a -> (Just a, Nothing)) xs)
                  (Map.map (\b -> (Nothing, Just b)) ys)
    where
      combine (Just a, Nothing) (Nothing, Just b) = (Just a, Just b)
      combine _ _ = error "outerJoin: the impossible happened"

instrP :: TH.QuasiQuoter
instrP = TH.QuasiQuoter {
  TH.quoteExp = undefined,
  TH.quotePat = let m = Map.fromList [ (ID.cAPIName i, ID.cppOpcode i) | i <- ID.instructionDefs ]
             in TH.dataToPatQ (const Nothing) . (m Map.!),
  TH.quoteType = undefined,
  TH.quoteDec = undefined
 }