packages feed

stim-parser 0.4.0.0 → 0.4.1.0

raw patch · 7 files changed

+67/−41 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ StimParser.DEM.Expr: TargetSeparator :: DEMTarget

Files

CHANGELOG.md view
@@ -1,5 +1,18 @@ # Revision history for stim-parser +## 0.4.1.0 -- 2026-07-20++* Parse DEM separator targets (`^`) inside `error(...)` instructions.+  Added `TargetSeparator` constructor to `DEMTarget`. This is a non-breaking+  API addition; downstream code pattern-matching on `DEMTarget` should add a+  wildcard or handle the new constructor.+* Require at least one target for gate and measurement instructions.+  `parseGate` and `parseMeasure` now use `some parseQ` instead of+  `parseExhaust parseQ`. This prevents single-letter gate/measure names such+  as `R`, `S`, `H`, `I`, and `M` from consuming the leading character of+  keywords like `REPEAT` and `SHIFT_COORDS` and producing misleading parse+  errors.+ ## 0.4.0.0 -- 2026-07-20  * Add Pauli-target support for `OBSERVABLE_INCLUDE` and `DETECTOR`
src/StimParser/DEM/Expr.hs view
@@ -11,10 +11,12 @@ newtype ObservableId = ObservableId { unObservableId :: Int }   deriving (Show, Eq, Ord) --- | Targets of an error instruction: detectors and/or observables.+-- | Targets of an error instruction: detectors, observables, or the+-- separator target (^) used to suggest a decomposition. data DEMTarget     = TargetDetector !DetectorId     | TargetObservable !ObservableId+    | TargetSeparator     deriving (Show, Eq)  -- | A single error instruction: error(p) D0 D1 L0
src/StimParser/DEM/Parse.hs view
@@ -31,11 +31,12 @@   targets <- many parseDEMTarget   return $ DEMError p targets --- | Parse a single target: D0 or L0+-- | Parse a single target: D0, L0, or ^ parseDEMTarget :: Parser DEMTarget parseDEMTarget =       TargetDetector   . DetectorId   <$> (lstring "D" *> parseInt)   <|> TargetObservable . ObservableId <$> (lstring "L" *> parseInt)+  <|> TargetSeparator                  <$  lstring "^"  -- | Parse a detector declaration: detector(0, 0) D0 parseDEMDetector :: Parser DEMDetector
src/StimParser/Parse.hs view
@@ -103,7 +103,7 @@ parseGate = do   gty <- parseGateTy   tag <- optional parseTag-  qs <- parseExhaust parseQ+  qs <- some parseQ   return $ Gate gty tag qs  parseMeasureTy :: Parser MeasureTy@@ -124,13 +124,13 @@       mty <- parseMeasureTy       tag <- optional parseTag       ph <- parsePh-      qs <- parseExhaust parseQ+      qs <- some parseQ       return $ Measure mty tag (Just ph) qs     -- parser for case: M[tag] 0     pm2 = do        mty <- parseMeasureTy       tag <- optional parseTag-      qs <- parseExhaust parseQ+      qs <- some parseQ       return $ Measure mty tag Nothing qs   -- the order is tricky - try phase version first   try pm1 <|> pm2
stim-parser.cabal view
@@ -20,7 +20,7 @@ -- PVP summary:     +-+------- breaking API changes --                  | | +----- non-breaking API additions --                  | | | +--- code changes with no API change-version:            0.4.0.0+version:            0.4.1.0  -- A short (one-line) description of the package. synopsis:           A parser combinator library for STIM quantum circuit files
test/Test/DEM/Parse.hs view
@@ -283,50 +283,32 @@       DEM [] ~=? run parseDEM ""   ] --- | Caret (^) rejection — '^' indicates unflattened DEM decompositions,--- which are not part of the stable DEM format. We reject them loudly--- instead of silently discarding them.+-- | Caret (^) separator targets inside error(...) instructions. testParseDEMCaret :: Test testParseDEMCaret = TestList-  [ "single caret fails" ~: TestCase $ do-      let result = runParser parseDEM "" "error(0.01) D0 ^ D1 L0"-      case result of-        Left _ -> return ()-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"--  , "multiple carets fails" ~: TestCase $ do-      let result = runParser parseDEM "" "error(0.01) D0 ^ D1 ^ D2 L0"-      case result of-        Left _ -> return ()-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"+  [ "single caret parses" ~:+      DEM [DEMInstrError (DEMError 0.02 [TargetDetector (DetectorId 2), TargetObservable (ObservableId 0), TargetSeparator, TargetDetector (DetectorId 5), TargetDetector (DetectorId 6)])]+      ~=? run parseDEM "error(0.02) D2 L0 ^ D5 D6\n" -  , "caret between detectors fails" ~: TestCase $ do-      let result = runParser parseDEM "" "error(0.01) D0 ^ D1 ^ D2"-      case result of-        Left _ -> return ()-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"+  , "multiple carets parse" ~:+      DEM [DEMInstrError (DEMError 0.01 [TargetDetector (DetectorId 0), TargetSeparator, TargetDetector (DetectorId 1), TargetSeparator, TargetObservable (ObservableId 0)])]+      ~=? run parseDEM "error(0.01) D0 ^ D1 ^ L0\n" -  , "caret with observables fails" ~: TestCase $ do-      let result = runParser parseDEM "" "error(0.01) D0 ^ L0 ^ D1"-      case result of-        Left _ -> return ()-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"+  , "caret between detectors parses" ~:+      DEM [DEMInstrError (DEMError 0.01 [TargetDetector (DetectorId 0), TargetSeparator, TargetDetector (DetectorId 1), TargetSeparator, TargetDetector (DetectorId 2)])]+      ~=? run parseDEM "error(0.01) D0 ^ D1 ^ D2\n" -  , "caret at start fails" ~: TestCase $ do-      let result = runParser parseDEM "" "error(0.01) ^ D0 D1"-      case result of-        Left _ -> return ()-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"+  , "caret at start parses" ~:+      DEM [DEMInstrError (DEMError 0.01 [TargetSeparator, TargetDetector (DetectorId 0), TargetDetector (DetectorId 1)])]+      ~=? run parseDEM "error(0.01) ^ D0 D1\n" -  , "caret at end fails" ~: TestCase $ do-      let result = runParser parseDEM "" "error(0.01) D0 D1 ^"-      case result of-        Left _ -> return ()-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"+  , "caret at end parses" ~:+      DEM [DEMInstrError (DEMError 0.01 [TargetDetector (DetectorId 0), TargetDetector (DetectorId 1), TargetSeparator])]+      ~=? run parseDEM "error(0.01) D0 D1 ^\n"    , "no-caret error still parses" ~:       DEM [DEMInstrError (DEMError 0.01 [TargetDetector (DetectorId 0), TargetDetector (DetectorId 1), TargetObservable (ObservableId 0)])]-      ~=? run parseDEM "error(0.01) D0 D1 L0"+      ~=? run parseDEM "error(0.01) D0 D1 L0\n"   ]  -- | EOF enforcement — parseDEM must consume all input
test/Test/Parse.hs view
@@ -1,6 +1,7 @@ module Test.Parse where  import Test.HUnit+import Text.Megaparsec (runParser) import StimParser.Parse import StimParser.ParseUtils (run) import StimParser.Expr@@ -25,6 +26,7 @@   , testParseFInd   , testParseAnn   , testParseStimNoiseOrder+  , testParseZeroTargetRejection   ]  -- Helper to compare using show (since types don't derive Eq)@@ -310,3 +312,29 @@       (StimList [StimG (Gate H Nothing [Q 0, Q 1, Q 2])])       (run parseStim "!!!Start\nH 0 1 2\n")   ]++-- | Zero-target gates/measures must fail, and keywords that share prefixes with+-- gate/measure names must not be misparsed.+testParseZeroTargetRejection :: Test+testParseZeroTargetRejection = TestList+  [ "parseGate H with no target fails" ~: TestCase $ do+      let result = runParser parseGate "" "H"+      case result of+        Left _ -> return ()+        Right _ -> assertFailure "gate with zero targets should fail"++  , "parseMeasure M with no target fails" ~: TestCase $ do+      let result = runParser parseMeasure "" "M"+      case result of+        Left _ -> return ()+        Right _ -> assertFailure "measure with zero targets should fail"++  , assertShowEqual "SHIFT_COORDS after TICK parses"+      (StimList [StimAnn (Ann TICK Nothing [] []), StimAnn (Ann SHIFT_COORDS Nothing [In 0, In 0, In 1] [])])+      (run parseStim "!!!Start\nTICK\nSHIFT_COORDS(0, 0, 1)\n")++  , assertShowEqual "REPEAT after DETECTOR parses"+      (StimList [StimAnn (Ann DETECTOR Nothing [In 0, In 0, In 0] [AnnRec (Rec (-1))]), StimRepeat 10 (StimList [StimAnn (Ann TICK Nothing [] [])])])+      (run parseStim "!!!Start\nDETECTOR(0, 0, 0) rec[-1]\nREPEAT 10 {\nTICK\n}\n")+  ]+