Skip to content

Commit

Permalink
docs/util: Cells now have properties
Browse files Browse the repository at this point in the history
Properties are both an option:
```
.. cell:def:: $add
   :properties: is_evaluable
```
and a field:
```
.. cell:def:: $eqx

   :property x-aware:
   :property is_evaluable:
```

Properties as an option appear in the property index: linking a given property to all cells with that property; while properties as a field display with the cell.
  • Loading branch information
KrystalDelusion committed Sep 6, 2024
1 parent fbbb401 commit 7f20645
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 21 deletions.
15 changes: 13 additions & 2 deletions docs/util/cellref.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class YosysCell:
code: str
inputs: list[str]
outputs: list[str]
properties: dict[str, bool]
properties: list[str]

class YosysCellGroupDocumenter(Documenter):
objtype = 'cellgroup'
Expand Down Expand Up @@ -298,11 +298,22 @@ def add_directive_header(self, sig: str) -> None:
self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename)

# options
opt_attrs = ["title", ]
opt_attrs = ["title", "properties", ]
for attr in opt_attrs:
val = getattr(cell, attr, None)
if isinstance(val, list):
val = ' '.join(val)
if val:
self.add_line(f' :{attr}: {val}', sourcename)

self.add_line('\n', sourcename)

# fields
field_attrs = ["properties", ]
for field in field_attrs:
attr = getattr(cell, field, [])
for val in attr:
self.add_line(f' :{field} {val}:', sourcename)

if self.options.noindex:
self.add_line(' :noindex:', sourcename)
Expand Down
190 changes: 171 additions & 19 deletions docs/util/cmdref.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

from __future__ import annotations

import re
from typing import cast

from docutils import nodes
from docutils.nodes import Node, Element
from docutils.nodes import Node, Element, system_message
from docutils.parsers.rst import directives
from docutils.parsers.rst.states import Inliner
from sphinx.application import Sphinx
from sphinx.domains import Domain, Index
from sphinx.domains.std import StandardDomain
from sphinx.environment import BuildEnvironment
from sphinx.roles import XRefRole
from sphinx.directives import ObjectDescription
from sphinx.directives.code import container_wrapper
from sphinx.util.nodes import make_refnode
from sphinx.util.docfields import Field
from sphinx import addnodes

class TocNode(ObjectDescription):
Expand Down Expand Up @@ -70,7 +75,8 @@ def handle_signature(self, sig, signode: addnodes.desc_signature):
return signode['fullname']

def add_target_and_index(self, name_cls, sig, signode):
signode['ids'].append(type(self).name + '-' + sig)
idx = type(self).name + '-' + sig
signode['ids'].append(idx)
if 'noindex' not in self.options:
name = "{}.{}.{}".format(self.name, type(self).__name__, sig)
tagmap = self.env.domaindata[type(self).name]['obj2tag']
Expand All @@ -79,13 +85,81 @@ def add_target_and_index(self, name_cls, sig, signode):
titlemap = self.env.domaindata[type(self).name]['obj2title']
titlemap[name] = title
objs = self.env.domaindata[type(self).name]['objects']
# (name, sig, typ, docname, anchor, prio)
objs.append((name,
sig,
title,
type(self).name,
self.env.docname,
type(self).name + '-' + sig,
idx,
0))

class PropNode(TocNode):
name = 'prop'
fieldname = 'props'

def handle_signature(self, sig: str, signode: addnodes.desc_signature):
signode['fullname'] = sig
signode['tocname'] = tocname = sig.split('::')[-1]
signode += addnodes.desc_name(text=tocname)
return signode['fullname']

def add_target_and_index(
self,
name: str,
sig: str,
signode: addnodes.desc_signature
) -> None:
idx = ".".join(name.split("::"))
signode['ids'].append(idx)
if 'noindex' not in self.options:
tocname: str = signode.get('tocname', name)
objs = self.env.domaindata[self.domain]['objects']
# (name, sig, typ, docname, anchor, prio)
objs.append((name,
tocname,
type(self).name,
self.env.docname,
idx,
1))

