packages feed

stim-parser-0.2.0.0: src/StimParser/Parse.hs

{-# OPTIONS_GHC -Wno-unused-do-bind #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use <$>" #-}
module StimParser.Parse where
import StimParser.Expr
import StimParser.ParseUtils
import Control.Monad
import Data.List
import Text.Megaparsec hiding (State)
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L

-- parsePauli is not lexemed
parsePauli :: Parser Pauli
parsePauli = 
  try (string "X" >> return PX) 
  <|> try (string "Y" >> return PY)
  <|> (string "Z" >> return PZ)

-- parsePauliInd is not lexemed
parsePauliInd :: Parser PauliInd
parsePauliInd = do 
  p <- parsePauli
  i <- L.decimal
  return $ PauliInd p i

parsePauliChain :: Parser PauliChain
parsePauliChain = do 
  let
    parseElm = do 
      pp <- parsePauliInd
      string "*"
      return pp 

    parsePCs_ = do 
      pcs <- parseExhaust parseElm
      pp <- parsePauliInd
      return $ pcs ++ [pp]

    -- parser for case: X2*X3*X5*X7
    parsePCs = P <$> parsePCs_
    -- parser for case: !Z3*Z4*Z5
    parseNPCs = do 
      string "!"
      N <$> parsePCs_
  lexeme $ try parseNPCs <|> parsePCs
      

parseQ :: Parser Q
parseQ = do
  let
    qint = Q <$> parseInt
    qrec = do
      lstring "rec"
      lstring "["
      i <- parseInt
      lstring "]"
      if i < 0 
        then return $ QRec (Rec i)
        else fail "rec[] index must be negative (e.g., rec[-1])"
    qsweep = do
      lstring "sweep"
      lstring "["
      i <- parseInt
      lstring "]"
      return $ QSweep (Sweep i)
    qnot = do
      lstring "!"
      Not <$> parseInt
  try qint <|> try qrec <|> try qsweep <|> qnot


parseShow :: (Show a) => a -> Parser a
parseShow x = lstring (show x) >> return x

-- | Case-insensitive version of parseShow
parseShowCI :: (Show a) => a -> Parser a
parseShowCI x = lstringCI (show x) >> return x

-- parseEnum :: (Show a) => [a] -> Parser a
-- parseEnum (x:xs) = do
--   let
--     parseTail = msum $ try . parseShow <$> xs
--   parseTail <|> parseShow x
-- parseEnum [] = error "value error"

parseEnum :: (Show a) => [a] -> Parser a
parseEnum [] = error "value error"
parseEnum xs = msum $ map parseShowCI $ sortOn (negate . length . show) xs

-- | Parse an optional tag: [tag_content]
-- Tag content can be any character except ], \r, \n
parseTag :: Parser Tag
parseTag = do
  lstring "["
  content <- many $ satisfy (\c -> c /= ']' && c /= '\r' && c /= '\n')
  lstring "]"
  return $ Tag content

parseGateTy :: Parser GateTy
parseGateTy = parseEnum gateTyList

-- -- always remember to update this, when new syntax element is supported
-- preparseNext :: Parser ()
-- preparseNext = 
--   try (void parseGateTy)
--   <|> try (void parseMeasureTy)
--   <|> void parseGppTy

parseGate :: Parser Gate
parseGate = do
  gty <- parseGateTy
  tag <- optional parseTag
  qs <- parseExhaust parseQ
  return $ Gate gty tag qs

parseMeasureTy :: Parser MeasureTy
parseMeasureTy = parseEnum measureTyList

parsePh :: Parser Double
parsePh = do 
  lstring "("
  ph <- parseFloat
  lstring ")"
  return ph

parseMeasure :: Parser Measure
parseMeasure = do 
  let 
    -- parser for case: M[tag](0.02) 2 3 5
    pm1 = do 
      mty <- parseMeasureTy
      tag <- optional parseTag
      ph <- parsePh
      qs <- parseExhaust parseQ
      return $ Measure mty tag (Just ph) qs
    -- parser for case: M[tag] 0
    pm2 = do 
      mty <- parseMeasureTy
      tag <- optional parseTag
      qs <- parseExhaust parseQ
      return $ Measure mty tag Nothing qs
  -- the order is tricky - try phase version first
  try pm1 <|> pm2


parseGppTy :: Parser GppTy
parseGppTy = parseEnum gppTyList

parseGpp :: Parser Gpp 
parseGpp = do 
  let 
    -- parser for case: MPP[tag](0.001) Z1*Z2 X1*X2
    pm1 = do 
      gty <- parseGppTy
      tag <- optional parseTag
      ph <- parsePh
      pcs <- parseExhaust parsePauliChain
      return $ Gpp gty tag (Just ph) pcs
    -- parser for case: MPP[tag] X1*Y2 !Z3*Z4*Z5
    pm2 = do 
      gty <- parseGppTy
      tag <- optional parseTag
      pcs <- parseExhaust parsePauliChain
      return $ Gpp gty tag Nothing pcs
  -- the order is tricky - try phase version first
  try pm1 <|> pm2

