#!/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