summaryrefslogtreecommitdiff
path: root/scheme2rst.py
blob: f1228496e44e50a9b858d07c53acc3a6eab9aaa0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""
usage: %prog [options]
-r, --rst: make an .rst file only
"""

"""
This script is able to include libraries in the IKARUS_LIBRARY_PATH
(with the @@NAME) syntax and to include snippets defined in the
current file.
"""

import os, sys, re, webbrowser
from docutils.core import publish_cmdline
from ms.optionparser import OptionParser
try:
    import sphinx.directives # enable code-block
except ImportError:
    print 'Warning: sphinx is not installed'

identifier = r'[-A-Z\d_=!\?\+\-\*/]+'
BIGCOMMENT = re.compile(r'#\|(.*)\|#(.*)', re.DOTALL)
SNIPPET = re.compile(r'\n\s*;+\s*(%s)\s*\n(.*?)\n\s*;+\s*END' % identifier,
                     re.DOTALL)
SNIPPETNAME = re.compile(r'\n\$\$(%s)\n' % identifier)
INCLUDE = re.compile(r'\$\$([-\w\d_\./]+):')
INCLUDESNIPPET = re.compile(r'\$\$([-\w\d_\.]+):(%s)\n' % identifier)

PATHS = os.environ['IKARUS_LIBRARY_PATH'].split(':')

APS_PATH = os.path.join(PATHS[0], 'aps')

PATHS = ['.'] + PATHS + [APS_PATH]

def include(fname, paths=PATHS, exts=('.ss', '.sls')):
    for path in paths:
        for ext in exts:
            try:
                return file(os.path.join(path, fname + ext)).read()
            except IOError, e:
                pass
    raise e

def indent(text):
    return '\n'.join('  ' + ln for ln in text.splitlines())

class SnippetExtractor(object):
    """
    Given some Scheme code, parses it to find snippets. Provides a .get method
    to extract a properly indented snippet and a cached .make classmethod to
    extract the code from a file name.
    """
    _cache = {}
    @classmethod
    def make(cls, fname, codeblock=False):
        try:
            self = cls._cache[fname]
        except KeyError:
            self = cls._cache[fname] = cls(include(fname), codeblock)
        return self
    def __init__(self, code, codeblock):
        if codeblock:
            self.templ = '\n.. code-block:: scheme\n\n%s\n'
        else:
            self.templ = '\n::\n\n%s\n'
        self._dict = dict(
            (mo.group(1), mo.group(2)) for mo in SNIPPET.finditer(code))
    def get(self, name):
        return self.templ % indent(self._dict.get(name, name))
        
def scheme2rst(fname, codeblock=False):
    mo = BIGCOMMENT.search(file(fname).read())
    if mo is None: 
        sys.exit('No #| ..|# found!')
    text, code = mo.groups()
    snippet = SnippetExtractor(code, codeblock)
    def repl(mo):
        "replace SNIPPETNAME with the content of the snippet dictionary"
        return snippet.get(mo.group(1))
    def include_file(mo):
        return snippet.templ % indent(include(mo.group(1)))
    def include_snippet(mo):
        fname, name = mo.groups()
        snippet = SnippetExtractor.make(fname)
        return snippet.get(name)
    text = SNIPPETNAME.sub(repl, text)
    text = INCLUDESNIPPET.sub(include_snippet, text)
    text = INCLUDE.sub(include_file, text)
    rstfile = os.path.splitext(fname)[0] + '.rst'
    file(rstfile, 'w').write(text)
    return rstfile

if __name__ == "__main__":
    option, schemefiles = OptionParser(__doc__).parse_args()
    if not option and not schemefiles:
        OptionParser.exit("usage: python rst2st.py <scheme source file>")
    for schemefile in schemefiles:
        fname, ext = os.path.splitext(schemefile)
        assert ext in ('.ss', '.scm')
        rstfile = scheme2rst(schemefile) # generate .rst
        if not option.rst: # generate .html unless option -r was set
            htmlfile = fname + '.html'
            publish_cmdline(writer_name='html', argv=[rstfile, htmlfile])
            webbrowser.open(htmlfile)