Reputation: 263
Consider the following code, which parses and evaluates strings like 567 +223
in Python.
import parsy as pr
from parsy import generate
def lex(p):
return p << pr.regex('\s*')
numberP = lex(pr.regex('[0-9]+').map(int))
@generate
def sumP():
a = yield numberP
yield lex(pr.string('+'))
b = yield numberP
return a+b
exp = sumP.parse('567 + 323')
print(exp)
The @generate is a total mystery for me. Does anyone have more information on how that trick works? It does allow us to write in a similar style to Haskell's monadic do notation. Is code reflection needed to make your own @generate, or is there a clever way to interpret that code literally.
Now here comes my main problem, I want to generalize sumP
to opP
that also takes an operator symbol and a combinator function:
import parsy as pr
from parsy import generate
def lex(p):
return p << pr.regex('\s*')
numberP = lex(pr.regex('[0-9]+').map(int))
@generate
def opP(symbol, f):
a = yield numberP
yield lex(pr.string(symbol))
b = yield numberP
return f(a,b)
exp = opP('+', lambda x,y:x+y).parse('567 + 323')
print(exp)
This gives an error. It seems that the generated opP already has two arguments, which I do not know how to deal with.
Upvotes: 1
Views: 151
Reputation: 370102
The way that decorators work in Python is that they're functions that are called with the decorated method as an argument and then their return value is assigned to the method name. In other words this:
@foo
def bar():
bla
Is equivalent to this:
def bar():
bla
bar = foo(bar)
Here foo
can do anything it wants with bar
. It may wrap it in something, it may introspect its code, it may call it.
What @generate
does is to wrap the given function in a parser object. The parser object, when parsing, will call the function without arguments, which is why you get an error about missing arguments when you apply @generate
to a function that takes arguments.
To create parameterized rules, you can apply @generate
to an inner 0-argument function and return that:
def opP(symbol, f):
@generate
def op():
a = yield numberP
yield lex(pr.string(symbol))
b = yield numberP
return f(a,b)
return op
Upvotes: 1