From 5af481d62df80d8be3f5835042d30372ef9cbe04 Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Sat, 31 Oct 2020 21:59:00 -0600 Subject: define and annotate some language elements --- _ext/crowbar_domain.py | 91 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 22 deletions(-) (limited to '_ext') diff --git a/_ext/crowbar_domain.py b/_ext/crowbar_domain.py index a5f2570..db1330e 100644 --- a/_ext/crowbar_domain.py +++ b/_ext/crowbar_domain.py @@ -1,5 +1,7 @@ from collections import defaultdict +import re +from docutils import nodes from docutils.parsers.rst import directives from sphinx import addnodes @@ -7,41 +9,74 @@ from sphinx.directives import ObjectDescription from sphinx.domains import Domain from sphinx.domains import Index from sphinx.roles import XRefRole +from sphinx.util import logging from sphinx.util.nodes import make_refnode +logger = logging.getLogger(__name__) -class KeywordDirective(ObjectDescription): +peg_token = re.compile(r"\s+|\+|\*|\?|\(|\)|'[^']+'|[\w\-]+") +def tokenize_peg(peg_def): + for token in peg_token.finditer(peg_def): + yield token[0] + +class ElementDirective(ObjectDescription): has_content = True required_arguments = 1 + option_spec = { + 'contains': directives.unchanged_required, + } def handle_signature(self, sig, signode): - signode += addnodes.desc_name(text=sig) - return sig - - def add_target_and_index(self, name_cls, sig, signode): - signode['ids'].append('keyword' + '-' + sig) + element, defs = sig.split(' <- ') + peg_rule = nodes.literal() + signode += peg_rule + peg_rule += addnodes.desc_name(text=element) + peg_rule += nodes.Text(' ') + peg_rule += addnodes.desc_sig_operator(text='<-') + defs = defs.split(' / ') + for d in defs: + if peg_rule.children[-1].astext() != '<-': + peg_rule += [nodes.Text(' '), addnodes.desc_sig_operator(text='/')] + peg_rule += nodes.Text(' ') + for token in tokenize_peg(d): + if re.fullmatch(r"[a-z][\w\-]+", token): + refattrs = dict(refdomain='std', refexplicit=False, reftarget=token.replace('-', ' '), reftype='term', refwarn=True) + reference = addnodes.pending_xref('', nodes.literal(text=token, classes=['xref', 'std', 'std-term']), **refattrs) + peg_rule += addnodes.desc_sig_name('', '', reference) + elif re.fullmatch(r'[A-Z]\w+', token): + refattrs = dict(refdomain='crowbar', refexplicit=False, reftarget=token, reftype='ref', refwarn=True) + reference = addnodes.pending_xref('', nodes.literal(text=token, classes=['xref', 'crowbar', 'crowbar-ref']), **refattrs) + peg_rule += addnodes.desc_sig_name('', '', reference) + elif len(token.strip()) == 0 or (token.startswith("'") and token.endswith("'")): + peg_rule += nodes.literal(text=token) + else: + peg_rule += addnodes.desc_sig_operator('', '', nodes.literal(text=token)) + return element + + def add_target_and_index(self, name, sig, signode): + signode['ids'].append('element' + '-' + name) if 'noindex' not in self.options: crowbar = self.env.get_domain('crowbar') - crowbar.add_keyword(sig) + crowbar.add_element(name) -class KeywordIndex(Index): - name = 'keyword' - localname = 'Keyword Index' - shortname = 'Keyword' +class ElementIndex(Index): + name = 'element' + localname = 'Element Index' + shortname = 'Element' def generate(self, docnames=None): content = defaultdict(list) - # sort the list of recipes in alphabetical order - recipes = self.domain.get_objects() - recipes = sorted(recipes, key=lambda recipe: recipe[0]) + # sort the list of elements in alphabetical order + elements = self.domain.get_elements() + elements = sorted(elements, key=lambda recipe: recipe[0]) # generate the expected output, shown below, from the above using the # first letter of the recipe as a key to group thing # # name, subtype, docname, anchor, extra, qualifier, description - for name, dispname, typ, docname, anchor, _ in recipes: + for name, dispname, typ, docname, anchor, _ in elements: content[dispname[0].lower()].append( (dispname, 0, docname, anchor, docname, '', typ)) @@ -55,25 +90,28 @@ class CrowbarDomain(Domain): name = 'crowbar' label = 'Crowbar' roles = { - 'ref': XRefRole() + 'ref': XRefRole(warn_dangling=True), } directives = { - 'keyword': KeywordDirective, + 'element': ElementDirective, } indices = { - KeywordIndex, + ElementIndex, } initial_data = { - 'keywords': [], + 'elements': [], } def get_full_qualified_name(self, node): - return '{}.{}'.format('keyword', node.arguments[0]) + return '{}.{}'.format('element', node.arguments[0]) - def get_objects(self): - for obj in self.data['keywords']: + def get_elements(self): + for obj in self.data['elements']: yield(obj) + def get_objects(self): + yield from self.get_elements() + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): match = [(docname, anchor) @@ -99,6 +137,15 @@ class CrowbarDomain(Domain): self.data['keywords'].append( (name, signature, 'Keyword', self.env.docname, anchor, 0)) + def add_element(self, signature): + """Add a new element to the domain.""" + name = '{}.{}'.format('element', signature) + anchor = 'element-{}'.format(signature) + + # name, dispname, type, docname, anchor, priority + self.data['elements'].append( + (name, signature, 'Element', self.env.docname, anchor, 0)) + def setup(app): app.add_domain(CrowbarDomain) -- cgit v1.2.3