module LLVM.General.DataLayout (
 dataLayoutToString,
 parseDataLayout
 ) where

import Control.Applicative

import Data.Word

import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.Set as Set

import Text.ParserCombinators.Parsec hiding (many)

import LLVM.General.AST.DataLayout
import LLVM.General.AST.AddrSpace

dataLayoutToString :: DataLayout -> String
dataLayoutToString dl = 
  let sTriple :: (Word32, AlignmentInfo) -> String
      sTriple (s, ai) = show s ++ ":" ++ show (abiAlignment ai) ++ (maybe "" (\p -> ":" ++ show p) (preferredAlignment ai))
      atChar at = case at of
                    IntegerAlign -> "i"
                    VectorAlign -> "v"
                    FloatAlign -> "f"
                    AggregateAlign -> "a"
                    StackAlign -> "s"
  in
  List.intercalate "-" (
    (case endianness dl of Just BigEndian -> ["E"]; Just LittleEndian -> ["e"]; _ -> [])
    ++
    (maybe [] (\s -> ["S" ++ show s]) (stackAlignment dl))
    ++
    [ "p" ++ (if a == 0 then "" else show a) ++ ":" ++ sTriple t | (AddrSpace a, t) <- Map.toList . pointerLayouts $ dl]
    ++
    [ atChar at ++ sTriple (s, ai) | ((at, s), ai) <- Map.toList . typeLayouts $ dl ]
    ++ 
    (maybe [] (\ns -> ["n" ++ (List.intercalate ":" (map show . Set.toList $ ns))]) (nativeSizes dl))
  )

parseDataLayout :: String -> Maybe DataLayout
parseDataLayout "" = Nothing
parseDataLayout s = 
  let
    num :: Parser Word32
    num = read <$> many digit
    triple :: Parser (Word32, AlignmentInfo)
    triple = do
      s <- num
      char ':'
      abi <- num
      pref <- optionMaybe $ do
                char ':'
                num
      return (s, (AlignmentInfo abi pref))
    parseSpec :: Parser (DataLayout -> DataLayout)
    parseSpec = choice [
      do
        char 'e'
        return $ \dl -> dl { endianness = Just LittleEndian },
      do
        char 'E' 
        return $ \dl -> dl { endianness = Just BigEndian },
      do
        char 'S'
        n <- num
        return $ \dl -> dl { stackAlignment = Just n },
      do
        char 'p'
        a <- AddrSpace <$> option 0 (read <$> many1 digit)
        char ':'
        t <- triple
        return $ \dl -> dl { pointerLayouts = Map.insert a t (pointerLayouts dl) },
      do
        at <- choice [
               char 'i' >> return IntegerAlign,
               char 'v' >> return VectorAlign,
               char 'f' >> return FloatAlign,
               char 'a' >> return AggregateAlign,
               char 's' >> return StackAlign
              ]
        (sz,ai) <- triple
        return $ \dl -> dl { typeLayouts = Map.insert (at,sz) ai (typeLayouts dl) },
      do 
        char 'n'
        ns <- num `sepBy` (char ':')
        return $ \dl -> dl { nativeSizes = Just (Set.fromList ns) }
     ]
  in 
    case parse (parseSpec `sepBy` (char '-')) "" s of
      Left _ -> error $ "ill formed data layout: " ++ show s
      Right fs -> Just $ foldr ($) defaultDataLayout fs