162306a36Sopenharmony_ci# -*- coding: utf-8; mode: python -*- 262306a36Sopenharmony_ci# pylint: disable=W0141,C0113,C0103,C0325 362306a36Sopenharmony_ciu""" 462306a36Sopenharmony_ci cdomain 562306a36Sopenharmony_ci ~~~~~~~ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Replacement for the sphinx c-domain. 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci :copyright: Copyright (C) 2016 Markus Heiser 1062306a36Sopenharmony_ci :license: GPL Version 2, June 1991 see Linux/COPYING for details. 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci List of customizations: 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci * Moved the *duplicate C object description* warnings for function 1562306a36Sopenharmony_ci declarations in the nitpicky mode. See Sphinx documentation for 1662306a36Sopenharmony_ci the config values for ``nitpick`` and ``nitpick_ignore``. 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci * Add option 'name' to the "c:function:" directive. With option 'name' the 1962306a36Sopenharmony_ci ref-name of a function can be modified. E.g.:: 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci .. c:function:: int ioctl( int fd, int request ) 2262306a36Sopenharmony_ci :name: VIDIOC_LOG_STATUS 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci The func-name (e.g. ioctl) remains in the output but the ref-name changed 2562306a36Sopenharmony_ci from 'ioctl' to 'VIDIOC_LOG_STATUS'. The function is referenced by:: 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci * :c:func:`VIDIOC_LOG_STATUS` or 2862306a36Sopenharmony_ci * :any:`VIDIOC_LOG_STATUS` (``:any:`` needs sphinx 1.3) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci * Handle signatures of function-like macros well. Don't try to deduce 3162306a36Sopenharmony_ci arguments types of function-like macros. 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci""" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cifrom docutils import nodes 3662306a36Sopenharmony_cifrom docutils.parsers.rst import directives 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciimport sphinx 3962306a36Sopenharmony_cifrom sphinx import addnodes 4062306a36Sopenharmony_cifrom sphinx.domains.c import c_funcptr_sig_re, c_sig_re 4162306a36Sopenharmony_cifrom sphinx.domains.c import CObject as Base_CObject 4262306a36Sopenharmony_cifrom sphinx.domains.c import CDomain as Base_CDomain 4362306a36Sopenharmony_cifrom itertools import chain 4462306a36Sopenharmony_ciimport re 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci__version__ = '1.1' 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci# Get Sphinx version 4962306a36Sopenharmony_cimajor, minor, patch = sphinx.version_info[:3] 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci# Namespace to be prepended to the full name 5262306a36Sopenharmony_cinamespace = None 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci# 5562306a36Sopenharmony_ci# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags 5662306a36Sopenharmony_ci# - Store the namespace if ".. c:namespace::" tag is found 5762306a36Sopenharmony_ci# 5862306a36Sopenharmony_ciRE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$') 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cidef markup_namespace(match): 6162306a36Sopenharmony_ci global namespace 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci namespace = match.group(1) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return "" 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci# 6862306a36Sopenharmony_ci# Handle c:macro for function-style declaration 6962306a36Sopenharmony_ci# 7062306a36Sopenharmony_ciRE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$') 7162306a36Sopenharmony_cidef markup_macro(match): 7262306a36Sopenharmony_ci return ".. c:function:: " + match.group(1) + ' ' + match.group(2) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci# 7562306a36Sopenharmony_ci# Handle newer c domain tags that are evaluated as .. c:type: for 7662306a36Sopenharmony_ci# backward-compatibility with Sphinx < 3.0 7762306a36Sopenharmony_ci# 7862306a36Sopenharmony_ciRE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$') 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cidef markup_ctype(match): 8162306a36Sopenharmony_ci return ".. c:type:: " + match.group(2) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci# 8462306a36Sopenharmony_ci# Handle newer c domain tags that are evaluated as :c:type: for 8562306a36Sopenharmony_ci# backward-compatibility with Sphinx < 3.0 8662306a36Sopenharmony_ci# 8762306a36Sopenharmony_ciRE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`') 8862306a36Sopenharmony_cidef markup_ctype_refs(match): 8962306a36Sopenharmony_ci return ":c:type:`" + match.group(2) + '`' 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci# 9262306a36Sopenharmony_ci# Simply convert :c:expr: and :c:texpr: into a literal block. 9362306a36Sopenharmony_ci# 9462306a36Sopenharmony_ciRE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`') 9562306a36Sopenharmony_cidef markup_c_expr(match): 9662306a36Sopenharmony_ci return '\\ ``' + match.group(2) + '``\\ ' 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci# 9962306a36Sopenharmony_ci# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones 10062306a36Sopenharmony_ci# 10162306a36Sopenharmony_cidef c_markups(app, docname, source): 10262306a36Sopenharmony_ci result = "" 10362306a36Sopenharmony_ci markup_func = { 10462306a36Sopenharmony_ci RE_namespace: markup_namespace, 10562306a36Sopenharmony_ci RE_expr: markup_c_expr, 10662306a36Sopenharmony_ci RE_macro: markup_macro, 10762306a36Sopenharmony_ci RE_ctype: markup_ctype, 10862306a36Sopenharmony_ci RE_ctype_refs: markup_ctype_refs, 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci lines = iter(source[0].splitlines(True)) 11262306a36Sopenharmony_ci for n in lines: 11362306a36Sopenharmony_ci match_iterators = [regex.finditer(n) for regex in markup_func] 11462306a36Sopenharmony_ci matches = sorted(chain(*match_iterators), key=lambda m: m.start()) 11562306a36Sopenharmony_ci for m in matches: 11662306a36Sopenharmony_ci n = n[:m.start()] + markup_func[m.re](m) + n[m.end():] 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci result = result + n 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci source[0] = result 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci# 12362306a36Sopenharmony_ci# Now implements support for the cdomain namespacing logic 12462306a36Sopenharmony_ci# 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cidef setup(app): 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace:: 12962306a36Sopenharmony_ci app.connect('source-read', c_markups) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (major == 1 and minor < 8): 13262306a36Sopenharmony_ci app.override_domain(CDomain) 13362306a36Sopenharmony_ci else: 13462306a36Sopenharmony_ci app.add_domain(CDomain, override=True) 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return dict( 13762306a36Sopenharmony_ci version = __version__, 13862306a36Sopenharmony_ci parallel_read_safe = True, 13962306a36Sopenharmony_ci parallel_write_safe = True 14062306a36Sopenharmony_ci ) 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciclass CObject(Base_CObject): 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci """ 14562306a36Sopenharmony_ci Description of a C language object. 14662306a36Sopenharmony_ci """ 14762306a36Sopenharmony_ci option_spec = { 14862306a36Sopenharmony_ci "name" : directives.unchanged 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci def handle_func_like_macro(self, sig, signode): 15262306a36Sopenharmony_ci u"""Handles signatures of function-like macros. 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci If the objtype is 'function' and the the signature ``sig`` is a 15562306a36Sopenharmony_ci function-like macro, the name of the macro is returned. Otherwise 15662306a36Sopenharmony_ci ``False`` is returned. """ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci global namespace 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if not self.objtype == 'function': 16162306a36Sopenharmony_ci return False 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci m = c_funcptr_sig_re.match(sig) 16462306a36Sopenharmony_ci if m is None: 16562306a36Sopenharmony_ci m = c_sig_re.match(sig) 16662306a36Sopenharmony_ci if m is None: 16762306a36Sopenharmony_ci raise ValueError('no match') 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci rettype, fullname, arglist, _const = m.groups() 17062306a36Sopenharmony_ci arglist = arglist.strip() 17162306a36Sopenharmony_ci if rettype or not arglist: 17262306a36Sopenharmony_ci return False 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup 17562306a36Sopenharmony_ci arglist = [a.strip() for a in arglist.split(",")] 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci # has the first argument a type? 17862306a36Sopenharmony_ci if len(arglist[0].split(" ")) > 1: 17962306a36Sopenharmony_ci return False 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci # This is a function-like macro, its arguments are typeless! 18262306a36Sopenharmony_ci signode += addnodes.desc_name(fullname, fullname) 18362306a36Sopenharmony_ci paramlist = addnodes.desc_parameterlist() 18462306a36Sopenharmony_ci signode += paramlist 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for argname in arglist: 18762306a36Sopenharmony_ci param = addnodes.desc_parameter('', '', noemph=True) 18862306a36Sopenharmony_ci # separate by non-breaking space in the output 18962306a36Sopenharmony_ci param += nodes.emphasis(argname, argname) 19062306a36Sopenharmony_ci paramlist += param 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if namespace: 19362306a36Sopenharmony_ci fullname = namespace + "." + fullname 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return fullname 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci def handle_signature(self, sig, signode): 19862306a36Sopenharmony_ci """Transform a C signature into RST nodes.""" 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci global namespace 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci fullname = self.handle_func_like_macro(sig, signode) 20362306a36Sopenharmony_ci if not fullname: 20462306a36Sopenharmony_ci fullname = super(CObject, self).handle_signature(sig, signode) 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if "name" in self.options: 20762306a36Sopenharmony_ci if self.objtype == 'function': 20862306a36Sopenharmony_ci fullname = self.options["name"] 20962306a36Sopenharmony_ci else: 21062306a36Sopenharmony_ci # FIXME: handle :name: value of other declaration types? 21162306a36Sopenharmony_ci pass 21262306a36Sopenharmony_ci else: 21362306a36Sopenharmony_ci if namespace: 21462306a36Sopenharmony_ci fullname = namespace + "." + fullname 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return fullname 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci def add_target_and_index(self, name, sig, signode): 21962306a36Sopenharmony_ci # for C API items we add a prefix since names are usually not qualified 22062306a36Sopenharmony_ci # by a module name and so easily clash with e.g. section titles 22162306a36Sopenharmony_ci targetname = 'c.' + name 22262306a36Sopenharmony_ci if targetname not in self.state.document.ids: 22362306a36Sopenharmony_ci signode['names'].append(targetname) 22462306a36Sopenharmony_ci signode['ids'].append(targetname) 22562306a36Sopenharmony_ci signode['first'] = (not self.names) 22662306a36Sopenharmony_ci self.state.document.note_explicit_target(signode) 22762306a36Sopenharmony_ci inv = self.env.domaindata['c']['objects'] 22862306a36Sopenharmony_ci if (name in inv and self.env.config.nitpicky): 22962306a36Sopenharmony_ci if self.objtype == 'function': 23062306a36Sopenharmony_ci if ('c:func', name) not in self.env.config.nitpick_ignore: 23162306a36Sopenharmony_ci self.state_machine.reporter.warning( 23262306a36Sopenharmony_ci 'duplicate C object description of %s, ' % name + 23362306a36Sopenharmony_ci 'other instance in ' + self.env.doc2path(inv[name][0]), 23462306a36Sopenharmony_ci line=self.lineno) 23562306a36Sopenharmony_ci inv[name] = (self.env.docname, self.objtype) 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci indextext = self.get_index_text(name) 23862306a36Sopenharmony_ci if indextext: 23962306a36Sopenharmony_ci self.indexnode['entries'].append( 24062306a36Sopenharmony_ci ('single', indextext, targetname, '', None)) 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciclass CDomain(Base_CDomain): 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci """C language domain.""" 24562306a36Sopenharmony_ci name = 'c' 24662306a36Sopenharmony_ci label = 'C' 24762306a36Sopenharmony_ci directives = { 24862306a36Sopenharmony_ci 'function': CObject, 24962306a36Sopenharmony_ci 'member': CObject, 25062306a36Sopenharmony_ci 'macro': CObject, 25162306a36Sopenharmony_ci 'type': CObject, 25262306a36Sopenharmony_ci 'var': CObject, 25362306a36Sopenharmony_ci } 254