lib: refactor some journal, period expression parsers
This commit is contained in:
parent
1287081ffd
commit
dfcafc2cdf
@ -45,6 +45,7 @@ module Hledger.Data.Dates (
|
|||||||
elapsedSeconds,
|
elapsedSeconds,
|
||||||
prevday,
|
prevday,
|
||||||
parsePeriodExpr,
|
parsePeriodExpr,
|
||||||
|
parsePeriodExpr',
|
||||||
nulldatespan,
|
nulldatespan,
|
||||||
failIfInvalidYear,
|
failIfInvalidYear,
|
||||||
failIfInvalidMonth,
|
failIfInvalidMonth,
|
||||||
@ -313,7 +314,13 @@ earliest (Just d1) (Just d2) = Just $ min d1 d2
|
|||||||
-- | Parse a period expression to an Interval and overall DateSpan using
|
-- | Parse a period expression to an Interval and overall DateSpan using
|
||||||
-- the provided reference date, or return a parse error.
|
-- the provided reference date, or return a parse error.
|
||||||
parsePeriodExpr :: Day -> Text -> Either (ParseError Char CustomErr) (Interval, DateSpan)
|
parsePeriodExpr :: Day -> Text -> Either (ParseError Char CustomErr) (Interval, DateSpan)
|
||||||
parsePeriodExpr refdate s = parsewith (periodexpr refdate <* eof) (T.toLower s)
|
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 $
|
||||||
|
parsePeriodExpr refdate s
|
||||||
|
|
||||||
maybePeriod :: Day -> Text -> Maybe (Interval,DateSpan)
|
maybePeriod :: Day -> Text -> Maybe (Interval,DateSpan)
|
||||||
maybePeriod refdate = either (const Nothing) Just . parsePeriodExpr refdate
|
maybePeriod refdate = either (const Nothing) Just . parsePeriodExpr refdate
|
||||||
@ -910,23 +917,23 @@ lastthisnextthing = do
|
|||||||
-- Right (DayOfMonth 2,DateSpan 2009/01/01-)
|
-- Right (DayOfMonth 2,DateSpan 2009/01/01-)
|
||||||
-- >>> p "every 2nd day of month 2009-"
|
-- >>> p "every 2nd day of month 2009-"
|
||||||
-- Right (DayOfMonth 2,DateSpan 2009/01/01-)
|
-- Right (DayOfMonth 2,DateSpan 2009/01/01-)
|
||||||
periodexpr :: Day -> SimpleTextParser (Interval, DateSpan)
|
periodexprp :: Day -> SimpleTextParser (Interval, DateSpan)
|
||||||
periodexpr rdate = surroundedBy (skipMany spacenonewline) . choice $ map try [
|
periodexprp rdate = surroundedBy (skipMany spacenonewline) . choice $ map try [
|
||||||
intervalanddateperiodexpr rdate,
|
intervalanddateperiodexprp rdate,
|
||||||
(,) NoInterval <$> periodexprdatespan rdate
|
(,) NoInterval <$> periodexprdatespanp rdate
|
||||||
]
|
]
|
||||||
|
|
||||||
intervalanddateperiodexpr :: Day -> SimpleTextParser (Interval, DateSpan)
|
intervalanddateperiodexprp :: Day -> SimpleTextParser (Interval, DateSpan)
|
||||||
intervalanddateperiodexpr rdate = do
|
intervalanddateperiodexprp rdate = do
|
||||||
i <- reportinginterval
|
i <- reportingintervalp
|
||||||
s <- option def . try $ do
|
s <- option def . try $ do
|
||||||
skipMany spacenonewline
|
skipMany spacenonewline
|
||||||
periodexprdatespan rdate
|
periodexprdatespanp rdate
|
||||||
return (i,s)
|
return (i,s)
|
||||||
|
|
||||||
-- Parse a reporting interval.
|
-- Parse a reporting interval.
|
||||||
reportinginterval :: SimpleTextParser Interval
|
reportingintervalp :: SimpleTextParser Interval
|
||||||
reportinginterval = choice' [
|
reportingintervalp = choice' [
|
||||||
tryinterval "day" "daily" Days,
|
tryinterval "day" "daily" Days,
|
||||||
tryinterval "week" "weekly" Weeks,
|
tryinterval "week" "weekly" Weeks,
|
||||||
tryinterval "month" "monthly" Months,
|
tryinterval "month" "monthly" Months,
|
||||||
@ -1007,19 +1014,19 @@ reportinginterval = choice' [
|
|||||||
singular' = T.pack singular
|
singular' = T.pack singular
|
||||||
plural' = T.pack $ singular ++ "s"
|
plural' = T.pack $ singular ++ "s"
|
||||||
|
|
||||||
periodexprdatespan :: Day -> SimpleTextParser DateSpan
|
periodexprdatespanp :: Day -> SimpleTextParser DateSpan
|
||||||
periodexprdatespan rdate = choice $ map try [
|
periodexprdatespanp rdate = choice $ map try [
|
||||||
doubledatespan rdate,
|
doubledatespanp rdate,
|
||||||
fromdatespan rdate,
|
fromdatespanp rdate,
|
||||||
todatespan rdate,
|
todatespanp rdate,
|
||||||
justdatespan rdate
|
justdatespanp rdate
|
||||||
]
|
]
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- -- >>> parsewith (doubledatespan (parsedate "2018/01/01") <* eof) "20180101-201804"
|
-- -- >>> parsewith (doubledatespan (parsedate "2018/01/01") <* eof) "20180101-201804"
|
||||||
-- Right DateSpan 2018/01/01-2018/04/01
|
-- Right DateSpan 2018/01/01-2018/04/01
|
||||||
doubledatespan :: Day -> SimpleTextParser DateSpan
|
doubledatespanp :: Day -> SimpleTextParser DateSpan
|
||||||
doubledatespan rdate = do
|
doubledatespanp rdate = do
|
||||||
optional (string "from" >> skipMany spacenonewline)
|
optional (string "from" >> skipMany spacenonewline)
|
||||||
b <- smartdate
|
b <- smartdate
|
||||||
skipMany spacenonewline
|
skipMany spacenonewline
|
||||||
@ -1027,8 +1034,8 @@ doubledatespan rdate = do
|
|||||||
e <- smartdate
|
e <- smartdate
|
||||||
return $ DateSpan (Just $ fixSmartDate rdate b) (Just $ fixSmartDate rdate e)
|
return $ DateSpan (Just $ fixSmartDate rdate b) (Just $ fixSmartDate rdate e)
|
||||||
|
|
||||||
fromdatespan :: Day -> SimpleTextParser DateSpan
|
fromdatespanp :: Day -> SimpleTextParser DateSpan
|
||||||
fromdatespan rdate = do
|
fromdatespanp rdate = do
|
||||||
b <- choice [
|
b <- choice [
|
||||||
do
|
do
|
||||||
string "from" >> skipMany spacenonewline
|
string "from" >> skipMany spacenonewline
|
||||||
@ -1041,14 +1048,14 @@ fromdatespan rdate = do
|
|||||||
]
|
]
|
||||||
return $ DateSpan (Just $ fixSmartDate rdate b) Nothing
|
return $ DateSpan (Just $ fixSmartDate rdate b) Nothing
|
||||||
|
|
||||||
todatespan :: Day -> SimpleTextParser DateSpan
|
todatespanp :: Day -> SimpleTextParser DateSpan
|
||||||
todatespan rdate = do
|
todatespanp rdate = do
|
||||||
choice [string "to", string "-"] >> skipMany spacenonewline
|
choice [string "to", string "-"] >> skipMany spacenonewline
|
||||||
e <- smartdate
|
e <- smartdate
|
||||||
return $ DateSpan Nothing (Just $ fixSmartDate rdate e)
|
return $ DateSpan Nothing (Just $ fixSmartDate rdate e)
|
||||||
|
|
||||||
justdatespan :: Day -> SimpleTextParser DateSpan
|
justdatespanp :: Day -> SimpleTextParser DateSpan
|
||||||
justdatespan rdate = do
|
justdatespanp rdate = do
|
||||||
optional (string "in" >> skipMany spacenonewline)
|
optional (string "in" >> skipMany spacenonewline)
|
||||||
d <- smartdate
|
d <- smartdate
|
||||||
return $ spanFromSmartDate rdate d
|
return $ spanFromSmartDate rdate d
|
||||||
|
|||||||
@ -223,8 +223,8 @@ instance Eq Posting where
|
|||||||
|
|
||||||
-- TODO: needs renaming, or removal if no longer needed. See also TextPosition in Hledger.UI.Editor
|
-- TODO: needs renaming, or removal if no longer needed. See also TextPosition in Hledger.UI.Editor
|
||||||
-- | The position of parse errors (eg), like parsec's SourcePos but generic.
|
-- | The position of parse errors (eg), like parsec's SourcePos but generic.
|
||||||
data GenericSourcePos = GenericSourcePos FilePath Int Int -- ^ name, 1-based line number and 1-based column number.
|
data GenericSourcePos = GenericSourcePos FilePath Int Int -- ^ file path, 1-based line number and 1-based column number.
|
||||||
| JournalSourcePos FilePath (Int, Int) -- ^ file name, inclusive range of 1-based line numbers (first, last).
|
| JournalSourcePos FilePath (Int, Int) -- ^ file path, inclusive range of 1-based line numbers (first, last).
|
||||||
deriving (Eq, Read, Show, Ord, Data, Generic, Typeable)
|
deriving (Eq, Read, Show, Ord, Data, Generic, Typeable)
|
||||||
|
|
||||||
instance NFData GenericSourcePos
|
instance NFData GenericSourcePos
|
||||||
|
|||||||
@ -86,7 +86,11 @@ module Hledger.Read.Common (
|
|||||||
postingcommentp,
|
postingcommentp,
|
||||||
|
|
||||||
-- ** bracketed dates
|
-- ** bracketed dates
|
||||||
bracketeddatetagsp
|
bracketeddatetagsp,
|
||||||
|
|
||||||
|
-- ** misc
|
||||||
|
singlespacedtextp,
|
||||||
|
singlespacep
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
--- * imports
|
--- * imports
|
||||||
@ -193,6 +197,7 @@ rjp = runJournalParser
|
|||||||
genericSourcePos :: SourcePos -> GenericSourcePos
|
genericSourcePos :: SourcePos -> GenericSourcePos
|
||||||
genericSourcePos p = GenericSourcePos (sourceName p) (fromIntegral . unPos $ sourceLine p) (fromIntegral . unPos $ sourceColumn p)
|
genericSourcePos p = GenericSourcePos (sourceName p) (fromIntegral . unPos $ sourceLine p) (fromIntegral . unPos $ sourceColumn p)
|
||||||
|
|
||||||
|
-- | Construct a generic start & end line parse position from start and end megaparsec SourcePos's.
|
||||||
journalSourcePos :: SourcePos -> SourcePos -> GenericSourcePos
|
journalSourcePos :: SourcePos -> SourcePos -> GenericSourcePos
|
||||||
journalSourcePos p p' = JournalSourcePos (sourceName p) (fromIntegral . unPos $ sourceLine p, fromIntegral $ line')
|
journalSourcePos p p' = JournalSourcePos (sourceName p) (fromIntegral . unPos $ sourceLine p, fromIntegral $ line')
|
||||||
where line'
|
where line'
|
||||||
@ -438,19 +443,26 @@ modifiedaccountnamep = do
|
|||||||
joinAccountNames parent
|
joinAccountNames parent
|
||||||
a
|
a
|
||||||
|
|
||||||
-- | Parse an account name. Account names start with a non-space, may
|
-- | Parse an account name, plus one following space if present.
|
||||||
-- have single spaces inside them, and are terminated by two or more
|
-- Account names start with a non-space, may have single spaces inside them,
|
||||||
-- spaces (or end of input). Also they have one or more components of
|
-- and are terminated by two or more spaces (or end of input).
|
||||||
-- at least one character, separated by the account separator char.
|
-- (Also they have one or more components of at least one character,
|
||||||
-- (This parser will also consume one following space, if present.)
|
-- separated by the account separator character, but we don't check that here.)
|
||||||
accountnamep :: TextParser m AccountName
|
accountnamep :: TextParser m AccountName
|
||||||
accountnamep = do
|
accountnamep = singlespacedtextp
|
||||||
|
|
||||||
|
-- | Parse any text beginning with a non-whitespace character, until a double space or the end of input.
|
||||||
|
-- Consumes one of the following spaces, if present.
|
||||||
|
singlespacedtextp :: TextParser m T.Text
|
||||||
|
singlespacedtextp = do
|
||||||
firstPart <- part
|
firstPart <- part
|
||||||
otherParts <- many $ try $ singleSpace *> part
|
otherParts <- many $ try $ singlespacep *> part
|
||||||
pure $! T.unwords $ firstPart : otherParts
|
pure $! T.unwords $ firstPart : otherParts
|
||||||
where
|
where
|
||||||
part = takeWhile1P Nothing (not . isSpace)
|
part = takeWhile1P Nothing (not . isSpace)
|
||||||
singleSpace = void spacenonewline *> notFollowedBy spacenonewline
|
|
||||||
|
-- | Parse one non-newline whitespace character that is not followed by another one.
|
||||||
|
singlespacep = void spacenonewline *> notFollowedBy spacenonewline
|
||||||
|
|
||||||
--- ** amounts
|
--- ** amounts
|
||||||
|
|
||||||
|
|||||||
@ -470,7 +470,7 @@ periodictransactionp = do
|
|||||||
transactionp :: JournalParser m Transaction
|
transactionp :: JournalParser m Transaction
|
||||||
transactionp = do
|
transactionp = do
|
||||||
-- ptrace "transactionp"
|
-- ptrace "transactionp"
|
||||||
pos <- getPosition
|
startpos <- getPosition
|
||||||
date <- datep <?> "transaction"
|
date <- datep <?> "transaction"
|
||||||
edate <- optional (lift $ secondarydatep date) <?> "secondary date"
|
edate <- optional (lift $ secondarydatep date) <?> "secondary date"
|
||||||
lookAhead (lift spacenonewline <|> newline) <?> "whitespace or newline"
|
lookAhead (lift spacenonewline <|> newline) <?> "whitespace or newline"
|
||||||
@ -480,8 +480,8 @@ transactionp = do
|
|||||||
(comment, tags) <- lift transactioncommentp
|
(comment, tags) <- lift transactioncommentp
|
||||||
let year = first3 $ toGregorian date
|
let year = first3 $ toGregorian date
|
||||||
postings <- postingsp (Just year)
|
postings <- postingsp (Just year)
|
||||||
pos' <- getPosition
|
endpos <- getPosition
|
||||||
let sourcepos = journalSourcePos pos pos'
|
let sourcepos = journalSourcePos startpos endpos
|
||||||
return $ txnTieKnot $ Transaction 0 sourcepos date edate status code description comment tags postings ""
|
return $ txnTieKnot $ Transaction 0 sourcepos date edate status code description comment tags postings ""
|
||||||
|
|
||||||
#ifdef TESTS
|
#ifdef TESTS
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user