I'm trying to match a sentence fragment of the form "after 3 days" or "after 1 month". I want to be particular with the single and plural forms, so "1 day" is valid but "1 days" is not.
I have the following code which is nearly there but the first two entries in the failure tests don't fail. Any suggestions please that use syntactical notation as I'd like, if possible, to avoid a set_parse_action() that checks the numeric value against the unit's plurality.
from pyparsing import *
units = Keyword('days') ^ Keyword('months')
unit = Keyword('day') ^ Keyword('month')
single = Literal('1') + unit
multi = Word(nums) + units
after = Keyword('after') + ( single ^ multi )
a = after.run_tests('''
after 1 day
after 2 days
after 1 month
after 2 months
''')
print('=============')
b = after.run_tests('''
after 1 days
after 2 day
after 1day
after 2days
''', failure_tests = True)
print('Success tests', 'passed' if a[0] else 'failed')
print('Failure tests', 'passed' if b[0] else 'failed')
Only the case after 1 days
passes when it should fail, the other three cases fail as expected.
The issue is that the check multi = Word(nums) + units
uses nums
which includes 1, so even if your singular variant does not work, this one will. I looked up how nums is defined, apparently it is nums = '0123456789'
(see here). Consequently you remove the 1. This works for me:
...
multi_nums = '023456789' # nums excluding 1
single = Literal('1') + unit
multi = Word(multi_nums) + units
...
EDIT:
The above fails for double digits including 1, see comments. Fixed version as per comments:
single = Literal('1') + unit
multi = ~Keyword('1') + Word(nums) + units