packages feed

megaparsec 5.0.1 → 5.1.0

raw patch · 39 files changed

+4172/−3231 lines, 39 filesdep +hspecdep +hspec-expectationsdep −HUnitdep −test-frameworkdep −test-framework-hunitdep ~QuickCheckdep ~megaparsecPVP ok

version bump matches the API change (PVP)

Dependencies added: hspec, hspec-expectations

Dependencies removed: HUnit, test-framework, test-framework-hunit, test-framework-quickcheck2

Dependency ranges changed: QuickCheck, megaparsec

API changes (from Hackage documentation)

- Text.Megaparsec.Error: instance (GHC.Show.Show t, Data.Typeable.Internal.Typeable t, GHC.Show.Show e, Data.Typeable.Internal.Typeable e) => GHC.Exception.Exception (Text.Megaparsec.Error.ParseError t e)
+ Text.Megaparsec: dbg :: forall e s m a. (Stream s, ShowToken (Token s), ShowErrorComponent e, Show a) => String -> ParsecT e s m a -> ParsecT e s m a
+ Text.Megaparsec.Error: instance (GHC.Show.Show t, Data.Typeable.Internal.Typeable t, GHC.Classes.Ord t, Text.Megaparsec.Error.ShowToken t, GHC.Show.Show e, Data.Typeable.Internal.Typeable e, Text.Megaparsec.Error.ShowErrorComponent e) => GHC.Exception.Exception (Text.Megaparsec.Error.ParseError t e)
+ Text.Megaparsec.Error: instance (Test.QuickCheck.Arbitrary.Arbitrary t, GHC.Classes.Ord t, Test.QuickCheck.Arbitrary.Arbitrary e, GHC.Classes.Ord e) => Test.QuickCheck.Arbitrary.Arbitrary (Text.Megaparsec.Error.ParseError t e)
+ Text.Megaparsec.Error: instance Test.QuickCheck.Arbitrary.Arbitrary Text.Megaparsec.Error.Dec
+ Text.Megaparsec.Error: instance Test.QuickCheck.Arbitrary.Arbitrary t => Test.QuickCheck.Arbitrary.Arbitrary (Text.Megaparsec.Error.ErrorItem t)
+ Text.Megaparsec.Error: parseErrorTextPretty :: (Ord t, ShowToken t, ShowErrorComponent e) => ParseError t e -> String
+ Text.Megaparsec.Pos: instance Test.QuickCheck.Arbitrary.Arbitrary Text.Megaparsec.Pos.Pos
+ Text.Megaparsec.Pos: instance Test.QuickCheck.Arbitrary.Arbitrary Text.Megaparsec.Pos.SourcePos
+ Text.Megaparsec.Prim: dbg :: forall e s m a. (Stream s, ShowToken (Token s), ShowErrorComponent e, Show a) => String -> ParsecT e s m a -> ParsecT e s m a
+ Text.Megaparsec.Prim: instance Test.QuickCheck.Arbitrary.Arbitrary a => Test.QuickCheck.Arbitrary.Arbitrary (Text.Megaparsec.Prim.State a)
+ Text.Megaparsec.Prim: observing :: MonadParsec e s m => m a -> m (Either (ParseError (Token s) e) a)

Files

