queries: Update boolean queries to be case-insensitive

That is, the query operators AND, OR, and NOT are now case-insensitive.
Queries are otherwise left the same as they were.
This commit is contained in:
Chris Lemaire 2023-01-25 21:03:12 +01:00 committed by Simon Michael
parent cddbae6467
commit 891853d2f2
2 changed files with 59 additions and 5 deletions

View File

@ -84,7 +84,7 @@ import qualified Data.Text as T
import Data.Time.Calendar (Day, fromGregorian )
import Safe (readDef, readMay, maximumByMay, maximumMay, minimumMay)
import Text.Megaparsec (between, noneOf, sepBy, try, (<?>), notFollowedBy)
import Text.Megaparsec.Char (char, string)
import Text.Megaparsec.Char (char, string, string')
import Hledger.Utils hiding (words')
@ -359,18 +359,18 @@ parseBooleanQuery d t = either (Left . ("failed to parse query:" <>) . customErr
Left err -> error' err
keywordSpaceP :: SimpleTextParser T.Text
keywordSpaceP = choice' ["NOT ", "AND ", "OR "]
keywordSpaceP = choice' (string' <$> ["not ", "and ", "or "])
parQueryP,notQueryP :: SimpleTextParser (Query, [QueryOpt])
parQueryP = between (char '(' >> skipNonNewlineSpaces)
(try $ skipNonNewlineSpaces >> char ')')
spacedQueriesP
<|> queryTermP
notQueryP = (maybe id (\_ (q, qopts) -> (Not q, qopts)) <$> optional (string "NOT" >> skipNonNewlineSpaces1)) <*> parQueryP
notQueryP = (maybe id (\_ (q, qopts) -> (Not q, qopts)) <$> optional (try $ string' "not" >> notFollowedBy (char ':') >> skipNonNewlineSpaces1)) <*> parQueryP
andQueriesP,orQueriesP,spacedQueriesP :: SimpleTextParser (Query, [QueryOpt])
andQueriesP = nArityOp And <$> notQueryP `sepBy` (try $ skipNonNewlineSpaces >> string "AND" >> skipNonNewlineSpaces1)
orQueriesP = nArityOp Or <$> andQueriesP `sepBy` (try $ skipNonNewlineSpaces >> string "OR" >> skipNonNewlineSpaces1)
andQueriesP = nArityOp And <$> notQueryP `sepBy` (try $ skipNonNewlineSpaces >> string' "and" >> skipNonNewlineSpaces1)
orQueriesP = nArityOp Or <$> andQueriesP `sepBy` (try $ skipNonNewlineSpaces >> string' "or" >> skipNonNewlineSpaces1)
spacedQueriesP = nArityOp combineQueryList <$> orQueriesP `sepBy` skipNonNewlineSpaces1
nArityOp :: ([Query] -> Query) -> [(Query, [QueryOpt])] -> (Query, [QueryOpt])
@ -939,6 +939,9 @@ tests_Query = testGroup "Query" [
parseBooleanQuery nulldate " acct:'a' acct:'b'" @?= Right (Or [Acct $ toRegexCI' "a", Acct $ toRegexCI' "b"], [])
parseBooleanQuery nulldate "not:a" @?= Right (Not $ Acct $ toRegexCI' "a", [])
parseBooleanQuery nulldate "expenses:food OR (tag:A expenses:drink)" @?= Right (Or [Acct $ toRegexCI' "expenses:food", And [Acct $ toRegexCI' "expenses:drink", Tag (toRegexCI' "A") Nothing]], [])
parseBooleanQuery nulldate "not a" @?= Right (Not $ Acct $ toRegexCI' "a", [])
parseBooleanQuery nulldate "nota" @?= Right (Acct $ toRegexCI' "nota", [])
parseBooleanQuery nulldate "not (acct:a)" @?= Right (Not $ Acct $ toRegexCI' "a", [])
,testCase "words''" $ do
(words'' [] "a b") @?= ["a","b"]

View File

@ -93,3 +93,54 @@ $ hledger -f - print expr:"NOT tag:'transactiontag=B' OR desc:4"
expenses:drink
>=
# 7. Boolean expression query keywords are case insensitive
$ hledger -f - print expr:"NoT tag:'transactiontag=B' OR desc:4"
2022-01-01 Transaction 1 ; transactiontag:A
assets:bank:main -1 ; A comment
expenses:food
2022-01-01 Transaction 2 ; transactiontag:A
assets:bank:main -1
assets:bank:secondary -1 ; atag:a
expenses:food
2022-01-01 Transaction 4 ; transactiontag:B
assets:bank:main -1 ; A comment
expenses:food 2
expenses:drink
>=
# 8. Lower case not is not confused with existing not: queries
$ hledger -f - print expr:"not tag:transactiontag=B"
2022-01-01 Transaction 1 ; transactiontag:A
assets:bank:main -1 ; A comment
expenses:food
2022-01-01 Transaction 2 ; transactiontag:A
assets:bank:main -1
assets:bank:secondary -1 ; atag:a
expenses:food
>=
# 9. Having parentheses directly follow 'not' sees 'not' as part of a query.
$ hledger -f - print expr:"not(tag:transactiontag=B)"
>2
hledger: Error: This regular expression is malformed, please correct it:
not(tag:transactiontag=B
>=1
# 10. ... whereas parentheses with a space between 'not' and '(' is fine.
$ hledger -f - print expr:"not (tag:transactiontag=B)"
2022-01-01 Transaction 1 ; transactiontag:A
assets:bank:main -1 ; A comment
expenses:food
2022-01-01 Transaction 2 ; transactiontag:A
assets:bank:main -1
assets:bank:secondary -1 ; atag:a
expenses:food
>=