]> rtime.felk.cvut.cz Git - opencv.git/blobdiff - opencv/doc/latex2sphinx/latex.py
new test category python_fragments from documentation
[opencv.git] / opencv / doc / latex2sphinx / latex.py
index a551ee8af178d524c1e94dce59e579e044ca3676..3edbb2cf3faf7f3dc4fe8bb17672adf4c1aff77e 100644 (file)
@@ -6,76 +6,51 @@ import cPickle as pickle
 import pyparsing as pp
 import StringIO
 from qfile import QOpen
+from string import Template
+
+# useful things for pyparsing
+def returnList(x):
+    def listify(s, loc, toks):
+        return [toks]
+    x.setParseAction(listify)
+    return x
+def returnTuple(x):
+    def listify(s, loc, toks):
+        return [tuple(toks)]
+    x.setParseAction(listify)
+    return x
+def CommaList(word):
+    return returnList(pp.Optional(word + pp.ZeroOrMore(pp.Suppress(',') + word)))
+def sl(s):
+    return pp.Suppress(pp.Literal(s))
 
 import pythonapi
 
 python_api = pythonapi.reader("../../interfaces/python/api")
 
-sources = ['../' + f for f in os.listdir('..') if f.endswith('.tex')]
-if distutils.dep_util.newer_group(["latexparser.py"] + sources, "pickled"):
-    fulldoc = latexparser(sys.argv[1], 0)
-    pickle.dump(fulldoc, open("pickled", 'wb'))
-    raw = open('fulldoc', 'w')
-    for x in fulldoc:
-        print >>raw, repr(x)
-    raw.close()
-else:
-    fulldoc = pickle.load(open("pickled", "rb"))
-
-# Filter on target language
-def preprocess_conditionals(fd, conditionals):
-    r = []
-    ifstack = []
-    for x in fd:
-        if isinstance(x, TexCmd):
-            ll = x.cmd.rstrip()
-            loc = (x.filename, x.lineno)
-            if ll.startswith("if"):
-                # print " " * len(ifstack), '{', loc
-                ifstack.append((conditionals.get(ll[2:], False), loc))
-            elif ll.startswith("else"):
-                ifstack[-1] = (not ifstack[-1][0], ifstack[-1][1])
-            elif ll.startswith("fi"):
-                ifstack.pop()
-                # print " " * len(ifstack), '}', loc
-            elif not False in [p for (p,_) in ifstack]:
-                r.append(x)
-        else:
-            if not False in [p for (p,_) in ifstack]:
-                r.append(x)
-    if ifstack != []:
-        print "unterminated if", ifstack
-        sys.exit(0)
-    return r
-
-language = 'py'
-doc = preprocess_conditionals(fulldoc, {
-                                      'C' : language=='c',
-                                      'Python' : language=='py',
-                                      'Py' : language=='py',
-                                      'CPy' : (language=='py' or language == 'c'),
-                                      'Cpp' : language=='cpp',
-                                      'plastex' : True})
-
-raw = open('raw', 'w')
-for x in doc:
-    print >>raw, repr(x)
-raw.close()
 
 class SphinxWriter:
-    def __init__(self, filename):
-        self.f_index = QOpen(filename, 'wt')
+    def __init__(self, filename, language, abspath):
+        assert language in ['py', 'c', 'cpp']
+        self.language = language
+
+        self.abspath = abspath
+        os.path.abspath(os.path.dirname(filename))
+
+        self.f_index = QOpen(os.path.join(self.language, filename), 'wt')
         self.f = self.f_index
         self.f_chapter = None
         self.f_section = None
         self.indent = 0
         self.state = None
         self.envstack = []
-        self.tags = []
-        self.errors = open('errors', 'wt')
+        self.tags = {}
+        self.errors = open('errors.%s' % language, 'wt')
         self.unhandled_commands = set()
         self.freshline = True
         self.function_props = {}
+        self.covered = set()        # covered functions, used for error report
+        self.description = ""
 
     def write(self, s):
         self.freshline = len(s) > 0 and (s[-1] == '\n')
@@ -91,6 +66,8 @@ class SphinxWriter:
             s = ":math:`%s`" % s[1:-1].strip()
         elif self.state != 'math':
             s.replace('\\_', '_')
