#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	README
#	sweb.c
#	sweb.1
#	reader.sw
# This archive created: Wed Jul 27 06:01:10 1994
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# SchemeWEB Makefile 
# $Id: Makefile,v 2.0 1994/02/25 13:50:51 ramsdell Exp $

CFLAGS = -O
srcdir = .
VPATH = .
INSTALL = cp
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL)

prefix = /usr/local
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
mandir = $(prefix)/man
manext = 1

SHELL=/bin/sh
DISTFILES=Makefile README sweb.c sweb.1 reader.sw

all:	sweb

sweb:	sweb.c
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(srcdir)/sweb.c

install:	sweb sweb.1
	$(INSTALL_PROGRAM) sweb $(bindir)/sweb
	$(INSTALL_DATA) sweb.1 $(mandir)/man$(manext)/sweb.$(manext)

uninstall:
	rm $(bindir)/sweb
	rm $(mandir)/man$(manext)/sweb.$(manext)

dist:	schemeweb.sh

schemeweb.sh:	$(DISTFILES)
	shar $(DISTFILES) > $@

clean:
	-rm sweb

.PHONY:	all install uninstall dist clean
SHAR_EOF
fi # end of overwriting check
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
$Id: README,v 2.1 94/07/22 07:00:10 ramsdell Exp $

SchemeWEB---Simple support for literate programming in Lisp.

SchemeWEB is a Unix filter that allows you to generate both Lisp and
LaTeX code from one source file.  The generated LaTeX code formats
Lisp programs in typewriter font obeying the spacing in the source
file.  Comments can include arbitrary LaTeX commands.  SchemeWEB was
originally developed for the Scheme dialect of Lisp, but it can easily
be used with most other dialects.

Version 2.1 implements several improvements over the version 1.2 which
was released in 1990.

* The LaTeX source generated from a SchemeWEB file no longer requires
the use of a style option, such as the astyped option required by the
previous version.  The output is similar to that produced by c2latex.

* By default, Lisp source generated from a SchemeWEB file includes all
comments.  As a result, the filter can translate the Lisp source
generated from a SchemeWEB file directly into LaTeX source.

* The filter can translate the Lisp source generated from a SchemeWEB
file into the original SchemeWEB file.

As a result of these changes, a SchemeWEB file can be stored and
distributed as a standard Lisp file by tangling the SchemeWEB file.
For the purposes of editing comments, the SchemeWEB file can be
recovered by untangling the Lisp file.  LaTeX documentation can be
generated from both the Lisp file or the SchemeWEB file.

Version 2.1 patches version 2.0 so it now correctly formats strings
which span more than one line of text.

Installation:

1) Create the executable by typing:

	make

2) Install the executable and the manual page by typing:

	make install

By default the makefile will install the executable in $(prefix)/bin
and the manual page in $(prefix)/man/man1, where prefix=/usr/local.
You can change the prefix by typing:

	make prefix=<Your path> install

You can also change the installation targets by editing the makefile.

3) You can modify the reader used by your Lisp system's standard LOAD
so that it can read SchemeWEB files directly.  To make this change,
follow the model given in "reader.sw", a reader of SchemeWEB source
for the R4RS dialect of Scheme.

A formatted version of the manual page follows.

-----------------------------------------------------------------

sweb(1)                  USER COMMANDS                    sweb(1)



NAME
     sweb - Simple support for literate programming in Lisp.

SYNOPSIS
     sweb [-stuvwx] [input_file [output_file]]

