1 #LyX 1.3 created this file. For more info see http://www.lyx.org/
3 \textclass literate-article
6 % This relaxes the noweb constraint that chunks are
7 % never broken across pages.
9 % This is from the noweb FAQ
11 \def\nwendcode{\endtrivlist \endgroup}
12 \let\nwdocspar=\smallbreak
15 \inputencoding default
18 \paperfontsize default
25 \use_numerical_citations 0
26 \paperorientation portrait
29 \paragraph_separation indent
31 \quotes_language english
35 \paperpagestyle default
45 Sylvan <kayvan@sylvan.com>
51 This document describes and implements a perl script for importing noweb
56 \begin_inset LatexCommand \tableofcontents{}
66 Since version 1.0.1, LyX now supports Literate Programming using
71 This addition to LyX made it very pleasant to write programs in the literate
72 style (like this one).
73 In addition to being able to write new literate programs, it would be quite
78 code could be imported into LyX in some fashion.
79 That's where this program comes in.
99 # Copyright (C) 1999 Kayvan A.
100 Sylvan <kayvan@sylvan.com>
102 # You are free to use and modify this code under the terms of
104 # the GNU General Public Licence version 2 or later.
108 # Written with assistance from:
110 # Edmar Wienskoski Jr.
111 <edmar-w-jr@technologist.com>
113 # Amir Karger <karger@post.harvard.edu>
117 # $Id: noweb2lyx.lyx,v 1.4 2003/01/17 13:50:11 lasgouttes Exp $
121 # NOTE: This file was automatically generated from noweb2lyx.lyx using noweb.
125 <<Setup variables from user supplied args>>
129 <<Convert noweb to LyX>>
134 The Noweb file defined
141 file is a collection of documentation and code chunks.
142 Documentation chunks simply start with an ``@'' and have no name:
145 @ Here is some documentation.
147 We can do arbitrary LaTeX code here.
153 Code chunks look like this:
175 code for the chunk goes here ...}
180 The ``@'' is a necessary delimiter to end the code chunk.
181 The other form that the ``@'' line takes is as follows:
203 code for the chunk ...}
205 @ %def identifier1 identifier2
208 In the latter form, we are declaring to
212 that this code chunk defines identifier1, identifier2, etc.
215 When first tackling this problem, I spoke with members of the LyX team that
216 knew about the literate programming extensions and reLyX (the LaTeX importing
220 One of the first ideas was to extend the reLyX code to understand the
225 This proved to be too hard and presents other problems
231 Not the least of these problems is the fact that << is a quote in French.
235 On the other hand, it turns out that reLyX contains a very useful literal
237 If the input file contains the construct
252 then reLyX will copy the surrounded code to the output file verbatim.
253 Given this, the first part of the translation is easy; we simply have to
254 copy the code chunks into an intermediate file that surrounds them with
270 Once reLyX is done with the input file, the problem is reduced to changing
271 the code chunks from LyX's LaTeX layout to the Scrap layout.
274 There is one final constraint on
279 We want to be able to run it as a simple pre-processor and post-processor
281 We can accomplish this by setting the flags
333 before we reach the main conversion code.
336 With all that preamble out of the way, we now have the basic high-level
337 outline for our code:
340 <<Convert noweb to LyX>>=
344 <<Transform noweb for reLyX>>
348 if ((!$pre_only) && (!$post_only)) {
350 <<Run reLyX on intermediate file>>
365 Making a file that reLyX can process
368 In this section, we present the code that performs the task of creating
369 the intermediate file that reLyX can process, using the algorithm that
371 This algorithm is outlined in the code that follows:
374 <<Transform noweb for reLyX>>=
376 <<Setup INPUT and OUTPUT>>
378 inputline: while(<INPUT>)
392 >=/) { # Beginning of a noweb scrap
394 <<Read in and output the noweb code chunk>>
398 s+(.*)/) { # Beginning of a documentation chunk
400 print OUTPUT $1; # We do not need the ``@'' part
410 ]/) { # noweb quoted code
412 <<Perform special input quoting of [[var]]>>
416 print OUTPUT; # Just let the line pass through
422 <<Close INPUT and OUTPUT>>
427 In the code above, we do some pre-processing of the noweb ``[[...]]'' construct.
428 This avoids some problems with reLyX confusing lists composed of ``[[...]]''
432 <<Perform special input quoting of [[var]]>>=
473 file, once we have identified a
477 code chunk, we transform it into a form that is usable by reLyX.
480 <<Read in and output the noweb code chunk>>=
482 <<Save the beginning of the scrap to savedScrap>>
484 <<Concatenate the rest of the scrap>>
486 <<print out the scrap in a reLyXskip block>>
491 File input and output for the pre-processing step
550 to read and write files.
551 In the code fragment above, we need to read from the input file and write
552 to a file that will be later transformed by reLyX.
553 If we are being called only to pre-process the input file, then there is
554 no need to create a temporary file.
557 <<Setup INPUT and OUTPUT>>=
561 &setup_files($input_file, $output_file);
565 $relyx_file = "temp$$";
567 &setup_files($input_file, $relyx_file);
574 This code uses a small perl subroutine,
600 , which we define below:
609 open(INPUT, "<$in") || die "Can not read $in: $!
613 open(OUTPUT, ">$out") || die "Can not write $out: $!
629 After we see the beginning of the scrap, we need to read in and save the
630 rest of the scrap for output.
633 <<Save the beginning of the scrap to savedScrap>>=
642 <<Concatenate the rest of the scrap>>=
644 scrapline: while (<INPUT>) {
646 last scrapline if /^@
658 s+$/) {$savedScrap .= $_; last switch; }
662 s+%def.*$/) {$savedScrap .= $_; last switch; }
666 s+(.*)$/) {$savedScrap .= "@
677 Printing out the scrap
680 The final piece of the first pass of the conversion is done by this code.
683 <<print out the scrap in a reLyXskip block>>=
693 print OUTPUT $savedScrap;
705 print OUTPUT "$endLine";
710 Finally, we need to close the
765 <<Close INPUT and OUTPUT>>=
777 In this section, we describe and implement the code that runs reLyX on the
808 Selecting the document class
811 In order to run reLyX, we need to know the article class of the input document
812 (to choose the corresponding literate document layout).
813 For this, we need to parse the intermediate file.
816 <<Run reLyX on intermediate file>>=
818 <<Parse for document class>>
820 <<Run reLyX with document class>>
825 In the code below, you'll see a strange regular expression to search for
827 The reason for this kludge is that without it, we can't run
835 file that is generated by LyX
897 class and gets confused, so we have to obfuscate it slightly.
901 With the regular expression as it is, we can actually run
905 on itself and a produce a quite reasonable LyX file.
908 <<Parse for document class>>=
910 open(INPUT, "<$relyx_file") ||
912 die "Can not read $relyx_file: $!
916 $class = "article"; # default if none found
918 parse: while(<INPUT>) {
924 docu[m]entclass{(.*)}/) {
939 Running reLyX with the corresponding literate document layout
942 Now that we know what the document class ought to be, we do:
945 <<Run reLyX with document class>>=
947 $doc_class = "literate-" .
950 die "reLyX returned non-zero: $!
954 if (system("reLyX -c $doc_class $relyx_file"));
959 reLyX performs the main bulk of the translation work.
960 Note that if the ``literate-
964 '' document layout is not found, then reLyX will fail with an error.
965 In that case, you may need to modify your
969 input file to a supported document type.
972 Fixing the reLyX output
975 We need to perform some post-processing of what reLyX produces in order
976 to have the best output for our literate document.
977 The outline of the post-processing steps are:
982 <<Setup INPUT and OUTPUT for the final output>>
988 <<Fix code chunks in latex layout>>
990 <<Fix [[var]] noweb construct>>
992 print OUTPUT; # default
996 <<Close INPUT and OUTPUT>>
1001 Note that in the perl code that is contained in the
1027 loop above, the perl construct
1053 is sufficient to restart the loop.
1054 We can use this construct to do some relatively complex parsing of the
1055 reLyX generated file.
1058 File input and output for the post-processing
1113 is taken care of by this code:
1116 <<Setup INPUT and OUTPUT for the final output>>=
1120 &setup_files("$input_file", "$output_file");
1124 &setup_files("$relyx_file.lyx", "$output_file");
1131 Making sure the code chunks are in the Scrap layout
1134 Now, as we outlined before, the final step is transforming the code-chunks
1135 which have been put into a LaTeX layout by LyX into the scrap layout.
1138 <<Fix code chunks in latex layout>>=
1144 latex latex/) { # Beginning of some latex code
1146 if (($line = <INPUT>) =~ /^
1148 s*<</) { # code scrap
1150 <<Transform this chunk into layout scrap>>
1156 latex latex line + next line
1158 print OUTPUT "$_$line";
1169 When we are sure that we are in a code chunk, we must read in the rest of
1170 the code chunk and output a scrap layout for it:
1173 <<Transform this chunk into layout scrap>>=
1185 codeline: while (<INPUT>) {
1189 last codeline if /^@
1195 print OUTPUT $savedScrap;
1197 <<Slurp up to the end of the latex layout>>
1202 Okay, now we just need to eat the rest of the latex layout.
1203 There should only be a few different types of lines for us to match:
1206 <<Slurp up to the end of the latex layout>>=
1208 slurp: while (<INPUT>) {
1226 warn "confused by line: $_";
1248 allows the user to use a special code quoting mechanism in documentation
1250 Fixing this ``[[quoted-code]]''
1254 syntax means putting the ``[[quoted-code]]'' in a LaTeX layout in the LyX
1256 Otherwise, LyX will backslash-quote the brackets, creating ugly output.
1257 The quoted-code is transformed by
1261 when it generates the final LaTeX code.
1264 <<Fix [[var]] noweb construct>>=
1274 ]/) { # special code for [[var]]
1309 Cleaning up intermediate files
1312 The cleanup code is very simple:
1317 system("rm -f $relyx_file*") unless ($post_only || $pre_only);
1322 User supplied arguments
1329 script understands two arguments, input-file and output-file.
1330 It is also set up to be used internally by reLyX to pre-process or postprocess
1331 files in the import pipeline.
1334 <<Setup variables from user supplied args>>=
1336 &usage() if ($#ARGV < 1); # zero or one argument
1338 if ($ARGV[0] eq "-pre") {
1340 &usage unless ($#ARGV == 2);
1342 $input_file = $ARGV[1]; $output_file = $ARGV[2]; $pre_only = 1;
1344 } elsif ($ARGV[0] eq "-post") {
1346 &usage unless ($#ARGV == 2);
1348 $input_file = $ARGV[1]; $output_file = $ARGV[2]; $post_only = 1;
1352 &usage unless ($#ARGV == 1);
1354 $input_file = $ARGV[0]; $output_file = $ARGV[1];
1356 $pre_only = 0; $post_only = 0;
1360 @ %def input_file output_file pre_only post_only
1367 print "Usage: noweb2lyx [-pre | -post] input-file output-file
1371 If -pre is specified, only pre-processes the input-file for reLyX.
1373 Similarly, in the case of -post, post-processes reLyX output.
1375 In case of bugs, Email Kayvan Sylvan <kayvan
1395 The noweb2lyx script can be tangled from LyX if you set
1401 to call a generic script that always extracts a scrap named
1406 Here is an example of such a script:
1411 notangle -Rbuild-script $1 | sh
1418 notangle -Rnoweb2lyx.in noweb2lyx.nw > noweb2lyx.in
1420 sed -e "s=@PERL@=$PREFIX/bin/perl=" noweb2lyx.in > noweb2lyx