+        if len(s) > 0 and s[-1] == '\n':
+            s = s[:-1]
         if self.state == 'fpreamble':
             self.description += s
         else:
@@ -105,12 +82,15 @@ class SphinxWriter:
                 self.state = None
                 self.write('\n\n')
         else:
-            if c.cmd == '[':
-                meth = self.cmd_gomath
+            if c.cmd == '\n':
+                self.write('\\\n')
             else:
-                cname = "cmd_" + c.cmd
-                meth = getattr(self, cname, self.unrecognized_cmd)
-            meth(c)
+                if c.cmd == '[':
+                    meth = self.cmd_gomath
+                else:
+                    cname = "cmd_" + c.cmd
+                    meth = getattr(self, cname, self.unrecognized_cmd)
+                meth(c)
 
     def cmd_gomath(self, c):
         self.state = 'math'
@@ -121,7 +101,7 @@ class SphinxWriter:
     def cmd_chapter(self, c):
         filename = str(c.params[0]).lower().replace(' ', '_').replace('/','_')
         self.f_index.write("    %s\n" % filename)
-        self.f_chapter = QOpen(filename + '.rst', 'wt')
+        self.f_chapter = QOpen(os.path.join(self.language, filename + '.rst'), 'wt')
         self.f_section = None
         self.f = self.f_chapter
         self.indent = 0
@@ -136,30 +116,30 @@ class SphinxWriter:
         filename = str(c.params[0]).lower().replace(' ', '_').replace('/','_')
         if not self.chapter_intoc:
             self.chapter_intoc = True
+            print >>self.f_chapter
             print >>self.f_chapter, '.. toctree::'
             print >>self.f_chapter, '    :maxdepth: 2'
             print >>self.f_chapter
         self.f_chapter.write("    %s\n" % filename)
-        self.f_section = QOpen(filename + '.rst', 'wt')
+        self.f_section = QOpen(os.path.join(self.language, filename + '.rst'), 'wt')
         self.f = self.f_section
         self.indent = 0
-        title = str(c.params[0])
+        title = self.render(c.params[0].str)
         print >>self, title
         print >>self, '=' * len(title)
         print >>self
-        print >>self, '.. highlight:: python'
+        print >>self, '.. highlight:: %s' % {'c': 'c', 'cpp': 'cpp', 'py': 'python'}[self.language]
         print >>self
 
     def cmd_subsection(self, c):
+        print >>self
         print >>self, str(c.params[0])
         print >>self, '-' * len(str(c.params[0]))
         print >>self
         self.function_props = {}
 
     def cmd_includegraphics(self, c):
-        filename = '../' + str(c.params[0])
-        if not os.path.isfile(filename):
-            print >>self.errors, "WARNING: missing image file", filename
+        filename = os.path.join('..', '..', str(c.params[0]))
         print >>self, "\n\n.. image:: %s\n\n" % filename
 
     def cmd_cvCppCross(self, c):
@@ -183,9 +163,18 @@ class SphinxWriter:
         print >>self, nm
         print >>self, '-' * len(nm)
         print >>self
-        print >>self, ".. class:: " + nm + "\n"
+        if self.language == 'py':
+            print >>self, ".. class:: " + nm + "\n"
+        else:
+            print >>self, ".. ctype:: " + nm + "\n"
         print >>self
-        self.tags.append("%s\t%s\t%d" % (nm, c.filename, c.lineno))
+        self.addtag(nm, c)
+        self.state = 'class'
+
+    def addtag(self, nm, c):
+        if nm == "":
+            self.report_error(c, "empty name")
+        self.tags[nm] = "%s\t%s\t%d" % (nm, os.path.join(os.getcwd(), c.filename), c.lineno)
 
     def cmd_cvfunc(self, c):
         self.cmd_cvCPyFunc(c)
@@ -199,31 +188,84 @@ class SphinxWriter:
         print >>self, '-' * len(nm)
         print >>self
         self.state = 'fpreamble'
+        if self.description != "":
+            self.report_error(c, "overflow - preceding cvfunc (starting %s) not terminated?" % repr(self.description[:30]))
         self.description = ""
