Anonymous avatar Anonymous committed cdb4049

Pulled desagetex stuff into .dtx file

Comments (0)

Files changed (6)

 
 clean: 
 	latexcleanup clean .
-	rm -fr sage-plots-for-* E2.sobj *.pyc sagetex.tar.gz sagetex.py sagetex.pyc sagetex.sty
+	rm -fr sage-plots-for-* E2.sobj *.pyc sagetex.tar.gz sagetex.py sagetex.pyc sagetex.sty makestatic.py desagetexparser.py
 
 install:
 	cp sagetex.py $(dest)
-	sed -e 's/ hrm iffalse and fi
+	sed -e '/\\iffalse/ d' \
+	    -e '/^%%/ d' \
+	    -e 's|directory with sagetex.py|$(dest)|'\
+		-e '50,55 s/\\fi//' sagetex.sty > $(dest)/sagetex.sty
 
 dist: all
 	@echo
 	@echo Did you turn off Imagemagick in example.tex?
 	@echo
-	@tar zcf sagetex.tar.gz ../sagetex/example.pdf ../sagetex/example.tex ../sagetex/README ../sagetex/sagetexpackage.dtx ../sagetex/sagetexpackage.ins ../sagetex/sagetexpackage.pdf ../sagetex/sagetex.py ../sagetex/sagetex.sty
+	@tar zcf sagetex.tar.gz ../sagetex/example.pdf ../sagetex/example.tex ../sagetex/README ../sagetex/sagetexpackage.dtx ../sagetex/sagetexpackage.ins ../sagetex/sagetexpackage.pdf
 
 * kpsewhich stuff from CTAN upload page?
 
-* Harald's static idea
+* desagetexparser -- leave Sage commands in as comments? document usage!
 
-* Jason's verb for % idea

desagetexparser.py

-import sys
-sys.path.append('/home/drake/k/urohome/download/pyparsing-1.4.11')
-
-from pyparsing import *
-
-
-def skipToMatching(opener, closer):
-  nest = nestedExpr(opener, closer)
-  nest.setParseAction(lambda l, s, t: l[s:getTokensEndLoc()])
-  return nest
-
-curlybrackets = skipToMatching('{', '}')
-squarebrackets = skipToMatching('[', ']')
-
-class SoutParser():
-
-  def __init__(self, fn):
-    self.label = {}
-
-    parselabel = '\\newlabel{@sagelabel' + \
-                 Word(nums).setResultsName('num') + \
-                 '}{' + \
-                 curlybrackets.setResultsName('result') + \
-                 '{}{}{}{}}'
-    parselabel.ignore('%' + restOfLine)
-    parselabel.setParseAction(self.newlabel)
-
-    OneOrMore(parselabel).parseFile(fn)
-
-  def newlabel(self, s, l, t):
-    self.label[int(t.num)] = t.result[1:-1]
-
-
-class DeSageTex():
-
-  def __init__(self, fn_):
-    self.fn = fn_
-    self.sagen = 0
-    self.plotn = 0
-
-    self.sout = SoutParser(self.fn + '.sout')
-
-    # \sage{}:
-    smacro = '\\sage' + curlybrackets
-    smacro.setParseAction(self.sage)
-
-    # \usepackage[]{sagetex}:
-    usepackage = '\\usepackage' + \
-                 Optional(squarebrackets) + \
-                 '{sagetex}'
-    usepackage.setParseAction(replaceWith("""\\RequirePackage{verbatim}
-\\RequirePackage{graphicx}"""))
-
-    # \sageplot[][]{}:
-    splot = '\\sageplot' + \
-            Optional(squarebrackets).setResultsName('options') + \
-            Optional(squarebrackets) + \
-            curlybrackets
-    splot.setParseAction(self.plot)
-
-    # the printed environments:
-    beginorend = oneOf('begin end')
-
-    blockorverb = 'sage' + oneOf('block verbatim') 
-    blockorverb.setParseAction(replaceWith('verbatim'))
-    senv = '\\' + beginorend + '{' + blockorverb + '}'
-
-    # the non-printed environment:
-    silent = Literal('sagesilent')
-    silent.setParseAction(replaceWith('comment'))
-
-    ssilent = '\\' + beginorend + '{' + silent + '}'
-
-    # \sagetexindent:
-    stexindent = Suppress('\\setlength{\\sagetexindent}' + curlybrackets)
-
-    doit = smacro | senv | ssilent | usepackage | splot | stexindent
-    doit.ignore('%' + restOfLine)
-    doit.ignore('\\begin{verbatim}' + SkipTo('\\end{verbatim}'))
-    doit.ignore('\\begin{comment}' + SkipTo('\\end{comment}'))
-    
-    f = open(self.fn + '.tex', 'r')
-    str = ''.join(f.readlines())
-    self.result = doit.transformString(str)
-
-  def sage(self, s, l, t):
-    self.sagen += 1
-    return self.sout.label[self.sagen - 1]
-
-  def plot(self, s, l, t):
-    self.plotn += 1
-    if len(t.options) == 0:
-      opts = '[width=.75\\textwidth]'
-    else:
-      opts = t.options[0]
-    return '\\includegraphics%s{sage-plots-for-%s.tex/plot-%s}' % \
-      (opts, self.fn, self.plotn - 1)
-
-
-if __name__ == '__main__':
-  sout = SoutParser('example.sout')
-  print '=' * 50
-  print sout.label

