diff options
author | Melody Horn <melody@boringcactus.com> | 2020-10-31 21:59:00 -0600 |
---|---|---|
committer | Melody Horn <melody@boringcactus.com> | 2020-10-31 21:59:00 -0600 |
commit | 5af481d62df80d8be3f5835042d30372ef9cbe04 (patch) | |
tree | 6d995adefedd90fd3db269f898a527313a37af10 /_ext | |
parent | c916253f17b329550250549ea0aef4b67ced026f (diff) | |
download | spec-5af481d62df80d8be3f5835042d30372ef9cbe04.tar.gz spec-5af481d62df80d8be3f5835042d30372ef9cbe04.zip |
define and annotate some language elements
Diffstat (limited to '_ext')
-rw-r--r-- | _ext/crowbar_domain.py | 91 |
1 files changed, 69 insertions, 22 deletions
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) |