lib: merge parsing of date-tags with that of tags

This commit is contained in:
Alex Chen 2018-05-12 14:16:22 -06:00 committed by Simon Michael
parent ec85271a0b
commit fcc10f018d

View File

@ -853,23 +853,38 @@ followingcommentandtagsp mdefdate = do
-- pdbg 0 $ "commentws:" ++ show commentLines
-- Reparse the comment for any tags.
tags <- case traverse (runTextParserAt tagsp) commentLines of
tagsWithPositions <- case traverse (runTextParserAt tagswithvaluepositions) commentLines of
Right tss -> pure $ concat tss
Left e -> throwError $ parseErrorPretty e
-- Reparse the comment for any posting dates.
-- Extract date-tag style posting dates from the tags.
-- Use the transaction date for defaults, if provided.
epdates <- fmap sequence
journal <- fmap
(\j -> j{jparsedefaultyear=first3.toGregorian <$> mdefdate}) get
let eTagDates = traverse (tagDate journal)
$ filter (isDateLabel . fst . snd) tagsWithPositions
where isDateLabel txt = txt == "date" || txt == "date2"
tagDates <- case eTagDates of
Right ds -> pure ds
Left e -> throwError e
-- Reparse the comment for any bracketed style posting dates.
-- Use the transaction date for defaults, if provided.
eBracketedDates <- fmap sequence
$ traverse (runErroringJournalParserAt (postingdatesp mdefdate))
commentLines
pdates <- case epdates of
bracketedDates <- case eBracketedDates of
Right dss -> pure $ concat dss
Left e -> throwError e
-- pdbg 0 $ "pdates: "++show pdates
let pdates = tagDates ++ bracketedDates
-- pdbg 0 $ "allDates: "++show pdates
let mdate = headMay $ map snd $ filter ((=="date") .fst) pdates
mdate2 = headMay $ map snd $ filter ((=="date2").fst) pdates
let strippedComment = T.unlines $ map (T.strip . snd) commentLines
tags = map snd tagsWithPositions
-- pdbg 0 $ "comment:"++show strippedComment
pure (strippedComment, tags, mdate, mdate2)
@ -877,9 +892,22 @@ followingcommentandtagsp mdefdate = do
where
runTextParserAt parser (pos, txt) =
runTextParser (setPosition pos *> parser) txt
runErroringJournalParserAt parser (pos, txt) =
runErroringJournalParser (setPosition pos *> parser) txt
parseWithStateAt' st parser (pos, txt) =
parseWithState' st (setPosition pos *> parser) txt
tagDate
:: Journal
-> (SourcePos, Tag)
-> Either String (TagName, Day)
tagDate journal (pos, (name, value)) =
case parseWithStateAt' journal datep (pos, value) of
Left e -> Left $ parseErrorPretty e
Right day -> Right (name, day)
-- A transaction/posting comment must start with a semicolon.
-- This parser discards the leading whitespace of the comment
-- and returns the source position of the comment's first non-whitespace character.
@ -929,7 +957,10 @@ commentTags s =
-- | Parse all tags found in a string.
tagsp :: SimpleTextParser [Tag]
tagsp = do
tagsp = map snd <$> tagswithvaluepositions
tagswithvaluepositions :: SimpleTextParser [(SourcePos, Tag)]
tagswithvaluepositions = do
-- pdbg 0 $ "tagsp"
-- If we parse in a single pass, we cannot know whether some text
@ -953,25 +984,22 @@ tagsp = do
tagValue =
T.strip . T.pack <$> anyChar `manyTill` (void (char ',') <|> eolof)
atSpaceChar :: SimpleTextParser [Tag]
atSpaceChar = skipSome spaceChar *> tagsp
atSpaceChar :: SimpleTextParser [(SourcePos, Tag)]
atSpaceChar = skipSome spaceChar *> tagswithvaluepositions
atColon :: Text -> SimpleTextParser [Tag]
atColon :: Text -> SimpleTextParser [(SourcePos, Tag)]
atColon tagName = do
char ':'
if T.null tagName
then tagsp
else (:) <$> fmap (tagName,) tagValue <*> tagsp
then tagswithvaluepositions
else do
pos <- getPosition
(:) <$> fmap (\val -> (pos, (tagName, val))) tagValue
<*> tagswithvaluepositions
atEof :: SimpleTextParser [Tag]
atEof :: SimpleTextParser [(SourcePos, Tag)]
atEof = eof *> pure []
tagvaluep :: TextParser m Text
tagvaluep = do
-- ptrace "tagvalue"
v <- anyChar `manyTill` (void (try (char ',')) <|> eolof)
return $ T.pack $ strip $ reverse $ dropWhile (==',') $ reverse $ strip v
--- ** posting dates
-- | Parse all posting dates found in a string. Posting dates can be
@ -982,52 +1010,12 @@ tagvaluep = do
postingdatesp :: Monad m => Maybe Day -> ErroringJournalParser m [(TagName,Day)]
postingdatesp mdefdate = do
-- pdbg 0 $ "postingdatesp"
let p = ((:[]) <$> datetagp mdefdate) <|> bracketeddatetagsp mdefdate
let p = bracketeddatetagsp mdefdate
nonp =
many (notFollowedBy p >> anyChar)
-- anyChar `manyTill` (lookAhead (try (p >> return ()) <|> eof))
concat <$> many (try (nonp >> p))
--- ** date tags
-- | Date tags are tags with name "date" or "date2". Their value is
-- parsed as a date, using the provided default date if any for
-- inferring a missing year if needed. Any error in date parsing is
-- reported and terminates parsing.
--
-- >>> rejp (datetagp Nothing) "date: 2000/1/2 "
-- Right ("date",2000-01-02)
--
-- >>> rejp (datetagp (Just $ fromGregorian 2001 2 3)) "date2:3/4"
-- Right ("date2",2001-03-04)
--
-- >>> rejp (datetagp Nothing) "date: 3/4"
-- Left ...1:9...partial date 3/4 found, but the current year is unknown...
--
datetagp :: Monad m => Maybe Day -> ErroringJournalParser m (TagName,Day)
datetagp mdefdate = do
-- pdbg 0 "datetagp"
string "date"
n <- fromMaybe "" <$> optional (mptext "2")
char ':'
startpos <- getPosition
v <- lift tagvaluep
-- re-parse value as a date.
j <- get
let ep :: Either (ParseError Char MPErr) Day
ep = parseWithState'
j{jparsedefaultyear=first3.toGregorian <$> mdefdate}
-- The value extends to a comma, newline, or end of file.
-- It seems like ignoring any extra stuff following a date
-- gives better errors here.
(do
setPosition startpos
datep) -- <* eof)
v
case ep
of Left e -> throwError $ parseErrorPretty e
Right d -> return ("date"<>n, d)
--- ** bracketed dates
-- tagorbracketeddatetagsp :: Monad m => Maybe Day -> TextParser u m [Tag]