Ron, As I mentioned, code dies when the authors stop touching it. This is especially so with interns and students. Github is a graveyard of code for exactly that reason. Even important Phd code gets thrown away once the thesis is done. Literate programming is an attempt to overcome this. This is my pain-less way of doing it.Inflict it on your students.
In general, I write code in an Emacs buffer and run a shell in another buffer. I run a pdf browser in another window. As I work (usually every 10 lines or so of input) I run a little make command that latex's my file and compiles my code so I catch mistakes early. This method of programming strongly encourages writing explanations in latex as you code. Knuth defined a file format Foo. The file contained both code and explanation. weave Foo >foo.tex generates a tex file for processing tangle Foo >foo.code generates a code file for compiling The idea of weaving in order to generate tex seems pointless. In my scheme there is no longer a need for Knuth's weave program because the file is already latex code. The only required code is a simple tangle program. I've defined 2 latex macros (see tangle.lisp for details) \begin{chunk}{name} ... \end{chunk} delimits a named code block \getchunk{name} requests a code block Foo expansion As per Knuth, you extract code from your latex file using tangle. The tangle program walks the Foo.tex file. Every chunk is put into a hash table under the given name (multiple name uses are concatenated into one hash block). The tangle program, when it finds a "getchunk" will output the named hash block. So to create a literate program you write standard latex and surround your code blocks with \begin{chunk}{name} ... \end{chunk} To extract that code block you write tangle Foo.tex name >name.code The default name is "*". You can name any (or all) blocks "*" and then tangle Foo.tex >the.code I tend to write a lot of named blocks and then have a single final block named "*" that consists of \begin{chunk}{*} \getchunk{block1} ... \getchunk{blockn} \end{chunk} The tangle program, when asked for "*" will then inline all the named code blocks on output. I've attached both lisp and C versions of the tangle command. To use C: gcc -o mytangle tanglec.c ./mytangle test.tex -- output the default block "*" ./mytangle test.tex foo -- output the block named "foo" pdflatex test.tex -- latex the file (pdf browser will update) Oh, by the way, you might already have a binary named 'tangle' on your system. This is NOT the tangle you're looking for. Use the attached code. (Aside: it would be a trivial but clever change to use latex to write the code file immediately during the latex step using \write. Then you don't need tangle at all. It all works by 'magic'...) Tim
tangle.lisp
Description: Binary data
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> // set this to 3 for further information #define DEBUG 0 /* forward reference for the C compiler */ int getchunk(char *chunkname); /* a memory mapped buffer copy of the file */ char *buffer; int bufsize; /* return the length of the next line */ int nextline(int i) { int j; if (i >= bufsize) return(-1); for (j=0; ((i+j < bufsize) && (buffer[i+j] != '\n')); j++); return(j); } /* output the line we need */ int printline(int i, int length) { int j; for (j=0; j<length; j++) { putchar(buffer[i+j]); } printf("\n"); return(0); } /* handle begin{chunk}{chunkname} */ /* is this chunk name we are looking for? && does the line start with \begin{chunk}? && is the next character a \{ && is the last character after the chunkname a \} */ int foundchunk(int i, char *chunkname) { if ((strncmp(&buffer[i+14],chunkname,strlen(chunkname)) == 0) && (strncmp(&buffer[i],"\\begin{chunk}",13) == 0) && (buffer[i+13] == '{') && (buffer[i+14+strlen(chunkname)] == '}')) { if (DEBUG==3) { printf("foundchunk(%s)\n",chunkname); } return(1); } return(0); } /* handle end{chunk} */ /* is it really an end? */ int foundEnd(int i, char* chunkname) { if ((buffer[i] == '\\') && (strncmp(&buffer[i+1],"end{chunk}",10) == 0)) { if (DEBUG==3) { printf("foundEnd(%s)\n",chunkname); } return(1); } return(0); } /* handle getchunk{chunkname} */ /* is this line a getchunk? */ int foundGetchunk(int i, int linelen) { int len; if (strncmp(&buffer[i],"\\getchunk{",10) == 0) { for(len=0; ((len < linelen) && (buffer[i+len] != '}')); len++); return(len-10); } return(0); } /* Somebody did a getchunk and we need a copy of the name */ /* malloc string storage for a copy of the getchunk name */ char *getChunkname(int k, int getlen) { char *result = (char *)malloc(getlen+1); strncpy(result,&buffer[k+10],getlen); result[getlen]='\0'; return(result); } /* print lines in this chunk, possibly recursing into getchunk */ int printchunk(int i, int chunklinelen, char *chunkname) { int k; int linelen; char *getname; int getlen = 0; if (DEBUG==3) { printf("=== \\start{%s} ===\n",chunkname); } for (k=i+chunklinelen+1; ((linelen=nextline(k)) != -1); ) { if ((getlen=foundGetchunk(k,linelen)) > 0) { getname = getChunkname(k,getlen); getchunk(getname); free(getname); k=k+getlen+12l; } else { if ((linelen >= 11) && (foundEnd(k,chunkname) == 1)) { if (DEBUG==3) { printf("=== \\end{%s} ===\n",chunkname); } return(k+12); } else { if (DEBUG==2) { printf("======== printchunk else %d %d\n",k,linelen); } printline(k,linelen); k=k+linelen+1; } }} if (DEBUG==2) { printf("=================\\out{%s} %d\n",chunkname,k); } return(k); } /* find the named chunk and call printchunk on it */ int getchunk(char *chunkname) { int i; int linelen; int chunklen = strlen(chunkname); if (DEBUG==3) { printf("getchunk(%s)\n",chunkname); } for (i=0; ((linelen=nextline(i)) != -1); ) { if (DEBUG==2) { printf("----"); printline(i,linelen); printf("----\n"); } if ((linelen >= chunklen+15) && (foundchunk(i,chunkname) == 1)) { if (DEBUG==2) { fprintf(stderr,"=================\\getchunk(%s)\n",chunkname); } i=printchunk(i,linelen,chunkname); } else { i=i+linelen+1; } } if (DEBUG==2) { fprintf(stderr,"=================getchunk returned=%d\n",i); } return(i); } /* memory map the input file into the global buffer and get the chunk */ int main(int argc, char *argv[]) { int fd; struct stat filestat; if ((argc == 1) || (argc > 3)) { perror("Usage: tangle filename chunkname"); exit(-1); } fd = open(argv[1],O_RDONLY); if (fd == -1) { perror("Error opening file for reading"); exit(-2); } if (fstat(fd,&filestat) < 0) { perror("Error getting input file size"); exit(-3); } bufsize = (int)filestat.st_size; buffer = mmap(0,filestat.st_size,PROT_READ,MAP_SHARED,fd,0); if (buffer == MAP_FAILED) { close(fd); perror("Error reading the file"); exit(-4); } if (argc == 2) { getchunk("*"); } else { getchunk(argv[2]); } close(fd); return(0); }
test.tex
Description: TeX document
_______________________________________________ Axiom-developer mailing list Axiom-developer@nongnu.org https://lists.nongnu.org/mailman/listinfo/axiom-developer