lib: fix backtracking in periodexpr parser
- Simplify doctests for periodexpr. - Besides consuming leading space consume ending space for periodexpr also. - Drop implicit option (def, def) behaviour of periodexpr. I.e. disallow hledger reg -p '' and auto-transaction with heading just '~'. - Slightly re-factor periodexpr. - Ensure that reportinginterval doesn't consume trailing space. Useful if we'll start disallowing periods like "every1stjan2009-".
This commit is contained in:
		
							parent
							
								
									8ab1911345
								
							
						
					
					
						commit
						48623b4ceb
					
				| @ -219,6 +219,12 @@ renderPostingCommentDates p = p { pcomment = comment' } | |||||||
| -- 2018/11/29 | -- 2018/11/29 | ||||||
| --     hi           $1.00 | --     hi           $1.00 | ||||||
| -- <BLANKLINE> | -- <BLANKLINE> | ||||||
|  | -- >>> gen "2017/1" | ||||||
|  | -- 2017/01/01 | ||||||
|  | --     hi           $1.00 | ||||||
|  | -- <BLANKLINE> | ||||||
|  | -- >>> gen "" | ||||||
|  | -- ... Failed to parse ... | ||||||
| -- >>> gen "weekly from 2017" | -- >>> gen "weekly from 2017" | ||||||
| -- *** Exception: Unable to generate transactions according to "weekly from 2017" as 2017-01-01 is not a first day of the week | -- *** Exception: Unable to generate transactions according to "weekly from 2017" as 2017-01-01 is not a first day of the week | ||||||
| -- >>> gen "monthly from 2017/5/4" | -- >>> gen "monthly from 2017/5/4" | ||||||
|  | |||||||
| @ -73,6 +73,7 @@ import Prelude () | |||||||
| import Prelude.Compat | import Prelude.Compat | ||||||
| import Control.Monad | import Control.Monad | ||||||
| import Data.List.Compat | import Data.List.Compat | ||||||
|  | import Data.Default | ||||||
| import Data.Maybe | import Data.Maybe | ||||||
| import Data.Text (Text) | import Data.Text (Text) | ||||||
| import qualified Data.Text as T | import qualified Data.Text as T | ||||||
| @ -627,11 +628,6 @@ parsedate s =  fromMaybe (error' $ "could not parse date \"" ++ s ++ "\"") | |||||||
| -- -- 2008-02-29 | -- -- 2008-02-29 | ||||||
| -- #endif | -- #endif | ||||||
| 
 | 
 | ||||||
| -- | Parse a time string to a time type using the provided pattern, or |  | ||||||
| -- return the default. |  | ||||||
| _parsetimewith :: ParseTime t => String -> String -> t -> t |  | ||||||
| _parsetimewith pat s def = fromMaybe def $ parsetime defaultTimeLocale pat s |  | ||||||
| 
 |  | ||||||
| {-| | {-| | ||||||
| Parse a date in any of the formats allowed in ledger's period expressions, | Parse a date in any of the formats allowed in ledger's period expressions, | ||||||
| and maybe some others: | and maybe some others: | ||||||
| @ -781,7 +777,7 @@ lastthisnextthing = do | |||||||
|   return ("", T.unpack r, T.unpack p) |   return ("", T.unpack r, T.unpack p) | ||||||
| 
 | 
 | ||||||
| -- | | -- | | ||||||
| -- >>> let p s = parsewith (periodexpr (parsedate "2008/11/26") <* eof) (T.toLower s) :: Either (ParseError Char MPErr) (Interval, DateSpan) | -- >>> let p = parsePeriodExpr (parsedate "2008/11/26") | ||||||
| -- >>> p "from Aug to Oct" | -- >>> p "from Aug to Oct" | ||||||
| -- Right (NoInterval,DateSpan 2008/08/01-2008/09/30) | -- Right (NoInterval,DateSpan 2008/08/01-2008/09/30) | ||||||
| -- >>> p "aug to oct" | -- >>> p "aug to oct" | ||||||
| @ -816,36 +812,28 @@ lastthisnextthing = do | |||||||
| -- Right (DayOfWeek 2,DateSpan -) | -- Right (DayOfWeek 2,DateSpan -) | ||||||
| -- >>> p "every 2nd day of week" | -- >>> p "every 2nd day of week" | ||||||
| -- Right (DayOfWeek 2,DateSpan -) | -- Right (DayOfWeek 2,DateSpan -) | ||||||
|  | -- >>> p "every 2nd day of month" | ||||||
|  | -- Right (DayOfMonth 2,DateSpan -) | ||||||
|  | -- >>> p "every 2nd day" | ||||||
|  | -- Right (DayOfMonth 2,DateSpan -) | ||||||
| -- >>> p "every 2nd day 2009-" | -- >>> p "every 2nd day 2009-" | ||||||
| -- Right (DayOfMonth 2,DateSpan 2009/01/01-) | -- Right (DayOfMonth 2,DateSpan 2009/01/01-) | ||||||
|  | -- >>> p "every 2nd day of month 2009-" | ||||||
|  | -- Right (DayOfMonth 2,DateSpan 2009/01/01-) | ||||||
| periodexpr :: Day -> SimpleTextParser (Interval, DateSpan) | periodexpr :: Day -> SimpleTextParser (Interval, DateSpan) | ||||||
| periodexpr rdate = choice $ map try [ | periodexpr rdate = surroundedBy (many spacenonewline) . choice $ map try [ | ||||||
|                     intervalanddateperiodexpr rdate, |                     intervalanddateperiodexpr rdate, | ||||||
|                     intervalperiodexpr, |                     (,) NoInterval <$> periodexprdatespan rdate | ||||||
|                     dateperiodexpr rdate, |  | ||||||
|                     (return (NoInterval,DateSpan Nothing Nothing)) |  | ||||||
|                    ] |                    ] | ||||||
| 
 | 
 | ||||||
| intervalanddateperiodexpr :: Day -> SimpleTextParser (Interval, DateSpan) | intervalanddateperiodexpr :: Day -> SimpleTextParser (Interval, DateSpan) | ||||||
| intervalanddateperiodexpr rdate = do | intervalanddateperiodexpr rdate = do | ||||||
|   many spacenonewline |  | ||||||
|   i <- reportinginterval |   i <- reportinginterval | ||||||
|   many spacenonewline |   s <- option def . try $ do | ||||||
|   s <- periodexprdatespan rdate |       many spacenonewline | ||||||
|  |       periodexprdatespan rdate | ||||||
|   return (i,s) |   return (i,s) | ||||||
| 
 | 
 | ||||||
| intervalperiodexpr :: SimpleTextParser (Interval, DateSpan) |  | ||||||
| intervalperiodexpr = do |  | ||||||
|   many spacenonewline |  | ||||||
|   i <- reportinginterval |  | ||||||
|   return (i, DateSpan Nothing Nothing) |  | ||||||
| 
 |  | ||||||
| dateperiodexpr :: Day -> SimpleTextParser (Interval, DateSpan) |  | ||||||
| dateperiodexpr rdate = do |  | ||||||
|   many spacenonewline |  | ||||||
|   s <- periodexprdatespan rdate |  | ||||||
|   return (NoInterval, s) |  | ||||||
| 
 |  | ||||||
| -- Parse a reporting interval. | -- Parse a reporting interval. | ||||||
| reportinginterval :: SimpleTextParser Interval | reportinginterval :: SimpleTextParser Interval | ||||||
| reportinginterval = choice' [ | reportinginterval = choice' [ | ||||||
| @ -877,9 +865,8 @@ reportinginterval = choice' [ | |||||||
|                           optOf_ "month" |                           optOf_ "month" | ||||||
|                           return $ DayOfMonth n, |                           return $ DayOfMonth n, | ||||||
|                        do string "every" |                        do string "every" | ||||||
|                           many spacenonewline |  | ||||||
|                           let mnth = choice' [month, mon] >>= \(_,m,_) -> return (read m) |                           let mnth = choice' [month, mon] >>= \(_,m,_) -> return (read m) | ||||||
|                           d_o_y <- makePermParser $ DayOfYear <$$> (mnth <* many spacenonewline) <||> (nth <* many spacenonewline) |                           d_o_y <- makePermParser $ DayOfYear <$$> try (many spacenonewline *> mnth) <||> try (many spacenonewline *> nth) | ||||||
|                           optOf_ "year" |                           optOf_ "year" | ||||||
|                           return d_o_y, |                           return d_o_y, | ||||||
|                        do string "every" |                        do string "every" | ||||||
|  | |||||||
| @ -43,6 +43,8 @@ data WhichDate = PrimaryDate | SecondaryDate deriving (Eq,Show) | |||||||
| 
 | 
 | ||||||
| data DateSpan = DateSpan (Maybe Day) (Maybe Day) deriving (Eq,Ord,Data,Generic,Typeable) | data DateSpan = DateSpan (Maybe Day) (Maybe Day) deriving (Eq,Ord,Data,Generic,Typeable) | ||||||
| 
 | 
 | ||||||
|  | instance Default DateSpan where def = DateSpan Nothing Nothing | ||||||
|  | 
 | ||||||
| instance NFData DateSpan | instance NFData DateSpan | ||||||
| 
 | 
 | ||||||
| -- synonyms for various date-related scalars | -- synonyms for various date-related scalars | ||||||
|  | |||||||
| @ -38,6 +38,9 @@ choice' = choice . map try | |||||||
| choiceInState :: [StateT s (ParsecT MPErr Text m) a] -> StateT s (ParsecT MPErr Text m) a | choiceInState :: [StateT s (ParsecT MPErr Text m) a] -> StateT s (ParsecT MPErr Text m) a | ||||||
| choiceInState = choice . map try | 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 (ParseError Char e) a | ||||||
| parsewith p = runParser p "" | parsewith p = runParser p "" | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user