parseNoiseTy :: Parser NoiseTy
parseNoiseTy = parseEnum noiseTyList

parseErrorTagTy :: Parser ErrorTagTy
parseErrorTagTy = parseEnum errorTagTyList

parseErrorTag :: Parser ErrorTag
parseErrorTag = do 
  let 
    -- parser for case: MULTIPLE_NOISE_MECHANISMS
    pm1 = ErrorTag <$> parseErrorTagTy
    -- parser for case: LEAKAGE_NOISE_FOR_AN_ADVANCED_SIMULATOR:0.1
    pm2 = do 
      ett <- parseErrorTagTy
      lstring ":"
      ph <- parseFloat
      return $ ErrorTagCoef ett ph 
  -- the order is tricky
  try pm2 <|> pm1

parseNoise :: Parser Noise
parseNoise = do 
  let 
    -- parser for case: X_ERROR[tag](0.01) 0 - with general tag and args
    pm1 = do 
      ty <- parseNoiseTy
      tag <- optional parseTag
      phs <- parseTupleFloat
      qs <- parseExhaust parseQ 
      return $ NoiseNormal ty tag Nothing phs qs 
    -- parser for case: CORRELATED_ERROR[tag](0.2) X1 Y2
    pm2 = do 
      ty <- parseNoiseTy
      tag <- optional parseTag
      lstring "("
      ph <- parseFloat
      lstring ")"
      ps <- some (lexeme parsePauliInd)
      return $ NoiseE ty tag ph ps 
    -- parser for case: X_ERROR[tag] 0 - with general tag, no args
    pm3 = do 
      ty <- parseNoiseTy
      tag <- optional parseTag
      qs <- parseExhaust parseQ 
      return $ NoiseNormal ty tag Nothing [] qs
    -- parser for case: II_ERROR[ErrorTag:coef] 0 2 4 6 - error tag without args
    pm4 = do 
      ty <- parseNoiseTy
      lstring "["
      et <- parseErrorTag
      lstring "]"
      tag <- optional parseTag  -- Optional general tag after error tag
      qs <- parseExhaust parseQ 
      return $ NoiseNormal ty tag (Just et) [] qs
    -- parser for case: II_ERROR[ErrorTag](0.1, 0.2) 0 2 4 6 - error tag with args
    pm5 = do 
      ty <- parseNoiseTy
      lstring "["
      et <- parseErrorTag
      lstring "]"
      tag <- optional parseTag  -- Optional general tag after error tag
      phs <- parseTupleFloat
      qs <- parseExhaust parseQ 
      return $ NoiseNormal ty tag (Just et) phs qs
  -- the order is tricky - try most specific first
  try pm2 <|> try pm5 <|> try pm4 <|> try pm1 <|> pm3

parseAnnTy :: Parser AnnTy
parseAnnTy = parseEnum annTyList

parseFInd :: Parser FInd
parseFInd = do 
  let 
    -- In Ind
    pm1 = do 
      i <- parseInt
      return $ In i 
    -- Fl Double
    pm2 = do
      f <- parseFloat
      return $ Fl f 
  -- the order is tricky 
  try pm2 <|> pm1

parseAnn :: Parser Ann 
parseAnn = do 
  let 
    -- parser for case: DETECTOR[tag](1, 0) rec[-3] rec[-6]
    pm1 = do 
      ty <- parseAnnTy 
      tag <- optional parseTag
      fds <- parseTuple parseFInd
      qs <- parseExhaust parseQ
      return $ Ann ty tag fds qs
    -- parser for case: DETECTOR[tag] rec[-3] rec[-4] rec[-7]
    pm2 = do 
      ty <- parseAnnTy
      tag <- optional parseTag
      qs <- parseExhaust parseQ
      return $ Ann ty tag [] qs
    -- parser for case: SHIFT_COORDS[tag](500.5)
    pm3 = do 
      ty <- parseAnnTy
      tag <- optional parseTag
      fds <- parseTuple parseFInd
      return $ Ann ty tag fds []
    -- parser for case: TICK[tag]
    pm4 = do
      ty <- parseShowCI TICK
      tag <- optional parseTag
      return $ Ann ty tag [] []
  -- the order is tricky
  try pm1 <|> try pm2 <|> try pm3 <|> pm4 

parseStart :: Parser ()
parseStart = void (lstring "!!!Start") 

parseStim :: Parser Stim 
parseStim = do 
  let 
    -- the order may be tricky
    parseUnit = 
      try (StimG <$> parseGate)
      <|> try (StimM <$> parseMeasure)
      <|> try (StimGpp <$> parseGpp)
      <|> try (StimNoise <$> parseNoise)
      <|> (StimAnn <$> parseAnn)

    parseUnitList = StimList <$> parseExhaust parseUnit
    parseRepeat = do 
      lstring "REPEAT"
      i <- parseInt
      lstring "{"
      us <- parseUnitList
      lstring "}"
      return $ StimRepeat i us

    parseUR = try parseRepeat <|> parseUnit

  parseStart
  
  StimList <$> safeManyTill parseUR eof