{-|
  Copyright  :  (C) 2012-2016, University of Twente
  License    :  BSD2 (see the file LICENSE)
  Maintainer :  Christiaan Baaij <christiaan.baaij@gmail.com>

  Parser definitions for BlackBox templates
-}

module CLaSH.Netlist.BlackBox.Parser
  (runParse)
where

import           Data.Text.Lazy                           (Text, pack)
import           Text.ParserCombinators.UU
import           Text.ParserCombinators.UU.BasicInstances hiding (Parser)
import qualified Text.ParserCombinators.UU.Core           as PCC (parse)
import           Text.ParserCombinators.UU.Utils          hiding (pBrackets)

import           CLaSH.Netlist.BlackBox.Types

type Parser a = P (Str Char Text LineColPos) a


-- | Parse a text as a BlackBoxTemplate, returns a list of errors in case
-- parsing fails
runParse :: Text -> (BlackBoxTemplate, [Error LineColPos])
runParse = PCC.parse ((,) <$> pBlackBoxD <*> pEnd)
         . createStr (LineColPos 0 0 0)

-- | Parse a BlackBoxTemplate (Declarations and Expressions)
pBlackBoxD :: Parser BlackBoxTemplate
pBlackBoxD = pSome pElement

-- | Parse a single Template Element
pElement :: Parser Element
pElement  =  pTagD
         <|> C <$> pText
         <|> C <$> (pack <$> pToken "~ ")

-- | Parse the Text part of a Template
pText :: Parser Text
pText = pack <$> pList1 (pRange ('\000','\125'))

-- | Parse a Declaration or Expression element
pTagD :: Parser Element
pTagD =  IF <$> (pTokenWS "~IF" *> pTagE)
            <*> (pSpaces *> (pToken "~THEN" *> pBlackBoxD))
            <*> (pToken "~ELSE" *> pBlackBoxD <* pToken "~FI")
     <|> D <$> pDecl
     <|> pTagE

-- | Parse a Declaration
pDecl :: Parser Decl
pDecl = Decl <$> (pTokenWS "~INST" *> pNatural) <*>
        ((:) <$> pOutput <*> pList pInput) <* pToken "~INST"

-- | Parse the output tag of Declaration
pOutput :: Parser (BlackBoxTemplate,BlackBoxTemplate)
pOutput = pTokenWS "~OUTPUT" *> pTokenWS "<=" *> ((,) <$> (pBlackBoxE <* pTokenWS "~") <*> pBlackBoxE) <* pTokenWS "~"

-- | Parse the input tag of Declaration
pInput :: Parser (BlackBoxTemplate,BlackBoxTemplate)
pInput = pTokenWS "~INPUT" *> pTokenWS "<=" *> ((,) <$> (pBlackBoxE <* pTokenWS "~") <*> pBlackBoxE) <* pTokenWS "~"

-- | Parse an Expression element
pTagE :: Parser Element
pTagE =  O                 <$  pToken "~RESULT"
     <|> I                 <$> (pToken "~ARG" *> pBrackets pNatural)
     <|> L                 <$> (pToken "~LIT" *> pBrackets pNatural)
     <|> (Clk . Just)      <$> (pToken "~CLK" *> pBrackets pNatural)
     <|> Clk Nothing       <$  pToken "~CLKO"
     <|> (Rst . Just)      <$> (pToken "~RST" *> pBrackets pNatural)
     <|> Rst Nothing       <$  pToken "~RSTO"
     <|> Sym               <$> (pToken "~SYM" *> pBrackets pNatural)
     <|> Typ Nothing       <$  pToken "~TYPO"
     <|> (Typ . Just)      <$> (pToken "~TYP" *> pBrackets pNatural)
     <|> TypM Nothing      <$  pToken "~TYPMO"
     <|> (TypM . Just)     <$> (pToken "~TYPM" *> pBrackets pNatural)
     <|> Err Nothing       <$  pToken "~ERRORO"
     <|> (Err . Just)      <$> (pToken "~ERROR" *> pBrackets pNatural)
     <|> TypElem           <$> (pToken "~TYPEL" *> pBrackets pTagE)
     <|> IndexType         <$> (pToken "~INDEXTYPE" *> pBrackets pTagE)
     <|> CompName          <$  pToken "~COMPNAME"
     <|> Size              <$> (pToken "~SIZE" *> pBrackets pTagE)
     <|> Length            <$> (pToken "~LENGTH" *> pBrackets pTagE)
     <|> FilePath          <$> (pToken "~FILE" *> pBrackets pTagE)
     <|> Gen               <$> (True <$ pToken "~GENERATE")
     <|> Gen               <$> (False <$ pToken "~ENDGENERATE")
     <|> SigD              <$> (pToken "~SIGD" *> pBrackets pSigD) <*> (Just <$> (pBrackets pNatural))
     <|> (`SigD` Nothing)  <$> (pToken "~SIGDO" *> pBrackets pSigD)
     <|> IW64              <$  pToken "~IW64"
     <|> (BV True)         <$> (pToken "~TOBV" *> pBrackets pSigD) <*> (Just <$> pBrackets pNatural)
     <|> (BV True)         <$> (pToken "~TOBVO" *> pBrackets pSigD) <*> pure Nothing
     <|> (BV False)        <$> (pToken "~FROMBV" *> pBrackets pSigD) <*> (Just <$> pBrackets pNatural)
     <|> (BV False)        <$> (pToken "~FROMBVO" *> pBrackets pSigD) <*> pure Nothing

-- | Parse a bracketed text
pBrackets :: Parser a -> Parser a
pBrackets p = pSym '[' *> p <* pSym ']'

-- | Parse a token and eat trailing whitespace
pTokenWS :: String -> Parser String
pTokenWS keyw = pToken keyw <* pSpaces

-- | Parse the expression part of Blackbox Templates
pBlackBoxE :: Parser BlackBoxTemplate
pBlackBoxE = pSome pElemE

-- | Parse an Expression or Text
pElemE :: Parser Element
pElemE = pTagE
      <|> C <$> pText

-- | Parse SigD
pSigD :: Parser [Element]
pSigD = pSome (pTagE <|> pLimitedText
                     <|> (C <$> (pack <$> pToken "[ "))
                     <|> (C <$> (pack <$> pToken " ]")))

-- | Text excluding square brackets and tilde
pLimitedText :: Parser Element
pLimitedText = C <$> (pack <$> pList1 (pSatisfy (`notElem` "[]~") (Insertion (show "notElem \"[]~\"") '_' 5)))