DESCRIPTION
     A SchemeWEB file is a Lisp source file which  contains  code
     sections  and  comment sections, but each section is identi-
     fied in a novel way.  A code  section  begins  with  a  line
     whose  first  character is a left parenthesis.  It continues
     until a line is found which contains  the  parenthesis  that
     matches the one which started the code section.  The remain-
     ing lines of text in the source file  are  treated  as  com-
     ments.

     Several operations involving SchemeWEB files are provided by
     the sweb program.

     The -t option translates a SchemeWEB file  into  a  standard
     Lisp  file.   It simply adds an initial semicolon to comment
     lines that lack  them,  and  passes  code  sections  through
     unchanged.   This  process  is called tangling the SchemeWEB
     file.

     The -w option translates  a  SchemeWEB  file  into  a  LaTeX
     source  file.   Text is added to code sections so that LaTeX
     will print the code verbatim.  Comment sections  are  passed
     through  unchanged  except  that if the first character of a
     line is a semicolon, it is excised.  This makes it  possible
     to  process  a  SchemeWEB file which has been tangled.  This
     process is called weaving the SchemeWEB file.  This  is  the
     default action of sweb.

     The -x option weaves a SchemeWEB file as above,  except  the
     LaTeX source produced inhibits page breaks in code sections.

     The -u option translates a Lisp file which has been produced
     from a SchemeWEB file back into a SchemeWEB file.  It simply
     removes the first semicolon in every line that  starts  with
     one  as  its first character.  This process is called untan-
     gling a sweb produced Lisp file.

     The -s option tangles a SchemeWEB file and strips  all  com-
     ments from the file.

     The -v option prints version information.

     A SchemeWEB file can be stored and distributed as a standard
     Lisp  file by tangling the SchemeWEB file.  For the purposes
     of editing comments, the SchemeWEB file can be recovered  by
     untangling  the  Lisp  file.   LaTeX  documentation  can  be
     generated from both the Lisp file or the SchemeWEB file.

     SchemeWEB was originally developed for the Scheme dialect of
     Lisp;  however,  it  became  obvious  that  no  changes were
     required to support most other dialects.   SchemeWEB's  name
     reflects its heritage.
SHAR_EOF
fi # end of overwriting check
if test -f 'sweb.c'
then
	echo shar: will not over-write existing file "'sweb.c'"
else
cat << \SHAR_EOF > 'sweb.c'
/* SchemeWEB -- WEB for Lisp.  John D. Ramsdell.
 * Simple support for literate programming in Lisp.
 */

/* 	$Id: sweb.c,v 2.1 94/07/27 06:00:56 ramsdell Exp $	 */

#ifndef lint
static char vcid[] = "$Id: sweb.c,v 2.1 94/07/27 06:00:56 ramsdell Exp $";
static char copyright[] = "Copyright 1994 by The MITRE Corporation.";
#endif /* lint */

#define VERSION "2.1"

/*
 * Copyright 1994 by The MITRE Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 1, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * For a copy of the GNU General Public License, write to the 
 * Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

/*
This program processes SchemeWEB files.  A SchemeWEB file is a Lisp
source file which contains code sections and comment sections, but
each section is identified in a novel way.  A code section begins with
a line whose first character is a left parenthesis.  It continues
until a line is found which contains the parenthesis that matches the
one which started the code section.  The remaining lines of text in
the source file are treated as comments.  Several operations involving
SchemeWEB files are provided by the this program.  See the manual
page for a complete description of the various operations.
*/

/* SchemeWEB is currently set up for use with LaTeX. */

/* Define TANGLE to make a program which translates SchemeWEB source
into Scheme source by default. */

/* Define SAVE_LEADING_SEMICOLON if you want text lines to be copied 
with any leading semicolon while weaving. */

#include <stdio.h>

typedef enum {FALSE, TRUE} bool;

/* Runtime flags */
bool weaving;			/* Weaving or tangling? */
bool strip_comments;		/* Strip comments while tangling. */

/* Formatting commands added into weaved documents. */
char *begin_comment = "\\mbox{"; /* This pair is used */
char *end_comment = "}";	/* to surround comments in code. */
char *begin_code = "\\begin{flushleft}\n"; /* This pair is used */
char *end_code = "\\end{flushleft}\n"; /* to surround code. */
char *code_line_separator = "\\\\ ";
char *begin_code_line = "\\verb|"; /* This pair is used */
char *end_code_line = "|";	/* to surround code lines. */

/* Information for error messages. */
char *prog = NULL;		/* Name of program. */
char *src = NULL;		/* Name of input file. */
int lineno = 1;			/* Line number. */

/* Output occurs through putchar, putstring, and code_putchar. */

#define putstring(s) (fputs(s, stdout))

int				/* Used while printing */
code_putchar(c)			/* a code section. */
     int c;
{
  if (c == '|' && weaving) return putstring("|\\verb-|-\\verb|");
  else return putchar(c);
}

/* All input occurs in the following routines so that TAB characters
can be expanded while weaving. TeX treats TAB characters as a
space--not what is wanted. */

int ch_buf;			/* Used to implement */
bool buf_used = FALSE;		/* one character push back. */

