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

Ensure that listener sees correct name when lexer rule refers to another lexer rule #239

Merged
merged 1 commit into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions grammarinator/runtime/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,28 @@ class UnlexerRuleContext(RuleContext):

def __init__(self, gen, name, parent=None):
if isinstance(parent, UnlexerRule):
# If parent node is also an UnlexerRule then this is a sub-rule and
# actually no child node is created, but the parent is kept as the
# current node
super().__init__(gen, parent)
self._start_depth = None
# So, save the name of the parent node and also that of the sub-rule
self._parent_name = parent.name
self._name = name
else:
node = UnlexerRule(name=name)
if parent:
parent += node
super().__init__(gen, node)
self._start_depth = self.gen._size.depth
self._parent_name = None
self._name = None

def __enter__(self):
# When entering a sub-rule, rename the current node to reflect the name
# of the sub-rule
if self._name is not None and self._parent_name is not None:
self.node.name = self._name
super().__enter__()
# Increment token count with the current token.
self.gen._size.tokens += 1
Expand All @@ -84,6 +96,10 @@ def __exit__(self, exc_type, exc_val, exc_tb):
super().__exit__(exc_type, exc_val, exc_tb)
if self._start_depth is not None:
self.node.size.depth -= self._start_depth
# When exiting a sub-rule, change the name of the current node back to
# that of the parent
if self._name is not None and self._parent_name is not None:
self.node.name = self._parent_name


class UnparserRuleContext(RuleContext):
Expand Down
48 changes: 48 additions & 0 deletions tests/grammars/Fragment.g4
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 listeners see the correct names when lexer rules
* refer to other lexer rules (usually, fragment rules).
*/

// TEST-PROCESS: {grammar}.g4 -o {tmpdir}
// TEST-GENERATE: {grammar}Generator.{grammar}Generator -r start -j 1 -n 1 --listener {grammar}Generator.CustomListener -o {tmpdir}/{grammar}%d.txt

grammar Fragment;

@header {

from collections import Counter
from grammarinator.runtime import Listener


class CustomListener(Listener):

def __init__(self):
self.cnt_enters = Counter()
self.cnt_exits = Counter()

def enter_rule(self, node):
self.cnt_enters[node.name] += 1

def exit_rule(self, node):
self.cnt_exits[node.name] += 1
if node.name == 'start':
assert self.cnt_enters == Counter(start=1, A=1, B=1, C=1, D=1, E=1), self.cnt_enters
assert self.cnt_exits == Counter(start=1, A=1, B=1, C=1, D=1, E=1), self.cnt_exits
}


start : A;
A : 'a' B ;
B : 'b' C ;
fragment C : 'c' D ;
fragment D : 'd' E ;
E : 'e' ;
Loading