#!/usr/bin/env python
##
## Name: fscheme.py
## Purpose: Trivial Scheme to LaTeX translator.
## Author: M. J. Fromberger
##
## Note: This is not a Scheme pretty-printer; all it does is to read a
## Scheme expression and convert the amount of indentation on each
## line into a tab stop for a tabbing environment. The code is copied
## to the output with tab stops inserted to get everything to the
## right place.
##
## This is useful for including Lisp code with the \ifthen package,
## because the verbatim environment does NOT work within \ifthen
## arguments.
##
import os, re, sys
class Block (list):
def __init__(self, start_line):
super(Block, self).__init__()
self._line = start_line
def start(self):
return self._line
def end(self):
return self._line + len(self) - 1
def span(self):
return self.start(), self.end()
def extract_blocks(fp):
"""Extract blocks of Scheme code from the given file. Comments
and blank lines are ignored, and a new block is considered to
start with any flush-left line. Returns a list of blocks, each
of which is a list of lines.
"""
blocks = []
line_num = 0
for line in fp:
line_num += 1
if re.match(r'^\s*(;|$)', line): continue
lsp = re.match('^\s*', line).group()
if len(lsp) == 0 or len(blocks) == 0:
blocks.append(Block(line_num))
blocks[-1].append(line.rstrip())
return blocks
def find_tab_stops(blk):
"""Given a list of strings, find the horizontal positions of all
the tab stops necessary to capture the levels of indentation used
in the original block of text. Returns a set of integers.
"""
stops = set()
for line in blk:
lsp = re.match('^\s*', line).group()
if len(lsp) > 0:
stops.add(len(lsp))
return stops
def make_tab_line(ts):
"""Given a set of tab stops as computed by find_tab_stops(),
return a string of tab definitions suitable for use in a tabbing
environment in LaTeX.
"""
last = 0 ; out = ''
for pos in sorted(ts):
out += 'x' * (pos - last) + '\\='
last = pos
return out + '\\kill'
def make_tab_map(ts):
"""Given a set of tab stops as computed by find_tab_stops(),
return a dictionary mapping a number of spaces of indent to
a number of tab stops.
"""
out = {0: 0}
for pos in sorted(ts):
out[pos] = len(out)
return out
def render_block(blk):
ts = find_tab_stops(blk)
tl = make_tab_line(ts)
tm = make_tab_map(ts)
pfx = '{\\tt\\begin{tabbing}' + tl + '\n'
out = []
for line in blk:
lsp = re.match('^\s*', line).group()
nt = tm[len(lsp)]
out.append('\\>' * nt +
re.sub(r'([?!]) ', r'\1\@ ',
line[len(lsp):]))
return pfx + ' \\\\\n'.join(out) + '\n\\end{tabbing}}'
def process_file(path):
dir, ext = os.path.splitext(path)
opath = dir + '.tex' ; c = 0
while os.path.exists(opath):
c += 1
opath = dir + '-%d.tex' % c
ifp = file(path, 'rU')
ofp = file(opath, 'wt')
print >> ofp, '%%\n%% Generated from "%s"\n%%\n' % \
os.path.basename(path)
for blk in extract_blocks(ifp):
s, e = blk.span()
if s == e:
print >> ofp, "%% Line %d" % s
else:
print >> ofp, "%% Lines %d-%d" % (s, e)
print >> ofp, render_block(blk)
print >> ofp
ifp.close()
ofp.close()
def main(argv):
if len(argv) == 0:
print >> sys.stderr, "Usage: fscheme.py +"
sys.exit(1)
for path in argv:
print >> sys.stderr, "Processing `%s' ... " % path
process_file(path)
print >> sys.stderr, ""
if __name__ == "__main__":
main(sys.argv[1:])
# Here there be dragons