int 
getchr()
{
  int c;
  static int spaces = 0;	/* Spaces left to print a TAB. */
  static int column = 0;	/* Current input column. */
  if (buf_used) {
    buf_used = FALSE;
    return ch_buf;
  }
  if (spaces > 0) {
    spaces--;
    return ' ';
  }
  switch (c = getc(stdin)) {
  case '\t':
    if (!weaving) return c;
    spaces = 7 - (7&column);	/* Maybe this should be 7&(~column). */
    column += spaces + 1;
    return ' ';
  case '\n':
    lineno++;
    column = 0;
    return c;
  default:
    column++;
    return c;
  }
}

void 
ungetchr(c)
     int c;
{
  buf_used = TRUE;
  ch_buf = c;
}

/* Error message for end of file found in code. */
bool 
report_eof_in_code()
{
  fprintf(stderr, "End of file within a code section.\n");
  return TRUE;
}

bool 
copy_text_saw_eof()		/* Copies a line of text out. */
{				/* Used while printing */
  int c;			/* a text section. */
  while (1) {
    c = getchr();
    if (c == EOF) return TRUE;
    if (c == '\n') return FALSE;
    putchar(c);
  }
}

bool 
strip_text_saw_eof()		/* Gobbles up a line of input. */
{
  int c;
  while (1) {
    c = getchr();
    if (c == EOF) return TRUE;
    if (c == '\n') return FALSE;
  }
}

bool				/* This copies comments */
copy_comment_saw_eof()		/* within code sections. */
{				
  if (weaving) putstring(begin_comment);
  putchar(';');
  if (copy_text_saw_eof()) return TRUE;
  if (weaving) putstring(end_comment);
  return FALSE;
}

bool				/* Copies a string found */
copy_string_saw_eof()		/* within a code section. */
{
  int c;
  while (1) {
    c = getchr();
    if (c == EOF) return TRUE;
    if (c == '\n') {		/* Found a string which continues on */
      if (weaving)		/* a new line. */
	putstring(end_code_line);
      putchar(c);		/* Close existing line, and then */
      if (weaving) {		/* begin copying the rest of */
	putstring(code_line_separator);	/* on the next line. */
	putstring(begin_code_line);
      }
      continue;
    }
    code_putchar(c);
    switch (c) {
    case '"': return FALSE;
    case '\\':
      c = getchr();
      if (c == EOF) return TRUE;
      code_putchar(c);
    }
  }
}

bool 
maybe_char_syntax_saw_eof()
{				/* Makes sure that the character */
  int c;			/* #\( does not get counted in */
  c = getchr();			/* balancing parentheses. */
  if (c == EOF) return TRUE;
  if (c != '\\') {
    ungetchr(c);
    return FALSE;
  }
  code_putchar(c);
  c = getchr();
  if (c == EOF) return TRUE;
  code_putchar(c);
  return FALSE;
}

bool				/* Copies a code section */
copy_code_failed()		/* containing S-exprs. */
{
  int parens = 1;		/* Used to balance parentheses. */
  int c;
  while (1) {			/* While parens are not balanced, */
    c = getchr();
    if (c == EOF)		/* Report failure on EOF. */
      return report_eof_in_code();
    if (c == '\n' && weaving)
      putstring(end_code_line);
    if (c == ';') { 		/* Report failure on EOF in a comment. */
      if (weaving) putstring(end_code_line);
      if (strip_comments
	  ? strip_text_saw_eof()
	  : copy_comment_saw_eof())
	return report_eof_in_code();
      else
	c = '\n';
    }
    code_putchar(c);		/* Write the character and then see */
    switch (c) {		/* if it requires special handling. */
    case '(':
      parens++;
      break;
    case ')':
      parens--;			
      if (parens < 0) {
	fprintf(stderr, "Too many right parentheses found.\n");
	return TRUE;
      }
      break;
    case '"':			/* Report failure on EOF in a string. */
      if (copy_string_saw_eof()) {
	fprintf(stderr, "End of file found within a string.\n");
	return TRUE;
      }
      break;
    case '#':			/* Report failure on EOF in a character. */
      if (maybe_char_syntax_saw_eof())
	return report_eof_in_code();
      break;
    case '\n':
      if (parens == 0) return FALSE;
      if (weaving) {
	putstring(code_line_separator);
	putstring(begin_code_line);
      }
    }
  }
}

