1 #This file was created by <kayvan> Sun May 2 15:56:35 1999
2 #LyX 1.0 (C) 1995-1999 Matthias Ettrich and the LyX Team
4 \textclass literate-article
7 % This relaxes the noweb constraint that chunks are
8 % never broken across pages.
10 % This is from the noweb FAQ
12 \def\nwendcode{\endtrivlist \endgroup}
13 \let\nwdocspar=\smallbreak
16 \inputencoding default
19 \paperfontsize default
25 \paperorientation portrait
28 \paragraph_separation indent
30 \quotes_language english
34 \paperpagestyle default
44 Sylvan <kayvan@sylvan.com>
50 This document describes and implements a perl script for importing noweb
55 \begin_inset LatexCommand \tableofcontents{}
65 Since version 1.0.1, LyX now supports Literate Programming using
70 This addition to LyX made it very pleasant to write programs in the literate
71 style (like this one).
72 In addition to being able to write new literate programs, it would be quite
77 code could be imported into LyX in some fashion.
78 That's where this program comes in.
98 # Copyright (C) 1999 Kayvan A.
99 Sylvan <kayvan@sylvan.com>
103 You are free to use and modify this code under the terms of
105 # the GNU General Public Licence version 2 or later.
111 Written with assistance from:
116 <edmar-w-jr@technologist.com>
120 Amir Karger <karger@post.harvard.edu>
124 # $Id: noweb2lyx.lyx,v 1.1 1999/09/27 18:44:32 larsbj Exp $
128 # NOTE: This file was automatically generated from noweb2lyx.lyx using noweb.
132 <<Setup variables from user supplied args>>
136 <<Convert noweb to LyX>>
141 The Noweb file defined
148 file is a collection of documentation and code chunks.
149 Documentation chunks simply start with an ``@'' and have no name:
152 @ Here is some documentation.
154 We can do arbitrary LaTeX code here.
160 Code chunks look like this:
163 <<Name of chunk here>>=
166 code for the chunk goes here ...}
171 The ``@'' is a necessary delimiter to end the code chunk.
172 The other form that the ``@'' line takes is as follows:
175 <<Name of chunk here>>=
178 code for the chunk ...}
180 @ %def identifier1 identifier2
183 In the latter form, we are declaring to
187 that this code chunk defines identifier1, identifier2, etc.
190 When first tackling this problem, I spoke with members of the LyX team that
191 knew about the literate programming extensions and reLyX (the LaTeX importing
195 One of the first ideas was to extend the reLyX code to understand the
200 This proved to be too hard and presents other problems
201 \begin_float footnote
204 Not the least of these problems is the fact that << is a quote in French.
207 On the other hand, it turns out that reLyX contains a very useful literal
209 If the input file contains the construct
224 then reLyX will copy the surrounded code to the output file verbatim.
225 Given this, the first part of the translation is easy; we simply have to
226 copy the code chunks into an intermediate file that surrounds them with
242 Once reLyX is done with the input file, the problem is reduced to changing
243 the code chunks from LyX's LaTeX layout to the Scrap layout.
246 There is one final constraint on
251 We want to be able to run it as a simple pre-processor and post-processor
253 We can accomplish this by setting the flags
261 before we reach the main conversion code.
264 With all that preamble out of the way, we now have the basic high-level
265 outline for our code:
268 <<Convert noweb to LyX>>=
276 <<Transform noweb for reLyX>>
280 if ((!$pre_only) && (!$post_only)) {
286 <<Run reLyX on intermediate file>>
305 Making a file that reLyX can process
308 In this section, we present the code that performs the task of creating
309 the intermediate file that reLyX can process, using the algorithm that
311 This algorithm is outlined in the code that follows:
314 <<Transform noweb for reLyX>>=
316 <<Setup INPUT and OUTPUT>>
318 inputline: while(<INPUT>)
336 >=/) { # Beginning of a noweb scrap
346 <<Read in and output the noweb code chunk>>
354 s+(.*)/) { # Beginning of a documentation chunk
364 print OUTPUT $1; # We do not need the ``@'' part
378 ]/) { # noweb quoted code
388 <<Perform special input quoting of [[var]]>>
404 print OUTPUT; # Just let the line pass through
414 <<Close INPUT and OUTPUT>>
419 In the code above, we do some pre-processing of the noweb ``[[...]]'' construct.
420 This avoids some problems with reLyX confusing lists composed of ``[[...]]''
424 <<Perform special input quoting of [[var]]>>=
443 file, once we have identified a
447 code chunk, we transform it into a form that is usable by reLyX.
450 <<Read in and output the noweb code chunk>>=
452 <<Save the beginning of the scrap to savedScrap>>
454 <<Concatenate the rest of the scrap>>
456 <<print out the scrap in a reLyXskip block>>
461 File input and output for the pre-processing step
476 to read and write files.
477 In the code fragment above, we need to read from the input file and write
478 to a file that will be later transformed by reLyX.
479 If we are being called only to pre-process the input file, then there is
480 no need to create a temporary file.
483 <<Setup INPUT and OUTPUT>>=
491 &setup_files($input_file, $output_file);
499 $relyx_file = "temp$$";
505 &setup_files($input_file, $relyx_file);
512 This code uses a small perl subroutine,
516 , which we define below:
533 open(INPUT, "<$in") || die "Can not read $in: $!
541 open(OUTPUT, ">$out") || die "Can not write $out: $!
561 After we see the beginning of the scrap, we need to read in and save the
562 rest of the scrap for output.
565 <<Save the beginning of the scrap to savedScrap>>=
574 <<Concatenate the rest of the scrap>>=
576 scrapline: while (<INPUT>) {
582 last scrapline if /^@
602 s+$/) {$savedScrap .= $_; last switch; }
610 s+%def.*$/) {$savedScrap .= $_; last switch; }
618 s+(.*)$/) {$savedScrap .= "@
629 Printing out the scrap
632 The final piece of the first pass of the conversion is done by this code.
635 <<print out the scrap in a reLyXskip block>>=
645 print OUTPUT $savedScrap;
657 print OUTPUT "$endLine";
662 Finally, we need to close the
673 <<Close INPUT and OUTPUT>>=
685 In this section, we describe and implement the code that runs reLyX on the
694 Selecting the document class
697 In order to run reLyX, we need to know the article class of the input document
698 (to choose the corresponding literate document layout).
699 For this, we need to parse the intermediate file.
702 <<Run reLyX on intermediate file>>=
704 <<Parse for document class>>
706 <<Run reLyX with document class>>
711 In the code below, you'll see a strange regular expression to search for
713 The reason for this kludge is that without it, we can't run
721 file that is generated by LyX
722 \begin_float footnote
737 class and gets confused, so we have to obfuscate it slightly.
740 With the regular expression as it is, we can actually run
744 on itself and a produce a quite reasonable LyX file.
747 <<Parse for document class>>=
749 open(INPUT, "<$relyx_file") ||
755 die "Can not read $relyx_file: $!
759 $class = "article"; # default if none found
761 parse: while(<INPUT>) {
771 docu[m]entclass{(.*)}/) {
806 Running reLyX with the corresponding literate document layout
809 Now that we know what the document class ought to be, we do:
812 <<Run reLyX with document class>>=
814 $doc_class = "literate-" .
817 die "reLyX returned non-zero: $!
825 if (system("reLyX -c $doc_class $relyx_file"));
830 reLyX performs the main bulk of the translation work.
831 Note that if the ``literate-
835 '' document layout is not found, then reLyX will fail with an error.
836 In that case, you may need to modify your
840 input file to a supported document type.
843 Fixing the reLyX output
846 We need to perform some post-processing of what reLyX produces in order
847 to have the best output for our literate document.
848 The outline of the post-processing steps are:
853 <<Setup INPUT and OUTPUT for the final output>>
863 <<Fix code chunks in latex layout>>
869 <<Fix [[var]] noweb construct>>
875 print OUTPUT; # default
879 <<Close INPUT and OUTPUT>>
884 Note that in the perl code that is contained in the
888 loop above, the perl construct
892 is sufficient to restart the loop.
893 We can use this construct to do some relatively complex parsing of the
894 reLyX generated file.
897 File input and output for the post-processing
908 is taken care of by this code:
911 <<Setup INPUT and OUTPUT for the final output>>=
919 &setup_files("$input_file", "$output_file");
925 &setup_files("$relyx_file.lyx", "$output_file");
932 Making sure the code chunks are in the Scrap layout
935 Now, as we outlined before, the final step is transforming the code-chunks
936 which have been put into a LaTeX layout by LyX into the scrap layout.
939 <<Fix code chunks in latex layout>>=
945 latex latex/) { # Beginning of some latex code
951 if (($line = <INPUT>) =~ /^
953 s*<</) { # code scrap
961 <<Transform this chunk into layout scrap>>
979 latex latex line + next line
989 print OUTPUT "$_$line";
1000 \protected_separator
1008 When we are sure that we are in a code chunk, we must read in the rest of
1009 the code chunk and output a scrap layout for it:
1012 <<Transform this chunk into layout scrap>>=
1024 codeline: while (<INPUT>) {
1027 \protected_separator
1029 \protected_separator
1033 \protected_separator
1035 \protected_separator
1036 last codeline if /^@
1042 print OUTPUT $savedScrap;
1044 <<Slurp up to the end of the latex layout>>
1049 Okay, now we just need to eat the rest of the latex layout.
1050 There should only be a few different types of lines for us to match:
1053 <<Slurp up to the end of the latex layout>>=
1055 slurp: while (<INPUT>) {
1058 \protected_separator
1060 \protected_separator
1068 \protected_separator
1070 \protected_separator
1078 \protected_separator
1080 \protected_separator
1086 \protected_separator
1088 \protected_separator
1089 warn "confused by line: $_";
1111 allows the user to use a special code quoting mechanism in documentation
1113 Fixing this ``[[quoted-code]]''
1117 syntax means putting the ``[[quoted-code]]'' in a LaTeX layout in the LyX
1119 Otherwise, LyX will backslash-quote the brackets, creating ugly output.
1120 The quoted-code is transformed by
1124 when it generates the final LaTeX code.
1127 <<Fix [[var]] noweb construct>>=
1137 ]/) { # special code for [[var]]
1140 \protected_separator
1142 \protected_separator
1168 \protected_separator
1170 \protected_separator
1174 \protected_separator
1176 \protected_separator
1184 Cleaning up intermediate files
1187 The cleanup code is very simple:
1192 system("rm -f $relyx_file*") unless ($post_only || $pre_only);
1197 User supplied arguments
1204 script understands two arguments, input-file and output-file.
1205 It is also set up to be used internally by reLyX to pre-process or postprocess
1206 files in the import pipeline.
1209 <<Setup variables from user supplied args>>=
1211 &usage() if ($#ARGV < 1); # zero or one argument
1213 if ($ARGV[0] eq "-pre") {
1216 \protected_separator
1218 \protected_separator
1219 &usage unless ($#ARGV == 2);
1222 \protected_separator
1224 \protected_separator
1225 $input_file = $ARGV[1]; $output_file = $ARGV[2]; $pre_only = 1;
1227 } elsif ($ARGV[0] eq "-post") {
1230 \protected_separator
1232 \protected_separator
1233 &usage unless ($#ARGV == 2);
1236 \protected_separator
1238 \protected_separator
1239 $input_file = $ARGV[1]; $output_file = $ARGV[2]; $post_only = 1;
1244 \protected_separator
1246 \protected_separator
1247 &usage unless ($#ARGV == 1);
1250 \protected_separator
1252 \protected_separator
1253 $input_file = $ARGV[0];
1254 \protected_separator
1255 $output_file = $ARGV[1];
1258 \protected_separator
1260 \protected_separator
1261 $pre_only = 0; $post_only = 0;
1265 @ %def input_file output_file pre_only post_only
1273 \protected_separator
1275 \protected_separator
1276 print "Usage: noweb2lyx [-pre | -post] input-file output-file
1280 If -pre is specified, only pre-processes the input-file for reLyX.
1282 Similarly, in the case of -post, post-processes reLyX output.
1284 In case of bugs, Email Kayvan Sylvan <kayvan
1291 \protected_separator
1293 \protected_separator
1308 The noweb2lyx script can be tangled from LyX if you set
1314 to call a generic script that always extracts a scrap named
1319 Here is an example of such a script:
1324 notangle -Rbuild-script $1 | sh
1331 notangle -Rnoweb2lyx.in noweb2lyx.nw > noweb2lyx.in
1333 sed -e "s=@PERL@=$PREFIX/bin/perl=" noweb2lyx.in > noweb2lyx