streamly-0.11.0: test/lib/Streamly/Test/Parser/Common.hs
module Streamly.Test.Parser.Common
( Move(..)
, jumpParser
, chunkedTape
, tape
, tapeLen
, expectedResult
, expectedResultMany
, parserSanityTests
)
where
--------------------------------------------------------------------------------
-- Imports
--------------------------------------------------------------------------------
import Streamly.Internal.Data.Parser (ParseErrorPos(..))
import qualified Streamly.Internal.Data.Parser as P
import Test.Hspec
--------------------------------------------------------------------------------
-- Parser driver tests
--------------------------------------------------------------------------------
data Move
= Consume Int
| Custom (P.Step () ())
deriving (Show)
jumpParser :: Monad m => [Move] -> P.Parser Int m [Int]
jumpParser jumps = P.Parser step initial done
where
initial = pure $ P.IPartial (jumps, [])
step ([], buf) _ = pure $ P.SDone 0 (reverse buf)
step (action:xs, buf) a =
case action of
Consume n
| n == 1 -> pure $ P.SContinue 1 (xs, a:buf)
| n > 0 -> pure $ P.SContinue 1 (Consume (n - 1) : xs, a:buf)
| otherwise -> error "Cannot consume <= 0"
Custom (P.SPartial i ()) -> pure $ P.SPartial i (xs, buf)
Custom (P.SContinue i ()) -> pure $ P.SContinue i (xs, buf)
Custom (P.SDone i ()) -> pure $ P.SDone i (reverse buf)
Custom (P.SError err) -> pure $ P.SError err
done ([], buf) = pure $ P.FDone 0 (reverse buf)
done (action:xs, buf) =
case action of
Consume _ -> pure $ P.FError "INCOMPLETE"
Custom (P.SPartial i ()) -> pure $ P.FContinue i (xs, buf)
Custom (P.SContinue i ()) -> pure $ P.FContinue i (xs, buf)
Custom (P.SDone i ()) -> pure $ P.FDone i (reverse buf)
Custom (P.SError err) -> pure $ P.FError err
chunkedTape :: [[Int]]
chunkedTape = Prelude.map (\x -> [x..(x+9)]) [1, 11 .. 91]
tape :: [Int]
tape = concat chunkedTape
tapeLen :: Int
tapeLen = length tape
expectedResult :: [Move] -> [Int] -> (Either ParseErrorPos [Int], [Int])
expectedResult moves inp = go 0 0 [] moves
where
inpLen = length inp
slice off len = Prelude.take len . Prelude.drop off
slice_ = Prelude.drop
-- i = Index of inp head
-- j = Minimum index of inp head
go i j ys [] = (Right ys, slice_ (max i j) inp)
go i j ys ((Consume n):xs)
| i + n > inpLen = (Left (ParseErrorPos inpLen "INCOMPLETE"), drop j inp)
| otherwise =
go (i + n) j (ys ++ slice i n inp) xs
go i j ys ((Custom step):xs)
| i > inpLen = error "i > inpLen"
| otherwise =
case step of
P.SPartial n () -> go (i + n) (max j (i + n)) ys xs
P.SContinue n () -> go (i + n) j ys xs
P.SDone n () -> (Right ys, slice_ (max (i + n) j) inp)
P.SError err -> (Left (ParseErrorPos (i + 1) err), slice_ j inp)
expectedResultMany :: [Move] -> [Int] -> [Either ParseErrorPos [Int]]
expectedResultMany = go 0
where
go _ _ [] = []
go off moves inp =
let (res, rest) = expectedResult moves inp
consumed = length inp - length rest
in
case res of
Left (ParseErrorPos relOff err) ->
[Left (ParseErrorPos (off + relOff) err)]
Right val -> Right val : go (off + consumed) moves rest
createPaths :: [a] -> [[a]]
createPaths xs =
Prelude.map (`Prelude.take` xs) [1..length xs]
parserSanityTests :: String -> ([Move] -> SpecWith ()) -> SpecWith ()
parserSanityTests desc testRunner =
describe desc $ do
Prelude.mapM_ testRunner $
createPaths
[ Consume (tapeLen + 1)
]
Prelude.mapM_ testRunner $
createPaths
[ Custom (P.SError "Message0")
]
Prelude.mapM_ testRunner $
createPaths
[ Consume 10
, Custom (P.SPartial 1 ())
, Consume 10
, Custom (P.SPartial 0 ())
, Consume 10
, Custom (P.SPartial (-10) ())
, Consume 10
, Custom (P.SContinue 1 ())
, Consume 10
, Custom (P.SContinue 0 ())
, Consume 10
, Custom (P.SContinue (-10) ())
, Custom (P.SError "Message1")
]
Prelude.mapM_ testRunner $
createPaths
[ Consume 10
, Custom (P.SContinue 1 ())
, Consume 10
, Custom (P.SContinue 0 ())
, Consume 10
, Custom (P.SContinue (-10) ())
, Consume 10
, Custom (P.SDone 1 ())
]
Prelude.mapM_ testRunner $
createPaths
[ Consume 20
, Custom (P.SContinue 1 ())
, Custom (P.SContinue (-10) ())
, Custom (P.SDone 0 ())
]
Prelude.mapM_ testRunner $
createPaths
[ Consume 20
, Custom (P.SContinue 1 ())
, Custom (P.SContinue (-10) ())
, Custom (P.SError "Message2")
]
Prelude.mapM_ testRunner $
createPaths
[ Consume 20
, Custom (P.SContinue 1 ())
, Custom (P.SContinue (-10) ())
, Custom (P.SDone (-4) ())
]
Prelude.mapM_ testRunner $
createPaths
[ Consume (tapeLen - 1)
, Custom (P.SContinue 1 ())
, Custom (P.SContinue (-9) ())
, Custom (P.SDone (-4) ())
]
Prelude.mapM_ testRunner $
createPaths
[ Consume (tapeLen - 1)
, Custom (P.SContinue 1 ())
, Custom (P.SContinue (-9) ())
, Custom (P.SError "Message3")
]