class CellGroupedField(Field):
"""Custom version of GroupedField which doesn't require content."""
is_grouped = True
list_type = nodes.bullet_list

def __init__(self, name: str, names: tuple[str, ...] = (), label: str = None,
rolename: str = None, can_collapse: bool = False) -> None:
super().__init__(name, names, label, True, rolename)
self.can_collapse = can_collapse

def make_field(self, types: dict[str, list[Node]], domain: str,
items: tuple, env: BuildEnvironment = None,
inliner: Inliner = None, location: Node = None) -> nodes.field:
fieldname = nodes.field_name('', self.label)
listnode = self.list_type()
for fieldarg, content in items:
par = nodes.paragraph()
if fieldarg:
par.extend(self.make_xrefs(self.rolename, domain,
fieldarg, nodes.Text,
env=env, inliner=inliner, location=location))

if len(content) == 1 and (
isinstance(content[0], nodes.Text) or
(isinstance(content[0], nodes.inline) and len(content[0]) == 1 and
isinstance(content[0][0], nodes.Text))):
par += nodes.Text(' -- ')
par += content
listnode += nodes.list_item('', par)

if len(items) == 1 and self.can_collapse:
list_item = cast(nodes.list_item, listnode[0])
fieldbody = nodes.field_body('', list_item[0])
return nodes.field('', fieldname, fieldbody)

fieldbody = nodes.field_body('', listnode)
return nodes.field('', fieldname, fieldbody)

class CellNode(TocNode):
"""A custom node that describes an internal cell."""

Expand All @@ -94,8 +168,15 @@ class CellNode(TocNode):
option_spec = {
'title': directives.unchanged,
'ports': directives.unchanged,
'properties': directives.unchanged,
}

doc_field_types = [
CellGroupedField('props', label='Properties', rolename='prop',
names=('properties', 'property', 'tag', 'tags'),
can_collapse=True),
]

def handle_signature(self, sig: str, signode: addnodes.desc_signature):
signode['fullname'] = sig
signode['tocname'] = tocname = sig.split('::')[-1]
Expand All @@ -113,16 +194,18 @@ def add_target_and_index(
signode['ids'].append(idx)
if 'noindex' not in self.options:
tocname: str = signode.get('tocname', name)
tagmap = self.env.domaindata[self.domain]['obj2tag']
tagmap[name] = list(self.options.get('tags', '').split(' '))
title: str = self.options.get('title', sig)
titlemap = self.env.domaindata[self.domain]['obj2title']
titlemap[name] = title
props = self.options.get('properties', '')
if props:
propmap = self.env.domaindata[self.domain]['obj2prop']
propmap[name] = props.split(' ')
objs = self.env.domaindata[self.domain]['objects']
# (name, sig, typ, docname, anchor, prio)
objs.append((name,
tocname,
title,
type(self).name,
self.env.docname,
idx,
0))
Expand Down Expand Up @@ -288,7 +371,7 @@ def generate(self, docnames=None):
in self.domain.get_objects()}

tmap = {}
tags = self.domain.data['obj2tag']
tags = self.domain.data[f'obj2{self.name}']
for name, tags in tags.items():
for tag in tags:
tmap.setdefault(tag,[])
Expand All @@ -304,10 +387,9 @@ def generate(self, docnames=None):
anchor,
docname, '', typ
))
re = [(k, v) for k, v in sorted(content.items())]

return (re, True)
ret = [(k, v) for k, v in sorted(content.items())]

return (ret, True)