-        self.tags.append("%s\t%s\t%d" % (nm, c.filename, c.lineno))
+        self.addtag(nm, c)
 
         self.function_props = {'name' : nm}
+        self.covered.add(nm)
+
+    def cmd_cvCppFunc(self, c):
+        self.indent = 0
+        nm = self.render(c.params[0].str)
+        print >>self, "\n.. index:: %s\n" % nm
+        print >>self, ".. _%s:\n" % nm
+        print >>self, nm
+        print >>self, '-' * len(nm)
+        print >>self
+        self.state = 'fpreamble'
+        if self.description != "":
+            self.report_error(c, "overflow - preceding cvfunc (starting %s) not terminated?" % repr(self.description[:30]))
+        self.description = ""
+        self.addtag(nm, c)
+
+        self.function_props = {'name' : nm}
+        self.covered.add(nm)
+
+    def cmd_cvdefC(self, c):
+        if self.language != 'c':
+            return
+        s = str(c.params[0]).replace('\\_', '_')
+        s = s.replace('\\par', '')
+        s = s.replace('\n', ' ')
+        s = s.replace(';', '')
+        self.indent = 0
+        for proto in s.split('\\newline'):
+            if proto.strip() != "":
+                print >>self, "\n\n.. cfunction:: " + proto.strip() + "\n"
+        # print >>self, "=", repr(c.params[0].str)
+        print >>self, '    ' + self.description
+        self.description = ""
+        print >>self
+        self.state = None
+        self.function_props['defpy'] = s
+
+    def cmd_cvdefCpp(self, c):
+        if self.language != 'cpp':
+            return
+        s = str(c.params[0]).replace('\\_', '_')
+        s = s.replace('\\par', '')
+        s = s.replace('\n', ' ')
+        s = s.replace(';', '')
+        self.indent = 0
+        for proto in s.split('\\newline'):
+            if proto.strip() != "":
+                print >>self, "\n\n.. cfunction:: " + proto.strip() + "\n"
+        # print >>self, "=", repr(c.params[0].str)
+        if self.description != "":
+            print >>self, '    ' + self.description
+        else:
+            self.report_error(c, 'empty description')
+        self.description = ""
+        print >>self
+        self.state = None
+        self.function_props['defpy'] = s
 
     def cmd_cvdefPy(self, c):
+        if self.language != 'py':
+            return
         s = str(c.params[0]).replace('\\_', '_')
         self.indent = 0
         print >>self, ".. function:: " + s + "\n"
         # print >>self, "=", repr(c.params[0].str)
         print >>self, '    ' + self.description
         print >>self
+        self.description = ""
         self.state = None
         self.function_props['defpy'] = s
 
         pp.ParserElement.setDefaultWhitespaceChars(" \n\t")
-        def returnList(x):
-            def listify(s, loc, toks):
-                return [toks]
-            x.setParseAction(listify)
-            return x
-        def CommaList(word):
-            return returnList(pp.Optional(word + pp.ZeroOrMore(pp.Suppress(',') + word)))
-        def sl(s):
-            return pp.Suppress(pp.Literal(s))
 
         ident = pp.Word(pp.alphanums + "_.+-")
         ident_or_tuple = ident | (sl('(') + CommaList(ident) + sl(')'))
@@ -252,48 +294,69 @@ class SphinxWriter:
                         doc_outs = [l[2]]
                     else:
                         doc_outs = l[2]
-                    print doc_outs, len(doc_outs)
                     if len(outs) != len(doc_outs):
                         self.report_error(c, "function %s output documented tuple %d, code %d" % (l[0], len(outs), len(doc_outs)))
+            else:
+                # self.report_error(c, "function %s documented but not found in code" % l[0])
+                pass
         except pp.ParseException, pe:
             self.report_error(c, str(pe))
             print s
             print pe
 
     def report_error(self, c, msg):
-        print >>self.errors, "%s:%d: Error %s" % (c.filename, c.lineno, msg)
+        print >>self.errors, "%s:%d: [%s] Error %s" % (c.filename, c.lineno, self.language, msg)
 
     def cmd_begin(self, c):