makestatic.py

-#!/usr/bin/env python
-
-import sys
-import getopt
-import os.path
-from desagetexparser import DeSageTex
-
-def usage():
-  print("""Usage: %s [-h|--help] [-o|--overwrite] inputfile [outputfile] 
-
-Removes SageTeX macros from `inputfile' and replaces them with the
-Sage-computed results to make a "static" file. You'll need to have run
-Sage on `inputfile' already.
-
-`inputfile' can include the .tex extension or not. If you provide
-`outputfile', the results will be written to a file of that name.
-Specify `-o' or `--overwrite' to overwrite the file if it exists.
-
-See the SageTeX documentation for more details.""" % sys.argv[0])
-
-try:
-  opts, args = getopt.getopt(sys.argv[1:], 'ho', ['help', 'overwrite'])
-except getopt.GetoptError, err:
-  print str(err)
-  usage()
-  sys.exit(1)
-
-overwrite = False
-for o, a in opts:
-  if o in ('-h', '--help'):
-    usage()
-    sys.exit()
-  elif o in ('-o', '--overwrite'):
-    overwrite = True
-
-if len(args) == 0 or len(args) > 2:
-  print('Error: wrong number of arguments. Make sure you specify options first.\n') 
-  usage()
-  sys.exit(1)
-
-if len(args) == 2 and (os.path.exists(args[1]) and not overwrite):
-  print('Error: %s exists and overwrite option not specified.' % args[1])
-  sys.exit(1)
-
-src, ext = os.path.splitext(args[0])
-desagetexed = DeSageTex(src)
-
-if len(args) == 2:
-  dest = open(args[1], 'w')
-  dest.write(desagetexed.result)
-else:
-  print(desagetexed.result)

sagetexpackage.dtx

 %   The Current Maintainer of this work is Dan Drake.
 %
 %   This work consists of the files sagetexpackage.dtx,
-%   sagetexpackage.ins, example.tex, and the derived files sagetex.sty
-%   and sagetex.py.
+%   sagetexpackage.ins, example.tex, and the derived files sagetex.sty,
+%   sagetex.py, desagetexparser.py, and makestatic.py.
 % 
 % \fi
 %
 %    \end{macrocode}
 % \end{macro}
 %
