0

I'm working with ANTLR to parse some language these days. I chose to work with Python. The parser class which ANTLR generates, contains many methods with similar names:

class autogeneratedparser(xxx):
    def something_enter(self,ctx):
       pass
    def something_exit(self,ctx)
       pass

I override these by defining an inheriting class

class myclass(autogeneratedparser)
    particularthing = False
    def particularthing_enter(self,ctx):
       print(ctx.name)
       myclass.particularthing = true
    def particularthing_exit(self,ctx):
       print(ctx.name)
       myclass.particularthing = false

I'd like to dynamically and automatically generate all those methods, changing their respective variable name included. In pseudo-code:

generate for particularthing in anything:
        $(particularthing) = False
        def $(particularthing)_enter(self,ctx):
           print(ctx.name)
           myclass.$(particularthing) = true
        def $(particularthing)_exit(self,ctx):
           print(ctx.name)
           myclass.$(particularthing) = false

I could obviously just tell vim to do it, but I'm sure Python has a way too, just don't know how :-)

Thanks for your valuable input :)

chrisvp
  • 174
  • 9

1 Answers1

0

All you need to do is iterate over the initial class' attributes, select only the ones that match SOMETHING_enter and SOMETHING_exit, then create stub methods and put it all together with type() (yes, it is used for creating dynamic classes).

Here's my version:

# the generated class
class SomeClass(object):
    def something_enter(self, ctx):
       pass
    def something_exit(self, ctx):
       pass


def generate_stubbed_class(klass_name, klass):
    # creates and returns a function that does the generic behavior (print's out ctx.name)
    # and sets the attribute to the given value
    def generate_stub(name, value):
        def stub(self, ctx):
            print(ctx.name)
            setattr(self, name, value)

        return stub

    attributes = {}

    for name in dir(klass): # lists all attributes (names only)
        try:
            key, oper = name.split('_')
        except ValueError:
            # not in the format of SOMETHING_SOMETHING
            continue

        attr = getattr(klass, name)

        # make sure it's a function and name ends in _enter or _exit
        if callable(attr) and oper in ('enter', 'exit'):
            value = oper == 'enter' # True for 'enter', False for 'exit'
            attributes[name] = generate_stub(key, value)
            attributes[key] = False # set more than once, but no negative effect

    # this is where the magic happens
    return type(klass_name, (klass,), attributes)


MyClass = generate_stubbed_class('MyClass', SomeClass)
obj = MyClass()

assert isinstance(obj, SomeClass)

# need some class with a 'name' attribute, so we create one on the fly
from collections import namedtuple
Data = namedtuple('Data', 'name')

# initially it's False
assert obj.something is False

# set to True, prints 'foo'
obj.something_enter(Data('foo'))
assert obj.something is True

# back to False, prints 'bar'
obj.something_exit(Data('bar'))
assert obj.something is False
Anonymous
  • 11,740
  • 3
  • 40
  • 50