Since you are quoting some tests, it looks like you at least tackled the problem. I assume that you have already determined one number, which can be integer or real - it does not matter, you still convert everything to float - and the fraction of two numbers, maybe something like this:
from pyparsing import Regex, Optional number = Regex(r"\d+(\.\d*)?").setParseAction(lambda t: float(t[0])) fraction = number("numerator") + "/" + number("denominator") fraction.setParseAction(lambda t: t.numerator / t.denominator)
(Pay attention to the use of parsing operations that perform floating point conversion and fractional division immediately during parsing. I prefer to do this during parsing when I know that something is a number or a fraction or something else, instead to come back later and sifting through a bunch of fragmented lines, trying to recreate the recognition logic that the parser has already done.)
Here are the test cases I wrote for your problem, consisting of an integer, a fraction, and an integer and a fraction, using both integers and numbers:
tests = """\ 1 1.0 1/2 1.0/2.0 1 1/2 1.0 1/2 1.0 1.0/2.0""".splitlines() for t in tests: print t, fractExpr.parseString(t)
The final step is to define a fractional expression, which can be a single number, a fraction, or a single number and a fraction.
Since pyparsing is from left to right, it does not perform the same return as regexen. Therefore, this expression will not work so well:
fractExpr = Optional(number) + Optional(fraction)
To sum the numerical values ββthat may come from the number and parts of a fraction, add this parsing action:
fractExpr.setParseAction(lambda t: sum(t))
Our tests are printed:
1 [1.0] 1.0 [1.0] 1/2 [1.0] 1.0/2.0 [1.0] 1 1/2 [1.5] 1.0 1/2 [1.5] 1.0 1.0/2.0 [1.5]
For test example 1/2 , containing only the fractional part, the leading numerator matches the term Optional(number) , but this leaves us only with "/ 2", which does not match Optional(fraction) - fortunately, since the second term is optional, this "passes", but in fact he does not do what we want.
We need to make fractExpr a little smarter, and at first it will look like a lone fraction, since there is such a potential confusion between a lone number and a leading numerator of a fraction. The easiest way to do this is to make fractExpr as follows:
fractExpr = fraction | number + Optional(fraction)
Now with this change, our tests look better:
1 [1.0] 1.0 [1.0] 1/2 [0.5] 1.0/2.0 [0.5] 1 1/2 [1.5] 1.0 1/2 [1.5] 1.0 1.0/2.0 [1.5]
There are a couple of classic pyparsing traps, and this is one of them. Just remember that pyparsing has only the idea you are talking about, otherwise it is just a direct analysis from left to right.