lib: Remove non-law-abiding Monoid instance for Journal.
This commit is contained in:
		
							parent
							
								
									702c958487
								
							
						
					
					
						commit
						e0dde6fe57
					
				@ -90,6 +90,7 @@ import Control.Monad.Extra
 | 
				
			|||||||
import Control.Monad.Reader as R
 | 
					import Control.Monad.Reader as R
 | 
				
			||||||
import Control.Monad.ST
 | 
					import Control.Monad.ST
 | 
				
			||||||
import Data.Array.ST
 | 
					import Data.Array.ST
 | 
				
			||||||
 | 
					import Data.Default (Default(..))
 | 
				
			||||||
import Data.Function ((&))
 | 
					import Data.Function ((&))
 | 
				
			||||||
import qualified Data.HashTable.ST.Cuckoo as H
 | 
					import qualified Data.HashTable.ST.Cuckoo as H
 | 
				
			||||||
import Data.List
 | 
					import Data.List
 | 
				
			||||||
@ -97,9 +98,8 @@ import Data.List.Extra (groupSort, nubSort)
 | 
				
			|||||||
import qualified Data.Map as M
 | 
					import qualified Data.Map as M
 | 
				
			||||||
import Data.Maybe
 | 
					import Data.Maybe
 | 
				
			||||||
#if !(MIN_VERSION_base(4,11,0))
 | 
					#if !(MIN_VERSION_base(4,11,0))
 | 
				
			||||||
import Data.Monoid
 | 
					import Data.Semigroup (Semigroup(..))
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
import qualified Data.Semigroup as Sem
 | 
					 | 
				
			||||||
import qualified Data.Set as S
 | 
					import qualified Data.Set as S
 | 
				
			||||||
import Data.Text (Text)
 | 
					import Data.Text (Text)
 | 
				
			||||||
import qualified Data.Text as T
 | 
					import qualified Data.Text as T
 | 
				
			||||||
@ -157,7 +157,7 @@ instance Show Journal where
 | 
				
			|||||||
--                      ,show $ map fst $ jfiles j
 | 
					--                      ,show $ map fst $ jfiles j
 | 
				
			||||||
--                      ]
 | 
					--                      ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- The monoid instance for Journal is useful for two situations.
 | 
					-- The semigroup instance for Journal is useful for two situations.
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
-- 1. concatenating finalised journals, eg with multiple -f options:
 | 
					-- 1. concatenating finalised journals, eg with multiple -f options:
 | 
				
			||||||
-- FIRST <> SECOND. The second's list fields are appended to the
 | 
					-- FIRST <> SECOND. The second's list fields are appended to the
 | 
				
			||||||
@ -168,7 +168,9 @@ instance Show Journal where
 | 
				
			|||||||
-- CHILD <> PARENT. A parsed journal's data is in reverse order, so
 | 
					-- CHILD <> PARENT. A parsed journal's data is in reverse order, so
 | 
				
			||||||
