content: {-|
    Copyright  :  (C) 2012-2016, University of Twente,
                      2017     , Myrtle Software Ltd,
                      2021-2022, QBayLogic B.V.
                      2022     , Google Inc.
    License    :  BSD2 (see the file LICENSE)
    Maintainer :  QBayLogic B.V. <devops@qbaylogic.com>
  
    Parser definitions for BlackBox templates
  -}
  
  {-# LANGUAGE OverloadedStrings #-}
  
  module Clash.Netlist.BlackBox.Parser
    (runParse)
  where
  
  import           Control.Applicative          ((<|>))
  import           Data.Text.Lazy               (Text, pack, unpack)
  import qualified Data.Text.Lazy               as Text
  import           Text.Trifecta                hiding (Err)
  import           Text.Trifecta.Delta
  
  import qualified Clash.Signal.Internal        as Signal
  import           Clash.Netlist.BlackBox.Types
  
  -- | Parse a text as a BlackBoxTemplate, returns a list of errors in case
  -- parsing fails
  runParse :: Text -> Result BlackBoxTemplate
  runParse = parseString (pBlackBoxD <* eof) (Directed "" 0 0 0 0) . unpack
  
  -- | Parse a BlackBoxTemplate (Declarations and Expressions)
  pBlackBoxD :: Parser BlackBoxTemplate
  pBlackBoxD = some pElement
  
  -- | Parse a single Template Element
  pElement :: Parser Element
  pElement  =  pTagD
           <|> Text <$> pText
           <|> Text <$> (pack <$> string "~ ")
  
  -- | Parse the Text part of a Template
  pText :: Parser Text
  pText = pack <$> some (satisfyRange '\000' '\125')
  
  pEdge :: Parser Signal.ActiveEdge
  pEdge =
    (pure Signal.Rising <* symbol "Rising") <|>
    (pure Signal.Falling <* symbol "Falling")
  
  
  -- | Parse a Declaration or Expression element
  pTagD :: Parser Element
  pTagD =  IF <$> (symbol "~IF" *> pTagE)
              <*> (spaces *> (string "~THEN" *> pBlackBoxD))
              <*> (string "~ELSE" *> option ([Text ""]) pBlackBoxD <* string "~FI")
       <|> Component <$> pDecl
       <|> pTagE
  
  -- | Parse a Declaration
  pDecl :: Parser Decl
  pDecl = Decl <$> (symbol "~INST" *> natural') <*> pure 0 <*>
          ((:) <$> pOutput <*> many pInput) <* string "~INST"
  
  -- | Parse the output tag of Declaration
  pOutput :: Parser (BlackBoxTemplate,BlackBoxTemplate)
  pOutput = symbol "~OUTPUT" *> symbol "<=" *> ((,) <$> (pBlackBoxE <* symbol "~") <*> pBlackBoxE) <* symbol "~"
  
  -- | Parse the input tag of Declaration
  pInput :: Parser (BlackBoxTemplate,BlackBoxTemplate)
  pInput = symbol "~INPUT" *> symbol "<=" *> ((,) <$> (pBlackBoxE <* symbol "~") <*> pBlackBoxE) <* symbol "~"
  
  -- | Parse an Expression element
  pTagE :: Parser Element
  pTagE =  Result            <$  string "~RESULT"
       <|> ArgGen            <$> (string "~ARGN" *> brackets' natural') <*> brackets' natural'
       <|> Arg               <$> (string "~ARG" *> brackets' natural')
       <|> Const             <$> (string "~CONST" *> brackets' natural')
       <|> Lit               <$> (string "~LIT" *> brackets' natural')
       <|> Name              <$> (string "~NAME" *> brackets' natural')
       <|> ToVar             <$> try (string "~VAR" *> brackets' pSigDorEmpty) <*> brackets' natural'
       <|> (Sym Text.empty)  <$> (string "~SYM" *> brackets' natural')
       <|> Typ Nothing       <$  string "~TYPO"
       <|> (Typ . Just)      <$> try (string "~TYP" *> brackets' natural')
       <|> TypM Nothing      <$  string "~TYPMO"
       <|> (TypM . Just)     <$> (string "~TYPM" *> brackets' natural')
       <|> Err Nothing       <$  string "~ERRORO"
       <|> (Err . Just)      <$> (string "~ERROR" *> brackets' natural')
       <|> TypElem           <$> (string "~TYPEL" *> brackets' pTagE)
       <|> IndexType         <$> (string "~INDEXTYPE" *> brackets' pTagE)
       <|> CompName          <$  string "~COMPNAME"
       <|> IncludeName       <$> (string "~INCLUDENAME" *> brackets' natural')
       <|> Size              <$> (string "~SIZE" *> brackets' pTagE)
       <|> Length            <$> (string "~LENGTH" *> brackets' pTagE)
       <|> Depth             <$> (string "~DEPTH" *> brackets' pTagE)
       <|> MaxIndex          <$> (string "~MAXINDEX" *> brackets' pTagE)
       <|> FilePath          <$> (string "~FILE" *> brackets' pTagE)
       <|> Gen               <$> (True <$ string "~GENERATE")
       <|> Gen               <$> (False <$ string "~ENDGENERATE")
       <|> (`SigD` Nothing)  <$> (string "~SIGDO" *> brackets' pSigD)
       <|> SigD              <$> (string "~SIGD" *> brackets' pSigD) <*> (Just <$> (brackets' natural'))
       <|> IW64              <$  string "~IW64"
       <|> CmpLE             <$> try (string "~CMPLE" *> brackets' pTagE) <*> brackets' pTagE
       <|> (HdlSyn Vivado)   <$  string "~VIVADO"
       <|> (HdlSyn Other)    <$  string "~OTHERSYN"
       <|> (BV True)         <$> (string "~TOBV" *> brackets' pSigD) <*> brackets' pTagE
       <|> (BV False)        <$> (string "~FROMBV" *> brackets' pSigD) <*> brackets' pTagE
       <|> Sel               <$> (string "~SEL" *> brackets' pTagE) <*> brackets' natural'
       <|> IsLit             <$> (string "~ISLIT" *> brackets' natural')
       <|> IsVar             <$> (string "~ISVAR" *> brackets' natural')
       <|> IsScalar          <$> (string "~ISSCALAR" *> brackets' natural')
       <|> IsActiveHigh      <$> (string "~ISACTIVEHIGH" *> brackets' natural')
       <|> IsActiveEnable    <$> (string "~ISACTIVEENABLE" *> brackets' natural')
       <|> IsUndefined       <$> (string "~ISUNDEFINED" *> brackets' natural')
       <|> StrCmp            <$> (string "~STRCMP" *> brackets' pSigD) <*> brackets' natural'
       -- Parse ~OUTPUTWIREREG for backwards compatibility
       <|> OutputUsage       <$> (string "~OUTPUTWIREREG" *> brackets' natural')
       <|> OutputUsage       <$> (string "~OUTPUTUSAGE" *> brackets' natural')
       <|> GenSym            <$> (string "~GENSYM" *> brackets' pSigD) <*> brackets' natural'
       <|> Template          <$> (string "~TEMPLATE" *> brackets' pSigD) <*> brackets' pSigD
       <|> Repeat            <$> (string "~REPEAT" *> brackets' pSigD) <*> brackets' pSigD
       <|> DevNull           <$> (string "~DEVNULL" *> brackets' pSigD)
       <|> And               <$> (string "~AND" *> brackets' (commaSep pTagE))
       <|> Vars              <$> (string "~VARS" *> brackets' natural')
  
       -- Domain attributes:
       <|> Tag               <$> (string "~TAG" *> brackets' natural')
       <|> Period            <$> (string "~PERIOD" *> brackets' natural')
       <|> ActiveEdge        <$> (string "~ACTIVEEDGE" *> brackets pEdge) <*> brackets' natural'
       <|> IsSync            <$> (string "~ISSYNC" *> brackets' natural')
       <|> IsInitDefined     <$> (string "~ISINITDEFINED" *> brackets' natural')
       <|> CtxName           <$  string "~CTXNAME"
       <|> LongestPeriod     <$  string "~LONGESTPERIOD"
  
  natural' :: TokenParsing m => m Int
  natural' = fmap fromInteger natural
  
  -- | Parse a bracketed text
  brackets' :: Parser a -> Parser a
  brackets' p = char '[' *> p <* char ']'
  
  -- | Parse the expression part of Blackbox Templates
  pBlackBoxE :: Parser BlackBoxTemplate
  pBlackBoxE = some pElemE
  
  -- | Parse an Expression or Text
  pElemE :: Parser Element
  pElemE = pTagE
        <|> Text <$> pText
  
  -- | Parse SigD
  pSigD :: Parser [Element]
  pSigD = some (pTagE <|> (EscapedSymbol SquareBracketOpen <$ string "[\\")
                      <|> (EscapedSymbol SquareBracketClose <$ string "\\]")
                      <|> (Text <$> (pack <$> some (satisfyRange '\000' '\90')))
                      <|> (Text <$> (pack <$> some (satisfyRange '\94' '\125'))))
  
  pSigDorEmpty :: Parser [Element]
  pSigDorEmpty = pSigD <|> mempty
  