Skip to content

Commit af0dfa2

Browse files
authored
Merge pull request #8247 from silene/inter_prodlist_refs
Allow production lists to refer to tokens from other production groups.
2 parents 9a905ce + ffacb2c commit af0dfa2

File tree

3 files changed

+50
-11
lines changed

3 files changed

+50
-11
lines changed

doc/usage/restructuredtext/directives.rst

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,20 +1206,29 @@ the definition of the symbol. There is this directive:
12061206
the following definition. If the definition spans multiple lines, each
12071207
continuation line must begin with a colon placed at the same column as in
12081208
the first line.
1209+
Blank lines are not allowed within ``productionlist`` directive arguments.
1210+
1211+
The definition can contain token names which are marked as interpreted text
1212+
(e.g., "``sum ::= `integer` "+" `integer```") -- this generates
1213+
cross-references to the productions of these tokens. Outside of the
1214+
production list, you can reference to token productions using
1215+
:rst:role:`token`.
12091216

12101217
The *productionGroup* argument to :rst:dir:`productionlist` serves to
12111218
distinguish different sets of production lists that belong to different
12121219
grammars. Multiple production lists with the same *productionGroup* thus
12131220
define rules in the same scope.
12141221

1215-
Blank lines are not allowed within ``productionlist`` directive arguments.
1222+
Inside of the production list, tokens implicitly refer to productions
1223+
from the current group. You can refer to the production of another
1224+
grammar by prefixing the token with its group name and a colon, e.g,
1225+
"``otherGroup:sum``". If the group of the token should not be shown in
1226+
the production, it can be prefixed by a tilde, e.g.,
1227+
"``~otherGroup:sum``". To refer to a production from an unnamed
1228+
grammar, the token should be prefixed by a colon, e.g., "``:sum``".
12161229

1217-
The definition can contain token names which are marked as interpreted text
1218-
(e.g. "``sum ::= `integer` "+" `integer```") -- this generates
1219-
cross-references to the productions of these tokens. Outside of the
1220-
production list, you can reference to token productions using
1221-
:rst:role:`token`.
1222-
However, if you have given a *productionGroup* argument you must prefix the
1230+
Outside of the production list,
1231+
if you have given a *productionGroup* argument you must prefix the
12231232
token name in the cross-reference with the group name and a colon,
12241233
e.g., "``myGroup:sum``" instead of just "``sum``".
12251234
If the group should not be shown in the title of the link either

sphinx/domains/std.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
# RE for option descriptions
4646
option_desc_re = re.compile(r'((?:/|--|-|\+)?[^\s=]+)(=?\s*.*)')
4747
# RE for grammar tokens
48-
token_re = re.compile(r'`(\w+)`', re.U)
48+
token_re = re.compile(r'`((~?\w*:)?\w+)`', re.U)
4949

5050

5151
class GenericObject(ObjectDescription[str]):
@@ -464,9 +464,23 @@ def token_xrefs(text: str, productionGroup: str = '') -> List[Node]:
464464
if m.start() > pos:
465465
txt = text[pos:m.start()]
466466
retnodes.append(nodes.Text(txt, txt))
467-
refnode = pending_xref(m.group(1), reftype='token', refdomain='std',
468-
reftarget=productionGroup + m.group(1))
469-
refnode += nodes.literal(m.group(1), m.group(1), classes=['xref'])
467+
token = m.group(1)
468+
if ':' in token:
469+
if token[0] == '~':
470+
_, title = token.split(':')
471+
target = token[1:]
472+
elif token[0] == ':':
473+
title = token[1:]
474+
target = title
475+
else:
476+
title = token
477+
target = token
478+
else:
479+
title = token
480+
target = productionGroup + token
481+
refnode = pending_xref(title, reftype='token', refdomain='std',
482+
reftarget=target)
483+
refnode += nodes.literal(token, title, classes=['xref'])
470484
retnodes.append(refnode)
471485
pos = m.end()
472486
if pos < len(text):

tests/test_domain_std.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,22 @@ def test_productionlist(app, status, warning):
405405
assert "A</strong> ::= B C D E F G" in text
406406

407407

408+
def test_productionlist2(app):
409+
text = (".. productionlist:: P2\n"
410+
" A: `:A` `A`\n"
411+
" B: `P1:B` `~P1:B`\n")
412+
doctree = restructuredtext.parse(app, text)
413+
refnodes = list(doctree.traverse(pending_xref))
414+
assert_node(refnodes[0], pending_xref, reftarget="A")
415+
assert_node(refnodes[1], pending_xref, reftarget="P2:A")
416+
assert_node(refnodes[2], pending_xref, reftarget="P1:B")
417+
assert_node(refnodes[3], pending_xref, reftarget="P1:B")
418+
assert_node(refnodes[0], [pending_xref, nodes.literal, "A"])
419+
assert_node(refnodes[1], [pending_xref, nodes.literal, "A"])
420+
assert_node(refnodes[2], [pending_xref, nodes.literal, "P1:B"])
421+
assert_node(refnodes[3], [pending_xref, nodes.literal, "B"])
422+
423+
408424
def test_disabled_docref(app):
409425
text = (":doc:`index`\n"
410426
":doc:`!index`\n")

0 commit comments

Comments
 (0)