class CommandIndex(Index):
name = 'cmd'
Expand Down Expand Up @@ -345,7 +427,8 @@ def generate(self, docnames=None):
content = {}
items = ((name, dispname, typ, docname, anchor)
for name, dispname, typ, docname, anchor, prio
in self.domain.get_objects())
in self.domain.get_objects()
if typ == self.name)
items = sorted(items, key=lambda item: item[0])
for name, dispname, typ, docname, anchor in items:
lis = content.setdefault(self.shortname, [])
Expand All @@ -354,15 +437,68 @@ def generate(self, docnames=None):
anchor,
'', '', typ
))
re = [(k, v) for k, v in sorted(content.items())]
ret = [(k, v) for k, v in sorted(content.items())]

return (re, True)
return (ret, True)

class CellIndex(CommandIndex):
name = 'cell'
localname = 'Internal cell reference'
shortname = 'Internal cell'

class PropIndex(TagIndex):
"""A custom directive that creates a properties matrix."""

name = 'prop'
localname = 'Property Index'
shortname = 'Prop'
fieldname = 'props'

def generate(self, docnames=None):
content = {}

cells = {name: (dispname, docname, anchor)
for name, dispname, typ, docname, anchor, _
in self.domain.get_objects()
if typ == 'cell'}
props = {name: (dispname, docname, anchor)
for name, dispname, typ, docname, anchor, _
in self.domain.get_objects()
if typ == 'prop'}

tmap: dict[str, list[str]] = {}
tags: dict[str, list[str]] = self.domain.data[f'obj2{self.name}']
for name, tags in tags.items():
for tag in tags:
tmap.setdefault(tag,[])
tmap[tag].append(name)

for tag in sorted(tmap.keys()):
test = re.match(r'^(\w+[_-])', tag)
tag_prefix = test.group(1)
lis = content.setdefault(tag_prefix, [])
try:
dispname, docname, anchor = props[tag]
except KeyError:
dispname = tag
docname = anchor = ''
lis.append((
dispname, 1, docname,
anchor,
'', '', docname or 'unavailable'
))
objlis = tmap[tag]
for objname in sorted(objlis):
dispname, docname, anchor = cells[objname]
lis.append((
dispname, 2, docname,
anchor,
'', '', docname
))
ret = [(k, v) for k, v in sorted(content.items())]

return (ret, True)

class CommandDomain(Domain):
name = 'cmd'
label = 'Yosys commands'
Expand Down Expand Up @@ -419,17 +555,33 @@ class CellDomain(CommandDomain):
name = 'cell'
label = 'Yosys internal cells'

roles = CommandDomain.roles.copy()
roles.update({
'prop': XRefRole()
})

directives = {
'def': CellNode,
'defprop': PropNode,
'source': CellSourceNode,
'group': CellGroupNode,
}

indices = {
CellIndex,
TagIndex
PropIndex
}

initial_data = {
'objects': [], # object list
'obj2prop': {}, # name -> properties
'obj2title': {}, # name -> title
}

def get_objects(self):
for obj in self.data['objects']:
yield(obj)

def autoref(name, rawtext: str, text: str, lineno, inliner: Inliner,
options=None, content=None):
role = 'cell:ref' if text[0] == '$' else 'cmd:ref'
Expand All @@ -448,17 +600,17 @@ def setup(app: Sphinx):
('cmd-tag', '', 'Tag Index')
StandardDomain.initial_data['labels']['cellindex'] =\
('cell-cell', '', 'Internal cell reference')
StandardDomain.initial_data['labels']['tagindex'] =\
('cell-tag', '', 'Tag Index')
StandardDomain.initial_data['labels']['propindex'] =\
('cell-prop', '', 'Property Index')

StandardDomain.initial_data['anonlabels']['commandindex'] =\
('cmd-cmd', '')
StandardDomain.initial_data['anonlabels']['tagindex'] =\
('cmd-tag', '')
StandardDomain.initial_data['anonlabels']['cellindex'] =\
('cell-cell', '')
StandardDomain.initial_data['anonlabels']['tagindex'] =\
('cell-tag', '')
StandardDomain.initial_data['anonlabels']['propindex'] =\
('cell-prop', '')

app.add_role('autoref', autoref)

Expand Down

0 comments on commit 7f20645

Please sign in to comment.