int 
schemeweb()
{
  int c;
  while (1) {			/* At loop start it's in text mode */
    c = getchr();		/* and at the begining of a line. */
    if (c == '(') {		/* text mode changed to code mode. */
      if (weaving) putstring(begin_code);
      do {			/* Copy code. */
	if (weaving) putstring(begin_code_line);
	putchar(c);
	if (copy_code_failed()) {
	  fputs(prog, stderr);
	  if (src != NULL)
	    fprintf(stderr, ":%s:", src);
	  else
	    fputs(":<stdin>:", stderr);
	  fprintf(stderr,
		  "%d: Error in a code section.\n",
		  lineno);
	  return 1;
	}
	c = getchr();		/* Repeat when there is code */
      } while (c == '(');	/* immediately after some code. */
      if (weaving) putstring(end_code);
    }
    /* Found a text line--now in text mode. */
#if !defined SAVE_LEADING_SEMICOLON
    if (c == ';' && weaving)
      c = getchr();
#endif
    if (c == EOF) return 0;	/* Files that do not end with */
    ungetchr(c);		/* a newline are okay. */

    if (strip_comments) {
      if (strip_text_saw_eof()) return 0;
    }
    else {
      if (c != '\n' && !weaving) putchar(';');
      if (copy_text_saw_eof()) return 0; /* Copy a text line. */
      putchar('\n');
    }
  }
}

int				/* Removes any semicolons */
untangle()			/* than start a line of text. */
{
  int c;
  
  while (1) {			/* At a beginning of a line of text */
    c = getchar();		/* when at this point in the code. */
    if (c == EOF) return 0;
    if (c != ';') putchar(c);
    while (c != '\n') {
      c = getchar();
      if (c == EOF) return 0;
      putchar(c);
    }
  }
}

bool				/* Open the file arguments */
open_file_args_failed(argc, argv)
     int argc;
     char *argv[];
{
  switch (argc) {
  case 2:
  case 1:
    src = argv[0];		/* Save for error messages. */
    if (NULL == freopen(argv[0], "r", stdin)) {
      fprintf(stderr, "Cannot open %s for reading.\n", argv[0]);
      break;
    }
    if (argc == 2 && NULL == freopen(argv[1], "w", stdout)) {
      fprintf(stderr, "Cannot open %s for writing.\n", argv[1]);
      break;
    }
  case 0:
    return FALSE;
  }
  return TRUE;
}

int 
usage()
{
  fprintf(stderr, 
	  "Usage: %s [-stuvwx] [input_file [output_file]]\n%s%s%s%s%s%s",
	  prog,
	  "\t-s:  tangle input stripping comments\n",
	  "\t-t:  tangle input retaining comments\n",
	  "\t-u:  untangle input\n",
	  "\t-v:  print version information\n",
	  "\t-w:  weave input\n",
	  "\t-x:  weave input and exclude line breaks in code sections\n");
  fprintf(stderr, "The default option is %s.\n",
#if defined TANGLE
	  "-t"
#else
	  "-w"
#endif
	  );
  return 1;
}

int 
main (argc, argv)
     int argc;
     char *argv[];
{
  bool untangling = FALSE;
#if defined TANGLE
  weaving = FALSE;
#else
  weaving = TRUE;
#endif
  strip_comments = FALSE;

  prog = argv[0];		/* Save program name for error messages. */

  /* Option processing.  Note only one option can be requested at a time. */
  /* -s: tangle input stripping comments. */
  /* -t: tangle input retaining comments. */
  /* -u: untangle input. */
  /* -v: print version information. */
  /* -w: weave input. */
  /* -x: weave input and exclude line breaks in code sections. */
  if (argc > 1 && argv[1][0] == '-') {
    switch (argv[1][1]) {
    case 's': weaving = FALSE; strip_comments = TRUE; break;
    case 't': weaving = FALSE; break;
    case 'u': untangling = TRUE; break;
    case 'v':
      fprintf(stderr, "This is SchemeWEB version %s.\n", VERSION);
      return 0;
    case 'w': weaving = TRUE; break;
    case 'x': weaving = TRUE; code_line_separator = "\\\\* "; break;
    default:
      fprintf(stderr, "Bad option: -%c.\n", argv[1][1]);
      return usage();
    }
    if (argv[1][2] != '\0') {
      fprintf(stderr, "Only one option allowed.\n");
      return usage();
    }
    argc--; argv++;
  }

  if (open_file_args_failed(argc - 1, argv + 1)) return usage();

  if (untangling) return untangle();
  return schemeweb();
}
SHAR_EOF
fi # end of overwriting check
if test -f 'sweb.1'
then
	echo shar: will not over-write existing file "'sweb.1'"
