Skip to content

Commit

Permalink
Support rule level named actions (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
renatahodovan authored May 24, 2024
1 parent 2db57db commit 85d6237
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 6 deletions.
18 changes: 12 additions & 6 deletions docs/guide/actions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Actions
-------

Actions serve the purpose of defining operations that cannot be expressed
solely through grammar rules. Actions can be defined either anonymously
solely through grammar rules. Actions can be defined either
within rule definitions or within the global scope of the grammar (outside
of any rule definitions), prefixed with specific directives.

Expand All @@ -30,15 +30,21 @@ Named Actions
=============

Named actions are similar to anonymous actions in that they are also inline
code blocks enclosed in braces. However, instead of placing them inside rule
definitions, named actions are inserted into the global scope. These named
actions allow for the inclusion of custom code that can be utilized across
multiple rules or provide global functionality to the grammar. They are
code blocks enclosed in braces. However, they have a prefix in form of
``@<name>``, where the name part specifies their purpose. There are rule and
global level named actions. Rule level actions are ``init`` and ``after``
and they define code blocks to be executed before any child node generation
(but after :meth:`~grammarinator.runtime.Listener.enter_rule` gets called)
and after the generation of all children (but before
:meth:`~grammarinator.runtime.Listener.exit_rule` gets called), respectively.
Global level named actions place the code blocks into the global scope.
These named actions allow for the inclusion of custom code that can be utilized
across multiple rules or provide global functionality to the grammar. They are
prefixed with specific directives to indicate their purpose. These directives
can take one of two values: ``header`` or ``members``. These named actions can
be defined for both lexer/parser and combined grammars.

The syntax of named actions looks like:
The syntax of global named actions looks like:

.. code-block:: antlr
Expand Down
15 changes: 15 additions & 0 deletions grammarinator/tool/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ def __init__(self, name, type):
self.args = []
self.locals = []
self.returns = []
self.init = ''
self.after = ''

def __str__(self):
return f'{super().__str__()}; name: {self.id}'
Expand Down Expand Up @@ -880,6 +882,19 @@ def build_expr(node, parent_id):
rule.args = parse_arg_action_block(node, 'args')
rule.locals = parse_arg_action_block(node.localsSpec(), 'locals')
rule.returns = parse_arg_action_block(node.ruleReturns(), 'returns')

for prequel in node.rulePrequel() or []:
rule_action = prequel.ruleAction()
if rule_action:
action_name = str(rule_action.identifier().TOKEN_REF() or rule_action.identifier().RULE_REF())
if action_name not in ['init', 'after']:
continue

src = ''.join(str(child) for child in rule_action.actionBlock().ACTION_CONTENT()).strip()
if action_name == 'init':
rule.init = src
elif action_name == 'after':
rule.after = src
build_expr(node.ruleBlock(), parent_id)

elif isinstance(node, (ANTLRv4Parser.RuleAltListContext, ANTLRv4Parser.AltListContext, ANTLRv4Parser.LexerAltListContext)):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,15 @@ class {{ graph.name }}({{ graph.superclass }}):
{% endif %}
with {{ rule.type }}Context(self, '{{ rule.name }}', parent) as rule:
current = rule.current
{% if rule.init %}
{{ resolveVarRefs(rule.init) }}
{% endif %}
{% for edge in rule.out_edges %}
{{ processNode(edge.dst, edge) | indent | indent | indent -}}
{% endfor %}
{% if rule.after %}
{{ resolveVarRefs(rule.after) }}
{% endif %}
{% for _, k, _ in rule.returns %}
current.{{ k }} = local_ctx['{{ k }}']
{% endfor %}
Expand Down
30 changes: 30 additions & 0 deletions tests/grammars/InitAfter.g4
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Renata Hodovan, Akos Kiss.
*
* Licensed under the BSD 3-Clause License
* <LICENSE.rst or https://opensource.org/licenses/BSD-3-Clause>.
* This file may not be copied, modified, or distributed except
* according to those terms.
*/

/*
* This test checks whether the processing of @init and @after rule
* actions work as expected.
*/

// TEST-PROCESS: {grammar}.g4 -o {tmpdir}
// TEST-GENERATE: {grammar}Generator.{grammar}Generator -j 1 -o {tmpdir}/{grammar}%d.txt

grammar InitAfter;

start : r=wrapped_rule {assert $r.testValue == 'endValue', $r.testValue} ;

wrapped_rule returns [testValue]
@init {
$testValue = 'startValue'
}
@after {
$testValue = 'endValue'
}
: {assert $testValue == 'startValue', $testValue} A ;
A : 'a' ;

0 comments on commit 85d6237

Please sign in to comment.