Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I attach callbacks to terminal nodes? #27

Closed
salimfadhley opened this issue Apr 27, 2013 · 2 comments
Closed

How do I attach callbacks to terminal nodes? #27

salimfadhley opened this issue Apr 27, 2013 · 2 comments

Comments

@salimfadhley
Copy link

Continuing from the previous example - I'd like to be able to define some kind of callback which will be called any time the parser encounters a terminal.

So continuing on from my previous example, supposing the parser had successfully parsed the expression:

"go north, south"

And supposing that matches a particular rule for 'movement instruction' - I'd like a particular callback to be invoked any time we have one of those.

Can it be done?

@erikrose
Copy link
Owner

Parsimonious divides the problem into 2 phases: parsing and tree walking. They're decoupled for various reason (https://github.com/erikrose/parsimonious#why-no-streaming-tree-processing). Parsimonious does the parsing for you, emitting a syntax tree—a tree of Node instances. Then you can do whatever you want with the tree. If I were going to make an adventure game, I'd do something along these lines…

Let's build on the example grammar I suggested in #26. I imagine there'd be more than one type of instruction: maybe a go_instruction and also a get_instruction that adds an item to your inventory, for example. You'd have a top-level rule like this, which dispatches between them:

instruction = go_instruction / get_instruction

You make that rule the top one in your grammar so it's the default entrypoint. Then you do something like this:

grammar = Grammar("""
    instruction = go_instruction / get_instruction
    ...
    """)
instruction_tree = grammar.parse(user_input)

That would give you a syntax tree representing the user's input. If you were a masochist, you could use it directly, if-then-ing your way down its branches to figure out what in-game thing to do.

To make it easier on yourself, you can first transform the tree a bit to make it easier to work with. First, tweak my #26 grammar a bit by breaking out the "additional directions" part:

go_instruction = "go " _ directions
directions = direction additional_directions*
additional_directions = "," _ direction
direction = "north" / "south" / "east" / "west"
_ = ~r"\s*"

Then you can write a fairly straightforward NodeVisitor subclass with a visit_additional_directions method which replaces an additional_directions node with its direction component (discarding commas and whitespace). Next, add a visit_directions method which flattens out all the directions into a list. Then you can walk the transformed tree pretty easily to figure out where to go. Remember, there's no need to return Nodes from your visitor methods; you can transform the tree into whatever data structure you find handiest.

I plan to add some common tree transformations to the grammar proper so you don't have to write visitors at all in common cases: https://github.com/erikrose/parsimonious#niceties. #23 has a good discussion of these and also some ideas for weaving grammar and tree-walker together without losing decoupling.

Does this help?

@erikrose
Copy link
Owner

Say, while we're on the subject, have you seen https://pypi.python.org/pypi/blessings/ ? It might make your output easier, if you're targeting the terminal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants