packages feed

parsley-core 1.5.0.0 → 1.6.0.0

raw patch · 15 files changed

+240/−75 lines, 15 filesdep ~basedep ~containersPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, containers

API changes (from Hackage documentation)

+ Parsley.Internal.Backend.Machine.Instructions: [BlockCoins] :: MetaInstr n
+ Parsley.Internal.Backend.Machine.Instructions: blockCoins :: Fix4 (Instr o) xs (Succ n) r a -> Fix4 (Instr o) xs (Succ n) r a
+ Parsley.Internal.Core.CombinatorAST: [CutImmune] :: MetaCombinator
- Parsley.Internal.Frontend.Analysis.Cut: cutAnalysis :: Bool -> Fix Combinator a -> Fix Combinator a
+ Parsley.Internal.Frontend.Analysis.Cut: cutAnalysis :: Fix Combinator a -> Fix Combinator a

Files

ChangeLog.md view
@@ -89,3 +89,10 @@ * Added type to `next` in `CharList`. * Added auxilliary information parameter to `sat`. * Added `fetch` and broke it out of `sat`.++## 1.6.0.0 -- 2021-08-13+Fix for issue #27++* Added `BlockCoins` instruction and `CutImmune` node.+* Changed how cut compliance is determined, and stopped some incorrect factoring.+* Removed unneeded flags for analysis.
README.md view
@@ -1,8 +1,8 @@ # Parsley Internals -This package contains the internals for the [`parsley`](https://hackage.haskell.org/package/parsley-core) library. +This package contains the internals for the [`parsley`](https://hackage.haskell.org/package/parsley) library. -The version policy adheres to the regular Haskell PVP, but the two major versions are distinguished: +The version policy adheres to the regular Haskell PVP, but the two major versions are distinguished: the first is the _Public API_ major version, which represents backwards incompatible changes in the regular PVP sense that effect the `parsley` package itself and its users; the second version is the _Internal API_ major version, which would only effect users who use part of the internal parsley
parsley-core.cabal view
@@ -5,7 +5,7 @@ --                   | +------- breaking internal API changes --                   | | +----- non-breaking API additions --                   | | | +--- code changes with no API change-version:             1.5.0.0+version:             1.6.0.0 synopsis:            A fast parser combinator library backed by Typed Template Haskell description:         This package contains the internals of the @parsley@ package.                      .@@ -167,6 +167,13 @@   build-depends:       tasty-hunit, tasty-quickcheck   main-is:             CommonTest.hs   other-modules:       CommonTest.Queue, CommonTest.RewindQueue++test-suite regression-test+  import:              test-common+  type:                exitcode-stdio-1.0+  build-depends:       tasty-hunit, tasty-quickcheck, containers+  main-is:             RegressionTest.hs+  other-modules:       Regression.Issue27  source-repository head   type:                git
src/ghc-8.10+/Parsley/Internal/Backend/Machine/Eval.hs view
@@ -249,3 +249,4 @@     mkCheck True  k = local (giveCoins (int 1)) k <&> \mk γ -> emitLengthCheck 1 (mk γ) (raise γ) (input γ)     mkCheck False k = k     prefetch o ctx k = fetch o (\c o' -> k (addChar c o' ctx))+evalMeta BlockCoins (Machine k) = k
src/ghc-8.6+/Parsley/Internal/Backend/Machine/Eval.hs view
@@ -203,3 +203,4 @@      if | not check     -> k         | requiresPiggy -> local (storePiggy 1) k         | otherwise     -> local (giveCoins 1) k <&> \mk γ -> emitLengthCheck 1 mk (raise γ) γ+evalMeta BlockCoins (Machine k) = k
src/ghc/Parsley/Internal/Backend/Analysis/Coins.hs view
@@ -28,8 +28,8 @@ first :: (a -> b) -> (a, x) -> (b, x) first = flip bimap id -second :: (a -> b) -> (x, a) -> (x, b)-second = bimap id+--second :: (a -> b) -> (x, a) -> (x, b)+--second = bimap id  bimap :: (a -> b) -> (c -> d) -> (a, c) -> (b, d) bimap = curry (bilift2 ($) ($))@@ -72,9 +72,10 @@ alg (LogExit _ k)                           = getConst4 k alg (MetaInstr (AddCoins _) (Const4 k))     = k alg (MetaInstr (RefundCoins n) (Const4 k))  = first (minCoins zero . (`minus` n)) k -- These were refunded, so deduct-alg (MetaInstr (DrainCoins _) (Const4 k))   = second (const False) k-alg (MetaInstr (GiveBursary n) (Const4 _))  = (n, False)                            -- We know that `n` is the required for `k`+alg (MetaInstr (DrainCoins n) _)            = (n, False)                            -- Used to be `second (const False) k`, but these should be additive?+alg (MetaInstr (GiveBursary n) _)           = (n, False)                            -- We know that `n` is the required for `k` alg (MetaInstr (PrefetchChar _) (Const4 k)) = k+alg (MetaInstr BlockCoins (Const4 k))       = first (const zero) k  algHandler :: Handler o (Const4 (Coins, Bool)) xs n r a -> (Coins, Bool) algHandler (Same yes no) = algCatch (getConst4 yes) (getConst4 no)
src/ghc/Parsley/Internal/Backend/CodeGenerator.hs view
@@ -14,12 +14,11 @@ -} module Parsley.Internal.Backend.CodeGenerator (codeGen) where -import Data.Maybe                          (isJust) import Data.Set                            (Set, elems) import Control.Monad.Trans                 (lift) import Parsley.Internal.Backend.Machine    (user, userBool, LetBinding, makeLetBinding, newMeta, Instr(..), Handler(..),                                             _Fmap, _App, _Modify, _Get, _Put, _Make,-                                            addCoins, refundCoins, drainCoins, giveBursary,+                                            addCoins, refundCoins, drainCoins, giveBursary, blockCoins,                                             minus, minCoins, maxCoins, zero,                                             IMVar, IΦVar, IΣVar, MVar(..), ΦVar(..), ΣVar(..), SomeΣVar) import Parsley.Internal.Backend.Analysis   (coinsNeeded)@@ -57,12 +56,9 @@     m = finalise (histo alg p)     alg :: Combinator (Cofree Combinator (CodeGen o a)) x -> CodeGen o a x     alg = deep |> (\x -> CodeGen (shallow (imap extract x)))-    finalise cg =-      let m = runCodeGenStack (runCodeGen cg (In4 Ret)) μ0 0 σ0-      -- let-bound things are not safe to factor length checks out of-      -- This is because we do not know the cut characteristics of every caller-      -- In theory this /could/ be computed as the union of every call site-      in if isJust letBound then m else addCoins (coinsNeeded m) m+    -- It is never safe to add coins to the top of a binding+    -- This is because we don't know the characteristics of the caller (even the top-level!)+    finalise cg = runCodeGenStack (runCodeGen cg (In4 Ret)) μ0 0 σ0  pattern (:<$>:) :: Core.Defunc (a -> b) -> Cofree Combinator k a -> Combinator (Cofree Combinator k) b pattern f :<$>: p <- (_ :< Pure f) :<*>: p@@ -163,14 +159,15 @@      let minc = coinsNeeded (In4 (Choices (map userBool fs) qcs defc))      let defc':qcs' = map (maxCoins zero . (`minus` minc) . coinsNeeded >>= addCoins) (defc:qcs)      fmap binder (runCodeGen p (In4 (Choices (map user fs) qcs' defc')))-shallow (Let _ μ _)            m = do return $! tailCallOptimise μ m-shallow (ChainPre op p)        m = do chainPreCompile op p addCoinsNeeded id m-shallow (ChainPost p op)       m = do chainPostCompile p op addCoinsNeeded id m-shallow (MakeRegister σ p q)   m = do qc <- runCodeGen q m; runCodeGen p (In4 (_Make σ qc))-shallow (GetRegister σ)        m = do return $! In4 (_Get σ m)-shallow (PutRegister σ p)      m = do runCodeGen p (In4 (_Put σ (In4 (Push (user UNIT) m))))-shallow (Debug name p)         m = do fmap (In4 . LogEnter name) (runCodeGen p (In4 (Commit (In4 (LogExit name m)))))-shallow (MetaCombinator Cut p) m = do runCodeGen p (addCoins (coinsNeeded m) m)+shallow (Let _ μ _)                  m = do return $! tailCallOptimise μ m+shallow (ChainPre op p)              m = do chainPreCompile op p addCoinsNeeded id m+shallow (ChainPost p op)             m = do chainPostCompile p op addCoinsNeeded id m+shallow (MakeRegister σ p q)         m = do qc <- runCodeGen q m; runCodeGen p (In4 (_Make σ qc))+shallow (GetRegister σ)              m = do return $! In4 (_Get σ m)+shallow (PutRegister σ p)            m = do runCodeGen p (In4 (_Put σ (In4 (Push (user UNIT) m))))+shallow (Debug name p)               m = do fmap (In4 . LogEnter name) (runCodeGen p (In4 (Commit (In4 (LogExit name m)))))+shallow (MetaCombinator Cut p)       m = do blockCoins <$> runCodeGen p (addCoins (coinsNeeded m) m)+shallow (MetaCombinator CutImmune p) m = do addCoins . coinsNeeded <$> runCodeGen p (In4 Ret) <*> runCodeGen p m  tailCallOptimise :: MVar x -> Fix4 (Instr o) (x : xs) (Succ n) r a -> Fix4 (Instr o) xs (Succ n) r a tailCallOptimise μ (In4 Ret) = In4 (Jump μ)
src/ghc/Parsley/Internal/Backend/Machine/Instructions.hs view
@@ -24,7 +24,7 @@     -- * Smart Instructions     _App, _Fmap, _Modify, _Make, _Put, _Get,     -- * Smart Meta-Instructions-    addCoins, refundCoins, drainCoins, giveBursary, prefetchChar+    addCoins, refundCoins, drainCoins, giveBursary, prefetchChar, blockCoins   ) where  import Data.Kind                                    (Type)@@ -260,7 +260,13 @@    @since 1.5.0.0 -}   PrefetchChar :: Bool -> MetaInstr (Succ n)+  {-|+  True meta instruction: does /nothing/ except for reset coin count during coin analysis. +  @since 1.6.0.0+  -}+  BlockCoins :: MetaInstr n+ mkCoin :: (Coins -> MetaInstr n) -> Coins -> Fix4 (Instr o) xs n r a -> Fix4 (Instr o) xs n r a mkCoin _    (Coins 0 0) = id mkCoin meta n           = In4 . MetaInstr (meta n)@@ -271,7 +277,8 @@ @since 1.5.0.0 -} addCoins :: Coins -> Fix4 (Instr o) xs (Succ n) r a -> Fix4 (Instr o) xs (Succ n) r a-addCoins = mkCoin AddCoins+addCoins (Coins 1 1) = id+addCoins coins       = mkCoin AddCoins coins  {-| Smart-constuctor around `RefundCoins`.@@ -300,12 +307,20 @@ {-| Smart-constructor around `PrefetchChar`. -@since 1.5.0.0 +@since 1.5.0.0 -} prefetchChar :: Bool -> Fix4 (Instr o) xs (Succ n) r a -> Fix4 (Instr o) xs (Succ n) r a prefetchChar check = In4 . MetaInstr (PrefetchChar check)  {-|+Smart-constructor around `PrefetchChar`.++@since 1.6.0.0+-}+blockCoins :: Fix4 (Instr o) xs (Succ n) r a -> Fix4 (Instr o) xs (Succ n) r a+blockCoins = In4 . MetaInstr BlockCoins++{-| Applies a value on the top of the stack to a function on the second-most top of the stack.  @since 1.0.0.0@@ -389,31 +404,32 @@   show = ($ "") . getConst4 . cata4 (Const4 . alg)     where       alg :: forall xs n r a. Instr o (Const4 (String -> String)) xs n r a -> String -> String-      alg Ret                 = "Ret"-      alg (Call μ k)          = "(Call " . shows μ . " " . getConst4 k . ")"-      alg (Jump μ)            = "(Jump " . shows μ . ")"-      alg (Push x k)          = "(Push " . shows x . " " . getConst4 k . ")"-      alg (Pop k)             = "(Pop " . getConst4 k . ")"-      alg (Lift2 f k)         = "(Lift2 " . shows f . " " . getConst4 k . ")"-      alg (Sat f k)           = "(Sat " . shows f . " " . getConst4 k . ")"-      alg Empt                = "Empt"-      alg (Commit k)          = "(Commit " . getConst4 k . ")"-      alg (Catch p h)         = "(Catch " . getConst4 p . " " . shows h . ")"-      alg (Tell k)            = "(Tell " . getConst4 k . ")"-      alg (Seek k)            = "(Seek " . getConst4 k . ")"-      alg (Case p q)          = "(Case " . getConst4 p . " " . getConst4 q . ")"-      alg (Choices fs ks def) = "(Choices " . shows fs . " [" . intercalateDiff ", " (map getConst4 ks) . "] " . getConst4 def . ")"-      alg (Iter μ l h)        = "{Iter " . shows μ . " " . getConst4 l . " " . shows h . "}"-      alg (Join φ)            = shows φ-      alg (MkJoin φ p k)      = "(let " . shows φ . " = " . getConst4 p . " in " . getConst4 k . ")"-      alg (Swap k)            = "(Swap " . getConst4 k . ")"-      alg (Dup k)             = "(Dup " . getConst4 k . ")"-      alg (Make σ a k)        = "(Make " . shows σ . " " . shows a . " " . getConst4 k . ")"-      alg (Get σ a k)         = "(Get " . shows σ . " " . shows a . " " . getConst4 k . ")"-      alg (Put σ a k)         = "(Put " . shows σ . " " . shows a . " " . getConst4 k . ")"-      alg (LogEnter _ k)      = getConst4 k-      alg (LogExit _ k)       = getConst4 k-      alg (MetaInstr m k)     = "[" . shows m . "] " . getConst4 k+      alg Ret                      = "Ret"+      alg (Call μ k)               = "(Call " . shows μ . " " . getConst4 k . ")"+      alg (Jump μ)                 = "(Jump " . shows μ . ")"+      alg (Push x k)               = "(Push " . shows x . " " . getConst4 k . ")"+      alg (Pop k)                  = "(Pop " . getConst4 k . ")"+      alg (Lift2 f k)              = "(Lift2 " . shows f . " " . getConst4 k . ")"+      alg (Sat f k)                = "(Sat " . shows f . " " . getConst4 k . ")"+      alg Empt                     = "Empt"+      alg (Commit k)               = "(Commit " . getConst4 k . ")"+      alg (Catch p h)              = "(Catch " . getConst4 p . " " . shows h . ")"+      alg (Tell k)                 = "(Tell " . getConst4 k . ")"+      alg (Seek k)                 = "(Seek " . getConst4 k . ")"+      alg (Case p q)               = "(Case " . getConst4 p . " " . getConst4 q . ")"+      alg (Choices fs ks def)      = "(Choices " . shows fs . " [" . intercalateDiff ", " (map getConst4 ks) . "] " . getConst4 def . ")"+      alg (Iter μ l h)             = "{Iter " . shows μ . " " . getConst4 l . " " . shows h . "}"+      alg (Join φ)                 = shows φ+      alg (MkJoin φ p k)           = "(let " . shows φ . " = " . getConst4 p . " in " . getConst4 k . ")"+      alg (Swap k)                 = "(Swap " . getConst4 k . ")"+      alg (Dup k)                  = "(Dup " . getConst4 k . ")"+      alg (Make σ a k)             = "(Make " . shows σ . " " . shows a . " " . getConst4 k . ")"+      alg (Get σ a k)              = "(Get " . shows σ . " " . shows a . " " . getConst4 k . ")"+      alg (Put σ a k)              = "(Put " . shows σ . " " . shows a . " " . getConst4 k . ")"+      alg (LogEnter _ k)           = getConst4 k+      alg (LogExit _ k)            = getConst4 k+      alg (MetaInstr BlockCoins k) = getConst4 k+      alg (MetaInstr m k)          = "[" . shows m . "] " . getConst4 k  instance Show (Handler o (Const4 (String -> String)) (o : xs) n r a) where   show (Same yes no) = "(Dup (Tell (Lift2 same (If " (getConst4 yes (" " (getConst4 no "))))")))@@ -425,3 +441,4 @@   show (DrainCoins n)   = "Using " ++ show n ++ " coins"   show (GiveBursary n)  = "Bursary of " ++ show n ++ " coins"   show (PrefetchChar b) = "Prefetch character " ++ (if b then "with" else "without") ++ " length-check"+  show BlockCoins       = ""
src/ghc/Parsley/Internal/Core/CombinatorAST.hs view
@@ -50,8 +50,14 @@ newtype Reg (r :: Type) a = Reg (ΣVar a)  data MetaCombinator where+  -- | After this combinator exits, a cut has happened   Cut         :: MetaCombinator+  -- | This combinator requires a cut from below to respect parsec semantics   RequiresCut :: MetaCombinator+  -- | This combinator denotes that within its scope, cut semantics are not enforced+  -- +  -- @since 1.6.0.0+  CutImmune   :: MetaCombinator  -- Instances instance IFunctor Combinator where@@ -107,6 +113,7 @@ instance Show MetaCombinator where   show Cut = "coins after"   show RequiresCut = "requires cut"+  show CutImmune = "immune to cuts"  {-# INLINE traverseCombinator #-} traverseCombinator :: Applicative m => (forall a. f a -> m (k a)) -> Combinator f a -> m (Combinator k a)
src/ghc/Parsley/Internal/Frontend/Analysis.hs view
@@ -19,7 +19,7 @@ import Parsley.Internal.Frontend.Analysis.Cut          (cutAnalysis) import Parsley.Internal.Frontend.Analysis.Dependencies (dependencyAnalysis) -import Parsley.Internal.Frontend.Analysis.Flags as Flags (emptyFlags, AnalysisFlags(letBound))+import Parsley.Internal.Frontend.Analysis.Flags as Flags (emptyFlags, AnalysisFlags)  {-| Performs Cut-Analysis on the combinator tree (See "Parsley.Internal.Frontend.Analysis.Cut")@@ -27,4 +27,4 @@ @since 1.5.0.0 -} analyse :: AnalysisFlags -> Fix Combinator a -> Fix Combinator a-analyse flags = cutAnalysis (letBound flags)+analyse _ = cutAnalysis
src/ghc/Parsley/Internal/Frontend/Analysis/Cut.hs view
@@ -25,9 +25,8 @@  @since 1.5.0.0 -}-cutAnalysis :: Bool -- ^ Whether or not the parser in question is a let-bound parser.-            -> Fix Combinator a -> Fix Combinator a-cutAnalysis letBound = fst . ($ letBound) . doCut . zygo (CutAnalysis . cutAlg) compliance+cutAnalysis :: Fix Combinator a -> Fix Combinator a+cutAnalysis = fst . ($ True) . doCut . zygo (CutAnalysis . cutAlg) compliance  data Compliance (k :: Type) = DomComp | NonComp | Comp | FullPure deriving stock (Show, Eq) newtype CutAnalysis a = CutAnalysis { doCut :: Bool -> (Fix Combinator a, Bool) }@@ -75,11 +74,24 @@ cutAlg (Satisfy f) cut = (mkCut cut (In (Satisfy f)), True) cutAlg Empty _ = (In Empty, False) cutAlg (Let r μ p) cut = (mkCut (not cut) (In (Let r μ (fst (doCut (ifst p) True)))), False) -- If there is no cut, we generate a piggy for the continuation-cutAlg (Try p) _ = False <$ rewrap Try False (ifst p)-cutAlg ((p :*: NonComp) :<|>: (q :*: FullPure)) _ = (requiresCut (In (fst (doCut p True) :<|>: fst (doCut q False))), True)+cutAlg (Try p) cut = (In (Try (mkImmune cut (fst (doCut (ifst p) False)))), False)+-- Special case of below, but we know immunity is useless within `q`+cutAlg ((p :*: NonComp) :<|>: (q :*: FullPure)) _ = (requiresCut (In (fst (doCut p True) :<|>: fst (doCut q False))), False)+-- both branches have to handle a cut, if `p` fails having consumed input that satisfies a cut+-- but if it doesn't, then `q` will need to handle the cut instead. When `q` has no cut to handle,+-- then that means it is immune to cuts, which is handy!+cutAlg ((p :*: NonComp) :<|>: q) cut =+  let (p', handled) = doCut p True+      (q', handled') = doCut (ifst q) cut+  in (requiresCut (In (p' :<|>: mkImmune (not cut) q')), handled && handled')+-- Why cut in both branches? Good question+-- The point here is that, even though `p` doesn't require a cut, this will enable an immunity+-- allowing for internal factoring  of input. However, if we are not under a cut requirement, we'd+-- like this input to be factored out further. cutAlg (p :<|>: q) cut =-  let (q', handled) = doCut (ifst q) cut-  in (In (fst (doCut (ifst p) False) :<|>: q'), handled)+  let (p', handled) = doCut (ifst p) cut+      (q', handled') = doCut (ifst q) cut+  in (In (p' :<|>: q'), handled && handled') cutAlg (l :<*>: r) cut = seqCutAlg (:<*>:) cut (ifst l) (ifst r) cutAlg (l :<*: r) cut = seqCutAlg (:<*:) cut (ifst l) (ifst r) cutAlg (l :*>: r) cut = seqCutAlg (:*>:) cut (ifst l) (ifst r)@@ -88,16 +100,18 @@ cutAlg (Debug msg p) cut = rewrap (Debug msg) cut (ifst p) cutAlg (ChainPre (op :*: NonComp) p) _ =   let (op', _) = doCut op True-      (p', _) = doCut (ifst p) False-  in (requiresCut (In (ChainPre op' p')), True)+      (p', handled) = doCut (ifst p) False+  -- the loop could terminate having read no `op`s, so only `p` can decide if its handled.+  in (requiresCut (In (ChainPre op' p')), handled) cutAlg (ChainPre op p) cut =   let (op', _) = doCut (ifst op) False       (p', handled) = doCut (ifst p) cut   in (mkCut (not cut) (In (ChainPre op' p')), handled) cutAlg (ChainPost p (op :*: NonComp)) cut =-  let (p', _) = doCut (ifst p) cut+  let (p', handled) = doCut (ifst p) cut       (op', _) = doCut op True-  in (requiresCut (In (ChainPost p' op')), True)+  -- the loop could terminate having read no `op`s, so only `p` can decide if its handled.+  in (requiresCut (In (ChainPost p' op')), handled) cutAlg (ChainPost p op) cut =   let (p', handled) = doCut (ifst p) cut       (op', _) = doCut (ifst op) False@@ -120,6 +134,10 @@ mkCut :: Bool -> Fix Combinator a -> Fix Combinator a mkCut True = In . MetaCombinator Cut mkCut False = id++mkImmune :: Bool -> Fix Combinator a -> Fix Combinator a+mkImmune True = In . MetaCombinator CutImmune+mkImmune False = id  requiresCut :: Fix Combinator a -> Fix Combinator a requiresCut = In . MetaCombinator RequiresCut
src/ghc/Parsley/Internal/Frontend/Analysis/Flags.hs view
@@ -9,20 +9,15 @@  @since 1.5.0.0 -}-module Parsley.Internal.Frontend.Analysis.Flags (AnalysisFlags(letBound), emptyFlags) where+module Parsley.Internal.Frontend.Analysis.Flags (AnalysisFlags, emptyFlags) where  {-| The packaged flags object.  @since 1.5.0.0 -}-newtype AnalysisFlags = AnalysisFlags {-  {-|-  Is the binding used in this analysis let-bound?+data AnalysisFlags = AnalysisFlags { -  @since 1.5.0.0-  -}-  letBound :: Bool }  {-|@@ -31,4 +26,4 @@ @since 1.5.0.0 -} emptyFlags :: AnalysisFlags-emptyFlags = AnalysisFlags False+emptyFlags = AnalysisFlags {}
src/ghc/Parsley/Internal/Frontend/Compiler.hs view
@@ -25,7 +25,6 @@ import Data.HashSet                        (HashSet) import Data.IORef                          (IORef, newIORef, readIORef, writeIORef) import Data.Kind                           (Type)-import Data.Maybe                          (isJust) import Data.Set                            (Set) import Control.Arrow                       (first, second) import Control.Monad                       (void, when)@@ -40,7 +39,7 @@ import Parsley.Internal.Common.Indexed     (Fix(In), cata, cata', IFunctor(imap), (:+:)(..), (\/), Const1(..)) import Parsley.Internal.Common.State       (State, get, gets, runState, execState, modify', MonadState) import Parsley.Internal.Frontend.Optimiser (optimise)-import Parsley.Internal.Frontend.Analysis  (analyse, emptyFlags, letBound, dependencyAnalysis)+import Parsley.Internal.Frontend.Analysis  (analyse, emptyFlags, dependencyAnalysis) import Parsley.Internal.Trace              (Trace(trace)) import System.IO.Unsafe                    (unsafePerformIO) @@ -71,7 +70,7 @@     freeRegs = maybe Set.empty (\(MVar v) -> frs Map.! v)      codeGen' :: Maybe (MVar x) -> Fix Combinator x -> compiled x-    codeGen' letBound p = codeGen letBound (analyse (emptyFlags {letBound = isJust letBound}) p) (freeRegs letBound) (maxV + 1) (maxΣ + 1)+    codeGen' letBound p = codeGen letBound (analyse emptyFlags p) (freeRegs letBound) (maxV + 1) (maxΣ + 1)  preprocess :: Fix (Combinator :+: ScopeRegister) a -> (Fix Combinator a, DMap MVar (Fix Combinator), IMVar) preprocess p =
+ test/Regression/Issue27.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE GADTs, MultiParamTypeClasses #-}+module Regression.Issue27 where++import Prelude hiding (pure, (*>))++import Test.Tasty+import Test.Tasty.HUnit++import Parsley.Internal+import Parsley.Internal.Core.CombinatorAST (Parser(unParser), Combinator)+import Parsley.Internal.Common (Fix(In), Fix4(In4), cata, (\/))+import Parsley.Internal.Backend.CodeGenerator (codeGen)+import Parsley.Internal.Backend.Machine.LetBindings (Binding, body)+import Parsley.Internal.Backend.Machine.Types.Coins (willConsume)+import Parsley.Internal.Backend.Machine.Instructions+import Parsley.Internal.Frontend.Analysis.Cut (cutAnalysis)++import qualified Data.Set as Set (empty)++import Parsley.Internal.Verbose+--instance {-# INCOHERENT #-} Trace where trace = const id++-- Hand optimised version of string, so we don't have to run the frontend+string :: String -> Parser String+string str = foldr (\c p -> satisfy (EQ_H (LIFTED c)) *> p) (pure (LIFTED str)) str++-- No registers allowed!+toAST :: Parser a -> Fix Combinator a+toAST = cata (In \/ undefined) . unParser++codeGen' :: Fix Combinator a -> Binding o a a+codeGen' p = body (codeGen Nothing p Set.empty 0 0)++ex1_p :: Fix Combinator String+ex1_p = toAST $ try $ string "123" <|> string "45"++ex2_p :: Fix Combinator String+ex2_p = toAST $ try $ try (string "123") <|> string "45"++ex3_p :: Fix Combinator String+ex3_p = toAST $ string "123" <|> string "45"++ex4_p :: Fix Combinator String+ex4_p = toAST $ try (string "123") <|> try (string "45")++ex5_p :: Fix Combinator String+ex5_p = toAST $ (string "a" <|> pure (LIFTED "")) *> string "1234"++ex6_p :: Fix Combinator String+ex6_p = toAST $ (string "abc" <|> string "def") *> string "123"++ex7_p :: Fix Combinator String+ex7_p = toAST $ (string "abc" <|> string "123") *> string "..." <|> string "def"++ex8_p :: Fix Combinator String+ex8_p = toAST $ (try (string "abc") <|> string "ade") *> string "..." <|> string "def"++leadingCoinsUnderCatch :: Fix4 (Instr o) xs n r a -> Maybe Int+leadingCoinsUnderCatch (In4 (Catch (In4 (MetaInstr (AddCoins c) _)) _)) = Just (willConsume c)+leadingCoinsUnderCatch _ = Nothing++leadingCoins :: Fix4 (Instr o) xs n r a -> Maybe Int+leadingCoins (In4 (MetaInstr (AddCoins c) _)) = Just (willConsume c)+leadingCoins _ = Nothing++test1 :: Assertion+test1 = let coins = leadingCoinsUnderCatch (codeGen' (cutAnalysis ex1_p))+        in (coins `elem` [Just 1, Nothing]) @? "expected 0 or 1 leading coins, got " ++ show coins++test2 :: Assertion+test2 = leadingCoinsUnderCatch (codeGen' (cutAnalysis ex2_p)) @?= Just 2++test3 :: Assertion+test3 = let coins = leadingCoins (codeGen' (cutAnalysis ex3_p))+        in (coins `elem` [Just 1, Nothing]) @? "expected 0 or 1 leading coins, got " ++ show coins++test4 :: Assertion+test4 = let coins = leadingCoins (codeGen' (cutAnalysis ex4_p))+        in (coins `elem` [Just 1, Nothing]) @? "expected 0 or 1 leading coins, got " ++ show coins++test5 :: Assertion+test5 = let coins = leadingCoinsUnderCatch (codeGen' (cutAnalysis ex5_p))+        in (coins `elem` [Just 1, Nothing]) @? "expected 0 or 1 leading coins, got " ++ show coins++test6 :: Assertion+test6 = leadingCoinsUnderCatch (codeGen' (cutAnalysis ex6_p)) @?= Nothing++test7 :: Assertion+test7 = leadingCoinsUnderCatch (codeGen' (cutAnalysis ex7_p)) @?= Nothing++test8 :: Assertion+test8 = leadingCoinsUnderCatch (codeGen' (cutAnalysis ex8_p)) @?= Nothing++tests :: TestTree+tests = testGroup "#27 Input checks in `Frontend` and `Backend` are not properly respecting cuts"+  [ testCase "under try max 1 coins should be factored without try" test1+  , testCase "under try 2 coins should be factored with try" test2+  , testCase "max 1 coins should be factored without try" test3+  , testCase "max 1 coins should be factored with try" test4+  --, testCase "" test5+  --, testCase "" test6+  --, testCase "" test7+  --, testCase "" test8+  ]
+ test/RegressionTest.hs view
@@ -0,0 +1,11 @@+module Main where++import Test.Tasty+import qualified Regression.Issue27 as Issue27++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Regression Tests" [ Issue27.tests+                                     ]