-- this gives what we want.
 | 
					-- this gives what we want.
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
instance Sem.Semigroup Journal where
 | 
					-- Note that (<>) is right-biased, so nulljournal is only a left identity.
 | 
				
			||||||
 | 
					-- In particular, this prevents Journal from being a monoid.
 | 
				
			||||||
 | 
					instance Semigroup Journal where
 | 
				
			||||||
  j1 <> j2 = Journal {
 | 
					  j1 <> j2 = Journal {
 | 
				
			||||||
     jparsedefaultyear          = jparsedefaultyear          j2
 | 
					     jparsedefaultyear          = jparsedefaultyear          j2
 | 
				
			||||||
    ,jparsedefaultcommodity     = jparsedefaultcommodity     j2
 | 
					    ,jparsedefaultcommodity     = jparsedefaultcommodity     j2
 | 
				
			||||||
@ -190,12 +192,8 @@ instance Sem.Semigroup Journal where
 | 
				
			|||||||
    ,jlastreadtime              = max (jlastreadtime j1) (jlastreadtime j2)
 | 
					    ,jlastreadtime              = max (jlastreadtime j1) (jlastreadtime j2)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
instance Monoid Journal where
 | 
					instance Default Journal where
 | 
				
			||||||
  mempty = nulljournal
 | 
					  def = nulljournal
 | 
				
			||||||
#if !(MIN_VERSION_base(4,11,0))
 | 
					 | 
				
			||||||
  -- This is redundant starting with base-4.11 / GHC 8.4.
 | 
					 | 
				
			||||||
  mappend = (Sem.<>)
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
nulljournal :: Journal
 | 
					nulljournal :: Journal
 | 
				
			||||||
nulljournal = Journal {
 | 
					nulljournal = Journal {
 | 
				
			||||||
 | 
				
			|||||||
@ -48,22 +48,24 @@ import Control.Arrow (right)
 | 
				
			|||||||
import qualified Control.Exception as C
 | 
					import qualified Control.Exception as C
 | 
				
			||||||
import Control.Monad (when)
 | 
					import Control.Monad (when)
 | 
				
			||||||
import "mtl" Control.Monad.Except (runExceptT)
 | 
					import "mtl" Control.Monad.Except (runExceptT)
 | 
				
			||||||
import Data.Default
 | 
					import Data.Default (def)
 | 
				
			||||||
import Data.Foldable (asum)
 | 
					import Data.Foldable (asum)
 | 
				
			||||||
import Data.List
 | 
					import Data.List (group, sort, sortBy)
 | 
				
			||||||
import Data.Maybe
 | 
					import Data.List.NonEmpty (nonEmpty)
 | 
				
			||||||
import Data.Ord
 | 
					import Data.Maybe (fromMaybe)
 | 
				
			||||||
 | 
					import Data.Ord (comparing)
 | 
				
			||||||
 | 
					import Data.Semigroup (sconcat)
 | 
				
			||||||
import Data.Text (Text)
 | 
					import Data.Text (Text)
 | 
				
			||||||
import qualified Data.Text as T
 | 
					import qualified Data.Text as T
 | 
				
			||||||
import Data.Time (Day)
 | 
					import Data.Time (Day)
 | 
				
			||||||
import Safe
 | 
					import Safe (headDef)
 | 
				
			||||||
import System.Directory (doesFileExist, getHomeDirectory)
 | 
					import System.Directory (doesFileExist, getHomeDirectory)
 | 
				
			||||||
import System.Environment (getEnv)
 | 
					import System.Environment (getEnv)
 | 
				
			||||||
import System.Exit (exitFailure)
 | 
					import System.Exit (exitFailure)
 | 
				
			||||||
import System.FilePath
 | 
					import System.FilePath ((<.>), (</>), splitDirectories, splitFileName)
 | 
				
			||||||
import System.Info (os)
 | 
					import System.Info (os)
 | 
				
			||||||
import System.IO
 | 
					import System.IO (stderr, writeFile)
 | 
				
			||||||
import Text.Printf
 | 
					import Text.Printf (hPrintf, printf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Hledger.Data.Dates (getCurrentDay, parsedate, showDate)
 | 
					import Hledger.Data.Dates (getCurrentDay, parsedate, showDate)
 | 
				
			||||||
import Hledger.Data.Types
 | 
					import Hledger.Data.Types
 | 
				
			||||||
@ -150,11 +152,7 @@ type PrefixedFilePath = FilePath
 | 
				
			|||||||
-- Also the final parse state saved in the Journal does span all files.
 | 
					-- Also the final parse state saved in the Journal does span all files.
 | 
				
			||||||
readJournalFiles :: InputOpts -> [PrefixedFilePath] -> IO (Either String Journal)
 | 
					readJournalFiles :: InputOpts -> [PrefixedFilePath] -> IO (Either String Journal)
 | 
				
			||||||
readJournalFiles iopts =
 | 
					readJournalFiles iopts =
 | 
				
			||||||
  (right mconcat1 . sequence <$>) . mapM (readJournalFile iopts)
 | 
					  fmap (right (maybe def sconcat . nonEmpty) . sequence) . mapM (readJournalFile iopts)
 | 
				
			||||||
  where
 | 
					 | 
				
			||||||
    mconcat1 :: Monoid t => [t] -> t
 | 
					 | 
				
			||||||
    mconcat1 [] = mempty
 | 
					 | 
				
			||||||
    mconcat1 x  = foldr1 mappend x
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Read a Journal from this file, or from stdin if the file path is -,
 | 
					-- | Read a Journal from this file, or from stdin if the file path is -,
 | 
				
			||||||
-- or return an error message. The file path can have a READER: prefix.
 | 
					-- or return an error message. The file path can have a READER: prefix.
 | 
				
			||||||
 | 
				
			|||||||
@ -222,7 +222,7 @@ rtp = runTextParser
 | 
				
			|||||||
runJournalParser, rjp
 | 
					runJournalParser, rjp
 | 
				
			||||||
  :: Monad m
 | 
					  :: Monad m
 | 
				
			||||||
  => JournalParser m a -> Text -> m (Either (ParseErrorBundle Text CustomErr) a)
 | 
					  => JournalParser m a -> Text -> m (Either (ParseErrorBundle Text CustomErr) a)
 | 
				
			||||||
runJournalParser p t = runParserT (evalStateT p mempty) "" t
 | 
					runJournalParser p t = runParserT (evalStateT p nulljournal) "" t
 | 
				
			||||||
rjp = runJournalParser
 | 
					rjp = runJournalParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Run an erroring journal parser in some monad. See also: parseWithState.
 | 
					-- | Run an erroring journal parser in some monad. See also: parseWithState.
 | 
				
			||||||
@ -232,7 +232,7 @@ runErroringJournalParser, rejp
 | 
				
			|||||||
  -> Text
 | 
					  -> Text
 | 
				
			||||||
  -> m (Either FinalParseError (Either (ParseErrorBundle Text CustomErr) a))
 | 
					  -> m (Either FinalParseError (Either (ParseErrorBundle Text CustomErr) a))
 | 
				
			||||||
runErroringJournalParser p t =
 | 
					runErroringJournalParser p t =
 | 
				
			||||||
  runExceptT $ runParserT (evalStateT p mempty) "" t
 | 
					  runExceptT $ runParserT (evalStateT p nulljournal) "" t
 | 
				
			||||||
rejp = runErroringJournalParser
 | 
					rejp = runErroringJournalParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
genericSourcePos :: SourcePos -> GenericSourcePos
 | 
					genericSourcePos :: SourcePos -> GenericSourcePos
 | 
				
			||||||
@ -680,7 +680,7 @@ amountwithoutpricep = do
 | 
				
			|||||||
-- | Parse an amount from a string, or get an error.
 | 
					-- | Parse an amount from a string, or get an error.
 | 
				
			||||||
amountp' :: String -> Amount
 | 
					amountp' :: String -> Amount
 | 
				
			||||||
amountp' s =
 | 
					amountp' s =
 | 
				
			||||||
  case runParser (evalStateT (amountp <* eof) mempty) "" (T.pack s) of
 | 
					  case runParser (evalStateT (amountp <* eof) nulljournal) "" (T.pack s) of
 | 
				
			||||||
    Right amt -> amt
 | 
					    Right amt -> amt
 | 
				
			||||||
    Left err  -> error' $ show err -- XXX should throwError
 | 
					    Left err  -> error' $ show err -- XXX should throwError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -963,7 +963,7 @@ mkPosting rules record number accountFld amountFld amountInFld amountOutFld bala
 | 
				
			|||||||
          | all isSpace str = Nothing
 | 
					          | all isSpace str = Nothing
 | 
				
			||||||
          | otherwise = Just
 | 
					          | otherwise = Just
 | 
				
			||||||
              (either (balanceerror n str) id $
 | 
					              (either (balanceerror n str) id $
 | 
				
			||||||
                runParser (evalStateT (amountp <* eof) mempty) "" $
 | 
					                runParser (evalStateT (amountp <* eof) nulljournal) "" $
 | 
				
			||||||
                T.pack $ (currency++) $ simplifySign str
 | 
					                T.pack $ (currency++) $ simplifySign str
 | 
				
			||||||
              ,nullsourcepos)  -- XXX parse position to show when assertion fails,
 | 
					              ,nullsourcepos)  -- XXX parse position to show when assertion fails,
 | 
				
			||||||
                               -- the csv record's line number would be good
 | 
					                               -- the csv record's line number would be good
 | 
				
			||||||
@ -1039,7 +1039,7 @@ chooseAmount rules record currency amountFld amountInFld amountOutFld =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   parseAmount currency amountstr =
 | 
					   parseAmount currency amountstr =
 | 
				
			||||||
     either (amounterror amountstr) (Mixed . (:[]))
 | 
					     either (amounterror amountstr) (Mixed . (:[]))
 | 
				
			||||||
     <$> runParser (evalStateT (amountp <* eof) mempty) ""
 | 
					     <$> runParser (evalStateT (amountp <* eof) nulljournal) ""
 | 
				
			||||||
     <$> T.pack
 | 
					     <$> T.pack
 | 
				
			||||||
     <$> (currency++)
 | 
					     <$> (currency++)
 | 
				
			||||||
     <$> simplifySign
 | 
					     <$> simplifySign
 | 
				
			||||||
 | 
				
			|||||||
@ -85,7 +85,7 @@ import Control.Monad.Trans.Class (lift)
 | 
				
			|||||||
import Data.Either (isRight)
 | 
					import Data.Either (isRight)
 | 
				
			||||||
import qualified Data.Map.Strict as M
 | 
					import qualified Data.Map.Strict as M
 | 
				
			||||||
#if !(MIN_VERSION_base(4,11,0))
 | 
					#if !(MIN_VERSION_base(4,11,0))
 | 
				
			||||||
import Data.Monoid ((<>))
 | 
					import Data.Semigroup ((<>))
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
import Data.Text (Text)
 | 
					import Data.Text (Text)
 | 
				
			||||||
import Data.String
 | 
					import Data.String
 | 
				
			||||||
@ -298,7 +298,7 @@ includedirectivep = do
 | 
				
			|||||||
      put $ updatedChildj <> parentj
 | 
					      put $ updatedChildj <> parentj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    newJournalWithParseStateFrom :: FilePath -> Journal -> Journal
 | 
					    newJournalWithParseStateFrom :: FilePath -> Journal -> Journal
 | 
				
			||||||
    newJournalWithParseStateFrom filepath j = mempty{
 | 
					    newJournalWithParseStateFrom filepath j = nulljournal{
 | 
				
			||||||
      jparsedefaultyear      = jparsedefaultyear j
 | 
					      jparsedefaultyear      = jparsedefaultyear j
 | 
				
			||||||
      ,jparsedefaultcommodity = jparsedefaultcommodity j
 | 
					      ,jparsedefaultcommodity = jparsedefaultcommodity j
 | 
				
			||||||
      ,jparseparentaccounts   = jparseparentaccounts j
 | 
					      ,jparseparentaccounts   = jparseparentaccounts j
 | 
				
			||||||
@ -747,7 +747,7 @@ tests_JournalReader = tests "JournalReader" [
 | 
				
			|||||||
    ,test "yearless date with no default year" $ assertParseError datep "1/1" "current year is unknown"
 | 
					    ,test "yearless date with no default year" $ assertParseError datep "1/1" "current year is unknown"
 | 
				
			||||||
    ,test "yearless date with default year" $ do
 | 
					    ,test "yearless date with default year" $ do
 | 
				
			||||||
      let s = "1/1"
 | 
					      let s = "1/1"
 | 
				
			||||||
      ep <- parseWithState mempty{jparsedefaultyear=Just 2018} datep s
 | 
					      ep <- parseWithState nulljournal{jparsedefaultyear=Just 2018} datep s
 | 
				
			||||||
      either (assertFailure . ("parse error at "++) . customErrorBundlePretty) (const $ return ()) ep
 | 
					      either (assertFailure . ("parse error at "++) . customErrorBundlePretty) (const $ return ()) ep
 | 
				
			||||||
    ,test "no leading zero" $ assertParse datep "2018/1/1"
 | 
					    ,test "no leading zero" $ assertParse datep "2018/1/1"
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
				
			|||||||
@ -25,8 +25,9 @@ where
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import Control.Monad.Except (ExceptT, runExceptT)
 | 
					import Control.Monad.Except (ExceptT, runExceptT)
 | 
				
			||||||
import Control.Monad.State.Strict (StateT, evalStateT, execStateT)
 | 
					import Control.Monad.State.Strict (StateT, evalStateT, execStateT)
 | 
				
			||||||
 | 
					import Data.Default (Default(..))
 | 
				
			||||||
#if !(MIN_VERSION_base(4,11,0))
 | 
					#if !(MIN_VERSION_base(4,11,0))
 | 
				
			||||||
import Data.Monoid ((<>))
 | 
					import Data.Semigroup ((<>))
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
-- import Data.CallStack
 | 
					-- import Data.CallStack
 | 
				
			||||||
import Data.List (isInfixOf)
 | 
					import Data.List (isInfixOf)
 | 
				
			||||||
@ -73,35 +74,35 @@ assertRight (Left a)  = assertFailure $ "expected Right, got (Left " ++ show a +
 | 
				
			|||||||
-- | Assert that this stateful parser runnable in IO successfully parses
 | 
					-- | Assert that this stateful parser runnable in IO successfully parses
 | 
				
			||||||
-- all of the given input text, showing the parse error if it fails.
 | 
					-- all of the given input text, showing the parse error if it fails.
 | 
				
			||||||
-- Suitable for hledger's JournalParser parsers.
 | 
					-- Suitable for hledger's JournalParser parsers.
 | 
				
			||||||
assertParse :: (HasCallStack, Eq a, Show a, Monoid st) =>
 | 
					assertParse :: (HasCallStack, Eq a, Show a, Default st) =>
 | 
				
			||||||
  StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> Assertion
 | 
					  StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> Assertion
 | 
				
			||||||
assertParse parser input = do
 | 
					assertParse parser input = do
 | 
				
			||||||
  ep <- runParserT (evalStateT (parser <* eof) mempty) "" input
 | 
					  ep <- runParserT (evalStateT (parser <* eof) def) "" input
 | 
				
			||||||
  either (assertFailure.(++"\n").("\nparse error at "++).customErrorBundlePretty)
 | 
					  either (assertFailure.(++"\n").("\nparse error at "++).customErrorBundlePretty)
 | 
				
			||||||
         (const $ return ())
 | 
					         (const $ return ())
 | 
				
			||||||
         ep
 | 
					         ep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Assert a parser produces an expected value.
 | 
					-- | Assert a parser produces an expected value.
 | 
				
			||||||
assertParseEq :: (HasCallStack, Eq a, Show a, Monoid st) =>
 | 
					assertParseEq :: (HasCallStack, Eq a, Show a, Default st) =>
 | 
				
			||||||
  StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> a -> Assertion
 | 
					  StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> a -> Assertion
 | 
				
			||||||
assertParseEq parser input expected = assertParseEqOn parser input id expected
 | 
					assertParseEq parser input expected = assertParseEqOn parser input id expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Like assertParseEq, but transform the parse result with the given function
 | 
					-- | Like assertParseEq, but transform the parse result with the given function
 | 
				
			||||||
-- before comparing it.
 | 
					-- before comparing it.
 | 
				
			||||||
assertParseEqOn :: (HasCallStack, Eq b, Show b, Monoid st) =>
 | 
					assertParseEqOn :: (HasCallStack, Eq b, Show b, Default st) =>
 | 
				
			||||||
  StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (a -> b) -> b -> Assertion
 | 
					  StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (a -> b) -> b -> Assertion
 | 
				
			||||||
assertParseEqOn parser input f expected = do
 | 
					assertParseEqOn parser input f expected = do
 | 
				
			||||||
  ep <- runParserT (evalStateT (parser <* eof) mempty) "" input
 | 
					  ep <- runParserT (evalStateT (parser <* eof) def) "" input
 | 
				
			||||||
  either (assertFailure . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
 | 
					  either (assertFailure . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
 | 
				
			||||||
         (assertEqual "" expected . f)
 | 
					         (assertEqual "" expected . f)
 | 
				
			||||||
         ep
 | 
					         ep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | Assert that this stateful parser runnable in IO fails to parse
 | 
					-- | Assert that this stateful parser runnable in IO fails to parse
 | 
				
			||||||
-- the given input text, with a parse error containing the given string.
 | 
					-- the given input text, with a parse error containing the given string.
 | 
				
			||||||
assertParseError :: (HasCallStack, Eq a, Show a, Monoid st) =>
 | 
					assertParseError :: (HasCallStack, Eq a, Show a, Default st) =>
 | 
				
			||||||
  StateT st (ParsecT CustomErr T.Text IO) a -> String -> String -> Assertion
 | 
					  StateT st (ParsecT CustomErr T.Text IO) a -> String -> String -> Assertion
 | 
				
			||||||
assertParseError parser input errstr = do
 | 
					assertParseError parser input errstr = do
 | 
				
			||||||
  ep <- runParserT (evalStateT parser mempty) "" (T.pack input)
 | 
					  ep <- runParserT (evalStateT parser def) "" (T.pack input)
 | 
				
			||||||
  case ep of
 | 
					  case ep of
 | 
				
			||||||
    Right v -> assertFailure $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n"
 | 
					    Right v -> assertFailure $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n"
 | 
				
			||||||
    Left e  -> do
 | 
					    Left e  -> do
 | 
				
			||||||
@ -113,28 +114,28 @@ assertParseError parser input errstr = do
 | 
				
			|||||||
-- | Run a stateful parser in IO like assertParse, then assert that the
 | 
					-- | Run a stateful parser in IO like assertParse, then assert that the
 | 
				
			||||||
-- final state (the wrapped state, not megaparsec's internal state),
 | 
					-- final state (the wrapped state, not megaparsec's internal state),
 | 
				
			||||||
-- transformed by the given function, matches the given expected value.
 | 
					-- transformed by the given function, matches the given expected value.
 | 
				
			||||||
assertParseStateOn :: (HasCallStack, Eq b, Show b, Monoid st) =>
 | 
					assertParseStateOn :: (HasCallStack, Eq b, Show b, Default st) =>
 | 
				
			||||||
     StateT st (ParsecT CustomErr T.Text IO) a
 | 
					     StateT st (ParsecT CustomErr T.Text IO) a
 | 
				
			||||||
  -> T.Text
 | 
					  -> T.Text
 | 
				
			||||||
  -> (st -> b)
 | 
					  -> (st -> b)
 | 
				
			||||||
  -> b
 | 
					  -> b
 | 
				
			||||||
  -> Assertion
 | 
					  -> Assertion
 | 
				
			||||||
assertParseStateOn parser input f expected = do
 | 
					assertParseStateOn parser input f expected = do
 | 
				
			||||||
  es <- runParserT (execStateT (parser <* eof) mempty) "" input
 | 
					  es <- runParserT (execStateT (parser <* eof) def) "" input
 | 
				
			||||||
  case es of
 | 
					  case es of
 | 
				
			||||||
    Left err -> assertFailure $ (++"\n") $ ("\nparse error at "++) $ customErrorBundlePretty err
 | 
					    Left err -> assertFailure $ (++"\n") $ ("\nparse error at "++) $ customErrorBundlePretty err
 | 
				
			||||||
    Right s  -> assertEqual "" expected $ f s
 | 
					    Right s  -> assertEqual "" expected $ f s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- | These "E" variants of the above are suitable for hledger's ErroringJournalParser parsers.
 | 
					-- | These "E" variants of the above are suitable for hledger's ErroringJournalParser parsers.
 | 
				
			||||||
assertParseE
 | 
					assertParseE
 | 
				
			||||||
  :: (HasCallStack, Eq a, Show a, Monoid st)
 | 
					  :: (HasCallStack, Eq a, Show a, Default st)
 | 
				
			||||||
  => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
 | 
					  => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
 | 
				
			||||||
  -> T.Text
 | 
					  -> T.Text
 | 
				
			||||||
  -> Assertion
 | 
					  -> Assertion
 | 
				
			||||||
assertParseE parser input = do
 | 
					assertParseE parser input = do
 | 
				
			||||||
  let filepath = ""
 | 
					  let filepath = ""
 | 
				
			||||||
  eep <- runExceptT $
 | 
					  eep <- runExceptT $
 | 
				
			||||||
           runParserT (evalStateT (parser <* eof) mempty) filepath input
 | 
					           runParserT (evalStateT (parser <* eof) def) filepath input
 | 
				
			||||||
  case eep of
 | 
					  case eep of
 | 
				
			||||||
    Left finalErr ->
 | 
					    Left finalErr ->
 | 
				
			||||||
      let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
 | 
					      let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
 | 
				
			||||||
@ -145,7 +146,7 @@ assertParseE parser input = do
 | 
				
			|||||||
             ep
 | 
					             ep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
assertParseEqE
 | 
					assertParseEqE
 | 
				
			||||||
  :: (Monoid st, Eq a, Show a, HasCallStack)
 | 
					  :: (Default st, Eq a, Show a, HasCallStack)
 | 
				
			||||||
  => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
 | 
					  => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
 | 
				
			||||||
  -> T.Text
 | 
					  -> T.Text
 | 
				
			||||||
  -> a
 | 
					  -> a
 | 
				
			||||||
@ -153,7 +154,7 @@ assertParseEqE
 | 
				
			|||||||
assertParseEqE parser input expected = assertParseEqOnE parser input id expected
 | 
					assertParseEqE parser input expected = assertParseEqOnE parser input id expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
assertParseEqOnE
 | 
					assertParseEqOnE
 | 
				
			||||||
  :: (HasCallStack, Eq b, Show b, Monoid st)
 | 
					  :: (HasCallStack, Eq b, Show b, Default st)
 | 
				
			||||||
  => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
 | 
					  => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
 | 
				
			||||||
  -> T.Text
 | 
					  -> T.Text
 | 
				
			||||||
  -> (a -> b)
 | 
					  -> (a -> b)
 | 
				
			||||||
@ -161,7 +162,7 @@ assertParseEqOnE
 | 
				
			|||||||
  -> Assertion
 | 
					  -> Assertion
 | 
				
			||||||
assertParseEqOnE parser input f expected = do
 | 
					assertParseEqOnE parser input f expected = do
 | 
				
			||||||
  let filepath = ""
 | 
					  let filepath = ""
 | 
				
			||||||
  eep <- runExceptT $ runParserT (evalStateT (parser <* eof) mempty) filepath input
 | 
					  eep <- runExceptT $ runParserT (evalStateT (parser <* eof) def) filepath input
 | 
				
			||||||
  case eep of
 | 
					  case eep of
 | 
				
			||||||
    Left finalErr ->
 | 
					    Left finalErr ->
 | 
				
			||||||
      let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
 | 
					      let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
 | 
				
			||||||
@ -172,14 +173,14 @@ assertParseEqOnE parser input f expected = do
 | 
				
			|||||||
             ep
 | 
					             ep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
assertParseErrorE
 | 
					assertParseErrorE
 | 
				
			||||||
  :: (Monoid st, Eq a, Show a, HasCallStack)
 | 
					  :: (Default st, Eq a, Show a, HasCallStack)
 | 
				
			||||||
  => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
 | 
					  => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
 | 
				
			||||||
  -> T.Text
 | 
					  -> T.Text
 | 
				
			||||||
  -> String
 | 
					  -> String
 | 
				
			||||||
  -> Assertion
 | 
					  -> Assertion
 | 
				
			||||||
assertParseErrorE parser input errstr = do
 | 
					assertParseErrorE parser input errstr = do
 | 
				
			||||||
  let filepath = ""
 | 
					  let filepath = ""
 | 
				
			||||||
  eep <- runExceptT $ runParserT (evalStateT parser mempty) filepath input
 | 
					  eep <- runExceptT $ runParserT (evalStateT parser def) filepath input
 | 
				
			||||||
  case eep of
 | 
					  case eep of
 | 
				
			||||||
    Left finalErr -> do
 | 
					    Left finalErr -> do
 | 
				
			||||||
      let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
 | 
					      let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
 | 
				
			||||||
 | 
				
			|||||||
@ -166,7 +166,7 @@ validatePostings acctRes amtRes = let
 | 
				
			|||||||
                          foldl (\s a -> s <> parseErrorTextPretty a) "" .
 | 
					                          foldl (\s a -> s <> parseErrorTextPretty a) "" .
 | 
				
			||||||
                          bundleErrors)
 | 
					                          bundleErrors)
 | 
				
			||||||
  checkAccount = errorToFormMsg . runParser (accountnamep <* eof) "" . T.strip
 | 
					  checkAccount = errorToFormMsg . runParser (accountnamep <* eof) "" . T.strip
 | 
				
			||||||
  checkAmount = errorToFormMsg . runParser (evalStateT (amountp <* eof) mempty) "" . T.strip
 | 
					  checkAmount = errorToFormMsg . runParser (evalStateT (amountp <* eof) nulljournal) "" . T.strip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  -- Add errors to forms with zero or one rows if the form is not a FormMissing
 | 
					  -- Add errors to forms with zero or one rows if the form is not a FormMissing
 | 
				
			||||||
  result :: [(Text, Text, Either (Maybe Text, Maybe Text) Posting)]
 | 
					  result :: [(Text, Text, Either (Maybe Text, Maybe Text) Posting)]
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user