lib: switch to megaparsec 7
This commit is contained in:
		
							parent
							
								
									26369c28a3
								
							
						
					
					
						commit
						3d2584d869
					
				| @ -77,6 +77,7 @@ where | ||||
| 
 | ||||
| import Prelude () | ||||
| import "base-compat-batteries" Prelude.Compat | ||||
| import Control.Applicative.Permutations | ||||
| import Control.Monad | ||||
| import "base-compat-batteries" Data.List.Compat | ||||
| import Data.Default | ||||
| @ -96,7 +97,7 @@ import Data.Time.LocalTime | ||||
| import Safe (headMay, lastMay, readMay) | ||||
| import Text.Megaparsec | ||||
| import Text.Megaparsec.Char | ||||
| import Text.Megaparsec.Perm | ||||
| import Text.Megaparsec.Custom | ||||
| import Text.Printf | ||||
| 
 | ||||
| import Hledger.Data.Types | ||||
| @ -314,13 +315,14 @@ earliest (Just d1) (Just d2) = Just $ min d1 d2 | ||||
| 
 | ||||
| -- | Parse a period expression to an Interval and overall DateSpan using | ||||
| -- the provided reference date, or return a parse error. | ||||
| parsePeriodExpr :: Day -> Text -> Either (ParseError Char CustomErr) (Interval, DateSpan) | ||||
| parsePeriodExpr | ||||
|   :: Day -> Text -> Either (ParseErrorBundle Text CustomErr) (Interval, DateSpan) | ||||
| parsePeriodExpr refdate s = parsewith (periodexprp refdate <* eof) (T.toLower s) | ||||
| 
 | ||||
