mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Added first version of the C++ signature parser
This commit is contained in:
parent
9334d1537c
commit
5db407f4ae
@ -238,6 +238,7 @@ class Domain(object):
|
||||
|
||||
|
||||
from sphinx.domains.c import CDomain
|
||||
from sphinx.domains.cpp import CPPDomain
|
||||
from sphinx.domains.std import StandardDomain
|
||||
from sphinx.domains.python import PythonDomain
|
||||
|
||||
@ -245,4 +246,5 @@ BUILTIN_DOMAINS = {
|
||||
'std': StandardDomain,
|
||||
'py': PythonDomain,
|
||||
'c': CDomain,
|
||||
'cpp': CPPDomain
|
||||
}
|
||||
|
340
sphinx/domains/cpp.py
Normal file
340
sphinx/domains/cpp.py
Normal file
@ -0,0 +1,340 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.domains.cpp
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The C++ language domain.
|
||||
|
||||
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.locale import l_, _
|
||||
from sphinx.domains import Domain, ObjType
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.util.nodes import make_refnode
|
||||
from sphinx.util.docfields import Field, TypedField
|
||||
|
||||
|
||||
_identifier_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
|
||||
_whitespace_re = re.compile(r'\s+(?u)')
|
||||
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
|
||||
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
||||
|
||||
|
||||
class DefinitionError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class _DefExpr(object):
|
||||
|
||||
def __unicode__(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def split_owner(self):
|
||||
return None, self
|
||||
|
||||
def __str__(self):
|
||||
return unicode(self).encode('utf-8')
|
||||
|
||||
def __repr__(self):
|
||||
return '<defexpr %s>' % self
|
||||
|
||||
|
||||
class _NameDefExpr(_DefExpr):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.name)
|
||||
|
||||
|
||||
class _PathDefExpr(_DefExpr):
|
||||
|
||||
def __init__(self, parts):
|
||||
self.path = parts
|
||||
|
||||
def split_owner(self):
|
||||
if len(self.path) > 1:
|
||||
return _PathDefExpr(self.path[:-1]), self.path[-1]
|
||||
return _DefExpr.split_owner(self)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'::'.join(map(unicode, self.path))
|
||||
|
||||
|
||||
class _TemplateDefExpr(_DefExpr):
|
||||
|
||||
def __init__(self, typename, args):
|
||||
self.typename = typename
|
||||
self.args = args
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s<%s>' % (self.typename, u', '.join(map(unicode, self.args)))
|
||||
|
||||
|
||||
class _ArgumentDefExpr(_DefExpr):
|
||||
|
||||
def __init__(self, type, name, default=None):
|
||||
self.type = type
|
||||
self.name = name
|
||||
self.default = default
|
||||
|
||||
def __unicode__(self):
|
||||
return (self.type is not None and u'%s %s' % (self.type, self.name)
|
||||
or unicode(self.name)) + (self.default is not None and
|
||||
u'=%s' % self.default or u'')
|
||||
|
||||
|
||||
class _FunctionDefExpr(_DefExpr):
|
||||
|
||||
def __init__(self, name, signature, const, pure_virtual):
|
||||
self.name = name
|
||||
self.signature = signature
|
||||
self.const = const
|
||||
self.pure_virtual = pure_virtual
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s(%s)%s%s' % (
|
||||
self.name,
|
||||
u', '.join(map(unicode, self.signature)),
|
||||
self.const and u' const' or u'',
|
||||
self.pure_virtual and ' = 0' or ''
|
||||
)
|
||||
|
||||
|
||||
class DefinitionParser(object):
|
||||
|
||||
def __init__(self, definition):
|
||||
self.definition = definition.strip()
|
||||
self.pos = 0
|
||||
self.end = len(self.definition)
|
||||
self.last_match = None
|
||||
self._previous_state = (0, None)
|
||||
|
||||
def fail(self, msg):
|
||||
raise DefinitionError('Invalid definition: "%s", %s [error at %d]' %
|
||||
(self.definition, msg, self.pos))
|
||||
|
||||
def match(self, regex):
|
||||
match = regex.match(self.definition, self.pos)
|
||||
if match is not None:
|
||||
self._previous_state = (self.pos, self.last_match)
|
||||
self.pos = match.end()
|
||||
self.last_match = match
|
||||
return True
|
||||
return False
|
||||
|
||||
def backout(self):
|
||||
self.pos, self.last_match = self._previous_state
|
||||
|
||||
def skip_string(self, string):
|
||||
strlen = len(string)
|
||||
if self.definition[self.pos:self.pos + strlen] == string:
|
||||
self.pos += strlen
|
||||
return True
|
||||
return False
|
||||
|
||||
def skip_ws(self):
|
||||
return self.match(_whitespace_re) is not None
|
||||
|
||||
@property
|
||||
def eof(self):
|
||||
return self.pos >= self.end
|
||||
|
||||
@property
|
||||
def current_char(self):
|
||||
try:
|
||||
return self.definition[self.pos]
|
||||
except IndexError:
|
||||
return 'EOF'
|
||||
|
||||
@property
|
||||
def matched_text(self):
|
||||
if self.last_match is not None:
|
||||
return self.last_match.group()
|
||||
|
||||
def _parse_name(self):
|
||||
if not self.match(_identifier_re):
|
||||
self.fail('expected name')
|
||||
return _NameDefExpr(self.matched_text)
|
||||
|
||||
def _parse_type_expr(self):
|
||||
typename = self._parse_name()
|
||||
self.skip_ws()
|
||||
if not self.skip_string('<'):
|
||||
return typename
|
||||
|
||||
args = []
|
||||
while 1:
|
||||
self.skip_ws()
|
||||
if self.skip_string('>'):
|
||||
break
|
||||
if args:
|
||||
if not self.skip_string(','):
|
||||
self.fail('"," or ">" in template expected')
|
||||
self.skip_ws()
|
||||
args.append(self._parse_type(True))
|
||||
return _TemplateDefExpr(typename, args)
|
||||
|
||||
def _parse_type(self, in_template=False):
|
||||
result = []
|
||||
|
||||
# if there is a leading :: or not, we don't care because we
|
||||
# treat them exactly the same
|
||||
self.skip_string('::')
|
||||
|
||||
while 1:
|
||||
self.skip_ws()
|
||||
if (in_template and self.current_char in ',>') or \
|
||||
(result and not self.skip_string('::')) or \
|
||||
self.eof:
|
||||
break
|
||||
result.append(self._parse_type_expr())
|
||||
|
||||
if not result:
|
||||
self.fail('expected type')
|
||||
if len(result) == 1:
|
||||
return result[0]
|
||||
return _PathDefExpr(result)
|
||||
|
||||
def _parse_default_expr(self):
|
||||
self.skip_ws()
|
||||
if self.match(_string_re):
|
||||
return self.matched_text
|
||||
idx1 = self.definition.find(',', self.pos)
|
||||
idx2 = self.definition.find(')', self.pos)
|
||||
if idx1 < 0:
|
||||
idx = idx2
|
||||
elif idx2 < 0:
|
||||
idx = idx1
|
||||
else:
|
||||
idx = min(idx1, idx2)
|
||||
if idx < 0:
|
||||
self.fail('unexpected end in default expression')
|
||||
rv = self.definition[self.pos:idx]
|
||||
self.pos = idx
|
||||
return rv
|
||||
|
||||
def _parse_signature(self):
|
||||
self.skip_ws()
|
||||
if not self.skip_string('('):
|
||||
self.fail('expected parentheses for function')
|
||||
|
||||
args = []
|
||||
while 1:
|
||||
self.skip_ws()
|
||||
if self.eof:
|
||||
self.fail('missing closing parentheses')
|
||||
if self.skip_string(')'):
|
||||
break
|
||||
if args:
|
||||
if not self.skip_string(','):
|
||||
self.fail('expected comma between arguments')
|
||||
self.skip_ws()
|
||||
|
||||
argname = self._parse_type()
|
||||
argtype = default = None
|
||||
self.skip_ws()
|
||||
if self.skip_string('='):
|
||||
self.pos += 1
|
||||
default = self._parse_default_expr()
|
||||
elif self.current_char not in ',)':
|
||||
argtype = argname
|
||||
argname = self._parse_name()
|
||||
self.skip_ws()
|
||||
if self.skip_string('='):
|
||||
default = self._parse_default_expr()
|
||||
|
||||
args.append(_ArgumentDefExpr(argtype, argname, default))
|
||||
self.skip_ws()
|
||||
const = self.skip_string('const')
|
||||
if const:
|
||||
self.skip_ws()
|
||||
if self.skip_string('='):
|
||||
self.skip_ws()
|
||||
if not (self.skip_string('0') or \
|
||||
self.skip_string('NULL') or \
|
||||
self.skip_string('nullptr')):
|
||||
self.fail('pure virtual functions must be defined with '
|
||||
'either 0, NULL or nullptr, other macros are '
|
||||
'not allowed')
|
||||
pure_virtual = True
|
||||
else:
|
||||
pure_virtual = False
|
||||
return args, const, pure_virtual
|
||||
|
||||
def parse_variable(self):
|
||||
type = self._parse_type()
|
||||
name = self._parse_type()
|
||||
return type, name
|
||||
|
||||
def parse_function(self):
|
||||
rv = self._parse_type()
|
||||
name = self._parse_type()
|
||||
return rv, _FunctionDefExpr(name, *self._parse_signature())
|
||||
|
||||
|
||||
class CPPObject(ObjectDescription):
|
||||
"""Description of a C++ language object."""
|
||||
|
||||
|
||||
class CPPDomain(Domain):
|
||||
"""C++ language domain."""
|
||||
name = 'cpp'
|
||||
label = 'C++'
|
||||
object_types = {
|
||||
'class': ObjType(l_('C++ class'), 'class'),
|
||||
'function': ObjType(l_('C++ function'), 'func'),
|
||||
'member': ObjType(l_('C++ member'), 'member'),
|
||||
'macro': ObjType(l_('C++ macro'), 'macro'),
|
||||
'type': ObjType(l_('C++ type'), 'type'),
|
||||
'var': ObjType(l_('C++ variable'), 'data'),
|
||||
}
|
||||
|
||||
directives = {
|
||||
'class': CPPObject,
|
||||
'function': CPPObject,
|
||||
'member': CPPObject,
|
||||
'macro': CPPObject,
|
||||
'type': CPPObject,
|
||||
'var': CPPObject,
|
||||
}
|
||||
roles = {
|
||||
'class': XRefRole(),
|
||||
'func' : XRefRole(fix_parens=True),
|
||||
'member': XRefRole(),
|
||||
'macro': XRefRole(),
|
||||
'data': XRefRole(),
|
||||
'type': XRefRole(),
|
||||
}
|
||||
initial_data = {
|
||||
'objects': {}, # fullname -> docname, objtype
|
||||
}
|
||||
|
||||
def clear_doc(self, docname):
|
||||
for fullname, (fn, _) in self.data['objects'].items():
|
||||
if fn == docname:
|
||||
del self.data['objects'][fullname]
|
||||
|
||||
def resolve_xref(self, env, fromdocname, builder,
|
||||
typ, target, node, contnode):
|
||||
# strip pointer asterisk
|
||||
target = target.rstrip(' *')
|
||||
if target not in self.data['objects']:
|
||||
return None
|
||||
obj = self.data['objects'][target]
|
||||
return make_refnode(builder, fromdocname, obj[0], target,
|
||||
contnode, target)
|
||||
|
||||
def get_objects(self):
|
||||
for refname, (docname, type) in self.data['objects'].iteritems():
|
||||
yield (refname, type, docname, refname, 1)
|
Loading…
Reference in New Issue
Block a user