+% \iffalse
+%</python>
+%<*staticscript>
+% \fi
+%
+% \section{Removing \ST commands}
+%
+% Here we describe the Python code for the scripts that remove \ST
+% commands to produce a ``static'' file.
+%
+% \subsection{makestatic.py}
+%
+% First, the command-line script. It's about the most basic, generic
+% Python script taking command-line arguments that you'll find. The
+% |#!/usr/bin/env python| line is provided for us by the |.ins| file's
+% preamble, so we don't put it here.
+%    \begin{macrocode}
+import sys
+import time
+import getopt
+import os.path
+from desagetexparser import DeSageTex
+
+def usage():
+  print("""Usage: %s [-h|--help] [-o|--overwrite] inputfile [outputfile]
+
+Removes SageTeX macros from `inputfile' and replaces them with the
+Sage-computed results to make a "static" file. You'll need to have run
+Sage on `inputfile' already.
+
+`inputfile' can include the .tex extension or not. If you provide
+`outputfile', the results will be written to a file of that name.
+Specify `-o' or `--overwrite' to overwrite the file if it exists.
+
+See the SageTeX documentation for more details.""" % sys.argv[0])
+
+try:
+  opts, args = getopt.getopt(sys.argv[1:], 'ho', ['help', 'overwrite'])
+except getopt.GetoptError, err:
+  print str(err)
+  usage()
+  sys.exit(1)
+
+overwrite = False
+for o, a in opts:
+  if o in ('-h', '--help'):
+    usage()
+    sys.exit()
+  elif o in ('-o', '--overwrite'):
+    overwrite = True
+
+if len(args) == 0 or len(args) > 2:
+  print('Error: wrong number of arguments. Make sure to specify options first.\n')
+  usage()
+  sys.exit(1)
+
+if len(args) == 2 and (os.path.exists(args[1]) and not overwrite):
+  print('Error: %s exists and overwrite option not specified.' % args[1])
+  sys.exit(1)
+
+src, ext = os.path.splitext(args[0])
+%    \end{macrocode}
+% The next line is where all the real work gets done. Sorry it's not
+% more exciting-looking.
+%    \begin{macrocode}
+desagetexed = DeSageTex(src)
+%    \end{macrocode}
+% This part is cool: we need double percent signs at the beginning of
+% the line because Python needs them (so they get turned into single
+% percent signs) \emph{and} because \textsf{Docstrip} needs them (so the
+% line gets passed into the generated file). It's perfect!
+%    \begin{macrocode}
+header = """\
+%% SageTeX commands have been automatically removed from this file and
+%% replaced with plain LaTeX. Processed %s.
+
+""" % time.strftime('%a %d %b %Y %H:%M:%S', time.localtime())
+
+if len(args) == 2:
+  dest = open(args[1], 'w')
+else:
+  dest = sys.stdout
+
+dest.write(header)
+dest.write(desagetexed.result)
+%    \end{macrocode}
+%
+% \iffalse
+%</staticscript>
+%<*staticmod>
+% \fi
+%
+% \subsection{The parser module}
+% 
+% Here's the module that does the actual parsing and replacing. It's
+% really quite simple, thanks to the awesome
+% \href{http://pyparsing.wikispaces.com}{Pyparsing module}. The parsing
+% code below is nearly self-documenting! Compare that to fancy regular
+% expressions, which sometimes look like someone sneezed punctuation all
+% over the screen.
+% 
+% import sys
+% sys.path.append('/home/drake/k/urohome/download/pyparsing-1.4.11')
+%    \begin{macrocode}
+from pyparsing import *
+%    \end{macrocode}
+% First, we define this very helpful parser: it finds the matching
+% bracket, and doesn't parse any of the intervening text. It's basically
+% like hitting the percent sign in Vim. This is useful for parsing \LTX
+% stuff, when you want to just grab everything enclosed by matching
+% brackets.
+%    \begin{macrocode}
+def skipToMatching(opener, closer):
+  nest = nestedExpr(opener, closer)
+  nest.setParseAction(lambda l, s, t: l[s:getTokensEndLoc()])
+  return nest
+
+curlybrackets = skipToMatching('{', '}')
+squarebrackets = skipToMatching('[', ']')
+%    \end{macrocode}
+% Here's the parser for the generated |.sout| file. The code below does
+% all the parsing of the |.sout| file and puts the results into a
+% dictionary. Notice that it's on the order of 10 lines of code---hooray
+% for Pyparsing!
+%    \begin{macrocode}
+class SoutParser():
+  def __init__(self, fn):
+    self.label = {}
+%    \end{macrocode}
+% A label line looks like
+% \begin{quote}
+%   |\newlabel{@sagelabel|\meta{integer}|}{|\marg{bunch of \LTX code}|{}{}{}{}}|
+% \end{quote}
+% which makes the parser definition below pretty obvious. We assign some
+% names to the interesting bits so the |newlabel| method can make them
+% into keys and values.
+%    \begin{macrocode}
+    parselabel = '\\newlabel{@sagelabel' + \
+                 Word(nums)('num') + \
+                 '}{' + \
+                 curlybrackets('result') + \
+                 '{}{}{}{}}'
+%    \end{macrocode}
+% We tell it to ignore comments, and hook up the dictionary-making
+% method. 
+%    \begin{macrocode}
+    parselabel.ignore('%' + restOfLine)
+    parselabel.setParseAction(self.newlabel)
+%    \end{macrocode}
+% A |.sout| file consists of one or more such lines. Now go parse the
+% file we were given.
+%    \begin{macrocode}
+    OneOrMore(parselabel).parseFile(fn)
+%    \end{macrocode}
+% Pyparser's parse actions get called with three arguments: the string
+% that matched, the location of the beginning, and the resulting parse
+% object. Here we just add a new key-value pair to the dictionary,
+% remembering to strip off the enclosing brackets from the ``result''
+% bit.
+%    \begin{macrocode}
+  def newlabel(self, s, l, t):
+    self.label[int(t.num)] = t.result[1:-1]
+%    \end{macrocode}
+% 
+% Now we define a parser for \LTX files that use \ST commands. We assume
+% that the provided |fn_| is just a basename.
+%    \begin{macrocode}
+class DeSageTex():
+  def __init__(self, fn_):
+    self.fn = fn_
+    self.sagen = 0
+    self.plotn = 0
+    self.sout = SoutParser(self.fn + '.sout')
+%    \end{macrocode}
+% Parse |\sage| macros. We just need to pull in the result from the
+% |.sout| file and increment the counter.
+%    \begin{macrocode}
+    smacro = '\\sage' + curlybrackets
+    smacro.setParseAction(self.sage)
+%    \end{macrocode}
+% Parse the |\usepackage{sagetex}| line. Right now we don't support
+% comma-separated lists of packages.
+%    \begin{macrocode}
+    usepackage = '\\usepackage' + \
+                 Optional(squarebrackets) + \
+                 '{sagetex}'
+    usepackage.setParseAction(replaceWith("""\\RequirePackage{verbatim}
+\\RequirePackage{graphicx}"""))
+%    \end{macrocode}
+% Parse |\sageplot| macros.
+%    \begin{macrocode}
+    splot = '\\sageplot' + \
+            Optional(squarebrackets)('options') + \
+            Optional(squarebrackets) + \
+            curlybrackets
+    splot.setParseAction(self.plot)
+%    \end{macrocode}
+% The printed environments (|sageblock| and |sageverbatim|) get turned
+% into |verbatim| environments.
+%    \begin{macrocode}
+    beginorend = oneOf('begin end')
+    blockorverb = 'sage' + oneOf('block verbatim')
+    blockorverb.setParseAction(replaceWith('verbatim'))
+    senv = '\\' + beginorend + '{' + blockorverb + '}'
+%    \end{macrocode}
+% And the non-printed |sagesilent| environment gets commented out. We
+% could remove all the text, but this works and makes going back to \ST
+% commands (de-de-\ST{}ing?) easier.
+%    \begin{macrocode}
+    silent = Literal('sagesilent')
+    silent.setParseAction(replaceWith('comment'))
+    ssilent = '\\' + beginorend + '{' + silent + '}'
+%    \end{macrocode}
+% The |\sagetexindent| macro is no longer relevant, so remove it from
+% the output (``suppress'', in Pyparsing terms). 
+%    \begin{macrocode}
+    stexindent = Suppress('\\setlength{\\sagetexindent}' + curlybrackets)
+%    \end{macrocode}
+% Now we define the parser that actually goes through the file. It just
+% looks for any one of the above bits, while ignoring anything that
+% should be ignored.
+%    \begin{macrocode}
+    doit = smacro | senv | ssilent | usepackage | splot | stexindent
+    doit.ignore('%' + restOfLine)
+    doit.ignore('\\begin{verbatim}' + SkipTo('\\end{verbatim}'))
+    doit.ignore('\\begin{comment}' + SkipTo('\\end{comment}'))
+%    \end{macrocode}
+% We can't use the |parseFile| method, because that expects a ``complete
+% grammar'' in which everything falls into some piece of the parser.
+% Instead we suck in the whole file as a single string, and run
+% |transformString| on it, since that will just pick out the interesting
+% bits and munge them according to the above definitions.
+%    \begin{macrocode}
+    f = open(self.fn + '.tex', 'r')
+    str = ''.join(f.readlines())
+    self.result = doit.transformString(str)
+%    \end{macrocode}
+% That's the end of the class constructor, and it's all we need to do
+% here. You access the results of parsing via the |result| string.
+%
+% We do have two methods to define. The first does the same thing that
+% |\ref| does in your \LTX file: returns the content of the label and
+% increments a counter.
+%    \begin{macrocode}
+  def sage(self, s, l, t):
+    self.sagen += 1
+    return self.sout.label[self.sagen - 1]
+%    \end{macrocode}
+% The second method returns the appropriate |\includegraphics| command.
+% It does need to account for the default argument.
+%    \begin{macrocode}
+  def plot(self, s, l, t):
+    self.plotn += 1
+    if len(t.options) == 0:
+      opts = '[width=.75\\textwidth]'
+    else:
+      opts = t.options[0]
+    return '\\includegraphics%s{sage-plots-for-%s.tex/plot-%s}' % \
+      (opts, self.fn, self.plotn - 1)
+%    \end{macrocode}
+% \iffalse
+%</staticmod>
+% \fi
+%
 % \section{Credits and acknowledgements}
 %
 % According to the original README file, this system was originally

sagetexpackage.ins

 
 \declarepreamble\defaultpreamble
 
-This is a generated file.
+This is a generated file. It is part of the SageTeX package.
 
 Copyright (C) 2008 by Dan Drake <ddrake@member.ams.org>
 
 \endpreamble
 
 % We use a name other than `sagetex' because when using the sagetex
-% package in a file called `foo.dtx', a file `foo.py' will be produced
-% -- and below, we generate a file called sagetex.py. Thus we use a name
-% other than `sagetex.dtx' so that building the documentation doesn't
-% clobber the very Python file we need to build the documentation!
+% package in a file called `foo.dtx', a file `foo.py' will be
+% produced---and below, we generate a file called sagetex.py. Thus we
+% use a name other than `sagetex.dtx' so that building the documentation
+% doesn't clobber the very Python file we need to build the
+% documentation!
 
 \generate{\file{sagetex.sty}{\from{sagetexpackage.dtx}{latex}}}
 
 % file.
 
 \edef\defaultpreamble{"""^^J%
-          \defaultpreamble^^J%
-          """}
+  \defaultpreamble^^J%
+  """}
 \edef\defaultpostamble{"""^^J%
-          \defaultpostamble^^J%
-          """}
+  \defaultpostamble^^J%
+  """}
 
 \generate{\file{sagetex.py}{\from{sagetexpackage.dtx}{python}}}
+\generate{\file{desagetexparser.py}{\from{sagetexpackage.dtx}{staticmod}}}
+
+% Now we modify the preamble again to get the shebang line at the top.
+
+\catcode`\#=12
+\newcommand{\shebang}{#!}
+\catcode`\#=6
+\edef\defaultpreamble{\shebang/usr/bin/env python^^J\defaultpreamble}
+
+\generate{\file{makestatic.py}{\from{sagetexpackage.dtx}{staticscript}}}
 
 \obeyspaces
 \Msg{******************************************************************}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.