+        if len(c.params) == 0:
+            self.report_error(c, "Malformed begin")
+            return
         self.write('\n')
         s = str(c.params[0])
         self.envstack.append((s, (c.filename, c.lineno)))
         if s == 'description':
-            if 'name' in self.function_props and not 'defpy' in self.function_props:
+            if self.language == 'py' and 'name' in self.function_props and not 'defpy' in self.function_props:
                 self.report_error(c, "No cvdefPy for function %s" % self.function_props['name'])
             self.indent += 1
         elif s == 'lstlisting':
-            print >>self, "\n::\n"
-            self.indent += 1
+            # Set indent to zero while collecting code; so later write will not double-indent
+            self.saved_f = self.f
+            self.saved_indent = self.indent
+            self.f = StringIO.StringIO()
+            self.indent = 0
         elif s in ['itemize', 'enumerate']:
             self.indent += 1
+        elif s == 'tabular':
+            self.f = StringIO.StringIO()
         else:
             self.default_cmd(c)
 
     def cmd_item(self, c):
+        if len(self.ee()) == 0:
+            self.report_error(c, "item without environment")
+            return
         self.indent -= 1
         markup = {'itemize' : '*', 'enumerate' : '#.', 'description' : '*'}[self.ee()[-1]]
         if len(c.args) > 0:
-            markup += " " + str(c.args[0])
+            markup += " " + self.render([c.args[0].str])
+        if len(c.params) > 0:
+            markup += " " + self.render(c.params[0].str)
         self.write("\n\n" + markup)
         self.indent += 1
 
     def cmd_end(self, c):
+        if len(c.params) != 1:
+            self.report_error(c, "Malformed end")
+            return
+        if len(self.envstack) == 0:
+            self.report_error(c, "end with no env")
+            return
         self.write('\n')
         s = str(c.params[0])
         if self.envstack == []:
             print "Cannot pop at", (c.filename, c.lineno)
         if self.envstack[-1][0] != s:
-            report_error(c, "end{%s} does not match current stack %s" % (s, repr(self.envstack)))
+            self.report_error(c, "end{%s} does not match current stack %s" % (s, repr(self.envstack)))
         self.envstack.pop()
         if s == 'description':
             self.indent -= 1
@@ -301,31 +364,58 @@ class SphinxWriter:
                 self.function_props['done'] = True
         elif s in ['itemize', 'enumerate']:
             self.indent -= 1
+        elif s == 'tabular':
+            tabletxt = self.f.getvalue()
+            self.f = self.f_section
+            self.f.write(self.handle_table(tabletxt))
         elif s == 'lstlisting':
+            listing = self.f.getvalue()
+
+            self.f = self.saved_f
+            self.indent = self.saved_indent
+            print >>self
+            if self.language == 'py':
+                ckeys = ['#define', 'void', '#include', ';\n']
+                found = [repr(k) for k in ckeys if k in listing]
+                if len(found) > 0:
+                    self.report_error(c, 'listing is probably C, found %s' % ",".join(found))
+            if (self.language == 'py') and ('>>>' in listing):
+                print >>self, "\n.. doctest::\n"
+            else:
+                print >>self, "\n::\n"
+            self.indent += 1
             print >>self
+            self.write(listing)
             self.indent -= 1
             print >>self
+            print >>self
             print >>self, ".."      # otherwise a following :param: gets treated as more listing
+        elif s == 'document':
+            pass
         else:
             self.default_cmd(c)
         
     def cmd_label(self, c):
         pass
 
-    def cmd_cvdefC(self, c):
-        pass
-    def cmd_cvdefCpp(self, c):
-        pass
+    def cmd_lstinputlisting(self, c):
+        s = str(c.params[0])
+        print >>self.f, ".. include:: %s" % os.path.normpath(os.path.join(self.abspath, s))
+        print >>self.f, "    :literal:"
+        print >>self.f
 
     # Conditionals
-    def cmd_cvCpp(self, c):
-        pass
     def cmd_cvC(self, c):
-        pass
-    def cmd_cvCPy(self, c):
-        self.doL(c.params[0].str, False)
+        self.do_conditional(['c'], c)
+    def cmd_cvCpp(self, c):
+        self.do_conditional(['cpp'], c)
     def cmd_cvPy(self, c):
