Listeners¶
Listeners are specific kind of actions that are executed before and after
processing (recognizing or generating) a rule. They are optional, but even
multiple of them can be defined and registered at the same time to the
same grammarinator.runtime.Generator.
Registration can happen with the --listener argument of the
grammarinator-generate script or through the
constructor of grammarinator.runtime.Generator.
When generating a rule, the grammarinator.runtime.Generator will look
up all the registered listeners and will call the enter_rule and
exit_rule methods of them. enter_rule methods will be called in the
order of the registration of the corresponding listeners, while exit_rule
methods will be called in reversed order.
Grammarinator defines two listeners which can be subclassed and customized further:
grammarinator.runtime.Listener: The subclasses can override theenter_rule()and theexit_rule()methods that take a rule object under construction as parameter. The methods are called for every rule.
grammarinator.runtime.DispatchingListener: A subclass ofListenerthat enables to write enter and exit methods on a per-rule basis (in the form ofenter_ruleAorexit_ruleBforruleAandruleB, respectively) which can make the listener code cleaner and more maintainable.
The following examples show how to subclass listeners.
The first example inherits from grammarinator.runtime.Listener
and collects statistical data about the amount of generated rules. It is
inherited from grammarinator.runtime.Listener since there is no
need to differentiate between the rules, all of them are counted, hence
the general enter and exit rules are sufficient.
from collections import defaultdict
from grammarinator.runtime import Listener
class StatisticsListener(Listener):
"""
Listener to collect statistical data about the distribution of the
generated rules.
"""
def __init__(self):
self.stat = defaultdict(int)
def enter_rule(self, node):
self.stat[node.name] += 1
The second example inherits from
grammarinator.runtime.DispatchingListener. It only follows the enters
and exits of a hypothetical function rule to keep track of the current
nesting level.
from grammarinator.runtime import DispatchingListener
class FunctionListener(DispatchingListener):
"""
Listener to keep track of the nesting level of inline functions.
"""
def __init__(self):
self.func_depth = 0
def enter_function(self, node):
self.func_depth += 1
def exit_function(self, node):
self.func_depth -= 1
The same can be implemented with subclassing
grammarinator.runtime.Listener of course, except that the name of the
node has to be inspected before incrementing the func_depth counter.
from grammarinator.runtime import Listener
class FunctionListener(Listener):
"""
Listener to keep track of the nesting level of inline functions.
"""
def __init__(self):
self.func_depth = 0
def enter_rule(self, node):
if node.name == 'function':
self.func_depth += 1
def exit_rule(self, node):
if node.name == 'function':
self.func_depth -= 1
Also see the documentation for details on listeners in ANTLRv4.