summaryrefslogtreecommitdiff
path: root/sphinx/domains/cpp.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/domains/cpp.py')
-rw-r--r--sphinx/domains/cpp.py2299
1 files changed, 1439 insertions, 860 deletions
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index cb64a60d..d4455a22 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -7,11 +7,143 @@
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
+
+ See http://www.nongnu.org/hcb/ for the grammar.
+ See http://mentorembedded.github.io/cxx-abi/abi.html#mangling for the
+ inspiration for the id generation.
+
+ common grammar things:
+ simple-declaration
+ -> attribute-specifier-seq[opt] decl-specifier-seq[opt]
+ init-declarator-list[opt] ;
+ # Drop the semi-colon. For now: drop the attributes (TODO).
+ # Use at most 1 init-declerator.
+ -> decl-specifier-seq init-declerator
+ -> decl-specifier-seq declerator initializer
+
+ decl-specifier ->
+ storage-class-specifier -> "static" (only for member_object and
+ function_object)
+ | type-specifier -> trailing-type-specifier
+ | function-specifier -> "inline" | "virtual" | "explicit" (only
+ for function_object)
+ | "friend" (only for function_object)
+ | "constexpr" (only for member_object and function_object)
+ trailing-type-specifier ->
+ simple-type-specifier
+ | elaborated-type-specifier
+ | typename-specifier
+ | cv-qualifier -> "const" | "volatile"
+ stricter grammar for decl-specifier-seq (with everything, each object
+ uses a subset):
+ visibility storage-class-specifier function-specifier "friend"
+ "constexpr" "volatile" "const" trailing-type-specifier
+ # where trailing-type-specifier can no be cv-qualifier
+ # Inside e.g., template paramters a strict subset is used
+ # (see type-specifier-seq)
+ trailing-type-specifier ->
+ simple-type-specifier ->
+ ::[opt] nested-name-specifier[opt] type-name
+ | ::[opt] nested-name-specifier "template" simple-template-id
+ | "char" | "bool" | ect.
+ | decltype-specifier
+ | elaborated-type-specifier ->
+ class-key attribute-specifier-seq[opt] ::[opt]
+ nested-name-specifier[opt] identifier
+ | class-key ::[opt] nested-name-specifier[opt] template[opt]
+ simple-template-id
+ | "enum" ::[opt] nested-name-specifier[opt] identifier
+ | typename-specifier ->
+ "typename" ::[opt] nested-name-specifier identifier
+ | "typename" ::[opt] nested-name-specifier template[opt]
+ simple-template-id
+ class-key -> "class" | "struct" | "union"
+ type-name ->* identifier | simple-template-id
+ # ignoring attributes and decltype, and then some left-factoring
+ trailing-type-specifier ->
+ rest-of-trailing
+ ("class" | "struct" | "union" | "typename") rest-of-trailing
+ build-in -> "char" | "bool" | ect.
+ decltype-specifier
+ rest-of-trailing -> (with some simplification)
+ "::"[opt] list-of-elements-separated-by-::
+ element ->
+ "template"[opt] identifier ("<" template-argument-list ">")[opt]
+ template-argument-list ->
+ template-argument "..."[opt]
+ | template-argument-list "," template-argument "..."[opt]
+ template-argument ->
+ constant-expression
+ | type-specifier-seq abstract-declerator
+ | id-expression
+
+
+ declerator ->
+ ptr-declerator
+ | noptr-declarator parameters-and-qualifiers trailing-return-type
+ (TODO: for now we don't support it)
+ ptr-declerator ->
+ noptr-declerator
+ | ptr-operator ptr-declarator
+ noptr-declerator ->
+ declarator-id attribute-specifier-seq[opt] ->
+ "..."[opt] id-expression
+ | rest-of-trailing
+ | noptr-declerator parameters-and-qualifiers
+ | noptr-declarator "[" constant-expression[opt] "]"
+ attribute-specifier-seq[opt]
+ | "(" ptr-declarator ")" # TODO: not implemented yet
+ # function_object must use a parameters-and-qualifiers, the others may
+ # use it (e.g., function poitners)
+ parameters-and-qualifiers ->
+ "(" parameter-clause ")" attribute-specifier-seq[opt]
+ cv-qualifier-seq[opt] ref-qualifier[opt]
+ exception-specification[opt]
+ ref-qualifier -> "&" | "&&"
+ exception-specification ->
+ "noexcept" ("(" constant-expression ")")[opt]
+ "throw" ("(" type-id-list ")")[opt]
+ # TODO: we don't implement attributes
+ # member functions can have initializers, but we fold them into here
+ memberFunctionInit -> "=" "0"
+ # (note: only "0" is allowed as the value, according to the standard,
+ # right?)
+
+
+ We additionally add the possibility for specifying the visibility as the
+ first thing.
+
+ type_object:
+ goal:
+ either a single type (e.g., "MyClass:Something_T" or a typedef-like
+ thing (e.g. "Something Something_T" or "int I_arr[]"
+ grammar, single type: based on a type in a function parameter, but
+ without a name:
+ parameter-declaration
+ -> attribute-specifier-seq[opt] decl-specifier-seq
+ abstract-declarator[opt]
+ # Drop the attributes
+ -> decl-specifier-seq abstract-declarator[opt]
+ grammar, typedef-like: no initilizer
+ decl-specifier-seq declerator
+
+
+ member_object:
+ goal: as a type_object which must have a declerator, and optionally
+ with a initializer
+ grammar:
+ decl-specifier-seq declerator initializer
+
+ function_object:
+ goal: a function declaration, TODO: what about templates? for now: skip
+ grammar: no initializer
+ decl-specifier-seq declerator
"""
import re
from copy import deepcopy
+from six import iteritems, text_type
from docutils import nodes
from sphinx import addnodes
@@ -21,6 +153,7 @@ from sphinx.domains import Domain, ObjType
from sphinx.directives import ObjectDescription
from sphinx.util.nodes import make_refnode
from sphinx.util.compat import Directive
+from sphinx.util.pycompat import UnicodeMixin
from sphinx.util.docfields import Field, GroupedField
@@ -40,88 +173,112 @@ _operator_re = re.compile(r'''(?x)
| [!<>=/*%+|&^~-]=?
''')
-_id_shortwords = {
- 'char': 'c',
- 'signed char': 'c',
- 'unsigned char': 'C',
- 'int': 'i',
- 'signed int': 'i',
- 'unsigned int': 'U',
- 'long': 'l',
- 'signed long': 'l',
- 'unsigned long': 'L',
- 'bool': 'b',
- 'size_t': 's',
- 'std::string': 'ss',
- 'std::ostream': 'os',
- 'std::istream': 'is',
- 'std::iostream': 'ios',
- 'std::vector': 'v',
- 'std::map': 'm',
- 'operator[]': 'subscript-operator',
- 'operator()': 'call-operator',
- 'operator!': 'not-operator',
- 'operator<': 'lt-operator',
- 'operator<=': 'lte-operator',
- 'operator>': 'gt-operator',
- 'operator>=': 'gte-operator',
- 'operator=': 'assign-operator',
- 'operator/': 'div-operator',
- 'operator*': 'mul-operator',
- 'operator%': 'mod-operator',
- 'operator+': 'add-operator',
- 'operator-': 'sub-operator',
- 'operator|': 'or-operator',
- 'operator&': 'and-operator',
- 'operator^': 'xor-operator',
- 'operator&&': 'sand-operator',
- 'operator||': 'sor-operator',
- 'operator==': 'eq-operator',
- 'operator!=': 'neq-operator',
- 'operator<<': 'lshift-operator',
- 'operator>>': 'rshift-operator',
- 'operator-=': 'sub-assign-operator',
- 'operator+=': 'add-assign-operator',
- 'operator*-': 'mul-assign-operator',
- 'operator/=': 'div-assign-operator',
- 'operator%=': 'mod-assign-operator',
- 'operator&=': 'and-assign-operator',
- 'operator|=': 'or-assign-operator',
- 'operator<<=': 'lshift-assign-operator',
- 'operator>>=': 'rshift-assign-operator',
- 'operator^=': 'xor-assign-operator',
- 'operator,': 'comma-operator',
- 'operator->': 'pointer-operator',
- 'operator->*': 'pointer-by-pointer-operator',
- 'operator~': 'inv-operator',
- 'operator++': 'inc-operator',
- 'operator--': 'dec-operator',
- 'operator new': 'new-operator',
- 'operator new[]': 'new-array-operator',
- 'operator delete': 'delete-operator',
- 'operator delete[]': 'delete-array-operator'
+_id_prefix = '_CPP'
+_id_fundamental = {
+ # not all of these are actually parsed as fundamental types, TODO: do that
+ 'void': 'v',
+ 'bool': 'b',
+ 'char': 'c',
+ 'signed char': 'a',
+ 'unsigned char': 'h',
+ 'wchar_t': 'w',
+ 'char32_t': 'Di',
+ 'char16_t': 'Ds',
+ 'short': 's',
+ 'short int': 's',
+ 'signed short': 's',
+ 'signed short int': 's',
+ 'unsigned short': 't',
+ 'unsigned short int': 't',
+ 'int': 'i',
+ 'signed': 'i',
+ 'signed int': 'i',
+ 'unsigned': 'j',
+ 'unsigned int': 'j',
+ 'long': 'l',
+ 'long int': 'l',
+ 'signed long': 'l',
+ 'signed long int': 'l',
+ 'unsigned long': 'm',
+ 'unsigned long int': 'm',
+ 'long long': 'x',
+ 'long long int': 'x',
+ 'signed long long': 'x',
+ 'signed long long int': 'x',
+ 'unsigned long long': 'y',
+ 'unsigned long long int': 'y',
+ 'float': 'f',
+ 'double': 'd',
+ 'long double': 'e',
+ 'auto': 'Da',
+ 'decltype(auto)': 'Dc',
+ 'std::nullptr_t': 'Dn'
+}
+_id_operator = {
+ 'new': 'nw',
+ 'new[]': 'na',
+ 'delete': 'dl',
+ 'delete[]': 'da',
+ # the arguments will make the difference between unary and binary
+ # '+(unary)' : 'ps',
+ # '-(unary)' : 'ng',
+ # '&(unary)' : 'ad',
+ # '*(unary)' : 'de',
+ '~': 'co',
+ '+': 'pl',
+ '-': 'mi',
+ '*': 'ml',
+ '/': 'dv',
+ '%': 'rm',
+ '&': 'an',
+ '|': 'or',
+ '^': 'eo',
+ '=': 'aS',
+ '+=': 'pL',
+ '-=': 'mI',
+ '*=': 'mL',
+ '/=': 'dV',
+ '%=': 'rM',
+ '&=': 'aN',
+ '|=': 'oR',
+ '^=': 'eO',
+ '<<': 'ls',
+ '>>': 'rs',
+ '<<=': 'lS',
+ '>>=': 'rS',
+ '==': 'eq',
+ '!=': 'ne',
+ '<': 'lt',
+ '>': 'gt',
+ '<=': 'le',
+ '>=': 'ge',
+ '!': 'nt',
+ '&&': 'aa',
+ '||': 'oo',
+ '++': 'pp',
+ '--': 'mm',
+ ',': 'cm',
+ '->*': 'pm',
+ '->': 'pt',
+ '()': 'cl',
+ '[]': 'ix'
}
-class DefinitionError(Exception):
-
+class DefinitionError(UnicodeMixin, Exception):
def __init__(self, description):
self.description = description
- def __str__(self):
- return unicode(self).encode('utf-8')
-
def __unicode__(self):
return self.description
-class DefExpr(object):
-
+class ASTBase(UnicodeMixin):
def __eq__(self, other):
if type(self) is not type(other):
return False
try:
- for key, value in self.__dict__.iteritems():
+ for key, value in iteritems(self.__dict__):
if value != getattr(other, key):
return False
except AttributeError:
@@ -139,7 +296,7 @@ class DefExpr(object):
def get_id(self):
"""Return the id for the node."""
- return u''
+ raise NotImplementedError(repr(self))
def get_name(self):
"""Return the name.
@@ -147,402 +304,748 @@ class DefExpr(object):
Returns either `None` or a node with a name you might call
:meth:`split_owner` on.
"""
- return None
+ raise NotImplementedError(repr(self))
- def split_owner(self):
- """Nodes returned by :meth:`get_name` can split off their
- owning parent. This function returns the owner and the
- name as a tuple of two items. If a node does not support
- it, it returns None as owner and self as name.
- """
- return None, self
-
- def prefix(self, prefix):
+ def prefix_nested_name(self, prefix):
"""Prefix a name node (a node returned by :meth:`get_name`)."""
- raise NotImplementedError()
-
- def __str__(self):
- return unicode(self).encode('utf-8')
+ raise NotImplementedError(repr(self))
def __unicode__(self):
- raise NotImplementedError()
+ raise NotImplementedError(repr(self))
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, self)
-class PrimaryDefExpr(DefExpr):
-
- def get_name(self):
- return self
-
- def prefix(self, prefix):
- if isinstance(prefix, PathDefExpr):
- prefix = prefix.clone()
- prefix.path.append(self)
- return prefix
- return PathDefExpr([prefix, self])
+def _verify_description_mode(mode):
+ if mode not in ('lastIsName', 'noneIsName', 'markType', 'param'):
+ raise Exception("Description mode '%s' is invalid." % mode)
-class NameDefExpr(PrimaryDefExpr):
-
- def __init__(self, name):
- self.name = name
+class ASTOperatorBuildIn(ASTBase):
+ def __init__(self, op):
+ self.op = op
def get_id(self):
- name = _id_shortwords.get(self.name)
- if name is not None:
- return name
- return self.name.replace(u' ', u'-')
+ if self.op not in _id_operator:
+ raise Exception('Internal error: Build-in operator "%s" can not '
+ 'be mapped to an id.' % self.op)
+ return _id_operator[self.op]
def __unicode__(self):
- return unicode(self.name)
-
-
-class PathDefExpr(PrimaryDefExpr):
+ if self.op in ('new', 'new[]', 'delete', 'delete[]'):
+ return u'operator ' + self.op
+ else:
+ return u'operator' + self.op
- def __init__(self, parts):
- self.path = parts
+ def get_name_no_template(self):
+ return text_type(self)
- def get_id(self):
- rv = u'::'.join(x.get_id() for x in self.path)
- return _id_shortwords.get(rv, rv)
+ def describe_signature(self, signode, mode, env, prefix):
+ _verify_description_mode(mode)
+ identifier = text_type(self)
+ if mode == 'lastIsName':
+ signode += addnodes.desc_name(identifier, identifier)
+ else:
+ signode += addnodes.desc_addname(identifier, identifier)
- def split_owner(self):
- if len(self.path) > 1:
- return PathDefExpr(self.path[:-1]), self.path[-1]
- return None, self
- def prefix(self, prefix):
- if isinstance(prefix, PathDefExpr):
- prefix = prefix.clone()
- prefix.path.extend(self.path)
- return prefix
- return PathDefExpr([prefix] + self.path)
+class ASTOperatorType(ASTBase):
+ def __init__(self, type):
+ self.type = type
def __unicode__(self):
- return u'::'.join(map(unicode, self.path))
+ return u''.join(['operator ', text_type(self.type)])
+ def get_id(self):
+ return u'cv' + self.type.get_id()
-class ArrayTypeSuffixDefExpr(object):
+ def get_name_no_template(self):
+ return text_type(self)
+
+ def describe_signature(self, signode, mode, env, prefix):
+ _verify_description_mode(mode)
+ identifier = text_type(self)
+ if mode == 'lastIsName':
+ signode += addnodes.desc_name(identifier, identifier)
+ else:
+ signode += addnodes.desc_addname(identifier, identifier)
- def __init__(self, size_hint=None):
- self.size_hint = size_hint
- def get_id_suffix(self):
- return 'A'
+class ASTTemplateArgConstant(ASTBase):
+ def __init__(self, value):
+ self.value = value
def __unicode__(self):
- return u'[%s]' % (
- self.size_hint is not None and unicode(self.size_hint) or u'',
- )
+ return text_type(self.value)
+ def get_id(self):
+ # TODO: doing this properly needs parsing of expressions, let's just
+ # juse it verbatim for now
+ return u'X' + text_type(self) + u'E'
-class TemplateDefExpr(PrimaryDefExpr):
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ signode += nodes.Text(text_type(self))
- def __init__(self, typename, args):
- self.typename = typename
- self.args = args
- def split_owner(self):
- owner, typename = self.typename.split_owner()
- return owner, TemplateDefExpr(typename, self.args)
+class ASTNestedNameElement(ASTBase):
+ def __init__(self, identifier, templateArgs):
+ self.identifier = identifier
+ self.templateArgs = templateArgs
def get_id(self):
- return u'%s:%s:' % (self.typename.get_id(),
- u'.'.join(x.get_id() for x in self.args))
+ res = []
+ if self.identifier == "std":
+ res.append(u'St')
+ else:
+ res.append(text_type(len(self.identifier)))
+ res.append(self.identifier)
+ if self.templateArgs:
+ res.append('I')
+ for a in self.templateArgs:
+ res.append(a.get_id())
+ res.append('E')
+ return u''.join(res)
def __unicode__(self):
- return u'%s<%s>' % (self.typename, u', '.join(map(unicode, self.args)))
-
-
-class ConstantTemplateArgExpr(PrimaryDefExpr):
+ res = []
+ res.append(self.identifier)
+ if self.templateArgs:
+ res.append('<')
+ first = True
+ for a in self.templateArgs:
+ if not first:
+ res.append(', ')
+ first = False
+ res.append(text_type(a))
+ res.append('>')
+ return u''.join(res)
+
+ def get_name_no_template(self):
+ return text_type(self.identifier)
+
+ def describe_signature(self, signode, mode, env, prefix):
+ _verify_description_mode(mode)
+ if mode == 'markType':
+ targetText = prefix + text_type(self)
+ pnode = addnodes.pending_xref(
+ '', refdomain='cpp', reftype='type',
+ reftarget=targetText, modname=None, classname=None)
+ if env: # during testing we don't have an env, do we?
+ pnode['cpp:parent'] = env.ref_context.get('cpp:parent')
+ pnode += nodes.Text(text_type(self.identifier))
+ signode += pnode
+ elif mode == 'lastIsName':
+ name = text_type(self.identifier)
+ signode += addnodes.desc_name(name, name)
+ else:
+ raise Exception('Unknown description mode: %s' % mode)
+ if self.templateArgs:
+ signode += nodes.Text('<')
+ first = True
+ for a in self.templateArgs:
+ if not first:
+ signode += nodes.Text(', ')
+ first = False
+ a.describe_signature(signode, 'markType', env)
+ signode += nodes.Text('>')
+
+
+class ASTNestedName(ASTBase):
+ def __init__(self, names):
+ """Use an empty string as the first name if it should start with '::'
+ """
+ self.names = names
- def __init__(self, arg):
- self.arg = arg
+ @property
+ def name(self):
+ return self
def get_id(self):
- return self.arg.replace(u' ', u'-')
+ res = []
+ if len(self.names) > 1:
+ res.append('N')
+ for n in self.names:
+ res.append(n.get_id())
+ if len(self.names) > 1:
+ res.append('E')
+ return u''.join(res)
+
+ def get_name_no_last_template(self):
+ res = u'::'.join([text_type(n) for n in self.names[:-1]])
+ if len(self.names) > 1:
+ res += '::'
+ res += self.names[-1].get_name_no_template()
+ return res
+
+ def prefix_nested_name(self, prefix):
+ if self.names[0] == '':
+ return self # it's defined at global namespace, don't tuch it
+ assert isinstance(prefix, ASTNestedName)
+ names = prefix.names[:]
+ names.extend(self.names)
+ return ASTNestedName(names)
def __unicode__(self):
- return unicode(self.arg)
+ return u'::'.join([text_type(n) for n in self.names])
+
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ if mode == 'lastIsName':
+ addname = u'::'.join([text_type(n) for n in self.names[:-1]])
+ if len(self.names) > 1:
+ addname += u'::'
+ name = text_type(self.names[-1])
+ signode += addnodes.desc_addname(addname, addname)
+ self.names[-1].describe_signature(signode, mode, env, '')
+ elif mode == 'noneIsName':
+ name = text_type(self)
+ signode += nodes.Text(name)
+ elif mode == 'param':
+ name = text_type(self)
+ signode += nodes.emphasis(name, name)
+ elif mode == 'markType':
+ # each element should be a pending xref targeting the complete
+ # prefix. however, only the identifier part should be a link, such
+ # that template args can be a link as well.
+ prefix = ''
+ first = True
+ for name in self.names:
+ if not first:
+ signode += nodes.Text('::')
+ prefix += '::'
+ first = False
+ if name != '':
+ name.describe_signature(signode, mode, env, prefix)
+ prefix += text_type(name)
+ else:
+ raise Exception('Unknown description mode: %s' % mode)
+
+class ASTTrailingTypeSpecFundamental(ASTBase):
+ def __init__(self, name):
+ self.name = name
-class WrappingDefExpr(DefExpr):
+ def __unicode__(self):
+ return self.name
- def __init__(self, typename):
- self.typename = typename
+ def get_id(self):
+ if self.name not in _id_fundamental:
+ raise Exception(
+ 'Semi-internal error: Fundamental type "%s" can not be mapped '
+ 'to an id. Is it a true fundamental type? If not so, the '
+ 'parser should have rejected it.' % self.name)
+ return _id_fundamental[self.name]
- def get_name(self):
- return self.typename.get_name()
+ def describe_signature(self, signode, mode, env):
+ signode += nodes.Text(text_type(self.name))
-class ModifierDefExpr(WrappingDefExpr):
+class ASTTrailingTypeSpecName(ASTBase):
+ def __init__(self, prefix, nestedName):
+ self.prefix = prefix
+ self.nestedName = nestedName
- def __init__(self, typename, modifiers):
- WrappingDefExpr.__init__(self, typename)
- self.modifiers = modifiers
+ @property
+ def name(self):
+ return self.nestedName
def get_id(self):
- pieces = [_id_shortwords.get(unicode(x), unicode(x))
- for x in self.modifiers]
- pieces.append(self.typename.get_id())
- return u'-'.join(pieces)
+ return self.nestedName.get_id()
def __unicode__(self):
- return u' '.join(map(unicode, list(self.modifiers) + [self.typename]))
+ res = []
+ if self.prefix:
+ res.append(self.prefix)
+ res.append(' ')
+ res.append(text_type(self.nestedName))
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env):
+ if self.prefix:
+ signode += addnodes.desc_annotation(self.prefix, self.prefix)
+ signode += nodes.Text(' ')
+ self.nestedName.describe_signature(signode, mode, env)
-class PtrDefExpr(WrappingDefExpr):
+class ASTFunctinoParameter(ASTBase):
+ def __init__(self, arg, ellipsis=False):
+ self.arg = arg
+ self.ellipsis = ellipsis
def get_id(self):
- return self.typename.get_id() + u'P'
+ if self.ellipsis:
+ return 'z'
+ else:
+ return self.arg.get_id()
def __unicode__(self):
- return u'%s*' % self.typename
+ if self.ellipsis:
+ return '...'
+ else:
+ return text_type(self.arg)
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ if self.ellipsis:
+ signode += nodes.Text('...')
+ else:
+ self.arg.describe_signature(signode, mode, env)
-class LValRefDefExpr(WrappingDefExpr):
- def get_id(self):
- return self.typename.get_id() + u'R'
+class ASTParametersQualifiers(ASTBase):
+ def __init__(self, args, volatile, const, refQual, exceptionSpec, override,
+ final, initializer):
+ self.args = args
+ self.volatile = volatile
+ self.const = const
+ self.refQual = refQual
+ self.exceptionSpec = exceptionSpec
+ self.override = override
+ self.final = final
+ self.initializer = initializer
+
+ def get_modifiers_id(self):
+ res = []
+ if self.volatile:
+ res.append('V')
+ if self.const:
+ res.append('K')
+ if self.refQual == '&&':
+ res.append('O')
+ elif self.refQual == '&':
+ res.append('R')
+ return u''.join(res)
+
+ def get_param_id(self):
+ if len(self.args) == 0:
+ return 'v'
+ else:
+ return u''.join(a.get_id() for a in self.args)
def __unicode__(self):
- return u'%s&' % self.typename
+ res = []
+ res.append('(')
+ first = True
+ for a in self.args:
+ if not first:
+ res.append(', ')
+ first = False
+ res.append(text_type(a))
+ res.append(')')
+ if self.volatile:
+ res.append(' volatile')
+ if self.const:
+ res.append(' const')
+ if self.refQual:
+ res.append(' ')
+ res.append(self.refQual)
+ if self.exceptionSpec:
+ res.append(' ')
+ res.append(text_type(self.exceptionSpec))
+ if self.final:
+ res.append(' final')
+ if self.override:
+ res.append(' override')
+ if self.initializer:
+ res.append(' = ')
+ res.append(self.initializer)
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ paramlist = addnodes.desc_parameterlist()
+ for arg in self.args:
+ param = addnodes.desc_parameter('', '', noemph=True)
+ if mode == 'lastIsName': # i.e., outer-function params
+ arg.describe_signature(param, 'param', env)
+ else:
+ arg.describe_signature(param, 'markType', env)
+ paramlist += param
+ signode += paramlist
+
+ def _add_anno(signode, text):
+ signode += nodes.Text(' ')
+ signode += addnodes.desc_annotation(text, text)
+
+ def _add_text(signode, text):
+ signode += nodes.Text(' ' + text)
+
+ if self.volatile:
+ _add_anno(signode, 'volatile')
+ if self.const:
+ _add_anno(signode, 'const')
+ if self.refQual:
+ _add_text(signode, self.refQual)
+ if self.exceptionSpec:
+ _add_anno(signode, text_type(self.exceptionSpec))
+ if self.final:
+ _add_anno(signode, 'final')
+ if self.override:
+ _add_anno(signode, 'override')
+ if self.initializer:
+ _add_text(signode, '= ' + text_type(self.initializer))
-class RValRefDefExpr(WrappingDefExpr):
+class ASTDeclSpecs(ASTBase):
+ def __init__(self, outer, visibility, storage, inline, virtual, explicit,
+ constexpr, volatile, const, trailing):
+ self.outer = outer
+ self.visibility = visibility
+ self.storage = storage
+ self.inline = inline
+ self.virtual = virtual
+ self.explicit = explicit
+ self.constexpr = constexpr
+ self.volatile = volatile
+ self.const = const
+ self.trailingTypeSpec = trailing
+
+ @property
+ def name(self):
+ return self.trailingTypeSpec.name
def get_id(self):
- return self.typename.get_id() + u'RR'
+ res = []
+ if self.volatile:
+ res.append('V')
+ if self.const:
+ res.append('K')
+ res.append(self.trailingTypeSpec.get_id())
+ return u''.join(res)
+
+ def _print_visibility(self):
+ return (self.visibility and
+ not (
+ self.outer in ('type', 'member', 'function') and
+ self.visibility == 'public'))
def __unicode__(self):
- return u'%s&&' % self.typename
+ res = []
+ if self._print_visibility():
+ res.append(self.visibility)
+ if self.storage:
+ res.append(self.storage)
+ if self.inline:
+ res.append('inline')
+ if self.virtual:
+ res.append('virtual')
+ if self.explicit:
+ res.append('explicit')
+ if self.constexpr:
+ res.append('constexpr')
+ if self.volatile:
+ res.append('volatile')
+ if self.const:
+ res.append('const')
+ if self.trailingTypeSpec:
+ res.append(text_type(self.trailingTypeSpec))
+ return u' '.join(res)
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ modifiers = []
-class ConstDefExpr(WrappingDefExpr):
+ def _add(modifiers, text):
+ if len(modifiers) > 0:
+ modifiers.append(nodes.Text(' '))
+ modifiers.append(addnodes.desc_annotation(text, text))
+
+ if self._print_visibility():
+ _add(modifiers, self.visibility)
+ if self.storage:
+ _add(modifiers, self.storage)
+ if self.inline:
+ _add(modifiers, 'inline')
+ if self.virtual:
+ _add(modifiers, 'virtual')
+ if self.explicit:
+ _add(modifiers, 'explicit')
+ if self.constexpr:
+ _add(modifiers, 'constexpr')
+ if self.volatile:
+ _add(modifiers, 'volatile')
+ if self.const:
+ _add(modifiers, 'const')
+ for m in modifiers:
+ signode += m
+ if self.trailingTypeSpec:
+ if len(modifiers) > 0:
+ signode += nodes.Text(' ')
+ self.trailingTypeSpec.describe_signature(signode, mode, env)
- def __init__(self, typename, prefix=False):
- WrappingDefExpr.__init__(self, typename)
- self.prefix = prefix
- def get_id(self):
- return self.typename.get_id() + u'C'
+class ASTPtrOpPtr(ASTBase):
+ def __init__(self, volatile, const):
+ self.volatile = volatile
+ self.const = const
def __unicode__(self):
- return (self.prefix and u'const %s' or u'%s const') % self.typename
+ res = ['*']
+ if self.volatile:
+ res.append('volatile ')
+ if self.const:
+ res.append('const ')
+ return u''.join(res)
+ def get_id(self):
+ res = ['P']
+ if self.volatile:
+ res.append('V')
+ if self.const:
+ res.append('C')
+ return u''.join(res)
-class CastOpDefExpr(PrimaryDefExpr):
- def __init__(self, typename):
- self.typename = typename
+class ASTPtrOpRef(ASTBase):
+ def __unicode__(self):
+ return '&'
def get_id(self):
- return u'castto-%s-operator' % self.typename.get_id()
+ return 'R'
+
+class ASTPtrOpParamPack(ASTBase):
def __unicode__(self):
- return u'operator %s' % self.typename
+ return '...'
+ def get_id(self):
+ return 'Dp'
-class ArgumentDefExpr(DefExpr):
- def __init__(self, type, name, type_suffixes, default=None):
- self.name = name
- self.type = type
- self.type_suffixes = type_suffixes
- self.default = default
+class ASTArray(ASTBase):
+ def __init__(self, size):
+ self.size = size
- def get_name(self):
- return self.name.get_name()
+ def __unicode__(self):
+ return u''.join(['[', text_type(self.size), ']'])
def get_id(self):
- buf = []
- buf.append(self.type and self.type.get_id() or 'X')
- for suffix in self.type_suffixes:
- buf.append(suffix.get_id_suffix())
- return u''.join(buf)
+ # TODO: this should maybe be done differently
+ return u'A' + text_type(self.size) + u'_'
- def __unicode__(self):
- buf = [(u'%s %s' % (self.type or u'', self.name or u'')).strip()]
- if self.default is not None:
- buf.append('=%s' % self.default)
- for suffix in self.type_suffixes:
- buf.append(unicode(suffix))
- return u''.join(buf)
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ signode += nodes.Text(text_type(self))
-class NamedDefExpr(DefExpr):
+class ASTDeclerator(ASTBase):
+ def __init__(self, ptrOps, declId, suffixOps):
+ self.ptrOps = ptrOps
+ self.declId = declId
+ self.suffixOps = suffixOps
- def __init__(self, name, visibility, static):
- self.name = name
- self.visibility = visibility
- self.static = static
+ @property
+ def name(self):
+ return self.declId
+
+ def get_modifiers_id(self): # only the modifiers for a function, e.g.,
+ # cv-qualifiers
+ for op in self.suffixOps:
+ if isinstance(op, ASTParametersQualifiers):
+ return op.get_modifiers_id()
+ raise Exception(
+ "This should only be called on a function: %s" % text_type(self))
+
+ def get_param_id(self): # only the parameters (if any)
+ for op in self.suffixOps:
+ if isinstance(op, ASTParametersQualifiers):
+ return op.get_param_id()
+ return ''
- def get_name(self):
- return self.name.get_name()
-
- def get_modifiers(self, visibility='public'):
- rv = []
- if self.visibility != visibility:
- rv.append(self.visibility)
- if self.static:
- rv.append(u'static')
- return rv
+ def get_ptr_suffix_id(self): # only the ptr ops and array specifiers
+ return u''.join(
+ a.get_id()
+ for a in self.ptrOps + self.suffixOps
+ if not isinstance(a, ASTParametersQualifiers))
+
+ def require_start_space(self):
+ if (len(self.ptrOps) > 0 and
+ isinstance(self.ptrOps[-1], ASTPtrOpParamPack)):
+ return False
+ else:
+ return self.declId is not None
+
+ def __unicode__(self):
+ res = []
+ for op in self.ptrOps:
+ res.append(text_type(op))
+ if isinstance(op, ASTPtrOpParamPack) and self.declId:
+ res.append(' ')
+ if self.declId:
+ res.append(text_type(self.declId))
+ for op in self.suffixOps:
+ res.append(text_type(op))
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ for op in self.ptrOps:
+ signode += nodes.Text(text_type(op))
+ if isinstance(op, ASTPtrOpParamPack) and self.declId:
+ signode += nodes.Text(' ')
+ if self.declId:
+ self.declId.describe_signature(signode, mode, env)
+ for op in self.suffixOps:
+ op.describe_signature(signode, mode, env)
+
+
+class ASTInitializer(ASTBase):
+ def __init__(self, value):
+ self.value = value
+
+ def __unicode__(self):
+ return u''.join([' = ', text_type(self.value)])
+ def describe_signature(self, signode, mode):
+ _verify_description_mode(mode)
+ signode += nodes.Text(text_type(self))
-class TypeObjDefExpr(NamedDefExpr):
- def __init__(self, name, visibility, static, typename, type_suffixes):
- NamedDefExpr.__init__(self, name, visibility, static)
- self.typename = typename
- self.type_suffixes = type_suffixes
+class ASTType(ASTBase):
+ def __init__(self, declSpecs, decl):
+ self.declSpecs = declSpecs
+ self.decl = decl
+ self.objectType = None
+
+ @property
+ def name(self):
+ name = self.decl.name
+ if not name:
+ name = self.declSpecs.name
+ return name
def get_id(self):
- if self.typename is None:
- buf = [self.name.get_id()]
- else:
- buf = [u'%s__%s' % (self.name.get_id(), self.typename.get_id())]
- for suffix in self.type_suffixes:
- buf.append(suffix.get_id_suffix())
- return u''.join(buf)
+ res = []
+ if self.objectType: # needs the name
+ res.append(_id_prefix)
+ if self.objectType == 'function': # also modifiers
+ res.append(self.decl.get_modifiers_id())
+ res.append(self.prefixedName.get_id())
+ res.append(self.decl.get_param_id())
+ elif self.objectType == 'type': # just the name
+ res.append(self.prefixedName.get_id())
+ else:
+ print(self.objectType)
+ assert False
+ else: # only type encoding
+ res.append(self.decl.get_ptr_suffix_id())
+ res.append(self.declSpecs.get_id())
+ res.append(self.decl.get_param_id())
+ return u''.join(res)
def __unicode__(self):
- buf = self.get_modifiers()
- if self.typename is None:
- buf.append(unicode(self.name))
- else:
- buf.extend(map(unicode, (self.typename, self.name)))
- buf = [u' '.join(buf)]
- for suffix in self.type_suffixes:
- buf.append(unicode(suffix))
- return u''.join(buf)
+ res = []
+ declSpecs = text_type(self.declSpecs)
+ res.append(declSpecs)
+ if self.decl.require_start_space() and len(declSpecs) > 0:
+ res.append(u' ')
+ res.append(text_type(self.decl))
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ self.declSpecs.describe_signature(signode, 'markType', env)
+ if (self.decl.require_start_space() and
+ len(text_type(self.declSpecs)) > 0):
+ signode += nodes.Text(' ')
+ self.decl.describe_signature(signode, mode, env)
-class MemberObjDefExpr(NamedDefExpr):
+class ASTTypeWithInit(ASTBase):
+ def __init__(self, type, init):
+ self.objectType = None
+ self.type = type
+ self.init = init
- def __init__(self, name, visibility, static, typename, type_suffixes,
- value):
- NamedDefExpr.__init__(self, name, visibility, static)
- self.typename = typename
- self.type_suffixes = type_suffixes
- self.value = value
+ @property
+ def name(self):
+ return self.type.name
def get_id(self):
- buf = [u'%s__%s' % (self.name.get_id(), self.typename.get_id())]
- for suffix in self.type_suffixes:
- buf.append(suffix.get_id_suffix())
- return u''.join(buf)
+ if self.objectType == 'member':
+ return _id_prefix + self.prefixedName.get_id()
+ else:
+ return self.type.get_id()
def __unicode__(self):
- buf = self.get_modifiers()
- buf.extend((unicode(self.typename), unicode(self.name)))
- buf = [u' '.join(buf)]
- for suffix in self.type_suffixes:
- buf.append(unicode(suffix))
- if self.value is not None:
- buf.append(u' = %s' % self.value)
- return u''.join(buf)
-
-
-class FuncDefExpr(NamedDefExpr):
-
- def __init__(self, name, visibility, static, explicit, constexpr, rv,
- signature, **kwargs):
- NamedDefExpr.__init__(self, name, visibility, static)
- self.rv = rv
- self.signature = signature
- self.explicit = explicit
- self.constexpr = constexpr
- self.const = kwargs.get('const', False)
- self.volatile = kwargs.get('volatile', False)
- self.noexcept = kwargs.get('noexcept', False)
- self.override = kwargs.get('override', False)
- self.rvalue_this = kwargs.get('rvalue_this', False)
- self.lvalue_this = kwargs.get('lvalue_this', False)
- self.pure_virtual = kwargs.get('pure_virtual', False)
- self.delete = kwargs.get('delete', False)
- self.default = kwargs.get('default', False)
+ res = []
+ res.append(text_type(self.type))
+ if self.init:
+ res.append(text_type(self.init))
+ return u''.join(res)
- def get_id(self):
- return u'%s%s%s%s' % (
- self.name.get_id(),
- self.signature and u'__' +
- u'.'.join(x.get_id() for x in self.signature) or u'',
- self.const and u'C' or u'',
- self.constexpr and 'CE' or ''
- )
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ self.type.describe_signature(signode, mode, env)
+ if self.init:
+ self.init.describe_signature(signode, mode)
- def __unicode__(self):
- buf = self.get_modifiers()
- if self.explicit:
- buf.append(u'explicit')
- if self.constexpr:
- buf.append(u'constexpr')
- if self.rv is not None:
- buf.append(unicode(self.rv))
- buf.append(u'%s(%s)' % (self.name, u', '.join(
- map(unicode, self.signature))))
- if self.const:
- buf.append(u'const')
- if self.volatile:
- buf.append(u'volatile')
- if self.rvalue_this:
- buf.append(u'&&')
- if self.lvalue_this:
- buf.append(u'&')
- if self.noexcept:
- buf.append(u'noexcept')
- if self.override:
- buf.append(u'override')
- if self.pure_virtual:
- buf.append(u'= 0')
- if self.default:
- buf.append(u'= default')
- if self.delete:
- buf.append(u'= delete')
- return u' '.join(buf)
+class ASTBaseClass(ASTBase):
+ def __init__(self, name, visibility):
+ self.name = name
+ self.visibility = visibility
+
+ def __unicode__(self):
+ res = []
+ if self.visibility != 'private':
+ res.append(self.visibility)
+ res.append(' ')
+ res.append(text_type(self.name))
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ if self.visibility != 'private':
+ signode += addnodes.desc_annotation(
+ self.visibility, self.visibility)
+ signode += nodes.Text(' ')
+ self.name.describe_signature(signode, mode, env)
-class ClassDefExpr(NamedDefExpr):
- def __init__(self, name, visibility, static, bases):
- NamedDefExpr.__init__(self, name, visibility, static)
+class ASTClass(ASTBase):
+ def __init__(self, name, bases):
+ self.name = name
self.bases = bases
def get_id(self):
- return self.name.get_id()
-
- def _tostring(self, visibility='public'):
- buf = self.get_modifiers(visibility)
- buf.append(unicode(self.name))
- if self.bases:
- buf.append(u':')
- buf.append(u', '.join(base._tostring('private')
- for base in self.bases))
- return u' '.join(buf)
+ return _id_prefix + self.prefixedName.get_id()
def __unicode__(self):
- return self._tostring('public')
+ res = []
+ res.append(text_type(self.name))
+ if len(self.bases) > 0:
+ res.append(' : ')
+ first = True
+ for b in self.bases:
+ if not first:
+ res.append(', ')
+ first = False
+ res.append(text_type(b))
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ self.name.describe_signature(signode, mode, env)
+ if len(self.bases) > 0:
+ signode += nodes.Text(' : ')
+ for b in self.bases:
+ b.describe_signature(signode, mode, env)
+ signode += nodes.Text(', ')
+ signode.pop()
+
class DefinitionParser(object):
+ # those without signedness and size modifiers
+ # see http://en.cppreference.com/w/cpp/language/types
+ _simple_fundemental_types = (
+ 'void', 'bool', 'char', 'wchar_t', 'char16_t', 'char32_t', 'int',
+ 'float', 'double', 'auto'
+ )
- # mapping of valid type modifiers. if the set is None it means
- # the modifier can prefix all types, otherwise only the types
- # (actually more keywords) in the set. Also check
- # _guess_typename when changing this.
- _modifiers = {
- 'volatile': None,
- 'register': None,
- 'mutable': None,
- 'const': None,
- 'typename': None,
- 'struct': None,
- 'unsigned': set(('char', 'short', 'int', 'long')),
- 'signed': set(('char', 'short', 'int', 'long')),
- 'short': set(('int',)),
- 'long': set(('int', 'long', 'double'))
- }
+ _prefix_keys = ('class', 'struct', 'union', 'typename')
def __init__(self, definition):
self.definition = definition.strip()
@@ -552,8 +1055,10 @@ class DefinitionParser(object):
self._previous_state = (0, None)
def fail(self, msg):
- raise DefinitionError('Invalid definition: %s [error at %d]\n %s' %
- (msg, self.pos, self.definition))
+ indicator = '-' * self.pos + '^'
+ raise DefinitionError(
+ 'Invalid definition: %s [error at %d]\n %s\n %s' %
+ (msg, self.pos, self.definition, indicator))
def match(self, regex):
match = regex.match(self.definition, self.pos)
@@ -602,379 +1107,527 @@ class DefinitionParser(object):
if self.last_match is not None:
return self.last_match.group()
+ def read_rest(self):
+ rv = self.definition[self.pos:]
+ self.pos = self.end
+ return rv
+
+ def assert_end(self):
+ self.skip_ws()
+ if not self.eof:
+ self.fail('expected end of definition, got %r' %
+ self.definition[self.pos:])
+
def _parse_operator(self):
self.skip_ws()
+ # adapted from the old code
# thank god, a regular operator definition
if self.match(_operator_re):
- return NameDefExpr('operator' +
- _whitespace_re.sub('', self.matched_text))
+ return ASTOperatorBuildIn(self.matched_text)
# new/delete operator?
- for allocop in 'new', 'delete':
- if not self.skip_word(allocop):
+ for op in 'new', 'delete':
+ if not self.skip_word(op):
continue
self.skip_ws()
if self.skip_string('['):
self.skip_ws()
if not self.skip_string(']'):
- self.fail('expected "]" for ' + allocop)
- allocop += '[]'
- return NameDefExpr('operator ' + allocop)
+ self.fail('Expected "]" after "operator ' + op + '["')
+ op += '[]'
+ return ASTOperatorBuildIn(op)
# oh well, looks like a cast operator definition.
# In that case, eat another type.
type = self._parse_type()
- return CastOpDefExpr(type)
-
- def _parse_name(self):
- return self._parse_name_or_template_arg(False)
-
- def _parse_name_or_template_arg(self, in_template):
- if not self.match(_identifier_re):
- if not in_template:
- self.fail('expected name')
- if not self.match(_template_arg_re):
- self.fail('expected name or constant template argument')
- return ConstantTemplateArgExpr(self.matched_text.strip())
- identifier = self.matched_text
-
- # strictly speaking, operators are not regular identifiers
- # but because operator is a keyword, it might not be used
- # for variable names anyways, so we can safely parse the
- # operator here as identifier
- if identifier == 'operator':
- return self._parse_operator()
-
- return NameDefExpr(identifier)
-
- def _guess_typename(self, path):
- if not path:
- return [], 'int'
- # for the long type, we don't want the int in there
- if 'long' in path:
- path = [x for x in path if x != 'int']
- # remove one long
- path.remove('long')
- return path, 'long'
- if path[-1] in ('int', 'char'):
- return path[:-1], path[-1]
- return path, 'int'
-
- def _attach_crefptr(self, expr, is_const=False):
- if is_const:
- expr = ConstDefExpr(expr, prefix=True)
- while 1:
- self.skip_ws()
- if self.skip_word('const'):
- expr = ConstDefExpr(expr)
- elif self.skip_string('*'):
- expr = PtrDefExpr(expr)
- elif self.skip_string('&'):
- if self.skip_string('&'):
- expr = RValRefDefExpr(expr)
- else:
- expr = LValRefDefExpr(expr)
- else:
- return expr
-
- def _try_parse_type_suffixes(self):
- rv = []
- while self.match(_array_def_re):
- rv.append(ArrayTypeSuffixDefExpr(self.last_match.group(1)))
- self.skip_ws()
- return rv
+ return ASTOperatorType(type)
- def _peek_const(self, path):
- try:
- path.remove('const')
- return True
- except ValueError:
- return False
+ def _parse_nested_name(self):
+ names = []
- def _parse_builtin(self, modifiers):
- modifier = modifiers[-1]
- path = modifiers
- following = self._modifiers[modifier]
+ self.skip_ws()
+ if self.skip_string('::'):
+ names.append(u'')
while 1:
self.skip_ws()
+ # TODO: parse the "template" keyword
if not self.match(_identifier_re):
- break
+ self.fail("expected identifier")
identifier = self.matched_text
- if identifier in following:
- path.append(identifier)
- following = self._modifiers[modifier]
- assert following
+ if identifier == 'operator':
+ op = self._parse_operator()
+ names.append(op)
else:
- self.backout()
- break
-
- is_const = self._peek_const(path)
- modifiers, typename = self._guess_typename(path)
- rv = ModifierDefExpr(NameDefExpr(typename), modifiers)
- return self._attach_crefptr(rv, is_const)
-
- def _parse_type_expr(self, in_template=False):
- typename = self._parse_name_or_template_arg(in_template)
- self.skip_ws()
- if not self.skip_string('<'):
- return typename
+ templateArgs = None
+ self.skip_ws()
+ if self.skip_string('<'):
+ templateArgs = []
+ while 1:
+ pos = self.pos
+ try:
+ type = self._parse_type(allowParams=True)
+ templateArgs.append(type)
+ except DefinitionError:
+ self.pos = pos
+ symbols = []
+ startPos = self.pos
+ self.skip_ws()
+ if self.match(_string_re):
+ value = self.matched_text
+ else:
+ while not self.eof:
+ if (len(symbols) == 0 and
+ self.current_char in (
+ ',', '>')):
+ break
+ # TODO: actually implement nice handling
+ # of quotes, braces, brackets, parens, and
+ # whatever
+ self.pos += 1
+ if self.eof:
+ self.pos = startPos
+ self.fail(
+ 'Could not find end of constant '
+ 'template argument.')
+ value = self.definition[startPos:self.pos].strip()
+ templateArgs.append(ASTTemplateArgConstant(value))
+ self.skip_ws()
+ if self.skip_string('>'):
+ break
+ elif self.skip_string(','):
+ continue
+ else:
+ self.fail('Expected ">" or "," in template '
+ 'argument list.')
+ names.append(ASTNestedNameElement(identifier, templateArgs))
- args = []
- while 1:
self.skip_ws()
- if self.skip_string('>'):
+ if not 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)
+ return ASTNestedName(names)
- def _parse_type(self, in_template=False):
+ def _parse_trailing_type_spec(self):
+ # fundemental types
self.skip_ws()
- result = []
- modifiers = []
-
- # if there is a leading :: or not, we don't care because we
- # treat them exactly the same. Buf *if* there is one, we
- # don't have to check for type modifiers
- if not self.skip_string('::'):
- self.skip_ws()
- while self.match(_identifier_re):
- modifier = self.matched_text
- if modifier in self._modifiers:
- following = self._modifiers[modifier]
- # if the set is not none, there is a limited set
- # of types that might follow. It is technically
- # impossible for a template to follow, so what
- # we do is go to a different function that just
- # eats types
- modifiers.append(modifier)
- if following is not None:
- return self._parse_builtin(modifiers)
- self.skip_ws()
- else:
- self.backout()
- break
+ for t in self._simple_fundemental_types:
+ if self.skip_word(t):
+ return ASTTrailingTypeSpecFundamental(t)
+ # TODO: this could/should be more strict
+ elements = []
+ self.skip_ws()
+ if self.skip_word_and_ws('signed'):
+ elements.append('signed')
+ elif self.skip_word_and_ws('unsigned'):
+ elements.append('unsigned')
while 1:
- self.skip_ws()
- if (in_template and self.current_char in ',>') or \
- (result and not self.skip_string('::')) or \
- self.eof:
+ if self.skip_word_and_ws('short'):
+ elements.append('short')
+ elif self.skip_word_and_ws('long'):
+ elements.append('long')
+ else:
break
- result.append(self._parse_type_expr(in_template))
-
- if not result:
- self.fail('expected type')
- if len(result) == 1:
- rv = result[0]
- else:
- rv = PathDefExpr(result)
- is_const = self._peek_const(modifiers)
- if modifiers:
- rv = ModifierDefExpr(rv, modifiers)
- return self._attach_crefptr(rv, is_const)
+ if self.skip_word_and_ws('int'):
+ elements.append('int')
+ elif self.skip_word_and_ws('double'):
+ elements.append('double')
+ if len(elements) > 0:
+ return ASTTrailingTypeSpecFundamental(u' '.join(elements))
+
+ # decltype
+ self.skip_ws()
+ if self.skip_word_and_ws('decltype'):
+ self.fail('"decltype(.)" in trailing_type_spec not implemented')
- def _parse_default_expr(self):
+ # prefixed
+ prefix = None
self.skip_ws()
- if self.match(_string_re):
- return self.matched_text
- paren_stack_depth = 0
- max_pos = len(self.definition)
- rv_start = self.pos
- while 1:
- idx0 = self.definition.find('(', self.pos)
- idx1 = self.definition.find(',', self.pos)
- idx2 = self.definition.find(')', self.pos)
- if idx0 < 0:
- idx0 = max_pos
- if idx1 < 0:
- idx1 = max_pos
- if idx2 < 0:
- idx2 = max_pos
- idx = min(idx0, idx1, idx2)
- if idx >= max_pos:
- self.fail('unexpected end in default expression')
- if idx == idx0:
- paren_stack_depth += 1
- elif idx == idx2:
- paren_stack_depth -= 1
- if paren_stack_depth < 0:
- break
- elif paren_stack_depth == 0:
+ for k in self._prefix_keys:
+ if self.skip_word_and_ws(k):
+ prefix = k
break
- self.pos = idx+1
- rv = self.definition[rv_start:idx]
- self.pos = idx
- return rv
+ nestedName = self._parse_nested_name()
+ return ASTTrailingTypeSpecName(prefix, nestedName)
- def _parse_signature(self):
+ def _parse_parameters_and_qualifiers(self, paramMode):
self.skip_ws()
if not self.skip_string('('):
- self.fail('expected parentheses for function')
-
+ if paramMode == 'function':
+ self.fail('Expecting "(" in parameters_and_qualifiers.')
+ else:
+ return None
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()
+ if not self.skip_string(')'):
+ while 1:
self.skip_ws()
-
- if self.skip_string('...'):
- args.append(ArgumentDefExpr(None, '...', [], None))
- if self.skip_string(')'):
+ if self.skip_string('...'):
+ args.append(ASTFunctinoParameter(None, True))
+ self.skip_ws()
+ if not self.skip_string(')'):
+ self.fail('Expected ")" after "..." in '
+ 'parameters_and_qualifiers.')
break
+ if paramMode == 'function':
+ arg = self._parse_type_with_init(named='maybe')
else:
- self.fail('expected closing parenthesis after ellipses')
+ arg = self._parse_type()
+ # TODO: parse default parameters
+ args.append(ASTFunctinoParameter(arg))
- argname = default = None
- argtype = self._parse_type()
- self.skip_ws()
- type_suffixes = self._try_parse_type_suffixes()
- if self.skip_string('='):
- default = self._parse_default_expr()
- elif self.current_char not in ',)':
- argname = self._parse_name()
self.skip_ws()
- type_suffixes.extend(self._try_parse_type_suffixes())
- if self.skip_string('='):
- default = self._parse_default_expr()
- if argname is None:
- argname = argtype
- argtype = None
-
- args.append(ArgumentDefExpr(argtype, argname,
- type_suffixes, default))
+ if self.skip_string(','):
+ continue
+ elif self.skip_string(')'):
+ break
+ else:
+ self.fail(
+ 'Expecting "," or ")" in parameters_and_qualifiers, '
+ 'got "%s".' % self.current_char)
+
+ if paramMode != 'function':
+ return ASTParametersQualifiers(
+ args, None, None, None, None, None, None, None)
+
self.skip_ws()
- attributes = dict(
- signature=args,
- const=self.skip_word_and_ws('const'),
- volatile=self.skip_word_and_ws('volatile'),
- noexcept=self.skip_word_and_ws('noexcept'),
- override=self.skip_word_and_ws('override'),
- pure_virtual=False,
- lvalue_this=False,
- rvalue_this=False,
- delete=False,
- default=False)
+ const = self.skip_word_and_ws('const')
+ volatile = self.skip_word_and_ws('volatile')
+ if not const: # the can be permuted
+ const = self.skip_word_and_ws('const')
+ refQual = None
if self.skip_string('&&'):
- attributes['rvalue_this'] = True
- if self.skip_string('&'):
- attributes['lvalue_this'] = True
+ refQual = '&&'
+ if not refQual and self.skip_string('&'):
+ refQual = '&'
+
+ exceptionSpec = None
+ override = None
+ final = None
+ initializer = None
+ self.skip_ws()
+ if self.skip_string('noexcept'):
+ exceptionSpec = 'noexcept'
+ self.skip_ws()
+ if self.skip_string('('):
+ self.fail('Parameterised "noexcept" not implemented.')
- if attributes['lvalue_this'] and attributes['rvalue_this']:
- self.fail('rvalue reference for *this specifier must be one of'
- '"&&" or "&"')
+ self.skip_ws()
+ override = self.skip_word_and_ws('override')
+ final = self.skip_word_and_ws('final')
+ if not override:
+ override = self.skip_word_and_ws(
+ 'override') # they can be permuted
+ self.skip_ws()
if self.skip_string('='):
self.skip_ws()
- if self.skip_string('0'):
- attributes['pure_virtual'] = True
- return attributes
- if self.skip_word('NULL') or self.skip_word('nullptr'):
- attributes['pure_virtual'] = True
- return attributes
- if self.skip_word('delete'):
- attributes['delete'] = True
- return attributes
- if self.skip_word('default'):
- attributes['default'] = True
- return attributes
-
- self.fail('functions must be defined with '
- 'either 0, NULL, nullptr, default or delete, other'
- 'macros are not allowed')
- return attributes
-
- def _parse_visibility_static(self):
- visibility = 'public'
- if self.match(_visibility_re):
- visibility = self.matched_text
- static = self.skip_word_and_ws('static')
- return visibility, static
-
- def parse_type(self):
- return self._parse_type()
+ valid = ('0', 'delete', 'default')
+ for w in valid:
+ if self.skip_word_and_ws(w):
+ initializer = w
+ break
+ if not initializer:
+ self.fail(
+ 'Expected "%s" in initializer-specifier.'
+ % u'" or "'.join(valid))
- def parse_type_object(self):
- visibility, static = self._parse_visibility_static()
- typename = self._parse_type()
- self.skip_ws()
- if not self.eof:
- name = self._parse_type()
- type_suffixes = self._try_parse_type_suffixes()
+ return ASTParametersQualifiers(
+ args, volatile, const, refQual, exceptionSpec, override, final,
+ initializer)
+
+ def _parse_decl_specs(self, outer, typed=True):
+ """
+ visibility storage-class-specifier function-specifier "constexpr"
+ "volatile" "const" trailing-type-specifier
+
+ storage-class-specifier -> "static" (only for member_object and
+ function_object)
+
+ function-specifier -> "inline" | "virtual" | "explicit" (only for
+ function_object)
+
+ "constexpr" (only for member_object and function_object)
+ """
+ visibility = None
+ storage = None
+ inline = None
+ virtual = None
+ explicit = None
+ constexpr = None
+ volatile = None
+ const = None
+
+ if outer:
+ self.skip_ws()
+ if self.match(_visibility_re):
+ visibility = self.matched_text
+
+ while 1: # accept any permutation of a subset of some decl-specs
+ self.skip_ws()
+ if not storage:
+ if outer in ('member', 'function'):
+ if self.skip_word('static'):
+ storage = 'static'
+ continue
+ if outer == 'member':
+ if self.skip_word('mutable'):
+ storage = 'mutable'
+ continue
+ if outer == 'fuction':
+ # TODO: maybe in more contexts, missing test cases
+ if self.skip_word('register'):
+ storage = 'register'
+ continue
+
+ if outer == 'function':
+ # function-specifiers
+ if not inline:
+ inline = self.skip_word('inline')
+ if inline:
+ continue
+ if not virtual:
+ virtual = self.skip_word('virtual')
+ if virtual:
+ continue
+ if not explicit:
+ explicit = self.skip_word('explicit')
+ if explicit:
+ continue
+
+ if not constexpr and outer in ('member', 'function'):
+ constexpr = self.skip_word("constexpr")
+ if constexpr:
+ continue
+ if not volatile and typed:
+ volatile = self.skip_word('volatile')
+ if volatile:
+ continue
+ if not const and typed:
+ const = self.skip_word('const')
+ if const:
+ continue
+ break
+
+ if typed:
+ trailing = self._parse_trailing_type_spec()
else:
- name = typename
- typename = None
- type_suffixes = []
- return TypeObjDefExpr(name, visibility, static, typename, type_suffixes)
+ trailing = None
+ return ASTDeclSpecs(
+ outer, visibility, storage, inline, virtual, explicit, constexpr,
+ volatile, const, trailing)
+
+ def _parse_declerator(self, named, paramMode=None, typed=True):
+ if paramMode:
+ if paramMode not in ('type', 'function'):
+ raise Exception(
+ "Internal error, unknown paramMode '%s'." % paramMode)
+ ptrOps = []
+ while 1:
+ if not typed:
+ break
+ self.skip_ws()
+ if self.skip_string('*'):
+ self.skip_ws()
+ volatile = self.skip_word_and_ws('volatile')
+ const = self.skip_word_and_ws('const')
+ ptrOps.append(ASTPtrOpPtr(volatile=volatile, const=const))
+ elif self.skip_string('&'):
+ ptrOps.append(ASTPtrOpRef())
+ elif self.skip_string('...'):
+ ptrOps.append(ASTPtrOpParamPack())
+ break
+ else:
+ break
- def parse_member_object(self):
- visibility, static = self._parse_visibility_static()
- typename = self._parse_type()
- name = self._parse_type()
- type_suffixes = self._try_parse_type_suffixes()
- self.skip_ws()
- if self.skip_string('='):
- value = self.read_rest().strip()
+ if named == 'maybe':
+ try:
+ declId = self._parse_nested_name()
+ except DefinitionError:
+ declId = None
+ elif named:
+ declId = self._parse_nested_name()
else:
- value = None
- return MemberObjDefExpr(name, visibility, static, typename,
- type_suffixes, value)
+ declId = None
+
+ suffixOpts = []
+ while 1:
+ self.skip_ws()
+ if typed and self.skip_string('['):
+ startPos = self.pos - 1
+ openCount = 1
+ while not self.eof:
+ c = self.current_char
+ if c == '[':
+ openCount += 1
+ elif c == ']':
+ openCount -= 1
+ if openCount == 0:
+ break
+ self.pos += 1
+ if self.eof:
+ self.pos = startPos
+ self.fail(
+ "Could not find closing square bracket for array.")
+ self.pos += 1
+ suffixOpts.append(ASTArray(
+ self.definition[startPos + 1:self.pos - 1].strip()))
+ continue
+ if paramMode:
+ paramQual = self._parse_parameters_and_qualifiers(paramMode)
+ if paramQual:
+ suffixOpts.append(paramQual)
+ break
- def parse_function(self):
- visibility, static = self._parse_visibility_static()
- explicit = self.skip_word_and_ws('explicit')
- constexpr = self.skip_word_and_ws('constexpr')
+ return ASTDeclerator(ptrOps, declId, suffixOpts)
- rv = self._parse_type()
+ def _parse_initializer(self, outer=None):
self.skip_ws()
- # some things just don't have return values
- if self.current_char == '(':
- name = rv
- rv = None
+ # TODO: support paren and brace initialization for memberObject
+ if not self.skip_string('='):
+ return None
else:
- name = self._parse_type()
- return FuncDefExpr(name, visibility, static, explicit, constexpr, rv,
- **self._parse_signature())
+ if outer == 'member':
+ value = self.read_rest().strip()
+ return ASTInitializer(value)
+ elif outer is None: # function parameter
+ symbols = []
+ startPos = self.pos
+ self.skip_ws()
+ if self.match(_string_re):
+ value = self.matched_text
+ return ASTInitializer(value)
+ while not self.eof:
+ if len(symbols) == 0 and self.current_char in (',', ')'):
+ break
+ elif len(symbols) > 0 and self.current_char == symbols[-1]:
+ symbols.pop()
+ elif self.current_char == '(':
+ symbols.append(')')
+ # TODO: actually implement nice handling of quotes, braces,
+ # brackets, parens, and whatever
+ self.pos += 1
+ if self.eof:
+ self.pos = startPos
+ self.fail(
+ 'Could not find end of default value for function '
+ 'parameter.')
+ value = self.definition[startPos:self.pos].strip()
+ return ASTInitializer(value)
+ else:
+ self.fail(
+ "Internal error, initializer for outer '%s' not "
+ "implemented." % outer)
- def parse_class(self):
- visibility, static = self._parse_visibility_static()
- name = self._parse_type()
+ def _parse_type(self, outer=None, named=False, allowParams=False):
+ """
+ named=False|'maybe'|True: 'maybe' is e.g., for function objects which
+ doesn't need to name the arguments
+ """
+ if outer: # always named
+ if outer not in ('type', 'member', 'function'):
+ raise Exception('Internal error, unknown outer "%s".' % outer)
+ assert not named
+
+ if outer in ('type', 'function'):
+ # We allow type objects to just be a name.
+ # Some functions don't have normal return types: constructors,
+ # destrutors, cast operators
+ startPos = self.pos
+ # first try without the type
+ try:
+ declSpecs = self._parse_decl_specs(outer=outer, typed=False)
+ decl = self._parse_declerator(named=True, paramMode=outer,
+ typed=False)
+ self.assert_end()
+ except DefinitionError as exUntyped:
+ self.pos = startPos
+ try:
+ declSpecs = self._parse_decl_specs(outer=outer)
+ decl = self._parse_declerator(named=True, paramMode=outer)
+ except DefinitionError as exTyped:
+ if outer == 'type':
+ raise DefinitionError(
+ 'Type must be either just a name or a '
+ 'typedef-like declaration.\nJust a name error: '
+ '%s\nTypedef-like expression error: %s'
+ % (exUntyped.description, exTyped.description))
+ else:
+ # do it again to get the proper traceback (how do you
+ # relieable save a traceback when an exception is
+ # constructed?)
+ self.pos = startPos
+ declSpecs = self._parse_decl_specs(outer=outer)
+ decl = self._parse_declerator(named=True,
+ paramMode=outer)
+ else:
+ if outer:
+ named = True
+ allowParams = True
+ if allowParams:
+ paramMode = 'type'
+ else:
+ paramMode = None
+ declSpecs = self._parse_decl_specs(outer=outer)
+ decl = self._parse_declerator(named=named, paramMode=paramMode)
+ return ASTType(declSpecs, decl)
+
+ def _parse_type_with_init(self, outer=None, named=False):
+ if outer:
+ assert outer in ('type', 'member', 'function')
+ type = self._parse_type(outer=outer, named=named)
+ init = self._parse_initializer(outer=outer)
+ return ASTTypeWithInit(type, init)
+
+ def _parse_class(self):
+ name = self._parse_nested_name()
bases = []
+ self.skip_ws()
if self.skip_string(':'):
- self.skip_ws()
while 1:
- access = 'private'
+ self.skip_ws()
+ visibility = 'private'
if self.match(_visibility_re):
- access = self.matched_text
- base = self._parse_type()
- bases.append(ClassDefExpr(base, access, False, []))
+ visibility = self.matched_text
+ baseName = self._parse_nested_name()
+ bases.append(ASTBaseClass(baseName, visibility))
+ self.skip_ws()
if self.skip_string(','):
- self.skip_ws()
+ continue
else:
break
- return ClassDefExpr(name, visibility, static, bases)
+ return ASTClass(name, bases)
- def read_rest(self):
- rv = self.definition[self.pos:]
- self.pos = self.end
- return rv
+ def parse_type_object(self):
+ res = self._parse_type(outer='type')
+ res.objectType = 'type'
+ return res
- def assert_end(self):
- self.skip_ws()
- if not self.eof:
- self.fail('expected end of definition, got %r' %
- self.definition[self.pos:])
+ def parse_member_object(self):
+ res = self._parse_type_with_init(outer='member')
+ res.objectType = 'member'
+ return res
+
+ def parse_function_object(self):
+ res = self._parse_type(outer='function')
+ res.objectType = 'function'
+ return res
+
+ def parse_class_object(self):
+ res = self._parse_class()
+ res.objectType = 'class'
+ return res
+
+ def parse_namespace_object(self):
+ res = self._parse_nested_name()
+ res.objectType = 'namespace'
+ return res
+
+ def parse_xref_object(self):
+ res = self._parse_nested_name()
+ res.objectType = 'xref'
+ return res
class CPPObject(ObjectDescription):
@@ -991,214 +1644,120 @@ class CPPObject(ObjectDescription):
names=('returns', 'return')),
]
- def attach_name(self, node, name):
- owner, name = name.split_owner()
- varname = unicode(name)
- if owner is not None:
- owner = unicode(owner) + '::'
- node += addnodes.desc_addname(owner, owner)
- node += addnodes.desc_name(varname, varname)
-
- def attach_type_suffixes(self, node, suffixes):
- for suffix in suffixes:
- node += nodes.Text(unicode(suffix))
-
- def attach_type(self, node, type):
- # XXX: link to c?
- text = unicode(type)
- pnode = addnodes.pending_xref(
- '', refdomain='cpp', reftype='type',
- reftarget=text, modname=None, classname=None)
- pnode['cpp:parent'] = self.env.temp_data.get('cpp:parent')
- pnode += nodes.Text(text)
- node += pnode
-
- def attach_modifiers(self, node, obj, visibility='public'):
- if obj.visibility != visibility:
- node += addnodes.desc_annotation(obj.visibility,
- obj.visibility)
- node += nodes.Text(' ')
- if obj.static:
- node += addnodes.desc_annotation('static', 'static')
- node += nodes.Text(' ')
- if getattr(obj, 'constexpr', False):
- node += addnodes.desc_annotation('constexpr', 'constexpr')
- node += nodes.Text(' ')
-
- def add_target_and_index(self, sigobj, sig, signode):
- theid = sigobj.get_id()
- name = unicode(sigobj.name)
+ def add_target_and_index(self, ast, sig, signode):
+ theid = ast.get_id()
+ name = text_type(ast.prefixedName)
if theid not in self.state.document.ids:
- signode['names'].append(theid)
+ # the name is not unique, the first one will win
+ objects = self.env.domaindata['cpp']['objects']
+ if name not in objects:
+ signode['names'].append(name)
signode['ids'].append(theid)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
-
- self.env.domaindata['cpp']['objects'].setdefault(name,
- (self.env.docname, self.objtype, theid))
+ if name not in objects:
+ objects.setdefault(name,
+ (self.env.docname, ast.objectType, theid))
+ # add the uninstantiated template if it doesn't exist
+ uninstantiated = ast.prefixedName.get_name_no_last_template()
+ if uninstantiated != name and uninstantiated not in objects:
+ signode['names'].append(uninstantiated)
+ objects.setdefault(uninstantiated, (
+ self.env.docname, ast.objectType, theid))
+ self.env.ref_context['cpp:lastname'] = ast.prefixedName
indextext = self.get_index_text(name)
- if indextext:
- self.indexnode['entries'].append(('single', indextext, theid, ''))
-
- def before_content(self):
- lastname = self.names and self.names[-1]
- if lastname and not self.env.temp_data.get('cpp:parent'):
- assert isinstance(lastname, NamedDefExpr)
- self.env.temp_data['cpp:parent'] = lastname.name
- self.parentname_set = True
- else:
- self.parentname_set = False
-
- def after_content(self):
- if self.parentname_set:
- self.env.temp_data['cpp:parent'] = None
+ if not re.compile(r'^[a-zA-Z0-9_]*$').match(theid):
+ self.state_machine.reporter.warning(
+ 'Index id generation for C++ object "%s" failed, please '
+ 'report as bug (id=%s).' % (text_type(ast), theid),
+ line=self.lineno)
+ self.indexnode['entries'].append(('single', indextext, theid, ''))
def parse_definition(self, parser):
raise NotImplementedError()
- def describe_signature(self, signode, arg):
+ def describe_signature(self, signode, ast):
raise NotImplementedError()
def handle_signature(self, sig, signode):
parser = DefinitionParser(sig)
try:
- rv = self.parse_definition(parser)
+ ast = self.parse_definition(parser)
parser.assert_end()
- except DefinitionError, e:
- self.state_machine.reporter.warning(e.description, line=self.lineno)
+ except DefinitionError as e:
+ self.state_machine.reporter.warning(e.description,
+ line=self.lineno)
raise ValueError
- self.describe_signature(signode, rv)
-
- parent = self.env.temp_data.get('cpp:parent')
- if parent is not None:
- rv = rv.clone()
- rv.name = rv.name.prefix(parent)
- return rv
-
-
-class CPPClassObject(CPPObject):
-
- def get_index_text(self, name):
- return _('%s (C++ class)') % name
-
- def parse_definition(self, parser):
- return parser.parse_class()
+ self.describe_signature(signode, ast)
- def describe_signature(self, signode, cls):
- self.attach_modifiers(signode, cls)
- signode += addnodes.desc_annotation('class ', 'class ')
- self.attach_name(signode, cls.name)
- if cls.bases:
- signode += nodes.Text(' : ')
- for base in cls.bases:
- self.attach_modifiers(signode, base, 'private')
- signode += nodes.emphasis(unicode(base.name),
- unicode(base.name))
- signode += nodes.Text(', ')
- signode.pop() # remove the trailing comma
+ parent = self.env.ref_context.get('cpp:parent')
+ if parent and len(parent) > 0:
+ ast = ast.clone()
+ ast.prefixedName = ast.name.prefix_nested_name(parent[-1])
+ else:
+ ast.prefixedName = ast.name
+ return ast
class CPPTypeObject(CPPObject):
-
def get_index_text(self, name):
- if self.objtype == 'type':
- return _('%s (C++ type)') % name
- return ''
+ return _('%s (C++ type)') % name
def parse_definition(self, parser):
return parser.parse_type_object()
- def describe_signature(self, signode, obj):
- self.attach_modifiers(signode, obj)
+ def describe_signature(self, signode, ast):
signode += addnodes.desc_annotation('type ', 'type ')
- if obj.typename is not None:
- self.attach_type(signode, obj.typename)
- signode += nodes.Text(' ')
- self.attach_name(signode, obj.name)
- self.attach_type_suffixes(signode, obj.type_suffixes)
+ ast.describe_signature(signode, 'lastIsName', self.env)
class CPPMemberObject(CPPObject):
-
def get_index_text(self, name):
- if self.objtype == 'member':
- return _('%s (C++ member)') % name
- return ''
+ return _('%s (C++ member)') % name
def parse_definition(self, parser):
return parser.parse_member_object()
- def describe_signature(self, signode, obj):
- self.attach_modifiers(signode, obj)
- self.attach_type(signode, obj.typename)
- signode += nodes.Text(' ')
- self.attach_name(signode, obj.name)
- self.attach_type_suffixes(signode, obj.type_suffixes)
- if obj.value is not None:
- signode += nodes.Text(u' = ' + obj.value)
+ def describe_signature(self, signode, ast):
+ ast.describe_signature(signode, 'lastIsName', self.env)
class CPPFunctionObject(CPPObject):
+ def get_index_text(self, name):
+ return _('%s (C++ function)') % name
- def attach_function(self, node, func):
- owner, name = func.name.split_owner()
- if owner is not None:
- owner = unicode(owner) + '::'
- node += addnodes.desc_addname(owner, owner)
-
- # cast operator is special. in this case the return value
- # is reversed.
- if isinstance(name, CastOpDefExpr):
- node += addnodes.desc_name('operator', 'operator')
- node += nodes.Text(u' ')
- self.attach_type(node, name.typename)
- else:
- funcname = unicode(name)
- node += addnodes.desc_name(funcname, funcname)
+ def parse_definition(self, parser):
+ return parser.parse_function_object()
- paramlist = addnodes.desc_parameterlist()
- for arg in func.signature:
- param = addnodes.desc_parameter('', '', noemph=True)
- if arg.type is not None:
- self.attach_type(param, arg.type)
- param += nodes.Text(u' ')
- param += nodes.emphasis(unicode(arg.name), unicode(arg.name))
- self.attach_type_suffixes(param, arg.type_suffixes)
- if arg.default is not None:
- def_ = u'=' + unicode(arg.default)
- param += nodes.emphasis(def_, def_)
- paramlist += param
+ def describe_signature(self, signode, ast):
+ ast.describe_signature(signode, 'lastIsName', self.env)
- node += paramlist
- if func.const:
- node += addnodes.desc_addname(' const', ' const')
- if func.noexcept:
- node += addnodes.desc_addname(' noexcept', ' noexcept')
- if func.pure_virtual:
- node += addnodes.desc_addname(' = 0', ' = 0')
+class CPPClassObject(CPPObject):
def get_index_text(self, name):
- return _('%s (C++ function)') % name
+ return _('%s (C++ class)') % name
+
+ def before_content(self):
+ lastname = self.env.ref_context['cpp:lastname']
+ assert lastname
+ if 'cpp:parent' in self.env.ref_context:
+ self.env.ref_context['cpp:parent'].append(lastname)
+ else:
+ self.env.ref_context['cpp:parent'] = [lastname]
+
+ def after_content(self):
+ self.env.ref_context['cpp:parent'].pop()
def parse_definition(self, parser):
- return parser.parse_function()
+ return parser.parse_class_object()
- def describe_signature(self, signode, func):
- self.attach_modifiers(signode, func)
- if func.explicit:
- signode += addnodes.desc_annotation('explicit', 'explicit')
- signode += nodes.Text(' ')
- # return value is None for things with a reverse return value
- # such as casting operator definitions or constructors
- # and destructors.
- if func.rv is not None:
- self.attach_type(signode, func.rv)
- signode += nodes.Text(u' ')
- self.attach_function(signode, func)
+ def describe_signature(self, signode, ast):
+ signode += addnodes.desc_annotation('class ', 'class ')
+ ast.describe_signature(signode, 'lastIsName', self.env)
-class CPPCurrentNamespace(Directive):
+class CPPNamespaceObject(Directive):
"""
This directive is just to tell Sphinx that we're documenting stuff in
namespace foo.
@@ -1213,26 +1772,27 @@ class CPPCurrentNamespace(Directive):
def run(self):
env = self.state.document.settings.env
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
- env.temp_data['cpp:prefix'] = None
+ env.ref_context['cpp:parent'] = []
else:
parser = DefinitionParser(self.arguments[0])
try:
- prefix = parser.parse_type()
+ prefix = parser.parse_namespace_object()
parser.assert_end()
- except DefinitionError, e:
+ except DefinitionError as e:
self.state_machine.reporter.warning(e.description,
line=self.lineno)
else:
- env.temp_data['cpp:prefix'] = prefix
+ env.ref_context['cpp:parent'] = [prefix]
return []
class CPPXRefRole(XRefRole):
-
def process_link(self, env, refnode, has_explicit_title, title, target):
- refnode['cpp:parent'] = env.temp_data.get('cpp:parent')
+ parent = env.ref_context.get('cpp:parent')
+ if parent:
+ refnode['cpp:parent'] = parent[:]
if not has_explicit_title:
- target = target.lstrip('~') # only has a meaning for the title
+ target = target.lstrip('~') # only has a meaning for the title
# if the first character is a tilde, don't display the module/class
# parts of the contents
if title[:1] == '~':
@@ -1248,70 +1808,89 @@ class CPPDomain(Domain):
name = 'cpp'
label = 'C++'
object_types = {
- 'class': ObjType(l_('class'), 'class'),
+ 'class': ObjType(l_('class'), 'class'),
'function': ObjType(l_('function'), 'func'),
- 'member': ObjType(l_('member'), 'member'),
- 'type': ObjType(l_('type'), 'type')
+ 'member': ObjType(l_('member'), 'member'),
+ 'type': ObjType(l_('type'), 'type')
}
directives = {
- 'class': CPPClassObject,
- 'function': CPPFunctionObject,
- 'member': CPPMemberObject,
- 'type': CPPTypeObject,
- 'namespace': CPPCurrentNamespace
+ 'class': CPPClassObject,
+ 'function': CPPFunctionObject,
+ 'member': CPPMemberObject,
+ 'type': CPPTypeObject,
+ 'namespace': CPPNamespaceObject
}
roles = {
- 'class': CPPXRefRole(),
- 'func' : CPPXRefRole(fix_parens=True),
+ 'class': CPPXRefRole(),
+ 'func': CPPXRefRole(fix_parens=True),
'member': CPPXRefRole(),
- 'type': CPPXRefRole()
+ 'type': CPPXRefRole()
}
initial_data = {
- 'objects': {}, # fullname -> docname, objtype
+ 'objects': {}, # prefixedName -> (docname, objectType, id)
}
def clear_doc(self, docname):
- for fullname, (fn, _, _) in self.data['objects'].items():
- if fn == docname:
+ for fullname, data in list(self.data['objects'].items()):
+ if data[0] == docname:
del self.data['objects'][fullname]
- def resolve_xref(self, env, fromdocname, builder,
- typ, target, node, contnode):
- def _create_refnode(expr):
- name = unicode(expr)
+ def merge_domaindata(self, docnames, otherdata):
+ # XXX check duplicates
+ for fullname, data in otherdata['objects'].items():
+ if data[0] in docnames:
+ self.data['objects'][fullname] = data
+
+ def _resolve_xref_inner(self, env, fromdocname, builder,
+ target, node, contnode, warn=True):
+ def _create_refnode(nameAst):
+ name = text_type(nameAst)
if name not in self.data['objects']:
- return None
- obj = self.data['objects'][name]
- if obj[1] not in self.objtypes_for_role(typ):
- return None
- return make_refnode(builder, fromdocname, obj[0], obj[2],
- contnode, name)
+ # try dropping the last template
+ name = nameAst.get_name_no_last_template()
+ if name not in self.data['objects']:
+ return None, None
+ docname, objectType, id = self.data['objects'][name]
+ return make_refnode(builder, fromdocname, docname, id, contnode,
+ name), objectType
parser = DefinitionParser(target)
try:
- expr = parser.parse_type().get_name()
+ nameAst = parser.parse_xref_object().name
parser.skip_ws()
- if not parser.eof or expr is None:
+ if not parser.eof:
raise DefinitionError('')
except DefinitionError:
- env.warn_node('unparseable C++ definition: %r' % target, node)
- return None
-
- parent = node.get('cpp:parent', None)
+ if warn:
+ env.warn_node('unparseable C++ definition: %r' % target, node)
+ return None, None
- rv = _create_refnode(expr)
- if rv is not None or parent is None:
- return rv
- parent = parent.get_name()
+ # try as is the name is fully qualified
+ res = _create_refnode(nameAst)
+ if res[0]:
+ return res
- rv = _create_refnode(expr.prefix(parent))
- if rv is not None:
- return rv
+ # try qualifying it with the parent
+ parent = node.get('cpp:parent', None)
+ if parent and len(parent) > 0:
+ return _create_refnode(nameAst.prefix_nested_name(parent[-1]))
+ else:
+ return None, None
- parent, name = parent.split_owner()
- return _create_refnode(expr.prefix(parent))
+ def resolve_xref(self, env, fromdocname, builder,
+ typ, target, node, contnode):
+ return self._resolve_xref_inner(env, fromdocname, builder, target, node,
+ contnode)[0]
+
+ def resolve_any_xref(self, env, fromdocname, builder, target,
+ node, contnode):
+ node, objtype = self._resolve_xref_inner(env, fromdocname, builder,
+ target, node, contnode, warn=False)
+ if node:
+ return [('cpp:' + self.role_for_objtype(objtype), node)]
+ return []
def get_objects(self):
- for refname, (docname, type, theid) in self.data['objects'].iteritems():
+ for refname, (docname, type, theid) in iteritems(self.data['objects']):
yield (refname, refname, type, docname, refname, 1)