else
cat << \SHAR_EOF > 'sweb.1'
.TH sweb 1
.\" $Id: sweb.1,v 2.0 1994/02/25 13:43:28 ramsdell Exp $
.SH NAME
sweb \- Simple support for literate programming in Lisp.
.SH SYNOPSIS
.B sweb
[-stuvwx] [input_file [output_file]]
.SH DESCRIPTION
A SchemeWEB file is a Lisp source file which contains code sections
and comment sections, but each section is identified in a novel way.
A code section begins with a line whose first character is a left
parenthesis.
It continues until a line is found which contains the
parenthesis that matches the one which started the code section.
The remaining lines of text in the source file are treated as comments.

Several operations involving SchemeWEB files are provided by the
.B sweb
program.
.PP
The 
.B \-t
option translates a SchemeWEB file into a standard Lisp file.
It simply adds an initial semicolon to comment lines that lack them, and
passes code sections through unchanged.
This process is called tangling the SchemeWEB file.
.PP
The 
.B \-w
option translates a SchemeWEB file into a LaTeX source file.
Text is added to code sections so that LaTeX will print the code verbatim.
Comment sections are passed through unchanged except that if the first
character of a line is a semicolon, it is excised.
This makes it possible to process a SchemeWEB file which has been tangled.
This process is called weaving the SchemeWEB file.
This is the default action of
.B sweb.
.PP
The 
.B \-x
option weaves a SchemeWEB file as above, except the LaTeX source
produced inhibits page breaks in code sections.
.PP
The 
.B \-u
option translates a Lisp file which has been produced from a
SchemeWEB file back into a SchemeWEB file.
It simply removes the first semicolon in every line that starts with
one as its first character.
This process is called untangling a
.B sweb
produced Lisp file.
.PP
The
.B \-s
option tangles a SchemeWEB file and strips all comments from the file.
.PP
The
.B \-v
option prints version information.
.PP
A SchemeWEB file can be stored and distributed as a standard Lisp
file by tangling the SchemeWEB file.  
For the purposes of editing comments, the SchemeWEB file can be
recovered by untangling the Lisp file.  
LaTeX documentation can be generated from both the Lisp file or the
SchemeWEB file.
.PP
SchemeWEB was originally developed for the Scheme dialect of Lisp;
however, it became obvious that no changes were required to support
most other dialects.  
SchemeWEB's name reflects its heritage.

SHAR_EOF
fi # end of overwriting check
if test -f 'reader.sw'
then
	echo shar: will not over-write existing file "'reader.sw'"
else
cat << \SHAR_EOF > 'reader.sw'
\documentstyle{article}
% $Id: reader.sw,v 2.0 1994/02/25 14:03:14 ramsdell Exp $
\title{{\tt read-sw}}
\author{John D. Ramsdell}
\date{\verb$Date: 1994/02/25 14:03:14 $}

\newcommand{\schemeweb}{Scheme{\tt WEB}}

\begin{document}

\maketitle

\verb|read-sw| reads a {\schemeweb} source file
much as \verb|read| reads a Scheme source file.
(define (read-sw . rest)		; Returns what \verb|read| returns.
  (let ((port (if (pair? rest)		; \verb|read-sw| arguments are
		  (car rest)		; the same as \verb|read|'s.
		  (current-input-port))))
    (letrec				
	((text-mode-and-saw-newline	; Lines of a {\schemeweb} file
	  (lambda ()			; beginning with ``{\tt(}'', 
	    (let ((ch (peek-char port))) ; start a code section.
	      (cond ((eof-object? ch) ch)
		    ((char=? ch #\()	; If code section, then use
		     (got-code (read port))) ; \verb|read| to get code,
		    (else		; else skip this line as it
		     (text-mode-within-a-line)))))) ; is a comment.
	 (text-mode-within-a-line
	  (lambda ()			; Ignore comments.
	    (let ((ch (read-char port)))
	      (cond ((eof-object? ch) ch)
		    ((char=? ch #\newline)
		     (text-mode-and-saw-newline))
		    (else (text-mode-within-a-line))))))
	 (got-code
	  (lambda (code)		; Ignore the remainder of the 
	    (let ((ch (read-char port))) ; last code line and return
	      (cond ((eof-object? ch) code) ;  the results of \verb|read|.
		    ((char=? ch #\newline)
		     code)
		    (else (got-code code)))))))
    (text-mode-and-saw-newline)		; Start by looking 
    )))					; for a code line.
\end{document}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

