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

Attachment: 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);
}

Attachment: test.tex
Description: TeX document

_______________________________________________
Axiom-developer mailing list
Axiom-developer@nongnu.org
https://lists.nongnu.org/mailman/listinfo/axiom-developer

Reply via email to