-        self.doL(c.params[0].str, False)
+        self.do_conditional(['py'], c)
+    def cmd_cvCPy(self, c):
+        self.do_conditional(['c', 'py'], c)
+    def do_conditional(self, langs, c):
+        if self.language in langs:
+            self.doL(c.params[0].str, False)
 
     def render(self, L):
         """ return L rendered as a string """
@@ -341,13 +431,29 @@ class SphinxWriter:
         return r
 
     def cmd_cvarg(self, c):
-        is_func_arg = self.ee() == ['description'] and not 'done' in self.function_props
+        if len(c.params) != 2:
+            self.report_error(c, "Malformed cvarg")
+            return
+        e = self.ee()
+        if self.state == 'class':
+            nm = self.render(c.params[0].str)
+            if '->' in nm:
+                print >>self, "\n\n.. method:: %s\n\n" % nm
+            else:
+                print >>self, "\n\n.. attribute:: %s\n\n" % nm
+            self.indent += 1
+            print >>self
+            self.doL(c.params[1].str, False)
+            self.indent -= 1
+            print >>self
+            return
+        is_func_arg = (e == ['description']) and (not 'done' in self.function_props)
         if is_func_arg:
             nm = self.render(c.params[0].str)
             print >>self, '\n:param %s: ' % nm,
             type = None         # Try to figure out the argument type
             # For now, multiple args get a pass
-            if 'signature' in self.function_props and (not ',' in nm):
+            if (self.language == 'py') and ('signature' in self.function_props) and (not ',' in nm):
                 sig = self.function_props['signature']
                 argnames = [a[0] for a in sig[1]]
                 if isinstance(sig[2], str):
@@ -364,12 +470,12 @@ class SphinxWriter:
                     arg = adict.get(nm, None)
                     if arg:
                         type = arg.ty
-        elif self.ee() == ['description']:
-            print >>self, '\n* **%s** ' % self.render(c.params[0].str),
-        elif self.ee() == ['description', 'description']:
+                    else:
+                        self.report_error(c, 'cannot find arg %s in code' % nm)
+        elif len(e) > 0 and e[-1] == 'description':
             print >>self, '\n* **%s** ' % self.render(c.params[0].str),
         else:
-            print 'strange env', self.envstack
+            self.report_error(c, "unexpected env (%s) for cvarg" % ",".join(e))
         self.indent += 1
         self.doL(c.params[1].str, False)
         self.indent -= 1
@@ -384,14 +490,18 @@ class SphinxWriter:
                 "int" : "int",
                 "float" : "float",
                 "char" : "str",
+                "cvarrseq" : ":class:`CvArr` or :class:`CvSeq`",
                 "CvPoint2D32fs" : "sequence of (float, float)",
+                "pts_npts_contours" : "list of lists of (x,y) pairs",
+                "CvSeqOfCvSURFPoint" : ":class:`CvSeq` of :class:`CvSURFPoint`",
+                "CvSeqOfCvSURFDescriptor" : ":class:`CvSeq` of list of float",
+                "cvpoint2d32f_count" : "int",
             }
             print >>self, "\n:type %s: %s" % (nm, translate.get(type, ':class:`%s`' % type))
 
     def cmd_genc(self, c): pass 
     def cmd_genpy(self, c): pass 
     def cmd_author(self, c): pass 
-    def cmd_cite(self, c): pass
     def cmd_date(self, c): pass
     def cmd_def(self, c): pass
     def cmd_documentclass(self, c): pass
@@ -404,10 +514,17 @@ class SphinxWriter:
     def cmd_usepackage(self, c): pass
     def cmd_title(self, c): pass
     def cmd_par(self, c): pass
-    def cmd_hline(self, c): pass
+    def cmd_hline(self, c):
+        print >>self, "\\hline"
+
+    def cmd_cite(self, c):
+        self.write("[%s]_" % str(c.params[0]))
 
     def cmd_href(self, c):