CHANGELOG.md view
@@ -1,3 +1,40 @@+## Megaparsec 5.1.0++* Defined `displayException` for `ParseError`, so exceptions are displayed+  in human-friendly form now. This works with GHC 7.10 and later.++* Line comments parsed by `skipLineComment` now may end at the end of input+  and do not necessarily require a newline to be parsed correctly. See #119.++* Exposed `parseErrorTextPretty` function in `Text.Megaparsec.Error` to+  allow to render `ParseError`s without stack of source positions.++* Eliminated the `old-tests` test suite — Parsec legacy. The cases that are+  not already *obviously* covered in the main test suite were included into+  it.++* Added `Arbitrary` instances for the following data types: `Pos`,+  `SourcePos`, `ErrorItem`, `Dec`, `ParseError` and `State`. This should+  make testing easier without the need to add orphan instances every time.+  The drawback is that we start to depend on `QuickCheck`, but that's a fair+  price.++* The test suite now uses the combination of Hspec and the+  `hpesc-megaparsec` package, which also improved the latter (that package+  is the recommended way to test Megaparsec parsers).++* The `try` combinator now truly backtracks parser state when its argument+  parser fails (either consuming input or not). Most users will never notice+  the difference though. See #142.++* Added the `dbg` function that should be helpful for debugging.++* Added `observing` primitive combinator that allows to “observe” parse+  errors without ending parsing (they are returned in `Left`, while normal+  results are wrapped in `Right`).++* Further documentation improvements.+ ## Megaparsec 5.0.1  * Derived `NFData` instances for `Pos`, `InvalidPosException`, `SourcePos`,
README.md view
@@ -83,6 +83,9 @@   continue parsing. Once parsing is finished, several parse errors may be   reported or ignored altogether. +* `observing` allows to “observe” parse errors without ending parsing (they+  are returned in `Left`, while normal results are wrapped in `Right`).+ * `eof` only succeeds at the end of input.  * `token` is used to parse single token.@@ -214,6 +217,9 @@ faster. If you happen to have some other benchmarks, I would appreciate if you add Megaparsec to them and let me know how it performs. +If you think your Megaparsec parser is not efficient enough, take a look+at [these instructions](https://mrkkrp.github.io/megaparsec/tutorials/writing-a-fast-parser.html).+ ## Comparison with other solutions  There are quite a few libraries that can be used for parsing in Haskell,@@ -274,6 +280,8 @@ * Megaparsec is faster.  If you want to see a detailed change log, `CHANGELOG.md` may be helpful.+Also see [this original announcement](https://notehub.org/w7037) for another+comparison.  To be honest Parsec's development has seemingly stagnated. It has no test suite (only three per-bug tests), and all its releases beginning from@@ -313,6 +321,14 @@ * [`hspec-megaparsec`](https://hackage.haskell.org/package/hspec-megaparsec)   — utilities for testing Megaparsec parsers with with   [Hspec](https://hackage.haskell.org/package/hspec).++* [`cassava-megaparsec`](https://hackage.haskell.org/package/cassava-megaparsec) —+  Megaparsec parser of CSV files that plays nicely+  with [Cassava](https://hackage.haskell.org/package/cassava).++* [`tagsoup-megaparsec`](https://hackage.haskell.org/package/tagsoup-megaparsec)+  — a library for easily using [TagSoup](https://hackage.haskell.org/package/tagsoup)+  as a token type in Megaparsec.  ## Authors 
Text/Megaparsec.hs view
@@ -140,6 +140,8 @@   , ShowToken (..)   , ShowErrorComponent (..)   , parseErrorPretty+    -- * Debugging+  , dbg     -- * Low-level operations   , Stream (..)   , State (..)@@ -177,15 +179,20 @@  -- $many ----- @many p@ applies the parser @p@ /zero/ or more times. Returns a list of--- the returned values of @p@.+-- @many p@ applies the parser @p@ /zero/ or more times and returns a list+-- of the returned values of @p@. Note that if the @p@ parser fails+-- consuming input, then the entire @many p@ parser fails with the error+-- message @p@ produced instead of just stopping iterating. In these cases+-- wrapping @p@ with 'try' may be desirable. ----- > identifier = (:) <$> letter <*> many (alphaNum <|> char '_')+-- > identifier = (:) <$> letter <*> many (alphaNumChar <|> char '_')  -- $some ----- @some p@ applies the parser @p@ /one/ or more times. Returns a list of--- the returned values of @p@.+-- @some p@ applies the parser @p@ /one/ or more times and returns a list of+-- the returned values of @p@. The note about behavior of the combinator in+-- case when @p@ fails consuming input (see 'A.many') applies to 'some' as+-- well. -- -- > word = some letter 
Text/Megaparsec/Char.hs view
@@ -402,7 +402,7 @@ -- | Case-insensitive equality test for characters.  casei :: Char -> Char -> Bool-casei x y = toLower x == toLower y+casei x y = toUpper x == toUpper y {-# INLINE casei #-}  -- | Case-insensitive 'elem'.
Text/Megaparsec/Error.hs view
@@ -26,7 +26,8 @@   , ShowToken (..)   , ShowErrorComponent (..)   , parseErrorPretty-  , sourcePosStackPretty )+  , sourcePosStackPretty+  , parseErrorTextPretty ) where  import Control.DeepSeq@@ -40,13 +41,14 @@ import Data.Typeable (Typeable) import GHC.Generics import Prelude hiding (concat)+import Test.QuickCheck hiding (label) import qualified Data.List.NonEmpty as NE import qualified Data.Set           as E  import Text.Megaparsec.Pos  #if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))+import Control.Applicative #endif  -- | Data type that is used to represent “unexpected\/expected” items in@@ -62,6 +64,18 @@  instance NFData t => NFData (ErrorItem t) +instance Arbitrary t => Arbitrary (ErrorItem t) where+  arbitrary = oneof+    [+#if !MIN_VERSION_QuickCheck(2,9,0)+      Tokens <$> (NE.fromList . getNonEmpty <$> arbitrary)+    , Label  <$> (NE.fromList . getNonEmpty <$> arbitrary)+#else+      Tokens <$> arbitrary+    , Label  <$> arbitrary+#endif+    , return EndOfInput ]+ -- | The type class defines how to represent information about various -- exceptional situations. Data types that are used as custom data component -- in 'ParseError' must be instances of this type class.@@ -103,6 +117,13 @@   rnf (DecFail str) = rnf str   rnf (DecIndentation ord ref act) = ord `seq` rnf ref `seq` rnf act +instance Arbitrary Dec where+  arbitrary = oneof+    [ sized (\n -> do+        k <- choose (0, n `div` 2)+        DecFail <$> vectorOf k arbitrary)+    , DecIndentation <$> arbitrary <*> arbitrary <*> arbitrary ]+ instance ErrorComponent Dec where   representFail        = DecFail   representIndentation = DecIndentation@@ -139,8 +160,31 @@   mappend = (<>)   {-# INLINE mappend #-} -instance (Show t, Typeable t, Show e, Typeable e) => Exception (ParseError t e)+instance ( Show t+         , Typeable t+         , Ord t+         , ShowToken t+         , Show e+         , Typeable e+         , ShowErrorComponent e )+  => Exception (ParseError t e) where+#if MIN_VERSION_base(4,8,0)+  displayException = parseErrorPretty+#endif +instance (Arbitrary t, Ord t, Arbitrary e, Ord e)+    => Arbitrary (ParseError t e) where+  arbitrary = ParseError+    <$>+#if !MIN_VERSION_QuickCheck(2,9,0)+      (NE.fromList . getNonEmpty <$> arbitrary)+#else+      arbitrary+#endif+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+ -- | Merge two error data structures into one joining their collections of -- message items and preferring longest match. In other words, earlier error -- message is discarded. This may seem counter-intuitive, but 'mergeError'@@ -186,15 +230,41 @@ -- character @ch@, suitable for using in error messages.  charPretty :: Char -> String-charPretty '\0' = "null"-charPretty '\a' = "bell"-charPretty '\b' = "backspace"-charPretty '\t' = "tab"-charPretty '\n' = "newline"-charPretty '\v' = "vertical tab"-charPretty '\f' = "form feed"-charPretty '\r' = "carriage return"-charPretty ' '  = "space"+charPretty '\NUL' = "null (control character)"+charPretty '\SOH' = "start of heading (control character)"+charPretty '\STX' = "start of text (control character)"+charPretty '\ETX' = "end of text (control character)"+charPretty '\EOT' = "end of transmission (control character)"+charPretty '\ENQ' = "enquiry (control character)"+charPretty '\ACK' = "acknowledge (control character)"+charPretty '\BEL' = "bell (control character)"+charPretty '\BS'  = "backspace"+charPretty '\t'   = "tab"+charPretty '\n'   = "newline"+charPretty '\v'   = "vertical tab"+charPretty '\f'   = "form feed (control character)"+charPretty '\r'   = "carriage return"+charPretty '\SO'  = "shift out (control character)"+charPretty '\SI'  = "shift in (control character)"+charPretty '\DLE' = "data link escape (control character)"+charPretty '\DC1' = "device control one (control character)"+charPretty '\DC2' = "device control two (control character)"+charPretty '\DC3' = "device control three (control character)"+charPretty '\DC4' = "device control four (control character)"+charPretty '\NAK' = "negative acknowledge (control character)"+charPretty '\SYN' = "synchronous idle (control character)"+charPretty '\ETB' = "end of transmission block (control character)"+charPretty '\CAN' = "cancel (control character)"+charPretty '\EM'  = "end of medium (control character)"+charPretty '\SUB' = "substitute (control character)"+charPretty '\ESC' = "escape (control character)"+charPretty '\FS'  = "file separator (control character)"+charPretty '\GS'  = "group separator (control character)"+charPretty '\RS'  = "record separator (control character)"+charPretty '\US'  = "unit separator (control character)"+charPretty '\DEL' = "delete (control character)"+charPretty ' '    = "space"+charPretty '\160' = "non-breaking space" charPretty x    = "'" ++ [x] ++ "'"  -- | The type class defines how to print custom data component of@@ -223,9 +293,14 @@                 EQ -> "equal to "                 GT -> "greater than " --- | Pretty-print 'ParseError'. Note that rendered 'String' always ends with--- a newline.+-- | Pretty-print 'ParseError'. The rendered 'String' always ends with a+-- newline. --+-- The function is defined as:+--+-- > parseErrorPretty e =+-- >   sourcePosStackPretty (errorPos e) ++ ":\n" ++ parseErrorTextPretty e+-- -- @since 5.0.0  parseErrorPretty :: ( Ord t@@ -233,14 +308,8 @@                     , ShowErrorComponent e )   => ParseError t e    -- ^ Parse error to render   -> String            -- ^ Result of rendering-parseErrorPretty (ParseError pos us ps xs) =-  sourcePosStackPretty pos ++ ":\n" ++-  if E.null us && E.null ps && E.null xs-    then "unknown parse error\n"-    else concat-      [ messageItemsPretty "unexpected " us-      , messageItemsPretty "expecting "  ps-      , unlines (showErrorComponent <$> E.toAscList xs) ]+parseErrorPretty e =+  sourcePosStackPretty (errorPos e) ++ ":\n" ++ parseErrorTextPretty e  -- | Pretty-print stack of source positions. --@@ -271,3 +340,22 @@ orList (x:|[])  = x orList (x:|[y]) = x ++ " or " ++ y orList xs       = intercalate ", " (NE.init xs) ++ ", or " ++ NE.last xs++-- | Pretty-print textual part of a 'ParseError', that is, everything except+-- stack of source positions. The rendered staring always ends with a new+-- line.+--+-- @since 5.1.0++parseErrorTextPretty :: ( Ord t+                        , ShowToken t+                        , ShowErrorComponent e )+  => ParseError t e    -- ^ Parse error to render+  -> String            -- ^ Result of rendering+parseErrorTextPretty (ParseError _ us ps xs) =+  if E.null us && E.null ps && E.null xs+    then "unknown parse error\n"+    else concat+      [ messageItemsPretty "unexpected " us+      , messageItemsPretty "expecting "  ps+      , unlines (showErrorComponent <$> E.toAscList xs) ]
Text/Megaparsec/Expr.hs view
@@ -54,6 +54,12 @@ -- This is not done by default because in some cases you don't want to allow -- repeating prefix or postfix operators. --+-- If you want to have an operator that is a prefix of another operator in+-- the table, use the following (or similar) wrapper instead of plain+-- 'symbol':+--+-- > op n = (lexeme . try) (string n <* notFollowedBy punctuationChar)+-- -- @makeExprParser@ takes care of all the complexity involved in building an -- expression parser. Here is an example of an expression parser that -- handles prefix signs, postfix increment and basic arithmetic:@@ -80,8 +86,8 @@   -> m a               -- ^ Resulting expression parser makeExprParser = foldl addPrecLevel --- | @addPrecLevel p ops@ adds ability to parse operators in table @ops@ to--- parser @p@.+-- | @addPrecLevel p ops@ adds the ability to parse operators in table @ops@+-- to parser @p@.  addPrecLevel :: MonadParsec e s m => m a -> [Operator m a] -> m a addPrecLevel term ops =@@ -92,7 +98,7 @@         las'  = pInfixL (choice las) term'         nas'  = pInfixN (choice nas) term' --- | @pTerm prefix term postfix@ parses term with @term@ surrounded by+-- | @pTerm prefix term postfix@ parses a term with @term@ surrounded by -- optional prefix and postfix unary operators. Parsers @prefix@ and -- @postfix@ are allowed to fail, in this case 'id' is used. 
Text/Megaparsec/Lexer.hs view
@@ -105,9 +105,9 @@   -> m () space ch line block = hidden . skipMany $ choice [ch, line, block] --- | This is wrapper for lexemes. Typical usage is to supply first argument--- (parser that consumes white space, probably defined via 'space') and use--- the resulting function to wrap parsers for every lexeme.+-- | This is a wrapper for lexemes. Typical usage is to supply the first+-- argument (parser that consumes white space, probably defined via 'space')+-- and use the resulting function to wrap parsers for every lexeme. -- -- > lexeme  = L.lexeme spaceConsumer -- > integer = lexeme L.integer@@ -148,7 +148,7 @@   -> m String symbol' spc = lexeme spc . C.string' --- | Given comment prefix this function returns parser that skips line+-- | Given comment prefix this function returns a parser that skips line -- comments. Note that it stops just before newline character but doesn't -- consume the newline. Newline is either supposed to be consumed by 'space' -- parser or picked up manually.@@ -158,7 +158,7 @@   -> m () skipLineComment prefix = p >> void (manyTill C.anyChar n)   where p = C.string prefix-        n = lookAhead C.newline+        n = lookAhead (void C.newline) <|> eof  -- | @skipBlockComment start end@ skips non-nested block comment starting -- with @start@ and ending with @end@.@@ -218,11 +218,11 @@  -- | @indentGuard spaceConsumer ord ref@ first consumes all white space -- (indentation) with @spaceConsumer@ parser, then it checks column--- position. Ordering between current indentation level and reference+-- position. Ordering between current indentation level and the reference -- indentation level @ref@ should be @ord@, otherwise the parser fails. On--- success current column position is returned.+-- success the current column position is returned. ----- When you want to parse block of indentation first run this parser with+-- When you want to parse a block of indentation, first run this parser with -- arguments like @indentGuard spaceConsumer GT (unsafePos 1)@ — this will -- make sure you have some indentation. Use returned value to check -- indentation on every subsequent line according to syntax of your@@ -240,7 +240,7 @@     then return actual     else incorrectIndent ord ref actual --- | Parse non-indented construction. This ensures that there is no+-- | Parse a non-indented construction. This ensures that there is no -- indentation before actual data. Useful, for example, as a wrapper for -- top-level function definitions. --@@ -375,7 +375,7 @@ -- Numbers  -- | Parse an integer without sign in decimal representation (according to--- format of integer literals described in Haskell report).+-- the format of integer literals described in the Haskell report). -- -- If you need to parse signed integers, see 'signed' combinator. @@ -389,10 +389,10 @@ decimal = nump "" C.digitChar <?> "decimal integer"  -- | Parse an integer in hexadecimal representation. Representation of--- hexadecimal number is expected to be according to Haskell report except--- for the fact that this parser doesn't parse “0x” or “0X” prefix. It is--- responsibility of the programmer to parse correct prefix before parsing--- the number itself.+-- hexadecimal number is expected to be according to the Haskell report+-- except for the fact that this parser doesn't parse “0x” or “0X” prefix.+-- It is responsibility of the programmer to parse correct prefix before+-- parsing the number itself. -- -- For example you can make it conform to Haskell report like this: --@@ -402,10 +402,10 @@ hexadecimal = nump "0x" C.hexDigitChar <?> "hexadecimal integer"  -- | Parse an integer in octal representation. Representation of octal--- number is expected to be according to Haskell report except for the fact--- that this parser doesn't parse “0o” or “0O” prefix. It is responsibility--- of the programmer to parse correct prefix before parsing the number--- itself.+-- number is expected to be according to the Haskell report except for the+-- fact that this parser doesn't parse “0o” or “0O” prefix. It is+-- responsibility of the programmer to parse correct prefix before parsing+-- the number itself.  octal :: (MonadParsec e s m, Token s ~ Char) => m Integer octal = nump "0o" C.octDigitChar <?> "octal integer"@@ -417,11 +417,11 @@ nump :: MonadParsec e s m => String -> m Char -> m Integer nump prefix baseDigit = read . (prefix ++) <$> some baseDigit --- | Parse floating point value as 'Scientific' number. 'Scientific' is+-- | Parse a floating point value as 'Scientific' number. 'Scientific' is -- great for parsing of arbitrary precision numbers coming from an untrusted -- source. See documentation in "Data.Scientific" for more information.--- Representation of floating point value is expected to be according to--- Haskell report.+-- Representation of the floating point value is expected to be according to+-- the Haskell report. -- -- This function does not parse sign, if you need to parse signed numbers, -- see 'signed'.@@ -432,7 +432,7 @@ scientific = label "floating point number" (read <$> f)   where f = (++) <$> some C.digitChar <*> (fraction <|> fExp) --- | Parse floating point number without sign. This is a simple shortcut+-- | Parse a floating point number without sign. This is a simple shortcut -- defined as: -- -- > float = toRealFloat <$> scientific@@ -468,10 +468,10 @@ number = label "number" (read <$> f)   where f = (++) <$> some C.digitChar <*> option "" (fraction <|> fExp) --- | @signed space p@ parser parses optional sign, then if there is a sign--- it will consume optional white space (using @space@ parser), then it runs--- parser @p@ which should return a number. Sign of the number is changed--- according to previously parsed sign.+-- | @signed space p@ parser parses an optional sign, then if there is a+-- sign it will consume optional white space (using @space@ parser), then it+-- runs parser @p@ which should return a number. Sign of the number is+-- changed according to previously parsed sign. -- -- For example, to parse signed integer you can write: --
Text/Megaparsec/Pos.hs view
@@ -39,10 +39,11 @@ import Data.Semigroup import Data.Typeable (Typeable) import GHC.Generics+import Test.QuickCheck import Unsafe.Coerce  #if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))+import Control.Applicative import Data.Word (Word) #endif @@ -58,6 +59,9 @@ newtype Pos = Pos Word   deriving (Show, Eq, Ord, Data, Typeable, NFData) +instance Arbitrary Pos where+  arbitrary = unsafePos <$> (getSmall <$> arbitrary `suchThat` (> 0))+ -- | Construction of 'Pos' from an instance of 'Integral'. The function -- throws 'InvalidPosException' when given non-positive argument. Note that -- the function is polymorphic with respect to 'MonadThrow' @m@, so you can@@ -102,6 +106,14 @@       ("Pos", r2) <- lex r1       (x,     r3) <- readsPrec 11 r2       (,r3) <$> mkPos (x :: Integer)++instance Arbitrary SourcePos where+  arbitrary = SourcePos+    <$> sized (\n -> do+          k <- choose (0, n `div` 2)+          vectorOf k arbitrary)+    <*> (unsafePos <$> choose (1, 1000))+    <*> (unsafePos <$> choose (1,  100))  -- | The exception is thrown by 'mkPos' when its argument is not a positive -- number.
Text/Megaparsec/Prim.hs view
@@ -54,7 +54,9 @@   , runParserT'   , parse   , parseMaybe-  , parseTest )+  , parseTest+    -- * Debugging+  , dbg ) where  import Control.DeepSeq@@ -74,8 +76,10 @@ import Data.Semigroup import Data.Set (Set) import Data.Typeable (Typeable)+import Debug.Trace import GHC.Generics import Prelude hiding (all)+import Test.QuickCheck hiding (Result (..), label) import qualified Control.Applicative               as A import qualified Control.Monad.Fail                as Fail import qualified Control.Monad.Trans.Reader        as L@@ -94,7 +98,7 @@ import Text.Megaparsec.Pos  #if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*), pure)+import Control.Applicative #endif  ----------------------------------------------------------------------------@@ -110,6 +114,17 @@  instance NFData s => NFData (State s) +instance Arbitrary a => Arbitrary (State a) where+  arbitrary = State+    <$> arbitrary+    <*>+#if !MIN_VERSION_QuickCheck(2,9,0)+      (NE.fromList . getNonEmpty <$> arbitrary)+#else+      arbitrary+#endif+    <*> (unsafePos <$> choose (1, 20))+ -- | All information available after parsing. This includes consumption of -- input, success (with returned value) or failure (with parse error), and -- parser state at the end of parsing.@@ -524,8 +539,8 @@   hidden :: m a -> m a   hidden = label "" -  -- | The parser @try p@ behaves like parser @p@, except that it-  -- pretends that it hasn't consumed any input when an error occurs.+  -- | The parser @try p@ behaves like parser @p@, except that it backtracks+  -- parser state when @p@ fails (either consuming input or not).   --   -- This combinator is used whenever arbitrary look ahead is needed. Since   -- it pretends that it hasn't consumed any input when @p@ fails, the@@ -563,16 +578,17 @@    try :: m a -> m a -  -- | @lookAhead p@ parses @p@ without consuming any input.-  ---  -- If @p@ fails and consumes some input, so does @lookAhead@. Combine with-  -- 'try' if this is undesirable.+  -- | If @p@ in @lookAhead p@ succeeds (either consuming input or not) the+  -- whole parser behaves like @p@ succeeded without consuming anything+  -- (parser state is not updated as well). If @p@ fails, @lookAhead@ has no+  -- effect, i.e. it will fail consuming input if @p@ fails consuming input.+  -- Combine with 'try' if this is undesirable.    lookAhead :: m a -> m a    -- | @notFollowedBy p@ only succeeds when parser @p@ fails. This parser-  -- does not consume any input and can be used to implement the “longest-  -- match” rule.+  -- /never consumes/ any input and /never modifies/ parser state. It can be+  -- used to implement the “longest match” rule.    notFollowedBy :: m a -> m () @@ -593,6 +609,19 @@     -> m a             -- ^ Original parser     -> m a             -- ^ Parser that can recover from failures +  -- | @observing p@ allows to “observe” failure of @p@ parser, should it+  -- happen, without actually ending parsing, but instead getting the+  -- 'ParseError' in 'Left'. On success parsed value is returned in 'Right'+  -- as usual. Note that this primitive just allows you to observe parse+  -- errors as they happen, it does not backtrack or change how the @p@+  -- parser works in any way.+  --+  -- @since 5.1.0++  observing+    :: m a+    -> m (Either (ParseError (Token s) e) a)+   -- | This parser only succeeds at the end of the input.    eof :: m ()@@ -668,6 +697,7 @@   lookAhead         = pLookAhead   notFollowedBy     = pNotFollowedBy   withRecovery      = pWithRecovery+  observing         = pObserving   eof               = pEof   token             = pToken   tokens            = pTokens@@ -696,7 +726,8 @@  pTry :: ParsecT e s m a -> ParsecT e s m a pTry p = ParsecT $ \s cok _ eok eerr ->-  unParser p s cok eerr eok eerr+  let eerr' err _ = eerr err s+  in unParser p s cok eerr' eok eerr' {-# INLINE pTry #-}  pLookAhead :: ParsecT e s m a -> ParsecT e s m a@@ -736,6 +767,15 @@   in unParser p s cok mcerr eok meerr {-# INLINE pWithRecovery #-} +pObserving+  :: ParsecT e s m a+  -> ParsecT e s m (Either (ParseError (Token s) e) a)+pObserving p = ParsecT $ \s cok _ eok _ ->+  let cerr' err s' = cok (Left err) s' mempty+      eerr' err s' = eok (Left err) s' (toHints err)+  in unParser p s (cok . Right) cerr' (eok . Right) eerr'+{-# INLINE pObserving #-}+ pEof :: forall e s m. Stream s => ParsecT e s m () pEof = ParsecT $ \s@(State input (pos:|z) w) _ _ eok eerr ->   case uncons input of@@ -1058,6 +1098,8 @@     notFollowedBy (fst <$> m s) >> return ((),s)   withRecovery r (L.StateT m) = L.StateT $ \s ->     withRecovery (\e -> L.runStateT (r e) s) (m s)+  observing     (L.StateT m) = L.StateT $ \s ->+    fixs s <$> observing (m s)   eof                        = lift eof   token test mt              = lift (token test mt)   tokens e ts                = lift (tokens e ts)@@ -1074,6 +1116,8 @@     notFollowedBy (fst <$> m s) >> return ((),s)   withRecovery r (S.StateT m) = S.StateT $ \s ->     withRecovery (\e -> S.runStateT (r e) s) (m s)+  observing     (S.StateT m) = S.StateT $ \s ->+    fixs s <$> observing (m s)   eof                        = lift eof   token test mt              = lift (token test mt)   tokens e ts                = lift (tokens e ts)@@ -1088,6 +1132,7 @@   notFollowedBy (L.ReaderT m) = L.ReaderT $ notFollowedBy . m   withRecovery r (L.ReaderT m) = L.ReaderT $ \s ->     withRecovery (\e -> L.runReaderT (r e) s) (m s)+  observing     (L.ReaderT m) = L.ReaderT $ observing . m   eof                         = lift eof   token test mt               = lift (token test mt)   tokens e ts                 = lift (tokens e ts)@@ -1104,6 +1149,8 @@     (,mempty) <$> notFollowedBy (fst <$> m)   withRecovery r (L.WriterT m) = L.WriterT $     withRecovery (L.runWriterT . r) m+  observing     (L.WriterT m) = L.WriterT $+    fixs mempty <$> observing m   eof                         = lift eof   token test mt               = lift (token test mt)   tokens e ts                 = lift (tokens e ts)@@ -1120,6 +1167,8 @@     (,mempty) <$> notFollowedBy (fst <$> m)   withRecovery r (S.WriterT m) = S.WriterT $     withRecovery (S.runWriterT . r) m+  observing     (S.WriterT m) = S.WriterT $+    fixs mempty <$> observing m   eof                         = lift eof   token test mt               = lift (token test mt)   tokens e ts                 = lift (tokens e ts)@@ -1134,8 +1183,140 @@   notFollowedBy (IdentityT m) = IdentityT $ notFollowedBy m   withRecovery r (IdentityT m) = IdentityT $     withRecovery (runIdentityT . r) m+  observing     (IdentityT m) = IdentityT $ observing m   eof                         = lift eof   token test mt               = lift (token test mt)   tokens e ts                 = lift $ tokens e ts   getParserState              = lift getParserState   updateParserState f         = lift $ updateParserState f++fixs :: s -> Either a (b, s) -> (Either a b, s)+fixs s (Left a)       = (Left a, s)+fixs _ (Right (b, s)) = (Right b, s)+{-# INLINE fixs #-}++----------------------------------------------------------------------------+-- Debugging++-- | @dbg label p@ parser works exactly like @p@, but when it's evaluated it+-- prints information useful for debugging. The @label@ is only used to+-- refer to this parser in the debugging output. This combinator uses the+-- 'trace' function from "Debug.Trace" under the hood.+--+-- Typical usage is to wrap every sub-parser in misbehaving parser with+-- 'dbg' assigning meaningful labels. Then give it a shot and go through the+-- print-out. As of current version, this combinator prints all available+-- information except for /hints/, which are probably only interesting to+-- the maintainer of Megaparsec itself and may be quite verbose to output in+-- general. Let me know if you would like to be able to see hints as part of+-- debugging output.+--+-- The output itself is pretty self-explanatory, although the following+-- abbreviations should be clarified (they are derived from low-level source+-- code):+--+--     * @COK@ — “consumed OK”. The parser consumed input and succeeded.+--     * @CERR@ — “consumed error”. The parser consumed input and failed.+--     * @EOK@ — “empty OK”. The parser succeeded without consuming input.+--     * @EERR@ — “empty error”. The parser failed without consuming input.+--+-- Due to how input streams are represented (see 'Stream'), we need to+-- traverse entire input twice (calculating length before and after @p@+-- parser) to understand what part of input was matched and consumed. This+-- makes this combinator very inefficient, be sure to remove it from your+-- code once you have finished with debugging.+--+-- Finally, it's not possible to lift this function into some monad+-- transformers without introducing surprising behavior (e.g. unexpected+-- state backtracking) or adding otherwise redundant constraints (e.g.+-- 'Show' instance for state), so this helper is only available for+-- 'ParsecT' monad, not 'MonadParsec' in general.+--+-- @since 5.1.0++dbg :: forall e s m a.+  ( Stream s+  , ShowToken (Token s)+  , ShowErrorComponent e+  , Show a )+  => String            -- ^ Debugging label+  -> ParsecT e s m a   -- ^ Parser to debug+  -> ParsecT e s m a   -- ^ Parser that prints debugging messages+dbg lbl p = ParsecT $ \s cok cerr eok eerr ->+  let l = dbgLog lbl :: DbgItem s e a -> String+      cok' x s' hs = flip trace (cok x s' hs) $+        l (DbgIn (unfold (stateInput s))) +++        l (DbgCOK (streamTake (streamDelta s s') (stateInput s)) x)+      cerr' err s' = flip trace (cerr err s') $+        l (DbgIn (unfold (stateInput s))) +++        l (DbgCERR (streamTake (streamDelta s s') (stateInput s)) err)+      eok' x s' hs = flip trace (eok x s' hs) $+        l (DbgIn (unfold (stateInput s))) +++        l (DbgEOK (streamTake (streamDelta s s') (stateInput s)) x)+      eerr' err s' = flip trace (eerr err s') $+        l (DbgIn (unfold (stateInput s))) +++        l (DbgEERR (streamTake (streamDelta s s') (stateInput s)) err)+  in unParser p s cok' cerr' eok' eerr'++-- | Single piece of info to be rendered with 'dbgLog'.++data DbgItem s e a+  = DbgIn   [Token s]+  | DbgCOK  [Token s] a+  | DbgCERR [Token s] (ParseError (Token s) e)+  | DbgEOK  [Token s] a+  | DbgEERR [Token s] (ParseError (Token s) e)++-- | Render a single piece of debugging info.++dbgLog :: (ShowToken (Token s), ShowErrorComponent e, Show a, Ord (Token s))+  => String            -- ^ Debugging label+  -> DbgItem s e a     -- ^ Information to render+  -> String            -- ^ Rendered result+dbgLog lbl item = prefix msg+  where+    prefix = unlines . fmap ((lbl ++ "> ") ++) . lines+    msg = case item of+      DbgIn   ts   ->+        "IN: " ++ showStream ts+      DbgCOK  ts a ->+        "MATCH (COK): " ++ showStream ts ++ "\nVALUE: " ++ show a+      DbgCERR ts e ->+        "MATCH (CERR): " ++ showStream ts ++ "\nERROR:\n" ++ parseErrorPretty e+      DbgEOK  ts a ->+        "MATCH (EOK): " ++ showStream ts ++ "\nVALUE: " ++ show a+      DbgEERR ts e ->+        "MATCH (EERR): " ++ showStream ts ++ "\nERROR:\n" ++ parseErrorPretty e++-- | Pretty-print a list of tokens.++showStream :: ShowToken t => [t] -> String+showStream ts =+  case NE.nonEmpty ts of+    Nothing -> "<EMPTY>"+    Just ne ->+      let (h, r) = splitAt 40 (showTokens ne)+      in if null r then h else h ++ " <…>"++-- | Calculate difference in length of two input streams from given parser+-- 'State's.++streamDelta :: Stream s+  => State s           -- ^ State of parser before consumption+  -> State s           -- ^ State of parser after consumption+  -> Int               -- ^ Number of consumed tokens+streamDelta s0 s1 = streamLength (stateInput s0) - streamLength (stateInput s1)+  where streamLength s = length (unfold s)++-- | Extract given number of tokens from the stream.++streamTake :: Stream s => Int -> s -> [Token s]+streamTake n s = take n (unfold s)++-- | Custom version of 'unfold' that matches signature of 'uncons' method in+-- 'Stream' type class we use.++unfold :: Stream s => s -> [Token s]+unfold s = case uncons s of+  Nothing -> []+  Just (t, s') -> t : unfold s'
megaparsec.cabal view
@@ -27,7 +27,7 @@ -- POSSIBILITY OF SUCH DAMAGE.  name:                 megaparsec-version:              5.0.1+version:              5.1.0 cabal-version:        >= 1.10 license:              BSD2 license-file:         LICENSE.md@@ -46,7 +46,7 @@   This is industrial-strength monadic parser combinator library. Megaparsec   is a fork of Parsec library originally written by Daan Leijen. -extra-source-files:   AUTHORS.md+extra-doc-files:      AUTHORS.md                     , CHANGELOG.md                     , README.md @@ -56,7 +56,8 @@   default:            False  library-  build-depends:      base         >= 4.6   && < 5.0+  build-depends:      QuickCheck   >= 2.7   && < 3.0+                    , base         >= 4.6   && < 5.0                     , bytestring   >= 0.2   && < 0.11                     , containers   >= 0.5   && < 0.6                     , deepseq      >= 1.3   && < 1.5@@ -98,59 +99,36 @@     ghc-options:      -O2 -Wall   default-language:   Haskell2010 -test-suite old-tests-  main-is:            Main.hs-  hs-source-dirs:     old-tests-  type:               exitcode-stdio-1.0-  if flag(dev)-    ghc-options:      -Wall -Werror-  else-    ghc-options:      -O2 -Wall-  other-modules:      Bugs-                    , Bugs.Bug2-                    , Bugs.Bug6-                    , Bugs.Bug9-                    , Bugs.Bug35-                    , Bugs.Bug39-                    , Util-  build-depends:      base                 >= 4.6 && < 5.0-                    , HUnit                >= 1.2 && < 1.4-                    , megaparsec           >= 5.0.1-                    , test-framework       >= 0.6 && < 1.0-                    , test-framework-hunit >= 0.2 && < 0.4-  default-language:   Haskell2010- test-suite tests-  main-is:            Main.hs+  main-is:            Spec.hs   hs-source-dirs:     tests   type:               exitcode-stdio-1.0   if flag(dev)     ghc-options:      -Wall -Werror   else     ghc-options:      -O2 -Wall-  other-modules:      Char-                    , Combinator-                    , Error-                    , Expr-                    , Lexer-                    , Perm-                    , Pos-                    , Prim-                    , Util-  build-depends:      base           >= 4.6   && < 5.0-                    , HUnit          >= 1.2   && < 1.4-                    , QuickCheck     >= 2.8.2 && < 3.0-                    , bytestring     >= 0.2   && < 0.11-                    , containers     >= 0.5   && < 0.6-                    , exceptions     >= 0.6   && < 0.9-                    , megaparsec     >= 5.0.1-                    , mtl            >= 2.0   && < 3.0-                    , scientific     >= 0.3.1 && < 0.4-                    , test-framework >= 0.6   && < 1.0-                    , test-framework-hunit       >= 0.3 && < 0.4-                    , test-framework-quickcheck2 >= 0.3 && < 0.4-                    , text           >= 0.2   && < 1.3-                    , transformers   >= 0.4   && < 0.6+  other-modules:      Test.Hspec.Megaparsec+                    , Test.Hspec.Megaparsec.AdHoc+                    , Text.Megaparsec.CharSpec+                    , Text.Megaparsec.CombinatorSpec+                    , Text.Megaparsec.ErrorSpec+                    , Text.Megaparsec.ExprSpec+                    , Text.Megaparsec.LexerSpec+                    , Text.Megaparsec.PermSpec+                    , Text.Megaparsec.PosSpec+                    , Text.Megaparsec.PrimSpec+  build-depends:      QuickCheck   >= 2.7   && < 3.0+                    , base         >= 4.6   && < 5.0+                    , bytestring   >= 0.2   && < 0.11+                    , containers   >= 0.5   && < 0.6+                    , exceptions   >= 0.6   && < 0.9+                    , hspec        >= 2.0   && < 3.0+                    , hspec-expectations >= 0.5 && < 0.8+                    , megaparsec   >= 5.1.0+                    , mtl          >= 2.0   && < 3.0+                    , scientific   >= 0.3.1 && < 0.4+                    , text         >= 0.2   && < 1.3+                    , transformers >= 0.4   && < 0.6    if !impl(ghc >= 8.0)     -- packages providing modules that moved into base-4.9.0.0@@ -169,11 +147,11 @@     ghc-options:      -O2 -Wall -Werror   else     ghc-options:      -O2 -Wall-  build-depends:      base       >= 4.6  && < 5.0-                    , bytestring >= 0.10 && < 0.11-                    , criterion  >= 0.6.2.1 && < 1.2-                    , megaparsec >= 5.0.1-                    , text       >= 0.2  && < 1.3+  build-depends:      base         >= 4.6  && < 5.0+                    , bytestring   >= 0.10 && < 0.11+                    , criterion    >= 0.6.2.1 && < 1.2+                    , megaparsec   >= 5.1.0+                    , text         >= 0.2  && < 1.3   default-language:   Haskell2010  source-repository head
− old-tests/Bugs.hs
@@ -1,16 +0,0 @@-module Bugs (bugs) where--import Test.Framework--import qualified Bugs.Bug2-import qualified Bugs.Bug6-import qualified Bugs.Bug9-import qualified Bugs.Bug35-import qualified Bugs.Bug39--bugs :: [Test]-bugs = [ Bugs.Bug2.main-       , Bugs.Bug6.main-       , Bugs.Bug9.main-       , Bugs.Bug35.main-       , Bugs.Bug39.main ]
− old-tests/Bugs/Bug2.hs
@@ -1,32 +0,0 @@-module Bugs.Bug2 (main) where--import Control.Applicative (empty)-import Control.Monad (void)--import Text.Megaparsec-import Text.Megaparsec.String-import qualified Text.Megaparsec.Lexer as L--import Test.Framework-import Test.Framework.Providers.HUnit-import Test.HUnit hiding (Test)--sc :: Parser ()-sc = L.space (void spaceChar) empty empty--lexeme :: Parser a -> Parser a-lexeme = L.lexeme sc--stringLiteral :: Parser String-stringLiteral = lexeme $ char '"' >> manyTill L.charLiteral (char '"')--main :: Test-main =-  testCase "Control Char Parsing (#2)" $-  parseString "\"test\\^Bstring\"" @?= "test\^Bstring"- where-   parseString :: String -> String-   parseString input =-      case parse stringLiteral "Example" input of-        Left{} -> error "Parse failure"-        Right str -> str
− old-tests/Bugs/Bug35.hs
@@ -1,35 +0,0 @@-module Bugs.Bug35 (main) where--import Text.Megaparsec-import Text.Megaparsec.String-import qualified Text.Megaparsec.Lexer as L--import Test.Framework-import Test.Framework.Providers.HUnit-import Test.HUnit hiding (Test)--trickyFloats :: [String]-trickyFloats =-    [ "1.5339794352098402e-118"-    , "2.108934760892056e-59"-    , "2.250634744599241e-19"-    , "5.0e-324"-    , "5.960464477539063e-8"-    , "0.25996181067141905"-    , "0.3572019862807257"-    , "0.46817723004874223"-    , "0.9640035681058178"-    , "4.23808622486133"-    , "4.540362294799751"-    , "5.212384849884261"-    , "13.958257048123212"-    , "32.96176575630599"-    , "38.47735512322269" ]--testBatch :: Assertion-testBatch = mapM_ testFloat trickyFloats-  where testFloat x = parse (L.float :: Parser Double) "" x-          @?= Right (read x :: Double)--main :: Test-main = testCase "Output of Text.Megaparsec.Lexer.float (#35)" testBatch
− old-tests/Bugs/Bug39.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE CPP #-}--module Bugs.Bug39 (main) where--import Control.Applicative (empty)-import Control.Monad (void)-#if MIN_VERSION_base(4,7,0)-import Data.Either (isLeft, isRight)-#endif--import Text.Megaparsec-import Text.Megaparsec.String-import qualified Text.Megaparsec.Lexer as L--import Test.Framework-import Test.Framework.Providers.HUnit-import Test.HUnit hiding (Test)--#if !MIN_VERSION_base(4,7,0)-isRight, isLeft :: Either a b -> Bool-isRight (Right _) = True-isRight _         = False-isLeft  (Left _ ) = True-isLeft  _         = False-#endif--shouldFail :: [String]-shouldFail = [" 1", " +1", " -1"]--shouldSucceed :: [String]-shouldSucceed = ["1", "+1", "-1", "+ 1 ", "- 1 ", "1 "]--sc :: Parser ()-sc = L.space (void spaceChar) empty empty--lexeme :: Parser a -> Parser a-lexeme = L.lexeme sc--integer :: Parser Integer-integer = lexeme $ L.signed sc L.integer--testBatch :: Assertion-testBatch = mapM_ (f testFail)    shouldFail >>-            mapM_ (f testSucceed) shouldSucceed-    where f           t a = t (parse integer "" a) a-          testFail    x a = assertBool-                            ("Should fail on " ++ show a) (isLeft x)-          testSucceed x a = assertBool-                            ("Should succeed on " ++ show a) (isRight x)--main :: Test-main = testCase "Lexer should fail on leading whitespace (#39)" testBatch
− old-tests/Bugs/Bug6.hs
@@ -1,22 +0,0 @@-module Bugs.Bug6 (main) where--import Text.Megaparsec-import Text.Megaparsec.String--import Test.Framework-import Test.Framework.Providers.HUnit-import Test.HUnit hiding (Test)--import Util--main :: Test-main =-  testCase "Look-ahead preserving error location (#6)" $-  parseErrors variable "return" @?= ["'return' is a reserved keyword"]--variable :: Parser String-variable = do-  x <- lookAhead (some letterChar)-  if x == "return"-    then fail "'return' is a reserved keyword"-    else string x
− old-tests/Bugs/Bug9.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE CPP #-}--module Bugs.Bug9 (main) where--import Control.Applicative (empty)-import Control.Monad (void)--import Text.Megaparsec-import Text.Megaparsec.Expr-import Text.Megaparsec.String (Parser)-import qualified Text.Megaparsec.Lexer as L--import Test.Framework-import Test.Framework.Providers.HUnit-import Test.HUnit hiding (Test)--import Util--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*), (<$))-#endif--data Expr = Const Integer | Op Expr Expr deriving Show--main :: Test-main =-  testCase "Tracing of current position in error message (#9)"-  $ result @?= ["unexpected '>'", "expecting end of input or operator"]-  where-    result :: [String]-    result = parseErrors parseTopLevel "4 >> 5"---- Syntax analysis--sc :: Parser ()-sc = L.space (void spaceChar) empty empty--lexeme :: Parser a -> Parser a-lexeme = L.lexeme sc--integer :: Parser Integer-integer = lexeme L.integer--operator :: String -> Parser String-operator = L.symbol sc--parseTopLevel :: Parser Expr-parseTopLevel = parseExpr <* eof--parseExpr :: Parser Expr-parseExpr = makeExprParser (Const <$> integer) table-  where table = [[ InfixL (Op <$ operator ">>>") ]]
− old-tests/Main.hs
@@ -1,6 +0,0 @@-import Test.Framework--import Bugs (bugs)--main :: IO ()-main = defaultMain [testGroup "Bugs" bugs]
− old-tests/Util.hs
@@ -1,12 +0,0 @@-module Util where--import Text.Megaparsec-import Text.Megaparsec.String (Parser)---- | Returns the error messages associated with a failed parse.--parseErrors :: Parser a -> String -> [String]-parseErrors p input =-  case parse p "" input of-    Left err -> drop 1 $ lines $ parseErrorPretty err-    Right _  -> []
− tests/Char.hs
@@ -1,246 +0,0 @@------ QuickCheck tests for Megaparsec's character parsers.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--{-# LANGUAGE CPP              #-}-{-# OPTIONS -fno-warn-orphans #-}--module Char (tests) where--import Data.Char-import Data.List (findIndex, isPrefixOf)-import Data.List.NonEmpty (NonEmpty (..))-import qualified Data.List.NonEmpty as NE--import Test.Framework-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.QuickCheck--import Text.Megaparsec.Char-import Text.Megaparsec.Error--import Util--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))-#endif--tests :: Test-tests = testGroup "Character parsers"-  [ testProperty "newline"         prop_newline-  , testProperty "crlf"            prop_crlf-  , testProperty "eol"             prop_eol-  , testProperty "tab"             prop_tab-  , testProperty "space"           prop_space-  , testProperty "controlChar"     prop_controlChar-  , testProperty "spaceChar"       prop_spaceChar-  , testProperty "upperChar"       prop_upperChar-  , testProperty "lowerChar"       prop_lowerChar-  , testProperty "letterChar"      prop_letterChar-  , testProperty "alphaNumChar"    prop_alphaNumChar-  , testProperty "printChar"       prop_printChar-  , testProperty "digitChar"       prop_digitChar-  , testProperty "hexDigitChar"    prop_hexDigitChar-  , testProperty "octDigitChar"    prop_octDigitChar-  , testProperty "markChar"        prop_markChar-  , testProperty "numberChar"      prop_numberChar-  , testProperty "punctuationChar" prop_punctuationChar-  , testProperty "symbolChar"      prop_symbolChar-  , testProperty "separatorChar"   prop_separatorChar-  , testProperty "asciiChar"       prop_asciiChar-  , testProperty "latin1Char"      prop_latin1Char-  , testProperty "charCategory"    prop_charCategory-  , testProperty "char"            prop_char-  , testProperty "char'"           prop_char'-  , testProperty "anyChar"         prop_anyChar-  , testProperty "oneOf"           prop_oneOf-  , testProperty "oneOf'"          prop_oneOf'-  , testProperty "noneOf"          prop_noneOf-  , testProperty "noneOf'"         prop_noneOf'-  , testProperty "string"          prop_string-  , testProperty "string'"         prop_string'_0-  , testProperty "string' (case)"  prop_string'_1 ]--instance Arbitrary GeneralCategory where-  arbitrary = elements [minBound..maxBound]--prop_newline :: String -> Property-prop_newline = checkChar newline (== '\n') (tkn '\n')--prop_crlf :: String -> Property-prop_crlf = checkString crlf "\r\n" (==)--prop_eol :: String -> Property-prop_eol s = checkParser eol r s-  where h = head s-        r | s == "\n"   = Right "\n"-          | s == "\r\n" = Right "\r\n"-          | null s      = posErr 0 s [ueof, elabel "end of line"]-          | h == '\n'   = posErr 1 s [utok (s !! 1), eeof]-          | h /= '\r'   = posErr 0 s [utok h, elabel "end of line"]-          | "\r\n" `isPrefixOf` s = posErr 2 s [utok (s !! 2), eeof]-          | otherwise   = posErr 0 s [ utoks (take 2 s)-                                     , utok '\r'-                                     , elabel "end of line" ]--prop_tab :: String -> Property-prop_tab = checkChar tab (== '\t') (tkn '\t')--prop_space :: String -> Property-prop_space s = checkParser space r s-  where r = case findIndex (not . isSpace) s of-              Just x  ->-                  let ch = s !! x-                  in posErr x s-                     [ utok ch-                     , utok ch-                     , elabel "white space"-                     , eeof ]-              Nothing -> Right ()--prop_controlChar :: String -> Property-prop_controlChar = checkChar controlChar isControl (lbl "control character")--prop_spaceChar :: String -> Property-prop_spaceChar = checkChar spaceChar isSpace (lbl "white space")--prop_upperChar :: String -> Property-prop_upperChar = checkChar upperChar isUpper (lbl "uppercase letter")--prop_lowerChar :: String -> Property-prop_lowerChar = checkChar lowerChar isLower (lbl "lowercase letter")--prop_letterChar :: String -> Property-prop_letterChar = checkChar letterChar isAlpha (lbl "letter")--prop_alphaNumChar :: String -> Property-prop_alphaNumChar = checkChar alphaNumChar isAlphaNum-  (lbl "alphanumeric character")--prop_printChar :: String -> Property-prop_printChar = checkChar printChar isPrint (lbl "printable character")--prop_digitChar :: String -> Property-prop_digitChar = checkChar digitChar isDigit (lbl "digit")--prop_octDigitChar :: String -> Property-prop_octDigitChar = checkChar octDigitChar isOctDigit (lbl "octal digit")--prop_hexDigitChar :: String -> Property-prop_hexDigitChar = checkChar hexDigitChar isHexDigit (lbl "hexadecimal digit")--prop_markChar :: String -> Property-prop_markChar = checkChar markChar isMark (lbl "mark character")--prop_numberChar :: String -> Property-prop_numberChar = checkChar numberChar isNumber (lbl "numeric character")--prop_punctuationChar :: String -> Property-prop_punctuationChar = checkChar punctuationChar isPunctuation (lbl "punctuation")--prop_symbolChar :: String -> Property-prop_symbolChar = checkChar symbolChar isSymbol (lbl "symbol")--prop_separatorChar :: String -> Property-prop_separatorChar = checkChar separatorChar isSeparator (lbl "separator")--prop_asciiChar :: String -> Property-prop_asciiChar = checkChar asciiChar isAscii (lbl "ASCII character")--prop_latin1Char :: String -> Property-prop_latin1Char = checkChar latin1Char isLatin1 (lbl "Latin-1 character")--prop_charCategory :: GeneralCategory -> String -> Property-prop_charCategory cat = checkChar (charCategory cat) p (lbl $ categoryName cat)-  where p c = generalCategory c == cat--prop_char :: Char -> String -> Property-prop_char c = checkChar (char c) (== c) (tkn c)--prop_char' :: Char -> String -> Property-prop_char' c s = checkParser (char' c) r s-  where h = head s-        l | isLower c = [c, toUpper c]-          | isUpper c = [c, toLower c]-          | otherwise = [c]-        r | null s         = posErr 0 s $ ueof : (etok <$> l)-          | length s == 1 && (h `elemi` l) = Right h-          | h `notElemi` l = posErr 0 s $ utok h : (etok <$> l)-          | otherwise      = posErr 1 s [utok (s !! 1), eeof]--prop_anyChar :: String -> Property-prop_anyChar = checkChar anyChar (const True) (lbl "character")--prop_oneOf :: String -> String -> Property-prop_oneOf a = checkChar (oneOf a) (`elem` a) Nothing--prop_oneOf' :: String -> String -> Property-prop_oneOf' a = checkChar (oneOf' a) (`elemi` a) Nothing--prop_noneOf :: String -> String -> Property-prop_noneOf a = checkChar (noneOf a) (`notElem` a) Nothing--prop_noneOf' :: String -> String -> Property-prop_noneOf' a = checkChar (noneOf' a) (`notElemi` a) Nothing--prop_string :: String -> String -> Property-prop_string a = checkString (string a) a (==)--prop_string'_0 :: String -> String -> Property-prop_string'_0 a = checkString (string' a) a casei---- | Randomly change the case in the given string.--fuzzyCase :: String -> Gen String-fuzzyCase s = zipWith f s <$> vector (length s)-  where f k True  = if isLower k then toUpper k else toLower k-        f k False = k--prop_string'_1 :: String -> Property-prop_string'_1 a = forAll (fuzzyCase a) $ \s ->-  checkString (string' a) a casei s---- | Case-insensitive equality test for characters.--casei :: Char -> Char -> Bool-casei x y = toLower x == toLower y---- | Case-insensitive 'elem'.--elemi :: Char -> String -> Bool-elemi c = any (casei c)---- | Case-insensitive 'notElem'.--notElemi :: Char -> String -> Bool-notElemi c = not . elemi c--tkn :: Char -> Maybe (ErrorItem Char)-tkn = Just . Tokens . (:|[])--lbl :: String -> Maybe (ErrorItem Char)-lbl = Just . Label . NE.fromList
− tests/Combinator.hs
@@ -1,233 +0,0 @@------ QuickCheck tests for Megaparsec's generic parser combinators.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--module Combinator (tests) where--import Control.Applicative-import Data.Char (isLetter, isDigit)-import Data.List (intersperse)-import Data.Maybe (fromMaybe, maybeToList, isNothing, fromJust)--import Test.Framework-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.QuickCheck--import Text.Megaparsec.Char-import Text.Megaparsec.Combinator--import Util--tests :: Test-tests = testGroup "Generic parser combinators"-  [ testProperty "combinator between"   prop_between-  , testProperty "combinator choice"    prop_choice-  , testProperty "combinator count"     prop_count-  , testProperty "combinator count'"    prop_count'-  , testProperty "combinator eitherP"   prop_eitherP-  , testProperty "combinator endBy"     prop_endBy-  , testProperty "combinator endBy1"    prop_endBy1-  , testProperty "combinator manyTill"  prop_manyTill-  , testProperty "combinator someTill"  prop_someTill-  , testProperty "combinator option"    prop_option-  , testProperty "combinator sepBy"     prop_sepBy-  , testProperty "combinator sepBy1"    prop_sepBy1-  , testProperty "combinator sepEndBy"  prop_sepEndBy-  , testProperty "combinator sepEndBy1" prop_sepEndBy1-  , testProperty "combinator skipMany"  prop_skipMany-  , testProperty "combinator skipSome"  prop_skipSome ]--prop_between :: String -> Char -> NonNegative Int -> String -> Property-prop_between pre c n' post = checkParser p r s-  where p = between (string pre) (string post) (many (char c))-        n = getNonNegative n'-        b = length $ takeWhile (== c) post-        r | b > 0 = posErr (length pre + n + b) s $ etoks post : etok c :-            [if length post == b-              then ueof-              else utoks [post !! b]]-          | otherwise = Right z-        z = replicate n c-        s = pre ++ z ++ post--prop_choice :: NonEmptyList Char -> Char -> Property-prop_choice cs' s' = checkParser p r s-  where cs = getNonEmpty cs'-        p = choice $ char <$> cs-        r | s' `elem` cs = Right s'-          | otherwise    = posErr 0 s $ utok s' : (etok <$> cs)-        s = [s']--prop_count :: Int -> NonNegative Int -> Property-prop_count n x' = checkParser p r s-  where x = getNonNegative x'-        p = count n (char 'x')-        r = simpleParse (count' n n (char 'x')) s-        s = replicate x 'x'--prop_count' :: Int -> Int -> NonNegative Int -> Property-prop_count' m n x' = checkParser p r s-  where x = getNonNegative x'-        p = count' m n (char 'x')-        r | n <= 0 || m > n  =-              if x == 0-                then Right ""-                else posErr 0 s [utok 'x', eeof]-          | m <= x && x <= n = Right s-          | x < m            = posErr x s [ueof, etok 'x']-          | otherwise        = posErr n s [utok 'x', eeof]-        s = replicate x 'x'--prop_eitherP :: Char -> Property-prop_eitherP ch = checkParser p r s-  where p = eitherP letterChar digitChar-        r | isLetter ch = Right (Left  ch)-          | isDigit  ch = Right (Right ch)-          | otherwise   = posErr 0 s [utok ch, elabel "letter", elabel "digit"]-        s = pure ch--prop_endBy :: NonNegative Int -> Char -> Property-prop_endBy n' c = checkParser p r s-  where n = getNonNegative n'-        p = endBy (char 'a') (char '-')-        r | c == 'a' && n == 0 = posErr 1 s [ueof, etok '-']-          | c == 'a'           = posErr (g n) s [utok 'a', etok '-']-          | c == '-' && n == 0 = posErr 0 s [utok '-', etok 'a', eeof]-          | c /= '-'           = posErr (g n) s $ utok c :-            (if n > 0 then etok '-' else eeof) :-            [etok 'a' | n == 0]-          | otherwise = Right (replicate n 'a')-        s = intersperse '-' (replicate n 'a') ++ [c]--prop_endBy1 :: NonNegative Int -> Char -> Property-prop_endBy1 n' c = checkParser p r s-  where n = getNonNegative n'-        p = endBy1 (char 'a') (char '-')-        r | c == 'a' && n == 0 = posErr 1 s [ueof, etok '-']-          | c == 'a'           = posErr (g n) s [utok 'a', etok '-']-          | c == '-' && n == 0 = posErr 0 s [utok '-', etok 'a']-          | c /= '-'           = posErr (g n) s $ utok c :-            [etok '-' | n > 0] ++ [etok 'a' | n == 0]-          | otherwise = Right (replicate n 'a')-        s = intersperse '-' (replicate n 'a') ++ [c]--prop_manyTill :: NonNegative Int -> NonNegative Int-              -> NonNegative Int -> Property-prop_manyTill a' b' c' = checkParser p r s-  where [a,b,c] = getNonNegative <$> [a',b',c']-        p = (,) <$> manyTill letterChar (char 'c') <*> many letterChar-        r | c == 0    = posErr (a + b) s [ueof, etok 'c', elabel "letter"]-          | otherwise = let (pre, post) = break (== 'c') s-                        in Right (pre, drop 1 post)-        s = abcRow a b c--prop_someTill :: NonNegative Int -> NonNegative Int-              -> NonNegative Int -> Property-prop_someTill a' b' c' = checkParser p r s-  where [a,b,c] = getNonNegative <$> [a',b',c']-        p = (,) <$> someTill letterChar (char 'c') <*> many letterChar-        r | null s    = posErr 0 s [ueof, elabel "letter"]-          | c == 0    = posErr (a + b) s [ueof, etok 'c', elabel "letter"]-          | s == "c"  = posErr 1 s [ueof, etok 'c', elabel "letter"]-          | head s == 'c' = Right ("c", drop 2 s)-          | otherwise = let (pre, post) = break (== 'c') s-                        in Right (pre, drop 1 post)-        s = abcRow a b c--prop_option :: String -> String -> String -> Property-prop_option d a s = checkParser p r s-  where p = option d (string a)-        r = simpleParse (fromMaybe d <$> optional (string a)) s--prop_sepBy :: NonNegative Int -> Maybe Char -> Property-prop_sepBy n' c' = checkParser p r s-  where n = getNonNegative n'-        c = fromJust c'-        p = sepBy (char 'a') (char '-')-        r | isNothing c' = Right (replicate n 'a')-          | c == 'a' && n == 0 = Right "a"-          | n == 0    = posErr 0 s [utok c, etok 'a', eeof]-          | c == '-'  = posErr (length s) s [ueof, etok 'a']-          | otherwise = posErr (g n) s [utok c, etok '-', eeof]-        s = intersperse '-' (replicate n 'a') ++ maybeToList c'--prop_sepBy1 :: NonNegative Int -> Maybe Char -> Property-prop_sepBy1 n' c' = checkParser p r s-  where n = getNonNegative n'-        c = fromJust c'-        p = sepBy1 (char 'a') (char '-')-        r | isNothing c' && n >= 1 = Right (replicate n 'a')-          | isNothing c' = posErr 0 s [ueof, etok 'a']-          | c == 'a' && n == 0 = Right "a"-          | n == 0    = posErr 0 s [utok c, etok 'a']-          | c == '-'  = posErr (length s) s [ueof, etok 'a']-          | otherwise = posErr (g n) s [utok c, etok '-', eeof]-        s = intersperse '-' (replicate n 'a') ++ maybeToList c'--prop_sepEndBy :: NonNegative Int -> Maybe Char -> Property-prop_sepEndBy n' c' = checkParser p r s-  where n = getNonNegative n'-        c = fromJust c'-        p = sepEndBy (char 'a') (char '-')-        a = Right $ replicate n 'a'-        r | isNothing c' = a-          | c == 'a' && n == 0 = Right "a"-          | n == 0    = posErr 0 s [utok c, etok 'a', eeof]-          | c == '-'  = a-          | otherwise = posErr (g n) s [utok c, etok '-', eeof]-        s = intersperse '-' (replicate n 'a') ++ maybeToList c'--prop_sepEndBy1 :: NonNegative Int -> Maybe Char -> Property-prop_sepEndBy1 n' c' = checkParser p r s-  where n = getNonNegative n'-        c = fromJust c'-        p = sepEndBy1 (char 'a') (char '-')-        a = Right $ replicate n 'a'-        r | isNothing c' && n >= 1 = a-          | isNothing c' = posErr 0 s [ueof, etok 'a']-          | c == 'a' && n == 0 = Right "a"-          | n == 0    = posErr 0 s [utok c, etok 'a']-          | c == '-'  = a-          | otherwise = posErr (g n) s [utok c, etok '-', eeof]-        s = intersperse '-' (replicate n 'a') ++ maybeToList c'--prop_skipMany :: Char -> NonNegative Int -> String -> Property-prop_skipMany c n' a = checkParser p r s-  where p = skipMany (char c) *> string a-        n = getNonNegative n'-        r = simpleParse (many (char c) >> string a) s-        s = replicate n c ++ a--prop_skipSome :: Char -> NonNegative Int -> String -> Property-prop_skipSome c n' a = checkParser p r s-  where p = skipSome (char c) *> string a-        n = getNonNegative n'-        r = simpleParse (some (char c) >> string a) s-        s = replicate n c ++ a--g :: Int -> Int-g x = x + if x > 0 then x - 1 else 0
− tests/Error.hs
@@ -1,152 +0,0 @@------ QuickCheck tests for Megaparsec's parse errors.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--{-# LANGUAGE CPP              #-}-{-# OPTIONS -fno-warn-orphans #-}--module Error (tests) where--import Data.Function (on)-import Data.List (isInfixOf)-import Data.List.NonEmpty (NonEmpty (..))-import Data.Monoid ((<>))-import Data.Set (Set)-import qualified Data.List.NonEmpty as NE-import qualified Data.Set           as E--import Test.Framework-import Test.Framework.Providers.HUnit (testCase)-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.HUnit (Assertion, (@?=))-import Test.QuickCheck--import Text.Megaparsec.Error-import Text.Megaparsec.Pos--import Util ()--#if !MIN_VERSION_base(4,8,0)-import Data.Foldable (Foldable, all)-import Data.Monoid (mempty)-import Prelude hiding (all)-#endif--tests :: Test-tests = testGroup "Parse errors"-  [ testProperty "monoid left identity"               prop_monoid_left_id-  , testProperty "monoid right identity"              prop_monoid_right_id-  , testProperty "monoid associativity"               prop_monoid_assoc-  , testProperty "consistency of Show/Read"           prop_showReadConsistency-  , testProperty "position of merged error"           prop_mergeErrorPos-  , testProperty "unexpected items in merged error"   prop_mergeErrorUnexpected-  , testProperty "expected items in merged error"     prop_mergeErrorExpected-  , testProperty "custom items in merged error"       prop_mergeErrorCustom-  , testCase     "showTokens (String instance)"       case_showTokens-  , testCase     "rendering of unknown parse error"   case_ppUnknownError-  , testProperty "source position in rendered error"  prop_ppSourcePos-  , testProperty "unexpected items in rendered error" prop_ppUnexpected-  , testProperty "expected items in rendered error"   prop_ppExpected-  , testProperty "custom data in rendered error"      prop_ppCustom ]--type PE = ParseError Char Dec--prop_monoid_left_id :: PE -> Property-prop_monoid_left_id x = mempty <> x === x .&&.-  mempty { errorPos = errorPos x } <> x === x--prop_monoid_right_id :: PE -> Property-prop_monoid_right_id x = x <> mempty === x .&&.-  mempty { errorPos = errorPos x } <> x === x--prop_monoid_assoc :: PE -> PE -> PE -> Property-prop_monoid_assoc x y z = (x <> y) <> z === x <> (y <> z)--prop_showReadConsistency :: PE -> Property-prop_showReadConsistency x = read (show x) === x--prop_mergeErrorPos :: PE -> PE -> Property-prop_mergeErrorPos e1 e2 =-  errorPos (e1 <> e2) === max (errorPos e1) (errorPos e2)--prop_mergeErrorUnexpected :: PE -> PE -> Property-prop_mergeErrorUnexpected = checkMergedItems errorUnexpected--prop_mergeErrorExpected :: PE -> PE -> Property-prop_mergeErrorExpected = checkMergedItems errorExpected--prop_mergeErrorCustom :: PE -> PE -> Property-prop_mergeErrorCustom = checkMergedItems errorCustom--checkMergedItems :: (Ord a, Show a) => (PE -> Set a) -> PE -> PE -> Property-checkMergedItems f e1 e2 = f (e1 <> e2) === r-  where r = case (compare `on` errorPos) e1 e2 of-              LT -> f e2-              EQ -> (E.union `on` f) e1 e2-              GT -> f e1--case_showTokens :: Assertion-case_showTokens = mapM_ (\(x,y) -> showTokens (NE.fromList x) @?= y)-  [ ("\r\n", "crlf newline")-  , ("\0",   "null")-  , ("\a",   "bell")-  , ("\b",   "backspace")-  , ("\t",   "tab")-  , ("\n",   "newline")-  , ("\v",   "vertical tab")-  , ("\f",   "form feed")-  , ("\r",   "carriage return")-  , (" ",    "space")-  , ("a",    "'a'")-  , ("foo",  "\"foo\"") ]--case_ppUnknownError :: Assertion-case_ppUnknownError =-  parseErrorPretty (err :: PE) @?= "1:1:\nunknown parse error\n"-  where-    err = ParseError-      { errorPos        = initialPos "" :| []-      , errorUnexpected = E.empty-      , errorExpected   = E.empty-      , errorCustom     = E.empty }--prop_ppSourcePos :: PE -> Property-prop_ppSourcePos = checkPresence errorPos sourcePosPretty--prop_ppUnexpected :: PE -> Property-prop_ppUnexpected = checkPresence errorUnexpected showErrorComponent--prop_ppExpected :: PE -> Property-prop_ppExpected = checkPresence errorExpected showErrorComponent--prop_ppCustom :: PE -> Property-prop_ppCustom = checkPresence errorCustom showErrorComponent--checkPresence :: Foldable t => (PE -> t a) -> (a -> String) -> PE -> Property-checkPresence g r e = property (all f (g e))-  where rendered = parseErrorPretty e-        f x = r x `isInfixOf` rendered
− tests/Expr.hs
@@ -1,189 +0,0 @@------ QuickCheck tests for Megaparsec's expression parsers.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--{-# LANGUAGE CPP              #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies     #-}--module Expr (tests) where--import Control.Applicative (some, (<|>))-import Data.Char (isDigit, digitToInt)--import Test.Framework-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.QuickCheck--import Text.Megaparsec.Char-import Text.Megaparsec.Combinator-import Text.Megaparsec.Expr-import Text.Megaparsec.Prim--import Util--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*), (<*>), (*>), pure)-#endif--tests :: Test-tests = testGroup "Expression parsers"-  [ testProperty "correctness of expression parser" prop_correctness-  , testProperty "error message on empty input"     prop_empty_error-  , testProperty "error message on missing term"    prop_missing_term-  , testProperty "error message on missing op"      prop_missing_op ]---- Algebraic structures to build abstract syntax tree of our expression.--data Node-  = Val Integer   -- ^ literal value-  | Neg Node      -- ^ negation (prefix unary)-  | Fac Node      -- ^ factorial (postfix unary)-  | Mod Node Node -- ^ modulo-  | Sum Node Node -- ^ summation (addition)-  | Sub Node Node -- ^ subtraction-  | Pro Node Node -- ^ product-  | Div Node Node -- ^ division-  | Exp Node Node -- ^ exponentiation-    deriving (Eq, Show)--instance Enum Node where-  fromEnum (Val _)   = 0-  fromEnum (Neg _)   = 0-  fromEnum (Fac _)   = 0-  fromEnum (Mod _ _) = 0-  fromEnum (Exp _ _) = 1-  fromEnum (Pro _ _) = 2-  fromEnum (Div _ _) = 2-  fromEnum (Sum _ _) = 3-  fromEnum (Sub _ _) = 3-  toEnum   _         = error "Oops!"--instance Ord Node where-  x `compare` y = fromEnum x `compare` fromEnum y--showNode :: Node -> String-showNode (Val x)     = show x-showNode n@(Neg x)   = "-" ++ showGT n x-showNode n@(Fac x)   = showGT n x ++ "!"-showNode n@(Mod x y) = showGE n x ++ " % " ++ showGE n y-showNode n@(Sum x y) = showGT n x ++ " + " ++ showGE n y-showNode n@(Sub x y) = showGT n x ++ " - " ++ showGE n y-showNode n@(Pro x y) = showGT n x ++ " * " ++ showGE n y-showNode n@(Div x y) = showGT n x ++ " / " ++ showGE n y-showNode n@(Exp x y) = showGE n x ++ " ^ " ++ showGT n y--showGT :: Node -> Node -> String-showGT parent node = (if node > parent then showCmp else showNode) node--showGE :: Node -> Node -> String-showGE parent node = (if node >= parent then showCmp else showNode) node--showCmp :: Node -> String-showCmp node = (if fromEnum node == 0 then showNode else inParens) node--inParens :: Node -> String-inParens x = "(" ++ showNode x ++ ")"--instance Arbitrary Node where-  arbitrary = sized arbitraryN0--arbitraryN0 :: Int -> Gen Node-arbitraryN0 n = frequency [ (1, Mod <$> leaf <*> leaf)-                          , (9, arbitraryN1 n) ]-  where leaf = arbitraryN1 (n `div` 2)--arbitraryN1 :: Int -> Gen Node-arbitraryN1 n =- frequency [ (1, Neg <$> arbitraryN2 n)-           , (1, Fac <$> arbitraryN2 n)-           , (7, arbitraryN2 n)]--arbitraryN2 :: Int -> Gen Node-arbitraryN2 0 = Val . getNonNegative <$> arbitrary-arbitraryN2 n = elements [Sum,Sub,Pro,Div,Exp] <*> leaf <*> leaf-  where leaf = arbitraryN0 (n `div` 2)---- Some helpers are put here since we don't want to depend on--- "Text.Megaparsec.Lexer".--lexeme :: (MonadParsec e s m, Token s ~ Char) => m a -> m a-lexeme p = p <* hidden space--symbol :: (MonadParsec e s m, Token s ~ Char) => String -> m String-symbol = lexeme . string--parens :: (MonadParsec e s m, Token s ~ Char) => m a -> m a-parens = between (symbol "(") (symbol ")")--integer :: (MonadParsec e s m, Token s ~ Char) => m Integer-integer = lexeme (read <$> some digitChar <?> "integer")---- Here we use table of operators that makes use of all features of--- 'makeExprParser'. Then we generate abstract syntax tree (AST) of complex--- but valid expressions and render them to get their textual--- representation.--expr :: (MonadParsec e s m, Token s ~ Char) => m Node-expr = makeExprParser term table--term :: (MonadParsec e s m, Token s ~ Char) => m Node-term = parens expr <|> (Val <$> integer) <?> "term"--table :: (MonadParsec e s m, Token s ~ Char) => [[Operator m Node]]-table = [ [ Prefix  (symbol "-" *> pure Neg)-          , Postfix (symbol "!" *> pure Fac)-          , InfixN  (symbol "%" *> pure Mod) ]-        , [ InfixR  (symbol "^" *> pure Exp) ]-        , [ InfixL  (symbol "*" *> pure Pro)-          , InfixL  (symbol "/" *> pure Div) ]-        , [ InfixL  (symbol "+" *> pure Sum)-          , InfixL  (symbol "-" *> pure Sub)] ]--prop_correctness :: Node -> Property-prop_correctness node = checkParser expr (Right node) (showNode node)--prop_empty_error :: Property-prop_empty_error = checkParser expr r s-  where r = posErr 0 s [ueof, elabel "term"]-        s = ""--prop_missing_term :: Char -> Property-prop_missing_term c = checkParser expr r s-  where r | c `elem` "-(" = posErr 1 s [ueof, elabel "term"]-          | isDigit c     = Right . Val . fromIntegral . digitToInt $ c-          | otherwise     = posErr 0 s [utok c, elabel "term"]-        s = pure c--prop_missing_op :: Node -> Node -> Property-prop_missing_op a b = checkParser expr r s-  where a' = inParens a-        c = s !! n-        n = succ $ length a'-        r | c == '-'  = Right $ Sub a b-          | otherwise = posErr n s [utok c, eeof, elabel "operator"]-        s = a' ++ " " ++ inParens b
− tests/Lexer.hs
@@ -1,402 +0,0 @@------ QuickCheck tests for Megaparsec's lexer.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--{-# LANGUAGE CPP              #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TupleSections    #-}-{-# LANGUAGE TypeFamilies     #-}--module Lexer (tests) where--import Control.Applicative (empty)-import Control.Monad (void)-import Data.Char-  ( readLitChar-  , showLitChar-  , isDigit-  , isAlphaNum-  , isSpace-  , toLower )-import Data.List (findIndices, isInfixOf, find)-import Data.Maybe-import Data.Scientific (fromFloatDigits)-import Numeric (showInt, showHex, showOct, showSigned)--import Test.Framework-import Test.Framework.Providers.HUnit (testCase)-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.HUnit (Assertion)-import Test.QuickCheck--import Text.Megaparsec.Error-import Text.Megaparsec.Lexer-import Text.Megaparsec.Pos-import Text.Megaparsec.Prim-import Text.Megaparsec.String-import qualified Text.Megaparsec.Char as C--import Util--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*), (<*>), (<$))-#endif--tests :: Test-tests = testGroup "Lexer"-  [ testProperty "space combinator"       prop_space-  , testProperty "symbol combinator"      prop_symbol-  , testProperty "symbol' combinator"     prop_symbol'-  , testCase     "skipBlockCommentNested" case_skipBlockCommentNested-  , testProperty "indentLevel"            prop_indentLevel-  , testProperty "incorrectIndent"        prop_incorrectIndent-  , testProperty "indentGuard combinator" prop_indentGuard-  , testProperty "nonIndented combinator" prop_nonIndented-  , testProperty "indentBlock combinator" prop_indentBlock-  , testProperty "indentBlock (many)"     prop_indentMany-  , testProperty "lineFold"               prop_lineFold-  , testProperty "charLiteral"            prop_charLiteral-  , testProperty "integer"                prop_integer-  , testProperty "decimal"                prop_decimal-  , testProperty "hexadecimal"            prop_hexadecimal-  , testProperty "octal"                  prop_octal-  , testProperty "float 0"                prop_float_0-  , testProperty "float 1"                prop_float_1-  , testProperty "number 0"               prop_number_0-  , testProperty "number 1"               prop_number_1-  , testProperty "number 2 (signed)"      prop_number_2-  , testProperty "signed"                 prop_signed ]---- White space--mkWhiteSpace :: Gen String-mkWhiteSpace = concat <$> listOf whiteUnit-  where whiteUnit = oneof [whiteChars, whiteLine, whiteBlock]--mkSymbol :: Gen String-mkSymbol = (++) <$> symbolName <*> whiteChars--mkInterspace :: String -> Int -> Gen String-mkInterspace x n = oneof [si, mkIndent x n]-  where si = (++ x) <$> listOf (elements " \t")--mkIndent :: String -> Int -> Gen String-mkIndent x n = (++) <$> mkIndent' x n <*> eol-  where eol = frequency [(5, return "\n"), (1, listOf1 (return '\n'))]--mkIndent' :: String -> Int -> Gen String-mkIndent' x n = concat <$> sequence [spc, sym, tra]-  where spc = frequency [(5, vectorOf n itm), (1, listOf itm)]-        tra = listOf itm-        itm = elements " \t"-        sym = return x--whiteChars :: Gen String-whiteChars = listOf (elements "\t\n ")--whiteLine :: Gen String-whiteLine = commentOut <$> arbitrary `suchThat` goodEnough-  where commentOut x = "//" ++ x ++ "\n"-        goodEnough x = '\n' `notElem` x--whiteBlock :: Gen String-whiteBlock = commentOut <$> arbitrary `suchThat` goodEnough-  where commentOut x = "/*" ++ x ++ "*/"-        goodEnough x = not $ "*/" `isInfixOf` x--symbolName :: Gen String-symbolName = listOf $ arbitrary `suchThat` isAlphaNum--sc :: Parser ()-sc = space (void $ C.oneOf " \t") empty empty--scn :: Parser ()-scn = space (void C.spaceChar) l b-  where l = skipLineComment "//"-        b = skipBlockComment "/*" "*/"--prop_space :: Property-prop_space = forAll mkWhiteSpace (checkParser p r)-  where p = scn-        r = Right ()--prop_symbol :: Maybe Char -> Property-prop_symbol t = forAll mkSymbol $ \s ->-  parseSymbol (symbol scn) id s t--prop_symbol' :: Maybe Char -> Property-prop_symbol' t = forAll mkSymbol $ \s ->-  parseSymbol (symbol' scn) (fmap toLower) s t--parseSymbol-  :: (String -> Parser String)-  -> (String -> String)-  -> String-  -> Maybe Char-  -> Property-parseSymbol p' f s' t = checkParser p r s-  where p = p' (f g)-        r | g == s || isSpace (last s) = Right g-          | otherwise = posErr (length s - 1) s [utok (last s), eeof]-        g = takeWhile (not . isSpace) s-        s = s' ++ maybeToList t--case_skipBlockCommentNested :: Assertion-case_skipBlockCommentNested = checkCase p r s-  where p = space (void C.spaceChar) empty-              (skipBlockCommentNested "/*" "*/") <* eof-        r = Right ()-        s = " /* foo bar /* baz */ quux */ "---- Indentation--prop_indentLevel :: SourcePos -> Property-prop_indentLevel pos = p /=\ sourceColumn pos-  where p = setPosition pos >> indentLevel--prop_incorrectIndent :: Ordering -> Pos -> Pos -> Property-prop_incorrectIndent ord ref actual = checkParser p r s-  where p = incorrectIndent ord ref actual :: Parser ()-        r = posErr 0 s (ii ord ref actual)-        s = ""--prop_indentGuard :: NonNegative (Small Int) -> Property-prop_indentGuard n =-  forAll ((,,) <$> mki <*> mki <*> mki) $ \(l0,l1,l2) ->-    let r | col0 <= pos1 = posErr 0 s (ii GT pos1 col0)-          | col1 /= col0 = posErr (getIndent l1 + g 1) s (ii EQ col0 col1)-          | col2 <= col0 = posErr (getIndent l2 + g 2) s (ii GT col0 col2)-          | otherwise    = Right ()-        (col0, col1, col2) = (getCol l0, getCol l1, getCol l2)-        fragments = [l0,l1,l2]-        g x = sum (length <$> take x fragments)-        s = concat fragments-    in checkParser p r s-  where mki = mkIndent sbla (getSmall $ getNonNegative n)-        p  = ip GT pos1 >>=-          \x -> sp >> ip EQ x >> sp >> ip GT x >> sp >> scn-        ip = indentGuard scn-        sp = void (symbol sc sbla <* C.eol)--prop_nonIndented :: Property-prop_nonIndented = forAll (mkIndent sbla 0) $ \s ->-  let i = getIndent s-      r | i == 0    = Right sbla-        | otherwise = posErr i s (ii EQ pos1 (getCol s))-  in checkParser p r s-  where p = nonIndented scn (symbol scn sbla)--prop_indentBlock :: Maybe (Positive (Small Int)) -> Property-prop_indentBlock mn'' = forAll mkBlock $ \(l0,l1,l2,l3,l4) ->-  let r | col1 <= col0 =-          posErr (getIndent l1 + g 1) s [utok (head sblb), eeof]-        | isJust mn && col1 /= ib' =-          posErr (getIndent l1 + g 1) s (ii EQ ib' col1)-        | col2 <= col1 =-          posErr (getIndent l2 + g 2) s (ii GT col1 col2)-        | col3 == col2 =-          posErr (getIndent l3 + g 3) s [utok (head sblb), etoks sblc]-        | col3 <= col0 =-          posErr (getIndent l3 + g 3) s [utok (head sblb), eeof]-        | col3 < col1 =-          posErr (getIndent l3 + g 3) s (ii EQ col1 col3)-        | col3 > col1 =-          posErr (getIndent l3 + g 3) s (ii EQ col2 col3)-        | col4 <= col3 =-          posErr (getIndent l4 + g 4) s (ii GT col3 col4)-        | otherwise = Right (sbla, [(sblb, [sblc]), (sblb, [sblc])])-      (col0, col1, col2, col3, col4) =-        (getCol l0, getCol l1, getCol l2, getCol l3, getCol l4)-      fragments = [l0,l1,l2,l3,l4]-      g x = sum (length <$> take x fragments)-      s = concat fragments-  in checkParser p r s-  where mkBlock = do-          l0 <- mkIndent sbla 0-          l1 <- mkIndent sblb ib-          l2 <- mkIndent sblc (ib + 2)-          l3 <- mkIndent sblb ib-          l4 <- mkIndent' sblc (ib + 2)-          return (l0,l1,l2,l3,l4)-        p = lvla-        lvla = indentBlock scn $ IndentMany mn      (l sbla) lvlb <$ b sbla-        lvlb = indentBlock scn $ IndentSome Nothing (l sblb) lvlc <$ b sblb-        lvlc = indentBlock scn $ IndentNone                  sblc <$ b sblc-        b    = symbol sc-        l x  = return . (x,)-        mn'  = getSmall . getPositive <$> mn''-        mn   = unsafePos . fromIntegral <$> mn'-        ib   = fromMaybe 2 mn'-        ib'  = unsafePos (fromIntegral ib)--prop_indentMany :: Property-prop_indentMany = forAll (mkIndent sbla 0) (checkParser p r)-  where r = Right (sbla, [])-        p = lvla-        lvla = indentBlock scn $ IndentMany Nothing (l sbla) lvlb <$ b sbla-        lvlb = b sblb-        b    = symbol sc-        l x  = return . (x,)--prop_lineFold :: Property-prop_lineFold = forAll mkFold $ \(l0,l1,l2) ->-  let r | end0 && col1 <= col0 =-          posErr (getIndent l1 + g 1) s (ii GT col0 col1)-        | end1 && col2 <= col0 =-          posErr (getIndent l2 + g 2) s (ii GT col0 col2)-        | otherwise = Right (sbla, sblb, sblc)-      (col0, col1, col2) = (getCol l0, getCol l1, getCol l2)-      (end0, end1)       = (getEnd l0, getEnd l1)-      fragments = [l0,l1,l2]-      g x = sum (length <$> take x fragments)-      s = concat fragments-  in checkParser p r s-  where-    mkFold = do-      l0 <- mkInterspace sbla 0-      l1 <- mkInterspace sblb 1-      l2 <- mkInterspace sblc 1-      return (l0,l1,l2)-    p = lineFold scn $ \sc' -> do-          a <- symbol sc' sbla-          b <- symbol sc' sblb-          c <- symbol scn sblc-          return (a, b, c)-    getEnd x = last x == '\n'--getIndent :: String -> Int-getIndent = length . takeWhile isSpace--getCol :: String -> Pos-getCol x = sourceColumn .-  updatePosString defaultTabWidth (initialPos "") $ take (getIndent x) x--sbla, sblb, sblc :: String-sbla = "aaa"-sblb = "bbb"-sblc = "ccc"--ii :: Ordering -> Pos -> Pos -> [EC]-ii ord ref actual = [cstm (DecIndentation ord ref actual)]--pos1 :: Pos-pos1 = unsafePos 1---- Character and string literals--prop_charLiteral :: String -> Bool -> Property-prop_charLiteral t i = checkParser charLiteral r s-  where b = listToMaybe $ readLitChar s-        (h, g) = fromJust b-        r | isNothing b = posErr 0 s $ elabel "literal character" :-            [ if null s then ueof else utok (head s) ]-          | null g      = Right h-          | otherwise   = posErr l s [utok (head g), eeof]-        l = length s - length g-        s = if null t || i then t else showLitChar (head t) (tail t)---- Numbers--prop_integer :: NonNegative Integer -> Int -> Property-prop_integer n' i = checkParser integer r s-  where (r, s) = quasiCorrupted n' i showInt "integer"--prop_decimal :: NonNegative Integer -> Int -> Property-prop_decimal n' i = checkParser decimal r s-  where (r, s) = quasiCorrupted n' i showInt "decimal integer"--prop_hexadecimal :: NonNegative Integer -> Int -> Property-prop_hexadecimal n' i = checkParser hexadecimal r s-  where (r, s) = quasiCorrupted n' i showHex "hexadecimal integer"--prop_octal :: NonNegative Integer -> Int -> Property-prop_octal n' i = checkParser octal r s-  where (r, s) = quasiCorrupted n' i showOct "octal integer"--prop_float_0 :: NonNegative Double -> Property-prop_float_0 n' = checkParser float r s-  where n = getNonNegative n'-        r = Right n-        s = show n--prop_float_1 :: Maybe (NonNegative Integer) -> Property-prop_float_1 n' = checkParser float r s-  where r | isNothing n' = posErr 0 s [ueof, elabel "floating point number"]-          | otherwise    = posErr (length s) s-            [ueof, etok '.', etok 'E', etok 'e', elabel "digit"]-        s = maybe "" (show . getNonNegative) n'--prop_number_0 :: Either (NonNegative Integer) (NonNegative Double) -> Property-prop_number_0 n' = checkParser number r s-  where r = Right $ case n' of-              Left  x -> fromIntegral . getNonNegative $ x-              Right x -> fromFloatDigits . getNonNegative $ x-        s = either (show . getNonNegative) (show . getNonNegative) n'--prop_number_1 :: Property-prop_number_1 = checkParser number r s-  where r = posErr 0 s [ueof, elabel "number"]-        s = ""--prop_number_2 :: Either Integer Double -> Property-prop_number_2 n = checkParser p r s-  where p = signed (hidden C.space) number-        r = Right $ case n of-              Left  x -> fromIntegral x-              Right x -> fromFloatDigits x-        s = either show show n--prop_signed :: Integer -> Int -> Bool -> Property-prop_signed n i plus = checkParser p r s-  where p = signed (hidden C.space) integer-        r | i > length z = Right n-          | otherwise = posErr i s $ utok '?' :-            (if i <= 0 then [etok '+', etok '-'] else []) ++-            [elabel $ if isNothing . find isDigit $ take i s-                        then "integer"-                        else "rest of integer"] ++-            [eeof | i > head (findIndices isDigit s)]-        z = let bar = showSigned showInt 0 n ""-            in if n < 0 || plus then bar else '+' : bar-        s = if i <= length z then take i z ++ "?" ++ drop i z else z--quasiCorrupted-  :: NonNegative Integer-  -> Int-  -> (Integer -> String -> String)-  -> String-  -> (Either (ParseError Char Dec) Integer, String)-quasiCorrupted n' i shower l = (r, s)-  where n = getNonNegative n'-        r | i > length z = Right n-          | otherwise    = posErr i s $ utok '?' :-            [ eeof | i > 0 ] ++-            [if i <= 0 || null l-               then elabel l-               else elabel $ "rest of " ++ l]-        z = shower n ""-        s = if i <= length z then take i z ++ "?" ++ drop i z else z
− tests/Main.hs
@@ -1,51 +0,0 @@------ QuickCheck tests for Megaparsec, main module.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--module Main (main) where--import Test.Framework (defaultMain)--import qualified Pos-import qualified Error-import qualified Prim-import qualified Combinator-import qualified Char-import qualified Expr-import qualified Perm-import qualified Lexer--main :: IO ()-main = defaultMain-  [ Pos.tests-  , Error.tests-  , Prim.tests-  , Combinator.tests-  , Char.tests-  , Expr.tests-  , Perm.tests-  , Lexer.tests ]
− tests/Perm.hs
@@ -1,103 +0,0 @@------ QuickCheck tests for Megaparsec's permutation phrases parsers.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--module Perm (tests) where--import Control.Applicative-import Data.List (nub, elemIndices)--import Test.Framework-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.QuickCheck--import Text.Megaparsec.Char-import Text.Megaparsec.Lexer (integer)-import Text.Megaparsec.Perm--import Util--tests :: Test-tests = testGroup "Permutation phrases parsers"-  [ testProperty "permutation parser pure" prop_pure-  , testProperty "permutation test 0"      prop_perm_0-  , testProperty "combinator (<$$>)"       prop_ddcomb ]--data CharRows = CharRows-  { getChars :: (Char, Char, Char)-  , getInput :: String }-  deriving (Eq, Show)--instance Arbitrary CharRows where-  arbitrary = do-    chars@(a,b,c) <- arbitrary `suchThat` different-    an            <- arbitrary-    bn            <- arbitrary-    cn            <- arbitrary-    input <- concat <$> shuffle-             [ replicate an a-             , replicate bn b-             , replicate cn c]-    return $ CharRows chars input-      where different (a,b,c) = let l = [a,b,c] in l == nub l--prop_pure :: Integer -> Property-prop_pure n = makePermParser p /=\ n-  where p = id <$?> (succ n, pure n)--prop_perm_0 :: String -> Char -> CharRows -> Property-prop_perm_0 a' c' v = checkParser (makePermParser p) r s-  where (a,b,c) = getChars v-        p = (,,) <$?> (a', some (char a))-                 <||> char b-                 <|?> (c', char c)-        r | length bis > 1 && (length cis <= 1 || head bis < head cis) =-              posErr (bis !! 1) s $ [utok b, eeof] ++-              [etok a | a `notElem` preb] ++-              [etok c | c `notElem` preb]-          | length cis > 1 =-            posErr (cis !! 1) s $ [utok c] ++-            [etok a | a `notElem` prec] ++-            [if b `elem` prec then eeof else etok b]-          | b `notElem` s = posErr (length s) s $ [ueof, etok b] ++-                            [etok a | a `notElem` s || last s == a] ++-                            [etok c | c `notElem` s]-          | otherwise = Right ( if a `elem` s then filter (== a) s else a'-                              , b-                              , if c `elem` s then c else c' )-        bis  = elemIndices b s-        preb = take (bis !! 1) s-        cis  = elemIndices c s-        prec = take (cis !! 1) s-        s    = getInput v--prop_ddcomb :: NonNegative Integer -> Property-prop_ddcomb n' = checkParser (makePermParser p) r s-  where p = succ <$$> integer-        r = Right (succ n)-        n = getNonNegative n'-        s = show n
− tests/Pos.hs
@@ -1,121 +0,0 @@------ QuickCheck tests for Megaparsec's textual source positions.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--{-# LANGUAGE CPP              #-}-{-# OPTIONS -fno-warn-orphans #-}--module Pos (tests) where--import Control.Monad.Catch-import Data.Function (on)-import Data.List (isInfixOf, elemIndices)-import Data.Semigroup ((<>))--import Test.Framework-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.QuickCheck--import Text.Megaparsec.Pos-import Util (updatePosString)--#if !MIN_VERSION_base(4,8,0)-import Data.Word (Word)-#endif--tests :: Test-tests = testGroup "Textual source positions"-  [ testProperty "creation of Pos (mkPos)"              prop_mkPos-  , testProperty "creation of Pos (unsafePos)"          prop_unsafePos-  , testProperty "consistency of Show/Read for Pos"     prop_showReadPos-  , testProperty "Ord instance of Pos"                  prop_ordPos-  , testProperty "Semigroup instance of Pos"            prop_semigroupPos-  , testProperty "construction of initial position"     prop_initialPos-  , testProperty "consistency of Show/Read for SourcePos" prop_showReadSourcePos-  , testProperty "pretty-printing: visible file path"   prop_ppFilePath-  , testProperty "pretty-printing: visible line"        prop_ppLine-  , testProperty "pretty-printing: visible column"      prop_ppColumn-  , testProperty "default updating of source position"  prop_defaultUpdatePos ]--prop_mkPos :: Word -> Property-prop_mkPos x' = case mkPos x' of-  Left  e -> fromException e === Just InvalidPosException-  Right x -> unPos x === x'--prop_unsafePos :: Positive Word -> Property-prop_unsafePos x' = unPos (unsafePos x) === x-  where x = getPositive x'--prop_showReadPos :: Pos -> Property-prop_showReadPos x = read (show x) === x--prop_ordPos :: Pos -> Pos -> Property-prop_ordPos x y = compare x y === (compare `on` unPos) x y--prop_semigroupPos :: Pos -> Pos -> Property-prop_semigroupPos x y =-  x <> y === unsafePos (unPos x + unPos y) .&&.-  unPos (x <> y) === unPos x + unPos y--prop_initialPos :: String -> Property-prop_initialPos fp =-  sourceName   x === fp          .&&.-  sourceLine   x === unsafePos 1 .&&.-  sourceColumn x === unsafePos 1-  where x = initialPos fp--prop_showReadSourcePos :: SourcePos -> Property-prop_showReadSourcePos x = read (show x) === x--prop_ppFilePath :: SourcePos -> Property-prop_ppFilePath x = property $-  sourceName x `isInfixOf` sourcePosPretty x--prop_ppLine :: SourcePos -> Property-prop_ppLine x = property $-  (show . unPos . sourceLine) x `isInfixOf` sourcePosPretty x--prop_ppColumn :: SourcePos -> Property-prop_ppColumn x = property $-  (show . unPos . sourceColumn) x `isInfixOf` sourcePosPretty x--prop_defaultUpdatePos :: Pos -> SourcePos -> String -> Property-prop_defaultUpdatePos w pos "" = updatePosString w pos "" === pos-prop_defaultUpdatePos w pos s =-  sourceName updated === sourceName pos .&&.-  unPos (sourceLine updated) === unPos (sourceLine pos) + inclines .&&.-  cols >= mincols && ((last s /= '\t') || ((cols - 1) `rem` unPos w == 0))-  where-    updated  = updatePosString w pos s-    cols     = unPos (sourceColumn updated)-    newlines = elemIndices '\n' s-    inclines = fromIntegral (length newlines)-    total    = fromIntegral (length s)-    mincols  =-      if null newlines-        then total + unPos (sourceColumn pos)-        else total - fromIntegral (maximum newlines)
− tests/Prim.hs
@@ -1,1017 +0,0 @@------ QuickCheck tests for Megaparsec's primitive parser combinators.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE Rank2Types        #-}-{-# LANGUAGE RecordWildCards   #-}-{-# LANGUAGE TypeFamilies      #-}-{-# OPTIONS -fno-warn-orphans  #-}--module Prim (tests) where--import Control.Applicative-import Control.Monad.Cont-import Control.Monad.Except-import Control.Monad.Identity-import Control.Monad.Reader-import Data.Char (isLetter, toUpper, chr)-import Data.Foldable (asum)-import Data.List (isPrefixOf, foldl')-import Data.List.NonEmpty (NonEmpty (..))-import Data.Maybe (maybeToList, fromMaybe)-import Data.Proxy-import Data.Set (Set)-import Data.Word (Word8)-import Prelude hiding (span)-import qualified Control.Monad.State.Lazy    as L-import qualified Control.Monad.State.Strict  as S-import qualified Control.Monad.Writer.Lazy   as L-import qualified Control.Monad.Writer.Strict as S-import qualified Data.ByteString.Char8       as B-import qualified Data.ByteString.Lazy.Char8  as BL-import qualified Data.List.NonEmpty          as NE-import qualified Data.Set                    as E-import qualified Data.Text                   as T-import qualified Data.Text.Lazy              as TL--import Test.Framework-import Test.Framework.Providers.HUnit (testCase)-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.QuickCheck hiding (label)-import Test.HUnit (Assertion)--import Text.Megaparsec.Char-import Text.Megaparsec.Combinator-import Text.Megaparsec.Error-import Text.Megaparsec.Pos-import Text.Megaparsec.Prim-import Text.Megaparsec.String--import Pos ()-import Error ()-import Util--tests :: Test-tests = testGroup "Primitive parser combinators"-  [ testProperty "Stream lazy byte string"             prop_byteStringL-  , testProperty "Stream lazy byte string (pos)"       prop_byteStringL_pos-  , testProperty "Stream strict byte string"           prop_byteStringS-  , testProperty "Stream strict byte string (pos)"     prop_byteStringS_pos-  , testProperty "Stream lazy text"                    prop_textL-  , testProperty "Stream lazy text (pos)"              prop_textL_pos-  , testProperty "Stream strict text"                  prop_textS-  , testProperty "Stream strict text (pos)"            prop_textS_pos-  , testProperty "position in custom stream, eof"      prop_cst_eof-  , testProperty "position in custom stream, token"    prop_cst_token-  , testProperty "position in custom stream, tokens"   prop_cst_tokens-  , testProperty "ParsecT functor"                     prop_functor-  , testProperty "ParsecT applicative (<*>)"           prop_applicative_0-  , testProperty "ParsecT applicative (<*>) meok-cerr" prop_applicative_1-  , testProperty "ParsecT applicative (*>)"            prop_applicative_2-  , testProperty "ParsecT applicative (<*)"            prop_applicative_3-  , testProperty "ParsecT alternative empty and (<|>)" prop_alternative_0-  , testProperty "ParsecT alternative (<|>)"           prop_alternative_1-  , testProperty "ParsecT alternative (<|>) pos"       prop_alternative_2-  , testProperty "ParsecT alternative (<|>) hints"     prop_alternative_3-  , testProperty "ParsecT alternative many"            prop_alternative_4-  , testProperty "ParsecT alternative some"            prop_alternative_5-  , testProperty "ParsecT alternative optional"        prop_alternative_6-  , testProperty "ParsecT monad return"                prop_monad_0-  , testProperty "ParsecT monad (>>)"                  prop_monad_1-  , testProperty "ParsecT monad (>>=)"                 prop_monad_2-  , testProperty "ParsecT monad fail"                  prop_monad_3-  , testProperty "ParsecT monad laws: left identity"   prop_monad_left_id-  , testProperty "ParsecT monad laws: right identity"  prop_monad_right_id-  , testProperty "ParsecT monad laws: associativity"   prop_monad_assoc-  , testProperty "ParsecT monad io (liftIO)"           prop_monad_io-  , testProperty "ParsecT monad reader ask"   prop_monad_reader_ask-  , testProperty "ParsecT monad reader local" prop_monad_reader_local-  , testProperty "ParsecT monad state get"    prop_monad_state_get-  , testProperty "ParsecT monad state put"    prop_monad_state_put-  , testProperty "ParsecT monad cont"         prop_monad_cont-  , testProperty "ParsecT monad error: throw" prop_monad_error_throw-  , testProperty "ParsecT monad error: catch" prop_monad_error_catch-  , testProperty "combinator unexpected"      prop_unexpected-  , testProperty "combinator failure"                  prop_failure-  , testProperty "combinator label"                    prop_label-  , testProperty "combinator hidden hints"             prop_hidden_0-  , testProperty "combinator hidden error"             prop_hidden_1-  , testProperty "combinator try"                      prop_try-  , testProperty "combinator lookAhead"                prop_lookAhead_0-  , testProperty "combinator lookAhead hints"          prop_lookAhead_1-  , testProperty "combinator lookAhead messages"       prop_lookAhead_2-  , testCase     "combinator lookAhead cerr"           case_lookAhead_3-  , testProperty "combinator notFollowedBy"       prop_notFollowedBy_0-  , testProperty "combinator notFollowedBy twice" prop_notFollowedBy_1-  , testProperty "combinator notFollowedBy eof"   prop_notFollowedBy_2-  , testCase     "combinator notFollowedBy cerr"  case_notFollowedBy_3a-  , testCase     "combinator notFollowedBy cerr"  case_notFollowedBy_3b-  , testCase     "combinator notFollowedBy eerr"  case_notFollowedBy_4a-  , testCase     "combinator notFollowedBy eerr"  case_notFollowedBy_4b-  , testProperty "combinator withRecovery"             prop_withRecovery_0-  , testCase     "combinator withRecovery eok"         case_withRecovery_1-  , testCase     "combinator withRecovery meerr-rcerr" case_withRecovery_2-  , testCase     "combinator withRecovery meerr-reok"  case_withRecovery_3a-  , testCase     "combinator withRecovery meerr-reok"  case_withRecovery_3b-  , testCase     "combinator withRecovery mcerr-rcok"  case_withRecovery_4a-  , testCase     "combinator withRecovery mcerr-rcok"  case_withRecovery_4b-  , testCase     "combinator withRecovery mcerr-rcerr" case_withRecovery_5-  , testCase     "combinator withRecovery mcerr-reok"  case_withRecovery_6a-  , testCase     "combinator withRecovery mcerr-reok"  case_withRecovery_6b-  , testCase     "combinator withRecovery mcerr-reerr" case_withRecovery_7-  , testCase     "combinator eof return value"    case_eof-  , testProperty "combinator token"                    prop_token-  , testProperty "combinator tokens"                   prop_tokens_0-  , testProperty "combinator tokens (consumption)"     prop_tokens_1-  , testProperty "parser state position"               prop_state_pos-  , testProperty "parser state position (push)"        prop_state_pushPosition-  , testProperty "parser state position (pop)"         prop_state_popPosition-  , testProperty "parser state input"                  prop_state_input-  , testProperty "parser state tab width"              prop_state_tab-  , testProperty "parser state general"                prop_state-  , testProperty "parseMaybe"                          prop_parseMaybe-  , testProperty "custom state parsing"                prop_runParser'-  , testProperty "custom state parsing (transformer)"  prop_runParserT'-  , testProperty "state on failure (mplus)"         prop_stOnFail_0-  , testProperty "state on failure (tab)"           prop_stOnFail_1-  , testProperty "state on failure (eof)"           prop_stOnFail_2-  , testProperty "state on failure (notFollowedBy)" prop_stOnFail_3-  , testProperty "ReaderT try"              prop_ReaderT_try-  , testProperty "ReaderT notFollowedBy"    prop_ReaderT_notFollowedBy-  , testProperty "StateT alternative (<|>)" prop_StateT_alternative-  , testProperty "StateT lookAhead"         prop_StateT_lookAhead-  , testProperty "StateT notFollowedBy"     prop_StateT_notFollowedBy-  , testProperty "WriterT"                  prop_WriterT ]--instance Arbitrary a => Arbitrary (State a) where-  arbitrary = State-    <$> arbitrary-    <*> arbitrary-    <*> (unsafePos <$> choose (1, 20))---- Various instances of Stream--prop_byteStringL :: Word8 -> NonNegative Int -> Property-prop_byteStringL ch' n = parse p "" (BL.pack s) === Right s-  where p  = many (char ch) :: Parsec Dec BL.ByteString String-        s  = replicate (getNonNegative n) ch-        ch = byteToChar ch'--prop_byteStringL_pos :: Pos -> SourcePos -> Char -> Property-prop_byteStringL_pos w pos ch =-  updatePos (Proxy :: Proxy String) w pos ch ===-  updatePos (Proxy :: Proxy BL.ByteString) w pos ch--prop_byteStringS :: Word8 -> NonNegative Int -> Property-prop_byteStringS ch' n = parse p "" (B.pack s) === Right s-  where p  = many (char ch) :: Parsec Dec B.ByteString String-        s  = replicate (getNonNegative n) ch-        ch = byteToChar ch'--prop_byteStringS_pos :: Pos -> SourcePos -> Char -> Property-prop_byteStringS_pos w pos ch =-  updatePos (Proxy :: Proxy String) w pos ch ===-  updatePos (Proxy :: Proxy B.ByteString) w pos ch--byteToChar :: Word8 -> Char-byteToChar = chr . fromIntegral--prop_textL :: Char -> NonNegative Int -> Property-prop_textL ch n = parse p "" (TL.pack s) === Right s-  where p = many (char ch) :: Parsec Dec TL.Text String-        s = replicate (getNonNegative n) ch--prop_textL_pos :: Pos -> SourcePos -> Char -> Property-prop_textL_pos w pos ch =-  updatePos (Proxy :: Proxy String) w pos ch ===-  updatePos (Proxy :: Proxy TL.Text) w pos ch--prop_textS :: Char -> NonNegative Int -> Property-prop_textS ch n = parse p "" (T.pack s) === Right s-  where p = many (char ch) :: Parsec Dec T.Text String-        s = replicate (getNonNegative n) ch--prop_textS_pos :: Pos -> SourcePos -> Char -> Property-prop_textS_pos w pos ch =-  updatePos (Proxy :: Proxy String) w pos ch ===-  updatePos (Proxy :: Proxy T.Text) w pos ch---- Custom stream of tokens and position advancing---- | This data type will represent tokens in input stream for the purposes--- of next several tests.--data Span = Span-  { spanStart :: SourcePos-  , spanEnd   :: SourcePos-  , spanBody  :: NonEmpty Char-  } deriving (Eq, Ord, Show)--instance Stream [Span] where-  type Token [Span] = Span-  uncons [] = Nothing-  uncons (t:ts) = Just (t, ts)-  updatePos _ _ _ (Span start end _) = (start, end)--instance Arbitrary Span where-  arbitrary = do-    start <- arbitrary-    end   <- arbitrary `suchThat` (> start)-    Span start end <$> arbitrary--type CustomParser = Parsec Dec [Span]--prop_cst_eof :: State [Span] -> Property-prop_cst_eof st =-  (not . null . stateInput) st ==> (runParser' p st === r)-  where-    p = eof :: CustomParser ()-    h = head (stateInput st)-    apos = let (_:|z) = statePos st in spanStart h :| z-    r = (st { statePos = apos }, Left ParseError-      { errorPos        = apos-      , errorUnexpected = E.singleton (Tokens (nes h))-      , errorExpected   = E.singleton EndOfInput-      , errorCustom     = E.empty })--prop_cst_token :: State [Span] -> Span -> Property-prop_cst_token st@State {..} span = runParser' p st === r-  where-    p = pSpan span-    h = head stateInput-    (apos, npos) =-      let z = NE.tail statePos-      in (spanStart h :| z, spanEnd h :| z)-    r | null stateInput =-        ( st-        , Left ParseError-          { errorPos        = statePos-          , errorUnexpected = E.singleton EndOfInput-          , errorExpected   = E.singleton (Tokens $ nes span)-          , errorCustom     = E.empty } )-      | spanBody h == spanBody span =-          ( st { statePos = npos-               , stateInput = tail stateInput }-          , Right span )-      | otherwise =-          ( st { statePos = apos }-          , Left ParseError-            { errorPos        = apos-            , errorUnexpected = E.singleton (Tokens $ nes h)-            , errorExpected   = E.singleton (Tokens $ nes span)-            , errorCustom     = E.empty } )--pSpan :: Span -> CustomParser Span-pSpan span = token testToken (Just span)-  where-    f = E.singleton . Tokens . nes-    testToken x =-      if spanBody x == spanBody span-        then Right span-        else Left (f x, f span , E.empty)--prop_cst_tokens :: State [Span] -> [Span] -> Property-prop_cst_tokens st' ts =-  forAll (incCoincidence st' ts) $ \st@State {..} ->-  let-    p = tokens compareTokens ts :: CustomParser [Span]-    compareTokens x y = spanBody x == spanBody y-    updatePos' = updatePos (Proxy :: Proxy [Span]) stateTabWidth-    ts' = NE.fromList ts-    il = length . takeWhile id $ zipWith compareTokens stateInput ts-    tl = length ts-    consumed = take il stateInput-    (apos, npos) =-      let (pos:|z) = statePos-      in ( spanStart (head stateInput) :| z-         , foldl' (\q t -> snd (updatePos' q t)) pos consumed :| z )-    r | null ts = (st, Right [])-      | null stateInput =-        ( st-        , Left ParseError-          { errorPos        = statePos-          , errorUnexpected = E.singleton EndOfInput-          , errorExpected   = E.singleton (Tokens ts')-          , errorCustom     = E.empty } )-      | il == tl =-        ( st { statePos   = npos-             , stateInput = drop (length ts) stateInput }-        , Right consumed )-      | otherwise =-        ( st { statePos = apos }-        , Left ParseError-          { errorPos        = apos-          , errorUnexpected = E.singleton-            (Tokens . NE.fromList $ take (il + 1) stateInput)-          , errorExpected   = E.singleton (Tokens ts')-          , errorCustom     = E.empty } )-  in runParser' p st === r--incCoincidence :: State [Span] -> [Span] -> Gen (State [Span])-incCoincidence st ts = do-  n <- getSmall <$> arbitrary-  let (pre, post) = splitAt n (stateInput st)-      pre' = zipWith (\x t -> x { spanBody = spanBody t }) pre ts-  return st { stateInput = pre' ++ post }---- Functor instance--prop_functor :: Integer -> Integer -> Property-prop_functor n m =-  ((+ m) <$> return n) /=\ n + m .&&. ((* n) <$> return m) /=\ n * m---- Applicative instance--prop_applicative_0 :: Integer -> Integer -> Property-prop_applicative_0 n m = ((+) <$> pure n <*> pure m) /=\ n + m--prop_applicative_1 :: Char -> Char -> Property-prop_applicative_1 a b = a /= b ==> checkParser p r s-  where-    p = pure toUpper <*> (char a >> char a)-    r = posErr 1 s [utok b, etok a]-    s = [a,b]--prop_applicative_2 :: Integer -> Integer -> Property-prop_applicative_2 n m = (pure n *> pure m) /=\ m--prop_applicative_3 :: Integer -> Integer -> Property-prop_applicative_3 n m = (pure n <* pure m) /=\ n---- Alternative instance--prop_alternative_0 :: Integer -> Property-prop_alternative_0 n = (empty <|> return n) /=\ n--prop_alternative_1 :: String -> String -> Property-prop_alternative_1 s0 s1-  | s0 == s1 = checkParser p (Right s0) s1-  | null s0  = checkParser p (posErr 0 s1 [utok (head s1), eeof]) s1-  | s0 `isPrefixOf` s1 =-      checkParser p (posErr s0l s1 [utok (s1 !! s0l), eeof]) s1-  | otherwise = checkParser p (Right s0) s0 .&&. checkParser p (Right s1) s1-    where p   = string s0 <|> string s1-          s0l = length s0--prop_alternative_2 :: Char -> Char -> Char -> Bool -> Property-prop_alternative_2 a b c l = checkParser p r s-  where p = char a <|> (char b >> char a)-        r | l         = Right a-          | a == b    = posErr 1 s [utok c, eeof]-          | a == c    = Right a-          | otherwise = posErr 1 s [utok c, etok a]-        s = if l then [a] else [b,c]--prop_alternative_3 :: Property-prop_alternative_3 = checkParser p r s-  where p  = asum [empty, string ">>>", empty, return "foo"] <?> "bar"-        p' = bsum [empty, string ">>>", empty, return "foo"] <?> "bar"-        bsum = foldl (<|>) empty-        r = simpleParse p' s-        s = ">>"--prop_alternative_4 :: NonNegative Int -> NonNegative Int-                   -> NonNegative Int -> Property-prop_alternative_4 a' b' c' = checkParser p r s-  where [a,b,c] = getNonNegative <$> [a',b',c']-        p = (++) <$> many (char 'a') <*> many (char 'b')-        r | null s = Right s-          | c > 0  = posErr (a + b) s $ [utok 'c', etok 'b', eeof]-                     ++ [etok 'a' | b == 0]-          | otherwise = Right s-        s = abcRow a b c--prop_alternative_5 :: NonNegative Int -> NonNegative Int-                   -> NonNegative Int -> Property-prop_alternative_5 a' b' c' = checkParser p r s-  where [a,b,c] = getNonNegative <$> [a',b',c']-        p = (++) <$> some (char 'a') <*> some (char 'b')-        r | null s = posErr 0 s [ueof, etok 'a']-          | a == 0 = posErr 0 s [utok (head s), etok 'a']-          | b == 0 = posErr a s $ [etok 'a', etok 'b'] ++-                     if c > 0 then [utok 'c'] else [ueof]-          | c > 0 = posErr (a + b) s [utok 'c', etok 'b', eeof]-          | otherwise = Right s-        s = abcRow a b c--prop_alternative_6 :: Bool -> Bool -> Bool -> Property-prop_alternative_6 a b c = checkParser p r s-  where p = f <$> optional (char 'a') <*> optional (char 'b')-        f x y = maybe "" (:[]) x ++ maybe "" (:[]) y-        r | c = posErr ab s $ [utok 'c', eeof] ++-                [etok 'a' | not a && not b] ++ [etok 'b' | not b]-          | otherwise = Right s-        s = abcRow a b c-        ab = fromEnum a + fromEnum b---- Monad instance--prop_monad_0 :: Integer -> Property-prop_monad_0 n = checkParser (return n) (Right n) ""--prop_monad_1 :: Char -> Char -> Maybe Char -> Property-prop_monad_1 a b c = checkParser p r s-  where p = char a >> char b-        r = simpleParse (char a *> char b) s-        s = a : b : maybeToList c--prop_monad_2 :: Char -> Char -> Maybe Char -> Property-prop_monad_2 a b c = checkParser p r s-  where p = char a >>= \x -> char b >> return x-        r = simpleParse (char a <* char b) s-        s = a : b : maybeToList c--prop_monad_3 :: String -> Property-prop_monad_3 msg = checkParser p r s-  where p = fail msg :: Parser ()-        r = posErr 0 s [cstm (DecFail msg)]-        s = ""--prop_monad_left_id :: Integer -> Integer -> Property-prop_monad_left_id a b = (return a >>= f) !=! f a-  where f x = return $ x + b--prop_monad_right_id :: Integer -> Property-prop_monad_right_id a = (m >>= return) !=! m-  where m = return a--prop_monad_assoc :: Integer -> Integer -> Integer -> Property-prop_monad_assoc a b c = ((m >>= f) >>= g) !=! (m >>= (\x -> f x >>= g))-  where m = return a-        f x = return $ x + b-        g x = return $ x + c---- MonadIO instance--prop_monad_io :: Integer -> Property-prop_monad_io n = ioProperty (liftM (=== Right n) (runParserT p "" ""))-  where p = liftIO (return n) :: ParsecT Dec String IO Integer---- MonadReader instance--prop_monad_reader_ask :: Integer -> Property-prop_monad_reader_ask a = runReader (runParserT p "" "") a === Right a-  where p = ask :: ParsecT Dec String (Reader Integer) Integer--prop_monad_reader_local :: Integer -> Integer -> Property-prop_monad_reader_local a b =-  runReader (runParserT p "" "") a === Right (a + b)-  where p = local (+ b) ask :: ParsecT Dec String (Reader Integer) Integer---- MonadState instance--prop_monad_state_get :: Integer -> Property-prop_monad_state_get a = L.evalState (runParserT p "" "") a === Right a-  where p = L.get :: ParsecT Dec String (L.State Integer) Integer--prop_monad_state_put :: Integer -> Integer -> Property-prop_monad_state_put a b = L.execState (runParserT p "" "") a === b-  where p = L.put b :: ParsecT Dec String (L.State Integer) ()---- MonadCont instance--prop_monad_cont :: Integer -> Integer -> Property-prop_monad_cont a b = runCont (runParserT p "" "") id === Right (max a b)-  where p :: ParsecT Dec String-             (Cont (Either (ParseError Char Dec) Integer)) Integer-        p = do x <- callCC $ \e -> when (a > b) (e a) >> return b-               return x---- MonadError instance--prop_monad_error_throw :: Integer -> Integer -> Property-prop_monad_error_throw a b = runExcept (runParserT p "" "") === Left a-  where p :: ParsecT Dec String (Except Integer) Integer-        p = throwError a >> return b--prop_monad_error_catch :: Integer -> Integer -> Property-prop_monad_error_catch a b =-  runExcept (runParserT p "" "") === Right (Right $ a + b)-  where p :: ParsecT Dec String (Except Integer) Integer-        p = (throwError a >> return b) `catchError` handler-        handler e = return (e + b)---- Primitive combinators--prop_unexpected :: ErrorItem Char -> Property-prop_unexpected item = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = unexpected item-        r = posErr 0 s [Unexpected item]-        s = ""--prop_failure-  :: Set (ErrorItem Char)-  -> Set (ErrorItem Char)-  -> Set Dec-  -> Property-prop_failure us ps xs = checkParser' p r s-  where p :: (MonadParsec Dec s m, Token s ~ Char) => m String-        p = failure us ps xs-        r = Left ParseError-          { errorPos        = nes (initialPos "")-          , errorUnexpected = us-          , errorExpected   = ps-          , errorCustom     = xs }-        s = ""--prop_label :: NonNegative Int -> NonNegative Int-           -> NonNegative Int -> String -> Property-prop_label a' b' c' l = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = (++) <$> many (char 'a') <*> (many (char 'b') <?> l)-        r | null s = Right s-          | c > 0 = posErr (a + b) s $ [utok 'c', eeof]-            ++ [etok 'a' | b == 0]-            ++ (if null l-                  then []-                  else [if b == 0-                         then elabel l-                         else elabel ("rest of " ++ l)])-          | otherwise = Right s-        s = abcRow a b c-        [a,b,c] = getNonNegative <$> [a',b',c']--prop_hidden_0 :: NonNegative Int -> NonNegative Int-              -> NonNegative Int -> Property-prop_hidden_0 a' b' c' = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = (++) <$> many (char 'a') <*> hidden (many (char 'b'))-        r | null s = Right s-          | c > 0  = posErr (a + b) s $ [utok 'c', eeof]-                     ++ [etok 'a' | b == 0]-          | otherwise = Right s-        s = abcRow a b c-        [a,b,c] = getNonNegative <$> [a',b',c']--prop_hidden_1 :: NonEmptyList Char -> String -> Property-prop_hidden_1 c' s = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m (Maybe String)-        p = optional (hidden $ string c)-        r | null s           = Right Nothing-          | c == s           = Right (Just s)-          | c `isPrefixOf` s = posErr cn s [utok (s !! cn), eeof]-          | otherwise        = posErr 0 s [utok (head s), eeof]-        c = getNonEmpty c'-        cn = length c--prop_try :: Char -> Char -> Char -> Property-prop_try pre ch1 ch2 = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = try (sequence [char pre, char ch1])-          <|> sequence [char pre, char ch2]-        r = posErr 1 s [ueof, etok ch1, etok ch2]-        s = [pre]--prop_lookAhead_0 :: Bool -> Bool -> Bool -> Property-prop_lookAhead_0 a b c = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m Char-        p = do-          l <- lookAhead (oneOf "ab" <?> "label")-          guard (l == h)-          char 'a'-        h = head s-        r | null s = posErr 0 s [ueof, elabel "label"]-          | s == "a" = Right 'a'-          | h == 'b' = posErr 0 s [utok 'b', etok 'a']-          | h == 'c' = posErr 0 s [utok 'c', elabel "label"]-          | otherwise  = posErr 1 s [utok (s !! 1), eeof]-        s = abcRow a b c--prop_lookAhead_1 :: String -> Property-prop_lookAhead_1 s = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m ()-        p = lookAhead (some letterChar) >> fail emsg-        h = head s-        r | null s     = posErr 0 s [ueof, elabel "letter"]-          | isLetter h = posErr 0 s [cstm (DecFail emsg)]-          | otherwise  = posErr 0 s [utok h, elabel "letter"]-        emsg = "ops!"--prop_lookAhead_2 :: Bool -> Bool -> Bool -> Property-prop_lookAhead_2 a b c = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m Char-        p = lookAhead (some (char 'a')) >> char 'b'-        r | null s    = posErr 0 s [ueof, etok 'a']-          | a         = posErr 0 s [utok 'a', etok 'b']-          | otherwise = posErr 0 s [utok (head s), etok 'a']-        s = abcRow a b c--case_lookAhead_3 :: Assertion-case_lookAhead_3 = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = lookAhead (char 'a' *> fail emsg)-        r = posErr 1 s [cstm (DecFail emsg)]-        emsg = "ops!"-        s = "abc"--prop_notFollowedBy_0 :: NonNegative Int -> NonNegative Int-                     -> NonNegative Int -> Property-prop_notFollowedBy_0 a' b' c' = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = many (char 'a') <* notFollowedBy (char 'b') <* many (char 'c')-        r | b > 0     = posErr a s [utok 'b', etok 'a']-          | otherwise = Right (replicate a 'a')-        s = abcRow a b c-        [a,b,c] = getNonNegative <$> [a',b',c']--prop_notFollowedBy_1 :: NonNegative Int -> NonNegative Int-                     -> NonNegative Int -> Property-prop_notFollowedBy_1 a' b' c' = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = many (char 'a')-          <* (notFollowedBy . notFollowedBy) (char 'c')-          <* many (char 'c')-        r | b == 0 && c > 0 = Right (replicate a 'a')-          | b > 0           = posErr a s [utok 'b', etok 'a']-          | otherwise       = posErr a s [ueof, etok 'a']-        s = abcRow a b c-        [a,b,c] = getNonNegative <$> [a',b',c']--prop_notFollowedBy_2 :: NonNegative Int -> NonNegative Int-                     -> NonNegative Int -> Property-prop_notFollowedBy_2 a' b' c' = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = many (char 'a') <* notFollowedBy eof <* many anyChar-        r | b > 0 || c > 0 = Right (replicate a 'a')-          | otherwise      = posErr a s [ueof, etok 'a']-        s = abcRow a b c-        [a,b,c] = getNonNegative <$> [a',b',c']--case_notFollowedBy_3a :: Assertion-case_notFollowedBy_3a = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m ()-        p = notFollowedBy (char 'a' *> char 'c')-        r = Right ()-        s = "ab"--case_notFollowedBy_3b :: Assertion-case_notFollowedBy_3b = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m ()-        p = notFollowedBy (char 'a' *> char 'd') <* char 'c'-        r = posErr 0 s [utok 'a', etok 'c']-        s = "ab"--case_notFollowedBy_4a :: Assertion-case_notFollowedBy_4a = checkCase' p r s-  where p :: MonadParsec e s m => m ()-        p = notFollowedBy mzero-        r = Right ()-        s = "ab"--case_notFollowedBy_4b :: Assertion-case_notFollowedBy_4b = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m ()-        p = notFollowedBy mzero <* char 'c'-        r = posErr 0 s [utok 'a', etok 'c']-        s = "ab"--prop_withRecovery_0-  :: NonNegative Int-  -> NonNegative Int-  -> NonNegative Int-  -> Property-prop_withRecovery_0 a' b' c' = checkParser' p r s-  where-    p :: (MonadParsec Dec s m, Token s ~ Char)-      => m (Either (ParseError Char Dec) String)-    p = let g = count' 1 3 . char in v <$>-      withRecovery (\e -> Left e <$ g 'b') (Right <$> g 'a') <*> g 'c'-    v (Right x) y = Right (x ++ y)-    v (Left  m) _ = Left m-    r | a == 0 && b == 0 && c == 0 = posErr 0 s [ueof, etok 'a']-      | a == 0 && b == 0 && c >  3 = posErr 0 s [utok 'c', etok 'a']-      | a == 0 && b == 0           = posErr 0 s [utok 'c', etok 'a']-      | a == 0 && b >  3           = posErr 3 s [utok 'b', etok 'a', etok 'c']-      | a == 0 &&           c == 0 = posErr b s [ueof, etok 'a', etok 'c']-      | a == 0 &&           c >  3 = posErr (b + 3) s [utok 'c', eeof]-      | a == 0                     = Right (posErr 0 s [utok 'b', etok 'a'])-      | a >  3                     = posErr 3 s [utok 'a', etok 'c']-      |           b == 0 && c == 0 = posErr a s $ [ueof, etok 'c'] ++ ma-      |           b == 0 && c >  3 = posErr (a + 3) s [utok 'c', eeof]-      |           b == 0           = Right (Right s)-      | otherwise                  = posErr a s $ [utok 'b', etok 'c'] ++ ma-    ma = [etok 'a' | a < 3]-    s = abcRow a b c-    [a,b,c] = getNonNegative <$> [a',b',c']--case_withRecovery_1 :: Assertion-case_withRecovery_1 = checkCase' p r s-  where p :: MonadParsec e s m => m String-        p = withRecovery (const $ return "bar") (return "foo")-        r = Right "foo"-        s = "abc"--case_withRecovery_2 :: Assertion-case_withRecovery_2 = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = withRecovery (\_ -> char 'a' *> mzero) (string "cba")-        r = posErr 0 s [utoks "a", etoks "cba"]-        s = "abc"--case_withRecovery_3a :: Assertion-case_withRecovery_3a = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = withRecovery (const $ return "abd") (string "cba")-        r = Right "abd"-        s = "abc"--case_withRecovery_3b :: Assertion-case_withRecovery_3b = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = withRecovery (const $ return "abd") (string "cba") <* char 'd'-        r = posErr 0 s [utok 'a', etoks "cba", etok 'd']-        s = "abc"--case_withRecovery_4a :: Assertion-case_withRecovery_4a = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = withRecovery (const $ string "bc") (char 'a' *> mzero)-        r = Right "bc"-        s = "abc"--case_withRecovery_4b :: Assertion-case_withRecovery_4b = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = withRecovery (const $ string "bc")-          (char 'a' *> char 'd' *> pure "foo") <* char 'f'-        r = posErr 3 s [ueof, etok 'f']-        s = "abc"--case_withRecovery_5 :: Assertion-case_withRecovery_5 = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = withRecovery (\_ -> char 'b' *> fail emsg) (char 'a' *> fail emsg)-        r = posErr 1 s [cstm (DecFail emsg)]-        emsg = "ops!"-        s = "abc"--case_withRecovery_6a :: Assertion-case_withRecovery_6a = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m String-        p = withRecovery (const $ return "abd") (char 'a' *> mzero)-        r = Right "abd"-        s = "abc"--case_withRecovery_6b :: Assertion-case_withRecovery_6b = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m Char-        p = withRecovery (const $ return 'g') (char 'a' *> char 'd') <* char 'f'-        r = posErr 1 s [utok 'b', etok 'd', etok 'f']-        s = "abc"--case_withRecovery_7 :: Assertion-case_withRecovery_7 = checkCase' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m Char-        p = withRecovery (const mzero) (char 'a' *> char 'd')-        r = posErr 1 s [utok 'b', etok 'd']-        s = "abc"--case_eof :: Assertion-case_eof = checkCase' eof (Right ()) ""--prop_token :: Maybe Char -> String -> Property-prop_token mtok s = checkParser' p r s-  where p :: (MonadParsec e s m, Token s ~ Char) => m Char-        p = token testChar mtok-        testChar x = if isLetter x-          then Right x-          else Left (E.singleton (Tokens $ nes x), E.empty, E.empty)-        h = head s-        r | null s = posErr 0 s $ ueof : maybeToList (etok <$> mtok)-          | isLetter h && length s == 1 = Right (head s)-          | isLetter h && length s > 1 = posErr 1 s [utok (s !! 1), eeof]-          | otherwise = posErr 0 s [utok h]--prop_tokens_0 :: String -> String -> Property-prop_tokens_0 a = checkString (tokens (==) a) a (==)--prop_tokens_1 :: String -> String -> String -> Property-prop_tokens_1 pre post post' =-  not (post `isPrefixOf` post') ==>-  (leftover === "" .||. leftover === s)-  where p :: Parser String-        p = tokens (==) (pre ++ post)-        s = pre ++ post'-        st = stateFromInput s-        leftover = stateInput . fst $ runParser' p st---- Parser state combinators--prop_state_pos :: State String -> SourcePos -> Property-prop_state_pos st pos = runParser' p st === r-  where p = (setPosition pos >> getPosition) :: Parser SourcePos-        r = (f st pos, Right pos)-        f (State s (_:|xs) w) y = State s (y:|xs) w--prop_state_pushPosition :: State String -> SourcePos -> Property-prop_state_pushPosition st pos = fst (runParser' p st) === r-  where p = pushPosition pos :: Parser ()-        r = st { statePos = NE.cons pos (statePos st) }--prop_state_popPosition :: State String -> Property-prop_state_popPosition st = fst (runParser' p st) === r-  where p = popPosition :: Parser ()-        r = st { statePos = fromMaybe pos (snd (NE.uncons pos)) }-        pos = statePos st--prop_state_input :: String -> Property-prop_state_input s = p /=\ s-  where p = do-          st0    <- getInput-          guard (null st0)-          setInput s-          result <- string s-          st1    <- getInput-          guard (null st1)-          return result--prop_state_tab :: Pos -> Property-prop_state_tab w = p /=\ w-  where p = setTabWidth w >> getTabWidth--prop_state :: State String -> State String -> Property-prop_state s1 s2 = checkParser' p r s-  where p :: MonadParsec Dec String m => m (State String)-        p = do-          st <- getParserState-          guard (st == State s (nes $ initialPos "") defaultTabWidth)-          setParserState s1-          updateParserState (f s2)-          liftM2 const getParserState (setInput "")-        f (State s1' pos w) (State s2' _ _) = State (max s1' s2' ) pos w-        r = Right (f s2 s1)-        s = ""---- Running a parser--prop_parseMaybe :: String -> String -> Property-prop_parseMaybe s s' = parseMaybe p s === r-  where p = string s' :: Parser String-        r = if s == s' then Just s else Nothing--prop_runParser' :: State String -> String -> Property-prop_runParser' st s = runParser' p st === r-  where p = string s-        r = emulateStrParsing st s--prop_runParserT' :: State String -> String -> Property-prop_runParserT' st s = runIdentity (runParserT' p st) === r-  where p = string s-        r = emulateStrParsing st s--emulateStrParsing-  :: State String-  -> String-  -> (State String, Either (ParseError Char Dec) String)-emulateStrParsing st@(State i (pos:|z) t) s =-  if l == length s-    then (State (drop l i) (updatePosString t pos s :| z) t, Right s)-    else (st, posErr' (pos:|z) (etoks s : [utoks (take (l + 1) i)]))-  where l = length (takeWhile id $ zipWith (==) s i)---- Additional tests to check returned state on failure--prop_stOnFail_0 :: Positive Int -> Positive Int -> Property-prop_stOnFail_0 na' nb' = runParser' p (stateFromInput s) === (i, r)-  where i = let (Left x) = r in State "" (errorPos x) defaultTabWidth-        na = getPositive na'-        nb = getPositive nb'-        p = try (many (char 'a') <* many (char 'b') <* char 'c')-          <|> (many (char 'a') <* char 'c')-        r = posErr (na + nb) s [etok 'b', etok 'c', ueof]-        s = replicate na 'a' ++ replicate nb 'b'--prop_stOnFail_1 :: Positive Int -> Pos -> Property-prop_stOnFail_1 na' t = runParser' p (stateFromInput s) === (i, r)-  where i = let (Left x) = r in State "" (errorPos x) t-        na = getPositive na'-        p = many (char 'a') <* setTabWidth t <* fail emsg-        r = posErr na s [cstm (DecFail emsg)]-        s = replicate na 'a'-        emsg = "failing now!"--prop_stOnFail_2 :: String -> Char -> Property-prop_stOnFail_2 s' ch = runParser' p (stateFromInput s) === (i, r)-  where i = let (Left x) = r in State [ch] (errorPos x) defaultTabWidth-        r = posErr (length s') s [utok ch, eeof]-        p = string s' <* eof-        s = s' ++ [ch]--prop_stOnFail_3 :: String -> Property-prop_stOnFail_3 s = runParser' p (stateFromInput s) === (i, r)-  where i = let (Left x) = r in State s (errorPos x) defaultTabWidth-        r = posErr 0 s [if null s then ueof else utok (head s)]-        p = notFollowedBy (string s)--stateFromInput :: s -> State s-stateFromInput s = State s (nes $ initialPos "") defaultTabWidth---- ReaderT instance of MonadParsec--prop_ReaderT_try :: Char -> Char -> Char -> Property-prop_ReaderT_try pre ch1 ch2 = checkParser (runReaderT p (s1, s2)) r s-  where s1 = pre : [ch1]-        s2 = pre : [ch2]-        getS1 = asks fst-        getS2 = asks snd-        p = try (g =<< getS1) <|> (g =<< getS2)-        g = sequence . fmap char-        r = posErr 1 s [ueof, etok ch1, etok ch2]-        s = [pre]--prop_ReaderT_notFollowedBy :: NonNegative Int -> NonNegative Int-                           -> NonNegative Int -> Property-prop_ReaderT_notFollowedBy a' b' c' = checkParser (runReaderT p 'a') r s-  where [a,b,c] = getNonNegative <$> [a',b',c']-        p = many (char =<< ask) <* notFollowedBy eof <* many anyChar-        r | b > 0 || c > 0 = Right (replicate a 'a')-          | otherwise      = posErr a s [ueof, etok 'a']-        s = abcRow a b c---- StateT instance of MonadParsec--prop_StateT_alternative :: Integer -> Property-prop_StateT_alternative n =-  checkParser (L.evalStateT p 0) (Right n) "" .&&.-  checkParser (S.evalStateT p' 0) (Right n) ""-  where p  = L.put n >> ((L.modify (* 2) >>-                          void (string "xxx")) <|> return ()) >> L.get-        p' = S.put n >> ((S.modify (* 2) >>-                          void (string "xxx")) <|> return ()) >> S.get--prop_StateT_lookAhead :: Integer -> Property-prop_StateT_lookAhead n =-  checkParser (L.evalStateT p 0) (Right n) "" .&&.-  checkParser (S.evalStateT p' 0) (Right n) ""-  where p  = L.put n >> lookAhead (L.modify (* 2) >> eof) >> L.get-        p' = S.put n >> lookAhead (S.modify (* 2) >> eof) >> S.get--prop_StateT_notFollowedBy :: Integer -> Property-prop_StateT_notFollowedBy n = checkParser (L.runStateT p 0) r "abx" .&&.-                              checkParser (S.runStateT p' 0) r "abx"-  where p = do-          L.put n-          let notEof = notFollowedBy (L.modify (* 2) >> eof)-          some (try (anyChar <* notEof)) <* char 'x'-        p' = do-          S.put n-          let notEof = notFollowedBy (S.modify (* 2) >> eof)-          some (try (anyChar <* notEof)) <* char 'x'-        r = Right ("ab", n)---- WriterT instance of MonadParsec--prop_WriterT :: String -> String -> Property-prop_WriterT pre post =-  checkParser (L.runWriterT p) r "abx" .&&.-  checkParser (S.runWriterT p') r "abx"-  where logged_letter  = letterChar >>= \x -> L.tell [x] >> return x-        logged_letter' = letterChar >>= \x -> L.tell [x] >> return x-        logged_eof     = eof >> L.tell "EOF"-        logged_eof'    = eof >> L.tell "EOF"-        p = do-          L.tell pre-          cs <- L.censor (fmap toUpper) $-                  some (try (logged_letter <* notFollowedBy logged_eof))-          L.tell post-          void logged_letter-          return cs-        p' = do-          L.tell pre-          cs <- L.censor (fmap toUpper) $-                  some (try (logged_letter' <* notFollowedBy logged_eof'))-          L.tell post-          void logged_letter'-          return cs-        r = Right ("ab", pre ++ "AB" ++ post ++ "x")--nes :: a -> NonEmpty a-nes x = x :| []-{-# INLINE nes #-}
+ tests/Spec.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE CPP #-}++#if __GLASGOW_HASKELL__ >= 708+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}+#else++module Spec (main) where++main :: IO ()+main = return ()+#endif
+ tests/Test/Hspec/Megaparsec.hs view
@@ -0,0 +1,376 @@+-- |+-- Module      :  Test.Hspec.Megaparsec+-- Copyright   :  © 2016 Mark Karpov+-- License     :  BSD 3 clause+--+-- Maintainer  :  Mark Karpov <markkarpov@openmailbox.org>+-- Stability   :  experimental+-- Portability :  portable+--+-- Utility functions for testing Megaparsec parsers with Hspec.++{-# LANGUAGE BangPatterns        #-}+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE DeriveDataTypeable  #-}+{-# LANGUAGE DeriveGeneric       #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies        #-}++module Test.Hspec.Megaparsec+  ( -- * Basic expectations+    shouldParse+  , parseSatisfies+  , shouldSucceedOn+  , shouldFailOn+    -- * Testing of error messages+  , shouldFailWith+    -- * Error message construction+    -- $errmsg+  , err+  , posI+  , posN+  , EC+  , utok+  , utoks+  , ulabel+  , ueof+  , etok+  , etoks+  , elabel+  , eeof+  , cstm+    -- * Incremental parsing+  , failsLeaving+  , succeedsLeaving+  , initialState )+where++import Control.Monad (unless)+import Data.Data (Data)+import Data.List.NonEmpty (NonEmpty (..))+import Data.Proxy+import Data.Semigroup+import Data.Set (Set)+import Data.Typeable (Typeable)+import GHC.Generics+import Test.Hspec.Expectations+import Text.Megaparsec+import Text.Megaparsec.Pos (defaultTabWidth)+import qualified Data.List.NonEmpty as NE+import qualified Data.Set           as E++----------------------------------------------------------------------------+-- Basic expectations++-- | Create an expectation by saying what the result should be.+--+-- > parse letterChar "" "x" `shouldParse` 'x'++shouldParse :: (Ord t, ShowToken t, ShowErrorComponent e, Eq a, Show a)+  => Either (ParseError t e) a+     -- ^ Result of parsing as returned by function like 'parse'+  -> a                 -- ^ Desired result+  -> Expectation+r `shouldParse` v = case r of+  Left e -> expectationFailure $ "expected: " ++ show v +++    "\nbut parsing failed with error:\n" ++ showParseError e+  Right x -> unless (x == v) . expectationFailure $+    "expected: " ++ show v ++ "\nbut got: " ++ show x++-- | Create an expectation by saying that the parser should successfully+-- parse a value and that the value should satisfy some predicate.+--+-- > parse (many punctuationChar) "" "?!!" `parseSatisfies` ((== 3) . length)++parseSatisfies :: (Ord t, ShowToken t, ShowErrorComponent e, Show a)+  => Either (ParseError t e) a+     -- ^ Result of parsing as returned by function like 'parse'+  -> (a -> Bool)       -- ^ Predicate+  -> Expectation+r `parseSatisfies` p = case r of+  Left e -> expectationFailure $+    "expected a parsed value to check against the predicate" +++    "\nbut parsing failed with error:\n" ++ showParseError e+  Right x -> unless (p x) . expectationFailure $+    "the value did not satisfy the predicate: " ++ show x++-- | Check that a parser fails on some given input.+--+-- > parse (char 'x') "" `shouldFailOn` "a"++shouldFailOn :: Show a+  => (s -> Either (ParseError t e) a)+     -- ^ Parser that takes stream and produces result or error message+  -> s                 -- ^ Input that the parser should fail on+  -> Expectation+p `shouldFailOn` s = shouldFail (p s)++-- | Check that a parser succeeds on some given input.+--+-- > parse (char 'x') "" `shouldSucceedOn` "x"++shouldSucceedOn :: (Ord t, ShowToken t, ShowErrorComponent e, Show a)+  => (s -> Either (ParseError t e) a)+     -- ^ Parser that takes stream and produces result or error message+  -> s                 -- ^ Input that the parser should succeed on+  -> Expectation+p `shouldSucceedOn` s = shouldSucceed (p s)++----------------------------------------------------------------------------+-- Testing of error messages++-- | Create an expectation that parser should fail producing certain+-- 'ParseError'. Use the 'err' function from this module to construct a+-- 'ParseError' to compare with.+--+-- > parse (char 'x') "" "b" `shouldFailWith` err posI (utok 'b' <> etok 'x')++shouldFailWith :: (Ord t, ShowToken t, ShowErrorComponent e, Show a)+  => Either (ParseError t e) a+  -> ParseError t e+  -> Expectation+r `shouldFailWith` e = case r of+  Left e' -> unless (e == e') . expectationFailure $+    "the parser is expected to fail with:\n" ++ showParseError e +++    "but it failed with:\n" ++ showParseError e'+  Right v -> expectationFailure $+    "the parser is expected to fail, but it parsed: " ++ show v++----------------------------------------------------------------------------+-- Error message construction++-- $errmsg When you wish to test error message on failure, the need to+-- construct a error message for comparison arises. These helpers allow to+-- construct virtually any sort of error message easily.++-- | Assemble a 'ParseErorr' from source position and @'EC' t e@ value. To+-- create source position, two helpers are available: 'posI' and 'posN'.+-- @'EC' t e@ is a monoid and can be built from primitives provided by this+-- module, see below.+--+-- @since 0.3.0++err+  :: NonEmpty SourcePos -- ^ 'ParseError' position+  -> EC t e             -- ^ Error components+  -> ParseError t e     -- ^ Resulting 'ParseError'+err pos (EC u e c) = ParseError pos u e c++-- | Initial source position with empty file name.+--+-- @since 0.3.0++posI :: NonEmpty SourcePos+posI = initialPos "" :| []++-- | @posN n s@ returns source position achieved by applying 'updatePos'+-- method corresponding to type of stream @s@ @n@ times.+--+-- @since 0.3.0++posN :: forall s n. (Stream s, Integral n)+  => n+  -> s+  -> NonEmpty SourcePos+posN n see = f (initialPos "") see n :| []+  where+    f p s !i =+      if i > 0+        then case uncons s of+          Nothing -> p+          Just (t,s') ->+            let p' = snd $ updatePos (Proxy :: Proxy s) defaultTabWidth p t+            in f p' s' (i - 1)+        else p++-- | Auxiliary type for construction of 'ParseError's. Note that it's a+-- monoid.+--+-- @since 0.3.0++data EC t e = EC+  { ecUnexpected :: Set (ErrorItem t) -- ^ Unexpected items+  , ecExpected   :: Set (ErrorItem t) -- ^ Expected items+  , _ecCustom    :: Set e             -- ^ Custom items+  } deriving (Eq, Data, Typeable, Generic)++instance (Ord t, Ord e) => Semigroup (EC t e) where+  (EC u0 e0 c0) <> (EC u1 e1 c1) =+    EC (E.union u0 u1) (E.union e0 e1) (E.union c0 c1)++instance (Ord t, Ord e) => Monoid (EC t e) where+  mempty  = EC E.empty E.empty E.empty+  mappend = (<>)++-- | Construct “unexpected token” error component.+--+-- @since 0.3.0++utok :: (Ord t, Ord e) => t -> EC t e+utok t = mempty { ecUnexpected = (E.singleton . Tokens . nes) t }++-- | Construct “unexpected tokens” error component. Empty string produces+-- 'EndOfInput'.+--+-- @since 0.3.0++utoks :: (Ord t, Ord e) => [t] -> EC t e+utoks t = mempty { ecUnexpected = (E.singleton . canonicalizeTokens) t }++-- | Construct “unexpected label” error component. Do not use with empty+-- strings (for empty strings it's bottom).+--+-- @since 0.3.0++ulabel :: (Ord t, Ord e) => String -> EC t e+ulabel l = mempty { ecUnexpected = (E.singleton . Label . NE.fromList) l }++-- | Construct “unexpected end of input” error component.+--+-- @since 0.3.0++ueof :: (Ord t, Ord e) => EC t e+ueof = mempty { ecUnexpected = E.singleton EndOfInput }++-- | Construct “expected token” error component.+--+-- @since 0.3.0++etok :: (Ord t, Ord e) => t -> EC t e+etok t = mempty { ecExpected = (E.singleton . Tokens . nes) t }++-- | Construct “expected tokens” error component. Empty string produces+-- 'EndOfInput'.+--+-- @since 0.3.0++etoks :: (Ord t, Ord e) => [t] -> EC t e+etoks t = mempty { ecExpected = (E.singleton . canonicalizeTokens) t }++-- | Construct “expected label” error component. Do not use with empty+-- strings.+--+-- @since 0.3.0++elabel :: (Ord t, Ord e) => String -> EC t e+elabel l = mempty { ecExpected = (E.singleton . Label . NE.fromList) l }++-- | Construct “expected end of input” error component.+--+-- @since 0.3.0++eeof :: (Ord t, Ord e) => EC t e+eeof = mempty { ecExpected = E.singleton EndOfInput }++-- | Construct custom error component.+--+-- @since 0.3.0++cstm :: e -> EC t e+cstm e = EC E.empty E.empty (E.singleton e)++----------------------------------------------------------------------------+-- Incremental parsing++-- | Check that a parser fails and leaves certain part of input+-- unconsumed. Use it with functions like 'runParser'' and 'runParserT''+-- that support incremental parsing.+--+-- > runParser' (many (char 'x') <* eof) (initialState "xxa")+-- >   `failsLeaving` "a"+--+-- See also: 'initialState'.++failsLeaving :: (Show a, Eq s, Show s, Stream s)+  => (State s, Either (ParseError (Token s) e) a)+     -- ^ Parser that takes stream and produces result along with actual+     -- state information+  -> s                 -- ^ Part of input that should be left unconsumed+  -> Expectation+(st,r) `failsLeaving` s =+  shouldFail r >> checkUnconsumed s (stateInput st)++-- | Check that a parser succeeds and leaves certain part of input+-- unconsumed. Use it with functions like 'runParser'' and 'runParserT''+-- that support incremental parsing.+--+-- > runParser' (many (char 'x')) (initialState "xxa")+-- >   `succeedsLeaving` "a"+--+-- See also: 'initialState'.++succeedsLeaving :: ( ShowToken (Token s)+                   , ShowErrorComponent e+                   , Show a+                   , Eq s+                   , Show s+                   , Stream s )+  => (State s, Either (ParseError (Token s) e) a)+     -- ^ Parser that takes stream and produces result along with actual+     -- state information+  -> s                 -- ^ Part of input that should be left unconsumed+  -> Expectation+(st,r) `succeedsLeaving` s =+  shouldSucceed r >> checkUnconsumed s (stateInput st)++-- | Given input for parsing, construct initial state for parser (that is,+-- with empty file name, default tab width and position at 1 line and 1+-- column).++initialState :: s -> State s+initialState s = State s (initialPos "" :| []) defaultTabWidth++----------------------------------------------------------------------------+-- Helpers++-- | Expectation that argument is result of a failed parser.++shouldFail :: Show a => Either (ParseError t e) a -> Expectation+shouldFail r = case r of+  Left _ -> return ()+  Right v -> expectationFailure $+    "the parser is expected to fail, but it parsed: " ++ show v++-- | Expectation that argument is result of a succeeded parser.++shouldSucceed :: (Ord t, ShowToken t, ShowErrorComponent e, Show a)+  => Either (ParseError t e) a -> Expectation+shouldSucceed r = case r of+  Left e -> expectationFailure $+    "the parser is expected to succeed, but it failed with:\n" +++    showParseError e+  Right _ -> return ()++-- | Compare two streams for equality and in the case of mismatch report it.++checkUnconsumed :: (Eq s, Show s, Stream s)+  => s                 -- ^ Expected unconsumed input+  -> s                 -- ^ Actual unconsumed input+  -> Expectation+checkUnconsumed e a = unless (e == a) . expectationFailure $+  "the parser is expected to leave unconsumed input: " ++ show e +++  "\nbut it left this: " ++ show a++-- | Render parse error in a way that is suitable for inserting it in test+-- suite report.++showParseError :: (Ord t, ShowToken t, ShowErrorComponent e)+  => ParseError t e -> String+showParseError = unlines . fmap ("  " ++) . lines . parseErrorPretty++-- | Make a singleton non-empty list from a value.++nes :: a -> NonEmpty a+nes x = x :| []+{-# INLINE nes #-}++-- | Construct appropriate 'ErrorItem' representation for given token+-- stream. Empty string produces 'EndOfInput'.++canonicalizeTokens :: [t] -> ErrorItem t+canonicalizeTokens ts =+  case NE.nonEmpty ts of+    Nothing -> EndOfInput+    Just xs -> Tokens xs
+ tests/Test/Hspec/Megaparsec/AdHoc.hs view
@@ -0,0 +1,185 @@+--+-- Tests for Megaparsec's expression parsers.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE CPP              #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes       #-}++module Test.Hspec.Megaparsec.AdHoc+  ( -- * Helpers to run parsers+    prs+  , prs'+  , prs_+  , grs+  , grs'+    -- * Working with source position+  , updatePosString+  , pos1+  , nes+    -- * Other+  , abcRow+  , toFirstMismatch )+where++import Control.Monad+import Control.Monad.Reader+import Control.Monad.Trans.Identity+import Data.Foldable (foldl')+import Data.List.NonEmpty (NonEmpty (..))+import Test.Hspec+import Test.Hspec.Megaparsec+import Text.Megaparsec.Error+import Text.Megaparsec.Pos+import Text.Megaparsec.Prim+import qualified Control.Monad.State.Lazy    as L+import qualified Control.Monad.State.Strict  as S+import qualified Control.Monad.Writer.Lazy   as L+import qualified Control.Monad.Writer.Strict as S++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative+#endif++----------------------------------------------------------------------------+-- Helpers to run parsers++-- | Apply parser to given input. This is a specialized version of 'parse'+-- that assumes empty file name.++prs+  :: Parsec Dec String a -- ^ Parser to run+  -> String            -- ^ Input for the parser+  -> Either (ParseError Char Dec) a -- ^ Result of parsing+prs p = parse p ""+{-# INLINE prs #-}++-- | Just like 'prs', but allows to inspect final state of the parser.++prs'+  :: Parsec Dec String a -- ^ Parser to run+  -> String            -- ^ Input for the parser+  -> (State String, Either (ParseError Char Dec) a) -- ^ Result of parsing+prs' p s = runParser' p (initialState s)+{-# INLINE prs' #-}++-- | Just like 'prs', but forces the parser to consume all input by adding+-- 'eof':+--+-- > prs_ p = parse (p <* eof) ""++prs_+  :: Parsec Dec String a -- ^ Parser to run+  -> String            -- ^ Input for the parser+  -> Either (ParseError Char Dec) a -- ^ Result of parsing+prs_ p = parse (p <* eof) ""+{-# INLINE prs_ #-}++-- | Just like 'prs', but interprets given parser as various monads (tries+-- all supported monads transformers in turn).++grs+  :: (forall m. MonadParsec Dec String m => m a) -- ^ Parser to run+  -> String            -- ^ Input for the parser+  -> (Either (ParseError Char Dec) a -> Expectation)+    -- ^ How to check result of parsing+  -> Expectation+grs p s r = do+  r (prs p s)+  r (prs (runIdentityT p)    s)+  r (prs (runReaderT   p ()) s)+  r (prs (L.evalStateT p ()) s)+  r (prs (S.evalStateT p ()) s)+  r (prs (evalWriterTL p)    s)+  r (prs (evalWriterTS p)    s)++-- | 'grs'' to 'grs' as 'prs'' to 'prs'.++grs'+  :: (forall m. MonadParsec Dec String m => m a) -- ^ Parser to run+  -> String            -- ^ Input for the parser+  -> ((State String, Either (ParseError Char Dec) a) -> Expectation)+    -- ^ How to check result of parsing+  -> Expectation+grs' p s r = do+  r (prs' p s)+  r (prs' (runIdentityT p)    s)+  r (prs' (runReaderT   p ()) s)+  r (prs' (L.evalStateT p ()) s)+  r (prs' (S.evalStateT p ()) s)+  r (prs' (evalWriterTL p)    s)+  r (prs' (evalWriterTS p)    s)++evalWriterTL :: Monad m => L.WriterT [Int] m a -> m a+evalWriterTL = liftM fst . L.runWriterT+evalWriterTS :: Monad m => S.WriterT [Int] m a -> m a+evalWriterTS = liftM fst . S.runWriterT++----------------------------------------------------------------------------+-- Working with source position++-- | A helper function that is used to advance 'SourcePos' given a 'String'.++updatePosString+  :: Pos               -- ^ Tab width+  -> SourcePos         -- ^ Initial position+  -> String            -- ^ 'String' — collection of tokens to process+  -> SourcePos         -- ^ Final position+updatePosString w = foldl' f+  where f p t = snd (defaultUpdatePos w p t)++-- | Position with minimal value.++pos1 :: Pos+pos1 = unsafePos 1++-- | Make a singleton non-empty list from a value.++nes :: a -> NonEmpty a+nes x = x :| []+{-# INLINE nes #-}++----------------------------------------------------------------------------+-- Other++-- | @abcRow a b c@ generates string consisting of character “a” repeated+-- @a@ times, character “b” repeated @b@ times, and character “c” repeated+-- @c@ times.++abcRow :: Int -> Int -> Int -> String+abcRow a b c = replicate a 'a' ++ replicate b 'b' ++ replicate c 'c'++-- | Given a comparing function, get prefix of one string till first+-- mismatch with another string (including first mismatching character).++toFirstMismatch+  :: (Char -> Char -> Bool) -- ^ Comparing function+  -> String            -- ^ First string+  -> String            -- ^ Second string+  -> String            -- ^ Resulting prefix+toFirstMismatch f str s = take (n + 1) s+  where n = length (takeWhile (uncurry f) (zip str s))
+ tests/Text/Megaparsec/CharSpec.hs view
@@ -0,0 +1,450 @@+--+-- Tests for Megaparsec's character parsers.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE CPP              #-}+{-# OPTIONS -fno-warn-orphans #-}++module Text.Megaparsec.CharSpec (spec) where++import Control.Monad+import Data.Char+import Data.List (partition, isPrefixOf)+import Data.Monoid ((<>))+import Test.Hspec+import Test.Hspec.Megaparsec+import Test.Hspec.Megaparsec.AdHoc+import Test.QuickCheck+import Text.Megaparsec.Char+import Text.Megaparsec.Error+import Text.Megaparsec.Prim++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative+#endif++instance Arbitrary GeneralCategory where+  arbitrary = elements [minBound..maxBound]++spec :: Spec+spec = do++  describe "newline" $+    checkStrLit "newline" "\n" (pure <$> newline)++  describe "csrf" $+    checkStrLit "crlf newline" "\r\n" crlf++  describe "eol" $ do+    context "when stream begins with a newline" $+      it "succeeds returning the newline" $+        property $ \s -> do+          let s' = '\n' : s+          prs  eol s' `shouldParse`     "\n"+          prs' eol s' `succeedsLeaving` s+    context "when stream begins with CRLF sequence" $+      it "parses the CSRF sequence" $+        property $ \s -> do+          let s' = '\r' : '\n' : s+          prs  eol s' `shouldParse`     "\r\n"+          prs' eol s' `succeedsLeaving` s+    context "when stream begins with '\\r', but it's not followed by '\\n'" $+      it "signals correct parse error" $+        property $ \ch -> ch /= '\n' ==> do+          let s = ['\r',ch]+          prs eol s `shouldFailWith` err posI+            (utoks s <> utok '\r' <> elabel "end of line")+    context "when input stream is '\\r'" $+      it "signals correct parse error" $+        prs eol "\r" `shouldFailWith` err posI+          (utok '\r' <> elabel "end of line")+    context "when stream does not begin with newline or CSRF sequence" $+      it "signals correct parse error" $+        property $ \ch s -> (ch `notElem` "\r\n") ==> do+          let s' = ch : s+          prs eol s' `shouldFailWith` err posI+            (utok ch <> elabel "end of line")+    context "when stream is empty" $+      it "signals correct parse error" $+        prs eol "" `shouldFailWith` err posI+          (ueof <> elabel "end of line")++  describe "tab" $+    checkStrLit "tab" "\t" (pure <$> tab)++  describe "space" $+    it "consumes it up to first non-space character" $+      property $ \s -> do+        let (s0,s1) = partition isSpace s+            s' = s0 ++ s1+        prs  space s' `shouldParse` ()+        prs' space s' `succeedsLeaving` s1++  describe "controlChar" $+    checkCharPred "control character" isControl controlChar++  describe "spaceChar" $+    checkCharRange "white space" " \160\t\n\r\f\v" spaceChar++  describe "upperChar" $+    checkCharPred "uppercase letter" isUpper upperChar++  describe "lowerChar" $+    checkCharPred "lowercase letter" isLower lowerChar++  describe "letterChar" $+    checkCharPred "letter" isAlpha letterChar++  describe "alphaNumChar" $+    checkCharPred "alphanumeric character" isAlphaNum alphaNumChar++  describe "printChar" $+    checkCharPred "printable character" isPrint printChar++  describe "digitChar" $+    checkCharRange "digit" ['0'..'9'] digitChar++  describe "octDigitChar" $+    checkCharRange "octal digit" ['0'..'7'] octDigitChar++  describe "hexDigitChar" $+    checkCharRange "hexadecimal digit" (['0'..'9'] ++ ['a'..'f'] ++ ['A'..'F']) hexDigitChar++  describe "markChar" $+    checkCharRange "mark character" "" markChar++  describe "numberChar" $+    let xs = "\185\178\179\188\189\190" ++ ['0'..'9']+    in checkCharRange "numeric character" xs numberChar++  describe "punctuationChar" $+    checkCharPred "punctuation" isPunctuation punctuationChar++  describe "symbolChar" $+#if MIN_VERSION_base(4,8,0)+    checkCharRange "symbol" "<>$£`~|×÷^®°¸¯=¬+¤±¢¨´©¥¦" symbolChar+#else+    checkCharRange "symbol" "<>$£`~|×÷^®°¸¯=¬+¤±¢¨´©¥¦§¶" symbolChar+#endif+  describe "separatorChar" $+    checkCharRange "separator" " \160" separatorChar++  describe "asciiChar" $+    checkCharPred "ASCII character" isAscii asciiChar++  describe "latin1Char" $ do+    context "when stream begins with Latin-1 character" $+      it "parses the Latin-1 character" $+        property $ \ch s -> isLatin1 ch ==> do+          let s' = ch : s+          prs  latin1Char s' `shouldParse`     ch+          prs' latin1Char s' `succeedsLeaving` s+    context "when stream does not begin with Latin-1 character" $+      it "signals correct parse error" $ do+        prs  latin1Char "б" `shouldFailWith`+          err posI (utok 'б' <> elabel "Latin-1 character")+        prs' latin1Char "в" `failsLeaving`   "в"+    context "when stream is empty" $+      it "signals correct parse error" $+        prs latin1Char "" `shouldFailWith` err posI (ueof <> elabel "Latin-1 character")++  describe "charCategory" $ do+    context "when parser corresponding to general category of next char is used" $+      it "succeeds" $+        property $ \ch s -> do+          let s' = ch : s+              g  = generalCategory ch+          prs  (charCategory g) s' `shouldParse`     ch+          prs' (charCategory g) s' `succeedsLeaving` s+    context "when parser's category does not match next character's category" $+      it "fails" $+        property $ \g ch s -> (generalCategory ch /= g) ==> do+          let s' = ch : s+          prs  (charCategory g) s' `shouldFailWith`+            err posI (utok ch <> elabel (categoryName g))+          prs' (charCategory g) s' `failsLeaving` s'+    context "when stream is empty" $+      it "signals correct parse error" $+        property $ \g ->+          prs (charCategory g) "" `shouldFailWith`+            err posI (ueof <> elabel (categoryName g))++  describe "char" $ do+    context "when stream begins with the character specified as argument" $+      it "parses the character" $+        property $ \ch s -> do+          let s' = ch : s+          prs  (char ch) s' `shouldParse` ch+          prs' (char ch) s' `succeedsLeaving` s+    context "when stream does not begin with the character specified as argument" $+      it "signals correct parse error" $+        property $ \ch ch' s -> ch /= ch' ==> do+          let s' = ch' : s+          prs  (char ch) s' `shouldFailWith` err posI (utok ch' <> etok ch)+          prs' (char ch) s' `failsLeaving`   s'+    context "when stream is empty" $+      it "signals correct parse error" $+        property $ \ch ->+          prs  (char ch) "" `shouldFailWith` err posI (ueof <> etok ch)++  describe "char'" $ do+    context "when stream begins with the character specified as argument" $+      it "parses the character" $+        property $ \ch s -> do+          let sl = toLower ch : s+              su = toUpper ch : s+          prs  (char' ch) sl `shouldParse`     toLower ch+          prs  (char' ch) su `shouldParse`     toUpper ch+          prs' (char' ch) sl `succeedsLeaving` s+          prs' (char' ch) su `succeedsLeaving` s+    context "when stream does not begin with the character specified as argument" $+      it "signals correct parse error" $+        property $ \ch ch' s -> toLower ch /= toLower ch' ==> do+          let s' = ch' : s+              ms = utok ch' <> etok (toLower ch) <> etok (toUpper ch)+          prs  (char' ch) s' `shouldFailWith` err posI ms+          prs' (char' ch) s' `failsLeaving`   s'+    context "when stream is empty" $+      it "signals correct parse error" $+        property $ \ch -> do+          let ms = ueof <> etok (toLower ch) <> etok (toUpper ch)+          prs  (char' ch) "" `shouldFailWith` err posI ms++  describe "anyChar" $ do+    context "when stream is not empty" $+      it "succeeds consuming next character in the stream" $+        property $ \ch s -> do+          let s' = ch : s+          prs  anyChar s' `shouldParse`     ch+          prs' anyChar s' `succeedsLeaving` s+    context "when stream is empty" $+      it "signals correct parse error" $+        prs anyChar "" `shouldFailWith` err posI (ueof <> elabel "character")++  describe "oneOf" $ do+    context "when stream begins with one of specified characters" $+      it "parses the character" $+        property $ \chs' n s -> do+          let chs = getNonEmpty chs'+              ch  = chs !! (getNonNegative n `rem` length chs)+              s'  = ch : s+          prs  (oneOf chs) s' `shouldParse`     ch+          prs' (oneOf chs) s' `succeedsLeaving` s+    context "when stream does not begin with any of specified characters" $+      it "signals correct parse error" $+        property $ \chs ch s  -> ch `notElem` (chs :: String) ==> do+          let s' = ch : s+          prs  (oneOf chs) s' `shouldFailWith` err posI (utok ch)+          prs' (oneOf chs) s' `failsLeaving`   s'+    context "when stream is empty" $+      it "signals correct parse error" $+        property $ \chs ->+          prs (oneOf (chs :: String)) "" `shouldFailWith` err posI ueof++  describe "oneOf'" $ do+    context "when stream begins with one of specified characters" $+      it "parses the character" $+        property $ \chs' n s -> do+          let chs = getNonEmpty chs'+              ch  = chs !! (getNonNegative n `rem` length chs)+              sl  = toLower ch : s+              su  = toUpper ch : s+          prs  (oneOf' chs) sl `shouldParse`     toLower ch+          prs  (oneOf' chs) su `shouldParse`     toUpper ch+          prs' (oneOf' chs) sl `succeedsLeaving` s+          prs' (oneOf' chs) su `succeedsLeaving` s+    context "when stream does not begin with any of specified characters" $+      it "signals correct parse error" $+        property $ \chs ch s  -> ch `notElemi` (chs :: String) ==> do+          let s' = ch : s+          prs  (oneOf' chs) s' `shouldFailWith` err posI (utok ch)+          prs' (oneOf' chs) s' `failsLeaving`   s'+    context "when stream is empty" $+      it "signals correct parse error" $+        property $ \chs ->+          prs (oneOf' (chs :: String)) "" `shouldFailWith` err posI ueof++  describe "noneOf" $ do+    context "when stream does not begin with any of specified characters" $+      it "parses the character" $+        property $ \chs ch s  -> ch `notElem` (chs :: String) ==> do+          let s' = ch : s+          prs  (noneOf chs) s' `shouldParse`     ch+          prs' (noneOf chs) s' `succeedsLeaving` s+    context "when stream begins with one of specified characters" $+      it "signals correct parse error" $+        property $ \chs' n s -> do+          let chs = getNonEmpty chs'+              ch  = chs !! (getNonNegative n `rem` length chs)+              s'  = ch : s+          prs  (noneOf chs) s' `shouldFailWith` err posI (utok ch)+          prs' (noneOf chs) s' `failsLeaving`   s'+    context "when stream is empty" $+      it "signals correct parse error" $+        property $ \chs ->+          prs (noneOf (chs :: String)) "" `shouldFailWith` err posI ueof++  describe "noneOf'" $ do+    context "when stream does not begin with any of specified characters" $+      it "parses the character" $+        property $ \chs ch s  -> ch `notElemi` (chs :: String) ==> do+          let sl  = toLower ch : s+              su  = toUpper ch : s+          prs  (noneOf' chs) sl `shouldParse`     toLower ch+          prs  (noneOf' chs) su `shouldParse`     toUpper ch+          prs' (noneOf' chs) sl `succeedsLeaving` s+          prs' (noneOf' chs) su `succeedsLeaving` s+    context "when stream begins with one of specified characters" $+      it "signals correct parse error" $+        property $ \chs' n s -> do+          let chs = getNonEmpty chs'+              ch  = chs !! (getNonNegative n `rem` length chs)+              s'  = ch : s+          prs  (noneOf' chs) s' `shouldFailWith` err posI (utok ch)+          prs' (noneOf' chs) s' `failsLeaving`   s'+    context "when stream is empty" $+      it "signals correct parse error" $+        property $ \chs ->+          prs (noneOf' (chs :: String)) "" `shouldFailWith` err posI ueof++  describe "string" $ do+    context "when stream is prefixed with given string" $+      it "parses the string" $+        property $ \str s -> do+          let s' = str ++ s+          prs  (string str) s' `shouldParse`     str+          prs' (string str) s' `succeedsLeaving` s+    context "when stream is not prefixed with given string" $+      it "signals correct parse error" $+        property $ \str s -> not (str `isPrefixOf` s) ==> do+          let n = length (takeWhile (uncurry (==)) (zip str s)) + 1+              common = take n s+          prs  (string str) s `shouldFailWith` err posI (utoks common <> etoks str)++  describe "string'" $ do+    context "when stream is prefixed with given string" $+      it "parses the string" $+        property $ \str s ->+          forAll (fuzzyCase str) $ \str' -> do+            let s' = str' ++ s+            prs  (string' str) s' `shouldParse`     str'+            prs' (string' str) s' `succeedsLeaving` s+    context "when stream is not prefixed with given string" $+      it "signals correct parse error" $+        property $ \str s -> not (str `isPrefixOfI` s) ==> do+          let n = length (takeWhile (uncurry casei) (zip str s)) + 1+              common = take n s+          prs  (string' str) s `shouldFailWith` err posI (utoks common <> etoks str)++----------------------------------------------------------------------------+-- Helpers++checkStrLit :: String -> String -> Parsec Dec String String -> SpecWith ()+checkStrLit name ts p = do+  context ("when stream begins with " ++ name) $+    it ("parses the " ++ name) $+      property $ \s -> do+        let s' = ts ++ s+        prs  p s' `shouldParse`     ts+        prs' p s' `succeedsLeaving` s+  context ("when stream does not begin with " ++ name) $+    it "signals correct parse error" $+      property $ \ch s -> ch /= head ts ==> do+       let s' = ch : s+       prs  p s' `shouldFailWith` err posI (utok ch <> etoks ts)+       prs' p s' `failsLeaving`   s'+  context "when stream is empty" $+    it "signals correct parse error" $+      prs p "" `shouldFailWith` err posI (ueof <> etoks ts)++checkCharPred :: String -> (Char -> Bool) -> Parsec Dec String Char -> SpecWith ()+checkCharPred name f p = do+  context ("when stream begins with " ++ name) $+    it ("parses the " ++ name) $+      property $ \ch s -> f ch ==> do+        let s' = ch : s+        prs  p s' `shouldParse`     ch+        prs' p s' `succeedsLeaving` s+  context ("when stream does not begin with " ++ name) $+    it "signals correct parse error" $+      property $ \ch s -> not (f ch) ==> do+       let s' = ch : s+       prs  p s' `shouldFailWith` err posI (utok ch <> elabel name)+       prs' p s' `failsLeaving`   s'+  context "when stream is empty" $+    it "signals correct parse error" $+      prs p "" `shouldFailWith` err posI (ueof <> elabel name)++checkCharRange :: String -> String -> Parsec Dec String Char -> SpecWith ()+checkCharRange name tchs p = do+  forM_ tchs $ \tch ->+    context ("when stream begins with " ++ showTokens (nes tch)) $+      it ("parses the " ++ showTokens (nes tch)) $+        property $ \s -> do+          let s' = tch : s+          prs  p s' `shouldParse`     tch+          prs' p s' `succeedsLeaving` s+  context ("when stream does not begin with " ++ name) $+    it "signals correct parse error" $+      property $ \ch s -> ch `notElem` tchs ==> do+       let s' = ch : s+       prs  p s' `shouldFailWith` err posI (utok ch <> elabel name)+       prs' p s' `failsLeaving`   s'+  context "when stream is empty" $+    it "signals correct parse error" $+      prs p "" `shouldFailWith` err posI (ueof <> elabel name)++-- | Randomly change the case in the given string.++fuzzyCase :: String -> Gen String+fuzzyCase s = zipWith f s <$> vector (length s)+  where f k True  = if isLower k then toUpper k else toLower k+        f k False = k++-- | Case-insensitive equality test for characters.++casei :: Char -> Char -> Bool+casei x y = toUpper x == toUpper y++-- | Case-insensitive 'elem'.++elemi :: Char -> String -> Bool+elemi c = any (casei c)++-- | Case-insensitive 'notElem'.++notElemi :: Char -> String -> Bool+notElemi c = not . elemi c++-- | The 'isPrefixOf' function takes two 'String's and returns 'True' iff+-- the first list is a prefix of the second with case-insensitive+-- comparison.++isPrefixOfI :: String -> String -> Bool+isPrefixOfI [] _  =  True+isPrefixOfI _  [] =  False+isPrefixOfI (x:xs) (y:ys) = x `casei` y && isPrefixOf xs ys
+ tests/Text/Megaparsec/CombinatorSpec.hs view
@@ -0,0 +1,256 @@+--+-- Tests for Megaparsec's generic parser combinators.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE MultiWayIf #-}++module Text.Megaparsec.CombinatorSpec (spec) where++import Control.Applicative+import Data.Char (isLetter, isDigit)+import Data.List (intersperse)+import Data.Maybe (fromMaybe, maybeToList, isNothing, fromJust)+import Data.Monoid+import Test.Hspec+import Test.Hspec.Megaparsec+import Test.Hspec.Megaparsec.AdHoc+import Test.QuickCheck+import Text.Megaparsec.Char+import Text.Megaparsec.Combinator++spec :: Spec+spec = do++  describe "between" . it "works" . property $ \pre c n' post -> do+    let p = between (string pre) (string post) (many (char c))+        n = getNonNegative n'+        b = length (takeWhile (== c) post)+        z = replicate n c+        s = pre ++ z ++ post+    if b > 0+      then prs_ p s `shouldFailWith` err (posN (length pre + n + b) s)+        ( etoks post <> etok c <>+          (if length post == b then ueof else utoks [post !! b]) )+      else prs_ p s `shouldParse` z++  describe "choice" . it "works" . property $ \cs' s' -> do+    let cs = getNonEmpty cs'+        p = choice (char <$> cs)+        s = [s']+    if s' `elem` cs+      then prs_ p s `shouldParse` s'+      else prs_ p s `shouldFailWith` err posI (utok s' <> mconcat (etok <$> cs))++  describe "count" . it "works" . property $ \n x' -> do+    let x = getNonNegative x'+        p = count n (char 'x')+        p' = count' n n (char 'x')+        s = replicate x 'x'+    prs_ p s `shouldBe` prs_ p' s++  describe "count'" . it "works" . property $ \m n x' -> do+    let x = getNonNegative x'+        p = count' m n (char 'x')+        s = replicate x 'x'+    if | n <= 0 || m > n ->+         if x == 0+           then prs_ p s `shouldParse` ""+           else prs_ p s `shouldFailWith` err posI (utok 'x' <> eeof)+       | m <= x && x <= n ->+         prs_ p s `shouldParse` s+       | x < m ->+         prs_ p s `shouldFailWith` err (posN x s) (ueof <> etok 'x')+       | otherwise ->+         prs_ p s `shouldFailWith` err (posN n s) (utok 'x' <> eeof)++  describe "eitherP" . it "works" . property $ \ch -> do+    let p = eitherP letterChar digitChar+        s = pure ch+    if | isLetter ch -> prs_ p s `shouldParse` Left ch+       | isDigit  ch -> prs_ p s `shouldParse` Right ch+       | otherwise   -> prs_ p s `shouldFailWith`+         err posI (utok ch <> elabel "letter" <> elabel "digit")++  describe "endBy" . it "works" . property $ \n' c -> do+    let n = getNonNegative n'+        p = endBy (char 'a') (char '-')+        s = intersperse '-' (replicate n 'a') ++ [c]+    if | c == 'a' && n == 0 ->+         prs_ p s `shouldFailWith` err (posN (1 :: Int) s) (ueof <> etok '-')+       | c == 'a' ->+         prs_ p s `shouldFailWith` err (posN (g n) s) (utok 'a' <> etok '-')+       | c == '-' && n == 0 ->+         prs_ p s `shouldFailWith` err posI (utok '-' <> etok 'a'<> eeof)+       | c /= '-' ->+         prs_ p s `shouldFailWith` err (posN (g n) s)+           ( utok c <>+             (if n > 0 then etok '-' else eeof) <>+             (if n == 0 then etok 'a' else mempty) )+       | otherwise -> prs_ p s `shouldParse` replicate n 'a'++  describe "endBy1" . it "works" . property $ \n' c -> do+    let n = getNonNegative n'+        p = endBy1 (char 'a') (char '-')+        s = intersperse '-' (replicate n 'a') ++ [c]+    if | c == 'a' && n == 0 ->+         prs_ p s `shouldFailWith` err (posN (1 :: Int) s) (ueof <> etok '-')+       | c == 'a' ->+         prs_ p s `shouldFailWith` err (posN (g n) s) (utok 'a' <> etok '-')+       | c == '-' && n == 0 ->+         prs_ p s `shouldFailWith` err posI (utok '-' <> etok 'a')+       | c /= '-' ->+         prs_ p s `shouldFailWith` err (posN (g n) s)+           ( utok c <>+             (if n > 0 then etok '-' else mempty) <>+             (if n == 0 then etok 'a' else mempty) )+       | otherwise -> prs_ p s `shouldParse` replicate n 'a'++  describe "manyTill" . it "works" . property $ \a' b' c' -> do+    let [a,b,c] = getNonNegative <$> [a',b',c']+        p = (,) <$> manyTill letterChar (char 'c') <*> many letterChar+        s = abcRow a b c+    if c == 0+      then prs_ p s `shouldFailWith` err (posN (a + b) s)+           (ueof <> etok 'c' <> elabel "letter")+      else let (pre, post) = break (== 'c') s+           in prs_ p s `shouldParse` (pre, drop 1 post)++  describe "someTill" . it "works" . property $ \a' b' c' -> do+    let [a,b,c] = getNonNegative <$> [a',b',c']+        p = (,) <$> someTill letterChar (char 'c') <*> many letterChar+        s = abcRow a b c+    if | null s ->+         prs_ p s `shouldFailWith` err posI (ueof <> elabel "letter")+       | c == 0 ->+         prs_ p s `shouldFailWith` err (posN (a + b) s)+           (ueof <> etok 'c' <> elabel "letter")+       | s == "c" ->+         prs_ p s `shouldFailWith` err+           (posN (1 :: Int) s) (ueof <> etok 'c' <> elabel "letter")+       | head s == 'c' ->+         prs_ p s `shouldParse` ("c", drop 2 s)+       | otherwise ->+         let (pre, post) = break (== 'c') s+         in prs_ p s `shouldParse` (pre, drop 1 post)++  describe "option" . it "works" . property $ \d a s -> do+    let p = option d (string a)+        p' = fromMaybe d <$> optional (string a)+    prs_ p s `shouldBe` prs_ p' s++  describe "sepBy" . it "works" . property $ \n' c' -> do+    let n = getNonNegative n'+        c = fromJust c'+        p = sepBy (char 'a') (char '-')+        s = intersperse '-' (replicate n 'a') ++ maybeToList c'+    if | isNothing c' ->+         prs_ p s `shouldParse` replicate n 'a'+       | c == 'a' && n == 0 ->+         prs_ p s `shouldParse` "a"+       | n == 0 ->+         prs_ p s `shouldFailWith` err posI+           (utok c <> etok 'a' <> eeof)+       | c == '-' ->+         prs_ p s `shouldFailWith` err (posN (length s) s)+           (ueof <> etok 'a')+       | otherwise ->+         prs_ p s `shouldFailWith` err (posN (g n) s)+           (utok c <> etok '-' <> eeof)++  describe "sepBy1" . it "works" . property $ \n' c' -> do+    let n = getNonNegative n'+        c = fromJust c'+        p = sepBy1 (char 'a') (char '-')+        s = intersperse '-' (replicate n 'a') ++ maybeToList c'+    if | isNothing c' && n >= 1 ->+         prs_ p s `shouldParse` replicate n 'a'+       | isNothing c' ->+         prs_ p s `shouldFailWith` err posI (ueof <> etok 'a')+       | c == 'a' && n == 0 ->+         prs_ p s `shouldParse` "a"+       | n == 0 ->+         prs_ p s `shouldFailWith` err posI (utok c <> etok 'a')+       | c == '-' ->+         prs_ p s `shouldFailWith` err (posN (length s) s) (ueof <> etok 'a')+       | otherwise ->+         prs_ p s `shouldFailWith` err (posN (g n) s) (utok c <> etok '-' <> eeof)++  describe "sepEndBy" . it "works" . property $ \n' c' -> do+    let n = getNonNegative n'+        c = fromJust c'+        p = sepEndBy (char 'a') (char '-')+        a = replicate n 'a'+        s = intersperse '-' (replicate n 'a') ++ maybeToList c'+    if | isNothing c' ->+         prs_ p s `shouldParse` a+       | c == 'a' && n == 0 ->+         prs_ p s `shouldParse` "a"+       | n == 0 ->+         prs_ p s `shouldFailWith` err posI (utok c <> etok 'a' <> eeof)+       | c == '-' ->+         prs_ p s `shouldParse` a+       | otherwise ->+         prs_ p s `shouldFailWith` err (posN (g n) s) (utok c <> etok '-' <> eeof)++  describe "sepEndBy1" . it "works" . property $ \n' c' -> do+    let n = getNonNegative n'+        c = fromJust c'+        p = sepEndBy1 (char 'a') (char '-')+        a = replicate n 'a'+        s = intersperse '-' (replicate n 'a') ++ maybeToList c'+    if | isNothing c' && n >= 1 ->+         prs_ p s `shouldParse` a+       | isNothing c' ->+         prs_ p s `shouldFailWith` err posI (ueof <> etok 'a')+       | c == 'a' && n == 0 ->+         prs_ p s `shouldParse` "a"+       | n == 0 ->+         prs_ p s `shouldFailWith` err posI (utok c <> etok 'a')+       | c == '-' ->+         prs_ p s `shouldParse` a+       | otherwise ->+         prs_ p s `shouldFailWith` err (posN (g n) s) (utok c <> etok '-' <> eeof)++  describe "skipMany" . it "works" . property $ \c n' a -> do+    let p = skipMany (char c) *> string a+        n = getNonNegative n'+        p' = many (char c) >> string a+        s = replicate n c ++ a+    prs_ p s `shouldBe` prs_ p' s++  describe "skipSome" . it "works" . property $ \c n' a -> do+    let p = skipSome (char c) *> string a+        n = getNonNegative n'+        p' = some (char c) >> string a+        s = replicate n c ++ a+    prs_ p s `shouldBe` prs_ p' s++----------------------------------------------------------------------------+-- Helpers++g :: Int -> Int+g x = x + if x > 0 then x - 1 else 0
+ tests/Text/Megaparsec/ErrorSpec.hs view
@@ -0,0 +1,223 @@+--+-- Tests for Megaparsec's parse errors.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE CPP              #-}+{-# OPTIONS -fno-warn-orphans #-}++module Text.Megaparsec.ErrorSpec (spec) where++import Data.Char (isControl, isSpace)+import Data.Function (on)+import Data.List (isInfixOf, isSuffixOf)+import Data.List.NonEmpty (NonEmpty (..))+import Data.Monoid+import Data.Set (Set)+import Test.Hspec+import Test.QuickCheck+import Text.Megaparsec.Error+import Text.Megaparsec.Pos+import qualified Data.List.NonEmpty as NE+import qualified Data.Semigroup     as S+import qualified Data.Set           as E++#if !MIN_VERSION_base(4,8,0)+import Data.Foldable (Foldable, all)+import Prelude hiding (all)+#else+import Control.Exception (Exception (..))+#endif++type PE = ParseError Char Dec++spec :: Spec+spec = do++  describe "Semigroup instance of ParseError" $+    it "associativity" $+      property $ \x y z ->+        (x S.<> y) S.<> z === (x S.<> (y S.<> z) :: PE)++  describe "Monoid instance of ParseError" $ do+    it "left identity" $+      property $ \x ->+        mempty <> x === (x :: PE)+    it "right identity" $+      property $ \x ->+        x <> mempty === (x :: PE)+    it "associativity" $+      property $ \x y z ->+        (x <> y) <> z === (x <> (y <> z) :: PE)++  describe "Read and Show instances of ParseError" $+    it "printed representation of ParseError can be read back" $+      property $ \x ->+        read (show x) === (x :: PE)++  describe "error merging with (<>)" $ do+    it "selects greater source position" $+      property $ \x y ->+        errorPos (x <> y :: PE) === max (errorPos x) (errorPos y)+    it "merges unexpected items correctly" $+      property (checkMergedItems errorUnexpected)+    it "merges expected items correctly" $+      property (checkMergedItems errorExpected)+    it "merges custom items correctly" $+      property (checkMergedItems errorCustom)++  describe "showTokens (Char instance)" $ do+    let f x y = showTokens (NE.fromList x) `shouldBe` y+    it "shows CRLF newline correctly"+      (f "\r\n" "crlf newline")+    it "shows null byte correctly"+      (f "\NUL" "null (control character)")+    it "shows start of heading correctly"+      (f "\SOH" "start of heading (control character)")+    it "shows start of text correctly"+      (f "\STX" "start of text (control character)")+    it "shows end of text correctly"+      (f "\ETX" "end of text (control character)")+    it "shows end of transmission correctly"+      (f "\EOT" "end of transmission (control character)")+    it "shows enquiry correctly"+      (f "\ENQ" "enquiry (control character)")+    it "shows acknowledge correctly"+      (f "\ACK" "acknowledge (control character)")+    it "shows bell correctly"+      (f "\BEL" "bell (control character)")+    it "shows backspace correctly"+      (f "\BS" "backspace")+    it "shows tab correctly"+      (f "\t" "tab")+    it "shows newline correctly"+      (f "\n" "newline")+    it "shows vertical tab correctly"+      (f "\v" "vertical tab")+    it "shows form feed correctly"+      (f "\f" "form feed (control character)")+    it "shows carriage return correctly"+      (f "\r" "carriage return")+    it "shows shift out correctly"+      (f "\SO" "shift out (control character)")+    it "shows shift in correctly"+      (f "\SI" "shift in (control character)")+    it "shows data link escape correctly"+      (f "\DLE" "data link escape (control character)")+    it "shows device control one correctly"+      (f "\DC1" "device control one (control character)")+    it "shows device control two correctly"+      (f "\DC2" "device control two (control character)")+    it "shows device control three correctly"+      (f "\DC3" "device control three (control character)")+    it "shows device control four correctly"+      (f "\DC4" "device control four (control character)")+    it "shows negative acknowledge correctly"+      (f "\NAK" "negative acknowledge (control character)")+    it "shows synchronous idle correctly"+      (f "\SYN" "synchronous idle (control character)")+    it "shows end of transmission block correctly"+      (f "\ETB" "end of transmission block (control character)")+    it "shows cancel correctly"+      (f "\CAN" "cancel (control character)")+    it "shows end of medium correctly"+      (f "\EM"  "end of medium (control character)")+    it "shows substitute correctly"+      (f "\SUB" "substitute (control character)")+    it "shows escape correctly"+      (f "\ESC" "escape (control character)")+    it "shows file separator correctly"+      (f "\FS"  "file separator (control character)")+    it "shows group separator correctly"+      (f "\GS"  "group separator (control character)")+    it "shows record separator correctly"+      (f "\RS"  "record separator (control character)")+    it "shows unit separator correctly"+      (f "\US"  "unit separator (control character)")+    it "shows delete correctly"+      (f "\DEL" "delete (control character)")+    it "shows space correctly"+      (f " "    "space")+    it "shows non-breaking space correctly"+      (f "\160" "non-breaking space")+    it "shows other single characters in single quotes" $+      property $ \ch ->+        not (isControl ch) && not (isSpace ch) ==>+          showTokens (ch :| []) === ['\'',ch,'\'']+    it "shows strings in double quotes" $+      property $ \str ->+        (length str > 1) && (str /= "\r\n") ==>+          showTokens (NE.fromList str) === ("\"" ++ str ++"\"")++  describe "parseErrorPretty" $ do+    it "shows unknown ParseError correctly" $+      parseErrorPretty (mempty :: PE) `shouldBe` "1:1:\nunknown parse error\n"+    it "result always ends with a newline" $+      property $ \x ->+        parseErrorPretty (x :: PE) `shouldSatisfy` ("\n" `isSuffixOf`)+    it "result contains representation of source pos stack" $+      property (contains errorPos sourcePosPretty)+    it "result contains representation of unexpected items" $+      property (contains errorUnexpected showErrorComponent)+    it "result contains representation of expected items" $+      property (contains errorExpected showErrorComponent)+    it "result contains representation of custom items" $+      property (contains errorCustom showErrorComponent)++  describe "sourcePosStackPretty" $+    it "result never ends with a newline " $+      property $ \x ->+        let pos = errorPos (x :: PE)+        in sourcePosStackPretty pos `shouldNotSatisfy` ("\n" `isSuffixOf`)++  describe "parseErrorTextPretty" $ do+    it "shows unknown ParseError correctly" $+      parseErrorTextPretty (mempty :: PE) `shouldBe` "unknown parse error\n"+    it "result always ends with a newline" $+      property $ \x ->+        parseErrorTextPretty (x :: PE) `shouldSatisfy` ("\n" `isSuffixOf`)++#if MIN_VERSION_base(4,8,0)+  describe "displayException" $+    it "produces the same result as parseErrorPretty" $+      property $ \x ->+        displayException x `shouldBe` parseErrorPretty (x :: PE)+#endif++----------------------------------------------------------------------------+-- Helpers++checkMergedItems :: (Ord a, Show a) => (PE -> Set a) -> PE -> PE -> Property+checkMergedItems f e1 e2 = f (e1 <> e2) === r+  where r = case (compare `on` errorPos) e1 e2 of+              LT -> f e2+              EQ -> (E.union `on` f) e1 e2+              GT -> f e1++contains :: Foldable t => (PE -> t a) -> (a -> String) -> PE -> Property+contains g r e = property (all f (g e))+  where rendered = parseErrorPretty e+        f x = r x `isInfixOf` rendered
+ tests/Text/Megaparsec/ExprSpec.hs view
@@ -0,0 +1,187 @@+--+-- Tests for Megaparsec's expression parsers.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE CPP              #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies     #-}++module Text.Megaparsec.ExprSpec (spec) where++import Control.Applicative (some, (<|>))+import Data.Monoid ((<>))+import Test.Hspec+import Test.Hspec.Megaparsec+import Test.Hspec.Megaparsec.AdHoc+import Test.QuickCheck+import Text.Megaparsec.Char+import Text.Megaparsec.Combinator+import Text.Megaparsec.Expr+import Text.Megaparsec.Prim++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>), (<*), (<*>), (*>), pure)+#endif++spec :: Spec+spec =+  describe "makeExprParser" $ do+    context "when given valid rendered AST" $+      it "can parse it back" $+        property $ \node -> do+          let s = showNode node+          prs  expr s `shouldParse`     node+          prs' expr s `succeedsLeaving` ""+    context "when stream in empty" $+      it "signals correct parse error" $+        prs (expr <* eof) "" `shouldFailWith` err posI (ueof <> elabel "term")+    context "when term is missing" $+      it "signals correct parse error" $ do+        let p = expr <* eof+            n = 1 :: Integer+        prs p "-" `shouldFailWith` err (posN n "-") (ueof <> elabel "term")+        prs p "(" `shouldFailWith` err (posN n "(") (ueof <> elabel "term")+        prs p "*" `shouldFailWith` err posI (utok '*' <> elabel "term")+    context "operator is missing" $+      it "signals correct parse error" $+        property $ \a b -> do+          let p = expr <* eof+              a' = inParens a+              n  = length a' + 1+              s  = a'  ++ " " ++ inParens b+              c  = s !! n+          if c == '-'+            then prs p s `shouldParse` Sub a b+            else prs p s `shouldFailWith`+                 err (posN n s) (utok c <> eeof <> elabel "operator")++-- Algebraic structures to build abstract syntax tree of our expression.++data Node+  = Val Integer   -- ^ literal value+  | Neg Node      -- ^ negation (prefix unary)+  | Fac Node      -- ^ factorial (postfix unary)+  | Mod Node Node -- ^ modulo+  | Sum Node Node -- ^ summation (addition)+  | Sub Node Node -- ^ subtraction+  | Pro Node Node -- ^ product+  | Div Node Node -- ^ division+  | Exp Node Node -- ^ exponentiation+    deriving (Eq, Show)++instance Enum Node where+  fromEnum (Val _)   = 0+  fromEnum (Neg _)   = 0+  fromEnum (Fac _)   = 0+  fromEnum (Mod _ _) = 0+  fromEnum (Exp _ _) = 1+  fromEnum (Pro _ _) = 2+  fromEnum (Div _ _) = 2+  fromEnum (Sum _ _) = 3+  fromEnum (Sub _ _) = 3+  toEnum   _         = error "Oops!"++instance Ord Node where+  x `compare` y = fromEnum x `compare` fromEnum y++showNode :: Node -> String+showNode (Val x)     = show x+showNode n@(Neg x)   = "-" ++ showGT n x+showNode n@(Fac x)   = showGT n x ++ "!"+showNode n@(Mod x y) = showGE n x ++ " % " ++ showGE n y+showNode n@(Sum x y) = showGT n x ++ " + " ++ showGE n y+showNode n@(Sub x y) = showGT n x ++ " - " ++ showGE n y+showNode n@(Pro x y) = showGT n x ++ " * " ++ showGE n y+showNode n@(Div x y) = showGT n x ++ " / " ++ showGE n y+showNode n@(Exp x y) = showGE n x ++ " ^ " ++ showGT n y++showGT :: Node -> Node -> String+showGT parent node = (if node > parent then showCmp else showNode) node++showGE :: Node -> Node -> String+showGE parent node = (if node >= parent then showCmp else showNode) node++showCmp :: Node -> String+showCmp node = (if fromEnum node == 0 then showNode else inParens) node++inParens :: Node -> String+inParens x = "(" ++ showNode x ++ ")"++instance Arbitrary Node where+  arbitrary = sized arbitraryN0++arbitraryN0 :: Int -> Gen Node+arbitraryN0 n = frequency [ (1, Mod <$> leaf <*> leaf)+                          , (9, arbitraryN1 n) ]+  where leaf = arbitraryN1 (n `div` 2)++arbitraryN1 :: Int -> Gen Node+arbitraryN1 n =+ frequency [ (1, Neg <$> arbitraryN2 n)+           , (1, Fac <$> arbitraryN2 n)+           , (7, arbitraryN2 n)]++arbitraryN2 :: Int -> Gen Node+arbitraryN2 0 = Val . getNonNegative <$> arbitrary+arbitraryN2 n = elements [Sum,Sub,Pro,Div,Exp] <*> leaf <*> leaf+  where leaf = arbitraryN0 (n `div` 2)++-- Some helpers are put here since we don't want to depend on+-- "Text.Megaparsec.Lexer".++lexeme :: (MonadParsec e s m, Token s ~ Char) => m a -> m a+lexeme p = p <* hidden space++symbol :: (MonadParsec e s m, Token s ~ Char) => String -> m String+symbol = lexeme . string++parens :: (MonadParsec e s m, Token s ~ Char) => m a -> m a+parens = between (symbol "(") (symbol ")")++integer :: (MonadParsec e s m, Token s ~ Char) => m Integer+integer = lexeme (read <$> some digitChar <?> "integer")++-- Here we use a table of operators that makes use of all features of+-- 'makeExprParser'. Then we generate abstract syntax tree (AST) of complex+-- but valid expressions and render them to get their textual+-- representation.++expr :: (MonadParsec e s m, Token s ~ Char) => m Node+expr = makeExprParser term table++term :: (MonadParsec e s m, Token s ~ Char) => m Node+term = parens expr <|> (Val <$> integer) <?> "term"++table :: (MonadParsec e s m, Token s ~ Char) => [[Operator m Node]]+table = [ [ Prefix  (symbol "-" *> pure Neg)+          , Postfix (symbol "!" *> pure Fac)+          , InfixN  (symbol "%" *> pure Mod) ]+        , [ InfixR  (symbol "^" *> pure Exp) ]+        , [ InfixL  (symbol "*" *> pure Pro)+          , InfixL  (symbol "/" *> pure Div) ]+        , [ InfixL  (symbol "+" *> pure Sum)+          , InfixL  (symbol "-" *> pure Sub)] ]
+ tests/Text/Megaparsec/LexerSpec.hs view
@@ -0,0 +1,486 @@+--+-- Tests for Megaparsec's lexer.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE CPP              #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiWayIf       #-}+{-# LANGUAGE TupleSections    #-}+{-# LANGUAGE TypeFamilies     #-}++module Text.Megaparsec.LexerSpec (spec) where++import Control.Applicative+import Control.Monad (void)+import Data.Char hiding (ord)+import Data.List (isInfixOf)+import Data.Maybe+import Data.Monoid ((<>))+import Data.Scientific (fromFloatDigits)+import Numeric (showInt, showHex, showOct)+import Test.Hspec+import Test.Hspec.Megaparsec+import Test.Hspec.Megaparsec.AdHoc+import Test.QuickCheck+import Text.Megaparsec.Error+import Text.Megaparsec.Lexer+import Text.Megaparsec.Pos+import Text.Megaparsec.Prim+import Text.Megaparsec.String+import qualified Text.Megaparsec.Char as C++spec :: Spec+spec = do++  describe "space" $+    it "consumes any sort of white space" $+      property $ forAll mkWhiteSpace $ \s -> do+        prs  scn s `shouldParse` ()+        prs' scn s `succeedsLeaving` ""++  describe "symbol" $+    context "when stream begins with the symbol" $+      it "parses the symbol and trailing whitespace" $+        property $ forAll mkSymbol $ \s -> do+          let p = symbol scn y+              y = takeWhile (not . isSpace) s+          prs  p s `shouldParse` y+          prs' p s `succeedsLeaving` ""++  describe "symbol'" $+    context "when stream begins with the symbol" $+      it "parses the symbol and trailing whitespace" $+        property $ forAll mkSymbol $ \s -> do+          let p = symbol' scn (toUpper <$> y)+              y = takeWhile (not . isSpace) s+          prs  p s `shouldParse` y+          prs' p s `succeedsLeaving` ""++  describe "skipLineComment" $+    context "when there is no newline at the end of line" $+      it "is picked up successfully" $ do+        let p = space (void C.spaceChar) (skipLineComment "//") empty <* eof+            s = "  // this line comment doesn't have a newline at the end "+        prs  p s `shouldParse` ()+        prs' p s `succeedsLeaving` ""++  describe "skipBlockCommentNested" $+    context "when it runs into nested block comments" $+      it "parses them all right" $ do+        let p = space (void C.spaceChar) empty+              (skipBlockCommentNested "/*" "*/") <* eof+            s = " /* foo bar /* baz */ quux */ "+        prs  p s `shouldParse` ()+        prs' p s `succeedsLeaving` ""++  describe "indentLevel" $+    it "returns current indentation level (column)" $+      property $ \pos -> do+        let p = setPosition pos *> indentLevel+        prs p "" `shouldParse` sourceColumn pos++  describe "incorrectIndent" $+    it "signals correct parse error" $+      property $ \ord ref actual -> do+        let p :: Parser ()+            p = incorrectIndent ord ref actual+        prs p "" `shouldFailWith` err posI (ii ord ref actual)++  describe "indentGuard" $+    it "works as intended" $+      property $ \n -> do+        let mki = mkIndent sbla (getSmall $ getNonNegative n)+        forAll ((,,) <$> mki <*> mki <*> mki) $ \(l0,l1,l2) -> do+          let (col0, col1, col2) = (getCol l0, getCol l1, getCol l2)+              fragments = [l0,l1,l2]+              g x = sum (length <$> take x fragments)+              s = concat fragments+              p  = ip GT pos1 >>=+                \x -> sp >> ip EQ x >> sp >> ip GT x >> sp >> scn+              ip = indentGuard scn+              sp = void (symbol sc sbla <* C.eol)+          if | col0 <= pos1 ->+               prs p s `shouldFailWith` err posI (ii GT pos1 col0)+             | col1 /= col0 ->+               prs p s `shouldFailWith` err (posN (getIndent l1 + g 1) s) (ii EQ col0 col1)+             | col2 <= col0 ->+               prs p s `shouldFailWith` err (posN (getIndent l2 + g 2) s) (ii GT col0 col2)+             | otherwise    ->+               prs p s `shouldParse` ()++  describe "nonIdented" $+    it "works as intended" $+      property $ forAll (mkIndent sbla 0) $ \s -> do+        let p = nonIndented scn (symbol scn sbla)+            i = getIndent s+        if i == 0+          then prs p s `shouldParse` sbla+          else prs p s `shouldFailWith` err (posN i s) (ii EQ pos1 (getCol s))++  describe "indentBlock" $ do+    it "works as indented" $+      property $ \mn'' -> do+        let mkBlock = do+              l0 <- mkIndent sbla 0+              l1 <- mkIndent sblb ib+              l2 <- mkIndent sblc (ib + 2)+              l3 <- mkIndent sblb ib+              l4 <- mkIndent' sblc (ib + 2)+              return (l0,l1,l2,l3,l4)+            ib  = fromMaybe 2 mn'+            mn' = getSmall . getPositive <$> mn''+            mn  = unsafePos . fromIntegral <$> mn'+        forAll mkBlock $ \(l0,l1,l2,l3,l4) -> do+          let (col0, col1, col2, col3, col4) =+                (getCol l0, getCol l1, getCol l2, getCol l3, getCol l4)+              fragments = [l0,l1,l2,l3,l4]+              g x = sum (length <$> take x fragments)+              s = concat fragments+              p = lvla <* eof+              lvla = indentBlock scn $ IndentMany mn      (l sbla) lvlb <$ b sbla+              lvlb = indentBlock scn $ IndentSome Nothing (l sblb) lvlc <$ b sblb+              lvlc = indentBlock scn $ IndentNone                  sblc <$ b sblc+              b    = symbol sc+              l x  = return . (x,)+              ib'  = unsafePos (fromIntegral ib)+          if | col1 <= col0 -> prs p s `shouldFailWith`+               err (posN (getIndent l1 + g 1) s) (utok (head sblb) <> eeof)+             | isJust mn && col1 /= ib' -> prs p s `shouldFailWith`+               err (posN (getIndent l1 + g 1) s) (ii EQ ib' col1)+             | col2 <= col1 -> prs p s `shouldFailWith`+               err (posN (getIndent l2 + g 2) s) (ii GT col1 col2)+             | col3 == col2 -> prs p s `shouldFailWith`+               err (posN (getIndent l3 + g 3) s) (utok (head sblb) <> etoks sblc)+             | col3 <= col0 -> prs p s `shouldFailWith`+               err (posN (getIndent l3 + g 3) s) (utok (head sblb) <> eeof)+             | col3 < col1 -> prs p s `shouldFailWith`+               err (posN (getIndent l3 + g 3) s) (ii EQ col1 col3)+             | col3 > col1 -> prs p s `shouldFailWith`+               err (posN (getIndent l3 + g 3) s) (ii EQ col2 col3)+             | col4 <= col3 -> prs p s `shouldFailWith`+               err (posN (getIndent l4 + g 4) s) (ii GT col3 col4)+             | otherwise -> prs p s `shouldParse`+               (sbla, [(sblb, [sblc]), (sblb, [sblc])])+    it "IndentMany works as intended" $+      property $ forAll (mkIndent sbla 0) $ \s -> do+        let p    = lvla+            lvla = indentBlock scn $ IndentMany Nothing (l sbla) lvlb <$ b sbla+            lvlb = b sblb+            b    = symbol sc+            l x  = return . (x,)+        prs  p s `shouldParse` (sbla, [])+        prs' p s `succeedsLeaving` ""++  describe "lineFold" $+    it "works as intended" $+      property $ do+        let mkFold = do+              l0 <- mkInterspace sbla 0+              l1 <- mkInterspace sblb 1+              l2 <- mkInterspace sblc 1+              return (l0,l1,l2)+        forAll mkFold $ \(l0,l1,l2) -> do+          let p = lineFold scn $ \sc' -> do+                a <- symbol sc' sbla+                b <- symbol sc' sblb+                c <- symbol scn sblc+                return (a, b, c)+              getEnd x = last x == '\n'+              fragments = [l0,l1,l2]+              g x = sum (length <$> take x fragments)+              s = concat fragments+              (col0, col1, col2) = (getCol l0, getCol l1, getCol l2)+              (end0, end1)       = (getEnd l0, getEnd l1)+          if | end0 && col1 <= col0 -> prs p s `shouldFailWith`+               err (posN (getIndent l1 + g 1) s) (ii GT col0 col1)+             | end1 && col2 <= col0 -> prs p s `shouldFailWith`+               err (posN (getIndent l2 + g 2) s) (ii GT col0 col2)+             | otherwise -> prs p s `shouldParse` (sbla, sblb, sblc)++  describe "charLiteral" $ do+    context "when stream begins with a literal character" $+      it "parses it" $+        property $ \ch -> do+          let p = charLiteral+              s = showLitChar ch ""+          prs  p s `shouldParse` ch+          prs' p s `succeedsLeaving` ""+    context "when stream does not begin with a literal character" $+      it "signals correct parse error" $ do+        let p = charLiteral+            s = "\\"+        prs  p s `shouldFailWith` err posI (utok '\\' <> elabel "literal character")+        prs' p s `failsLeaving` s+    context "when stream is empty" $+      it "signals correct parse error" $ do+        let p = charLiteral+        prs p "" `shouldFailWith` err posI (ueof <> elabel "literal character")++  describe "integer" $ do+    context "when stream begins with decimal digits" $+      it "they are parsed as an integer" $+        property $ \n' -> do+          let p = integer+              n = getNonNegative n'+              s = showInt n ""+          prs  p s `shouldParse` n+          prs' p s `succeedsLeaving` ""+    context "when stream does not begin with decimal digits" $+      it "signals correct parse error" $+        property $ \a as -> not (isDigit a) ==> do+          let p = integer+              s = a : as+          prs  p s `shouldFailWith` err posI (utok a <> elabel "integer")+    context "when stream is empty" $+      it "signals correct parse error" $+        prs integer "" `shouldFailWith`+          err posI (ueof <> elabel "integer")++  describe "decimal" $ do+    context "when stream begins with decimal digits" $+      it "they are parsed as an integer" $+        property $ \n' -> do+          let p = decimal+              n = getNonNegative n'+              s = showInt n ""+          prs  p s `shouldParse` n+          prs' p s `succeedsLeaving` ""+    context "when stream does not begin with decimal digits" $+      it "signals correct parse error" $+        property $ \a as -> not (isDigit a) ==> do+          let p = decimal+              s = a : as+          prs  p s `shouldFailWith` err posI (utok a <> elabel "decimal integer")+    context "when stream is empty" $+      it "signals correct parse error" $+        prs decimal "" `shouldFailWith`+          err posI (ueof <> elabel "decimal integer")++  describe "hexadecimal" $ do+    context "when stream begins with hexadecimal digits" $+      it "they are parsed as an integer" $+        property $ \n' -> do+          let p = hexadecimal+              n = getNonNegative n'+              s = showHex n ""+          prs  p s `shouldParse` n+          prs' p s `succeedsLeaving` ""+    context "when stream does not begin with hexadecimal digits" $+      it "signals correct parse error" $+        property $ \a as -> not (isHexDigit a) ==> do+          let p = hexadecimal+              s = a : as+          prs  p s `shouldFailWith`+            err posI (utok a <> elabel "hexadecimal integer")+    context "when stream is empty" $+      it "signals correct parse error" $+        prs hexadecimal "" `shouldFailWith`+          err posI (ueof <> elabel "hexadecimal integer")++  describe "octal" $ do+    context "when stream begins with octal digits" $+      it "they are parsed as an integer" $+        property $ \n' -> do+          let p = octal+              n = getNonNegative n'+              s = showOct n ""+          prs  p s `shouldParse` n+          prs' p s `succeedsLeaving` ""+    context "when stream does not begin with octal digits" $+      it "signals correct parse error" $+        property $ \a as -> not (isOctDigit a) ==> do+          let p = octal+              s = a : as+          prs  p s `shouldFailWith`+            err posI (utok a <> elabel "octal integer")+    context "when stream is empty" $+      it "signals correct parse error" $+        prs octal "" `shouldFailWith`+          err posI (ueof <> elabel "octal integer")++  describe "float" $ do+    context "when stream begins with a float" $+      it "parses it" $+        property $ \n' -> do+          let p = float+              n = getNonNegative n'+              s = show n+          prs  p s `shouldParse` n+          prs' p s `succeedsLeaving` ""+    context "when stream does not begin with a float" $+      it "signals correct parse error" $+        property $ \a as -> not (isDigit a) ==> do+          let p = float+              s = a : as+          prs  p s `shouldFailWith`+            err posI (utok a <> elabel "floating point number")+          prs' p s `failsLeaving` s+    context "when stream begins with a decimal number" $+      it "signals correct parse error" $+        property $ \n' -> do+          let p = float+              n = getNonNegative n'+              s = show (n :: Integer)+          prs  p s `shouldFailWith` err (posN (length s) s)+            (ueof <> etok '.' <> etok 'E' <> etok 'e' <> elabel "digit")+          prs' p s `failsLeaving` ""+    context "when stream is empty" $+      it "signals correct parse error" $+        prs float "" `shouldFailWith`+          err posI (ueof <> elabel "floating point number")+    context "when there is float with exponent without explicit sign" $+      it "parses it all right" $ do+        let p = float+            s = "123e3"+        prs  p s `shouldParse` 123e3+        prs' p s `succeedsLeaving` ""++  describe "number" $ do+    context "when stream begins with a number" $+      it "parses it" $+        property $ \n' -> do+          let p = number+              s = either (show . getNonNegative) (show . getNonNegative)+                (n' :: Either (NonNegative Integer) (NonNegative Double))+          prs p s `shouldParse` case n' of+            Left  x -> fromIntegral    (getNonNegative x)+            Right x -> fromFloatDigits (getNonNegative x)+          prs' p s `succeedsLeaving` ""+    context "when stream is empty" $+      it "signals correct parse error" $+        prs number "" `shouldFailWith`+          err posI (ueof <> elabel "number")++  describe "signed" $ do+    context "with integer" $+      it "parses signed integers" $+        property $ \n -> do+          let p = signed (hidden C.space) integer+              s = show n+          prs  p s `shouldParse` n+          prs' p s `succeedsLeaving` ""+    context "with float" $+      it "parses signed floats" $+        property $ \n -> do+          let p = signed (hidden C.space) float+              s = show n+          prs  p s `shouldParse` n+          prs' p s `succeedsLeaving` ""+    context "with number" $+      it "parses singed numbers" $+        property $ \n -> do+          let p = signed (hidden C.space) number+              s = either show show (n :: Either Integer Double)+          prs p s `shouldParse` case n of+            Left  x -> fromIntegral    x+            Right x -> fromFloatDigits x+    context "when number is prefixed with plus sign" $+      it "parses the number" $+        property $ \n' -> do+          let p = signed (hidden C.space) integer+              n = getNonNegative n'+              s = '+' : show n+          prs  p s `shouldParse` n+          prs' p s `succeedsLeaving` ""+    context "when number is prefixed with white space" $+      it "signals correct parse error" $+        property $ \n -> do+          let p = signed (hidden C.space) integer+              s = ' ' : show (n :: Integer)+          prs  p s `shouldFailWith` err posI+            (utok ' ' <> etok '+' <> etok '-' <> elabel "integer")+          prs' p s `failsLeaving` s+    context "when there is white space between sign and digits" $+      it "parses it all right" $ do+        let p = signed (hidden C.space) integer+            s = "- 123"+        prs  p s `shouldParse` (-123)+        prs' p s `succeedsLeaving` ""++----------------------------------------------------------------------------+-- Helpers++mkWhiteSpace :: Gen String+mkWhiteSpace = concat <$> listOf whiteUnit+  where whiteUnit = oneof [whiteChars, whiteLine, whiteBlock]++mkSymbol :: Gen String+mkSymbol = (++) <$> symbolName <*> whiteChars++mkInterspace :: String -> Int -> Gen String+mkInterspace x n = oneof [si, mkIndent x n]+  where si = (++ x) <$> listOf (elements " \t")++mkIndent :: String -> Int -> Gen String+mkIndent x n = (++) <$> mkIndent' x n <*> eol+  where eol = frequency [(5, return "\n"), (1, listOf1 (return '\n'))]++mkIndent' :: String -> Int -> Gen String+mkIndent' x n = concat <$> sequence [spc, sym, tra]+  where spc = frequency [(5, vectorOf n itm), (1, listOf itm)]+        tra = listOf itm+        itm = elements " \t"+        sym = return x++whiteChars :: Gen String+whiteChars = listOf (elements "\t\n ")++whiteLine :: Gen String+whiteLine = commentOut <$> arbitrary `suchThat` goodEnough+  where commentOut x = "//" ++ x ++ "\n"+        goodEnough x = '\n' `notElem` x++whiteBlock :: Gen String+whiteBlock = commentOut <$> arbitrary `suchThat` goodEnough+  where commentOut x = "/*" ++ x ++ "*/"+        goodEnough x = not $ "*/" `isInfixOf` x++symbolName :: Gen String+symbolName = listOf $ arbitrary `suchThat` isAlphaNum++sc :: Parser ()+sc = space (void $ C.oneOf " \t") empty empty++scn :: Parser ()+scn = space (void C.spaceChar) l b+  where l = skipLineComment "//"+        b = skipBlockComment "/*" "*/"++getIndent :: String -> Int+getIndent = length . takeWhile isSpace++getCol :: String -> Pos+getCol x = sourceColumn .+  updatePosString defaultTabWidth (initialPos "") $ take (getIndent x) x++sbla, sblb, sblc :: String+sbla = "aaa"+sblb = "bbb"+sblc = "ccc"++ii :: Ordering -> Pos -> Pos -> EC Char Dec+ii ord ref actual = cstm (DecIndentation ord ref actual)
+ tests/Text/Megaparsec/PermSpec.hs view
@@ -0,0 +1,125 @@+--+-- Tests for Megaparsec's permutation phrases parsers.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE MultiWayIf #-}++module Text.Megaparsec.PermSpec (spec) where++import Control.Applicative+import Data.List (nub, elemIndices)+import Data.Monoid+import Test.Hspec+import Test.Hspec.Megaparsec+import Test.Hspec.Megaparsec.AdHoc+import Test.QuickCheck+import Text.Megaparsec.Char+import Text.Megaparsec.Lexer (integer)+import Text.Megaparsec.Perm++data CharRows = CharRows+  { getChars :: (Char, Char, Char)+  , getInput :: String }+  deriving (Eq, Show)++instance Arbitrary CharRows where+  arbitrary = do+    chars@(a,b,c) <- arbitrary `suchThat` different+    an            <- arbitrary+    bn            <- arbitrary+    cn            <- arbitrary+    input <- concat <$> shuffle+             [ replicate an a+             , replicate bn b+             , replicate cn c]+    return $ CharRows chars input+      where different (a,b,c) = let l = [a,b,c] in l == nub l++spec :: Spec+spec = do++  describe "(<$$>)" $ do+    context "when supplied parser succeeds" $+      it "returns value returned by the parser" $+        property $ \n -> do+          let p = makePermParser (succ <$$> pure (n :: Integer))+          prs p "" `shouldParse` succ n+    context "when supplied parser fails" $+      it "signals correct parse error" $ do+          let p = makePermParser (succ <$$> integer)+          prs p "" `shouldFailWith` err posI (ueof <> elabel "integer")++  describe "(<$?>)" $ do+    context "when supplied parser succeeds" $+      it "returns value returned by the parser" $+        property $ \n m -> do+          let p = makePermParser (succ <$?> (n :: Integer, pure (m :: Integer)))+          prs p "" `shouldParse` succ m+    context "when supplied parser fails" $+      it "returns the default value" $+        property $ \n -> do+          let p = makePermParser (succ <$?> (n :: Integer, fail "foo"))+          prs p "" `shouldParse` succ n+    context "when stream in empty" $+      it "returns the default value" $+        property $ \n -> do+          let p = makePermParser (succ <$?> (n :: Integer, integer))+          prs p "" `shouldParse` succ n++  describe "makeExprParser" $+    it "works" $+      property $ \a' c' v -> do+        let (a,b,c) = getChars v+            p = makePermParser+              ((,,) <$?> (a' :: String, some (char a))+                <||> char b+                <|?> (c', char c))+            bis  = elemIndices b s+            preb = take (bis !! 1) s+            cis  = elemIndices c s+            prec = take (cis !! 1) s+            s    = getInput v+        if | length bis > 1 && (length cis <= 1 || head bis < head cis) ->+               prs_ p s `shouldFailWith` err (posN (bis !! 1) s)+                 ( utok b <> eeof <>+                   (if a `elem` preb then mempty else etok a) <>+                   (if c `elem` preb then mempty else etok c) )+           | length cis > 1 ->+               prs_ p s `shouldFailWith` err (posN (cis !! 1) s)+                 ( utok c <>+                   (if a `elem` prec then mempty else etok a) <>+                   (if b `elem` prec then eeof   else etok b) )+           | b `notElem` s ->+               prs_ p s `shouldFailWith` err (posN (length s) s)+                 ( ueof <> etok b <>+                   (if a `notElem` s || last s == a then etok a else mempty) <>+                   (if c `elem` s then mempty else etok c) )+           | otherwise ->+               prs_ p s `shouldParse`+                 ( if a `elem` s then filter (== a) s else a'+                 , b+                 , if c `elem` s then c else c' )
+ tests/Text/Megaparsec/PosSpec.hs view
@@ -0,0 +1,127 @@+--+-- Tests for Megaparsec's textual source positions.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE CPP              #-}+{-# OPTIONS -fno-warn-orphans #-}++module Text.Megaparsec.PosSpec (spec) where++import Data.Function (on)+import Data.List (isInfixOf)+import Data.Semigroup ((<>))+import Test.Hspec+import Test.Hspec.Megaparsec.AdHoc+import Test.QuickCheck+import Text.Megaparsec.Pos++#if !MIN_VERSION_base(4,8,0)+import Data.Word (Word)+#endif++spec :: Spec+spec = do++  describe "mkPos" $ do+    context "when the argument is 0" $+      it "throws InvalidPosException" $+        mkPos (0 :: Word) `shouldThrow` (== InvalidPosException)+    context "when the argument is not 0" $+      it "returns Pos with the given value" $+        property $ \n ->+          (n > 0) ==> (mkPos n >>= shouldBe n . unPos)++  describe "unsafePos" $+    context "when the argument is a positive integer" $+      it "returns Pos with the given value" $+        property $ \n ->+          (n > 0) ==> (unPos (unsafePos n) === n)++  describe "Read and Show instances of Pos" $+    it "printed representation of Pos is isomorphic to its value" $+      property $ \x ->+        read (show x) === (x :: Pos)++  describe "Ord instance of Pos" $+    it "works just like Ord instance of underlying Word" $+      property $ \x y ->+        compare x y === (compare `on` unPos) x y++  describe "Semigroup instance of Pos" $+    it "works like addition" $+      property $ \x y ->+        x <> y === unsafePos (unPos x + unPos y) .&&.+        unPos (x <> y) === unPos x + unPos y++  describe "initialPos" $+    it "consturcts initial position correctly" $+      property $ \path ->+        let x = initialPos path+        in sourceName   x === path        .&&.+           sourceLine   x === unsafePos 1 .&&.+           sourceColumn x === unsafePos 1++  describe "Read and Show instances of SourcePos" $+    it "printed representation of SourcePos in isomorphic to its value" $+      property $ \x ->+        read (show x) === (x :: SourcePos)++  describe "sourcePosPretty" $ do+    it "displays file name" $+      property $ \x ->+        sourceName x `isInfixOf` sourcePosPretty x+    it "displays line number" $+      property $ \x ->+        (show . unPos . sourceLine) x `isInfixOf` sourcePosPretty x+    it "displays column number" $+      property $ \x ->+        (show . unPos . sourceColumn) x `isInfixOf` sourcePosPretty x++  describe "defaultUpdatePos" $ do+    it "returns actual position unchanged" $+      property $ \w pos ch ->+        fst (defaultUpdatePos w pos ch) === pos+    it "does not change file name" $+      property $ \w pos ch ->+        (sourceName . snd) (defaultUpdatePos w pos ch) === sourceName pos+    context "when given newline character" $+      it "increments line number" $+        property $ \w pos ->+          (sourceLine . snd) (defaultUpdatePos w pos '\n')+            === (sourceLine pos <> pos1)+    context "when given tab character" $+      it "shits column number to next tab position" $+        property $ \w pos ->+          let c  = sourceColumn pos+              c' = (sourceColumn . snd) (defaultUpdatePos w pos '\t')+          in c' > c .&&. (((unPos c' - 1) `rem` unPos w) == 0)+    context "when given character other than newline or tab" $+      it "increments column number by one" $+        property $ \w pos ch ->+          (ch /= '\n' && ch /= '\t') ==>+          (sourceColumn . snd) (defaultUpdatePos w pos ch)+            === (sourceColumn pos <> pos1)
+ tests/Text/Megaparsec/PrimSpec.hs view
@@ -0,0 +1,1298 @@+--+-- Tests for Megaparsec's primitive parser combinators.+--+-- Copyright © 2015–2016 Megaparsec contributors+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are+-- met:+--+-- * Redistributions of source code must retain the above copyright notice,+--   this list of conditions and the following disclaimer.+--+-- * Redistributions in binary form must reproduce the above copyright+--   notice, this list of conditions and the following disclaimer in the+--   documentation and/or other materials provided with the distribution.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY+-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY+-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+-- POSSIBILITY OF SUCH DAMAGE.++{-# LANGUAGE CPP               #-}+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiWayIf        #-}+{-# LANGUAGE Rank2Types        #-}+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE TypeFamilies      #-}+{-# OPTIONS -fno-warn-orphans  #-}++module Text.Megaparsec.PrimSpec (spec) where++import Control.Applicative+import Control.Monad.Cont+import Control.Monad.Except+import Control.Monad.Identity+import Control.Monad.Reader+import Data.Char (toUpper, chr)+import Data.Foldable (asum, concat)+import Data.List (isPrefixOf, foldl')+import Data.List.NonEmpty (NonEmpty (..))+import Data.Maybe (fromMaybe)+import Data.Monoid+import Data.Proxy+import Data.Word (Word8)+import Prelude hiding (span, concat)+import Test.Hspec+import Test.Hspec.Megaparsec+import Test.Hspec.Megaparsec.AdHoc+import Test.QuickCheck hiding (label)+import Text.Megaparsec.Char+import Text.Megaparsec.Combinator+import Text.Megaparsec.Error+import Text.Megaparsec.Pos+import Text.Megaparsec.Prim+import Text.Megaparsec.String+import qualified Control.Monad.State.Lazy    as L+import qualified Control.Monad.State.Strict  as S+import qualified Control.Monad.Writer.Lazy   as L+import qualified Control.Monad.Writer.Strict as S+import qualified Data.ByteString.Char8       as B+import qualified Data.ByteString.Lazy.Char8  as BL+import qualified Data.List.NonEmpty          as NE+import qualified Data.Set                    as E+import qualified Data.Text                   as T+import qualified Data.Text.Lazy              as TL++spec :: Spec+spec = do++  describe "non-String instances of Stream" $ do+    context "lazy ByteString" $ do+      it "unconses correctly" $+        property $ \ch' n -> do+          let p  = many (char ch) :: Parsec Dec BL.ByteString String+              s  = replicate (getNonNegative n) ch+              ch = byteToChar ch'+          parse p "" (BL.pack s) `shouldParse` s+      it "updates position like with String" $+        property $ \w pos ch ->+          updatePos (Proxy :: Proxy BL.ByteString) w pos ch `shouldBe`+          updatePos (Proxy :: Proxy String) w pos ch+    context "strict ByteString" $ do+      it "unconses correctly" $+        property $ \ch' n -> do+          let p  = many (char ch) :: Parsec Dec B.ByteString String+              s  = replicate (getNonNegative n) ch+              ch = byteToChar ch'+          parse p "" (B.pack s) `shouldParse` s+      it "updates position like with String" $+        property $ \w pos ch ->+          updatePos (Proxy :: Proxy B.ByteString) w pos ch `shouldBe`+          updatePos (Proxy :: Proxy String) w pos ch+    context "lazy Text" $ do+      it "unconses correctly" $+        property $ \ch n -> do+          let p = many (char ch) :: Parsec Dec TL.Text String+              s = replicate (getNonNegative n) ch+          parse p "" (TL.pack s) `shouldParse` s+      it "updates position like with String" $+        property $ \w pos ch ->+          updatePos (Proxy :: Proxy TL.Text) w pos ch `shouldBe`+          updatePos (Proxy :: Proxy String) w pos ch+    context "strict Text" $ do+      it "unconses correctly" $+        property $ \ch n -> do+          let p = many (char ch) :: Parsec Dec T.Text String+              s = replicate (getNonNegative n) ch+          parse p "" (T.pack s) `shouldParse` s+      it "updates position like with String" $+        property $ \w pos ch ->+          updatePos (Proxy :: Proxy T.Text) w pos ch `shouldBe`+          updatePos (Proxy :: Proxy String) w pos ch++  describe "position in custom stream" $ do++    describe "eof" $+      it "updates position in stream correctly" $+        property $ \st -> (not . null . stateInput) st ==> do+          let p = eof :: CustomParser ()+              h = head (stateInput st)+              apos = let (_:|z) = statePos st in spanStart h :| z+          runParser' p st `shouldBe`+            ( st { statePos = apos }+            , Left (err apos $ utok h <> eeof) )++    describe "token" $+      it "updates position in stream correctly" $+        property $ \st@State {..} span -> do+          let p = pSpan span+              h = head stateInput+              (apos, npos) =+                let z = NE.tail statePos+                in (spanStart h :| z, spanEnd h :| z)+          if | null stateInput -> runParser' p st `shouldBe`+               ( st+               , Left (err statePos $ ueof <> etok span) )+             | spanBody h == spanBody span -> runParser' p st `shouldBe`+               ( st { statePos = npos+                    , stateInput = tail stateInput }+               , Right span )+             | otherwise -> runParser' p st `shouldBe`+               ( st { statePos = apos}+               , Left (err apos $ utok h <> etok span))++    describe "tokens" $+      it "updates position is stream correctly" $+        property $ \st' ts -> forAll (incCoincidence st' ts) $ \st@State {..} -> do+          let p = tokens compareTokens ts :: CustomParser [Span]+              compareTokens x y = spanBody x == spanBody y+              updatePos' = updatePos (Proxy :: Proxy [Span]) stateTabWidth+              il = length . takeWhile id $ zipWith compareTokens stateInput ts+              tl = length ts+              consumed = take il stateInput+              (apos, npos) =+                let (pos:|z) = statePos+                in ( spanStart (head stateInput) :| z+                   , foldl' (\q t -> snd (updatePos' q t)) pos consumed :| z )+          if | null ts -> runParser' p st `shouldBe` (st, Right [])+             | null stateInput -> runParser' p st `shouldBe`+               ( st+               , Left (err statePos $ ueof <> etoks ts) )+             | il == tl -> runParser' p st `shouldBe`+               ( st { statePos   = npos+                    , stateInput = drop (length ts) stateInput }+               , Right consumed )+             | otherwise -> runParser' p st `shouldBe`+               ( st { statePos = apos }+               , Left (err apos $ utoks (take (il + 1) stateInput) <> etoks ts) )++  describe "ParsecT Functor instance" $ do+    it "obeys identity law" $+      property $ \n ->+        prs (fmap id (pure (n :: Int))) "" ===+        prs (id (pure n))               ""+    it "obeys composition law" $+      property $ \n m t ->+        let f = (+ m)+            g = (* t)+        in prs (fmap (f . g) (pure (n :: Int))) "" ===+           prs ((fmap f . fmap g) (pure n))     ""++  describe "ParsecT Applicative instance" $ do+    it "obeys identity law" $+      property $ \n ->+        prs (pure id <*> pure (n :: Int)) "" ===+        prs (pure n) ""+    it "obeys composition law" $+      property $ \n m t ->+        let u = pure (+ m)+            v = pure (* t)+            w = pure (n :: Int)+        in prs (pure (.) <*> u <*> v <*> w) "" ===+           prs (u <*> (v <*> w)) ""+    it "obeys homomorphism law" $+      property $ \x m ->+        let f = (+ m)+        in prs (pure f <*> pure (x :: Int)) "" ===+           prs (pure (f x)) ""+    it "obeys interchange law" $+      property $ \n y ->+        let u = pure (+ n)+        in prs (u <*> pure (y :: Int)) "" ===+           prs (pure ($ y) <*> u) ""+    describe "(<*>)" $+      context "when first parser succeeds without consuming" $+        context "when second parser fails consuming input" $+          it "fails consuming input" $ do+            let p = m <*> n+                m = return (\x -> 'a' : x)+                n = string "bc" <* empty+                s = "bc"+            prs  p s `shouldFailWith` err (posN (4 :: Int) s) mempty+            prs' p s `failsLeaving`   ""+    describe "(*>)" $+      it "works correctly" $+        property $ \n m ->+          let u = pure (+ (m :: Int))+              v = pure (n :: Int)+          in prs (u *> v) "" ===+             prs (pure (const id) <*> u <*> v) ""+    describe "(<*)" $+      it "works correctly" $+        property $ \n m ->+          let u = pure (m :: Int)+              v = pure (+ (n :: Int))+          in prs (u <* v) "" === prs (pure const <*> u <*> v) ""++  describe "ParsecT Alternative instance" $ do++    describe "empty" $+      it "always fails" $+        property $ \n ->+          prs (empty <|> pure n) "" `shouldParse` (n :: Integer)++    describe "(<|>)" $ do+      context "with two strings" $ do+        context "stream begins with the first string" $+          it "parses the string" $+            property $ \s0 s1 s -> not (s1 `isPrefixOf` s0) ==> do+              let s' = s0 ++ s+                  p = string s0 <|> string s1+              prs  p s' `shouldParse` s0+              prs' p s' `succeedsLeaving` s+        context "stream begins with the second string" $+          it "parses the string" $+            property $ \s0 s1 s -> not (s0 `isPrefixOf` s1) && not (s0 `isPrefixOf` s) ==> do+              let s' = s1 ++ s+                  p = string s0 <|> string s1+              prs  p s' `shouldParse` s1+              prs' p s' `succeedsLeaving` s+        context "when stream does not begin with either string" $+          it "signals correct error message" $+            property $ \s0 s1 s -> not (s0 `isPrefixOf` s) && not (s1 `isPrefixOf` s) ==> do+              let p = string s0 <|> string s1+                  z0' = toFirstMismatch (==) s0 s+                  z1' = toFirstMismatch (==) s1 s+              prs  p s `shouldFailWith` err posI+                (etoks s0 <>+                 etoks s1 <>+                 (if null s then ueof else mempty) <>+                 (if null z0' then mempty else utoks z0') <>+                 (if null z1' then mempty else utoks z1'))+      context "with two complex parsers" $ do+        context "when stream begins with matching character" $+          it "parses it" $+            property $ \a b -> a /= b ==> do+              let p = char a <|> (char b *> char a)+                  s = [a]+              prs  p s `shouldParse` a+              prs' p s `succeedsLeaving` ""+        context "when stream begins with only one matching character" $+          it "signals correct parse error" $+            property $ \a b c -> a /= b && a /= c ==> do+              let p = char a <|> (char b *> char a)+                  s = [b,c]+              prs  p s `shouldFailWith` err (posN (1 :: Int) s) (utok c <> etok a)+              prs' p s `failsLeaving` [c]+        context "when stream begins with not matching character" $+          it "signals correct parse error" $+            property $ \a b c -> a /= b && a /= c && b /= c ==> do+              let p = char a <|> (char b *> char a)+                  s = [c,b]+              prs  p s `shouldFailWith` err posI (utok c <> etok a <> etok b)+              prs' p s `failsLeaving` s+        context "when stream is emtpy" $+          it "signals correct parse error" $+            property $ \a b -> do+              let p = char a <|> (char b *> char a)+              prs  p "" `shouldFailWith` err posI (ueof <> etok a <> etok b)+      it "associativity of fold over alternatives should not matter" $ do+        let p  = asum [empty, string ">>>", empty, return "foo"] <?> "bar"+            p' = bsum [empty, string ">>>", empty, return "foo"] <?> "bar"+            bsum = foldl (<|>) empty+            s  = ">>"+        prs p s `shouldBe` prs p' s++    describe "many" $ do+      context "when stream begins with things argument of many parses" $+        it "they are parsed" $+          property $ \a' b' c' -> do+            let [a,b,c] = getNonNegative <$> [a',b',c']+                p = many (char 'a')+                s = abcRow a b c+            prs  p s `shouldParse` replicate a 'a'+            prs' p s `succeedsLeaving` drop a s+      context "when stream does not begin with thing argument of many parses" $+        it "does nothing" $+          property $ \a' b' c' -> do+            let [a,b,c] = getNonNegative <$> [a',b',c']+                p = many (char 'd')+                s = abcRow a b c+            prs  p s `shouldParse` ""+            prs' p s `succeedsLeaving` s+      context "when stream is empty" $+        it "succeeds parsing nothing" $ do+          let p = many (char 'a')+          prs  p "" `shouldParse` ""+      context "when there are two many combinators in a row that parse nothing" $+        it "accumulated hints are reflected in parse error" $ do+          let p = many (char 'a') *> many (char 'b') *> eof+          prs p "c" `shouldFailWith` err posI+            (utok 'c' <> etok 'a' <> etok 'b' <> eeof)++    describe "some" $ do+      context "when stream begins with things argument of some parses" $+        it "they are parsed" $+          property $ \a' b' c' -> do+            let a = getPositive a'+                [b,c] = getNonNegative <$> [b',c']+                p = some (char 'a')+                s = abcRow a b c+            prs  p s `shouldParse` replicate a 'a'+            prs' p s `succeedsLeaving` drop a s+      context "when stream does not begin with thing argument of some parses" $+        it "signals correct parse error" $+          property $ \a' b' c' -> do+            let [a,b,c] = getNonNegative <$> [a',b',c']+                p = some (char 'd')+                s = abcRow a b c ++ "g"+            prs  p s `shouldFailWith` err posI (utok (head s) <> etok 'd')+            prs' p s `failsLeaving` s+      context "when stream is empty" $+        it "signals correct parse error" $+          property $ \ch -> do+            let p = some (char ch)+            prs  p "" `shouldFailWith` err posI (ueof <> etok ch)+    context "optional" $ do+      context "when stream begins with that optional thing" $+        it "parses it" $+          property $ \a b -> do+            let p = optional (char a) <* char b+                s = [a,b]+            prs  p s `shouldParse` Just a+            prs' p s `succeedsLeaving` ""+      context "when stream does not begin with that optional thing" $+        it "succeeds parsing nothing" $+          property $ \a b -> a /= b ==> do+            let p = optional (char a) <* char b+                s = [b]+            prs  p s `shouldParse` Nothing+            prs' p s `succeedsLeaving` ""+      context "when stream is empty" $+        it "succeeds parsing nothing" $+          property $ \a -> do+            let p = optional (char a)+            prs  p "" `shouldParse` Nothing++  describe "ParsecT Monad instance" $ do+    it "satisfies left identity law" $+      property $ \a k' -> do+        let k = return . (+ k')+            p = return (a :: Int) >>= k+        prs p "" `shouldBe` prs (k a) ""+    it "satisfies right identity law" $+      property $ \a -> do+        let m = return (a :: Int)+            p = m >>= return+        prs p "" `shouldBe` prs m ""+    it "satisfies associativity law" $+      property $ \m' k' h' -> do+        let m = return (m' :: Int)+            k = return . (+ k')+            h = return . (* h')+            p = m >>= (\x -> k x >>= h)+            p' = (m >>= k) >>= h+        prs p "" `shouldBe` prs p' ""+    it "fails signals correct parse error" $+      property $ \msg -> do+        let p = fail msg :: Parsec Dec String ()+        prs p "" `shouldFailWith` err posI (cstm (DecFail msg))+    it "pure is the same as return" $+      property $ \n ->+        prs (pure (n :: Int)) "" `shouldBe` prs (return n) ""+    it "(<*>) is the same as ap" $+      property $ \m' k' -> do+        let m = return (m' :: Int)+            k = return (+ k')+        prs (k <*> m) "" `shouldBe` prs (k `ap` m) ""++  describe "ParsecT MonadFail instance" $+    describe "fail" $+      it "signals correct parse error" $+        property $ \s msg -> do+          let p = void (fail msg)+          prs  p s `shouldFailWith` err posI (cstm $ DecFail msg)+          prs' p s `failsLeaving` s++  describe "ParsecT MonadIO instance" $+    it "liftIO works" $+      property $ \n -> do+        let p = liftIO (return n) :: ParsecT Dec String IO Integer+        runParserT p "" "" `shouldReturn` Right n++  describe "ParsecT MonadReader instance" $ do++    describe "ask" $+      it "returns correct value of context" $+        property $ \n -> do+          let p = ask :: ParsecT Dec String (Reader Integer) Integer+          runReader (runParserT p "" "") n `shouldBe` Right n++    describe "local" $+      it "modifies reader context correctly" $+        property $ \n k -> do+          let p = local (+ k) ask :: ParsecT Dec String (Reader Integer) Integer+          runReader (runParserT p "" "") n `shouldBe` Right (n + k)++  describe "ParsecT MonadState instance" $ do++    describe "get" $+      it "returns correct state value" $+        property $ \n -> do+          let p = L.get :: ParsecT Dec String (L.State Integer) Integer+          L.evalState (runParserT p "" "") n `shouldBe` Right n+    describe "put" $+      it "replaces state value" $+        property $ \a b -> do+          let p = L.put b :: ParsecT Dec String (L.State Integer) ()+          L.execState (runParserT p "" "") a `shouldBe` b++  describe "ParsecT MonadCont instance" $++    describe "callCC" $+      it "works properly" $+        property $ \a b -> do+          let p :: ParsecT Dec String (Cont (Either (ParseError Char Dec) Integer)) Integer+              p = callCC $ \e -> when (a > b) (e a) >> return b+          runCont (runParserT p "" "") id `shouldBe` Right (max a b)++  describe "ParsecT MonadError instance" $ do++    describe "throwError" $+      it "throws the error" $+        property $ \a b -> do+          let p :: ParsecT Dec String (Except Integer) Integer+              p = throwError a >> return b+          runExcept (runParserT p "" "") `shouldBe` Left a++    describe "catchError" $+      it "catches the error" $+        property $ \a b -> do+          let p :: ParsecT Dec String (Except Integer) Integer+              p = (throwError a >> return b) `catchError` handler+              handler e = return (e + b)+          runExcept (runParserT p "" "") `shouldBe` Right (Right $ a + b)++  describe "primitive combinators" $ do++    describe "unexpected" $+      it "signals correct parse error" $+        property $ \item -> do+          let p :: MonadParsec Dec String m => m ()+              p = void (unexpected item)+          grs p "" (`shouldFailWith` ParseError+            { errorPos        = posI+            , errorUnexpected = E.singleton item+            , errorExpected   = E.empty+            , errorCustom     = E.empty })++    describe "failure" $+      it "signals correct parse error" $+        property $ \us ps xs -> do+          let p :: MonadParsec Dec String m => m ()+              p = void (failure us ps xs)+          grs p "" (`shouldFailWith` ParseError+            { errorPos        = posI+            , errorUnexpected = us+            , errorExpected   = ps+            , errorCustom     = xs })++    describe "label" $ do+      context "when inner parser succeeds consuming input" $ do+        context "inner parser does not produce any hints" $+          it "collection of hints remains empty" $+            property $ \lbl a -> not (null lbl) ==> do+              let p :: MonadParsec Dec String m => m Char+                  p = label lbl (char a) <* empty+                  s = [a]+              grs  p s (`shouldFailWith` err (posN (1 :: Int) s) mempty)+              grs' p s (`failsLeaving` "")+        context "inner parser produces hints" $+          it "replaces the last hint with “rest of <label>”" $+            property $ \lbl a -> not (null lbl) ==> do+              let p :: MonadParsec Dec String m => m String+                  p = label lbl (many (char a)) <* empty+                  s = [a]+              grs  p s (`shouldFailWith` err (posN (1 :: Int) s) (elabel $ "rest of " ++ lbl))+              grs' p s (`failsLeaving` "")+      context "when inner parser consumes and fails" $+        it "reports parse error without modification" $+          property $ \lbl a b c -> not (null lbl) && b /= c ==> do+            let p :: MonadParsec Dec String m => m Char+                p = label lbl (char a *> char b)+                s = [a,c]+            grs  p s (`shouldFailWith` err (posN (1 :: Int) s) (utok c <> etok b))+            grs' p s (`failsLeaving` [c])+      context "when inner parser succeeds without consuming" $ do+        context "inner parser does not produce any hints" $+          it "collection of hints remains empty" $+            property $ \lbl a -> not (null lbl) ==> do+              let p :: MonadParsec Dec String m => m Char+                  p = label lbl (return a) <* empty+              grs p "" (`shouldFailWith` err posI mempty)+        context "inner parser produces hints" $+          it "replaces the last hint with given label" $+            property $ \lbl a -> not (null lbl) ==> do+              let p :: MonadParsec Dec String m => m String+                  p = label lbl (many (char a)) <* empty+              grs p "" (`shouldFailWith` err posI (elabel lbl))+      context "when inner parser fails without consuming" $+        it "is mentioned in parse error via its label" $+          property $ \lbl -> not (null lbl) ==> do+            let p :: MonadParsec Dec String m => m ()+                p = label lbl empty+            grs p "" (`shouldFailWith` err posI (elabel lbl))++    describe "hidden" $ do+      context "when inner parser succeeds consuming input" $ do+        context "inner parser does not produce any hints" $+          it "collection of hints remains empty" $+            property $ \a -> do+              let p :: MonadParsec Dec String m => m Char+                  p = hidden (char a) <* empty+                  s = [a]+              grs  p s (`shouldFailWith` err (posN (1 :: Int) s) mempty)+              grs' p s (`failsLeaving` "")+        context "inner parser produces hints" $+          it "hides the parser in the error message" $+            property $ \a -> do+              let p :: MonadParsec Dec String m => m String+                  p = hidden (many (char a)) <* empty+                  s = [a]+              grs  p s (`shouldFailWith` err (posN (1 :: Int) s) mempty)+              grs' p s (`failsLeaving` "")+      context "when inner parser consumes and fails" $+        it "reports parse error without modification" $+          property $ \a b c -> b /= c ==> do+            let p :: MonadParsec Dec String m => m Char+                p = hidden (char a *> char b)+                s = [a,c]+            grs  p s (`shouldFailWith` err (posN (1 :: Int) s) (utok c <> etok b))+            grs' p s (`failsLeaving` [c])+      context "when inner parser succeeds without consuming" $ do+        context "inner parser does not produce any hints" $+          it "collection of hints remains empty" $+            property $ \a -> do+              let p :: MonadParsec Dec String m => m Char+                  p = hidden (return a) <* empty+              grs p "" (`shouldFailWith` err posI mempty)+        context "inner parser produces hints" $+          it "hides the parser in the error message" $+            property $ \a -> do+              let p :: MonadParsec Dec String m => m String+                  p = hidden (many (char a)) <* empty+              grs p "" (`shouldFailWith` err posI mempty)+      context "when inner parser fails without consuming" $+        it "hides the parser in the error message" $ do+          let p :: MonadParsec Dec String m => m ()+              p = hidden empty+          grs p "" (`shouldFailWith` err posI mempty)++    describe "try" $ do+      context "when inner parser succeeds consuming" $+        it "try has no effect" $+          property $ \a -> do+            let p :: MonadParsec Dec String m => m Char+                p = try (char a)+                s = [a]+            grs  p s (`shouldParse` a)+            grs' p s (`succeedsLeaving` "")+      context "when inner parser fails consuming" $+        it "backtracks, it appears as if the parser has not consumed anything" $+          property $ \a b c -> b /= c ==> do+            let p :: MonadParsec Dec String m => m Char+                p = try (char a *> char b)+                s = [a,c]+            grs  p s (`shouldFailWith` err (posN (1 :: Int) s) (utok c <> etok b))+            grs' p s (`failsLeaving` s)+      context "when inner parser succeeds without consuming" $+        it "try has no effect" $+          property $ \a -> do+            let p :: MonadParsec Dec String m => m Char+                p = try (return a)+            grs p "" (`shouldParse` a)+      context "when inner parser fails without consuming" $+        it "try backtracks parser state anyway" $+          property $ \w -> do+            let p :: MonadParsec Dec String m => m Char+                p = try (setTabWidth w *> empty)+            grs  p "" (`shouldFailWith` err posI mempty)+            grs' p "" ((`shouldBe` defaultTabWidth) . stateTabWidth . fst)++    describe "lookAhead" $ do+      context "when inner parser succeeds consuming" $ do+        it "result is returned but parser state is not changed" $+          property $ \a w -> do+            let p :: MonadParsec Dec String m => m Pos+                p = lookAhead (setTabWidth w *> char a) *> getTabWidth+                s = [a]+            grs  p s (`shouldParse` defaultTabWidth)+            grs' p s (`succeedsLeaving` s)+        it "hints are not preserved" $+          property $ \a -> do+            let p :: MonadParsec Dec String m => m String+                p = lookAhead (many (char a)) <* empty+                s = [a]+            grs  p s (`shouldFailWith` err posI mempty)+            grs' p s (`failsLeaving` s)+      context "when inner parser fails consuming" $+        it "error message is reported as usual" $+          property $ \a b c -> b /= c ==> do+            let p :: MonadParsec Dec String m => m Char+                p = lookAhead (char a *> char b)+                s = [a,c]+            grs  p s (`shouldFailWith` err (posN (1 :: Int) s) (utok c <> etok b))+            grs' p s (`failsLeaving` [c])+      context "when inner parser succeeds without consuming" $ do+        it "result is returned but parser state in not changed" $+          property $ \a w -> do+            let p :: MonadParsec Dec String m => m Pos+                p = lookAhead (setTabWidth w *> char a) *> getTabWidth+                s = [a]+            grs  p s (`shouldParse` defaultTabWidth)+            grs' p s (`succeedsLeaving` s)+        it "hints are not preserved" $+          property $ \a b -> a /= b ==> do+            let p :: MonadParsec Dec String m => m String+                p = lookAhead (many (char a)) <* empty+                s = [b]+            grs  p s (`shouldFailWith` err posI mempty)+            grs' p s (`failsLeaving` s)+      context "when inner parser fails without consuming" $+        it "error message is reported as usual" $ do+          let p :: MonadParsec Dec String m => m Char+              p = lookAhead empty+          grs p "" (`shouldFailWith` err posI mempty)++    describe "notFollowedBy" $ do+      context "when inner parser succeeds consuming" $+        it "signals correct parse error" $+          property $ \a w -> do+            let p :: MonadParsec Dec String m => m ()+                p = notFollowedBy (setTabWidth w <* char a)+                s = [a]+            grs  p s (`shouldFailWith` err posI (utok a))+            grs' p s (`failsLeaving` s)+            grs' p s ((`shouldBe` defaultTabWidth) . stateTabWidth . fst)+      context "when inner parser fails consuming" $ do+        it "succeeds without consuming" $+          property $ \a b c w -> b /= c ==> do+            let p :: MonadParsec Dec String m => m ()+                p = notFollowedBy (setTabWidth w *> char a *> char b)+                s = [a,c]+            grs' p s (`succeedsLeaving` s)+            grs' p s ((`shouldBe` defaultTabWidth) . stateTabWidth . fst)+        it "hints are not preserved" $+          property $ \a b -> a /= b ==> do+            let p :: MonadParsec Dec String m => m ()+                p = notFollowedBy (char b *> many (char a) <* char a) <* empty+                s = [b,b]+            grs  p s (`shouldFailWith` err posI mempty)+            grs' p s (`failsLeaving` s)+      context "when inner parser succeeds without consuming" $+        it "signals correct parse error" $+          property $ \a w -> do+            let p :: MonadParsec Dec String m => m ()+                p = notFollowedBy (setTabWidth w *> return a)+                s = [a]+            grs  p s (`shouldFailWith` err posI (utok a))+            grs' p s (`failsLeaving` s)+            grs' p s ((`shouldBe` defaultTabWidth) . stateTabWidth . fst)+      context "when inner parser fails without consuming" $ do+        it "succeeds without consuming" $+          property $ \w -> do+            let p :: MonadParsec Dec String m => m ()+                p = notFollowedBy (setTabWidth w *> empty)+            grs  p "" (`shouldParse` ())+            grs' p "" ((`shouldBe` defaultTabWidth) . stateTabWidth . fst)+        it "hints are not preserved" $+          property $ \a -> do+            let p :: MonadParsec Dec String m => m ()+                p = notFollowedBy (many (char a) <* char a) <* empty+                s = ""+            grs  p s (`shouldFailWith` err posI mempty)+            grs' p s (`failsLeaving` s)++    describe "withRecovery" $ do+      context "when inner parser succeeds consuming" $+        it "the result is returned as usual" $+          property $ \a as -> do+            let p :: MonadParsec Dec String m => m (Maybe Char)+                p = withRecovery (const $ return Nothing) (pure <$> char a)+                s = a : as+            grs  p s (`shouldParse` Just a)+            grs' p s (`succeedsLeaving` as)+      context "when inner parser fails consuming" $ do+        context "when recovering parser succeeds consuming input" $ do+          it "its result is returned and position is advanced" $+            property $ \a b c as -> b /= c ==> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (\e -> Left e <$ string (c : as))+                        (Right <$> char a <* char b)+                  s = a : c : as+              grs  p s (`shouldParse` Left (err (posN (1 :: Int) s) (utok c <> etok b)))+              grs' p s (`succeedsLeaving` "")+          it "hints are not preserved" $+            property $ \a b c as -> b /= c ==> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (\e -> Left e <$ string (c : as))+                        (Right <$> char a <* many (char b) <* char b) <* empty+                  s = a : c : as+              grs  p s (`shouldFailWith` err (posN (length s) s) mempty)+              grs' p s (`failsLeaving` "")+        context "when recovering parser fails consuming input" $+          it "the original parse error (and state) is reported" $+            property $ \a b c as -> b /= c ==> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (\e -> Left e <$ char c <* empty)+                        (Right <$> char a <* char b)+                  s = a : c : as+              grs  p s (`shouldFailWith` err (posN (1 :: Int) s) (utok c <> etok b))+              grs' p s (`failsLeaving` (c : as))+        context "when recovering parser succeeds without consuming" $ do+          it "its result is returned (and state)" $+            property $ \a b c as -> b /= c ==> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (return . Left) (Right <$> char a <* char b)+                  s = a : c : as+              grs  p s (`shouldParse` Left (err (posN (1 :: Int) s) (utok c <> etok b)))+              grs' p s (`succeedsLeaving` (c : as))+          it "original hints are preserved" $+            property $ \a b c as -> b /= c ==> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (return . Left)+                        (Right <$> char a <* many (char b) <* char b) <* empty+                  s = a : c : as+              grs  p s (`shouldFailWith` err (posN (1 :: Int) s) (etok b))+              grs' p s (`failsLeaving` (c:as))+        context "when recovering parser fails without consuming" $+          it "the original parse error (and state) is reported" $+            property $ \a b c as -> b /= c ==> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (\e -> Left e <$ empty)+                        (Right <$> char a <* char b)+                  s = a : c : as+              grs  p s (`shouldFailWith` err (posN (1 :: Int) s) (utok c <> etok b))+              grs' p s (`failsLeaving` (c : as))+      context "when inner parser succeeds without consuming" $+        it "the result is returned as usual" $+          property $ \a s -> do+            let p :: MonadParsec Dec String m => m (Maybe Char)+                p = withRecovery (const $ return Nothing) (return a)+            grs  p s (`shouldParse` a)+            grs' p s (`succeedsLeaving` s)+      context "when inner parser fails without consuming" $ do+        context "when recovering parser succeeds consuming input" $+          it "its result is returned and position is advanced" $+            property $ \a as -> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (\e -> Left e <$ string s) empty+                  s = a : as+              grs  p s (`shouldParse` Left (err posI mempty))+              grs' p s (`succeedsLeaving` "")+        context "when recovering parser fails consuming input" $+          it "the original parse error (and state) is reported" $+            property $ \a b as -> a /= b ==> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (\e -> Left e <$ char a <* char b <* empty)+                        (Right <$> empty)+                  s = a : as+              grs  p s (`shouldFailWith` err posI mempty)+              grs' p s (`failsLeaving` s)+        context "when recovering parser succeeds without consuming" $ do+          it "its result is returned (and state)" $+            property $ \s -> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (return . Left) empty+              grs  p s (`shouldParse` Left (err posI mempty))+              grs' p s (`succeedsLeaving` s)+          it "original hints are preserved" $+            property $ \a b as -> a /= b ==> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) String)+                  p = withRecovery (return . Left)+                        (Right <$> many (char a) <* empty) <* empty+                  s = b : as+              grs  p s (`shouldFailWith` err posI (etok a))+              grs' p s (`failsLeaving` s)+        context "when recovering parser fails without consuming" $+          it "the original parse error (and state) is reported" $+            property $ \s -> do+              let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                  p = withRecovery (\e -> Left e <$ empty) empty+              grs  p s (`shouldFailWith` err posI mempty)+              grs' p s (`failsLeaving` s)+      it "works in complex situations too" $+        property $ \a' b' c' -> do+          let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) String)+              p = let g = count' 1 3 . char in v <$>+                withRecovery (\e -> Left e <$ g 'b') (Right <$> g 'a') <*> g 'c'+              v (Right x) y = Right (x ++ y)+              v (Left  m) _ = Left m+              ma = if a < 3 then etok 'a' else mempty+              s = abcRow a b c+              [a,b,c] = getNonNegative <$> [a',b',c']+              f = flip shouldFailWith+              z = flip shouldParse+              r | a == 0 && b == 0 && c == 0 = f (err posI (ueof <> etok 'a'))+                | a == 0 && b == 0 && c >  3 = f (err posI (utok 'c' <> etok 'a'))+                | a == 0 && b == 0           = f (err posI (utok 'c' <> etok 'a'))+                | a == 0 && b >  3           = f (err (posN (3 :: Int) s) (utok 'b' <> etok 'a' <> etok 'c'))+                | a == 0 &&           c == 0 = f (err (posN b s) (ueof <> etok 'a' <> etok 'c'))+                | a == 0 &&           c >  3 = f (err (posN (b + 3) s) (utok 'c' <> eeof))+                | a == 0                     = z (Left (err posI (utok 'b' <> etok 'a')))+                | a >  3                     = f (err (posN (3 :: Int) s) (utok 'a' <> etok 'c'))+                |           b == 0 && c == 0 = f (err (posN a s) (ueof <> etok 'c' <> ma))+                |           b == 0 && c >  3 = f (err (posN (a + 3) s) (utok 'c' <> eeof))+                |           b == 0           = z (Right s)+                | otherwise                  = f (err (posN a s) (utok 'b' <> etok 'c' <> ma))+          grs (p <* eof) s r++    describe "observing" $ do+      context "when inner parser succeeds consuming" $+        it "returns its result in Right" $+          property $ \a as -> do+            let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                p = observing (char a)+                s = a : as+            grs  p s (`shouldParse` Right a)+            grs' p s (`succeedsLeaving` as)+      context "when inner parser fails consuming" $ do+        it "returns its parse error in Left preserving state" $+          property $ \a b c as -> b /= c ==> do+            let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                p = observing (char a *> char b)+                s = a : c : as+            grs  p s (`shouldParse` Left (err (posN (1 :: Int) s) (utok c <> etok b)))+            grs' p s (`succeedsLeaving` (c:as))+        it "does not create any hints" $+          property $ \a b c as -> b /= c ==> do+            let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                p = observing (char a *> char b) *> empty+                s = a : c : as+            grs  p s (`shouldFailWith` err (posN (1 :: Int) s) mempty)+            grs' p s (`failsLeaving` (c:as))+      context "when inner parser succeeds without consuming" $+        it "returns its result in Right" $+          property $ \a s -> do+            let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                p = observing (return a)+            grs  p s (`shouldParse` Right a)+            grs' p s (`succeedsLeaving` s)+      context "when inner parser fails without consuming" $ do+        it "returns its parse error in Left preserving state" $+          property $ \s -> do+            let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) ())+                p = observing empty+            grs  p s (`shouldParse` Left (err posI mempty))+            grs' p s (`succeedsLeaving` s)+        it "creates correct hints" $+          property $ \a b as -> a /= b ==> do+            let p :: MonadParsec Dec String m => m (Either (ParseError Char Dec) Char)+                p = observing (char a) <* empty+                s = b : as+            grs  p s (`shouldFailWith` err posI (etok a))+            grs' p s (`failsLeaving` (b:as))++    describe "eof" $ do+      context "when input stream is empty" $+        it "succeeds" $+          grs eof "" (`shouldParse` ())+      context "when input stream is not empty" $+        it "signals correct error message" $+          property $ \a as -> do+            let s = a : as+            grs  eof s (`shouldFailWith` err posI (utok a <> eeof))+            grs' eof s (`failsLeaving` s)++    describe "token" $ do+      let f x = E.singleton (Tokens $ nes x)+          testChar a x = if x == a then Right x else Left (f x, f a, E.empty)+      context "when supplied predicate is satisfied" $+        it "succeeds" $+          property $ \a as mtok -> do+            let p :: MonadParsec Dec String m => m Char+                p = token (testChar a) mtok+                s = a : as+            grs  p s (`shouldParse` a)+            grs' p s (`succeedsLeaving` as)+      context "when supplied predicate is not satisfied" $+        it "signals correct parse error" $+          property $ \a b as mtok -> a /= b ==> do+            let p :: MonadParsec Dec String m => m Char+                p = token (testChar b) mtok+                s = a : as+            grs  p s (`shouldFailWith` ParseError+              { errorPos        = posI+              , errorUnexpected = E.singleton (Tokens $ nes a)+              , errorExpected   = E.singleton (Tokens $ nes b)+              , errorCustom     = E.empty })+            grs' p s (`failsLeaving` s)+      context "when stream is empty" $+        it "signals correct parse error" $+          property $ \a mtok -> do+            let p :: MonadParsec Dec String m => m Char+                p = token (testChar a) mtok+            grs p "" (`shouldFailWith` ParseError+              { errorPos        = posI+              , errorUnexpected = E.singleton EndOfInput+              , errorExpected   = maybe E.empty (E.singleton . Tokens . nes) mtok+              , errorCustom     = E.empty })++    describe "tokens" $ do+      context "when stream is prefixed with given string" $+        it "parses the string" $+          property $ \str s -> do+            let p :: MonadParsec Dec String m => m String+                p = tokens (==) str+                s' = str ++ s+            grs  p s' (`shouldParse` str)+            grs' p s' (`succeedsLeaving` s)+      context "when stream is not prefixed with given string" $+        it "signals correct parse error" $+          property $ \str s -> not (str `isPrefixOf` s) ==> do+            let p :: MonadParsec Dec String m => m String+                p = tokens (==) str+                z = toFirstMismatch (==) str s+            grs  p s (`shouldFailWith` err posI (utoks z <> etoks str))+            grs' p s (`failsLeaving` s)++  describe "combinators for manipulating parser state" $ do++    describe "setPosition and getPosition" $+      it "sets position and gets it back" $+        property $ \st pos -> do+          let p :: Parser SourcePos+              p = setPosition pos >> getPosition+              f (State s (_:|xs) w) y = State s (y:|xs) w+          runParser' p st `shouldBe` (f st pos, Right pos)++    describe "pushPosition" $+      it "adds a layer to position stack and parser continues on that level" $+        property $ \st pos ->  do+          let p :: Parser ()+              p = pushPosition pos+          fst (runParser' p st) `shouldBe`+            st { statePos = NE.cons pos (statePos st) }++    describe "popPosition" $+      it "removes a layer from position stack" $+        property $ \st -> do+          let p :: Parser ()+              p = popPosition+              pos = statePos st+          fst (runParser' p st) `shouldBe`+            st { statePos = fromMaybe pos (snd (NE.uncons pos)) }++    describe "setInput and getInput" $+      it "sets input and gets it back" $+        property $ \s -> do+          let p = do+                st0 <- getInput+                guard (null st0)+                setInput s+                result <- string s+                st1 <- getInput+                guard (null st1)+                return result+          prs p "" `shouldParse` s++    describe "setTabWidth and getTabWidth" $+      it "sets tab width and gets it back" $+        property $ \w -> do+          let p = setTabWidth w >> getTabWidth+          prs p "" `shouldParse` w++    describe "setParserState and getParserState" $+      it "sets parser state and gets it back" $+        property $ \s1 s2 -> do+          let p :: MonadParsec Dec String m => m (State String)+              p = do+                st <- getParserState+                guard (st == State s posI defaultTabWidth)+                setParserState s1+                updateParserState (f s2)+                liftM2 const getParserState (setInput "")+              f (State s1' pos w) (State s2' _ _) = State (max s1' s2') pos w+              s = ""+          grs p s (`shouldParse` f s2 s1)++  describe "running a parser" $ do+    describe "parseMaybe" $+      it "returns result on success and Nothing on failure" $+        property $ \s s' -> do+          let p = string s' :: Parser String+          parseMaybe p s `shouldBe`+            if s == s' then Just s else Nothing++    describe "runParser'" $+      it "works" $+        property $ \st s -> do+          let p = string s+          runParser' p st `shouldBe` emulateStrParsing st s++    describe "runParserT'" $+      it "works" $+        property $ \st s -> do+          let p = string s+          runIdentity (runParserT' p st) `shouldBe` emulateStrParsing st s++  describe "MonadParsec instance of ReaderT" $ do++    describe "try" $+      it "generally works" $+        property $ \pre ch1 ch2 -> do+          let s1 = pre : [ch1]+              s2 = pre : [ch2]+              getS1 = asks fst+              getS2 = asks snd+              p = try (g =<< getS1) <|> (g =<< getS2)+              g = sequence . fmap char+              s = [pre]+          prs (runReaderT p (s1, s2)) s `shouldFailWith`+            err (posN (1 :: Int) s) (ueof <> etok ch1 <> etok ch2)++    describe "notFollowedBy" $+      it "generally works" $+        property $ \a' b' c' -> do+          let p = many (char =<< ask) <* notFollowedBy eof <* many anyChar+              [a,b,c] = getNonNegative <$> [a',b',c']+              s = abcRow a b c+          if b > 0 || c > 0+            then prs (runReaderT p 'a') s `shouldParse` replicate a 'a'+            else prs (runReaderT p 'a') s `shouldFailWith`+                   err (posN a s) (ueof <> etok 'a')++  describe "MonadParsec instance of lazy StateT" $ do++    describe "(<|>)" $+      it "generally works" $+        property $ \n -> do+          let p = L.put n >>+                ((L.modify (* 2) >> void (string "xxx")) <|> return ()) >> L.get+          prs (L.evalStateT p 0) "" `shouldParse` (n :: Integer)++    describe "lookAhead" $+      it "generally works" $+        property $ \n -> do+          let p = L.put n >> lookAhead (L.modify (* 2) >> eof) >> S.get+          prs (L.evalStateT p 0) "" `shouldParse` (n :: Integer)++    describe "notFollowedBy" $+      it "generally works" $+        property $ \n -> do+          let p = do+                L.put n+                let notEof = notFollowedBy (L.modify (* 2) >> eof)+                some (try (anyChar <* notEof)) <* char 'x'+          prs (L.runStateT p 0) "abx" `shouldParse` ("ab", n :: Integer)++    describe "observing" $ do+      context "when inner parser succeeds" $+        it "can affect state" $+          property $ \m n -> do+            let p = do+                  L.put m+                  observing (L.modify (+ n))+            prs (L.execStateT p 0) "" `shouldParse` (m + n :: Integer)+      context "when inner parser fails" $+        it "cannot affect state" $+          property $ \m n -> do+            let p = do+                  L.put m+                  observing (L.modify (+ n) <* empty)+            prs (L.execStateT p 0) "" `shouldParse` (m :: Integer)++  describe "MonadParsec instance of strict StateT" $ do++    describe "(<|>)" $+      it "generally works" $+        property $ \n -> do+          let p = S.put n >>+                ((S.modify (* 2) >> void (string "xxx")) <|> return ()) >> S.get+          prs (S.evalStateT p 0) "" `shouldParse` (n :: Integer)++    describe "lookAhead" $+      it "generally works" $+        property $ \n -> do+          let p = S.put n >> lookAhead (S.modify (* 2) >> eof) >> S.get+          prs (S.evalStateT p 0) "" `shouldParse` (n :: Integer)++    describe "notFollowedBy" $+      it "generally works" $+        property $ \n -> do+          let p = do+                S.put n+                let notEof = notFollowedBy (S.modify (* 2) >> eof)+                some (try (anyChar <* notEof)) <* char 'x'+          prs (S.runStateT p 0) "abx" `shouldParse` ("ab", n :: Integer)++    describe "observing" $ do+      context "when inner parser succeeds" $+        it "can affect state" $+          property $ \m n -> do+            let p = do+                  S.put m+                  observing (L.modify (+ n))+            prs (S.execStateT p 0) "" `shouldParse` (m + n :: Integer)+      context "when inner parser fails" $+        it "cannot affect state" $+          property $ \m n -> do+            let p = do+                  S.put m+                  observing (L.modify (+ n) <* empty)+            prs (S.execStateT p 0) "" `shouldParse` (m :: Integer)++  describe "MonadParsec instance of lazy WriterT" $ do++    it "generally works" $+      property $ \pre post -> do+        let loggedLetter = letterChar >>= \x -> L.tell [x] >> return x+            loggedEof    = eof >> L.tell "EOF"+            p = do+              L.tell pre+              cs <- L.censor (fmap toUpper) $+                some (try (loggedLetter <* notFollowedBy loggedEof))+              L.tell post+              void loggedLetter+              return cs+        prs (L.runWriterT p) "abx" `shouldParse` ("ab", pre ++ "AB" ++ post ++ "x")++    describe "observing" $ do+      context "when inner parser succeeds" $+        it "can affect log" $+          property $ \n -> do+            let p = observing (L.tell $ Sum n)+            prs (L.execWriterT p) "" `shouldParse` (Sum n :: Sum Integer)+      context "when inner parser fails" $+        it "cannot affect log" $+          property $ \n -> do+            let p = observing (L.tell (Sum n) <* empty)+            prs (L.execWriterT p) "" `shouldParse` (mempty :: Sum Integer)++  describe "MonadParsec instance of strict WriterT" $ do++    it "generally works" $+      property $ \pre post -> do+        let loggedLetter = letterChar >>= \x -> S.tell [x] >> return x+            loggedEof    = eof >> S.tell "EOF"+            p = do+              S.tell pre+              cs <- L.censor (fmap toUpper) $+                some (try (loggedLetter <* notFollowedBy loggedEof))+              S.tell post+              void loggedLetter+              return cs+        prs (S.runWriterT p) "abx" `shouldParse` ("ab", pre ++ "AB" ++ post ++ "x")++    describe "observing" $ do+      context "when inner parser succeeds" $+        it "can affect log" $+          property $ \n -> do+            let p = observing (S.tell $ Sum n)+            prs (S.execWriterT p) "" `shouldParse` (Sum n :: Sum Integer)+      context "when inner parser fails" $+        it "cannot affect log" $+          property $ \n -> do+            let p = observing (S.tell (Sum n) <* empty)+            prs (S.execWriterT p) "" `shouldParse` (mempty :: Sum Integer)++  describe "dbg" $ do+    -- NOTE We don't test properties here to avoid flood of debugging output+    -- when the test runs.+    context "when inner parser succeeds consuming input" $ do+      it "has no effect on how parser works" $ do+        let p = dbg "char" (char 'a')+            s = "ab"+        prs  p s `shouldParse` 'a'+        prs' p s `succeedsLeaving` "b"+      it "its hints are preserved" $+        property $ \a b as -> a /= b ==> do+          let p = dbg "many chars" (many (char a)) <* empty+              s = a : b : as+          prs  p s `shouldFailWith` err (posN (1 :: Int) s) (etok a)+          prs' p s `failsLeaving` (b:as)+    context "when inner parser fails consuming input" $+      it "has no effect on how parser works" $ do+        let p = dbg "chars" (char 'a' *> char 'c')+            s = "abc"+        prs  p s `shouldFailWith` err (posN (1 :: Int) s) (utok 'b' <> etok 'c')+        prs' p s `failsLeaving` "bc"+    context "when inner parser succeeds without consuming" $ do+      it "has no effect on how parser works" $ do+        let p = dbg "return" (return 'a')+            s = "abc"+        prs  p s `shouldParse` 'a'+        prs' p s `succeedsLeaving` s+      it "its hints are preserved" $+        property $ \a b as -> a /= b ==> do+          let p = dbg "many chars" (many (char a)) <* empty+              s = b : as+          prs  p s `shouldFailWith` err posI (etok a)+          prs' p s `failsLeaving` (b:as)+    context "when inner parser fails without consuming" $+      it "has no effect on how parser works" $ do+        let p = dbg "empty" (void empty)+            s = "abc"+        prs  p s `shouldFailWith` err posI mempty+        prs' p s `failsLeaving` s++----------------------------------------------------------------------------+-- Helpers++byteToChar :: Word8 -> Char+byteToChar = chr . fromIntegral++-- | This data type represents tokens in custom input stream.++data Span = Span+  { spanStart :: SourcePos+  , spanEnd   :: SourcePos+  , spanBody  :: NonEmpty Char+  } deriving (Eq, Ord, Show)++instance Stream [Span] where+  type Token [Span] = Span+  uncons [] = Nothing+  uncons (t:ts) = Just (t, ts)+  updatePos _ _ _ (Span start end _) = (start, end)++instance Arbitrary Span where+  arbitrary = do+    start <- arbitrary+    end   <- arbitrary `suchThat` (> start)+    Span start end <$>+#if !MIN_VERSION_QuickCheck(2,9,0)+      (NE.fromList . getNonEmpty <$> arbitrary)+#else+      arbitrary+#endif++instance ShowToken Span where+  showTokens ts = concat (NE.toList . spanBody <$> ts)++type CustomParser = Parsec Dec [Span]++pSpan :: Span -> CustomParser Span+pSpan span = token testToken (Just span)+  where+    f = E.singleton . Tokens . nes+    testToken x =+      if spanBody x == spanBody span+        then Right span+        else Left (f x, f span , E.empty)++incCoincidence :: State [Span] -> [Span] -> Gen (State [Span])+incCoincidence st ts = do+  n <- getSmall <$> arbitrary+  let (pre, post) = splitAt n (stateInput st)+      pre' = zipWith (\x t -> x { spanBody = spanBody t }) pre ts+  return st { stateInput = pre' ++ post }++emulateStrParsing+  :: State String+  -> String+  -> (State String, Either (ParseError Char Dec) String)+emulateStrParsing st@(State i (pos:|z) t) s =+  if l == length s+    then (State (drop l i) (updatePosString t pos s :| z) t, Right s)+    else (st, Left $ err (pos:|z) (etoks s <> utoks (take (l + 1) i)))+  where l = length (takeWhile id $ zipWith (==) s i)
− tests/Util.hs
@@ -1,367 +0,0 @@------ QuickCheck tests for Megaparsec, utility functions for parser testing.------ Copyright © 2015–2016 Megaparsec contributors------ Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are--- met:------ * Redistributions of source code must retain the above copyright notice,---   this list of conditions and the following disclaimer.------ * Redistributions in binary form must reproduce the above copyright---   notice, this list of conditions and the following disclaimer in the---   documentation and/or other materials provided with the distribution.------ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY--- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED--- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE--- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY--- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,--- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN--- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE--- POSSIBILITY OF SUCH DAMAGE.--{-# LANGUAGE CPP              #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes       #-}-{-# OPTIONS -fno-warn-orphans #-}--module Util-  ( checkParser-  , checkParser'-  , checkCase-  , checkCase'-  , simpleParse-  , checkChar-  , checkString-  , updatePosString-  , (/=\)-  , (!=!)-  , abcRow-  , EC (..)-  , posErr-  , posErr'-  , utok-  , utoks-  , ulabel-  , ueof-  , etok-  , etoks-  , elabel-  , eeof-  , cstm )-where--import Control.Monad.Reader-import Control.Monad.Trans.Identity-import Data.Foldable (foldl')-import Data.List.NonEmpty (NonEmpty (..))-import Data.Maybe (mapMaybe, maybeToList)-import qualified Control.Monad.State.Lazy    as L-import qualified Control.Monad.State.Strict  as S-import qualified Control.Monad.Writer.Lazy   as L-import qualified Control.Monad.Writer.Strict as S-import qualified Data.List.NonEmpty          as NE-import qualified Data.Set                    as E--import Test.QuickCheck-import Test.HUnit (Assertion, (@?=))--import Text.Megaparsec.Error-import Text.Megaparsec.Pos-import Text.Megaparsec.Prim-import Text.Megaparsec.String--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*>), (<*))-#endif---- | @checkParser p r s@ tries to run parser @p@ on input @s@ to parse--- entire @s@. Result of the parsing is compared with expected result @r@,--- it should match, otherwise the property doesn't hold and the test fails.--checkParser :: (Eq a, Show a)-  => Parser a          -- ^ Parser to test-  -> Either (ParseError Char Dec) a -- ^ Expected result of parsing-  -> String            -- ^ Input for the parser-  -> Property          -- ^ Resulting property-checkParser p r s = simpleParse p s === r---- | A variant of 'checkParser' that runs given parser code with all--- standard instances of 'MonadParsec'. Useful when testing primitive--- combinators.--checkParser' :: (Eq a, Show a)-  => (forall m. MonadParsec Dec String m => m a) -- ^ Parser to test-  -> Either (ParseError Char Dec) a -- ^ Expected result of parsing-  -> String            -- ^ Input for the parser-  -> Property          -- ^ Resulting property-checkParser' p r s = conjoin-  [ checkParser p                   r s-  , checkParser (runIdentityT p)    r s-  , checkParser (runReaderT   p ()) r s-  , checkParser (L.evalStateT p ()) r s-  , checkParser (S.evalStateT p ()) r s-  , checkParser (evalWriterTL p)    r s-  , checkParser (evalWriterTS p)    r s ]---- | Similar to 'checkParser', but produces HUnit's 'Assertion's instead.--checkCase :: (Eq a, Show a)-  => Parser a          -- ^ Parser to test-  -> Either (ParseError Char Dec) a -- ^ Expected result of parsing-  -> String            -- ^ Input for the parser-  -> Assertion         -- ^ Resulting assertion-checkCase p r s = simpleParse p s @?= r---- | Similar to 'checkParser'', but produces HUnit's 'Assertion's instead.--checkCase' :: (Eq a, Show a)-  => (forall m. MonadParsec Dec String m => m a) -- ^ Parser to test-  -> Either (ParseError Char Dec) a -- ^ Expected result of parsing-  -> String            -- ^ Input for the parser-  -> Assertion         -- ^ Resulting assertion-checkCase' p r s = do-  parse p                   "" s @?= r-  parse (runIdentityT p)    "" s @?= r-  parse (runReaderT   p ()) "" s @?= r-  parse (L.evalStateT p ()) "" s @?= r-  parse (S.evalStateT p ()) "" s @?= r-  parse (evalWriterTL p)    "" s @?= r-  parse (evalWriterTS p)    "" s @?= r--evalWriterTL :: Monad m => L.WriterT [Int] m a -> m a-evalWriterTL = liftM fst . L.runWriterT-evalWriterTS :: Monad m => S.WriterT [Int] m a -> m a-evalWriterTS = liftM fst . S.runWriterT---- | @simpleParse p s@ runs parser @p@ on input @s@ and returns corresponding--- result of type @Either ParseError a@, where @a@ is type of parsed--- value. This parser tries to parser end of file too and name of input file--- is always empty string.--simpleParse :: Parser a -> String -> Either (ParseError Char Dec) a-simpleParse p = parse (p <* eof) ""---- | @checkChar p test label s@ runs parser @p@ on input @s@ and checks if--- the parser correctly parses single character that satisfies @test@. The--- character may be labelled, in this case @label@ is used to check quality--- of error messages.--checkChar-  :: Parser Char       -- ^ Parser to run-  -> (Char -> Bool)    -- ^ Predicate to test parsed char-  -> Maybe (ErrorItem Char) -- ^ Representation to use in error messages-  -> String            -- ^ Input stream-  -> Property          -- ^ Resulting property-checkChar p f rep' s = checkParser p r s-  where h = head s-        rep = Expected <$> maybeToList rep'-        r | null s = posErr 0 s (ueof : rep)-          | length s == 1 && f h = Right h-          | not (f h) = posErr 0 s (utok h : rep)-          | otherwise = posErr 1 s [utok (s !! 1), eeof]---- | @checkString p a test label s@ runs parser @p@ on input @s@ and checks if--- the result is equal to @a@ and also quality of error messages. @test@ is--- used to compare tokens. @label@ is used as expected representation of--- parser's result in error messages.--checkString-  :: Parser String     -- ^ Parser to run-  -> String            -- ^ Expected result-  -> (Char -> Char -> Bool) -- ^ Function used to compare tokens-  -> String            -- ^ Input stream-  -> Property-checkString p a' test s' = checkParser p (w a' 0 s') s'-  where w [] _ []    = Right s'-        w [] i (s:_) = posErr i s' [utok s, eeof]-        w _  0 []    = posErr 0 s' [ueof, etoks a']-        w _  i []    = posErr 0 s' [utoks (take i s'), etoks a']-        w (a:as) i (s:ss)-          | test a s  = w as i' ss-          | otherwise = posErr 0 s' [utoks (take i' s'), etoks a']-            where i'  = succ i---- | A helper function that is used to advance 'SourcePos' given a 'String'.--updatePosString-  :: Pos               -- ^ Tab width-  -> SourcePos         -- ^ Initial position-  -> String            -- ^ 'String' — collection of tokens to process-  -> SourcePos         -- ^ Final position-updatePosString w = foldl' f-  where f p t = snd (defaultUpdatePos w p t)--infix 4 /=\   -- preserve whitespace on automatic trim---- | @p /=\\ x@ runs parser @p@ on empty input and compares its result--- (which should be successful) with @x@. Succeeds when the result is equal--- to @x@, prints counterexample on failure.--(/=\) :: (Eq a, Show a) => Parser a -> a -> Property-p /=\ x = simpleParse p "" === Right x--infix 4 !=!---- | @n !=! m@ represents property that holds when results of running @n@--- and @m@ parsers are identical. This is useful when checking monad laws--- for example.--(!=!) :: (Eq a, Show a) => Parser a -> Parser a -> Property-n !=! m = simpleParse n "" === simpleParse m ""---- | @abcRow a b c@ generates string consisting of character “a” repeated--- @a@ times, character “b” repeated @b@ times, and finally character “c”--- repeated @c@ times.--abcRow :: Enum a => a -> a -> a -> String-abcRow a b c = f a 'a' ++ f b 'b' ++ f c 'c'-  where f x = replicate (fromEnum x)---- | A component of parse error, useful for fast and dirty construction of--- parse errors with 'posErr' and other helpers.--data EC-  = Unexpected (ErrorItem Char)-  | Expected   (ErrorItem Char)-  | Custom     Dec--instance Arbitrary a => Arbitrary (NonEmpty a) where-  arbitrary = NE.fromList . getNonEmpty <$> arbitrary--instance Arbitrary t => Arbitrary (ErrorItem t) where-  arbitrary = oneof-    [ Tokens <$> arbitrary-    , Label  <$> arbitrary-    , return EndOfInput ]--instance Arbitrary Pos where-  arbitrary = unsafePos . getPositive <$> arbitrary--instance Arbitrary SourcePos where-  arbitrary = SourcePos-    <$> shortString-    <*> (unsafePos <$> choose (1, 1000))-    <*> (unsafePos <$> choose (1,  100))--instance Arbitrary Dec where-  arbitrary = oneof-    [ DecFail        <$> shortString-    , DecIndentation <$> arbitrary <*> arbitrary <*> arbitrary ]--instance (Arbitrary t, Ord t, Arbitrary e, Ord e)-    => Arbitrary (ParseError t e) where-  arbitrary = ParseError-    <$> arbitrary-    <*> arbitrary-    <*> arbitrary-    <*> arbitrary--shortString :: Gen String-shortString = sized $ \n -> do-  k <- choose (0, n `div` 2)-  vectorOf k arbitrary---- | @posErr pos s ms@ is an easy way to model result of parser that fails.--- @pos@ is how many tokens (characters) has been consumed before failure.--- @s@ is input of the parser. @ms@ is a list, collection of 'Message's. See--- 'utok', 'utoks', 'ulabel', 'ueof', 'etok', 'etoks', 'elabel', and 'eeof'--- for easy ways to create error messages.--posErr-  :: Int               -- ^ How many tokens to drop from beginning of steam-  -> String            -- ^ The input stream (just a 'String' here)-  -> [EC]              -- ^ Collection of error components-  -> Either (ParseError Char Dec) a -- ^ 'ParseError' inside of 'Left'-posErr i s = posErr' (pos :| [])-  where pos = updatePosString defaultTabWidth (initialPos "") (take i s)---- | The same as 'posErr', but 'SourcePos' should be provided directly.--posErr'-  :: NonEmpty SourcePos -- ^ Position of the error-  -> [EC]              -- ^ Collection of error components-  -> Either (ParseError Char Dec) a -- ^ 'ParseError' inside of 'Left'-posErr' pos ecs = Left ParseError-  { errorPos        = pos-  , errorUnexpected = E.fromList (mapMaybe getUnexpected ecs)-  , errorExpected   = E.fromList (mapMaybe getExpected   ecs)-  , errorCustom     = E.fromList (mapMaybe getCustom     ecs) }-  where-    getUnexpected (Unexpected x) = Just x-    getUnexpected _              = Nothing-    getExpected   (Expected   x) = Just x-    getExpected   _              = Nothing-    getCustom     (Custom     x) = Just x-    getCustom     _              = Nothing---- | Construct “unexpected token” error component.--utok :: Char -> EC-utok = Unexpected . Tokens . nes---- | Construct “unexpected steam” error component. This function respects--- some conventions described in 'canonicalizeStream'.--utoks :: String -> EC-utoks = Unexpected . canonicalizeStream---- | Construct “unexpected label” error component. Do not use with empty--- strings.--ulabel :: String -> EC-ulabel = Unexpected . Label . NE.fromList---- | Construct “unexpected end of input” error component.--ueof :: EC-ueof = Unexpected EndOfInput---- | Construct “expecting token” error component.--etok :: Char -> EC-etok = Expected . Tokens . nes---- | Construct “expecting stream” error component. This function respects--- some conventions described in 'canonicalizeStream'.--etoks :: String -> EC-etoks = Expected . canonicalizeStream---- | Construct “expecting label” error component. Do not use with empty--- strings.--elabel :: String -> EC-elabel = Expected . Label . NE.fromList---- | Construct “expecting end of input” component.--eeof :: EC-eeof = Expected EndOfInput---- | Construct error component consisting of custom data.--cstm :: Dec -> EC-cstm = Custom---- | Construct appropriate 'MessageItem' representation for given token--- stream. Empty string produces 'EndOfInput', single token — a 'Token', and--- in other cases the 'TokenStream' constructor is used.--canonicalizeStream :: String -> ErrorItem Char-canonicalizeStream stream =-  case NE.nonEmpty stream of-    Nothing      -> EndOfInput-    Just xs      -> Tokens xs---- | Make a singleton non-empty list from a value.--nes :: a -> NonEmpty a-nes x = x :| []-{-# INLINE nes #-}