3

Is there any way to get pyparsing to automatically set the resultsName of a grammar element to whatever it's named in my source code? That is, I would write code like

my_number = Word(nums)

and it would automatically execute

my_number.setResultsName('my_number')
Hooked
  • 84,485
  • 43
  • 192
  • 261
Elliot Gorokhovsky
  • 3,610
  • 2
  • 31
  • 56

2 Answers2

4

You should be able to simply do:

my_number = Word(nums)('my_number')

using the shortcut for .setResultsName. Python, in general, makes it hard to get at the name of the variable in question.

As an alternative, if you had a list of them as a dictionary you could do something like:

for key,val in grammar_dict.items():
    grammar_dict[key] = val.setResultsName(key)
Community
  • 1
  • 1
Hooked
  • 84,485
  • 43
  • 192
  • 261
  • 1
    Note that `setResultsName` makes and returns a *copy* of the original expression, so be sure to use the copy when assembling lower-level expressions into higher-level ones. – PaulMcG Feb 10 '17 at 18:51
  • Thanks. What other functions does this shortcut work with? e.g. can I write `expression = Forward()('expression')`? – Elliot Gorokhovsky Feb 10 '17 at 19:03
  • 1
    AFAIK you should be able to use it for _all_ pyparsing expressions. It really comes in handy when the grammar gets complex! – Hooked Feb 10 '17 at 19:18
  • You can also make a copy of an expression using just `()`, which I find useful when I want to have a specialized form of a generic expression like `quotedString` at one point in a large grammar, but I don't want to change all the other places `quotedString` is used. Like `quoted_string_of_numeric_digits = quotedString().addCondition(lambda t: t[0][1:-1].isdigit())`. This also evokes the Prototype pattern, where an existing instance is used to create new instances. – PaulMcG Feb 11 '17 at 13:23
2

Using the inspect module you can do what you want. Define a function srn:

import inspect

def srn(expr):
    """Sets the results name to the variable *name* of `expr`"""
    cf = inspect.currentframe()
    of = inspect.getouterframes(cf)[1]
    fs = inspect.getframeinfo(of[0]).code_context[0].strip()

    # name of FIRST parameter
    try:
        args = fs[fs.find('(') + 1:-1].split(',')
        n = args[0]
        if n.find('=') != -1:
            name = n.split('=')[1].strip()
        else:
            name = n
        expr.resultsName = name
    except IndexError:
        pass
    return expr

Then after my_number = Word(nums) call:

srn(my_number)

and my_number contains "my_number" as resultsName.

Disclaimer 1: This approach works well, but is quite hacky and dives deep into the Python internals, so do it at your own risk.

Disclaimer 2: The idea for this is not mine - I got it somewhere from StackOverflow but I don't now exactly where...

halloleo
  • 9,216
  • 13
  • 64
  • 122