-        self.write("`%s <%s>`_" % (str(c.params[1]), str(c.params[0])))
+        if len(c.params) == 2:
+            self.write("`%s <%s>`_" % (str(c.params[1]), self.render(c.params[0].str)))
+        else:
+            self.report_error(c, "href should have two params")
 
     def cmd_url(self, c):
         self.write(str(c.params[0]))
@@ -429,12 +546,17 @@ class SphinxWriter:
             self.write(repr(c))
 
     def unrecognized_cmd(self, c):
-        if self.f == self.f_section:
-            self.write(repr(c))
-        self.unhandled_commands.add(c.cmd)
+        # if writing the index or chapter heading, anything goes
+        if not self.f in [self.f_index, self.f_chapter]:
+            self.write(c.cmd)
+            if (not 'lstlisting' in self.ee()) and (not c.cmd in "#{}%&*\\_"):
+                if not c.cmd in self.unhandled_commands:
+                    self.report_error(c, 'unhandled command %s' % c.cmd)
+                    self.unhandled_commands.add(c.cmd)
 
     def doL(self, L, newlines = True):
         for x in L:
+            pos0 = self.f.tell()
             if isinstance(x, TexCmd):
                 self.docmd(x)
             else:
@@ -442,25 +564,64 @@ class SphinxWriter:
                     self.doplain(x)
                 else:
                     self.doplain(x.lstrip())
-            if self.state in ['math'] or not newlines:
-                self.appendspace()
-            else:
-                self.write('\n')
+            pos1 = self.f.tell()
+            if pos0 != pos1:
+                if self.state in ['math'] or not newlines:
+                    self.appendspace()
+                else:
+                    if not 'lstlisting' in self.ee():
+                        self.write('\n')
+
+    def handle_table(self, s):
+        oneline = s.replace('\n', ' ').strip()
+        rows = [r.strip() for r in oneline.split('\\hline')]
+        tab = []
+        for r in rows:
+            if r != "":
+                cols = [c.strip() for c in r.split('&')]
+                tab.append(cols)
+        widths = [max([len(r[i]) for r in tab]) for i in range(len(tab[0]))]
+
+        st = ""         # Sphinx table
+
+        if 0:
+            sep = "+" + "+".join(["-" * w for w in widths]) + "+"
+            st += sep + '\n'
+            for r in tab:
+                st += "|" + "|".join([c.center(w) for (c, w) in zip(r, widths)]) + "|" + '\n'
+                st += sep + '\n'
+
+        st = '.. table::\n\n'
+        sep = "  ".join(["=" * w for w in widths])
+        st += '    ' + sep + '\n'
+        for y,r in enumerate(tab):
+            st += '    ' + "  ".join([c.ljust(w) for (c, w) in zip(r, widths)]) + '\n'
+            if y == 0:
+                st += '    ' + sep + '\n'
+        st += '    ' + sep + '\n'
+        return st
 
     def ee(self):
         """ Return tags of the envstack.  envstack[0] is 'document', so skip it """
         return [n for (n,_) in self.envstack[1:]]
 
+    def get_tags(self):
+        return self.tags
+
     def close(self):
 
         if self.envstack != []:
             print >>self.errors, "Error envstack not empty at end of doc: " + repr(self.envstack)
-        print >>self.errors, "unrecognized commands:"
-        print >>self.errors, "\n    ".join(sorted(self.unhandled_commands))
+        print >>self.errors, "Unrecognized commands:"
+        for c in sorted(self.unhandled_commands):
+            print >>self.errors, "\n    " + c
         print >>self.errors
+        if self.language == 'py':
+            print >>self.errors, "The following functions are undocumented"
+            for f in sorted(set(python_api) - self.covered):
+                print >>self.errors, '    ', f
 
-        open('TAGS', 'w').write("\n".join(sorted(self.tags)))
-
+        print >>self.f_index, "    bibliography"
         print >>self.f_index, """
 
 Indices and tables
@@ -470,15 +631,95 @@ Indices and tables
 * :ref:`search`
 """
 
