18c2ecf20Sopenharmony_ci# -*- coding: utf-8; mode: python -*-
28c2ecf20Sopenharmony_ci# pylint: disable=W0141,C0113,C0103,C0325
38c2ecf20Sopenharmony_ciu"""
48c2ecf20Sopenharmony_ci    cdomain
58c2ecf20Sopenharmony_ci    ~~~~~~~
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci    Replacement for the sphinx c-domain.
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci    :copyright:  Copyright (C) 2016  Markus Heiser
108c2ecf20Sopenharmony_ci    :license:    GPL Version 2, June 1991 see Linux/COPYING for details.
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci    List of customizations:
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci    * Moved the *duplicate C object description* warnings for function
158c2ecf20Sopenharmony_ci      declarations in the nitpicky mode. See Sphinx documentation for
168c2ecf20Sopenharmony_ci      the config values for ``nitpick`` and ``nitpick_ignore``.
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci    * Add option 'name' to the "c:function:" directive.  With option 'name' the
198c2ecf20Sopenharmony_ci      ref-name of a function can be modified. E.g.::
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci          .. c:function:: int ioctl( int fd, int request )
228c2ecf20Sopenharmony_ci             :name: VIDIOC_LOG_STATUS
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci      The func-name (e.g. ioctl) remains in the output but the ref-name changed
258c2ecf20Sopenharmony_ci      from 'ioctl' to 'VIDIOC_LOG_STATUS'. The function is referenced by::
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci          * :c:func:`VIDIOC_LOG_STATUS` or
288c2ecf20Sopenharmony_ci          * :any:`VIDIOC_LOG_STATUS` (``:any:`` needs sphinx 1.3)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci     * Handle signatures of function-like macros well. Don't try to deduce
318c2ecf20Sopenharmony_ci       arguments types of function-like macros.
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci"""
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cifrom docutils import nodes
368c2ecf20Sopenharmony_cifrom docutils.parsers.rst import directives
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciimport sphinx
398c2ecf20Sopenharmony_cifrom sphinx import addnodes
408c2ecf20Sopenharmony_cifrom sphinx.domains.c import c_funcptr_sig_re, c_sig_re
418c2ecf20Sopenharmony_cifrom sphinx.domains.c import CObject as Base_CObject
428c2ecf20Sopenharmony_cifrom sphinx.domains.c import CDomain as Base_CDomain
438c2ecf20Sopenharmony_cifrom itertools import chain
448c2ecf20Sopenharmony_ciimport re
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci__version__  = '1.1'
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci# Get Sphinx version
498c2ecf20Sopenharmony_cimajor, minor, patch = sphinx.version_info[:3]
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci# Namespace to be prepended to the full name
528c2ecf20Sopenharmony_cinamespace = None
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#
558c2ecf20Sopenharmony_ci# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags
568c2ecf20Sopenharmony_ci# - Store the namespace if ".. c:namespace::" tag is found
578c2ecf20Sopenharmony_ci#
588c2ecf20Sopenharmony_ciRE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cidef markup_namespace(match):
618c2ecf20Sopenharmony_ci    global namespace
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci    namespace = match.group(1)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci    return ""
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#
688c2ecf20Sopenharmony_ci# Handle c:macro for function-style declaration
698c2ecf20Sopenharmony_ci#
708c2ecf20Sopenharmony_ciRE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$')
718c2ecf20Sopenharmony_cidef markup_macro(match):
728c2ecf20Sopenharmony_ci    return ".. c:function:: " + match.group(1) + ' ' + match.group(2)
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#
758c2ecf20Sopenharmony_ci# Handle newer c domain tags that are evaluated as .. c:type: for
768c2ecf20Sopenharmony_ci# backward-compatibility with Sphinx < 3.0
778c2ecf20Sopenharmony_ci#
788c2ecf20Sopenharmony_ciRE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$')
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cidef markup_ctype(match):
818c2ecf20Sopenharmony_ci    return ".. c:type:: " + match.group(2)
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#
848c2ecf20Sopenharmony_ci# Handle newer c domain tags that are evaluated as :c:type: for
858c2ecf20Sopenharmony_ci# backward-compatibility with Sphinx < 3.0
868c2ecf20Sopenharmony_ci#
878c2ecf20Sopenharmony_ciRE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`')
888c2ecf20Sopenharmony_cidef markup_ctype_refs(match):
898c2ecf20Sopenharmony_ci    return ":c:type:`" + match.group(2) + '`'
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#
928c2ecf20Sopenharmony_ci# Simply convert :c:expr: and :c:texpr: into a literal block.
938c2ecf20Sopenharmony_ci#
948c2ecf20Sopenharmony_ciRE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`')
958c2ecf20Sopenharmony_cidef markup_c_expr(match):
968c2ecf20Sopenharmony_ci    return '\ ``' + match.group(2) + '``\ '
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#
998c2ecf20Sopenharmony_ci# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones
1008c2ecf20Sopenharmony_ci#
1018c2ecf20Sopenharmony_cidef c_markups(app, docname, source):
1028c2ecf20Sopenharmony_ci    result = ""
1038c2ecf20Sopenharmony_ci    markup_func = {
1048c2ecf20Sopenharmony_ci        RE_namespace: markup_namespace,
1058c2ecf20Sopenharmony_ci        RE_expr: markup_c_expr,
1068c2ecf20Sopenharmony_ci        RE_macro: markup_macro,
1078c2ecf20Sopenharmony_ci        RE_ctype: markup_ctype,
1088c2ecf20Sopenharmony_ci        RE_ctype_refs: markup_ctype_refs,
1098c2ecf20Sopenharmony_ci    }
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci    lines = iter(source[0].splitlines(True))
1128c2ecf20Sopenharmony_ci    for n in lines:
1138c2ecf20Sopenharmony_ci        match_iterators = [regex.finditer(n) for regex in markup_func]
1148c2ecf20Sopenharmony_ci        matches = sorted(chain(*match_iterators), key=lambda m: m.start())
1158c2ecf20Sopenharmony_ci        for m in matches:
1168c2ecf20Sopenharmony_ci            n = n[:m.start()] + markup_func[m.re](m) + n[m.end():]
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci        result = result + n
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci    source[0] = result
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#
1238c2ecf20Sopenharmony_ci# Now implements support for the cdomain namespacing logic
1248c2ecf20Sopenharmony_ci#
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cidef setup(app):
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci    # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace::
1298c2ecf20Sopenharmony_ci    app.connect('source-read', c_markups)
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci    if (major == 1 and minor < 8):
1328c2ecf20Sopenharmony_ci        app.override_domain(CDomain)
1338c2ecf20Sopenharmony_ci    else:
1348c2ecf20Sopenharmony_ci        app.add_domain(CDomain, override=True)
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci    return dict(
1378c2ecf20Sopenharmony_ci        version = __version__,
1388c2ecf20Sopenharmony_ci        parallel_read_safe = True,
1398c2ecf20Sopenharmony_ci        parallel_write_safe = True
1408c2ecf20Sopenharmony_ci    )
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciclass CObject(Base_CObject):
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci    """
1458c2ecf20Sopenharmony_ci    Description of a C language object.
1468c2ecf20Sopenharmony_ci    """
1478c2ecf20Sopenharmony_ci    option_spec = {
1488c2ecf20Sopenharmony_ci        "name" : directives.unchanged
1498c2ecf20Sopenharmony_ci    }
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci    def handle_func_like_macro(self, sig, signode):
1528c2ecf20Sopenharmony_ci        u"""Handles signatures of function-like macros.
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci        If the objtype is 'function' and the the signature ``sig`` is a
1558c2ecf20Sopenharmony_ci        function-like macro, the name of the macro is returned. Otherwise
1568c2ecf20Sopenharmony_ci        ``False`` is returned.  """
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci        global namespace
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci        if not self.objtype == 'function':
1618c2ecf20Sopenharmony_ci            return False
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci        m = c_funcptr_sig_re.match(sig)
1648c2ecf20Sopenharmony_ci        if m is None:
1658c2ecf20Sopenharmony_ci            m = c_sig_re.match(sig)
1668c2ecf20Sopenharmony_ci            if m is None:
1678c2ecf20Sopenharmony_ci                raise ValueError('no match')
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci        rettype, fullname, arglist, _const = m.groups()
1708c2ecf20Sopenharmony_ci        arglist = arglist.strip()
1718c2ecf20Sopenharmony_ci        if rettype or not arglist:
1728c2ecf20Sopenharmony_ci            return False
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci        arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
1758c2ecf20Sopenharmony_ci        arglist = [a.strip() for a in arglist.split(",")]
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci        # has the first argument a type?
1788c2ecf20Sopenharmony_ci        if len(arglist[0].split(" ")) > 1:
1798c2ecf20Sopenharmony_ci            return False
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci        # This is a function-like macro, it's arguments are typeless!
1828c2ecf20Sopenharmony_ci        signode  += addnodes.desc_name(fullname, fullname)
1838c2ecf20Sopenharmony_ci        paramlist = addnodes.desc_parameterlist()
1848c2ecf20Sopenharmony_ci        signode  += paramlist
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci        for argname in arglist:
1878c2ecf20Sopenharmony_ci            param = addnodes.desc_parameter('', '', noemph=True)
1888c2ecf20Sopenharmony_ci            # separate by non-breaking space in the output
1898c2ecf20Sopenharmony_ci            param += nodes.emphasis(argname, argname)
1908c2ecf20Sopenharmony_ci            paramlist += param
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci        if namespace:
1938c2ecf20Sopenharmony_ci            fullname = namespace + "." + fullname
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci        return fullname
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci    def handle_signature(self, sig, signode):
1988c2ecf20Sopenharmony_ci        """Transform a C signature into RST nodes."""
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci        global namespace
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci        fullname = self.handle_func_like_macro(sig, signode)
2038c2ecf20Sopenharmony_ci        if not fullname:
2048c2ecf20Sopenharmony_ci            fullname = super(CObject, self).handle_signature(sig, signode)
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci        if "name" in self.options:
2078c2ecf20Sopenharmony_ci            if self.objtype == 'function':
2088c2ecf20Sopenharmony_ci                fullname = self.options["name"]
2098c2ecf20Sopenharmony_ci            else:
2108c2ecf20Sopenharmony_ci                # FIXME: handle :name: value of other declaration types?
2118c2ecf20Sopenharmony_ci                pass
2128c2ecf20Sopenharmony_ci        else:
2138c2ecf20Sopenharmony_ci            if namespace:
2148c2ecf20Sopenharmony_ci                fullname = namespace + "." + fullname
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci        return fullname
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci    def add_target_and_index(self, name, sig, signode):
2198c2ecf20Sopenharmony_ci        # for C API items we add a prefix since names are usually not qualified
2208c2ecf20Sopenharmony_ci        # by a module name and so easily clash with e.g. section titles
2218c2ecf20Sopenharmony_ci        targetname = 'c.' + name
2228c2ecf20Sopenharmony_ci        if targetname not in self.state.document.ids:
2238c2ecf20Sopenharmony_ci            signode['names'].append(targetname)
2248c2ecf20Sopenharmony_ci            signode['ids'].append(targetname)
2258c2ecf20Sopenharmony_ci            signode['first'] = (not self.names)
2268c2ecf20Sopenharmony_ci            self.state.document.note_explicit_target(signode)
2278c2ecf20Sopenharmony_ci            inv = self.env.domaindata['c']['objects']
2288c2ecf20Sopenharmony_ci            if (name in inv and self.env.config.nitpicky):
2298c2ecf20Sopenharmony_ci                if self.objtype == 'function':
2308c2ecf20Sopenharmony_ci                    if ('c:func', name) not in self.env.config.nitpick_ignore:
2318c2ecf20Sopenharmony_ci                        self.state_machine.reporter.warning(
2328c2ecf20Sopenharmony_ci                            'duplicate C object description of %s, ' % name +
2338c2ecf20Sopenharmony_ci                            'other instance in ' + self.env.doc2path(inv[name][0]),
2348c2ecf20Sopenharmony_ci                            line=self.lineno)
2358c2ecf20Sopenharmony_ci            inv[name] = (self.env.docname, self.objtype)
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci        indextext = self.get_index_text(name)
2388c2ecf20Sopenharmony_ci        if indextext:
2398c2ecf20Sopenharmony_ci            if major == 1 and minor < 4:
2408c2ecf20Sopenharmony_ci                # indexnode's tuple changed in 1.4
2418c2ecf20Sopenharmony_ci                # https://github.com/sphinx-doc/sphinx/commit/e6a5a3a92e938fcd75866b4227db9e0524d58f7c
2428c2ecf20Sopenharmony_ci                self.indexnode['entries'].append(
2438c2ecf20Sopenharmony_ci                    ('single', indextext, targetname, ''))
2448c2ecf20Sopenharmony_ci            else:
2458c2ecf20Sopenharmony_ci                self.indexnode['entries'].append(
2468c2ecf20Sopenharmony_ci                    ('single', indextext, targetname, '', None))
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ciclass CDomain(Base_CDomain):
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci    """C language domain."""
2518c2ecf20Sopenharmony_ci    name = 'c'
2528c2ecf20Sopenharmony_ci    label = 'C'
2538c2ecf20Sopenharmony_ci    directives = {
2548c2ecf20Sopenharmony_ci        'function': CObject,
2558c2ecf20Sopenharmony_ci        'member':   CObject,
2568c2ecf20Sopenharmony_ci        'macro':    CObject,
2578c2ecf20Sopenharmony_ci        'type':     CObject,
2588c2ecf20Sopenharmony_ci        'var':      CObject,
2598c2ecf20Sopenharmony_ci    }
260