#!/usr/bin/env python # -*- coding: utf-8 -*- """ ocv domain, a modified copy of sphinx.domains.cpp + shpinx.domains.python. The original copyright is below ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The OpenCV C/C++/Python/Java/... language domain. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ import re from copy import deepcopy from docutils import nodes from docutils.parsers.rst import directives 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.docfields import Field, GroupedField, TypedField ########################### Python Part ########################### # REs for Python signatures py_sig_re = re.compile( r'''^ ([\w.]*\.)? # class name(s) (\w+) \s* # thing name (?: \((.*)\) # optional: arguments (?:\s* -> \s* (.*))? # return annotation )? $ # and nothing more ''', re.VERBOSE) def _pseudo_parse_arglist(signode, arglist): """"Parse" a list of arguments separated by commas. Arguments can have "optional" annotations given by enclosing them in brackets. Currently, this will split at any comma, even if it's inside a string literal (e.g. default argument value). """ paramlist = addnodes.desc_parameterlist() stack = [paramlist] try: for argument in arglist.split(','): argument = argument.strip() ends_open = ends_close = 0 while argument.startswith('['): stack.append(addnodes.desc_optional()) stack[-2] += stack[-1] argument = argument[1:].strip() while argument.startswith(']'): stack.pop() argument = argument[1:].strip() while argument.endswith(']'): ends_close += 1 argument = argument[:-1].strip() while argument.endswith('['): ends_open += 1 argument = argument[:-1].strip() if argument: stack[-1] += addnodes.desc_parameter(argument, argument, noemph=True) while ends_open: stack.append(addnodes.desc_optional()) stack[-2] += stack[-1] ends_open -= 1 while ends_close: stack.pop() ends_close -= 1 if len(stack) != 1: raise IndexError except IndexError: # if there are too few or too many elements on the stack, just give up # and treat the whole argument list as one argument, discarding the # already partially populated paramlist node signode += addnodes.desc_parameterlist() signode[-1] += addnodes.desc_parameter(arglist, arglist) else: signode += paramlist class OCVPyObject(ObjectDescription): """ Description of a general Python object. """ option_spec = { 'noindex': directives.flag, 'module': directives.unchanged, } doc_field_types = [ TypedField('parameter', label=l_('Parameters'), names=('param', 'parameter', 'arg', 'argument', 'keyword', 'kwarg', 'kwparam'), typerolename='obj', typenames=('paramtype', 'type'), can_collapse=True), TypedField('variable', label=l_('Variables'), rolename='obj', names=('var', 'ivar', 'cvar'), typerolename='obj', typenames=('vartype',), can_collapse=True), GroupedField('exceptions', label=l_('Raises'), rolename='exc', names=('raises', 'raise', 'exception', 'except'), can_collapse=True), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=l_('Return type'), has_arg=False, names=('rtype',)), ] def get_signature_prefix(self, sig): """ May return a prefix to put before the object name in the signature. """ return '' def needs_arglist(self): """ May return true if an empty argument list is to be generated even if the document contains none. """ return False def handle_signature(self, sig, signode): """ Transform a Python signature into RST nodes. Returns (fully qualified name of the thing, classname if any). If inside a class, the current class name is handled intelligently: * it is stripped from the displayed name if present * it is added to the full name (return value) if not present """ signode += nodes.strong("Python:", "Python:") signode += addnodes.desc_name(" ", " ") m = py_sig_re.match(sig) if m is None: raise ValueError name_prefix, name, arglist, retann = m.groups() # determine module and class name (if applicable), as well as full name modname = self.options.get( 'module', self.env.temp_data.get('py:module')) classname = self.env.temp_data.get('py:class') if classname: add_module = False if name_prefix and name_prefix.startswith(classname): fullname = name_prefix + name # class name is given again in the signature name_prefix = name_prefix[len(classname):].lstrip('.') elif name_prefix: # class name is given in the signature, but different # (shouldn't happen) fullname = classname + '.' + name_prefix + name else: # class name is not given in the signature fullname = classname + '.' + name else: add_module = True if name_prefix: classname = name_prefix.rstrip('.') fullname = name_prefix + name else: classname = '' fullname = name signode['module'] = modname signode['class'] = classname signode['fullname'] = fullname sig_prefix = self.get_signature_prefix(sig) if sig_prefix: signode += addnodes.desc_annotation(sig_prefix, sig_prefix) if name_prefix: signode += addnodes.desc_addname(name_prefix, name_prefix) # exceptions are a special case, since they are documented in the # 'exceptions' module. elif add_module and self.env.config.add_module_names: modname = self.options.get( 'module', self.env.temp_data.get('py:module')) if modname and modname != 'exceptions': nodetext = modname + '.' signode += addnodes.desc_addname(nodetext, nodetext) signode += addnodes.desc_name(name, name) if not arglist: if self.needs_arglist(): # for callables, add an empty parameter list signode += addnodes.desc_parameterlist() if retann: signode += addnodes.desc_returns(retann, retann) return fullname, name_prefix _pseudo_parse_arglist(signode, arglist) if retann: signode += addnodes.desc_returns(retann, retann) return fullname, name_prefix def get_index_text(self, modname, name): """ Return the text for the index entry of the object. """ raise NotImplementedError('must be implemented in subclasses') def add_target_and_index(self, name_cls, sig, signode): modname = self.options.get( 'module', self.env.temp_data.get('py:module')) fullname = (modname and modname + '.' or '') + name_cls[0] # note target if fullname not in self.state.document.ids: signode['names'].append(fullname) signode['ids'].append(fullname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) objects = self.env.domaindata['ocv']['objects'] if fullname in objects: self.env.warn( self.env.docname, 'duplicate object description of %s, ' % fullname + 'other instance in ' + self.env.doc2path(objects[fullname][0]) + ', use :noindex: for one of them', self.lineno) objects.setdefault(fullname, (self.env.docname, self.objtype, name_cls[0])) indextext = self.get_index_text(modname, name_cls) if indextext: self.indexnode['entries'].append(('single', indextext, fullname, fullname)) def before_content(self): # needed for automatic qualification of members (reset in subclasses) self.clsname_set = False def after_content(self): if self.clsname_set: self.env.temp_data['py:class'] = None class OCVPyModulelevel(OCVPyObject): """ Description of an object on module level (functions, data). """ directive_prefix = 'py' def needs_arglist(self): return self.objtype == self.__class__.directive_prefix + 'function' def get_index_text(self, modname, name_cls): if self.objtype == self.__class__.directive_prefix + 'function': if not modname: fname = name_cls[0] if not fname.startswith("cv") and not fname.startswith("cv2"): return _('%s() (Python function)') % fname pos = fname.find(".") modname = fname[:pos] fname = fname[pos+1:] return _('%s() (Python function in %s)') % (fname, modname) return _('%s() (Python function in %s)') % (name_cls[0], modname) elif self.objtype == 'pydata': if not modname: return _('%s (Python variable)') % name_cls[0] return _('%s (in module %s)') % (name_cls[0], modname) else: return '' class OCVPyXRefRole(XRefRole): def process_link(self, env, refnode, has_explicit_title, title, target): refnode['ocv:module'] = env.temp_data.get('ocv:module') refnode['ocv:class'] = env.temp_data.get('ocv:class') if not has_explicit_title: title = title.lstrip('.') # only has a meaning for the target 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[0:1] == '~': title = title[1:] dot = title.rfind('.') if dot != -1: title = title[dot+1:] # if the first character is a dot, search more specific namespaces first # else search builtins first if target[0:1] == '.': target = target[1:] refnode['refspecific'] = True return title, target ########################### C/C++/Java Part ########################### _identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*\b)') _argument_name_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*\b(?:\[\d*\])?|\.\.\.)') _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') _operator_re = re.compile(r'''(?x) \[\s*\] | \(\s*\) | (<<|>>)=? | \+\+ | -- | ->\*? | [!<>=/*%+|&^-]=? | ~ | && | \| | \|\| | \, ''') _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' } class DefinitionError(Exception): def __init__(self, description): self.description = description def __unicode__(self): return self.description def __str__(self): return unicode(self.encode('utf-8')) class DefExpr(object): def __unicode__(self): raise NotImplementedError() def __eq__(self, other): if type(self) is not type(other): return False try: for key, value in self.__dict__.iteritems(): if value != getattr(other, value): return False except AttributeError: return False return True def __ne__(self, other): return not self.__eq__(other) def clone(self): """Close a definition expression node""" return deepcopy(self) def get_id(self): """Returns the id for the node""" return u'' def get_name(self): """Returns the name. Returns either `None` or a node with a name you might call :meth:`split_owner` on. """ return None 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): """Prefixes a name node (a node returned by :meth:`get_name`).""" raise NotImplementedError() def __str__(self): return unicode(self).encode('utf-8') 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]) class NameDefExpr(PrimaryDefExpr): def __init__(self, name): self.name = name def get_id(self): name = _id_shortwords.get(self.name) if name is not None: return name return self.name.replace(u' ', u'-') def __unicode__(self): return unicode(self.name) class PathDefExpr(PrimaryDefExpr): def __init__(self, parts): self.path = parts def get_id(self): rv = u'::'.join(x.get_id() for x in self.path) return _id_shortwords.get(rv, rv) 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) def __unicode__(self): return u'::'.join(map(unicode, self.path)) class TemplateDefExpr(PrimaryDefExpr): 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) def get_id(self): return u'%s:%s:' % (self.typename.get_id(), u'.'.join(x.get_id() for x in self.args)) def __unicode__(self): return u'%s<%s>' % (self.typename, u', '.join(map(unicode, self.args))) class WrappingDefExpr(DefExpr): def __init__(self, typename): self.typename = typename def get_name(self): return self.typename.get_name() class ModifierDefExpr(WrappingDefExpr): def __init__(self, typename, modifiers): WrappingDefExpr.__init__(self, typename) self.modifiers = modifiers 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) def __unicode__(self): return u' '.join(map(unicode, list(self.modifiers) + [self.typename])) class PtrDefExpr(WrappingDefExpr): def get_id(self): return self.typename.get_id() + u'P' def __unicode__(self): return u'%s*' % self.typename class RefDefExpr(WrappingDefExpr): def get_id(self): return self.typename.get_id() + u'R' def __unicode__(self): return u'%s&' % self.typename class ConstDefExpr(WrappingDefExpr): def __init__(self, typename, prefix=False): WrappingDefExpr.__init__(self, typename) self.prefix = prefix def get_id(self): return self.typename.get_id() + u'C' def __unicode__(self): return (self.prefix and u'const %s' or u'%s const') % self.typename class ConstTemplateDefExpr(WrappingDefExpr): def __init__(self, typename, prefix=False): WrappingDefExpr.__init__(self, typename) self.prefix = prefix def get_id(self): return self.typename.get_id() + u'C' def __unicode__(self): return (self.prefix and u'const %s' or u'%s const') % self.typename class CastOpDefExpr(PrimaryDefExpr): def __init__(self, typename): self.typename = typename def get_id(self): return u'castto-%s-operator' % self.typename.get_id() def __unicode__(self): return u'operator %s' % self.typename class ArgumentDefExpr(DefExpr): def __init__(self, type, name, default=None): self.name = name self.type = type self.default = default def get_name(self): return self.name.get_name() def get_id(self): if self.type is None: return 'X' return self.type.get_id() def __unicode__(self): return (u'%s %s' % (self.type or u'', self.name or u'')).strip() + \ (self.default is not None and u'=%s' % self.default or u'') class NamedDefExpr(DefExpr): def __init__(self, name, visibility, static): self.name = name self.visibility = visibility self.static = static def get_name(self): return self.name.get_name() def get_modifiers(self): rv = [] if self.visibility != 'public': rv.append(self.visibility) if self.static: rv.append(u'static') return rv class TypeObjDefExpr(NamedDefExpr): def __init__(self, name, visibility, static, typename): NamedDefExpr.__init__(self, name, visibility, static) self.typename = typename def get_id(self): if self.typename is None: return self.name.get_id() return u'%s__%s' % (self.name.get_id(), self.typename.get_id()) 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))) return u' '.join(buf) class MemberObjDefExpr(NamedDefExpr): def __init__(self, name, visibility, static, typename, value): NamedDefExpr.__init__(self, name, visibility, static) self.typename = typename self.value = value def get_id(self): return u'%s__%s' % (self.name.get_id(), self.typename.get_id()) def __unicode__(self): buf = self.get_modifiers() buf.append(u'%s %s' % (self.typename, self.name)) 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, rv, signature, const, pure_virtual, virtual): NamedDefExpr.__init__(self, name, visibility, static) self.rv = rv self.signature = signature self.explicit = explicit self.const = const self.pure_virtual = pure_virtual self.virtual = virtual def get_id(self): return u'%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'' ) def __unicode__(self): buf = self.get_modifiers() if self.explicit: buf.append(u'explicit') if self.virtual: buf.append(u'virtual') 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.pure_virtual: buf.append(u'= 0') return u' '.join(buf) class ClassDefExpr(NamedDefExpr): def __init__(self, name, visibility, static, parents = None): NamedDefExpr.__init__(self, name, visibility, static) self.parents = parents def get_id(self): return self.name.get_id() def __unicode__(self): buf = self.get_modifiers() buf.append(unicode(self.name)) return u' '.join(buf) class DefinitionParser(object): # 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, 'unsigned': set(('char', 'short', 'int', 'long')), 'signed': set(('char', 'short', 'int', 'long')), 'short': set(('int',)), 'long': set(('int', 'long', 'double')) } def __init__(self, definition): self.definition = definition.strip() self.pos = 0 self.end = len(self.definition) self.last_match = None self._previous_state = (0, None) def fail(self, msg): raise DefinitionError('Invalid definition: %s [error at %d]\n %s' % (msg, self.pos, self.definition)) 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) @property def eof(self): return self.pos >= self.end @property def current_char(self): try: return self.definition[self.pos] except IndexError: return 'EOF' @property def matched_text(self): if self.last_match is not None: return self.last_match.group() def _parse_operator(self): self.skip_ws() # thank god, a regular operator definition if self.match(_operator_re): return NameDefExpr('operator' + _whitespace_re.sub('', self.matched_text)) # new/delete operator? for allocop in 'new', 'delete': if not self.skip_word(allocop): 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) # 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): if not self.match(_argument_name_re): self.fail('expected name') 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('&'): expr = RefDefExpr(expr) else: return expr def _peek_const(self, path): try: path.remove('const') return True except ValueError: return False def _parse_builtin(self, modifier): path = [modifier] following = self._modifiers[modifier] while 1: self.skip_ws() if not self.match(_identifier_re): break identifier = self.matched_text if identifier in following: path.append(identifier) following = self._modifiers[modifier] assert following 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): typename = self._parse_name() if typename and self.skip_string('['): typename.name += '[' if self.match(re.compile(r'\d*')): typename.name += self.last_match.group(0) typename.name += ']' if not self.skip_string(']'): self.fail('expected type') self.skip_ws() if not self.skip_string('<'): return typename args = [] while 1: self.skip_ws() if self.skip_string('>'): break if args: if not self.skip_string(','): self.fail('"," or ">" in template expected') self.skip_ws() args.append(self._parse_type(True)) return TemplateDefExpr(typename, args) def _parse_type(self, in_template=False): self.skip_ws() result = [] modifiers = [] if self.match(re.compile(r'template\w*<([^>]*)>')): args = self.last_match.group(1).split(',') args = [a.strip() for a in args] modifiers.append(TemplateDefExpr('template', args)) # 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 if following is not None: return self._parse_builtin(modifier) modifiers.append(modifier) else: self.backout() break while 1: self.skip_ws() if (in_template and self.current_char in ',>') or \ (result and not self.skip_string('::')) or \ self.eof: break result.append(self._parse_type_expr()) if not result: self.fail('expected type') if len(result) == 1: rv = result[0] else: rv = PathDefExpr(result) is_const = self._peek_const(modifiers) if is_const: rv = ConstDefExpr(rv, prefix=True) if modifiers: rv = ModifierDefExpr(rv, modifiers) return self._attach_crefptr(rv, False) def _parse_default_expr(self): 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: break self.pos = idx+1 rv = self.definition[rv_start:idx] self.pos = idx return rv def _parse_signature(self): if r'CvStatModel::train' in self.definition: # hack to skip parsing of problematic definition self.pos = self.end return [ArgumentDefExpr("const Mat&", "train_data", None), ArgumentDefExpr(None, self.definition[self.definition.find("["):-1], None)], False, True self.skip_ws() if not self.skip_string('('): self.fail('expected parentheses for function') args = [] while 1: self.skip_ws() if self.eof: self.fail('missing closing parentheses') if self.skip_string(')'): break if args: if not self.skip_string(','): self.fail('expected comma between arguments') self.skip_ws() argtype = self._parse_type() self.skip_ws() if unicode(argtype) == u"...": if not self.skip_string(')'): self.fail("var arg must be the last argument") args.append(ArgumentDefExpr(None, argtype, None)) break argname = default = None if self.skip_string('='): self.pos += 1 default = self._parse_default_expr() elif self.current_char not in ',)': argname = self._parse_name() self.skip_ws() if self.skip_string('='): default = self._parse_default_expr() args.append(ArgumentDefExpr(argtype, argname, default)) self.skip_ws() const = self.skip_word('const') if const: self.skip_ws() if self.skip_string('='): self.skip_ws() if not (self.skip_string('0') or \ self.skip_word('NULL') or \ self.skip_word('nullptr')): self.fail('pure virtual functions must be defined with ' 'either 0, NULL or nullptr, other macros are ' 'not allowed') pure_virtual = True else: pure_virtual = False return args, const, pure_virtual def _parse_visibility_static(self): visibility = 'public' if self.match(_visibility_re): visibility = self.matched_text static = self.skip_word('static') return visibility, static def parse_type(self): return self._parse_type() 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() else: name = typename typename = None return TypeObjDefExpr(name, visibility, static, typename) def parse_member_object(self): visibility, static = self._parse_visibility_static() typename = self._parse_type() name = self._parse_type() self.skip_ws() if self.skip_string('='): value = self.read_rest().strip() else: value = None return MemberObjDefExpr(name, visibility, static, typename, value) def parse_enum_member_object(self): visibility, static = self._parse_visibility_static() typename = None name = self._parse_type() self.skip_ws() if self.skip_string('='): value = self.read_rest().strip() else: value = None return MemberObjDefExpr(name, visibility, static, typename, value) def parse_function(self): visibility, static = self._parse_visibility_static() if self.skip_word('explicit'): explicit = True self.skip_ws() else: explicit = False if self.skip_word('virtual'): virtual = True self.skip_ws() else: virtual = False rv = self._parse_type() self.skip_ws() # some things just don't have return values if self.current_char == '(': name = rv rv = None else: name = self._parse_type() return FuncDefExpr(name, visibility, static, explicit, rv, *self._parse_signature(), virtual = virtual) def parse_class(self): visibility, static = self._parse_visibility_static() typename = self._parse_type() parent = None self.skip_ws() parents = [] if self.skip_string(':'): while not self.eof: self.skip_ws() classname_pos = self.pos pvisibility, pstatic = self._parse_visibility_static() if pstatic: self.fail('unsepected static keyword, got %r' % self.definition[self.classname_pos:]) parents.append(ClassDefExpr(self._parse_type(), pvisibility, pstatic)) if not self.skip_string(','): break return ClassDefExpr(typename, visibility, static, parents) 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:]) class OCVObject(ObjectDescription): """Description of a C++ language object.""" langname = "C++" ismember = False doc_field_types = [ TypedField('parameter', label=l_('Parameters'), names=('param', 'parameter', 'arg', 'argument'), typerolename='type', typenames=('type',)), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=l_('Return type'), has_arg=False, names=('rtype',)), ] 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(self, node, type): # XXX: link to c? text = unicode(type) pnode = addnodes.pending_xref( '', refdomain='ocv', reftype='type', reftarget=text, modname=None, classname=None) pnode['ocv:parent'] = self.env.temp_data.get('ocv:parent') pnode += nodes.Text(text) node += pnode def attach_modifiers(self, node, obj): if not self.__class__.ismember: lname = self.__class__.langname node += nodes.strong(lname + ":", lname + ":") node += addnodes.desc_name(" ", " ") if obj.visibility != 'public': node += addnodes.desc_annotation(obj.visibility, obj.visibility) node += nodes.Text(' ') if obj.static: node += addnodes.desc_annotation('static', 'static') node += nodes.Text(' ') def add_target_and_index(self, sigobj, sig, signode): theid = sig#obj.get_id() theid = re.sub(r" +", " ", theid) if self.objtype == 'emember': theid = re.sub(r" ?=.*", "", theid) theid = re.sub(r"=[^,()]+\([^)]*?\)[^,)]*(,|\))", "\\1", theid) theid = re.sub(r"=\w*[^,)(]+(,|\))", "\\1", theid) theid = theid.replace("( ", "(").replace(" )", ")") name = unicode(sigobj.name) if theid not in self.state.document.ids: signode['names'].append(theid) signode['ids'].append(theid) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) #self.env.domaindata['ocv']['objects'].setdefault(name, #(self.env.docname, self.objtype, theid)) self.env.domaindata['ocv']['objects'].setdefault(theid, (self.env.docname, self.objtype, theid)) self.env.domaindata['ocv']['objects2'].setdefault(name, (self.env.docname, self.objtype, theid)) indextext = self.get_index_text(name) if indextext: self.indexnode['entries'].append(('single', indextext, theid, name)) def before_content(self): lastname = self.names and self.names[-1] if lastname and not self.env.temp_data.get('ocv:parent'): assert isinstance(lastname, NamedDefExpr) self.env.temp_data['ocv:parent'] = lastname.name self.parentname_set = True else: self.parentname_set = False def after_content(self): if self.parentname_set: self.env.temp_data['ocv:parent'] = None def parse_definition(self, parser): raise NotImplementedError() def describe_signature(self, signode, arg): raise NotImplementedError() def handle_signature(self, sig, signode): parser = DefinitionParser(sig) try: rv = self.parse_definition(parser) parser.assert_end() except DefinitionError, e: self.env.warn(self.env.docname, e.description, self.lineno) raise ValueError self.describe_signature(signode, rv) parent = self.env.temp_data.get('ocv:parent') if parent is not None: rv = rv.clone() rv.name = rv.name.prefix(parent) return rv class OCVClassObject(OCVObject): object_annotation = "class " object_long_name = "class" def attach_modifiers(self, node, obj, skip_visibility = 'public'): if obj.visibility != skip_visibility: node += addnodes.desc_annotation(obj.visibility, obj.visibility) node += nodes.Text(' ') if obj.static: node += addnodes.desc_annotation('static', 'static') node += nodes.Text(' ') def get_index_text(self, name): return _('%s (C++ %s)') % (name, self.__class__.object_long_name) def parse_definition(self, parser): return parser.parse_class() def describe_signature(self, signode, cls): self.attach_modifiers(signode, cls) signode += addnodes.desc_annotation(self.__class__.object_annotation, self.__class__.object_annotation) self.attach_name(signode, cls.name) first_parent = True for p in cls.parents: if first_parent: signode += nodes.Text(' : ') first_parent = False else: signode += nodes.Text(', ') self.attach_modifiers(signode, p, None) self.attach_name(signode, p.name) class OCVStructObject(OCVClassObject): object_annotation = "struct " object_long_name = "structure" class OCVTypeObject(OCVObject): def get_index_text(self, name): if self.objtype == 'type': return _('%s (C++ type)') % name return '' def parse_definition(self, parser): return parser.parse_type_object() def describe_signature(self, signode, obj): self.attach_modifiers(signode, obj) 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) class OCVEnumObject(OCVObject): def get_index_text(self, name): if self.objtype == 'enum': return _('%s (enum)') % name return '' def parse_definition(self, parser): return parser.parse_type_object() def describe_signature(self, signode, obj): self.attach_modifiers(signode, obj) signode += addnodes.desc_annotation('enum ', 'enum ') if obj.typename is not None: self.attach_type(signode, obj.typename) signode += nodes.Text(' ') self.attach_name(signode, obj.name) class OCVMemberObject(OCVObject): ismember = True def get_index_text(self, name): if self.objtype == 'member': return _('%s (C++ member)') % name return '' def parse_definition(self, parser): parent_class = self.env.temp_data.get('ocv:parent') if parent_class is None: parser.fail("missing parent structure/class") return parser.parse_member_object() def describe_signature(self, signode, obj): self.attach_modifiers(signode, obj) if obj.typename: self.attach_type(signode, obj.typename) signode += nodes.Text(' ') self.attach_name(signode, obj.name) if obj.value is not None: signode += nodes.Text(u' = ' + obj.value) class OCVEnumMemberObject(OCVMemberObject): def parse_definition(self, parser): # parent_class = self.env.temp_data.get('ocv:parent') # if parent_class is None: # parser.fail("missing parent structure/class") return parser.parse_enum_member_object() class OCVFunctionObject(OCVObject): 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) 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)) sbrIdx = unicode(arg.name).find("[") if sbrIdx < 0: param += nodes.strong(unicode(arg.name), unicode(arg.name)) else: param += nodes.strong(unicode(arg.name)[:sbrIdx], unicode(arg.name)[:sbrIdx]) param += nodes.Text(unicode(arg.name)[sbrIdx:]) if arg.default is not None: def_ = u'=' + unicode(arg.default) #param += nodes.emphasis(def_, def_) param += nodes.Text(def_) paramlist += param node += paramlist if func.const: node += addnodes.desc_addname(' const', ' const') if func.pure_virtual: node += addnodes.desc_addname(' = 0', ' = 0') def get_index_text(self, name): lname = self.__class__.langname if lname == "C" and name.startswith("cv"): name = name[2:] return _('%s (%s function)') % (name, lname) def parse_definition(self, parser): return parser.parse_function() def describe_signature(self, signode, func): self.attach_modifiers(signode, func) if func.explicit: signode += addnodes.desc_annotation('explicit', 'explicit') signode += nodes.Text(' ') if func.virtual: signode += addnodes.desc_annotation('virtual', 'virtual') 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) class OCVCurrentNamespace(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.temp_data['ocv:prefix'] = None else: parser = DefinitionParser(self.arguments[0]) try: prefix = parser.parse_type() parser.assert_end() except DefinitionError, e: self.env.warn(self.env.docname, e.description, self.lineno) else: env.temp_data['ocv:prefix'] = prefix return [] class OCVXRefRole(XRefRole): def process_link(self, env, refnode, has_explicit_title, title, target): refnode['ocv:parent'] = env.temp_data.get('ocv: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 OCVCFunctionObject(OCVFunctionObject): langname = "C" class OCVJavaFunctionObject(OCVFunctionObject): langname = "Java" class OCVDomain(Domain): """OpenCV C++ language domain.""" name = 'ocv' label = 'C++' object_types = { 'class': ObjType(l_('class'), 'class'), 'struct': ObjType(l_('struct'), 'struct'), 'function': ObjType(l_('function'), 'func', 'funcx'), 'cfunction': ObjType(l_('cfunction'), 'cfunc', 'cfuncx'), 'jfunction': ObjType(l_('jfunction'), 'jfunc', 'jfuncx'), 'pyfunction': ObjType(l_('pyfunction'), 'pyfunc'), 'member': ObjType(l_('member'), 'member'), 'emember': ObjType(l_('emember'), 'emember'), 'type': ObjType(l_('type'), 'type'), 'enum': ObjType(l_('enum'), 'enum') } directives = { 'class': OCVClassObject, 'struct': OCVStructObject, 'function': OCVFunctionObject, 'cfunction': OCVCFunctionObject, 'jfunction': OCVJavaFunctionObject, 'pyfunction': OCVPyModulelevel, 'member': OCVMemberObject, 'emember': OCVEnumMemberObject, 'type': OCVTypeObject, 'enum': OCVEnumObject, 'namespace': OCVCurrentNamespace } roles = { 'class': OCVXRefRole(), 'struct': OCVXRefRole(), 'func' : OCVXRefRole(fix_parens=True), 'funcx' : OCVXRefRole(), 'cfunc' : OCVXRefRole(fix_parens=True), 'cfuncx' : OCVXRefRole(), 'jfunc' : OCVXRefRole(fix_parens=True), 'jfuncx' : OCVXRefRole(), 'pyfunc' : OCVPyXRefRole(), 'member': OCVXRefRole(), 'emember': OCVXRefRole(), 'type': OCVXRefRole(), 'enum': OCVXRefRole() } initial_data = { 'objects': {}, # fullname -> docname, objtype } def __init__(self, env): Domain.__init__(self, env) self.data['objects2'] = {} def clear_doc(self, docname): for fullname, (fn, _, _) in self.data['objects'].items(): if fn == docname: del self.data['objects'][fullname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _create_refnode(expr): name = unicode(expr) if "type" in self.objtypes_for_role(typ): return None if "cfunction" in self.objtypes_for_role(typ): if not name.startswith(u'cv'): name = u'cv' + name dict = self.data['objects'] if name not in dict: dict = self.data['objects2'] if name not in dict: refdoc = node.get('refdoc', fromdocname) env.warn(refdoc, 'unresolved reference: %r - %r' % (target, typ), node.line) return None obj = dict[name] if obj[1] not in self.objtypes_for_role(typ): return None title = obj[2] if "class" in self.objtypes_for_role(typ): title = u"class " + title elif "struct" in self.objtypes_for_role(typ): title = u"struct " + title return make_refnode(builder, fromdocname, obj[0], obj[2], contnode, title) parser = DefinitionParser(target) try: expr = parser.parse_type().get_name() parser.skip_ws() if not parser.eof or expr is None: raise DefinitionError('') except DefinitionError: refdoc = node.get('refdoc', fromdocname) env.warn(refdoc, 'unparseable C++ definition: %r' % target, node.line) return None parent = node['ocv:parent'] rv = _create_refnode(expr) if rv is not None or parent is None: return rv parent = parent.get_name() rv = _create_refnode(expr.prefix(parent)) if rv is not None: return rv parent, name = parent.split_owner() return _create_refnode(expr.prefix(parent)) def get_objects(self): for refname, (docname, type, theid) in self.data['objects'].iteritems(): yield (refname, refname, type, docname, refname, 1) def get_type_name(self, type, primary=False): """ Return full name for given ObjType. """ if primary: return type.lname return { 'class': _('C++ class'), 'struct': _('C/C++ struct'), 'function': _('C++ function'), 'cfunction': _('C function'), 'jfunction': _('Java method'), 'pyfunction': _('Python function'), 'member': _('C++ member'), 'emember': _('enum member'), 'type': _('C/C++ type'), 'enum': _('C/C++ enum'), 'namespace': _('C++ namespace'), }.get(type.lname, _('%s %s') % (self.label, type.lname)) def setup(app): app.add_domain(OCVDomain)