-sr = SphinxWriter('index.rst')
-print >>sr, """
-OpenCV |version| Python Reference
-=================================
+# Quick and dirty bibtex parser
 
-Test code::
+def parseBib(filename, language):
+    pp.ParserElement.setDefaultWhitespaceChars(" \n\t")
+    entry = returnList(pp.Word('@', pp.alphanums) + sl('{') +
+        pp.Word(pp.alphanums + "_") + sl(',') +
+        CommaList(returnTuple(pp.Word(pp.alphanums) + sl('=') + pp.QuotedString('{', endQuoteChar = '}'))) +
+        pp.Suppress(pp.Optional(',')) +
+        sl('}'))
+    r = (pp.ZeroOrMore(entry) | pp.Suppress('#' + pp.ZeroOrMore(pp.CharsNotIn('\n'))) + pp.StringEnd()).parseFile(filename)
 
-    import foo
-    print a + 3
+    bibliography = QOpen(os.path.join(language, "bibliography.rst"), 'wt')
+    print >>bibliography, "Bibliography"
+    print >>bibliography, "============"
+    print >>bibliography
+
+    for _,e in sorted([(str(x[1]), x) for x in r]):
+        (etype, tag, attrs) = str(e[0][1:]), str(e[1]), dict([(str(a), str(b)) for (a,b) in e[2]])
+        
+        representations = {
+            'article' :         '$author, "$title". $journal $volume $number, pp $pages ($year)',
+            'inproceedings' :   '$author "$title", $booktitle, $year',
+            'misc' :            '$author "$title", $year',
+            'techreport' :      '$author "$title", $edition, $edition ($year)',
+        }
+        if etype in representations:
+            if 0:
+                print >>bibliography, tag
+                print >>bibliography, "^" * len(tag)
+                print >>bibliography
+
+            print >>bibliography, ".. [%s] %s" % (tag, Template(representations[etype]).safe_substitute(attrs))
+            print >>bibliography
+    bibliography.close()
+
+if 1:
+    fulldoc = latexparser(sys.argv[1])
+
+    abspath = os.path.abspath(os.path.dirname(sys.argv[1]))
+
+    raw = open('raw.full', 'w')
+    for x in fulldoc:
+        print >>raw, repr(x)
+    raw.close()
+
+    # Filter on target language
+    def preprocess_conditionals(fd, conditionals):
+        r = []
+        ifstack = []
+        for x in fd:
+            if isinstance(x, TexCmd):
+                ll = x.cmd.rstrip()
+                loc = (x.filename, x.lineno)
+                if ll.startswith("if"):
+                    # print " " * len(ifstack), '{', loc
+                    ifstack.append((conditionals.get(ll[2:], False), loc))
+                elif ll.startswith("else"):
+                    ifstack[-1] = (not ifstack[-1][0], ifstack[-1][1])
+                elif ll.startswith("fi"):
+                    ifstack.pop()
+                    # print " " * len(ifstack), '}', loc
+                elif not False in [p for (p,_) in ifstack]:
+                    r.append(x)
+            else:
+                if not False in [p for (p,_) in ifstack]:
+                    r.append(x)
+        if ifstack != []:
+            print "unterminated if", ifstack
+            sys.exit(0)
+        return r
+
+    tags = {}
+    for language in sys.argv[2:]:
+        doc = preprocess_conditionals(fulldoc, {
+                                              'C' : language=='c',
+                                              'Python' : language=='py',
+                                              'Py' : language=='py',
+                                              'CPy' : (language=='py' or language == 'c'),
+                                              'Cpp' : language=='cpp',
+                                              'plastex' : True})
+
+        raw = open('raw.%s' % language, 'w')
+        for x in doc:
+            print >>raw, repr(x)
+        raw.close()
+        sr = SphinxWriter('index.rst', language, abspath)
+        print >>sr, """
+OpenCV |version| %s Reference
+=================================
 
 The OpenCV Wiki is here: http://opencv.willowgarage.com/
 
@@ -487,6 +728,10 @@ Contents:
 .. toctree::
     :maxdepth: 2
 
-"""
-sr.doL(doc)
-sr.close()
+""" % {'c': 'C', 'cpp': 'C++', 'py': 'Python'}[language]
+        sr.doL(doc)
+        sr.close()
+        parseBib('../opencv.bib', language)
+        tags.update(sr.get_tags())
+    open('TAGS', 'w').write("\n".join(sorted(tags.values())) + "\n")
+