mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
1891 lines
62 KiB
Python
1891 lines
62 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
sphinx.domains.cpp
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
The C++ language domain.
|
|
|
|
: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
|
|
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.compat import Directive
|
|
from sphinx.util.pycompat import UnicodeMixin
|
|
from sphinx.util.docfields import Field, GroupedField
|
|
|
|
|
|
_identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b')
|
|
_whitespace_re = re.compile(r'\s+(?u)')
|
|
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
|
|
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
|
_visibility_re = re.compile(r'\b(public|private|protected)\b')
|
|
_array_def_re = re.compile(r'\[\s*([^\]]+?)?\s*\]')
|
|
_template_arg_re = re.compile(r'(%s)|([^,>]+)' % _string_re.pattern, re.S)
|
|
_operator_re = re.compile(r'''(?x)
|
|
\[\s*\]
|
|
| \(\s*\)
|
|
| \+\+ | --
|
|
| ->\*? | \,
|
|
| (<<|>>)=? | && | \|\|
|
|
| [!<>=/*%+|&^~-]=?
|
|
''')
|
|
|
|
_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(UnicodeMixin, Exception):
|
|
def __init__(self, description):
|
|
self.description = description
|
|
|
|
def __unicode__(self):
|
|
return self.description
|
|
|
|
|
|
class ASTBase(UnicodeMixin):
|
|
def __eq__(self, other):
|
|
if type(self) is not type(other):
|
|
return False
|
|
try:
|
|
for key, value in iteritems(self.__dict__):
|
|
if value != getattr(other, key):
|
|
return False
|
|
except AttributeError:
|
|
return False
|
|
return True
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
|
|
__hash__ = None
|
|
|
|
def clone(self):
|
|
"""Clone a definition expression node."""
|
|
return deepcopy(self)
|
|
|
|
def get_id(self):
|
|
"""Return the id for the node."""
|
|
raise NotImplementedError(repr(self))
|
|
|
|
def get_name(self):
|
|
"""Return the name.
|
|
|
|
Returns either `None` or a node with a name you might call
|
|
:meth:`split_owner` on.
|
|
"""
|
|
raise NotImplementedError(repr(self))
|
|
|
|
def prefix_nested_name(self, prefix):
|
|
"""Prefix a name node (a node returned by :meth:`get_name`)."""
|
|
raise NotImplementedError(repr(self))
|
|
|
|
def __unicode__(self):
|
|
raise NotImplementedError(repr(self))
|
|
|
|
def __repr__(self):
|
|
return '<%s %s>' % (self.__class__.__name__, self)
|
|
|
|
|
|
def _verify_description_mode(mode):
|
|
if mode not in ('lastIsName', 'noneIsName', 'markType', 'param'):
|
|
raise Exception("Description mode '%s' is invalid." % mode)
|
|
|
|
|
|
class ASTOperatorBuildIn(ASTBase):
|
|
def __init__(self, op):
|
|
self.op = op
|
|
|
|
def get_id(self):
|
|
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):
|
|
if self.op in ('new', 'new[]', 'delete', 'delete[]'):
|
|
return u'operator ' + self.op
|
|
else:
|
|
return u'operator' + self.op
|
|
|
|
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)
|
|
|
|
|
|
class ASTOperatorType(ASTBase):
|
|
def __init__(self, type):
|
|
self.type = type
|
|
|
|
def __unicode__(self):
|
|
return u''.join(['operator ', text_type(self.type)])
|
|
|
|
def get_id(self):
|
|
return u'cv' + self.type.get_id()
|
|
|
|
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)
|
|
|
|
|
|
class ASTTemplateArgConstant(ASTBase):
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __unicode__(self):
|
|
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'
|
|
|
|
def describe_signature(self, signode, mode, env):
|
|
_verify_description_mode(mode)
|
|
signode += nodes.Text(text_type(self))
|
|
|
|
|
|
class ASTNestedNameElement(ASTBase):
|
|
def __init__(self, identifier, templateArgs):
|
|
self.identifier = identifier
|
|
self.templateArgs = templateArgs
|
|
|
|
def get_id(self):
|
|
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):
|
|
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
|
|
|
|
@property
|
|
def name(self):
|
|
return self
|
|
|
|
def get_id(self):
|
|
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 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
|
|
|
|
def __unicode__(self):
|
|
return self.name
|
|
|
|
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 describe_signature(self, signode, mode, env):
|
|
signode += nodes.Text(text_type(self.name))
|
|
|
|
|
|
class ASTTrailingTypeSpecName(ASTBase):
|
|
def __init__(self, prefix, nestedName):
|
|
self.prefix = prefix
|
|
self.nestedName = nestedName
|
|
|
|
@property
|
|
def name(self):
|
|
return self.nestedName
|
|
|
|
def get_id(self):
|
|
return self.nestedName.get_id()
|
|
|
|
def __unicode__(self):
|
|
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 ASTFunctinoParameter(ASTBase):
|
|
def __init__(self, arg, ellipsis=False):
|
|
self.arg = arg
|
|
self.ellipsis = ellipsis
|
|
|
|
def get_id(self):
|
|
if self.ellipsis:
|
|
return 'z'
|
|
else:
|
|
return self.arg.get_id()
|
|
|
|
def __unicode__(self):
|
|
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 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):
|
|
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 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):
|
|
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):
|
|
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 = []
|
|
|
|
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)
|
|
|
|
|
|
class ASTPtrOpPtr(ASTBase):
|
|
def __init__(self, volatile, const):
|
|
self.volatile = volatile
|
|
self.const = const
|
|
|
|
def __unicode__(self):
|
|
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 ASTPtrOpRef(ASTBase):
|
|
def __unicode__(self):
|
|
return '&'
|
|
|
|
def get_id(self):
|
|
return 'R'
|
|
|
|
|
|
class ASTPtrOpParamPack(ASTBase):
|
|
def __unicode__(self):
|
|
return '...'
|
|
|
|
def get_id(self):
|
|
return 'Dp'
|
|
|
|
|
|
class ASTArray(ASTBase):
|
|
def __init__(self, size):
|
|
self.size = size
|
|
|
|
def __unicode__(self):
|
|
return u''.join(['[', text_type(self.size), ']'])
|
|
|
|
def get_id(self):
|
|
# TODO: this should maybe be done differently
|
|
return u'A' + text_type(self.size) + u'_'
|
|
|
|
def describe_signature(self, signode, mode, env):
|
|
_verify_description_mode(mode)
|
|
signode += nodes.Text(text_type(self))
|
|
|
|
|
|
class ASTDeclerator(ASTBase):
|
|
def __init__(self, ptrOps, declId, suffixOps):
|
|
self.ptrOps = ptrOps
|
|
self.declId = declId
|
|
self.suffixOps = suffixOps
|
|
|
|
@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_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 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):
|
|
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):
|
|
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 ASTTypeWithInit(ASTBase):
|
|
def __init__(self, type, init):
|
|
self.objectType = None
|
|
self.type = type
|
|
self.init = init
|
|
|
|
@property
|
|
def name(self):
|
|
return self.type.name
|
|
|
|
def get_id(self):
|
|
if self.objectType == 'member':
|
|
return _id_prefix + self.prefixedName.get_id()
|
|
else:
|
|
return self.type.get_id()
|
|
|
|
def __unicode__(self):
|
|
res = []
|
|
res.append(text_type(self.type))
|
|
if self.init:
|
|
res.append(text_type(self.init))
|
|
return u''.join(res)
|
|
|
|
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)
|
|
|
|
|
|
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 ASTClass(ASTBase):
|
|
def __init__(self, name, bases):
|
|
self.name = name
|
|
self.bases = bases
|
|
|
|
def get_id(self):
|
|
return _id_prefix + self.prefixedName.get_id()
|
|
|
|
def __unicode__(self):
|
|
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'
|
|
)
|
|
|
|
_prefix_keys = ('class', 'struct', 'union', 'typename')
|
|
|
|
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):
|
|
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)
|
|
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_word(self, word):
|
|
return self.match(re.compile(r'\b%s\b' % re.escape(word)))
|
|
|
|
def skip_ws(self):
|
|
return self.match(_whitespace_re)
|
|
|
|
def skip_word_and_ws(self, word):
|
|
if self.skip_word(word):
|
|
self.skip_ws()
|
|
return True
|
|
return False
|
|
|
|
@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 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 ASTOperatorBuildIn(self.matched_text)
|
|
|
|
# new/delete operator?
|
|
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 "]" 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 ASTOperatorType(type)
|
|
|
|
def _parse_nested_name(self):
|
|
names = []
|
|
|
|
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):
|
|
self.fail("expected identifier")
|
|
identifier = self.matched_text
|
|
if identifier == 'operator':
|
|
op = self._parse_operator()
|
|
names.append(op)
|
|
else:
|
|
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))
|
|
|
|
self.skip_ws()
|
|
if not self.skip_string('::'):
|
|
break
|
|
return ASTNestedName(names)
|
|
|
|
def _parse_trailing_type_spec(self):
|
|
# fundemental types
|
|
self.skip_ws()
|
|
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:
|
|
if self.skip_word_and_ws('short'):
|
|
elements.append('short')
|
|
elif self.skip_word_and_ws('long'):
|
|
elements.append('long')
|
|
else:
|
|
break
|
|
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')
|
|
|
|
# prefixed
|
|
prefix = None
|
|
self.skip_ws()
|
|
for k in self._prefix_keys:
|
|
if self.skip_word_and_ws(k):
|
|
prefix = k
|
|
break
|
|
|
|
nestedName = self._parse_nested_name()
|
|
return ASTTrailingTypeSpecName(prefix, nestedName)
|
|
|
|
def _parse_parameters_and_qualifiers(self, paramMode):
|
|
self.skip_ws()
|
|
if not self.skip_string('('):
|
|
if paramMode == 'function':
|
|
self.fail('Expecting "(" in parameters_and_qualifiers.')
|
|
else:
|
|
return None
|
|
args = []
|
|
self.skip_ws()
|
|
if not self.skip_string(')'):
|
|
while 1:
|
|
self.skip_ws()
|
|
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:
|
|
arg = self._parse_type()
|
|
# TODO: parse default parameters
|
|
args.append(ASTFunctinoParameter(arg))
|
|
|
|
self.skip_ws()
|
|
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()
|
|
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('&&'):
|
|
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.')
|
|
|
|
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()
|
|
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))
|
|
|
|
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:
|
|
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
|
|
|
|
if named == 'maybe':
|
|
try:
|
|
declId = self._parse_nested_name()
|
|
except DefinitionError:
|
|
declId = None
|
|
elif named:
|
|
declId = self._parse_nested_name()
|
|
else:
|
|
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
|
|
|
|
return ASTDeclerator(ptrOps, declId, suffixOpts)
|
|
|
|
def _parse_initializer(self, outer=None):
|
|
self.skip_ws()
|
|
# TODO: support paren and brace initialization for memberObject
|
|
if not self.skip_string('='):
|
|
return None
|
|
else:
|
|
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_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(':'):
|
|
while 1:
|
|
self.skip_ws()
|
|
visibility = 'private'
|
|
if self.match(_visibility_re):
|
|
visibility = self.matched_text
|
|
baseName = self._parse_nested_name()
|
|
bases.append(ASTBaseClass(baseName, visibility))
|
|
self.skip_ws()
|
|
if self.skip_string(','):
|
|
continue
|
|
else:
|
|
break
|
|
return ASTClass(name, bases)
|
|
|
|
def parse_type_object(self):
|
|
res = self._parse_type(outer='type')
|
|
res.objectType = 'type'
|
|
return res
|
|
|
|
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):
|
|
"""Description of a C++ language object."""
|
|
|
|
doc_field_types = [
|
|
GroupedField('parameter', label=l_('Parameters'),
|
|
names=('param', 'parameter', 'arg', 'argument'),
|
|
can_collapse=True),
|
|
GroupedField('exceptions', label=l_('Throws'), rolename='cpp:class',
|
|
names=('throws', 'throw', 'exception'),
|
|
can_collapse=True),
|
|
Field('returnvalue', label=l_('Returns'), has_arg=False,
|
|
names=('returns', 'return')),
|
|
]
|
|
|
|
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:
|
|
# 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)
|
|
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 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, ast):
|
|
raise NotImplementedError()
|
|
|
|
def handle_signature(self, sig, signode):
|
|
parser = DefinitionParser(sig)
|
|
try:
|
|
ast = self.parse_definition(parser)
|
|
parser.assert_end()
|
|
except DefinitionError as e:
|
|
self.state_machine.reporter.warning(e.description,
|
|
line=self.lineno)
|
|
raise ValueError
|
|
self.describe_signature(signode, ast)
|
|
|
|
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):
|
|
return _('%s (C++ type)') % name
|
|
|
|
def parse_definition(self, parser):
|
|
return parser.parse_type_object()
|
|
|
|
def describe_signature(self, signode, ast):
|
|
signode += addnodes.desc_annotation('type ', 'type ')
|
|
ast.describe_signature(signode, 'lastIsName', self.env)
|
|
|
|
|
|
class CPPMemberObject(CPPObject):
|
|
def get_index_text(self, name):
|
|
return _('%s (C++ member)') % name
|
|
|
|
def parse_definition(self, parser):
|
|
return parser.parse_member_object()
|
|
|
|
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 parse_definition(self, parser):
|
|
return parser.parse_function_object()
|
|
|
|
def describe_signature(self, signode, ast):
|
|
ast.describe_signature(signode, 'lastIsName', self.env)
|
|
|
|
|
|
class CPPClassObject(CPPObject):
|
|
def get_index_text(self, 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_class_object()
|
|
|
|
def describe_signature(self, signode, ast):
|
|
signode += addnodes.desc_annotation('class ', 'class ')
|
|
ast.describe_signature(signode, 'lastIsName', self.env)
|
|
|
|
|
|
class CPPNamespaceObject(Directive):
|
|
"""
|
|
This directive is just to tell Sphinx that we're documenting stuff in
|
|
namespace foo.
|
|
"""
|
|
|
|
has_content = False
|
|
required_arguments = 1
|
|
optional_arguments = 0
|
|
final_argument_whitespace = True
|
|
option_spec = {}
|
|
|
|
def run(self):
|
|
env = self.state.document.settings.env
|
|
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
|
|
env.ref_context['cpp:parent'] = []
|
|
else:
|
|
parser = DefinitionParser(self.arguments[0])
|
|
try:
|
|
prefix = parser.parse_namespace_object()
|
|
parser.assert_end()
|
|
except DefinitionError as e:
|
|
self.state_machine.reporter.warning(e.description,
|
|
line=self.lineno)
|
|
else:
|
|
env.ref_context['cpp:parent'] = [prefix]
|
|
return []
|
|
|
|
|
|
class CPPXRefRole(XRefRole):
|
|
def process_link(self, env, refnode, has_explicit_title, title, target):
|
|
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
|
|
# if the first character is a tilde, don't display the module/class
|
|
# parts of the contents
|
|
if title[:1] == '~':
|
|
title = title[1:]
|
|
dcolon = title.rfind('::')
|
|
if dcolon != -1:
|
|
title = title[dcolon + 2:]
|
|
return title, target
|
|
|
|
|
|
class CPPDomain(Domain):
|
|
"""C++ language domain."""
|
|
name = 'cpp'
|
|
label = 'C++'
|
|
object_types = {
|
|
'class': ObjType(l_('class'), 'class'),
|
|
'function': ObjType(l_('function'), 'func'),
|
|
'member': ObjType(l_('member'), 'member'),
|
|
'type': ObjType(l_('type'), 'type')
|
|
}
|
|
|
|
directives = {
|
|
'class': CPPClassObject,
|
|
'function': CPPFunctionObject,
|
|
'member': CPPMemberObject,
|
|
'type': CPPTypeObject,
|
|
'namespace': CPPNamespaceObject
|
|
}
|
|
roles = {
|
|
'class': CPPXRefRole(),
|
|
'func': CPPXRefRole(fix_parens=True),
|
|
'member': CPPXRefRole(),
|
|
'type': CPPXRefRole()
|
|
}
|
|
initial_data = {
|
|
'objects': {}, # prefixedName -> (docname, objectType, id)
|
|
}
|
|
|
|
def clear_doc(self, docname):
|
|
for fullname, data in list(self.data['objects'].items()):
|
|
if data[0] == docname:
|
|
del self.data['objects'][fullname]
|
|
|
|
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']:
|
|
# 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:
|
|
nameAst = parser.parse_xref_object().name
|
|
parser.skip_ws()
|
|
if not parser.eof:
|
|
raise DefinitionError('')
|
|
except DefinitionError:
|
|
if warn:
|
|
env.warn_node('unparseable C++ definition: %r' % target, node)
|
|
return None, None
|
|
|
|
# try as is the name is fully qualified
|
|
res = _create_refnode(nameAst)
|
|
if res[0]:
|
|
return res
|
|
|
|
# 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
|
|
|
|
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 iteritems(self.data['objects']):
|
|
yield (refname, refname, type, docname, refname, 1)
|