aboutsummaryrefslogtreecommitdiff
path: root/_ext
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2020-10-31 21:59:00 -0600
committerMelody Horn <melody@boringcactus.com>2020-10-31 21:59:00 -0600
commit5af481d62df80d8be3f5835042d30372ef9cbe04 (patch)
tree6d995adefedd90fd3db269f898a527313a37af10 /_ext
parentc916253f17b329550250549ea0aef4b67ced026f (diff)
downloadspec-5af481d62df80d8be3f5835042d30372ef9cbe04.tar.gz
spec-5af481d62df80d8be3f5835042d30372ef9cbe04.zip
define and annotate some language elements
Diffstat (limited to '_ext')
-rw-r--r--_ext/crowbar_domain.py91
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)