| -- | Like parsePeriodExpr, but call error' on failure. | ||||
| parsePeriodExpr' :: Day -> Text -> (Interval, DateSpan) | ||||
| parsePeriodExpr' refdate s = | ||||
|   either (error' . ("failed to parse:" ++) . parseErrorPretty) id $ | ||||
|   either (error' . ("failed to parse:" ++) . customErrorBundlePretty) id $ | ||||
|   parsePeriodExpr refdate s | ||||
| 
 | ||||
| maybePeriod :: Day -> Text -> Maybe (Interval,DateSpan) | ||||
| @ -380,13 +382,14 @@ fixSmartDateStr :: Day -> Text -> String | ||||
| fixSmartDateStr d s = either | ||||
|                        (\e->error' $ printf "could not parse date %s %s" (show s) (show e)) | ||||
|                        id | ||||
|                        $ (fixSmartDateStrEither d s :: Either (ParseError Char CustomErr) String) | ||||
|                        $ (fixSmartDateStrEither d s :: Either (ParseErrorBundle Text CustomErr) String) | ||||
| 
 | ||||
| -- | A safe version of fixSmartDateStr. | ||||
| fixSmartDateStrEither :: Day -> Text -> Either (ParseError Char CustomErr) String | ||||
| fixSmartDateStrEither :: Day -> Text -> Either (ParseErrorBundle Text CustomErr) String | ||||
| fixSmartDateStrEither d = either Left (Right . showDate) . fixSmartDateStrEither' d | ||||
| 
 | ||||
| fixSmartDateStrEither' :: Day -> Text -> Either (ParseError Char CustomErr) Day | ||||
| fixSmartDateStrEither' | ||||
|   :: Day -> Text -> Either (ParseErrorBundle Text CustomErr) Day | ||||
| fixSmartDateStrEither' d s = case parsewith smartdateonly (T.toLower s) of | ||||
|                                Right sd -> Right $ fixSmartDate d sd | ||||
|                                Left e -> Left e | ||||
| @ -987,7 +990,9 @@ reportingintervalp = choice' [ | ||||
|                           return $ DayOfMonth n, | ||||
|                        do string' "every" | ||||
|                           let mnth = choice' [month, mon] >>= \(_,m,_) -> return (read m) | ||||
|                           d_o_y <- makePermParser $ DayOfYear <$$> try (skipMany spacenonewline *> mnth) <||> try (skipMany spacenonewline *> nth) | ||||
|                           d_o_y <- runPermutation $ | ||||
|                             DayOfYear <$> toPermutation (try (skipMany spacenonewline *> mnth)) | ||||
|                                       <*> toPermutation (try (skipMany spacenonewline *> nth)) | ||||
|                           optOf_ "year" | ||||
|                           return d_o_y, | ||||
|                        do string' "every" | ||||
|  | ||||
| @ -194,12 +194,15 @@ rawOptsToInputOpts rawopts = InputOpts{ | ||||
| --- * parsing utilities | ||||
| 
 | ||||
| -- | Run a text parser in the identity monad. See also: parseWithState. | ||||
| runTextParser, rtp :: TextParser Identity a -> Text -> Either (ParseError Char CustomErr) a | ||||
| runTextParser, rtp | ||||
|   :: TextParser Identity a -> Text -> Either (ParseErrorBundle Text CustomErr) a | ||||
| runTextParser p t =  runParser p "" t | ||||
| rtp = runTextParser | ||||
| 
 | ||||
| -- | Run a journal parser in some monad. See also: parseWithState. | ||||
| runJournalParser, rjp :: Monad m => JournalParser m a -> Text -> m (Either (ParseError Char CustomErr) a) | ||||
| runJournalParser, rjp | ||||
|   :: Monad m | ||||
|   => JournalParser m a -> Text -> m (Either (ParseErrorBundle Text CustomErr) a) | ||||
| runJournalParser p t = runParserT (evalStateT p mempty) "" t | ||||
| rjp = runJournalParser | ||||
| 
 | ||||
| @ -208,7 +211,7 @@ runErroringJournalParser, rejp | ||||
|   :: Monad m | ||||
|   => ErroringJournalParser m a | ||||
|   -> Text | ||||
|   -> m (Either FinalParseError (Either (ParseError Char CustomErr) a)) | ||||
|   -> m (Either FinalParseError (Either (ParseErrorBundle Text CustomErr) a)) | ||||
| runErroringJournalParser p t = | ||||
|   runExceptT $ runParserT (evalStateT p mempty) "" t | ||||
| rejp = runErroringJournalParser | ||||
| @ -246,10 +249,10 @@ parseAndFinaliseJournal parser iopts f txt = do | ||||
|     runParserT (evalStateT parser initJournal) f txt | ||||
|   case eep of | ||||
|     Left finalParseError -> | ||||
|       throwError $ finalParseErrorPretty $ attachSource f txt finalParseError | ||||
|       throwError $ finalErrorBundlePretty $ attachSource f txt finalParseError | ||||
| 
 | ||||
|     Right ep -> case ep of | ||||
|       Left e -> throwError $ customParseErrorPretty txt e | ||||
|       Left e -> throwError $ customErrorBundlePretty e | ||||
| 
 | ||||
|       Right pj -> | ||||
|         let pj' = if auto_ iopts then applyTransactionModifiers pj else pj in | ||||
| @ -267,7 +270,7 @@ parseAndFinaliseJournal' parser iopts f txt = do | ||||
|         , jincludefilestack = [f] } | ||||
|   ep <- liftIO $ runParserT (evalStateT parser initJournal) f txt | ||||
|   case ep of | ||||
|     Left e   -> throwError $ customParseErrorPretty txt e | ||||
|     Left e   -> throwError $ customErrorBundlePretty e | ||||
| 
 | ||||
|     Right pj ->  | ||||
|       let pj' = if auto_ iopts then applyTransactionModifiers pj else pj in | ||||
| @ -385,43 +388,43 @@ datep = do | ||||
| 
 | ||||
| datep' :: Maybe Year -> TextParser m Day | ||||
| datep' mYear = do | ||||
|   startPos <- getPosition | ||||
|   startOffset <- getOffset | ||||
|   d1 <- decimal <?> "year or month" | ||||
|   sep <- satisfy isDateSepChar <?> "date separator" | ||||
|   d2 <- decimal <?> "month or day" | ||||
|   fullDate startPos d1 sep d2 <|> partialDate startPos mYear d1 sep d2 | ||||
|   fullDate startOffset d1 sep d2 <|> partialDate startOffset mYear d1 sep d2 | ||||
|   <?> "full or partial date" | ||||
| 
 | ||||
|   where | ||||
| 
 | ||||
|   fullDate :: SourcePos -> Integer -> Char -> Int -> TextParser m Day | ||||
|   fullDate startPos year sep1 month = do | ||||
|   fullDate :: Int -> Integer -> Char -> Int -> TextParser m Day | ||||
|   fullDate startOffset year sep1 month = do | ||||
|     sep2 <- satisfy isDateSepChar <?> "date separator" | ||||
|     day <- decimal <?> "day" | ||||
|     endPos <- getPosition | ||||
|     endOffset <- getOffset | ||||
|     let dateStr = show year ++ [sep1] ++ show month ++ [sep2] ++ show day | ||||
| 
 | ||||
|     when (sep1 /= sep2) $ customFailure $ parseErrorAtRegion startPos endPos $ | ||||
|     when (sep1 /= sep2) $ customFailure $ parseErrorAtRegion startOffset endOffset $ | ||||
|       "invalid date (mixing date separators is not allowed): " ++ dateStr | ||||
| 
 | ||||
|     case fromGregorianValid year month day of | ||||
|       Nothing -> customFailure $ parseErrorAtRegion startPos endPos $ | ||||
|       Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $ | ||||
|                    "well-formed but invalid date: " ++ dateStr | ||||
|       Just date -> pure $! date | ||||
| 
 | ||||
|   partialDate | ||||
|     :: SourcePos -> Maybe Year -> Integer -> Char -> Int -> TextParser m Day | ||||
|   partialDate startPos mYear month sep day = do | ||||
|     endPos <- getPosition | ||||
|     :: Int -> Maybe Year -> Integer -> Char -> Int -> TextParser m Day | ||||
|   partialDate startOffset mYear month sep day = do | ||||
|     endOffset <- getOffset | ||||
|     case mYear of | ||||
|       Just year -> | ||||
|         case fromGregorianValid year (fromIntegral month) day of | ||||
|           Nothing -> customFailure $ parseErrorAtRegion startPos endPos $ | ||||
|           Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $ | ||||
|                       "well-formed but invalid date: " ++ dateStr | ||||
|           Just date -> pure $! date | ||||
|         where dateStr = show year ++ [sep] ++ show month ++ [sep] ++ show day | ||||
| 
 | ||||
|       Nothing -> customFailure $ parseErrorAtRegion startPos endPos $ | ||||
|       Nothing -> customFailure $ parseErrorAtRegion startOffset endOffset $ | ||||
|         "partial date "++dateStr++" found, but the current year is unknown" | ||||
|         where dateStr = show month ++ [sep] ++ show day | ||||
| 
 | ||||
| @ -449,26 +452,26 @@ datetimep' mYear = do | ||||
|   where | ||||
|     timeOfDay :: TextParser m TimeOfDay | ||||
|     timeOfDay = do | ||||
|       pos1 <- getPosition | ||||
|       off1 <- getOffset | ||||
|       h' <- twoDigitDecimal <?> "hour" | ||||
|       pos2 <- getPosition | ||||
|       off2 <- getOffset | ||||
|       unless (h' >= 0 && h' <= 23) $ customFailure $ | ||||
|         parseErrorAtRegion pos1 pos2 "invalid time (bad hour)" | ||||
|         parseErrorAtRegion off1 off2 "invalid time (bad hour)" | ||||
| 
 | ||||
|       char ':' <?> "':' (hour-minute separator)" | ||||
|       pos3 <- getPosition | ||||
|       off3 <- getOffset | ||||
|       m' <- twoDigitDecimal <?> "minute" | ||||
|       pos4 <- getPosition | ||||
|       off4 <- getOffset | ||||
|       unless (m' >= 0 && m' <= 59) $ customFailure $ | ||||
|         parseErrorAtRegion pos3 pos4 "invalid time (bad minute)" | ||||
|         parseErrorAtRegion off3 off4 "invalid time (bad minute)" | ||||
| 
 | ||||
|       s' <- option 0 $ do | ||||
|         char ':' <?> "':' (minute-second separator)" | ||||
|         pos5 <- getPosition | ||||
|         off5 <- getOffset | ||||
|         s' <- twoDigitDecimal <?> "second" | ||||
|         pos6 <- getPosition | ||||
|         off6 <- getOffset | ||||
|         unless (s' >= 0 && s' <= 59) $ customFailure $ | ||||
|           parseErrorAtRegion pos5 pos6 "invalid time (bad second)" | ||||
|           parseErrorAtRegion off5 off6 "invalid time (bad second)" | ||||
|           -- we do not support leap seconds | ||||
|         pure s' | ||||
| 
 | ||||
| @ -565,22 +568,22 @@ amountwithoutpricep = do | ||||
|     suggestedStyle <- getAmountStyle c | ||||
|     commodityspaced <- lift $ skipMany' spacenonewline | ||||
|     sign2 <- lift $ signp | ||||
|     posBeforeNum <- getPosition | ||||
|     offBeforeNum <- getOffset | ||||
|     ambiguousRawNum <- lift rawnumberp | ||||
|     mExponent <- lift $ optional $ try exponentp | ||||
|     posAfterNum <- getPosition | ||||
|     let numRegion = (posBeforeNum, posAfterNum) | ||||
|     offAfterNum <- getOffset | ||||
|     let numRegion = (offBeforeNum, offAfterNum) | ||||
|     (q,prec,mdec,mgrps) <- lift $ interpretNumber numRegion suggestedStyle ambiguousRawNum mExponent | ||||
|     let s = amountstyle{ascommodityside=L, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps} | ||||
|     return $ Amount c (sign (sign2 q)) NoPrice s mult | ||||
| 
 | ||||
|   rightornosymbolamountp :: Bool -> (Decimal -> Decimal) -> JournalParser m Amount | ||||
|   rightornosymbolamountp mult sign = label "amount" $ do | ||||
|     posBeforeNum <- getPosition | ||||
|     offBeforeNum <- getOffset | ||||
|     ambiguousRawNum <- lift rawnumberp | ||||
|     mExponent <- lift $ optional $ try exponentp | ||||
|     posAfterNum <- getPosition | ||||
|     let numRegion = (posBeforeNum, posAfterNum) | ||||
|     offAfterNum <- getOffset | ||||
|     let numRegion = (offBeforeNum, offAfterNum) | ||||
|     mSpaceAndCommodity <- lift $ optional $ try $ (,) <$> skipMany' spacenonewline <*> commoditysymbolp | ||||
|     case mSpaceAndCommodity of | ||||
|       -- right symbol amount | ||||
| @ -604,7 +607,7 @@ amountwithoutpricep = do | ||||
|   -- For reducing code duplication. Doesn't parse anything. Has the type | ||||
|   -- of a parser only in order to throw parse errors (for convenience). | ||||
|   interpretNumber | ||||
|     :: (SourcePos, SourcePos) | ||||
|     :: (Int, Int) -- offsets | ||||
|     -> Maybe AmountStyle | ||||
|     -> Either AmbiguousNumber RawNumber | ||||
|     -> Maybe Int | ||||
| @ -671,7 +674,7 @@ partialbalanceassertionp :: JournalParser m BalanceAssertion | ||||
| partialbalanceassertionp = optional $ do | ||||
|   sourcepos <- try $ do | ||||
|     lift (skipMany spacenonewline) | ||||
|     sourcepos <- genericSourcePos <$> lift getPosition | ||||
|     sourcepos <- genericSourcePos <$> lift getSourcePos | ||||
|     char '=' | ||||
|     pure sourcepos | ||||
|   lift (skipMany spacenonewline) | ||||
| @ -830,10 +833,10 @@ rawnumberp = label "number" $ do | ||||
|     fail "invalid number (invalid use of separator)" | ||||
| 
 | ||||
|   mExtraFragment <- optional $ lookAhead $ try $ | ||||
|     char ' ' *> getPosition <* digitChar | ||||
|     char ' ' *> getOffset <* digitChar | ||||
|   case mExtraFragment of | ||||
|     Just pos -> customFailure $ | ||||
|                   parseErrorAt pos "invalid number (excessive trailing digits)" | ||||
|     Just off -> customFailure $ | ||||
|                   parseErrorAt off "invalid number (excessive trailing digits)" | ||||
|     Nothing -> pure () | ||||
| 
 | ||||
|   return $ dbg8 "rawnumberp" rawNumber | ||||
| @ -1193,19 +1196,19 @@ commenttagsanddatesp mYear = do | ||||
| -- default date is provided. A missing year in DATE2 will be inferred | ||||
| -- from DATE. | ||||
| -- | ||||
| -- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[2016/1/2=3/4]" | ||||
| -- >>> either (Left . customErrorBundlePretty) Right $ rtp (bracketeddatetagsp Nothing) "[2016/1/2=3/4]" | ||||
| -- Right [("date",2016-01-02),("date2",2016-03-04)] | ||||
| -- | ||||
| -- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[1]" | ||||
| -- >>> either (Left . customErrorBundlePretty) Right $ rtp (bracketeddatetagsp Nothing) "[1]" | ||||
| -- Left ...not a bracketed date... | ||||
| -- | ||||
| -- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[2016/1/32]" | ||||
| -- Left ...1:11:...well-formed but invalid date: 2016/1/32... | ||||
| -- >>> either (Left . customErrorBundlePretty) Right $ rtp (bracketeddatetagsp Nothing) "[2016/1/32]" | ||||
| -- Left ...1:2:...well-formed but invalid date: 2016/1/32... | ||||
| -- | ||||
| -- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[1/31]" | ||||
| -- Left ...1:6:...partial date 1/31 found, but the current year is unknown... | ||||
| -- >>> either (Left . customErrorBundlePretty) Right $ rtp (bracketeddatetagsp Nothing) "[1/31]" | ||||
| -- Left ...1:2:...partial date 1/31 found, but the current year is unknown... | ||||
| -- | ||||
| -- >>> either (Left . parseErrorPretty) Right $ rtp (bracketeddatetagsp Nothing) "[0123456789/-.=/-.=]" | ||||
| -- >>> either (Left . customErrorBundlePretty) Right $ rtp (bracketeddatetagsp Nothing) "[0123456789/-.=/-.=]" | ||||
| -- Left ...1:13:...expecting month or day... | ||||
| -- | ||||
| bracketeddatetagsp | ||||
|  | ||||
| @ -38,7 +38,6 @@ import Control.Monad.Except | ||||
| import Control.Monad.State.Strict (StateT, get, modify', evalStateT) | ||||
| import Data.Char (toLower, isDigit, isSpace, ord) | ||||
| import "base-compat-batteries" Data.List.Compat | ||||
| import Data.List.NonEmpty (fromList) | ||||
| import Data.Maybe | ||||
| import Data.Ord | ||||
| import qualified Data.Set as S | ||||
| @ -59,12 +58,12 @@ import System.FilePath | ||||
| import qualified Data.Csv as Cassava | ||||
| import qualified Data.Csv.Parser.Megaparsec as CassavaMP | ||||
| import qualified Data.ByteString as B | ||||
| import Data.ByteString.Lazy (fromStrict) | ||||
| import qualified Data.ByteString.Lazy as BL | ||||
| import Data.Foldable | ||||
| import Text.Megaparsec hiding (parse) | ||||
| import Text.Megaparsec.Char | ||||
| import Text.Megaparsec.Custom | ||||
| import Text.Printf (printf) | ||||
| import Data.Word | ||||
| 
 | ||||
| import Hledger.Data | ||||
| import Hledger.Utils | ||||
| @ -76,7 +75,7 @@ type Record = [Field] | ||||
| 
 | ||||
| type Field = String | ||||
| 
 | ||||
| data CSVError = CSVError (ParseError Word8 CassavaMP.ConversionError) | ||||
| data CSVError = CSVError (ParseErrorBundle BL.ByteString CassavaMP.ConversionError) | ||||
|     deriving Show | ||||
| 
 | ||||
| reader :: Reader | ||||
| @ -193,7 +192,7 @@ parseCassava separator path content = | ||||
|         Left  msg -> Left $ CSVError msg | ||||
|         Right a   -> Right a | ||||
|     where parseResult = fmap parseResultToCsv $ CassavaMP.decodeWith (decodeOptions separator) Cassava.NoHeader path lazyContent | ||||
|           lazyContent = fromStrict $ T.encodeUtf8 content | ||||
|           lazyContent = BL.fromStrict $ T.encodeUtf8 content | ||||
| 
 | ||||
| decodeOptions :: Char -> Cassava.DecodeOptions | ||||
| decodeOptions separator = Cassava.defaultDecodeOptions { | ||||
| @ -431,19 +430,19 @@ parseAndValidateCsvRules :: FilePath -> T.Text -> ExceptT String IO CsvRules | ||||
| parseAndValidateCsvRules rulesfile s = do | ||||
|   let rules = parseCsvRules rulesfile s | ||||
|   case rules of | ||||
|     Left e -> ExceptT $ return $ Left $ parseErrorPretty e | ||||
|     Left e -> ExceptT $ return $ Left $ customErrorBundlePretty e | ||||
|     Right r -> do | ||||
|                r_ <- liftIO $ runExceptT $ validateRules r | ||||
|                ExceptT $ case r_ of | ||||
|                  Left  s -> return $ Left $ parseErrorPretty $ makeParseError rulesfile s | ||||
|                  Left  s -> return $ Left $ parseErrorPretty $ makeParseError s | ||||
|                  Right r -> return $ Right r | ||||
| 
 | ||||
|   where | ||||
|     makeParseError :: FilePath -> String -> ParseError Char String | ||||
|     makeParseError f s = FancyError (fromList [initialPos f]) (S.singleton $ ErrorFail s) | ||||
|     makeParseError :: String -> ParseError T.Text String | ||||
|     makeParseError s = FancyError 0 (S.singleton $ ErrorFail s) | ||||
| 
 | ||||
| -- | Parse this text as CSV conversion rules. The file path is for error messages. | ||||
| parseCsvRules :: FilePath -> T.Text -> Either (ParseError Char CustomErr) CsvRules | ||||
| parseCsvRules :: FilePath -> T.Text -> Either (ParseErrorBundle T.Text CustomErr) CsvRules | ||||
| -- parseCsvRules rulesfile s = runParser csvrulesfile nullrules{baseAccount=takeBaseName rulesfile} rulesfile s | ||||
| parseCsvRules rulesfile s = | ||||
|   runParser (evalStateT rulesp rules) rulesfile s | ||||
| @ -513,7 +512,7 @@ directives = | ||||
|   ] | ||||
| 
 | ||||
| directivevalp :: CsvRulesParser String | ||||
| directivevalp = anyChar `manyTill` lift eolof | ||||
| directivevalp = anySingle `manyTill` lift eolof | ||||
| 
 | ||||
| fieldnamelistp :: CsvRulesParser [CsvFieldName] | ||||
| fieldnamelistp = (do | ||||
| @ -588,7 +587,7 @@ assignmentseparatorp = do | ||||
| fieldvalp :: CsvRulesParser String | ||||
| fieldvalp = do | ||||
|   lift $ dbgparse 2 "trying fieldvalp" | ||||
|   anyChar `manyTill` lift eolof | ||||
|   anySingle `manyTill` lift eolof | ||||
| 
 | ||||
| conditionalblockp :: CsvRulesParser ConditionalBlock | ||||
| conditionalblockp = do | ||||
| @ -631,7 +630,7 @@ regexp = do | ||||
|   lift $ dbgparse 3 "trying regexp" | ||||
|   notFollowedBy matchoperatorp | ||||
|   c <- lift nonspace | ||||
|   cs <- anyChar `manyTill` lift eolof | ||||
|   cs <- anySingle `manyTill` lift eolof | ||||
|   return $ strip $ c:cs | ||||
| 
 | ||||
| -- fieldmatcher = do | ||||
|  | ||||
| @ -180,30 +180,32 @@ includedirectivep = do | ||||
|   lift (skipSome spacenonewline) | ||||
|   filename <- T.unpack <$> takeWhileP Nothing (/= '\n') -- don't consume newline yet | ||||
| 
 | ||||
|   parentpos <- getPosition | ||||
|   parentoff <- getOffset | ||||
|   parentpos <- getSourcePos | ||||
| 
 | ||||
|   filepaths <- getFilePaths parentpos filename | ||||
|   filepaths <- getFilePaths parentoff parentpos filename | ||||
| 
 | ||||
|   forM_ filepaths $ parseChild parentpos | ||||
| 
 | ||||
|   void newline | ||||
| 
 | ||||
|   where | ||||
|     getFilePaths :: MonadIO m => SourcePos -> FilePath -> JournalParser m [FilePath] | ||||
|     getFilePaths parserpos filename = do | ||||
|     getFilePaths | ||||
|       :: MonadIO m => Int -> SourcePos -> FilePath -> JournalParser m [FilePath] | ||||
|     getFilePaths parseroff parserpos filename = do | ||||
|         curdir <- lift $ expandPath (takeDirectory $ sourceName parserpos) "" | ||||
|                          `orRethrowIOError` (show parserpos ++ " locating " ++ filename) | ||||
|         -- Compiling filename as a glob pattern works even if it is a literal | ||||
|         fileglob <- case tryCompileWith compDefault{errorRecovery=False} filename of | ||||
|             Right x -> pure x | ||||
|             Left e -> customFailure $ | ||||
|                         parseErrorAt parserpos $ "Invalid glob pattern: " ++ e | ||||
|                         parseErrorAt parseroff $ "Invalid glob pattern: " ++ e | ||||
|         -- Get all matching files in the current working directory, sorting in | ||||
|         -- lexicographic order to simulate the output of 'ls'. | ||||
|         filepaths <- liftIO $ sort <$> globDir1 fileglob curdir | ||||
|         if (not . null) filepaths | ||||
|             then pure filepaths | ||||
|             else customFailure $ parseErrorAt parserpos $ | ||||
|             else customFailure $ parseErrorAt parseroff $ | ||||
|                    "No existing files match pattern: " ++ filename | ||||
| 
 | ||||
|     parseChild :: MonadIO m => SourcePos -> FilePath -> ErroringJournalParser m () | ||||
| @ -229,7 +231,6 @@ includedirectivep = do | ||||
|       -- discard child's parse info,  combine other fields | ||||
|       put $ updatedChildj <> parentj | ||||
| 
 | ||||
| 
 | ||||
|     newJournalWithParseStateFrom :: FilePath -> Journal -> Journal | ||||
|     newJournalWithParseStateFrom filepath j = mempty{ | ||||
|       jparsedefaultyear      = jparsedefaultyear j | ||||
| @ -279,17 +280,17 @@ commoditydirectivep = commoditydirectiveonelinep <|> commoditydirectivemultiline | ||||
| -- >>> Right _ <- rjp commoditydirectiveonelinep "commodity $1.00 ; blah\n" | ||||
| commoditydirectiveonelinep :: JournalParser m () | ||||
| commoditydirectiveonelinep = do | ||||
|   (pos, Amount{acommodity,astyle}) <- try $ do | ||||
|   (off, Amount{acommodity,astyle}) <- try $ do | ||||
|     string "commodity" | ||||
|     lift (skipSome spacenonewline) | ||||
|     pos <- getPosition | ||||
|     off <- getOffset | ||||
|     amount <- amountp | ||||
|     pure $ (pos, amount) | ||||
|     pure $ (off, amount) | ||||
|   lift (skipMany spacenonewline) | ||||
|   _ <- lift followingcommentp | ||||
|   let comm = Commodity{csymbol=acommodity, cformat=Just $ dbg2 "style from commodity directive" astyle} | ||||
|   if asdecimalpoint astyle == Nothing | ||||
|   then customFailure $ parseErrorAt pos pleaseincludedecimalpoint | ||||
|   then customFailure $ parseErrorAt off pleaseincludedecimalpoint | ||||
|   else modify' (\j -> j{jcommodities=M.insert acommodity comm $ jcommodities j}) | ||||
| 
 | ||||
| pleaseincludedecimalpoint :: String | ||||
| @ -316,15 +317,15 @@ formatdirectivep :: CommoditySymbol -> JournalParser m AmountStyle | ||||
| formatdirectivep expectedsym = do | ||||
|   string "format" | ||||
|   lift (skipSome spacenonewline) | ||||
|   pos <- getPosition | ||||
|   off <- getOffset | ||||
|   Amount{acommodity,astyle} <- amountp | ||||
|   _ <- lift followingcommentp | ||||
|   if acommodity==expectedsym | ||||
|     then  | ||||
|       if asdecimalpoint astyle == Nothing | ||||
|       then customFailure $ parseErrorAt pos pleaseincludedecimalpoint | ||||
|       then customFailure $ parseErrorAt off pleaseincludedecimalpoint | ||||
|       else return $ dbg2 "style from format subdirective" astyle | ||||
|     else customFailure $ parseErrorAt pos $ | ||||
|     else customFailure $ parseErrorAt off $ | ||||
|          printf "commodity directive symbol \"%s\" and format directive symbol \"%s\" should be the same" expectedsym acommodity | ||||
| 
 | ||||
| keywordp :: String -> JournalParser m () | ||||
| @ -366,7 +367,7 @@ basicaliasp = do | ||||
|   old <- rstrip <$> (some $ noneOf ("=" :: [Char])) | ||||
|   char '=' | ||||
|   skipMany spacenonewline | ||||
|   new <- rstrip <$> anyChar `manyTill` eolof  -- eol in journal, eof in command lines, normally | ||||
|   new <- rstrip <$> anySingle `manyTill` eolof  -- eol in journal, eof in command lines, normally | ||||
|   return $ BasicAlias (T.pack old) (T.pack new) | ||||
| 
 | ||||
| regexaliasp :: TextParser m AccountAlias | ||||
| @ -378,7 +379,7 @@ regexaliasp = do | ||||
|   skipMany spacenonewline | ||||
|   char '=' | ||||
|   skipMany spacenonewline | ||||
|   repl <- anyChar `manyTill` eolof | ||||
|   repl <- anySingle `manyTill` eolof | ||||
|   return $ RegexAlias re repl | ||||
| 
 | ||||
| endaliasesdirectivep :: JournalParser m () | ||||
| @ -413,11 +414,11 @@ defaultcommoditydirectivep :: JournalParser m () | ||||
| defaultcommoditydirectivep = do | ||||
|   char 'D' <?> "default commodity" | ||||
|   lift (skipSome spacenonewline) | ||||
|   pos <- getPosition | ||||
|   off <- getOffset | ||||
|   Amount{acommodity,astyle} <- amountp | ||||
|   lift restofline | ||||
|   if asdecimalpoint astyle == Nothing | ||||
|   then customFailure $ parseErrorAt pos pleaseincludedecimalpoint | ||||
|   then customFailure $ parseErrorAt off pleaseincludedecimalpoint | ||||
|   else setDefaultCommodityAndStyle (acommodity, astyle) | ||||
| 
 | ||||
| marketpricedirectivep :: JournalParser m MarketPrice | ||||
| @ -471,12 +472,12 @@ periodictransactionp = do | ||||
|   char '~' <?> "periodic transaction" | ||||
|   lift $ skipMany spacenonewline | ||||
|   -- a period expression | ||||
|   pos <- getPosition | ||||
|   off <- getOffset | ||||
|   d <- liftIO getCurrentDay | ||||
|   (periodtxt, (interval, span)) <- lift $ first T.strip <$> match (periodexprp d) | ||||
|   -- In periodic transactions, the period expression has an additional constraint: | ||||
|   case checkPeriodicTransactionStartDate interval span periodtxt of | ||||
|     Just e -> customFailure $ parseErrorAt pos e | ||||
|     Just e -> customFailure $ parseErrorAt off e | ||||
|     Nothing -> pure () | ||||
|   -- The line can end here, or it can continue with one or more spaces | ||||
|   -- and then zero or more of the following fields. A bit awkward. | ||||
| @ -511,7 +512,7 @@ periodictransactionp = do | ||||
| transactionp :: JournalParser m Transaction | ||||
| transactionp = do | ||||
|   -- dbgparse 0 "transactionp" | ||||
|   startpos <- getPosition | ||||
|   startpos <- getSourcePos | ||||
|   date <- datep <?> "transaction" | ||||
|   edate <- optional (lift $ secondarydatep date) <?> "secondary date" | ||||
|   lookAhead (lift spacenonewline <|> newline) <?> "whitespace or newline" | ||||
| @ -521,7 +522,7 @@ transactionp = do | ||||
|   (comment, tags) <- lift transactioncommentp | ||||
|   let year = first3 $ toGregorian date | ||||
|   postings <- postingsp (Just year) | ||||
|   endpos <- getPosition | ||||
|   endpos <- getSourcePos | ||||
|   let sourcepos = journalSourcePos startpos endpos | ||||
|   return $ txnTieKnot $ Transaction 0 sourcepos date edate status code description comment tags postings "" | ||||
| 
 | ||||
| @ -589,8 +590,9 @@ tests_JournalReader = tests "JournalReader" [ | ||||
|     test "YYYY.MM.DD" $ expectParse datep "2018.01.01" | ||||
|     test "yearless date with no default year" $ expectParseError datep "1/1" "current year is unknown" | ||||
|     test "yearless date with default year" $ do  | ||||
|       ep <- parseWithState mempty{jparsedefaultyear=Just 2018} datep "1/1" | ||||
|       either (fail.("parse error at "++).parseErrorPretty) (const ok) ep | ||||
|       let s = "1/1" | ||||
|       ep <- parseWithState mempty{jparsedefaultyear=Just 2018} datep s | ||||
|       either (fail.("parse error at "++).customErrorBundlePretty) (const ok) ep | ||||
|     test "no leading zero" $ expectParse datep "2018/1/1" | ||||
| 
 | ||||
|   ,test "datetimep" $ do | ||||
|  | ||||
| @ -58,7 +58,6 @@ import           Data.Maybe (fromMaybe) | ||||
| import           Data.Text (Text) | ||||
| import qualified Data.Text as T | ||||
| import           Text.Megaparsec hiding (parse) | ||||
| import           Text.Megaparsec.Char | ||||
| 
 | ||||
| import           Hledger.Data | ||||
| -- XXX too much reuse ? | ||||
| @ -105,7 +104,7 @@ timeclockfilep = do many timeclockitemp | ||||
| -- | Parse a timeclock entry. | ||||
| timeclockentryp :: JournalParser m TimeclockEntry | ||||
| timeclockentryp = do | ||||
|   sourcepos <- genericSourcePos <$> lift getPosition | ||||
|   sourcepos <- genericSourcePos <$> lift getSourcePos | ||||
|   code <- oneOf ("bhioO" :: [Char]) | ||||
|   lift (skipSome spacenonewline) | ||||
|   datetime <- datetimep | ||||
|  | ||||
| @ -104,7 +104,7 @@ timedotdayp = do | ||||
| timedotentryp :: JournalParser m Transaction | ||||
| timedotentryp = do | ||||
|   traceParse "  timedotentryp" | ||||
|   pos <- genericSourcePos <$> getPosition | ||||
|   pos <- genericSourcePos <$> getSourcePos | ||||
|   lift (skipMany spacenonewline) | ||||
|   a <- modifiedaccountnamep | ||||
|   lift (skipMany spacenonewline) | ||||
|  | ||||
| @ -48,7 +48,7 @@ import Data.Default | ||||
| import Safe | ||||
| import System.Console.ANSI (hSupportsANSI) | ||||
| import System.IO (stdout) | ||||
| import Text.Megaparsec.Error | ||||
| import Text.Megaparsec.Custom | ||||
| 
 | ||||
| import Hledger.Data | ||||
| import Hledger.Query | ||||
| @ -240,11 +240,11 @@ beginDatesFromRawOpts d = catMaybes . map (begindatefromrawopt d) | ||||
|   where | ||||
|     begindatefromrawopt d (n,v) | ||||
|       | n == "begin" = | ||||
|           either (\e -> usageError $ "could not parse "++n++" date: "++parseErrorPretty e) Just $ | ||||
|           either (\e -> usageError $ "could not parse "++n++" date: "++customErrorBundlePretty e) Just $ | ||||
|           fixSmartDateStrEither' d (T.pack v) | ||||
|       | n == "period" = | ||||
|         case | ||||
|           either (\e -> usageError $ "could not parse period option: "++parseErrorPretty e) id $ | ||||
|           either (\e -> usageError $ "could not parse period option: "++customErrorBundlePretty e) id $ | ||||
|           parsePeriodExpr d (stripquotes $ T.pack v) | ||||
|         of | ||||
|           (_, DateSpan (Just b) _) -> Just b | ||||
| @ -258,11 +258,11 @@ endDatesFromRawOpts d = catMaybes . map (enddatefromrawopt d) | ||||
|   where | ||||
|     enddatefromrawopt d (n,v) | ||||
|       | n == "end" = | ||||
|           either (\e -> usageError $ "could not parse "++n++" date: "++parseErrorPretty e) Just $ | ||||
|           either (\e -> usageError $ "could not parse "++n++" date: "++customErrorBundlePretty e) Just $ | ||||
|           fixSmartDateStrEither' d (T.pack v) | ||||
|       | n == "period" = | ||||
|         case | ||||
|           either (\e -> usageError $ "could not parse period option: "++parseErrorPretty e) id $ | ||||
|           either (\e -> usageError $ "could not parse period option: "++customErrorBundlePretty e) id $ | ||||
|           parsePeriodExpr d (stripquotes $ T.pack v) | ||||
|         of | ||||
|           (_, DateSpan _ (Just e)) -> Just e | ||||
| @ -276,7 +276,7 @@ intervalFromRawOpts = lastDef NoInterval . catMaybes . map intervalfromrawopt | ||||
|   where | ||||
|     intervalfromrawopt (n,v) | ||||
|       | n == "period" = | ||||
|           either (\e -> usageError $ "could not parse period option: "++parseErrorPretty e) (Just . fst) $ | ||||
|           either (\e -> usageError $ "could not parse period option: "++customErrorBundlePretty e) (Just . fst) $ | ||||
|           parsePeriodExpr nulldate (stripquotes $ T.pack v) -- reference date does not affect the interval | ||||
|       | n == "daily"     = Just $ Days 1 | ||||
|       | n == "weekly"    = Just $ Weeks 1 | ||||
|  | ||||
| @ -225,7 +225,7 @@ plogAt lvl | ||||
| -- (position and next input) to the console. (See also megaparsec's dbg.) | ||||
| traceParse :: String -> TextParser m () | ||||
| traceParse msg = do | ||||
|   pos <- getPosition | ||||
|   pos <- getSourcePos | ||||
|   next <- (T.take peeklength) `fmap` getInput | ||||
|   let (l,c) = (sourceLine pos, sourceColumn pos) | ||||
|       s  = printf "at line %2d col %2d: %s" (unPos l) (unPos c) (show next) :: String | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| {-# LANGUAGE FlexibleContexts #-} | ||||
| {-# LANGUAGE TypeFamilies #-} | ||||
| 
 | ||||
| module Hledger.Utils.Parse ( | ||||
| @ -72,15 +73,21 @@ choiceInState = choice . map try | ||||
| surroundedBy :: Applicative m => m openclose -> m a -> m a | ||||
| surroundedBy p = between p p | ||||
| 
 | ||||
| parsewith :: Parsec e Text a -> Text -> Either (ParseError Char e) a | ||||
| parsewith :: Parsec e Text a -> Text -> Either (ParseErrorBundle Text e) a | ||||
| parsewith p = runParser p "" | ||||
| 
 | ||||
| parsewithString :: Parsec e String a -> String -> Either (ParseError Char e) a | ||||
| parsewithString | ||||
|   :: Parsec e String a -> String -> Either (ParseErrorBundle String e) a | ||||
| parsewithString p = runParser p "" | ||||
| 
 | ||||
| -- | Run a stateful parser with some initial state on a text. | ||||
| -- See also: runTextParser, runJournalParser. | ||||
| parseWithState :: Monad m => st -> StateT st (ParsecT CustomErr Text m) a -> Text -> m (Either (ParseError Char CustomErr) a) | ||||
| parseWithState | ||||
|   :: Monad m | ||||
|   => st | ||||
|   -> StateT st (ParsecT CustomErr Text m) a | ||||
|   -> Text | ||||
|   -> m (Either (ParseErrorBundle Text CustomErr) a) | ||||
| parseWithState ctx p s = runParserT (evalStateT p ctx) "" s | ||||
| 
 | ||||
| parseWithState' | ||||
| @ -88,19 +95,23 @@ parseWithState' | ||||
|   => st | ||||
|   -> StateT st (ParsecT e s Identity) a | ||||
|   -> s | ||||
|   -> (Either (ParseError (Token s) e) a) | ||||
|   -> (Either (ParseErrorBundle s e) a) | ||||
| parseWithState' ctx p s = runParser (evalStateT p ctx) "" s | ||||
| 
 | ||||
| fromparse :: (Show t, Show e) => Either (ParseError t e) a -> a | ||||
| fromparse | ||||
|   :: (Show t, Show (Token t), Show e) => Either (ParseErrorBundle t e) a -> a | ||||
| fromparse = either parseerror id | ||||
| 
 | ||||
| parseerror :: (Show t, Show e) => ParseError t e -> a | ||||
| parseerror :: (Show t, Show (Token t), Show e) => ParseErrorBundle t e -> a | ||||
| parseerror e = error' $ showParseError e | ||||
| 
 | ||||
| showParseError :: (Show t, Show e) => ParseError t e -> String | ||||
| showParseError | ||||
|   :: (Show t, Show (Token t), Show e) | ||||
|   => ParseErrorBundle t e -> String | ||||
| showParseError e = "parse error at " ++ show e | ||||
| 
 | ||||
| showDateParseError :: (Show t, Show e) => ParseError t e -> String | ||||
| showDateParseError | ||||
|   :: (Show t, Show (Token t), Show e) => ParseErrorBundle t e -> String | ||||
| showDateParseError e = printf "date parse error (%s)" (intercalate ", " $ tail $ lines $ show e) | ||||
| 
 | ||||
| nonspace :: TextParser m Char | ||||
| @ -113,7 +124,7 @@ spacenonewline :: (Stream s, Char ~ Token s) => ParsecT CustomErr s m Char | ||||
| spacenonewline = satisfy isNonNewlineSpace | ||||
| 
 | ||||
| restofline :: TextParser m String | ||||
| restofline = anyChar `manyTill` newline | ||||
| restofline = anySingle `manyTill` newline | ||||
| 
 | ||||
| eolof :: TextParser m () | ||||
| eolof = (newline >> return ()) <|> eof | ||||
|  | ||||
| @ -112,7 +112,9 @@ expectParse :: (Monoid st, Eq a, Show a, HasCallStack) => | ||||
|   StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> E.Test () | ||||
| expectParse parser input = do | ||||
|   ep <- E.io (runParserT (evalStateT (parser <* eof) mempty) "" input) | ||||
|   either (fail.(++"\n").("\nparse error at "++).parseErrorPretty) (const ok) ep | ||||
|   either (fail.(++"\n").("\nparse error at "++).customErrorBundlePretty) | ||||
|          (const ok) | ||||
|          ep | ||||
| 
 | ||||
| -- Suitable for hledger's ErroringJournalParser parsers. | ||||
| expectParseE | ||||
| @ -126,11 +128,12 @@ expectParseE parser input = do | ||||
|            runParserT (evalStateT (parser <* eof) mempty) filepath input | ||||
|   case eep of | ||||
|     Left finalErr -> | ||||
|       let prettyErr = finalParseErrorPretty $ attachSource filepath input finalErr | ||||
|       let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr | ||||
|       in  fail $ "parse error at " <> prettyErr | ||||
|     Right ep -> either (fail.(++"\n").("\nparse error at "++).parseErrorPretty) | ||||
|                        (const ok) | ||||
|                        ep | ||||
|     Right ep -> | ||||
|       either (fail.(++"\n").("\nparse error at "++).customErrorBundlePretty) | ||||
|              (const ok) | ||||
|              ep | ||||
| 
 | ||||
| -- | Test that this stateful parser runnable in IO fails to parse  | ||||
| -- the given input text, with a parse error containing the given string.  | ||||
| @ -141,7 +144,7 @@ expectParseError parser input errstr = do | ||||
|   case ep of | ||||
|     Right v -> fail $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n" | ||||
|     Left e  -> do | ||||
|       let e' = parseErrorPretty e | ||||
|       let e' = customErrorBundlePretty e | ||||
|       if errstr `isInfixOf` e' | ||||
|       then ok | ||||
|       else fail $ "\nparse error is not as expected:\n" ++ e' ++ "\n" | ||||
| @ -157,14 +160,14 @@ expectParseErrorE parser input errstr = do | ||||
|   eep <- E.io $ runExceptT $ runParserT (evalStateT parser mempty) filepath input | ||||
|   case eep of | ||||
|     Left finalErr -> do | ||||
|       let prettyErr = finalParseErrorPretty $ attachSource filepath input finalErr | ||||
|       let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr | ||||
|       if errstr `isInfixOf` prettyErr | ||||
|       then ok | ||||
|       else fail $ "\nparse error is not as expected:\n" ++ prettyErr ++ "\n" | ||||
|     Right ep -> case ep of | ||||
|       Right v -> fail $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n" | ||||
|       Left e  -> do | ||||
|         let e' = parseErrorPretty e | ||||
|         let e' = customErrorBundlePretty e | ||||
|         if errstr `isInfixOf` e' | ||||
|         then ok | ||||
|         else fail $ "\nparse error is not as expected:\n" ++ e' ++ "\n" | ||||
| @ -189,7 +192,9 @@ expectParseEqOn :: (Monoid st, Eq b, Show b, HasCallStack) => | ||||
|   StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (a -> b) -> b -> E.Test () | ||||
| expectParseEqOn parser input f expected = do | ||||
|   ep <- E.io $ runParserT (evalStateT (parser <* eof) mempty) "" input | ||||
|   either (fail . (++"\n") . ("\nparse error at "++) . parseErrorPretty) (expectEqPP expected . f) ep | ||||
|   either (fail . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty) | ||||
|          (expectEqPP expected . f) | ||||
|          ep | ||||
| 
 | ||||
| expectParseEqOnE | ||||
|   :: (Monoid st, Eq b, Show b, HasCallStack) | ||||
| @ -204,10 +209,10 @@ expectParseEqOnE parser input f expected = do | ||||
|            runParserT (evalStateT (parser <* eof) mempty) filepath input | ||||
|   case eep of | ||||
|     Left finalErr -> | ||||
|       let prettyErr = finalParseErrorPretty $ attachSource filepath input finalErr | ||||
|       let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr | ||||
|       in  fail $ "parse error at " <> prettyErr | ||||
|     Right ep -> | ||||
|       either (fail . (++"\n") . ("\nparse error at "++) . parseErrorPretty) | ||||
|       either (fail . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty) | ||||
|              (expectEqPP expected . f) | ||||
|              ep | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| {-# LANGUAGE BangPatterns #-} | ||||
| {-# LANGUAGE FlexibleContexts #-} | ||||
| {-# LANGUAGE LambdaCase #-} | ||||
| {-# LANGUAGE PackageImports #-} | ||||
| @ -14,7 +13,7 @@ module Text.Megaparsec.Custom ( | ||||
|   parseErrorAtRegion, | ||||
| 
 | ||||
|   -- * Pretty-printing custom parse errors | ||||
|   customParseErrorPretty, | ||||
|   customErrorBundlePretty, | ||||
| 
 | ||||
| 
 | ||||
|   -- * Final parse error types | ||||
| @ -24,7 +23,7 @@ module Text.Megaparsec.Custom ( | ||||
|   FinalParseErrorBundle', | ||||
| 
 | ||||
|   -- * Constructing final parse errors | ||||
|   errorFinal, | ||||
|   finalError, | ||||
|   finalFancyFailure, | ||||
|   finalFail, | ||||
|   finalCustomFailure, | ||||
| @ -34,7 +33,7 @@ module Text.Megaparsec.Custom ( | ||||
|   attachSource, | ||||
| 
 | ||||
|   -- * Pretty-printing final parse errors | ||||
|   finalParseErrorPretty, | ||||
|   finalErrorBundlePretty, | ||||
| ) | ||||
| where | ||||
| 
 | ||||
| @ -45,10 +44,8 @@ import Control.Monad.Except | ||||
| import Control.Monad.State.Strict (StateT, evalStateT) | ||||
| import Data.Foldable (asum, toList) | ||||
| import qualified Data.List.NonEmpty as NE | ||||
| import Data.Proxy (Proxy (Proxy)) | ||||
| import qualified Data.Set as S | ||||
| import Data.Text (Text) | ||||
| import Data.Void (Void) | ||||
| import Text.Megaparsec | ||||
| 
 | ||||
| 
 | ||||
| @ -60,8 +57,8 @@ import Text.Megaparsec | ||||
| data CustomErr | ||||
|   -- | Fail with a message at a specific source position interval. The | ||||
|   -- interval must be contained within a single line. | ||||
|   = ErrorFailAt SourcePos -- Starting position | ||||
|                 Pos -- Ending position (column; same line as start) | ||||
|   = ErrorFailAt Int -- Starting offset | ||||
|                 Int -- Ending offset | ||||
|                 String -- Error message | ||||
|   deriving (Show, Eq, Ord) | ||||
| 
 | ||||
| @ -70,62 +67,68 @@ data CustomErr | ||||
| -- derive it, but this requires an (orphan) instance for 'ParseError'. | ||||
| -- Hopefully this does not cause any trouble. | ||||
| 
 | ||||
| deriving instance (Ord c, Ord e) => Ord (ParseError c e) | ||||
| deriving instance (Eq (Token c), Ord (Token c), Ord c, Ord e) => Ord (ParseError c e) | ||||
| 
 | ||||
| instance ShowErrorComponent CustomErr where | ||||
|   showErrorComponent (ErrorFailAt _ _ errMsg) = errMsg | ||||
|   errorComponentLen (ErrorFailAt startOffset endOffset _) = | ||||
|     endOffset - startOffset | ||||
| 
 | ||||
| 
 | ||||
| --- * Constructing custom parse errors | ||||
| 
 | ||||
| -- | Fail at a specific source position. | ||||
| -- | Fail at a specific source position, given by the raw offset from the | ||||
| -- start of the input stream (the number of tokens processed at that | ||||
| -- point). | ||||
| 
 | ||||
| parseErrorAt :: SourcePos -> String -> CustomErr | ||||
| parseErrorAt pos msg = ErrorFailAt pos (sourceColumn pos) msg | ||||
| parseErrorAt :: Int -> String -> CustomErr | ||||
| parseErrorAt offset msg = ErrorFailAt offset (offset+1) msg | ||||
| 
 | ||||
| -- | Fail at a specific source interval (within a single line). The | ||||
| -- interval is inclusive on the left and exclusive on the right; that is, | ||||
| -- it spans from the start position to just before (and not including) the | ||||
| -- end position. | ||||
| -- | Fail at a specific source interval, given by the raw offsets of its | ||||
| -- endpoints from the start of the input stream (the numbers of tokens | ||||
| -- processed at those points). | ||||
| -- | ||||
| -- Note that care must be taken to ensure that the specified interval does | ||||
| -- not span multiple lines of the input source, as this will not be | ||||
| -- checked. | ||||
| 
 | ||||
| parseErrorAtRegion | ||||
|   :: SourcePos -- ^ Start position | ||||
|   -> SourcePos -- ^ End position | ||||
|   -> String    -- ^ Error message | ||||
|   :: Int    -- ^ Start offset | ||||
|   -> Int    -- ^ End end offset | ||||
|   -> String -- ^ Error message | ||||
|   -> CustomErr | ||||
| parseErrorAtRegion startPos endPos msg = | ||||
|   let startCol = sourceColumn startPos | ||||
|       endCol' = mkPos $ subtract 1 $ unPos $ sourceColumn endPos | ||||
|       endCol = if startCol <= endCol' | ||||
|                     && sourceLine startPos == sourceLine endPos | ||||
|                then endCol' else startCol | ||||
|   in  ErrorFailAt startPos endCol msg | ||||
| parseErrorAtRegion startOffset endOffset msg = | ||||
|   if startOffset < endOffset | ||||
|     then ErrorFailAt startOffset endOffset msg | ||||
|     else ErrorFailAt startOffset (startOffset+1) msg | ||||
| 
 | ||||
| 
 | ||||
| --- * Pretty-printing custom parse errors | ||||
| 
 | ||||
| -- | Pretty-print our custom parse errors and display the line on which | ||||
| -- the parse error occured. Use this instead of 'parseErrorPretty'. | ||||
| -- the parse error occured. | ||||
| -- | ||||
| -- If any custom errors are present, arbitrarily take the first one (since | ||||
| -- only one custom error should be used at a time). | ||||
| -- Use this instead of 'errorBundlePretty' when custom parse errors are | ||||
| -- thrown, otherwise the continuous highlighting in the pretty-printed | ||||
| -- parse error will be displaced from its proper position. | ||||
| 
 | ||||
| customParseErrorPretty :: Text -> ParseError Char CustomErr -> String | ||||
| customParseErrorPretty source err = case findCustomError err of | ||||
|   Nothing -> customParseErrorPretty' source err pos1 | ||||
| 
 | ||||
|   Just (ErrorFailAt sourcePos col errMsg) -> | ||||
|     let newPositionStack = sourcePos NE.:| NE.tail (errorPos err) | ||||
|         errorIntervalLength = mkPos $ max 1 $ | ||||
|           unPos col - unPos (sourceColumn sourcePos) + 1 | ||||
| 
 | ||||
|         newErr :: ParseError Char Void | ||||
|         newErr = FancyError newPositionStack (S.singleton (ErrorFail errMsg)) | ||||
| 
 | ||||
|     in  customParseErrorPretty' source newErr errorIntervalLength | ||||
| customErrorBundlePretty :: ParseErrorBundle Text CustomErr -> String | ||||
| customErrorBundlePretty errBundle = | ||||
|   let errBundle' = errBundle | ||||
|         { bundleErrors = fmap setCustomErrorOffset $ bundleErrors errBundle } | ||||
|   in  errorBundlePretty errBundle' | ||||
| 
 | ||||
|   where | ||||
|     findCustomError :: ParseError Char CustomErr -> Maybe CustomErr | ||||
|     setCustomErrorOffset | ||||
|       :: ParseError Text CustomErr -> ParseError Text CustomErr | ||||
|     setCustomErrorOffset err = case findCustomError err of | ||||
|       Nothing -> err | ||||
|       Just errFailAt@(ErrorFailAt startOffset _ _) -> | ||||
|         FancyError startOffset $ S.singleton $ ErrorCustom errFailAt | ||||
| 
 | ||||
|     -- If any custom errors are present, arbitrarily take the first one | ||||
|     -- (since only one custom error should be used at a time). | ||||
|     findCustomError :: ParseError Text CustomErr -> Maybe CustomErr | ||||
|     findCustomError err = case err of | ||||
|       FancyError _ errSet ->  | ||||
|         finds (\case {ErrorCustom e -> Just e; _ -> Nothing}) errSet | ||||
| @ -139,23 +142,26 @@ customParseErrorPretty source err = case findCustomError err of | ||||
| 
 | ||||
| -- | A parse error type intended for throwing parse errors without the | ||||
| -- possiblity of backtracking. Intended for use as the error type in an | ||||
| -- 'ExceptT' layer of the parser. | ||||
| -- 'ExceptT' layer of the parser. The 'ExceptT' layer is responsible for | ||||
| -- handling include files, so this type also records a stack of include | ||||
| -- files in order to report the stack in parse errors. | ||||
| -- | ||||
| -- In order to pretty-print a parse error, we must bundle it with the | ||||
| -- source text and its filepaths (the 'ErrorBundle' constructor). However, | ||||
| -- when an error is thrown from within a parser, we do not have access to | ||||
| -- the (full) source, so we must hold the parse error until it can be | ||||
| -- joined with the source text and its filepath by the parser's caller | ||||
| -- (the 'ErrorFinal' constructor). | ||||
| -- In order to pretty-print our custom parse errors, we must bundle them | ||||
| -- with their full source text and filepaths (the 'FinalBundleWithStack' | ||||
| -- constructor). However, when an error is thrown from within a parser, we | ||||
| -- do not have access to the full source, so we must hold the parse error | ||||
| -- (the 'FinalError' constructor) until it can be joined with the source | ||||
| -- text and its filepath by the parser's caller. | ||||
| 
 | ||||
| data FinalParseError' e | ||||
|   = ErrorFinal  (ParseError Char e) | ||||
|   | ErrorBundle (FinalParseErrorBundle' e) | ||||
|   = FinalError           (ParseError Text e) | ||||
|   | FinalBundle          (ParseErrorBundle Text e) | ||||
|   | FinalBundleWithStack (FinalParseErrorBundle' e) | ||||
|   deriving (Show) | ||||
| 
 | ||||
| type FinalParseError = FinalParseError' CustomErr | ||||
| 
 | ||||
| -- A 'Monoid' instance is necessary for 'ExceptT (FinalParseError'' e)' to | ||||
| -- A 'Monoid' instance is necessary for 'ExceptT (FinalParseError' e)' to | ||||
| -- be an instance of Alternative and MonadPlus, which are required for the | ||||
| -- use of e.g. the 'many' parser combinator. This monoid instance simply | ||||
| -- takes the first (left-most) error. | ||||
| @ -164,22 +170,16 @@ instance Semigroup (FinalParseError' e) where | ||||
|   e <> _ = e | ||||
| 
 | ||||
| instance Monoid (FinalParseError' e) where | ||||
|   mempty = ErrorFinal $ | ||||
|     FancyError (initialPos "" NE.:| []) | ||||
|                (S.singleton (ErrorFail "default parse error")) | ||||
|   mempty = FinalError $ FancyError 0 $ | ||||
|             S.singleton (ErrorFail "default parse error") | ||||
|   mappend = (<>) | ||||
| 
 | ||||
| -- | A type bundling a 'ParseError' with its source file and a stack of | ||||
| -- include file paths (for pretty printing). Although Megaparsec 6 | ||||
| -- maintains a stack of source files, making a field of this type | ||||
| -- redundant, this capability will be removed in Megaparsec 7. Therefore, | ||||
| -- we implement stacks of source files here for a smoother transition in | ||||
| -- the future. | ||||
| -- | A type bundling a 'ParseError' with its full source file and a stack | ||||
| -- of include file paths (for pretty printing). | ||||
| 
 | ||||
| data FinalParseErrorBundle' e = FinalParseErrorBundle' | ||||
|   { finalParseError :: ParseError Char e | ||||
|   , errorSource     :: Text | ||||
|   , sourceFileStack :: NE.NonEmpty FilePath | ||||
|   { finalErrorBundle :: ParseErrorBundle Text e | ||||
|   , sourceFileStack  :: NE.NonEmpty FilePath | ||||
|   } deriving (Show) | ||||
| 
 | ||||
| type FinalParseErrorBundle = FinalParseErrorBundle' CustomErr | ||||
| @ -189,8 +189,8 @@ type FinalParseErrorBundle = FinalParseErrorBundle' CustomErr | ||||
| 
 | ||||
| -- | Convert a "regular" parse error into a "final" parse error. | ||||
| 
 | ||||
| errorFinal :: ParseError Char e -> FinalParseError' e | ||||
| errorFinal = ErrorFinal | ||||
| finalError :: ParseError Text e -> FinalParseError' e | ||||
| finalError = FinalError | ||||
| 
 | ||||
| -- | Like 'fancyFailure', but as a "final" parse error. | ||||
| 
 | ||||
| @ -198,9 +198,8 @@ finalFancyFailure | ||||
|   :: (MonadParsec e s m, MonadError (FinalParseError' e) m) | ||||
|   => S.Set (ErrorFancy e) -> m a | ||||
| finalFancyFailure errSet = do | ||||
|   pos <- getPosition | ||||
|   let parseErr = FancyError (pos NE.:| []) errSet | ||||
|   throwError $ ErrorFinal parseErr | ||||
|   offset <- getOffset | ||||
|   throwError $ FinalError $ FancyError offset errSet | ||||
| 
 | ||||
| -- | Like 'fail', but as a "final" parse error. | ||||
| 
 | ||||
| @ -235,24 +234,30 @@ parseIncludeFile parser initState filepath text = | ||||
|       eResult <- lift $ lift $ | ||||
|                   runParserT (evalStateT parser initState) filepath text | ||||
|       case eResult of | ||||
|         Left parseError -> throwError $ errorFinal parseError | ||||
|         Left parseErrorBundle -> throwError $ FinalBundle parseErrorBundle | ||||
|         Right result -> pure result | ||||
| 
 | ||||
|     handler e = throwError $ ErrorBundle $ attachSource filepath text e | ||||
|     handler e = throwError $ FinalBundleWithStack $ attachSource filepath text e | ||||
| 
 | ||||
| 
 | ||||
| attachSource | ||||
|   :: FilePath -> Text -> FinalParseError' e -> FinalParseErrorBundle' e | ||||
| attachSource filePath sourceText finalParseError = | ||||
|   case finalParseError of | ||||
|     ErrorFinal parseError -> FinalParseErrorBundle' | ||||
|       { finalParseError = parseError | ||||
|       , errorSource     = sourceText | ||||
|       , sourceFileStack = filePath NE.:| [] | ||||
|       } | ||||
|     ErrorBundle bundle -> bundle | ||||
|       { sourceFileStack = filePath NE.<| sourceFileStack bundle | ||||
|       } | ||||
| attachSource filePath sourceText finalParseError = case finalParseError of | ||||
| 
 | ||||
|     FinalError parseError -> | ||||
|       let bundle = ParseErrorBundle | ||||
|             { bundleErrors = parseError NE.:| [] | ||||
|             , bundlePosState = initialPosState filePath sourceText } | ||||
|       in  FinalParseErrorBundle' | ||||
|             { finalErrorBundle = bundle | ||||
|             , sourceFileStack  = filePath NE.:| [] } | ||||
| 
 | ||||
|     FinalBundle peBundle -> FinalParseErrorBundle' | ||||
|       { finalErrorBundle = peBundle | ||||
|       , sourceFileStack  = filePath NE.:| [] } | ||||
| 
 | ||||
|     FinalBundleWithStack fpeBundle -> fpeBundle | ||||
|       { sourceFileStack = filePath NE.<| sourceFileStack fpeBundle } | ||||
| 
 | ||||
| 
 | ||||
| --- * Pretty-printing final parse errors | ||||
| @ -260,125 +265,23 @@ attachSource filePath sourceText finalParseError = | ||||
| -- | Pretty-print a "final" parse error: print the stack of include files, | ||||
| -- then apply the pretty-printer for custom parse errors. | ||||
| 
 | ||||
| finalParseErrorPretty :: FinalParseErrorBundle' CustomErr -> String | ||||
| finalParseErrorPretty bundle = | ||||
| finalErrorBundlePretty :: FinalParseErrorBundle' CustomErr -> String | ||||
| finalErrorBundlePretty bundle = | ||||
|      concatMap printIncludeFile (NE.init (sourceFileStack bundle)) | ||||
|   <> customParseErrorPretty (errorSource bundle) (finalParseError bundle) | ||||
|   <> customErrorBundlePretty (finalErrorBundle bundle) | ||||
|   where | ||||
|     printIncludeFile path = "in file included from " <> path <> ",\n" | ||||
| 
 | ||||
| 
 | ||||
| --- * Modified Megaparsec source | ||||
| --- * Helpers | ||||
| 
 | ||||
| -- The below code has been copied from Megaparsec (v.6.4.1, | ||||
| -- Text.Megaparsec.Error) and modified to suit our needs. These changes are | ||||
| -- indicated by square brackets. The following copyright notice, conditions, | ||||
| -- and disclaimer apply to all code below this point. | ||||
| -- | ||||
| -- Copyright © 2015–2018 Megaparsec contributors<br> | ||||
| -- Copyright © 2007 Paolo Martini<br> | ||||
| -- Copyright © 1999–2000 Daan Leijen | ||||
| -- | ||||
| -- All rights reserved. | ||||
| -- | ||||
| -- 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. | ||||
| 
 | ||||
| 
 | ||||
| -- | Pretty-print a 'ParseError Char CustomErr' and display the line on | ||||
| -- which the parse error occurred. The rendered 'String' always ends with | ||||
| -- a newline. | ||||
| 
 | ||||
| customParseErrorPretty' | ||||
|   :: ( ShowToken (Token s) | ||||
|      , LineToken (Token s) | ||||
|      , ShowErrorComponent e | ||||
|      , Stream s ) | ||||
|   => s                 -- ^ Original input stream | ||||
|   -> ParseError (Token s) e -- ^ Parse error to render | ||||
|   -> Pos               -- ^ Length of error interval [added] | ||||
|   -> String            -- ^ Result of rendering | ||||
| customParseErrorPretty' = customParseErrorPretty_ defaultTabWidth | ||||
| 
 | ||||
| 
 | ||||
| customParseErrorPretty_ | ||||
|   :: forall s e. | ||||
|      ( ShowToken (Token s) | ||||
|      , LineToken (Token s) | ||||
|      , ShowErrorComponent e | ||||
|      , Stream s ) | ||||
|   => Pos               -- ^ Tab width | ||||
|   -> s                 -- ^ Original input stream | ||||
|   -> ParseError (Token s) e -- ^ Parse error to render | ||||
|   -> Pos               -- ^ Length of error interval [added] | ||||
|   -> String            -- ^ Result of rendering | ||||
| customParseErrorPretty_ w s e l = | ||||
|   sourcePosStackPretty (errorPos e) <> ":\n" <> | ||||
|     padding <> "|\n" <> | ||||
|     lineNumber <> " | " <> rline <> "\n" <> | ||||
|     padding <> "| " <> rpadding <> highlight <> "\n" <> -- [added `highlight`] | ||||
|     parseErrorTextPretty e | ||||
|   where | ||||
|     epos       = NE.head (errorPos e) -- [changed from NE.last to NE.head] | ||||
|     lineNumber = (show . unPos . sourceLine) epos | ||||
|     padding    = replicate (length lineNumber + 1) ' ' | ||||
|     rpadding   = replicate (unPos (sourceColumn epos) - 1) ' ' | ||||
|     highlight  = replicate (unPos l) '^' -- [added] | ||||
|     rline      = | ||||
|       case rline' of | ||||
|         [] -> "<empty line>" | ||||
|         xs -> expandTab w xs | ||||
|     rline'     = fmap tokenAsChar . chunkToTokens (Proxy :: Proxy s) $ | ||||
|       selectLine (sourceLine epos) s | ||||
| 
 | ||||
| -- | Select a line from input stream given its number. | ||||
| 
 | ||||
| selectLine | ||||
|   :: forall s. (LineToken (Token s), Stream s) | ||||
|   => Pos               -- ^ Number of line to select | ||||
|   -> s                 -- ^ Input stream | ||||
|   -> Tokens s          -- ^ Selected line | ||||
| selectLine l = go pos1 | ||||
|   where | ||||
|     go !n !s = | ||||
|       if n == l | ||||
|         then fst (takeWhile_ notNewline s) | ||||
|         else go (n <> pos1) (stripNewline $ snd (takeWhile_ notNewline s)) | ||||
|     notNewline = not . tokenIsNewline | ||||
|     stripNewline s = | ||||
|       case take1_ s of | ||||
|         Nothing -> s | ||||
|         Just (_, s') -> s' | ||||
| 
 | ||||
| -- | Replace tab characters with given number of spaces. | ||||
| 
 | ||||
| expandTab | ||||
|   :: Pos | ||||
|   -> String | ||||
|   -> String | ||||
| expandTab w' = go 0 | ||||
|   where | ||||
|     go 0 []        = [] | ||||
|     go 0 ('\t':xs) = go w xs | ||||
|     go 0 (x:xs)    = x : go 0 xs | ||||
|     go !n xs       = ' ' : go (n - 1) xs | ||||
|     w              = unPos w' | ||||
| -- The "tab width" and "line prefix" are taken from the defaults defined | ||||
| -- in 'initialState'. | ||||
| 
 | ||||
| initialPosState :: FilePath -> Text -> PosState Text | ||||
| initialPosState filePath sourceText = PosState | ||||
|   { pstateInput      = sourceText | ||||
|   , pstateOffset     = 0 | ||||
|   , pstateSourcePos  = initialPos filePath | ||||
|   , pstateTabWidth   = defaultTabWidth | ||||
|   , pstateLinePrefix = "" } | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| -- | ||||
| -- see: https://github.com/sol/hpack | ||||
| -- | ||||
| -- hash: 5d69eead3be5d0a10a8e272a4bdf63ba320e9e6914fae3d6031538bd8bd6206d | ||||
| -- hash: 54632c4329f85aa921fb91abbed9c0871465e0cfb4cdfa05a390447c6d796b83 | ||||
| 
 | ||||
| name:           hledger-lib | ||||
| version:        1.10.99 | ||||
| @ -122,7 +122,7 @@ library | ||||
|     , extra | ||||
|     , filepath | ||||
|     , hashtables >=1.2.3.1 | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , mtl | ||||
|     , mtl-compat | ||||
|     , old-time | ||||
| @ -222,7 +222,7 @@ test-suite doctests | ||||
|     , extra | ||||
|     , filepath | ||||
|     , hashtables >=1.2.3.1 | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , mtl | ||||
|     , mtl-compat | ||||
|     , old-time | ||||
| @ -322,7 +322,7 @@ test-suite easytests | ||||
|     , filepath | ||||
|     , hashtables >=1.2.3.1 | ||||
|     , hledger-lib | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , mtl | ||||
|     , mtl-compat | ||||
|     , old-time | ||||
|  | ||||
| @ -57,7 +57,7 @@ dependencies: | ||||
| - easytest | ||||
| - filepath | ||||
| - hashtables >=1.2.3.1 | ||||
| - megaparsec >=6.4.1 && < 7 | ||||
| - megaparsec >=6.4.1 | ||||
| - mtl | ||||
| - mtl-compat | ||||
| - old-time | ||||
|  | ||||
| @ -112,8 +112,8 @@ esHandle _ _ = error "event handler called with wrong screen type, should not ha | ||||
| -- Temporary, we should keep the original parse error location. XXX | ||||
| hledgerparseerrorpositionp :: ParsecT Void String t (String, Int, Int) | ||||
| hledgerparseerrorpositionp = do | ||||
|   anyChar `manyTill` char '"' | ||||
|   f <- anyChar `manyTill` (oneOf ['"','\n']) | ||||
|   anySingle `manyTill` char '"' | ||||
|   f <- anySingle `manyTill` (oneOf ['"','\n']) | ||||
|   string " (line " | ||||
|   l <- read <$> some digitChar | ||||
|   string ", column " | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| -- | ||||
| -- see: https://github.com/sol/hpack | ||||
| -- | ||||
| -- hash: fc23afcaa9a76cad46878b6bc6d6f9f6bb3f59623438031956b1d8cdb9315c17 | ||||
| -- hash: 88116009cafa64bb3351a332b88f9848d895f7bc4e614a8647f9c26c6405ba35 | ||||
| 
 | ||||
| name:           hledger-ui | ||||
| version:        1.10.99 | ||||
| @ -77,7 +77,7 @@ executable hledger-ui | ||||
|     , fsnotify >=0.2.1.2 && <0.4 | ||||
|     , hledger >=1.10.99 && <1.11 | ||||
|     , hledger-lib >=1.10.99 && <1.11 | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , microlens >=0.4 | ||||
|     , microlens-platform >=0.2.3.1 | ||||
|     , pretty-show >=1.6.4 | ||||
|  | ||||
| @ -54,7 +54,7 @@ dependencies: | ||||
| - fsnotify >=0.2.1.2 && <0.4 | ||||
| - microlens >=0.4 | ||||
| - microlens-platform >=0.2.3.1 | ||||
| - megaparsec >=6.4.1 && < 7 | ||||
| - megaparsec >=6.4.1 | ||||
| - pretty-show >=1.6.4 | ||||
| - process >=1.2 | ||||
| - safe >=0.2 | ||||
|  | ||||
| @ -21,7 +21,7 @@ import qualified Data.Text as T | ||||
| import Data.Time (Day) | ||||
| import Text.Blaze.Internal (Markup, preEscapedString) | ||||
| import Text.JSON | ||||
| import Text.Megaparsec (eof, parseErrorPretty, runParser) | ||||
| import Text.Megaparsec (eof, errorBundlePretty, runParser) | ||||
| import Yesod | ||||
| 
 | ||||
| import Hledger | ||||
| @ -131,7 +131,7 @@ validatePostings a b = | ||||
|     catPostings (t, t', Left (e, e')) xs = (t, t', e, e') : xs | ||||
|     catPostings (t, t', Right _) xs = (t, t', Nothing, Nothing) : xs | ||||
| 
 | ||||
|     errorToFormMsg = first (("Invalid value: " <>) . T.pack . parseErrorPretty) | ||||
|     errorToFormMsg = first (("Invalid value: " <>) . T.pack . errorBundlePretty) | ||||
|     validateAccount = errorToFormMsg . runParser (accountnamep <* eof) "" . T.strip | ||||
|     validateAmount = errorToFormMsg . runParser (evalStateT (amountp <* eof) mempty) "" . T.strip | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| -- | ||||
| -- see: https://github.com/sol/hpack | ||||
| -- | ||||
| -- hash: f7bbdd2a2c0bf60f14a1c2d1538414933ed62708598213563167d021baba748b | ||||
| -- hash: b77366b5a138b9d5a3b4c4541bfb875642f06b621bd690712d022f53ab1afbf6 | ||||
| 
 | ||||
| name:           hledger-web | ||||
| version:        1.10.99 | ||||
| @ -169,7 +169,7 @@ library | ||||
|     , http-client | ||||
|     , http-conduit | ||||
|     , json | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , mtl | ||||
|     , semigroups | ||||
|     , shakespeare >=2.0.2.2 | ||||
|  | ||||
| @ -114,7 +114,7 @@ library: | ||||
|   - http-conduit | ||||
|   - http-client | ||||
|   - json | ||||
|   - megaparsec >=6.4.1 && < 7 | ||||
|   - megaparsec >=6.4.1 | ||||
|   - mtl | ||||
|   - semigroups | ||||
|   - shakespeare >=2.0.2.2 | ||||
|  | ||||
| @ -296,7 +296,7 @@ amountAndCommentWizard EntryState{..} = do | ||||
|       amountandcommentp = do | ||||
|         a <- amountp | ||||
|         lift (skipMany spacenonewline) | ||||
|         c <- T.pack <$> fromMaybe "" `fmap` optional (char ';' >> many anyChar) | ||||
|         c <- T.pack <$> fromMaybe "" `fmap` optional (char ';' >> many anySingle) | ||||
|         -- eof | ||||
|         return (a,c) | ||||
|       balancingamt = negate $ sum $ map pamount realps where realps = filter isReal esPostings | ||||
|  | ||||
| @ -193,7 +193,7 @@ transactionModifierFromOpts CliOpts{rawopts_=rawopts,reportopts_=ropts} = | ||||
|   where | ||||
|     q = T.pack $ query_ ropts | ||||
|     ps = map (parseposting . stripquotes . T.pack) $ listofstringopt "add-posting" rawopts | ||||
|     parseposting t = either (error' . parseErrorPretty' t') id ep  | ||||
|     parseposting t = either (error' . errorBundlePretty) id ep | ||||
|       where | ||||
|         ep = runIdentity (runJournalParser (postingp Nothing <* eof) t') | ||||
|         t' = " " <> t <> "\n" -- inject space and newline for proper parsing | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| -- | ||||
| -- see: https://github.com/sol/hpack | ||||
| -- | ||||
| -- hash: 70e6e178ba5d2d6601ebf07e79fdcc19d2480a0544225da23ee3155e928fd85c | ||||
| -- hash: eeed47cc18e00b190b0dd220f044f4f63c60442fa26ee301c44454b5f66e09ca | ||||
| 
 | ||||
| name:           hledger | ||||
| version:        1.10.99 | ||||
| @ -131,7 +131,7 @@ library | ||||
|     , here | ||||
|     , hledger-lib >=1.10.99 && <1.11 | ||||
|     , lucid | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , mtl | ||||
|     , mtl-compat | ||||
|     , old-time | ||||
| @ -182,7 +182,7 @@ executable hledger | ||||
|     , here | ||||
|     , hledger | ||||
|     , hledger-lib >=1.10.99 && <1.11 | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , mtl | ||||
|     , mtl-compat | ||||
|     , old-time | ||||
| @ -236,7 +236,7 @@ test-suite test | ||||
|     , here | ||||
|     , hledger | ||||
|     , hledger-lib >=1.10.99 && <1.11 | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , mtl | ||||
|     , mtl-compat | ||||
|     , old-time | ||||
| @ -291,7 +291,7 @@ benchmark bench | ||||
|     , hledger | ||||
|     , hledger-lib >=1.10.99 && <1.11 | ||||
|     , html | ||||
|     , megaparsec >=6.4.1 && <7 | ||||
|     , megaparsec >=6.4.1 | ||||
|     , mtl | ||||
|     , mtl-compat | ||||
|     , old-time | ||||
|  | ||||
| @ -93,7 +93,7 @@ dependencies: | ||||
| - filepath | ||||
| - haskeline >=0.6 | ||||
| - here | ||||
| - megaparsec >=6.4.1 && < 7 | ||||
| - megaparsec >=6.4.1 | ||||
| - mtl | ||||
| - mtl-compat | ||||
| - old-time | ||||
|  | ||||
| @ -26,8 +26,8 @@ extra-deps: | ||||
| - base-orphans-0.7 | ||||
| - bifunctors-5.5.2 | ||||
| - brick-0.37.1 | ||||
| - cassava-megaparsec-1.0.0 | ||||
| - config-ini-0.2.2.0 | ||||
| - cassava-megaparsec-2.0.0 | ||||
| - config-ini-0.2.3.0 | ||||
| - criterion-1.4.1.0 | ||||
| - data-clist-0.1.2.1 | ||||
| - directory-1.2.7.0 | ||||
| @ -43,13 +43,13 @@ extra-deps: | ||||
| - integer-logarithms-1.0.2.1 | ||||
| - kan-extensions-5.1 | ||||
| - lens-4.16.1 | ||||
| - megaparsec-6.4.1 | ||||
| - megaparsec-7.0.1 | ||||
| - microstache-1.0.1.1 | ||||
| - mmorph-1.1.2 | ||||
| - monad-control-1.0.2.3 | ||||
| - network-2.6.3.5 | ||||
| - optparse-applicative-0.14.2.0 | ||||
| - parser-combinators-0.4.0 | ||||
| - parser-combinators-1.0.0 | ||||
| - persistent-2.7.0 | ||||
| - persistent-template-2.5.4 | ||||
| - profunctors-5.2.2 | ||||
|  | ||||
| @ -20,7 +20,8 @@ extra-deps: | ||||
| - base-compat-0.10.1 | ||||
| - base-compat-batteries-0.10.1 | ||||
| - bifunctors-5.5.2 | ||||
| - cassava-megaparsec-1.0.0 | ||||
| - cassava-megaparsec-2.0.0 | ||||
| - config-ini-0.2.3.0 | ||||
| - criterion-1.4.1.0 | ||||
| - doctest-0.16.0 | ||||
| - generics-sop-0.3.2.0 | ||||
| @ -29,11 +30,11 @@ extra-deps: | ||||
| - http-types-0.12.1 | ||||
| - insert-ordered-containers-0.2.1.0 | ||||
| - lens-4.16.1 | ||||
| - megaparsec-6.4.1 | ||||
| - megaparsec-7.0.1 | ||||
| - microstache-1.0.1.1 | ||||
| - mmorph-1.1.2 | ||||
| - network-2.6.3.5 | ||||
| - parser-combinators-0.4.0 | ||||
| - parser-combinators-1.0.0 | ||||
| - persistent-template-2.5.4 | ||||
| - scientific-0.3.6.2 | ||||
| - servant-0.13.0.1 | ||||
|  | ||||
| @ -15,9 +15,12 @@ extra-deps: | ||||
| - aeson-1.3.1.1 | ||||
| - base-compat-0.10.1 | ||||
| - base-compat-batteries-0.10.1 | ||||
| - cassava-megaparsec-1.0.0 | ||||
| - cassava-megaparsec-2.0.0 | ||||
| - config-ini-0.2.3.0 | ||||
| - criterion-1.4.1.0 | ||||
| - doctest-0.16.0 | ||||
| - megaparsec-7.0.1 | ||||
| - parser-combinators-1.0.0 | ||||
| - swagger2-2.2.2 | ||||
| # avoid no hashable instance for AccountName from doctests | ||||
| - hashtables-1.2.3.1 | ||||
|  | ||||
| @ -10,7 +10,9 @@ packages: | ||||
| - hledger-api | ||||
| 
 | ||||
| extra-deps: | ||||
| - cassava-megaparsec-1.0.0 | ||||
| - cassava-megaparsec-2.0.0 | ||||
| - megaparsec-7.0.1 | ||||
| - config-ini-0.2.3.0 | ||||
| 
 | ||||
| nix: | ||||
|   pure: false | ||||
|  | ||||
| @ -12,7 +12,7 @@ hledger: -:1:5: | ||||
| 1 | 2018 | ||||
|   |     ^ | ||||
| unexpected newline | ||||
| expecting date separator or the rest of year or month | ||||
| expecting date separator or digit | ||||
| 
 | ||||
| >=1 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user