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

"No visitor method was defined" message unhelpful for unnamed expressions #110

Closed
lucasribeiroufrj opened this issue Feb 27, 2017 · 14 comments

Comments

@lucasribeiroufrj
Copy link

I have downloaded the framework parsimonious yesterday (version 0.7.0) and created the following code.
hello.py.txt

The problem is that when I run the code I get the error msg:
"parsimonious.exceptions.VisitationError: NotImplementedError: No visitor method was defined for ."

Is it a bug? There is no rule called "."!

The full error: error.txt

@jwhitlock
Copy link
Contributor

You should use https://gist.github.com for pasting code - it makes it easier to view, comment, and fix. Here's your code as a gist: hello.py

I find it useful to run my code under the Python debugger, so that I can investigate what is going on: python -m pdb hello.py. In this case, it isn't a rule called . - it's a rule with no name, which is a little more apparent when you look at the generic_visit code:

        raise NotImplementedError("No visitor method was defined for %s." %
                                  node.expr_name)

That %s should be in quotes (like '%s'), so it is more apparent that a rule with an empty name is being complained about. I have a similar memory of hitting this bug.

An implicit, unnamed node is being created by the zero-or-more rule around spacing:

spacing			 = ( comment / EndOfLine / Space )*

One way to work around this is to name the implicit node:

spacing       = spacing_token*
spacing_token = (comment / EndOfLine / Space)

And now declare a visit_spacing_token member.

Another way is to define a generic_visit function that does what you want with nodes you don't care about.

@lucaswiman
Copy link
Collaborator

This is definitely something I've found really confusing as well.

@lucasribeiroufrj
Copy link
Author

Humm https://gist.github.com, good to know about it!

Thank you for the debug information, I did not know about it.

Right on the spot! I have fixed using your explanation, thank you very much!!

erikrose added a commit that referenced this issue Mar 2, 2017
…r message.

This was confusing people, as in #110.

I also did an audit of the rest of the %s occurrences in the codebase, and no other obviously beneficial quotes are missing.
@erikrose
Copy link
Owner

erikrose commented Mar 2, 2017

There, that should help for the moment. When somebody (or I) have a few more minutes, we could make the message more helpful in the case of an unnamed rule by having it suggest you define a generic_visitor or name the subexpression (which we can also call out into the error message by using its repr).

@erikrose erikrose changed the title "No visitor method was defined for ." "No visitor method was defined" message unhelpful for unnamed expressions Mar 2, 2017
@cjw296
Copy link

cjw296 commented Mar 6, 2017

@erikrose - I think there's a bug lurking here. Take the following example:

from parsimonious import Grammar, NodeVisitor


class Query(NodeVisitor):

    grammar = Grammar("""
expression = field clause*
clause     = op field
field      = ~"[a-z_]+"
op         = "+"
""")

    def visit_op(self, node, children):
        return ' plus '

    def visit_field(self, node, children):
        return node.match.group(0)

    def visit_clause(self, node, children):
        return ' '.join(children)

    def visit_expression(self, node, children):
        return ' '.join(children)

    def generic_visit(sef, node, children):
        raise Exception(repr((node, children)))


print Query().parse("x+y")

This blows up as follows:

    raise Exception(repr((node, children)))
parsimonious.exceptions.VisitationError: Exception: (s = 'x+y'
Node('', s, 1, 3, children=[Node('clause', s, 1, 3, children=[Node('op', s, 1, 2), RegexNode('field', s, 2, 3)])]), [' plus  y'])

Parse tree:
<Node matching "+y">  <-- *** We were here. ***
    <Node called "clause" matching "+y">
        <Node called "op" matching "+">
        <RegexNode called "field" matching "y">

How can I give a name to the subexpression here?

@erikrose
Copy link
Owner

erikrose commented Mar 9, 2017

It looks like the nameless top-level expression in the traceback is clause*: a ZeroOrMore containing the atom clause. We should do a nicer job in Node.prettily(), grabbing the expression's own pretty-printing if it has no label.

@erikrose
Copy link
Owner

erikrose commented Mar 9, 2017

In fact, that's what #59 is about: a great first patch for somebody!

@cjw296
Copy link

cjw296 commented Mar 9, 2017

Okay, but still, how can I give a name to that ZeroOrMore here, so that I can define a visitor method for it?

@erikrose
Copy link
Owner

erikrose commented Mar 9, 2017

Oh! I didn't understand your question. All you should have to do is…

clauses = clause*

And then say…

expression = field clauses

@cjw296
Copy link

cjw296 commented Mar 9, 2017

Have you tried your suggestion? My experience is that generic_visit will still get called even with a clauses rule defined.

@erikrose
Copy link
Owner

erikrose commented May 5, 2017

I just tried it, and generic_visit does not appear to be called for me in the presence of a clauses rule:

from parsimonious import Grammar, NodeVisitor


class Query(NodeVisitor):

    grammar = Grammar("""
expression = field clauses
clauses = clause*
clause     = op field
field      = ~"[a-z_]+"
op         = "+"
""")

    def visit_op(self, node, children):
        return ' plus '

    def visit_field(self, node, children):
        return node.match.group(0)

    def visit_clause(self, node, children):
        return ' '.join(children)

    def visit_expression(self, node, children):
        return ' '.join(children)

    def visit_clauses(self, node, children):
        return "ha"

print Query().parse("x+y")
x ha

@cjw296
Copy link

cjw296 commented May 10, 2017

Hmm, well, without being able to reproduce myself, I'll retract my assertion that there's a bug lurking there until such time as I can reproduce it again ;-)

@ghost
Copy link

ghost commented Jun 21, 2017

I'm getting the same empty node name error with this peg.

grammar = Grammar(r"""
    sexp = lp ( atom / sexp )* rp
    atom = num
    num = ~"[0-9]+" delim
    delim = &lp / &rp / ws
    lp = "("
    rp = ")"
    ws = ~"[\r\a\t\n ]+"
    """)

tree = grammar.parse('(1 2 3 (4 5))')

print LispFormatter().visit(tree)
parsimonious.exceptions.VisitationError: NotImplementedError: No visitor method was defined for .

Parse tree:
<Node matching "1 ">  <-- *** We were here. ***
    <RegexNode called "num" matching "1">
    <Node called "delim" matching " ">
        <RegexNode called "ws" matching " ">

@lonnen
Copy link
Collaborator

lonnen commented Mar 29, 2022

issue #59 is long closed, and I think that means all the desired remediation for this is done. Please file a new issue or reopen this one if I'm mistaken

@lonnen lonnen closed this as completed Mar 29, 2022
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

6 participants