summaryrefslogtreecommitdiff
path: root/web/spiderweb/src/master
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/spiderweb/src/master
Initial commit
Diffstat (limited to 'web/spiderweb/src/master')
-rw-r--r--web/spiderweb/src/master/Make.pretty18
-rw-r--r--web/spiderweb/src/master/Makefile18
-rw-r--r--web/spiderweb/src/master/WebMakefile146
-rw-r--r--web/spiderweb/src/master/common.c1001
-rw-r--r--web/spiderweb/src/master/common.h146
-rw-r--r--web/spiderweb/src/master/common.web1198
-rw-r--r--web/spiderweb/src/master/cycle.awk86
-rw-r--r--web/spiderweb/src/master/cycle.web81
-rw-r--r--web/spiderweb/src/master/junk.list22
-rw-r--r--web/spiderweb/src/master/multitang.ch107
-rw-r--r--web/spiderweb/src/master/newdefs.awk11
-rw-r--r--web/spiderweb/src/master/nodups.awk13
-rw-r--r--web/spiderweb/src/master/pathopen.c122
-rw-r--r--web/spiderweb/src/master/pathopen.h9
-rw-r--r--web/spiderweb/src/master/pathopen.web192
-rw-r--r--web/spiderweb/src/master/pretty.awk2
-rw-r--r--web/spiderweb/src/master/spider.awk4799
-rw-r--r--web/spiderweb/src/master/spider.web2580
-rw-r--r--web/spiderweb/src/master/tangle.web1800
-rw-r--r--web/spiderweb/src/master/transcheck.awk23
-rw-r--r--web/spiderweb/src/master/transcheck.list14
-rw-r--r--web/spiderweb/src/master/weave.web3250
-rw-r--r--web/spiderweb/src/master/webhead2
-rw-r--r--web/spiderweb/src/master/webtail1
24 files changed, 15641 insertions, 0 deletions
diff --git a/web/spiderweb/src/master/Make.pretty b/web/spiderweb/src/master/Make.pretty
new file mode 100644
index 0000000000..2b0b2b5820
--- /dev/null
+++ b/web/spiderweb/src/master/Make.pretty
@@ -0,0 +1,18 @@
+# Copyright 1989 by Norman Ramsey, Odyssey Research Associates
+# Not to be sold, but may be used freely for any purpose
+# For more information, see file COPYRIGHT in the parent directory
+
+WEBDIR=/usr/norman/web/src/master
+
+.SUFFIXES: .ada .larch .pretty
+
+.ada.pretty:
+ cat $(WEBDIR)/webhead $*.ada $(WEBDIR)/webtail > $*.web
+ aweave $*
+ awk -f $(WEBDIR)/pretty.awk < $*.tex > $*.pretty
+
+.larch.pretty:
+ cat $(WEBDIR)/webhead $*.larch $(WEBDIR)/webtail > $*.web
+ lweave $*
+ awk -f $(WEBDIR)/pretty.awk < $*.tex > $*.pretty
+
diff --git a/web/spiderweb/src/master/Makefile b/web/spiderweb/src/master/Makefile
new file mode 100644
index 0000000000..caa3debac1
--- /dev/null
+++ b/web/spiderweb/src/master/Makefile
@@ -0,0 +1,18 @@
+# Copyright 1989 by Norman Ramsey, Odyssey Research Associates
+# Not to be sold, but may be used freely for any purpose
+# For more information, see file COPYRIGHT in the parent directory
+
+OBJECTDIRS=sun3 vax
+
+clean:
+ -rm -f *.tex *.dvi *.log *.wlog *~ *.toc
+ @-for i in $(OBJECTDIRS); do \
+ rm -f $$i/*.o; \
+ done
+ -rm -f *.o
+
+superclean:
+ -rm -f sun3/*.o vax/*.o *.o
+ -rm -f *.c
+ -rm -f *.tex *.dvi *.log *~ *.toc
+
diff --git a/web/spiderweb/src/master/WebMakefile b/web/spiderweb/src/master/WebMakefile
new file mode 100644
index 0000000000..e6d4c84905
--- /dev/null
+++ b/web/spiderweb/src/master/WebMakefile
@@ -0,0 +1,146 @@
+# Copyright 1989 by Norman Ramsey, Odyssey Research Associates.
+# To be used for research purposes only.
+# For more information, see file COPYRIGHT in the parent directory.
+
+HOME=/u/norman# # Make no longer inherits environment vars
+
+THETANGLE=tangle
+THEWEAVE=weave
+SPIDER=any.spider
+#
+DVI=dvi
+CFLAGS=-DDEBUG -g -DSTAT
+
+# CPUTYPE is a grim hack that attempts to solve the problem of multiple
+# cpus sharing a file system. In my environment I have to have different
+# copies of object and executable for vax, sun3, next, iris, and other
+# cpu types. If you will be using Spidery WEB in a homogenous processor
+# environment, you can just set CPUTYPE to a constant, or eliminate it
+# entirely.
+#
+# In my environment, the 'cputype' program returns a string that
+# describes the current processor. That means that the easiest thing
+# for you to do is to define a 'cputype' program that does something
+# sensible. A shell script that says 'echo "vax"' is fine.
+
+CPUTYPE=`cputype`
+
+# Change the following three directories to match your installation
+#
+# the odd placement of # is to prevent any trailing spaces from slipping in
+
+WEBROOT=$(HOME)/pu/web/spiderweb# # root of the WEB source distribution
+DEST=$(HOME)/bin/$(CPUTYPE)# # place where the executables go
+MACROS=$(HOME)/lib/tex/inputs/web# # place where the macros go
+
+MASTER=$(WEBROOT)/src/master# # master source directory
+OBDIR=$(MASTER)/$(CPUTYPE)# # common object files
+
+TANGLESRC=tangle
+CTANGLE=ceetangle -I$(MASTER)
+CWEAVE=ceeweave -I$(MASTER)
+AWKTANGLE=awktangle -I$(MASTER)
+COMMONOBJS=$(OBDIR)/common.o $(OBDIR)/pathopen.o
+COMMONC=$(MASTER)/common.c $(MASTER)/pathopen.c
+COMMONSRC=$(COMMONC) $(MASTER)/spider.awk
+
+
+
+# Our purpose is to make tangle and weave
+
+web: tangle weave
+
+tangle: $(COMMONOBJS) $(TANGLESRC).o
+ $(CC) $(CFLAGS) -o $(DEST)/$(THETANGLE) $(COMMONOBJS) $(TANGLESRC).o
+
+weave: $(COMMONOBJS) weave.o
+ $(CC) $(CFLAGS) -o $(DEST)/$(THEWEAVE) $(COMMONOBJS) weave.o
+
+
+source: $(TANGLESRC).c $(COMMONSRC) # make tangle.c and common src, then clean
+ if [ -f WebMakefile ]; then exit 1; fi # don't clean the master!
+ if [ -f spiderman.tex ]; then exit 1; fi # don't clean the manual
+ -rm -f tangle.web weave.* common.* # remove links that may be obsolete
+ -rm -f *.unsorted *.list grammar.web outtoks.web scraps.web
+ -rm -f cycle.test spider.slog
+ -rm -f *.o *.tex *.toc *.dvi *.log *.makelog *~ *.wlog *.printlog
+
+# Here is how we make the common stuff
+
+$(MASTER)/common.c: $(MASTER)/common.web # no change file
+ $(CTANGLE) $(MASTER)/common
+
+$(OBDIR)/common.o: $(MASTER)/common.c $(OBDIR)
+ $(CC) $(CFLAGS) -c $(MASTER)/common.c
+ mv common.o $(OBDIR)
+
+
+$(MASTER)/pathopen.c: $(MASTER)/pathopen.web # no change file
+ $(CTANGLE) $(MASTER)/pathopen
+ mv pathopen.h $(MASTER)
+
+$(OBDIR)/pathopen.o: $(MASTER)/pathopen.c $(OBDIR)
+ $(CC) $(CFLAGS) -c $(MASTER)/pathopen.c
+ mv pathopen.o $(OBDIR)
+
+$(OBDIR):
+ if /bin/test ! -d $(OBDIR) ; then mkdir $(OBDIR) ; fi
+
+## Now we make the tangle and weave source locally
+
+$(TANGLESRC).c: $(MASTER)/$(TANGLESRC).web $(MASTER)/common.h grammar.web
+ -/bin/rm -f $(TANGLESRC).web
+ ln $(MASTER)/$(TANGLESRC).web $(TANGLESRC).web
+# chmod -w $(TANGLESRC).web
+ $(CTANGLE) $(TANGLESRC)
+
+weave.c: $(MASTER)/weave.web $(MASTER)/common.h grammar.web
+ -/bin/rm -f weave.web
+ ln $(MASTER)/weave.web weave.web
+# chmod -w weave.web
+ $(CTANGLE) weave
+
+## Here's where we run SPIDER to create the source
+
+grammar.web: $(MASTER)/cycle.awk $(MASTER)/spider.awk $(SPIDER)
+ echo "date" `date` | cat - $(SPIDER) | awk -f $(MASTER)/spider.awk
+ cat $(MASTER)/transcheck.list trans_keys.unsorted | awk -f $(MASTER)/transcheck.awk
+ awk -f $(MASTER)/cycle.awk < cycle.test
+ sort *.unsorted | awk -f $(MASTER)/nodups.awk
+ mv *web.tex $(MACROS)
+
+## We might have to make spider first.
+
+$(MASTER)/spider.awk: $(MASTER)/spider.web
+ $(AWKTANGLE) $(MASTER)/spider
+ mv cycle.awk nodups.awk transcheck.awk $(MASTER)
+ rm junk.list
+
+
+# $(MASTER)/cycle.awk: $(MASTER)/cycle.web # making spider also makes cycle
+# $(AWKTANGLE) $(MASTER)/cycle
+
+
+# This cleanup applies to every language
+
+clean:
+ if [ -f WebMakefile ]; then exit 1; fi # don't clean the master!
+ if [ -f spiderman.tex ]; then exit 1; fi # don't clean the manual
+ -rm -f tangle.* weave.* common.* # remove links that may be obsolete
+ -rm -f *.unsorted *.list grammar.web outtoks.web scraps.web
+ -rm -f cycle.test spider.slog
+ -rm -f *.c *.o *.tex *.toc *.dvi *.log *.makelog *~ *.wlog *.printlog
+
+
+
+# booting the new distribution
+boot:
+ if [ ! -d $(OBDIR) ]; then mkdir $(OBDIR); fi
+ cd ../master; rm -f *.o; for i in $(COMMONC); do \
+ $(CC) $(CFLAGS) -c $$i; \
+ mv *.o $(OBDIR) ; \
+ done; cd ../c
+ $(CC) $(CFLAGS) -c $(TANGLESRC).c; \
+ $(CC) $(CFLAGS) -o $(DEST)/$(THETANGLE) $(COMMONOBJS) $(TANGLESRC).o
+
+
diff --git a/web/spiderweb/src/master/common.c b/web/spiderweb/src/master/common.c
new file mode 100644
index 0000000000..62d062cf27
--- /dev/null
+++ b/web/spiderweb/src/master/common.c
@@ -0,0 +1,1001 @@
+/*1:*/
+#line 49 "/u/norman/pu/web/dist/src/master/common.web"
+
+#include<stdio.h>
+#ifdef MSDOS
+#define index strchr
+#endif
+
+/*3:*/
+#line 71 "/u/norman/pu/web/dist/src/master/common.web"
+
+extern char the_at_sign;
+
+
+/*:3*//*65:*/
+#line 1052 "/u/norman/pu/web/dist/src/master/common.web"
+extern char C_file_extension[];
+#line 1053 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*:65*/
+#line 55 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*9:*/
+#line 197 "/u/norman/pu/web/dist/src/master/common.web"
+char setting_up;
+#line 198 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*:9*/
+#line 56 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*4:*/
+#line 75 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 76 "/u/norman/pu/web/dist/src/master/common.web"
+typedef short boolean;
+typedef char unsigned eight_bits;
+boolean program;
+
+/*:4*//*6:*/
+#line 159 "/u/norman/pu/web/dist/src/master/common.web"
+
+typedef char ASCII;
+typedef char outer_char;
+
+/*:6*//*7:*/
+#line 167 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 168 "/u/norman/pu/web/dist/src/master/common.web"
+ASCII xord[127+1];
+outer_char xchr[128];
+
+/*:7*//*12:*/
+#line 259 "/u/norman/pu/web/dist/src/master/common.web"
+
+ASCII buffer[500];
+ASCII *buffer_end= buffer+200-2;
+ASCII *limit;
+ASCII *loc;
+
+/*:12*//*14:*/
+#line 312 "/u/norman/pu/web/dist/src/master/common.web"
+
+int include_depth;
+FILE *file[10];
+FILE *change_file;
+char file_name[10][60];
+
+char change_file_name[60];
+int line[10];
+int change_line;
+boolean input_has_ended;
+boolean changing;
+
+/*:14*//*26:*/
+#line 501 "/u/norman/pu/web/dist/src/master/common.web"
+
+typedef unsigned short sixteen_bits;
+sixteen_bits module_count;
+boolean changed_module[2000];
+boolean print_where= 0;
+
+/*:26*//*37:*/
+#line 638 "/u/norman/pu/web/dist/src/master/common.web"
+
+typedef struct name_info{
+ASCII *byte_start;
+ /*41:*/
+#line 677 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 678 "/u/norman/pu/web/dist/src/master/common.web"
+struct name_info *link;
+
+/*:41*//*48:*/
+#line 767 "/u/norman/pu/web/dist/src/master/common.web"
+
+union{
+struct name_info *Rlink;
+
+eight_bits Ilk;
+}dummy;
+
+/*:48*//*55:*/
+#line 906 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 907 "/u/norman/pu/web/dist/src/master/common.web"
+ASCII *equiv_or_xref;
+
+/*:55*/
+#line 641 "/u/norman/pu/web/dist/src/master/common.web"
+
+}name_info;
+typedef name_info *name_pointer;
+ASCII byte_mem[90000];
+ASCII *byte_mem_end= byte_mem+90000-1;
+name_info name_dir[4000];
+name_pointer name_dir_end= name_dir+4000-1;
+
+/*:37*//*39:*/
+#line 663 "/u/norman/pu/web/dist/src/master/common.web"
+
+name_pointer name_ptr;
+ASCII *byte_ptr;
+
+/*:39*//*42:*/
+#line 690 "/u/norman/pu/web/dist/src/master/common.web"
+
+typedef name_pointer *hash_pointer;
+name_pointer hash[353];
+hash_pointer hash_end= hash+353-1;
+hash_pointer h;
+
+/*:42*//*56:*/
+#line 926 "/u/norman/pu/web/dist/src/master/common.web"
+
+int history= 0;
+
+/*:56*//*64:*/
+#line 1039 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1040 "/u/norman/pu/web/dist/src/master/common.web"
+int argc;
+char * *argv;
+char C_file_name[60];
+char tex_file_name[60];
+int no_xref;
+
+/*:64*//*75:*/
+#line 1165 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1166 "/u/norman/pu/web/dist/src/master/common.web"
+FILE *C_file;
+FILE *tex_file;
+
+/*:75*/
+#line 57 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*5:*/
+#line 86 "/u/norman/pu/web/dist/src/master/common.web"
+int phase;
+#line 87 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*:5*//*15:*/
+#line 334 "/u/norman/pu/web/dist/src/master/common.web"
+
+ASCII change_buffer[200];
+ASCII *change_limit;
+
+/*:15*//*30:*/
+#line 536 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1 "pathopen.h"
+
+#line 187"/u/nr/web/src/master/pathopen.web"
+
+#line 188"/u/nr/web/src/master/pathopen.web"
+void pathaddname();
+void pathaddpath();
+void pathreset();
+FILE *pathopen();
+
+#line 538 "/u/norman/pu/web/dist/src/master/common.web"
+/*:30*//*50:*/
+#line 792 "/u/norman/pu/web/dist/src/master/common.web"
+
+name_pointer install_node();
+
+/*:50*/
+#line 58 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*8:*/
+#line 183 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 184 "/u/norman/pu/web/dist/src/master/common.web"
+common_init()
+{
+strncpy(xchr," !\"#$%&'()*+,-./0123456789\
+:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ",128);
+/*11:*/
+#line 238 "/u/norman/pu/web/dist/src/master/common.web"
+
+
+/*:11*/
+#line 188 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*10:*/
+#line 202 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 203 "/u/norman/pu/web/dist/src/master/common.web"
+int i;
+for(i= 0;i<=127;i++)
+xord[i]= 32;
+for(i= 1;i<127;i++)xord[xchr[i]]= i;
+}
+
+/*:10*/
+#line 189 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*40:*/
+#line 667 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 668 "/u/norman/pu/web/dist/src/master/common.web"
+name_dir->byte_start= byte_ptr= byte_mem;
+name_ptr= name_dir+1;
+name_ptr->byte_start= byte_mem;
+
+/*:40*//*43:*/
+#line 698 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 699 "/u/norman/pu/web/dist/src/master/common.web"
+for(h= hash;h<=hash_end; *h++= NULL);
+
+/*:43*//*49:*/
+#line 774 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 775 "/u/norman/pu/web/dist/src/master/common.web"
+name_dir->dummy.Rlink= NULL;
+
+/*:49*/
+#line 190 "/u/norman/pu/web/dist/src/master/common.web"
+;
+setting_up= 1;
+/*76:*/
+#line 1169 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1170 "/u/norman/pu/web/dist/src/master/common.web"
+scan_args();
+/*28:*/
+#line 532 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 533 "/u/norman/pu/web/dist/src/master/common.web"
+pathaddpath(getenv("WEBPATH"),':');
+/*:28*/
+#line 1171 "/u/norman/pu/web/dist/src/master/common.web"
+
+if(program==0){
+if((C_file= fopen(C_file_name,"w"))==NULL)
+{printf("! Cannot open output file ");err_print(C_file_name);history= 3;wrap_up();};
+}
+else{
+if((tex_file= fopen(tex_file_name,"w"))==NULL)
+{printf("! Cannot open output file ");err_print(tex_file_name);history= 3;wrap_up();};
+}
+
+/*:76*/
+#line 192 "/u/norman/pu/web/dist/src/master/common.web"
+;
+setting_up= 0;
+}
+
+/*:8*//*13:*/
+#line 269 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 270 "/u/norman/pu/web/dist/src/master/common.web"
+#include<stdio.h>
+input_ln(fp)
+FILE *fp;
+{
+register int c;
+register ASCII *k;
+if(feof(fp))return(0);
+limit= k= buffer;
+while(k<=buffer_end&&(c= getc(fp))!=EOF&&c!='\n')
+if(( *(k++)= xord[c])!=32)limit= k;
+if(k>buffer_end)
+if((c= getc(fp))!=EOF&&c!='\n'){
+ungetc(c,fp);loc= buffer;err_print("\n! Input line too long");
+
+}
+if(c==EOF&&limit==buffer)return(0);
+
+return(1);
+}
+
+/*:13*//*16:*/
+#line 344 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 345 "/u/norman/pu/web/dist/src/master/common.web"
+prime_the_change_buffer()
+{
+change_limit= 0;
+/*17:*/
+#line 358 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 359 "/u/norman/pu/web/dist/src/master/common.web"
+while(1){
+change_line++;
+if(!input_ln(change_file))return;
+if(limit<buffer+2)continue;
+if(buffer[0]!=the_at_sign)continue;
+/*18:*/
+#line 376 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 377 "/u/norman/pu/web/dist/src/master/common.web"
+if(buffer[1]>=88&&buffer[1]<=90||buffer[1]==73)buffer[1]+= 122-90;
+
+/*:18*/
+#line 364 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*19:*/
+#line 381 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 382 "/u/norman/pu/web/dist/src/master/common.web"
+if(buffer[1]==105){
+loc= buffer+2;
+err_print("! No includes allowed in change file");
+
+}
+}
+
+/*:19*/
+#line 365 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if(buffer[1]==120)break;
+if(buffer[1]==121||buffer[1]==122){
+loc= buffer+2;
+err_print("! Where is the matching @x?");
+
+}
+}
+
+/*:17*/
+#line 348 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*20:*/
+#line 391 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 392 "/u/norman/pu/web/dist/src/master/common.web"
+do{
+change_line++;
+if(!input_ln(change_file)){
+err_print("! Change file ended after @x");
+
+return;
+}
+}while(limit==buffer);
+
+/*:20*/
+#line 349 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*21:*/
+#line 401 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 402 "/u/norman/pu/web/dist/src/master/common.web"
+{
+change_limit= change_buffer-buffer+limit;
+strncpy(change_buffer,buffer,limit-buffer+1);
+}
+
+/*:21*/
+#line 350 "/u/norman/pu/web/dist/src/master/common.web"
+;
+}
+
+/*:16*//*22:*/
+#line 416 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 417 "/u/norman/pu/web/dist/src/master/common.web"
+check_change()
+{
+int n= 0;
+if((change_limit-change_buffer!=limit-buffer||strncmp(buffer,change_buffer,limit-buffer)))return;
+while(1){
+changing= 1;print_where= 1;change_line++;
+if(!input_ln(change_file)){
+err_print("! Change file ended before @y");
+
+change_limit= 0;changing= 0;print_where= 1;
+return;
+}
+if(limit>buffer+1&&buffer[0]==the_at_sign)
+/*19:*/
+#line 381 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 382 "/u/norman/pu/web/dist/src/master/common.web"
+if(buffer[1]==105){
+loc= buffer+2;
+err_print("! No includes allowed in change file");
+
+}
+}
+
+/*:19*/
+#line 430 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*23:*/
+#line 447 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 448 "/u/norman/pu/web/dist/src/master/common.web"
+if(limit>buffer+1&&buffer[0]==the_at_sign){
+/*18:*/
+#line 376 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 377 "/u/norman/pu/web/dist/src/master/common.web"
+if(buffer[1]>=88&&buffer[1]<=90||buffer[1]==73)buffer[1]+= 122-90;
+
+/*:18*/
+#line 449 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if(buffer[1]==120||buffer[1]==122){
+loc= buffer+2;err_print("! Where is the matching @y?");
+
+}
+else if(buffer[1]==121){
+if(n>0){
+loc= buffer+2;
+err_print("! Hmm... some of the preceding lines failed to match");
+
+}
+return;
+}
+}
+
+/*:23*/
+#line 432 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*21:*/
+#line 401 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 402 "/u/norman/pu/web/dist/src/master/common.web"
+{
+change_limit= change_buffer-buffer+limit;
+strncpy(change_buffer,buffer,limit-buffer+1);
+}
+
+/*:21*/
+#line 433 "/u/norman/pu/web/dist/src/master/common.web"
+;
+changing= 0;print_where= 1;line[include_depth]++;
+while(!input_ln(file[include_depth])){
+if(include_depth==0){
+err_print("! WEB file ended during a change");
+
+input_has_ended= 1;return;
+}
+include_depth--;print_where= 1;line[include_depth]++;
+}
+if((change_limit-change_buffer!=limit-buffer||strncmp(buffer,change_buffer,limit-buffer)))n++;
+}
+}
+
+/*:22*//*24:*/
+#line 467 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 468 "/u/norman/pu/web/dist/src/master/common.web"
+reset_input()
+{
+limit= buffer;loc= buffer+1;buffer[0]= 32;
+/*25:*/
+#line 480 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 481 "/u/norman/pu/web/dist/src/master/common.web"
+if((file[0]= fopen(file_name[0],"r"))==NULL)
+{printf("! Cannot open input file ");err_print(file_name[0]);history= 3;wrap_up();};
+if((change_file= fopen(change_file_name,"r"))==NULL)
+{printf("! Cannot open change file ");err_print(change_file_name);history= 3;wrap_up();};
+
+/*:25*/
+#line 471 "/u/norman/pu/web/dist/src/master/common.web"
+;
+line[include_depth]= 0;change_line= 0;include_depth= 0;
+changing= 1;prime_the_change_buffer();changing= !changing;
+limit= buffer;loc= buffer+1;buffer[0]= 32;input_has_ended= 0;
+}
+
+/*:24*//*27:*/
+#line 507 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 508 "/u/norman/pu/web/dist/src/master/common.web"
+get_line()
+{
+restart:
+if(changing)changed_module[module_count]= 1;
+else/*34:*/
+#line 574 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 575 "/u/norman/pu/web/dist/src/master/common.web"
+line[include_depth]++;
+while(!input_ln(file[include_depth])){
+print_where= 1;
+if(include_depth==0){input_has_ended= 1;break;}
+else{fclose(file[include_depth]);include_depth--;line[include_depth]++;}
+}
+if(!input_has_ended)
+if(limit==change_limit-change_buffer+buffer)
+if(buffer[0]==change_buffer[0])
+if(change_limit>change_buffer)check_change();
+}
+
+/*:34*/
+#line 512 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if(changing){
+/*35:*/
+#line 587 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 588 "/u/norman/pu/web/dist/src/master/common.web"
+change_line++;
+if(!input_ln(change_file)){
+err_print("! Change file ended without @z");
+
+buffer[0]= the_at_sign;buffer[1]= 122;limit= buffer+2;
+}
+if(limit>buffer+1)
+if(buffer[0]==the_at_sign){
+/*18:*/
+#line 376 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 377 "/u/norman/pu/web/dist/src/master/common.web"
+if(buffer[1]>=88&&buffer[1]<=90||buffer[1]==73)buffer[1]+= 122-90;
+
+/*:18*/
+#line 596 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*19:*/
+#line 381 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 382 "/u/norman/pu/web/dist/src/master/common.web"
+if(buffer[1]==105){
+loc= buffer+2;
+err_print("! No includes allowed in change file");
+
+}
+}
+
+/*:19*/
+#line 597 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if(buffer[1]==120||buffer[1]==121){
+loc= buffer+2;err_print("! Where is the matching @z?");
+
+}
+else if(buffer[1]==122){
+prime_the_change_buffer();changing= !changing;print_where= 1;
+}
+}
+}
+
+/*:35*/
+#line 514 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if(!changing){
+changed_module[module_count]= 1;goto restart;
+}
+}
+loc= buffer; *limit= 32;
+if( *buffer==the_at_sign&&( *(buffer+1)==105|| *(buffer+1)==73))
+/*33:*/
+#line 546 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 547 "/u/norman/pu/web/dist/src/master/common.web"
+ASCII *k, *j;
+loc= buffer+2;
+while(loc<=limit&&( *loc==32|| *loc==9|| *loc==34))loc++;
+if(loc>=limit)err_print("! Include file name not given");
+
+else{
+if(++include_depth<10){
+k= file_name[include_depth];j= loc;
+while( *loc!=32&& *loc!=9&& *loc!=34) *k++= xchr[ *loc++];
+ *k= '\0';
+if((file[include_depth]= pathopen(file_name[include_depth],"r"))==NULL){
+loc= j;
+include_depth--;
+err_print("! Cannot find include file on path");
+
+}
+else{line[include_depth]= 0;print_where= 1;}
+}
+else{
+include_depth--;
+err_print("! Too many nested includes");
+
+}
+}
+goto restart;
+}
+
+/*:33*/
+#line 521 "/u/norman/pu/web/dist/src/master/common.web"
+;
+return(!input_has_ended);
+}
+
+/*:27*//*32:*/
+#line 539 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 540 "/u/norman/pu/web/dist/src/master/common.web"
+void overflow (s)
+char *s;
+{{{printf("\n! Sorry, capacity exceeded: ");err_print(s);history= 3;wrap_up();};};}
+/*:32*//*36:*/
+#line 611 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 612 "/u/norman/pu/web/dist/src/master/common.web"
+check_complete(){
+if(change_limit!=0){
+strncpy(buffer,change_buffer,change_limit-change_buffer+1);
+limit= change_limit-change_buffer+buffer;
+changing= 1;loc= change_limit;
+err_print("! Change file entry did not match");
+
+}
+}
+
+/*:36*//*57:*/
+#line 940 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 941 "/u/norman/pu/web/dist/src/master/common.web"
+err_print(s)
+char *s;
+{
+ASCII *k, *l;
+printf("\n%s",(s==(char *)0)?"Bad error message!!!":s);
+if(setting_up){
+printf("\nError occurred while scanning arguments or opening output files. ");
+}else{
+/*58:*/
+#line 963 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 964 "/u/norman/pu/web/dist/src/master/common.web"
+if(changing)printf(". (l. %d of change file)\n",change_line);
+else if(include_depth==0)printf(". (l. %d)\n",line[include_depth]);
+else printf(". (l. %d of include file %s)\n",line[include_depth],file_name[include_depth]);
+l= (loc>=limit?limit:loc);
+if(l>buffer){
+for(k= buffer;k<l;k++)
+if( *k=='\t')putchar(' ');
+else putchar(xchr[ *k]);
+putchar('\n');
+for(k= buffer;k<l;k++)putchar(' ');
+}
+for(k= l;k<limit;k++)putchar(xchr[ *k]);
+if( *limit==124)putchar('|');
+putchar(' ');
+
+/*:58*/
+#line 949 "/u/norman/pu/web/dist/src/master/common.web"
+;
+}
+fflush(stdout);history= 2;
+}
+
+/*:57*//*62:*/
+#line 1009 "/u/norman/pu/web/dist/src/master/common.web"
+
+wrap_up(){
+putchar('\n');
+/*63:*/
+#line 1017 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1018 "/u/norman/pu/web/dist/src/master/common.web"
+switch(history){
+case 0:printf("(No errors were found.)\n");break;
+case 1:
+printf("(Did you see the warning message above?)\n");break;
+case 2:
+printf("(Pardon me, but I think I spotted something wrong.)\n");break;
+case 3:printf("(That was a fatal error, my friend.)\n");
+}
+
+/*:63*/
+#line 1012 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if(history>1)exit(1);
+else exit(0);
+}
+
+/*:62*//*66:*/
+#line 1060 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1061 "/u/norman/pu/web/dist/src/master/common.web"
+scan_args()
+{
+char *dot_pos, *index();
+boolean found_web= 0,found_change= 0;
+no_xref= 0;
+while(--argc>0){
+if( * *(++argv)!='-'){
+if(!found_web)/*67:*/
+#line 1084 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1085 "/u/norman/pu/web/dist/src/master/common.web"
+{
+if(strlen( *argv)>60-5)
+/*73:*/
+#line 1156 "/u/norman/pu/web/dist/src/master/common.web"
+{printf("! Filename %s too long\n");err_print( *argv);history= 3;wrap_up();};
+#line 1157 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*:73*/
+#line 1087 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if((dot_pos= index( *argv,'.'))==NULL)
+sprintf(file_name[0],"%s.web", *argv);
+else{
+sprintf(file_name[0],"%s", *argv);
+/*68:*/
+#line 1100 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1101 "/u/norman/pu/web/dist/src/master/common.web"
+{char *p;
+p= dot_pos;
+while( *++p!='\0')
+if( *p=='.')dot_pos= p;
+}
+
+/*:68*/
+#line 1092 "/u/norman/pu/web/dist/src/master/common.web"
+
+ *dot_pos= 0;
+}
+sprintf(tex_file_name,"%s.tex", *argv);
+sprintf(C_file_name,"%s.%s", *argv,C_file_extension);
+found_web= 1;
+}
+
+/*:67*/
+#line 1069 "/u/norman/pu/web/dist/src/master/common.web"
+
+else if(!found_change)/*69:*/
+#line 1107 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1108 "/u/norman/pu/web/dist/src/master/common.web"
+{
+if(strlen( *argv)>60-5)
+/*73:*/
+#line 1156 "/u/norman/pu/web/dist/src/master/common.web"
+{printf("! Filename %s too long\n");err_print( *argv);history= 3;wrap_up();};
+#line 1157 "/u/norman/pu/web/dist/src/master/common.web"
+
+/*:73*/
+#line 1110 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if((dot_pos= index( *argv,'.'))==NULL)
+sprintf(change_file_name,"%s.ch", *argv);
+else sprintf(change_file_name,"%s", *argv);
+found_change= 1;
+}
+
+/*:69*/
+#line 1070 "/u/norman/pu/web/dist/src/master/common.web"
+
+else/*72:*/
+#line 1147 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1148 "/u/norman/pu/web/dist/src/master/common.web"
+{
+if(program==0)
+{printf("! Usage: tangle webfile[.web] [changefile[.ch]] [-Ipathname ...]\n");err_print(0);history= 3;wrap_up();}
+else
+{printf("! Usage: weave webfile[.web] [changefile[.ch]] [-x] [-Ipathname ...]\n");err_print(
+0);history= 3;wrap_up();};
+}
+
+/*:72*/
+#line 1071 "/u/norman/pu/web/dist/src/master/common.web"
+;
+}
+else/*71:*/
+#line 1132 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1133 "/u/norman/pu/web/dist/src/master/common.web"
+{
+switch(( *argv)[1]){
+case'x':
+no_xref= 1;
+break;
+case'I':
+/*29:*/
+#line 534 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 535 "/u/norman/pu/web/dist/src/master/common.web"
+pathaddname( *argv+2);
+/*:29*/
+#line 1139 "/u/norman/pu/web/dist/src/master/common.web"
+
+break;
+default:
+/*74:*/
+#line 1158 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 1159 "/u/norman/pu/web/dist/src/master/common.web"
+printf("! Unknown option in argument %s\n", *argv);{if(history==0)history= 1;};
+}
+
+/*:74*/
+#line 1142 "/u/norman/pu/web/dist/src/master/common.web"
+;
+break;
+}
+}
+
+/*:71*/
+#line 1073 "/u/norman/pu/web/dist/src/master/common.web"
+;
+}
+if(!found_web)/*72:*/
+#line 1147 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1148 "/u/norman/pu/web/dist/src/master/common.web"
+{
+if(program==0)
+{printf("! Usage: tangle webfile[.web] [changefile[.ch]] [-Ipathname ...]\n");err_print(0);history= 3;wrap_up();}
+else
+{printf("! Usage: weave webfile[.web] [changefile[.ch]] [-x] [-Ipathname ...]\n");err_print(
+0);history= 3;wrap_up();};
+}
+
+/*:72*/
+#line 1075 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if(!found_change)/*70:*/
+#line 1117 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 1118 "/u/norman/pu/web/dist/src/master/common.web"
+#ifdef MSDOS
+strcpy(change_file_name,"NUL");
+#else
+strcpy(change_file_name,"/dev/null");
+#endif
+/*:70*/
+#line 1076 "/u/norman/pu/web/dist/src/master/common.web"
+;
+}
+
+/*:66*/
+#line 59 "/u/norman/pu/web/dist/src/master/common.web"
+;
+
+/*:1*//*44:*/
+#line 703 "/u/norman/pu/web/dist/src/master/common.web"
+name_pointer
+#line 704 "/u/norman/pu/web/dist/src/master/common.web"
+ id_lookup(first,last,t)
+ASCII *first;
+ASCII *last;
+eight_bits t;
+{
+ASCII *i= first;
+int h;
+int l;
+name_pointer p;
+if(last==NULL)for(last= first; *last!='\0';last++);
+l= last-first;
+/*45:*/
+#line 725 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 726 "/u/norman/pu/web/dist/src/master/common.web"
+h= *i;while(++i<last)h= (h+h+ *i)%353;
+
+/*:45*/
+#line 715 "/u/norman/pu/web/dist/src/master/common.web"
+;
+/*46:*/
+#line 731 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 732 "/u/norman/pu/web/dist/src/master/common.web"
+p= hash[h];
+while(p&&!names_match(p,first,l,t))p= p->link;
+if(p==NULL){
+p= name_ptr;
+p->link= hash[h];hash[h]= p;
+}
+
+/*:46*/
+#line 716 "/u/norman/pu/web/dist/src/master/common.web"
+;
+if(p==name_ptr)/*47:*/
+#line 743 "/u/norman/pu/web/dist/src/master/common.web"
+{
+#line 744 "/u/norman/pu/web/dist/src/master/common.web"
+if(byte_ptr+l>byte_mem_end){{printf("\n! Sorry, capacity exceeded: ");err_print("byte memory");history= 3;wrap_up();};};
+if(name_ptr>=name_dir_end){{printf("\n! Sorry, capacity exceeded: ");err_print("name");history= 3;wrap_up();};};
+strncpy(byte_ptr,first,l);
+(++name_ptr)->byte_start= byte_ptr+= l;
+init_p(p,t);
+}
+
+/*:47*/
+#line 717 "/u/norman/pu/web/dist/src/master/common.web"
+;
+return(p);
+}
+
+/*:44*//*51:*/
+#line 795 "/u/norman/pu/web/dist/src/master/common.web"
+name_pointer
+#line 796 "/u/norman/pu/web/dist/src/master/common.web"
+ mod_lookup(k,l)
+ASCII *k;
+ASCII *l;
+{
+short c= 2;
+name_pointer p= name_dir->dummy.Rlink;
+name_pointer q= name_dir;
+while(p){
+c= web_strcmp(k,l+1,p->byte_start,(p+1)->byte_start);
+q= p;
+switch(c){
+case 0:p= p->link;continue;
+case 2:p= p->dummy.Rlink;continue;
+case 1:return p;
+default:err_print("! Incompatible section names");return NULL;
+
+}
+}
+return(install_node(q,c,k,l-k+1));
+}
+
+/*:51*//*52:*/
+#line 820 "/u/norman/pu/web/dist/src/master/common.web"
+
+#line 821 "/u/norman/pu/web/dist/src/master/common.web"
+web_strcmp(j,j1,k,k1)
+ASCII *j;
+ASCII *j1;
+ASCII *k;
+ASCII *k1;
+{
+while(k<k1&&j<j1&& *j== *k)k++,j++;
+if(k==k1)if(j==j1)return 1;
+else return 4;
+else if(j==j1)return 3;
+else if( *j< *k)return 0;
+else return 2;
+}
+
+/*:52*//*53:*/
+#line 843 "/u/norman/pu/web/dist/src/master/common.web"
+name_pointer
+#line 844 "/u/norman/pu/web/dist/src/master/common.web"
+ install_node(parent,c,j,name_len)
+name_pointer parent;
+int c;
+ASCII *j;
+int name_len;
+{
+name_pointer node= name_ptr;
+if(byte_ptr+name_len>byte_mem_end){{printf("\n! Sorry, capacity exceeded: ");err_print("byte memory");history= 3;wrap_up();};};
+if(name_ptr==name_dir_end){{printf("\n! Sorry, capacity exceeded: ");err_print("name");history= 3;wrap_up();};};
+if(c==0)parent->link= node;else parent->dummy.Rlink= node;
+node->link= NULL;node->dummy.Rlink= NULL;
+init_node(node);
+strncpy(byte_ptr,j,name_len);
+(++name_ptr)->byte_start= byte_ptr+= name_len;
+return(node);
+}
+
+/*:53*//*54:*/
+#line 867 "/u/norman/pu/web/dist/src/master/common.web"
+name_pointer
+#line 868 "/u/norman/pu/web/dist/src/master/common.web"
+ prefix_lookup(k,l)
+ASCII *k;
+ASCII *l;
+{
+short c= 2;
+short count= 0;
+name_pointer p= name_dir->dummy.Rlink;
+name_pointer q= NULL;
+
+name_pointer r= NULL;
+while(p){
+c= web_strcmp(k,l+1,p->byte_start,(p+1)->byte_start);
+switch(c){
+case 0:p= p->link;break;
+case 2:p= p->dummy.Rlink;break;
+default:r= p;count++;q= p->dummy.Rlink;p= p->link;
+}
+if(p==NULL){
+p= q;q= NULL;
+}
+}
+if(count==0)err_print("! Name does not match");
+
+if(count>1)err_print("! Ambiguous prefix");
+
+return(r);
+}
+
+/*:54*/
diff --git a/web/spiderweb/src/master/common.h b/web/spiderweb/src/master/common.h
new file mode 100644
index 0000000000..fdce4ce9e2
--- /dev/null
+++ b/web/spiderweb/src/master/common.h
@@ -0,0 +1,146 @@
+@ Here's stuff from the file |'common.web'| that has to be included in
+|'tangle.web'| and |'weave.web'|.
+
+General stuff:
+
+@d tangle = 0
+@d weave = 1
+
+@<Common code for \.{WEAVE} and \.{TANGLE}@>=
+typedef short boolean;
+typedef char unsigned eight_bits;
+extern boolean program; /* \.{WEAVE} or \.{TANGLE}? */
+extern int phase; /* which phase are we in? */
+
+@ @<Include files@>=
+#include <stdio.h>
+
+@ Code related to the character set:
+@d tab_mark = 011 /* ASCII code used as tab-skip */
+@d line_feed = 012 /* ASCII code thrown away at end of line */
+@d form_feed = 014 /* ASCII code used at end of page */
+@d carriage_return = 015 /* ASCII code used at end of line */
+
+@<Common code...@>=
+typedef char ASCII; /* type of characters inside \.{WEB} */
+typedef char outer_char; /* type of characters outside \.{WEB} */
+extern ASCII xord[]; /* specifies conversion of input characters */
+extern outer_char xchr[]; /* specifies conversion of output characters */
+ASCII mod_text[longest_name+1]; /* name being sought for */
+ASCII *mod_text_end = mod_text+longest_name; /* end of |mod_text| */
+ASCII *id_first; /* where the current identifier begins in the buffer */
+ASCII *id_loc; /* just after the current identifier in the buffer */
+
+@ Code related to input routines:
+Killed |buf_size|; already in weave and tangle.
+
+@<Common code...@>=
+extern ASCII buffer[]; /* where each line of input goes */
+extern ASCII *buffer_end; /* end of |buffer| */
+extern ASCII *loc; /* points to the next character to be read from the buffer*/
+extern ASCII *limit; /* points to the last character in the buffer */
+
+@ Code related to identifier and module name storage:
+Killed |max_bytes|, |max_names|, and |hash_size| --- all duplicated.
+
+@d length(c) = ((c+1)->byte_start-(c)->byte_start) /* the length of a name */
+@d print_id(c) = ASCII_write((c)->byte_start,length((c)))
+ /* print identifier or module name */
+@d llink = link /* left link in binary search tree for module names */
+@d rlink = dummy.Rlink /* right link in binary search tree for module names */
+@d ilk = dummy.Ilk /* ilk in checking identifiers */
+@d root = name_dir->rlink /* the root of the binary search tree
+ for module names */
+
+@<Common code...@>=
+typedef struct name_info {
+ ASCII *byte_start; /* beginning of the name in |byte_mem| */
+ struct name_info *link;
+ union {
+ struct name_info *Rlink; /* right link in binary search tree for module
+ names */
+ eight_bits Ilk; /* used by identifiers in \.{WEAVE} (and for checking
+ macros in \.{TANGLE}?) */
+ } dummy;
+ ASCII *equiv_or_xref; /* info corresponding to names */
+} name_info; /* contains information about an identifier or module name */
+typedef name_info *name_pointer; /* pointer into array of |name_info|s */
+typedef name_pointer *hash_pointer;
+extern ASCII byte_mem[]; /* characters of names */
+extern ASCII *byte_mem_end; /* end of |byte_mem| */
+extern name_info name_dir[]; /* information about names */
+extern name_pointer name_dir_end; /* end of |name_dir| */
+extern name_pointer name_ptr; /* first unused position in |byte_start| */
+extern ASCII *byte_ptr; /* first unused position in |byte_mem| */
+extern name_pointer hash[]; /* heads of hash lists */
+extern hash_pointer hash_end; /* end of |hash| */
+extern hash_pointer h; /* index into hash-head array */
+extern name_pointer id_lookup(); /* looks up a string in the identifier table */
+extern name_pointer mod_lookup(); /* finds module name */
+extern name_pointer prefix_lookup(); /* finds module name given a prefix */
+
+@ Code related to error handling:
+@d spotless = 0 /* |history| value for normal jobs */
+@d harmless_message = 1 /* |history| value when non-serious info was printed */
+@d error_message = 2 /* |history| value when an error was noted */
+@d fatal_message = 3 /* |history| value when we had to stop prematurely */
+@d mark_harmless = {if (history==spotless) history=harmless_message;}
+@d mark_error = history=error_message
+@d confusion(s) = fatal("\n! This can't happen: ",s)
+@d fatal(s1,s2) = {
+ printf(s1); err_print(s2);
+ history=fatal_message; wrap_up();
+}
+@d overflow(s) = {
+ fatal("\n! Sorry, capacity exceeded: ",s);
+}
+
+@<Common...@>=
+extern history; /* indicates how bad this run was */
+
+@ Code related to file handling:
+@f line x /* make |line| an unreserved word */
+@d max_file_name_length = 60
+@d cur_file = file[include_depth] /* current file */
+@d cur_file_name = file_name[include_depth] /* current file name */
+@d web_file_name = file_name[0] /* main source file name */
+@d cur_line = line[include_depth] /* number of current line in current file */
+
+@<Common code...@>=
+extern include_depth; /* current level of nesting */
+extern FILE *file[]; /* stack of non-change files */
+extern FILE *change_file; /* change file */
+extern char C_file_name[]; /* name of |C_file| */
+extern char tex_file_name[]; /* name of |tex_file| */
+extern char file_name[][max_file_name_length];
+ /* stack of non-change file names */
+extern char change_file_name[]; /* name of change file */
+extern line[]; /* number of current line in the stacked files */
+extern change_line; /* number of current line in change file */
+extern boolean input_has_ended; /* if there is no more input */
+extern boolean changing; /* if the current line is from |change_file| */
+
+@ Code related to module numbers:
+@<Common code...@>=
+typedef unsigned short sixteen_bits;
+extern sixteen_bits module_count; /* the current module number */
+extern boolean changed_module[]; /* is the module changed? */
+extern boolean print_where; /* tells \.{TANGLE} to print line and file info */
+
+@ Code related to command line arguments:
+@<Common code...@>=
+extern int argc; /* copy of |ac| parameter to |main| */
+extern char **argv; /* copy of |av| parameter to |main| */
+extern no_xref; /* should \.{WEAVE} print an index? */
+
+@ Code relating to output:
+@d update_terminal = fflush(stdout) /* empty the terminal output buffer */
+@d new_line = putchar('\n') @d putxchar = putchar
+@d ASCII_write(a,b) = fflush(stdout), write(1,a,b) /* write on the standard output */
+@d line_write(c) = write(fileno(C_file),c) /* write on the C output file */
+@d C_printf(c,a) = fprintf(C_file,c,a)
+@d C_putc(c) = putc(c,C_file)
+
+@<Common code...@>=
+FILE *C_file; /* where output of \.{TANGLE} goes */
+FILE *tex_file; /* where output of \.{WEAVE} goes */
diff --git a/web/spiderweb/src/master/common.web b/web/spiderweb/src/master/common.web
new file mode 100644
index 0000000000..5a1c3fa901
--- /dev/null
+++ b/web/spiderweb/src/master/common.web
@@ -0,0 +1,1198 @@
+% Copyright 1989 by Norman Ramsey, Odyssey Research Associates
+% Not to be sold, but may be used freely for any purpose
+% For more information, see file COPYRIGHT in the parent directory
+
+% This file is part of Spidery WEB
+
+% This program by Norman Ramsey is based on programs Silvio Levy
+% and D. E. Knuth. Silvio Levy wrote most of the code.
+% It is distributed WITHOUT ANY WARRANTY, express or implied.
+% Dec 1987
+
+% Here is TeX material that gets inserted after \input webmac
+
+\message{OK, entering \string\batchmode...}
+\batchmode
+
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps
+\def\cee{C}
+\def\pb{$\.|\ldots\.|$} % C brackets (|...|)
+\def\v{\char'174} % vertical (|) in typewriter font
+\def\ceeref{{\it The C Reference Manual}}
+\mathchardef\RA="3221 % right arrow
+\mathchardef\BA="3224 % double arrow
+
+\def\title{Spidery COMMON}
+\def\contentspagenumber{1} % should be odd
+\def\topofcontents{\null\vfill
+ \titlefalse % include headline on the contents page
+ \def\rheader{\hfil}
+ \centerline{\titlefont Common Code in Spidery {\ttitlefont WEB}}
+ \vfill
+}
+
+
+@* Introduction. This file contains code common
+to both \.{TANGLE} and \.{WEAVE}, that roughly concerns the following
+problems: character uniformity, input routines, error handling and
+parsing of command line. We have tried to concentrate in this file
+all the system dependencies, so as to maximize portability.
+
+In the texts below we will
+sometimes use \.{WEB} to refer to either of the two component
+programs, if no confusion can arise.
+
+Here is the overall appearance of this file:
+
+@u@1
+#include <stdio.h>
+#ifdef MSDOS
+#define index strchr
+#endif
+
+@<External variables@>@;
+@<Global variables@>@;
+@<Definitions that should agree with \.{TANGLE} and \.{WEAVE}@>@;
+@<Other definitions@>@;
+@<Functions@>;
+
+@ In certain cases \.{TANGLE} and \.{WEAVE} should do almost, but not
+quite, the same thing. In these case we've written common code for
+both, differentiating between the two by means of the global variable
+|program|.
+
+@d tangle = 0
+@d weave = 1
+
+@ Give us an at sign that isn't always `@@':
+@d at_sign = the_at_sign
+@<External...@>=
+extern char the_at_sign;
+
+
+@ @<Definitions...@>=
+typedef short boolean;
+typedef char unsigned eight_bits;
+boolean program; /* \.{WEAVE} or \.{TANGLE}? */
+
+@ \.{CWEAVE} operates in three phases: first it inputs the source
+file and stores cross-reference data, then it inputs the source once again and
+produces the \TeX\ output file, and finally it sorts and outputs the index.
+Similarly, \.{CTANGLE} operates in two phases.
+The global variable |phase| tells which phase we are in.
+
+@<Other...@>= int phase; /* which phase are we in? */
+
+@* The character set.
+One of the main goals in the design of \.{WEB} has been to make it readily
+portable between a wide variety of computers. Yet \.{WEB} by its very
+nature must use a greater variety of characters than most computer
+programs deal with, and character encoding is one of the areas in which
+existing machines differ most widely from each other.
+
+To resolve this problem, all input to \.{WEAVE} and \.{TANGLE} is converted
+to an internal seven-bit code that is essentially standard ASCII, the
+``American Standard Code for Information Interchange.'' The conversion
+is done immediately when each character is read in. Conversely,
+characters are converted from ASCII to the user's external
+representation just before they are output.
+
+Such an internal code can be accessed by users of \.{WEB} by means of
+constructions like \.{@@`A'}, which should be distinguished from
+\.{'A'}. The former is transformed by
+\.{TANGLE} into an integer that is the internal code of \.A, but
+the latter, a |char| constant, is not touched by
+\.{WEB}, and will be interpreted by the \cee\ complier according to
+the machine's character set. (Actually, of course, it gets translated
+into \.{WEB}'s internal code just like any other character in the
+input file, but then it gets translated back at output time.)
+@^ASCII code@>
+
+Here is a table of the standard visible ASCII codes (\.{ } stands for
+a blank space):
+$$\def\:{\char\count255\global\advance\count255 by 1}
+\count255='40
+\vbox{
+\hbox{\hbox to 40pt{\it\hfill0\/\hfill}%
+\hbox to 40pt{\it\hfill1\/\hfill}%
+\hbox to 40pt{\it\hfill2\/\hfill}%
+\hbox to 40pt{\it\hfill3\/\hfill}%
+\hbox to 40pt{\it\hfill4\/\hfill}%
+\hbox to 40pt{\it\hfill5\/\hfill}%
+\hbox to 40pt{\it\hfill6\/\hfill}%
+\hbox to 40pt{\it\hfill7\/\hfill}}
+\vskip 4pt
+\hrule
+\def\^{\vrule height 10.5pt depth 4.5pt}
+\halign{\hbox to 0pt{\hskip -24pt\O{#0}\hfill}&\^
+\hbox to 40pt{\tt\hfill#\hfill\^}&
+&\hbox to 40pt{\tt\hfill#\hfill\^}\cr
+04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+17&\:&\:&\:&\:&\:&\:&\:\cr}
+\hrule width 280pt}$$
+
+We introduce new types to distinguish between the transliterated characters
+and the characters in the outside world. Let all
+``interesting'' values that a |char| variable may take lie between
+|first_text_char| and |last_text_char|; for the ASCII code we can
+take |first_text_char=0| and |last_text_char=@'177|. We will tell \.{WEB}
+to convert all input characters in this range to its own code, and balk at
+characters outside the range. We make two assumptions:
+|first_text_char>=0| and |char| has room for at least eight bits.
+@^system dependencies@>
+
+@d first_text_char = 0 /* lowest interesting value of a |char| */
+@d last_text_char = @'177 /* highest interesting value of a |char| */
+
+@<Definitions...@>=
+typedef char ASCII; /* type of characters inside \.{WEB} */
+typedef char outer_char; /* type of characters outside \.{WEB} */
+
+@ The \.{WEAVE} and \.{TANGLE} processors convert between ASCII code and
+the user's external character set by means of arrays |xord| and |xchr|
+that are analogous to PASCAL's |ord| and |chr| functions.
+
+@<Definitions...@>=
+ASCII xord[last_text_char+1]; /* specifies conversion of input characters */
+outer_char xchr[@'200]; /* specifies conversion of output characters */
+
+@ Every system supporting \cee\ must be able to read and write the 95
+visible characters of standard ASCII above (although not necessarily using the
+ASCII codes to represent them). Conversely, these characters, plus
+the newline, are sufficient to write any \cee\ program. Other
+characters are desirable mainly in strings, and they can be referred
+to by means of escape sequences like \.{'\t'}.
+
+The basic implementation of \.{WEB}, then, only has to assign an
+|xord| to these 95 characters (newlines are swallowed by the reading
+routines). The easiest way to do this is to assign the characters to
+their positions in |xchr| and then invert the correspondence:
+
+@<Functions...@>=
+common_init()
+{
+ strncpy(xchr," !\"#$%&'()*+,-./0123456789\
+:;<=>?@@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ",@'200);
+ @<System-dependent parts of character set@>;
+ @<Invert |xchr| to get |xord|@>;
+ @<Initialize pointers@>;
+ setting_up=1;
+ @<Scan arguments and open output files@>;
+ setting_up=0;
+}
+
+@ We use this variable to tell us what to do in |err_print|.
+@<Global var...@>=char setting_up;
+
+@ The following system-independent code makes the |xord| array contain
+a suitable inverse to the information in |xchr|.
+
+@<Invert |xchr|...@>= {
+ int i; /* to invert the correspondence */
+ for (i=first_text_char; i<=last_text_char; i++)
+ xord[i]=@'40; /* ASCII code for space */
+ for (i=1; i<@'177; i++) xord[xchr[i]]=i;
+}
+
+@ Some \cee\ compilers, accept an extended character
+set, so that one can type things like \.^^Z\ instead of \.{!=}.
+If that's the case in your system, you should change this module,
+assigning positions |@'1| to |@'37| in the most convenient way;
+for example, at MIT you can just say
+$$\hbox{|for (i=1; i<=@'37; i++) xchr[i]=i;|}$$
+since \.{WEB}'s character set is essentially identical to MIT's,
+even with respect to characters less than |@'40| (see the definitions
+below). If, however, the changes do not conform with these
+definitions you should change the definitions as well.
+@^system dependencies@>
+@^notes to myself@>
+
+@d and_and = @'4 /* `\.{\&\&}' */
+@d tab_mark = @'11 /* ASCII code used as tab-skip */
+@d line_feed = @'12 /* ASCII code thrown away at end of line */
+@d form_feed = @'14 /* ASCII code used at end of page */
+@d carriage_return = @'15 /* ASCII code used at end of line */
+@d gt_gt = @'20 /* `\.{>>}'; this doesn't exist in MIT */
+@d lt_lt = @'22 /* `\.{<<}'; this doesn't exist in MIT */
+@d plus_plus = @'13 /* `\.{++}'; this corresponds to MIT's up-arrow */
+@d minus_minus = @'1 /* `\.{--}'; this corresponds to MIT's down-arrow */
+@d minus_gt = @'31 /* `\.{->}' */
+@d not_eq = @'32 /* `\.{!=}' */
+@d lt_eq = @'34 /* `\.{<=}' */
+@d gt_eq = @'35 /* `\.{>=}' */
+@d eq_eq = @'36 /* `\.{==}' */
+@d or_or = @'37 /* `\.{\vert\vert}' */
+
+@<System-dependent parts of character set@>= /* nothing needs to be done */
+
+@* Input routines. The lowest level of input to the \.{WEB} programs
+is performed by |input_ln|, which must be told which file to read from.
+The return value of |input_ln| is 1 if the read is successful and 0 if
+not (generally this means the file has ended). The conventions
+of \TeX\ are followed; i.e., the characters of the next line of the file
+are translated to |ASCII| code and copied into the |buffer| array,
+and the global variable |limit| is set to the first unoccupied position.
+Trailing blanks are ignored. The value of |limit| must be strictly less
+than |buf_size|, so that |buffer[buf_size-1]| is never filled.
+
+We assume that none of the |ASCII| values of |*j| for |buffer<=j<limit|
+is equal to 0, |@'177|, |line_feed|, |form_feed|, or |carriage_return|.
+Since |buf_size| is strictly less than |long_buf_size|,
+some of \.{WEB}'s routines use the fact that it is safe to refer to
+|*(limit+2)| without overstepping the bounds of the array.
+
+@d buf_size = 200 /* for \.{WEAVE} and \.{TANGLE} */
+@d long_buf_size = 500 /* for \.{WEAVE} */
+
+@<Definitions...@>=
+ASCII buffer[long_buf_size]; /* where each line of input goes */
+ASCII *buffer_end=buffer+buf_size-2; /* end of |buffer| */
+ASCII *limit; /* points to the last character in the buffer */
+ASCII *loc; /* points to the next character to be read from the buffer */
+
+@ In the unlikely event that you standard I/O library does not
+support |feof|, |getc| and |ungetc| you may have to change things here.
+@^system dependencies@>
+
+@<Func...@>=
+#include <stdio.h>
+input_ln(fp) /* copies a line into |buffer| or returns 0 */
+FILE *fp; /* what file to read from */
+{
+ register int c; /* the character read */
+ register ASCII *k; /* where next character goes */
+ if (feof(fp)) return(0); /* we have hit end-of-file */
+ limit = k = buffer; /* beginning of buffer */
+ while (k<=buffer_end && (c=getc(fp)) != EOF && c!='\n')
+ if ((*(k++) = xord[c]) != @` ') limit = k;
+ if (k>buffer_end)
+ if ((c=getc(fp))!=EOF && c!='\n') {
+ ungetc(c,fp); loc=buffer; err_print("\n! Input line too long");
+@.Input line too long@>
+ }
+ if (c==EOF && limit==buffer) return(0); /* there was nothing after
+ the last newline */
+ return(1);
+}
+
+@ Now comes the problem of deciding which file to read from next.
+Recall that the actual text that \.{WEB} should process comes from two
+streams: a |web_file|, which can contain possibly nested include
+commands `|@i|', and a |change_file|, which should not contain
+includes. The |web_file| together with the currently open include
+files form a stack |file|, whose names are stored in a parallel stack
+|file_name|. The boolean |changing| tells whether or not we're reading
+form the |change_file|.
+
+The line number of each open file is also kept for error reporting and
+for the benefit of \.{TANGLE}.
+
+@f line x /* make |line| an unreserved word */
+@d max_include_depth = 10 /* maximum number of source files open
+ simultaneously, not counting the change file */
+@d max_file_name_length = 60
+@d cur_file = file[include_depth] /* current file */
+@d cur_file_name = file_name[include_depth] /* current file name */
+@d cur_line = line[include_depth] /* number of current line in current file */
+@d web_file = file[0] /* main source file */
+@d web_file_name = file_name[0] /* main source file name */
+
+@<Definitions...@>=
+int include_depth; /* current level of nesting */
+FILE *file[max_include_depth]; /* stack of non-change files */
+FILE *change_file; /* change file */
+char file_name[max_include_depth][max_file_name_length];
+ /* stack of non-change file names */
+char change_file_name[max_file_name_length]; /* name of change file */
+int line[max_include_depth]; /* number of current line in the stacked files */
+int change_line; /* number of current line in change file */
+boolean input_has_ended; /* if there is no more input */
+boolean changing; /* if the current line is from |change_file| */
+
+@ When |changing=0|, the next line of |change_file| is kept in
+|change_buffer|, for purposes of comparison with the next
+line of |cur_file|. After the change file has been completely input, we
+set |change_limit=change_buffer|, so that no further matches will be made.
+
+Here's a shorthand expression for inequality between the two lines:
+
+@d lines_dont_match = (change_limit-change_buffer != limit-buffer ||
+ strncmp(buffer, change_buffer, limit-buffer))
+
+@<Other...@>=
+ASCII change_buffer[buf_size]; /* where next line of |change_file| is kept */
+ASCII *change_limit; /* points to the last character in |change_buffer| */
+
+@ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
+for the next matching operation. Since blank lines in the change file are
+not used for matching, we have |(change_limit==0 && !changing)| if and
+only if the change file is exhausted. This procedure is called only
+when |changing| is 1; hence error messages will be reported correctly.
+
+@<Func...@>=
+prime_the_change_buffer()
+{
+ change_limit=0; /* this value will be used if the change file ends */
+ @<Skip over comment lines in the change file; |return| if end of file@>;
+ @<Skip to the next nonblank line; |return| if end of file@>;
+ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
+}
+
+@ While looking for a line that begins with \.{@@x} in the change file,
+we allow lines that begin with \.{@@}, as long as they don't begin with
+\.{@@y} or \.{@@z} (which would probably indicate that the change file is
+fouled up).
+
+@<Skip over comment lines in the change file...@>=
+while(1) {
+ change_line++;
+ if (!input_ln(change_file)) return;
+ if (limit<buffer+2) continue;
+ if (buffer[0]!=the_at_sign) continue;
+ @<Lowercasify |buffer[1]|@>;
+ @<Check for erroneous \.{@@i}@>;
+ if (buffer[1]==@`x') break;
+ if (buffer[1]==@`y' || buffer[1]==@`z') {
+ loc=buffer+2;
+ err_print("! Where is the matching @@x?");
+@.Where is the match...@>
+ }
+}
+
+@ This line of code makes |"@@X"| equivalent to |"@@x"| and so on.
+
+@<Lowerc...@>=
+if (buffer[1]>=@`X' && buffer[1]<=@`Z' || buffer[1]==@`I') buffer[1]+=@`z'-@`Z';
+
+@ We do not allow includes in a change file, so as to avoid confusion.
+
+@<Check for erron...@>= {
+ if (buffer[1]==@`i') {
+ loc=buffer+2;
+ err_print("! No includes allowed in change file");
+@.No includes allowed...@>
+ }
+}
+
+@ Here we are looking at lines following the \.{@@x}.
+
+@<Skip to the next nonblank line...@>=
+do {
+ change_line++;
+ if (!input_ln(change_file)) {
+ err_print("! Change file ended after @@x");
+@.Change file ended...@>
+ return;
+ }
+} while (limit==buffer);
+
+@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
+{
+ change_limit=change_buffer-buffer+limit;
+ strncpy(change_buffer,buffer,limit-buffer+1);
+}
+
+@ The following procedure is used to see if the next change entry should
+go into effect; it is called only when |changing| is 0.
+The idea is to test whether or not the current
+contents of |buffer| matches the current contents of |change_buffer|.
+If not, there's nothing more to do; but if so, a change is called for:
+All of the text down to the \.{@@y} is supposed to match. An error
+message is issued if any discrepancy is found. Then the procedure
+prepares to read the next line from |change_file|.
+
+@<Func...@>=
+check_change() /* switches to |change_file| if the buffers match */
+{
+ int n=0; /* the number of discrepancies found */
+ if (lines_dont_match) return;
+ while (1) {
+ changing=1; print_where=1; change_line++;
+ if (!input_ln(change_file)) {
+ err_print("! Change file ended before @@y");
+@.Change file ended...@>
+ change_limit=0; changing=0; print_where=1;
+ return;
+ }
+ if (limit>buffer+1 && buffer[0]==the_at_sign)
+ @<Check for erron...@>;
+ @<If the current line starts with \.{@@y},
+ report any discrepancies and |return|@>;@/
+ @<Move |buffer| and |limit|...@>;@/
+ changing=0; print_where=1; cur_line++;
+ while (!input_ln(cur_file)) { /* pop the stack or quit */
+ if (include_depth==0) {
+ err_print("! WEB file ended during a change");
+@.WEB file ended...@>
+ input_has_ended=1; return;
+ }
+ include_depth--; print_where=1; cur_line++;
+ }
+ if (lines_dont_match) n++;
+ }
+}
+
+@ @<If the current line starts with \.{@@y}...@>=
+if (limit>buffer+1 && buffer[0]==the_at_sign) {
+ @<Lowerc...@>;
+ if (buffer[1]==@`x' || buffer[1]==@`z') {
+ loc=buffer+2; err_print("! Where is the matching @@y?");
+@.Where is the match...@>
+ }
+ else if (buffer[1]==@`y') {
+ if (n>0) {
+ loc=buffer+2;
+ err_print("! Hmm... some of the preceding lines failed to match");
+@.Hmm... some of the preceding...@>
+ }
+ return;
+ }
+}
+
+@ The |reset_input| procedure, which gets \.{CWEAVE} ready to read the
+user's \.{WEB} input, is used at the beginning of phases one and two.
+
+@<Func...@>=
+reset_input()
+{
+ limit=buffer; loc=buffer+1; buffer[0]=@` ';
+ @<Open input files@>;
+ cur_line=0; change_line=0; include_depth=0;
+ changing=1; prime_the_change_buffer(); changing=!changing;
+ limit=buffer; loc=buffer+1; buffer[0]=@` '; input_has_ended=0;
+}
+
+@ The following code opens the input files.
+@^system dependencies@>
+For the input files, we {\em don't} look on the search path.
+@<Open input files@>=
+if ((web_file=fopen(web_file_name,"r"))==NULL)
+ fatal("! Cannot open input file ", web_file_name);
+if ((change_file=fopen(change_file_name,"r"))==NULL)
+ fatal("! Cannot open change file ", change_file_name);
+
+@ The |get_line| procedure is called when |loc>limit|; it puts the next
+line of merged input into the buffer and updates the other variables
+appropriately. A space is placed at the right end of the line.
+This procedure returns |!input_has_ended| because we often want to
+check the value of that variable after calling the procedure.
+
+If we've just changed from the |cur_file| to the |change_file|, or if
+the |cur_file| has changed, we tell \.{TANGLE} to print this
+information in the \cee\ file by means of the |print_where| flag.
+
+@d max_modules = 2000 /* number of identifiers, strings, module names;
+ must be less than 10240 */
+
+
+
+@<Defin...@>=
+typedef unsigned short sixteen_bits;
+sixteen_bits module_count; /* the current module number */
+boolean changed_module[max_modules]; /* is the module changed? */
+boolean print_where=0; /* tells \.{TANGLE} to print line and file info */
+
+@ @<Fun...@>=
+get_line() /* inputs the next line */
+{
+ restart:
+ if (changing) changed_module[module_count]=1;
+ else @<Read from |cur_file| and maybe turn on |changing|@>;
+ if (changing) {
+ @<Read from |change_file| and maybe turn off |changing|@>;
+ if (! changing) {
+ changed_module[module_count]=1; goto restart;
+ }
+ }
+ loc=buffer; *limit=@` ';
+ if (*buffer==the_at_sign && (*(buffer+1)==@`i' || *(buffer+1)==@`I'))
+ @<Push stack and go to |restart|@>;
+ return (!input_has_ended);
+}
+
+@ When a \.{@@i} line is found in the |cur_file|, we must temporarily
+stop reading it and start reading from the named include file. The
+\.{@@i} line should give a complete file name with or without \.{"..."};
+\.{WEB} will look for include files in standard directories using
+the |pathopen| module.
+That is set up at the beginning using the {\tt -I} arguments
+and the {\tt WEBPATH} environment variable.
+@<Add environment variable |"WEBPATH"| to search path@>=
+ pathaddpath(getenv("WEBPATH"),':');
+@ @<Add path name from {\tt -I} to search path@>=
+ pathaddname(*argv+2);
+@ @<Other...@>=
+@i pathopen.h
+@ We need to define an |overflow| {\em function} for |pathopen| to call.
+@ @<Functions@>=
+void @= overflow @>(s)
+ char *s;
+{ overflow(s);}
+@ The included file name should only contain visible ASCII characters,
+since the characters are translated into ASCII and back again.
+
+@<Push stack and...@>= {
+ ASCII *k, *j;
+ loc=buffer+2;
+ while (loc<=limit && (*loc==@` '||*loc==@`\t'||*loc==@`"')) loc++;
+ if (loc>=limit) err_print("! Include file name not given");
+@.Include file name not given@>
+ else {
+ if (++include_depth<max_include_depth) {
+ k=cur_file_name; j=loc;
+ while (*loc!=@` '&&*loc!=@`\t'&&*loc!=@`"') *k++=xchr[*loc++];
+ *k='\0';
+ if ((cur_file=pathopen(cur_file_name,"r"))==NULL) {
+ loc=j;
+ include_depth--;
+ err_print("! Cannot find include file on path");
+@.Cannot find include file on path@>
+ }
+ else {cur_line=0; print_where=1;}
+ }
+ else {
+ include_depth--;
+ err_print("! Too many nested includes");
+@.Too many nested includes@>
+ }
+ }
+ goto restart;
+}
+
+@ @<Read from |cur_file|...@>= {
+ cur_line++;
+ while (!input_ln(cur_file)) { /* pop the stack or quit */
+ print_where=1;
+ if (include_depth==0) {input_has_ended=1; break;}
+ else {fclose(cur_file); include_depth--; cur_line++;}
+ }
+ if (!input_has_ended)
+ if (limit==change_limit-change_buffer+buffer)
+ if (buffer[0]==change_buffer[0])
+ if (change_limit>change_buffer) check_change();
+}
+
+@ @<Read from |change_file|...@>= {
+ change_line++;
+ if (!input_ln(change_file)) {
+ err_print("! Change file ended without @@z");
+@.Change file ended...@>
+ buffer[0]=the_at_sign; buffer[1]=@`z'; limit=buffer+2;
+ }
+ if (limit>buffer+1) /* check if the change has ended */
+ if (buffer[0]==the_at_sign) {
+ @<Lowerc...@>;
+ @<Check for erron...@>;
+ if (buffer[1]==@`x' || buffer[1]==@`y') {
+ loc=buffer+2; err_print("! Where is the matching @@z?");
+@.Where is the match...@>
+ }
+ else if (buffer[1]==@`z') {
+ prime_the_change_buffer(); changing=!changing; print_where=1;
+ }
+ }
+}
+
+@ At the end of the program, we will tell the user if the change file
+had a line that didn't match any relevant line in |web_file|.
+
+@<Funct...@>=
+check_complete(){
+ if (change_limit!=0) { /* |changing| is 0 */
+ strncpy(buffer,change_buffer,change_limit-change_buffer+1);
+ limit=change_limit-change_buffer+buffer;
+ changing=1; loc=change_limit;
+ err_print("! Change file entry did not match");
+ @.Change file entry did not match@>
+ }
+}
+
+@* Storage of identifiers and module names.
+Both \.{WEAVE} and \.{TANGLE} store identifiers, module names and
+other strings in a large array of |ASCII|s, called |byte_mem|.
+Information about the names is kept in the array |name_dir|, whose
+elements are structures of type \&{name\_info}, containing a pointer into
+the |byte_mem| array (the address where the name begins) and other data.
+A \&{name\_pointer} variable is a pointer into |name_dir|.
+
+This module has to go after the module that defines the types of the
+other elements, so it has a special name.
+
+@d max_bytes = 90000 /* the number of bytes in identifiers,
+ index entries, and module names */
+@d max_names = 4000 /* number of identifiers, strings, module names;
+ must be less than 10240 */
+
+@<Definitions that...@>=
+typedef struct name_info {
+ ASCII *byte_start; /* beginning of the name in |byte_mem| */
+ ===> @<More elements of |name_info| structure@>@;
+} name_info; /* contains information about an identifier or module name */
+typedef name_info *name_pointer; /* pointer into array of |name_info|s */
+ASCII byte_mem[max_bytes]; /* characters of names */
+ASCII *byte_mem_end = byte_mem+max_bytes-1; /* end of |byte_mem| */
+name_info name_dir[max_names]; /* information about names */
+name_pointer name_dir_end = name_dir+max_names-1; /* end of |name_dir| */
+
+@ The actual sequence of characters in the name pointed to by a
+|name_pointer| |p| appears in positions
+|p->byte_start| to |(p+1)->byte_start|, inclusive.
+The |print_id| macro prints this text on the user's terminal.
+
+@d length(c) = (c+1)->byte_start-(c)->byte_start /* the length of a name */
+@d print_id(c) = ASCII_write((c)->byte_start,length((c)))
+ /* print identifier or module name */
+
+@ The first unused position in |byte_mem| and |name_dir| is
+kept in |byte_ptr| and |name_ptr|, respectively. Thus we
+usually have |name_ptr->byte_start=byte_ptr|, and certainly
+we want to keep |name_ptr<=name_dir_end| and |byte_ptr<=byte_mem_end|.
+
+@<Defini...@>=
+name_pointer name_ptr; /* first unused position in |byte_start| */
+ASCII *byte_ptr; /* first unused position in |byte_mem| */
+
+@ @<Init...@>=
+name_dir->byte_start=byte_ptr=byte_mem; /* position zero in both arrays */
+name_ptr=name_dir+1; /* |name_dir[0]| will not be used */
+name_ptr->byte_start=byte_mem; /* this makes name 0 of length zero */
+
+@ The names of identifiers are found by computing a hash address |h| and
+then looking at strings of bytes signified by the |name_pointer|s
+|hash[h]|, |hash[h]->link|, |hash[h]->link->link|, \dots,
+until either finding the desired name or encountering the null pointer.
+
+@<More elements of |name...@>=
+struct name_info *link;
+
+@ The hash table itself
+consists of |hash_size| entries of type |name_pointer|, and is
+updated by the |id_lookup| procedure, which finds a given identifier
+and returns the appropriate |name_pointer|. The matching is done by the
+function |names_match|, which is slightly different in
+\.{WEAVE} and \.{TANGLE}. If there is no match for the identifier,
+it is inserted into the table.
+
+@d hash_size = 353 /* should be prime */
+
+@<Defini...@>=
+typedef name_pointer *hash_pointer;
+name_pointer hash[hash_size]; /* heads of hash lists */
+hash_pointer hash_end = hash+hash_size-1; /* end of |hash| */
+hash_pointer h; /* index into hash-head array */
+
+@ Initially all the hash lists are empty.
+
+@<Init...@>=
+for (h=hash; h<=hash_end; *h++=NULL) ;
+
+@ Here is the main procedure for finding identifiers:
+
+@u name_pointer
+id_lookup(first,last,t) /* looks up a string in the identifier table */
+ASCII *first; /* first character of string */
+ASCII *last; /* last character of string plus one */
+eight_bits t; /* the |ilk|; used by \.{WEAVE} and by \.{TANGLE} for macros */
+{
+ ASCII *i=first; /* position in |buffer| */
+ int h; /* hash code */
+ int l; /* length of the given identifier */
+ name_pointer p; /* where the identifier is being sought */
+ if (last==NULL) for (last=first; *last!='\0'; last++);
+ l=last-first; /* compute the length */
+ @<Compute the hash code |h|@>;
+ @<Compute the name location |p|@>;
+ if (p==name_ptr) @<Enter a new name into the table at position |p|@>;
+ return(p);
+}
+
+@ A simple hash code is used: If the sequence of
+ASCII codes is $c_1c_2\ldots c_n$, its hash value will be
+$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$
+
+@<Compute the hash...@>=
+h=*i; while (++i<last) h=(h+h+*i) % hash_size;
+
+@ If the identifier is new, it will be placed in position |p=name_ptr|,
+otherwise |p| will point to its existing location.
+
+@<Compute the name location...@>=
+p=hash[h];
+while (p && !names_match(p,first,l,t)) p=p->link;
+if (p==NULL) {
+ p=name_ptr; /* the current identifier is new */
+ p->link=hash[h]; hash[h]=p; /* insert |p| at beginning of hash list */
+}
+
+@ The information associated with a new identifier must be initialized
+in a slightly different way in \.{WEAVE} than in \.{TANGLE}; hence the
+|init_p| procedure.
+
+@<Enter a new name...@>= {
+ if (byte_ptr+l>byte_mem_end) overflow("byte memory");
+ if (name_ptr>=name_dir_end) overflow("name");
+ strncpy(byte_ptr,first,l);
+ (++name_ptr)->byte_start=byte_ptr+=l;
+ init_p(p,t); /* set ilk */
+}
+
+@ The names of modules are stored in |byte_mem| together
+with the identifier names, but a hash table is not used for them because
+\.{TANGLE} needs to be able to recognize a module name when given a prefix of
+that name. A conventional binary seach tree is used to retrieve module names,
+with fields called |llink| and |rlink| (where |llink| takes the place
+of |link|). The root of this tree is stored in |name_dir->rlink|;
+this will be the only information in |name_dir[0]|.
+
+Since the space used by |rlink| has a different function for
+identifiers than for module names, we declare it as a |union|.
+
+@d llink = link /* left link in binary search tree for module names */
+@d rlink = dummy.Rlink /* right link in binary search tree for module names */
+@d root = name_dir->rlink /* the root of the binary search tree
+ for module names */
+
+@<More elements of |name...@>=
+union {
+ struct name_info *Rlink; /* right link in binary search tree for module
+ names */
+ eight_bits Ilk; /* used by identifiers in \.{WEAVE} only */
+} dummy;
+
+@ @<Init...@>=
+root=NULL; /* the binary search tree starts out with nothing in it */
+
+@ The |mod_lookup| procedure finds a module name in the
+search tree, after inserting it if necessary, and returns a pointer to
+where it was found.
+
+According to the rules of \.{WEB}, no module name should be a proper
+prefix of another, so a ``clean'' comparison should occur between any
+two names. The result of |mod_lookup| is |NULL| if this prefix condition
+is violated. An error message is printed when such violations are detected.
+
+@d less = 0 /* the first name is lexicographically less than the second */
+@d equal = 1 /* the first name is equal to the second */
+@d greater = 2 /* the first name is lexicographically greater than the second */
+@d prefix = 3 /* the first name is a proper prefix of the second */
+@d extension = 4 /* the first name is a proper extension of the second */
+
+@<Other...@>=
+name_pointer install_node();
+
+@ @u name_pointer
+mod_lookup(k,l) /* finds module name */
+ASCII *k; /* first character of name */
+ASCII *l; /* last character of name */
+{
+ short c = greater; /* comparison between two names */
+ name_pointer p = root; /* current node of the search tree */
+ name_pointer q = name_dir; /* father of node |p| */
+ while (p) {
+ c=web_strcmp(k,l+1,p->byte_start,(p+1)->byte_start);
+ q=p;
+ switch(c) {
+ case less: p=p->llink; continue;
+ case greater: p=p->rlink; continue;
+ case equal: return p;
+ default: err_print("! Incompatible section names"); return NULL;
+@.Incompatible section names@>
+ }
+ }
+ return(install_node(q,c,k,l-k+1));
+}
+
+@ This function is like |strcmp|, but it does not assume the strings
+are null-terminated.
+
+@u
+web_strcmp(j,j1,k,k1) /* fuller comparison than |strcmp| */
+ASCII *j; /* beginning of first string */
+ASCII *j1; /* end of first string plus one */
+ASCII *k; /* beginning of second string */
+ASCII *k1; /* end of second string plus one */
+{
+ while (k<k1 && j<j1 && *j==*k) k++, j++;
+ if (k==k1) if (j==j1) return equal;
+ else return extension;
+ else if (j==j1) return prefix;
+ else if (*j<*k) return less;
+ else return greater;
+}
+
+@ The reason we initialized |c| to |greater| is so that
+|name_pointer| will make |name_dir->rlink| point to the root of the tree
+when |q=name_dir|, that is, the first time it is called.
+
+The information associated with a new node must be initialized
+in a slightly different way in \.{WEAVE} than in \.{TANGLE}; hence the
+|init_node| procedure.
+
+@u name_pointer
+install_node(parent,c,j,name_len) /* install a new node in the tree */
+name_pointer parent; /* parent of new node */
+int c; /* right or left? */
+ASCII *j; /* where replacement text starts */
+int name_len; /* length of replacement text */
+{
+ name_pointer node=name_ptr; /* new node */
+ if (byte_ptr+name_len>byte_mem_end) overflow("byte memory");
+ if (name_ptr==name_dir_end) overflow("name");
+ if (c==less) parent->llink=node; else parent->rlink=node;
+ node->llink=NULL; node->rlink=NULL;
+ init_node(node);
+ strncpy(byte_ptr,j,name_len);
+ (++name_ptr)->byte_start=byte_ptr+=name_len;
+ return(node);
+}
+
+@ The |prefix_lookup| procedure is supposed to find exactly one module
+name that has $|k|\ldots|l|$ as a prefix. Actually the algorithm
+silently accepts also the situation that some module name is a prefix of
+$|k|\ldots|l|$, because the user who painstakingly typed in more than
+necessary probably doesn't want to be told about the wasted effort.
+
+@u name_pointer
+prefix_lookup(k,l) /* finds module name given a prefix */
+ASCII *k; /* first char of prefix */
+ASCII *l; /* last char of prefix */
+{
+ short c = greater; /* comparison between two names */
+ short count = 0; /* the number of hits */
+ name_pointer p = root; /* current node of the search tree */
+ name_pointer q = NULL;
+ /* another place to resume the search after one is done */
+ name_pointer r = NULL; /* extension found */
+ while (p) {
+ c=web_strcmp(k,l+1,p->byte_start,(p+1)->byte_start);
+ switch(c) {
+ case less: p=p->llink; break;
+ case greater: p=p->rlink; break;
+ default: r=p; count++; q=p->rlink; p=p->llink;
+ }
+ if (p==NULL) {
+ p=q; q=NULL;
+ }
+ }
+ if (count==0) err_print("! Name does not match");
+@.Name does not match@>
+ if (count>1) err_print("! Ambiguous prefix");
+@.Ambiguous prefix@>
+ return(r); /* the result will be |NULL| if there was no match */
+}
+
+@ The last component of |name_info| is different for \.{TANGLE} and
+\.{WEAVE}. In \.{TANGLE}, if |p| is a pointer to a module name,
+|p->equiv| is a pointer to its replacement text, an element of the
+array |text_info|. In \.{WEAVE}, on the other hand, if
+|p| points to an identifier, |p->xref| is a pointer to its
+list of cross-references, an element of the array |xmem|. The make-up
+of |text_info| and |xmem| is discussed in the \.{TANGLE} and \.{WEAVE}
+source files, respectively; here we just declare a common field
+|equiv_or_xref| as a pointer to an |ASCII|.
+
+@<More elements of |name...@>=
+ASCII *equiv_or_xref; /* info corresponding to names */
+
+@* Reporting errors to the user.
+A global variable called |history| will contain one of four values
+at the end of every run: |spotless| means that no unusual messages were
+printed; |harmless_message| means that a message of possible interest
+was printed but no serious errors were detected; |error_message| means that
+at least one error was found; |fatal_message| means that the program
+terminated abnormally. The value of |history| does not influence the
+behavior of the program; it is simply computed for the convenience
+of systems that might want to use such information.
+
+@d spotless = 0 /* |history| value for normal jobs */
+@d harmless_message = 1 /* |history| value when non-serious info was printed */
+@d error_message = 2 /* |history| value when an error was noted */
+@d fatal_message = 3 /* |history| value when we had to stop prematurely */
+@d mark_harmless = {if (history==spotless) history=harmless_message;}
+@d mark_error = history=error_message
+
+@<Definit...@>=
+int history=spotless; /* indicates how bad this run was */
+
+@ The command `|err_print("! Error message")|' will report a syntax error to
+the user, by printing the error message at the beginning of a new line and
+then giving an indication of where the error was spotted in the source file.
+Note that no period follows the error message, since the error routine
+will automatically supply a period.
+
+The actual error indications are provided by a procedure called |error|.
+However, error messages are not actually reported during phase one,
+since errors detected on the first pass will be detected again
+during the second.
+
+@<Functions...@>=
+err_print(s) /* prints `\..' and location of error message */
+char *s;
+{
+ ASCII *k,*l; /* pointers into |buffer| */
+ printf("\n%s",(s==(char *)0) ? "Bad error message!!!" : s);
+ if (setting_up) {
+printf("\nError occurred while scanning arguments or opening output files. ");
+ } else {
+ @<Print error location based on input buffer@>;
+ }
+ fflush(stdout); mark_error;
+}
+
+@ The error locations can be indicated by using the global variables
+|loc|, |cur_line|, |cur_file_name| and |changing|,
+which tell respectively the first
+unlooked-at position in |buffer|, the current line number, the current
+file, and whether the current line is from |change_file| or |cur_file|.
+This routine should be modified on systems whose standard text editor
+has special line-numbering conventions.
+@^system dependencies@>
+
+@<Print error location based on input buffer@>=
+if (changing) printf(". (l. %d of change file)\n", change_line);
+else if (include_depth==0) printf(". (l. %d)\n", cur_line);
+ else printf(". (l. %d of include file %s)\n", cur_line, cur_file_name);
+l= (loc>=limit? limit: loc);
+if (l>buffer) {
+ for (k=buffer; k<l; k++)
+ if (*k=='\t') putchar(' ');
+ else putchar(xchr[*k]); /* print the characters already read */
+ putchar('\n');
+ for (k=buffer; k<l; k++) putchar(' '); /* space out the next line */
+}
+for (k=l; k<limit; k++) putchar(xchr[*k]); /* print the part not yet read */
+if (*limit==@`|') putchar('|'); /* end of \cee\ text in module names */
+putchar(' '); /* to separate the message from future asterisks */
+
+@ When no recovery from some error has been provided, we have to wrap
+up and quit as graciously as possible. This is done by calling the
+function |wrap_up| at the end of the code.
+
+@d fatal(s1,s2) = {
+ printf(s1); err_print(s2);
+ history=fatal_message; wrap_up();
+}
+
+@ Sometimes the program's behavior is far different from what it should be,
+and \.{WEB} prints an error message that is really for the \.{WEB}
+maintenance person, not the user. In such cases the program says
+|confusion("indication of where we are")|.
+
+@d confusion(s) = fatal("\n! This can't happen: ",s)
+@.This can't happen@>
+
+@ An overflow stop occurs if \.{WEB}'s tables aren't large enough.
+
+@d overflow(s) = {
+ fatal("\n! Sorry, capacity exceeded: ",s);
+}
+@.Sorry, x capacity exceeded@>
+
+@ Some implementations may wish to pass the |history| value to the
+operating system so that it can be used to govern whether or not other
+programs are started. Here, for instance, we pass the operating system
+a status of 0 if and only if only harmless messages were printed.
+@^system dependencies@>
+
+@<Func...@>=
+wrap_up() {
+ putchar('\n');
+ @<Print the job |history|@>;
+ if (history > harmless_message) exit(1);
+ else exit(0);
+}
+
+@ @<Print the job |history|@>=
+switch (history) {
+case spotless: printf("(No errors were found.)\n"); break;
+case harmless_message:
+ printf("(Did you see the warning message above?)\n"); break;
+case error_message:
+ printf("(Pardon me, but I think I spotted something wrong.)\n"); break;
+case fatal_message: printf("(That was a fatal error, my friend.)\n");
+} /* there are no other cases */
+
+@* Command line arguments.
+The user calls \.{CWEAVE} and \.{CTANGLE} with arguments on the command line.
+These are either file names or flags (beginning with |'-'|).
+The following globals are for communicating the user's desires to the rest
+of the program. The various file name variables contain strings with
+the names of those files.
+
+The only flag that affects \.{CWEAVE} is |"-x"|, whose status is kept in
+|no_xref|; it means the cross-referencing information---the index, the
+module name list, and the table of contents---is to be suppressed.
+No flags affect \.{CTANGLE}.
+
+@<Defin...@>=
+int argc; /* copy of |ac| parameter to |main| */
+char **argv; /* copy of |av| parameter to |main| */
+char C_file_name[max_file_name_length]; /* name of |C_file| */
+char tex_file_name[max_file_name_length]; /* name of |tex_file| */
+int no_xref; /* should \.{WEAVE} print an index? */
+
+@ We now must look at the command line arguments and set the file names
+accordingly. At least one file name must be present: the \.{WEB}
+file. It may have an extension, or it may omit it to get |".web"|
+added. The \TeX\ output file name is formed by replacing the \.{WEB}
+file name extension by |".tex"|, and the \cee\ file name by replacing
+the extension by |C_file_extension|
+@<External...@>=extern char C_file_extension[];
+
+@ If there is another file name present among the arguments, it is the
+change file, again either with an extension or without one to get |".ch"|
+An omitted change file argument means that |"/dev/null"| should be used,
+when no changes are desired.
+@^system dependencies@>
+
+@<Function...@>=
+scan_args()
+{
+ char *dot_pos, *index(); /* position of |'.'| in the argument */
+ boolean found_web=0,found_change=0; /* have these names have been seen? */
+ no_xref=0;
+ while (--argc > 0) {
+ if (**(++argv)!='-') {
+ if (!found_web) @<Make
+ |web_file_name|, |tex_file_name| and |C_file_name|@>@;
+ else if (!found_change) @<Make |change_file_name| from |fname|@>@;
+ else @<Print usage error message and quit@>;
+ }
+ else @<Handle flag argument@>;
+ }
+ if (!found_web) @<Print usage error message and quit@>;
+ if (!found_change) @<Set up null change file@>;
+}
+
+@ We use all of |*argv| for the |web_file_name| if there is a |'.'| in it,
+otherwise add |".web"|. The other file names come from adding things
+after the dot. We must check that there is enough room in
+|web_file_name| and the other arrays for the argument.
+
+@<Make |web_file_name|...@>=
+{
+ if (strlen(*argv) > max_file_name_length-5)
+ @<Complain about argument length@>;
+ if ((dot_pos=index(*argv,'.'))==NULL)
+ sprintf(web_file_name,"%s.web",*argv);
+ else {
+ sprintf(web_file_name,"%s",*argv);
+ @<Advance |dot_pos| to the last |'.'| in |*argv|@>
+ *dot_pos=0; /* string now ends where the dot was */
+ }
+ sprintf(tex_file_name,"%s.tex",*argv);
+ sprintf(C_file_name,"%s.%s",*argv,C_file_extension);
+ found_web=1;
+}
+
+@ @<Advance |dot_pos| to the last |'.'| in |*argv|@>=
+{ char *p;
+ p=dot_pos;
+ while (*++p != '\0')
+ if (*p=='.') dot_pos=p;
+}
+
+@ @<Make |change_file_name|...@>=
+{
+ if (strlen(*argv) > max_file_name_length-5)
+ @<Complain about argument length@>;
+ if ((dot_pos=index(*argv,'.'))==NULL)
+ sprintf(change_file_name,"%s.ch",*argv);
+ else sprintf(change_file_name,"%s",*argv);
+ found_change=1;
+}
+
+@ @<Set up null...@>=
+#ifdef MSDOS
+strcpy(change_file_name,"NUL");
+#else
+strcpy(change_file_name,"/dev/null");
+#endif
+@
+We have two possible flags:
+\medskip
+\halign{\tabskip=1em\tt#\hfil&#\hfil\cr
+\omit{\bf Argument}\hfil&\omit\bf Function\hfil\cr
+-x&Turns off \.{WEAVE} cross-reference.\cr
+-I<pathname>&%
+Adds {\tt <pathname>} to the directories searched for included files.\cr
+}
+@<Handle flag...@>=
+{
+ switch((*argv)[1]) {
+ case 'x':
+ no_xref=1;
+ break;
+ case 'I':
+ @<Add path name from {\tt -I} to search path@>@;
+ break;
+ default:
+ @<Complain about unknown option@>;
+ break;
+ }
+}
+
+@ @<Print usage error message and quit@>=
+{
+if (program==tangle)
+fatal("! Usage: tangle webfile[.web] [changefile[.ch]] [-Ipathname ...]\n",0)@;
+else
+fatal("! Usage: weave webfile[.web] [changefile[.ch]] [-x] [-Ipathname ...]\n",
+ 0);
+}
+
+@ @<Complain about arg...@>= fatal("! Filename %s too long\n", *argv);
+
+@ @<Complain about unk...@>= {
+printf("! Unknown option in argument %s\n", *argv); mark_harmless;
+}
+
+@* Output. Here is the code that opens the output file:
+@^system dependencies@>
+
+@<Defin...@>=
+FILE *C_file; /* where output of \.{TANGLE} goes */
+FILE *tex_file; /* where output of \.{WEAVE} goes */
+
+@ @<Scan arguments and open output files@>=
+scan_args();
+@<Add environment variable |"WEBPATH"| to search path@>@;
+if (program==tangle) {
+ if ((C_file=fopen(C_file_name,"w"))==NULL)
+ fatal("! Cannot open output file ", C_file_name);
+}
+else {
+ if ((tex_file=fopen(tex_file_name,"w"))==NULL)
+ fatal("! Cannot open output file ", tex_file_name);
+}
+
+@ The |update_terminal| procedure is called when we want
+to make sure that everything we have output to the terminal so far has
+actually left the computer's internal buffers and been sent.
+@^system dependencies@>
+
+@d update_terminal = fflush(stdout) /* empty the terminal output buffer */
+
+@ Terminal output uses |putchar| and |putc| when we have to
+translate from \.{WEB}'s code into the external character code,
+and |printf| when we just want to print strings.
+@^system dependencies@>
+@d new_line = putchar('\n') @d putxchar = putchar
+@d ASCII_write(a,b) = fflush(stdout), write(1,a,b) /* write on the standard output */
+@d line_write(c) = write(fileno(C_file),c) /* write on the C output file */
+@d C_printf(c,a) = fprintf(C_file,c,a)
+@d C_putc(c) = putc(c,C_file)
+
+@* Index.
diff --git a/web/spiderweb/src/master/cycle.awk b/web/spiderweb/src/master/cycle.awk
new file mode 100644
index 0000000000..602544b18a
--- /dev/null
+++ b/web/spiderweb/src/master/cycle.awk
@@ -0,0 +1,86 @@
+##157:
+
+#line 22 "cycle.web"
+
+#line 23 "cycle.web"
+NF==4&&$3=="-->"{
+incoming[$4]+=1
+if(incoming[$2]==""){
+incoming[$2]=0
+}
+successors[$2]=successors[$2]" "$4
+outgoing_edges[$2]=outgoing_edges[$2]$0"\n"
+next
+}
+!/^ *$/{
+print"What's all this?",$0
+exit 1
+}
+END{
+##158:
+
+#line 57 "cycle.web"
+
+#line 58 "cycle.web"
+for(node in incoming){
+if(incoming[node]==0){
+work[high++]=node
+}
+}
+
+##:158
+
+#line 38 "cycle.web"
+
+n=0
+while(n<high){
+##159:
+
+#line 64 "cycle.web"
+
+#line 65 "cycle.web"
+thisnode=work[n]
+temp=successors[thisnode]
+m=split(temp,newnodes," ")
+for(j=1;j<=m;j++){
+thisnode=newnodes[j]
+incoming[thisnode]-=1
+if(incoming[thisnode]==0){
+work[high++]=thisnode
+}
+}
+
+##:159
+
+#line 42 "cycle.web"
+
+n++
+}
+for(node in incoming){
+if(incoming[node]!=0){
+##160:
+
+#line 76 "cycle.web"
+
+#line 77 "cycle.web"
+if(cycle==0){
+print"There is a potential cycle here somewhere -- check the context"
+cycle=1
+}
+printf"%s",outgoing_edges[node]
+#line 2527 "/u/norman/pu/web/dist/src/master/spider.web"
+##:160
+
+#line 47 "cycle.web"
+
+}
+}
+if(cycle==0){
+print"There can't possibly be a cycle in the graph"
+}else{
+exit 0
+}
+}
+
+##:157
+
diff --git a/web/spiderweb/src/master/cycle.web b/web/spiderweb/src/master/cycle.web
new file mode 100644
index 0000000000..fed5efc20a
--- /dev/null
+++ b/web/spiderweb/src/master/cycle.web
@@ -0,0 +1,81 @@
+% Copyright 1989 by Norman Ramsey, Odyssey Research Associates
+% Not to be sold, but may be used freely for any purpose
+% For more information, see file COPYRIGHT in the parent directory
+
+#*Detecting cycles in a directed graph of productions.
+{\tt SPIDER} writes a list of possibly cyclic productions on
+{\tt cycle.test}. The program {\tt cycle.awk} reads those and
+reports on whether there can be a cycle in the graph.
+If there were a cycle, it might lead to an infinite loop in \.{WEAVE}.
+One should inspect contexts to see if that could really happen.
+
+# Input is a list of edges in the form
+$$\hbox{\tt name leftnode --> rightnode}$$
+We detect a cycle if one exists.
+
+# We will use the following strategy: assemble all the edges, then
+read through them to label each node with the number of incoming edges
+and then the names of the successor nodes.
+We will then put nodes with no incoming edges on the work list, and
+remove them one by one, decrementing the incoming counts of their successors.
+If anything is left, there must be a cycle.
+#(cycle.awk#>=
+NF==4 && $3=="-->" {
+ incoming[$4]+=1
+ if (incoming[$2]=="") {
+ incoming[$2]=0
+ }
+ successors[$2]=successors[$2] " " $4
+ outgoing_edges[$2]=outgoing_edges[$2] $0 "\n"
+ next
+ }
+#=!/^ *$/#> {
+ print "What's all this?", $0
+ exit 1
+ }
+END {
+ #<Put all nodes with no incoming edges in |work[n]|, where
+ |0 <= n < high|#>
+ n=0
+ while (n<high) {
+ #<For each successor of |work[n]|, decrement the
+ incoming edge count and add to |work[]| if zero#>
+ n++
+ }
+ for (node in incoming) {
+ if (incoming[node]!=0) {
+ #<Announce a cycle and set |cycle=1|#>
+ }
+ }
+ if (cycle==0) {
+ print "There can't possibly be a cycle in the graph"
+ } else {
+ exit 0 ## no error until we check context
+ }
+ }
+
+# #<Put all nodes with no incoming edges in...#>=
+for (node in incoming) {
+ if (incoming[node]==0) {
+ work[high++]=node
+ }
+ }
+
+# #<For each successor...#>=
+thisnode=work[n]
+temp=successors[thisnode]
+m = split(temp,newnodes," ")
+for (j=1; j<=m; j++) {
+ thisnode=newnodes[j]
+ incoming[thisnode]-=1
+ if (incoming[thisnode]==0) {
+ work[high++]=thisnode
+ }
+ }
+
+# #<Announce a cycle...#>=
+if (cycle==0) {
+ print "There is a potential cycle here somewhere -- check the context"
+ cycle=1
+ }
+printf "%s", outgoing_edges[node]
diff --git a/web/spiderweb/src/master/junk.list b/web/spiderweb/src/master/junk.list
new file mode 100644
index 0000000000..0d21e78e20
--- /dev/null
+++ b/web/spiderweb/src/master/junk.list
@@ -0,0 +1,22 @@
+##163:
+
+#line 2555 "spider.web"
+
+#line 2556 "spider.web"
+good translations
+break_space
+force
+big_force
+opt
+backup
+big_cancel
+cancel
+indent
+outdent
+math_rel
+math_bin
+math_op
+test translations
+
+##:163
+
diff --git a/web/spiderweb/src/master/multitang.ch b/web/spiderweb/src/master/multitang.ch
new file mode 100644
index 0000000000..349a54d866
--- /dev/null
+++ b/web/spiderweb/src/master/multitang.ch
@@ -0,0 +1,107 @@
+@x 423
+output:
+@y
+output.
+We have made some modifications to \.{TANGLE} so it will write output
+on multiple files.
+We do this very simply: if a module name is introduced by \.{@@(}
+instead of \.{@@<}, we treat it as the name of a file.
+All these special modules are saved on a stack, |output_files|.
+We write them out after we've done the unnamed module.
+@z
+@x 425
+@c
+@y
+@d max_files 256
+@<Glob...@>=
+name_pointer output_files[max_files];
+name_pointer *cur_file, *end_output_files;
+char cur_module_char; /* is it |'<'| or |'('| */
+char output_file_name[longest_name]; /* name of the file */
+
+@ We make |end_output_files| point jsut beyond the end of
+|output_files|.
+|cur_file| starts out there. Every time we see a new file, we
+decrement |cur_file| and then write it in.
+@<Set initial...@>=
+cur_file=end_output_files=output_files+max_files;
+
+@ @<Add |cur_module| to the output file stack, or complain we're out
+of room@>=
+{
+if (cur_file>output_files)
+ *--curfile=cur_module;
+else {
+ overflow("output files");
+}
+}
+
+@ Here is the output switch, then...
+
+@c
+@z
+@x 434
+ printf("\nWriting the output file"); update_terminal;
+@y
+ printf("\nWriting the output files: (%s)",C_file_name); update_terminal;
+@z
+@x 438
+ printf("\nDone.");
+@y
+ @<Write all the named output files@>
+ printf("\nDone.");
+@z
+@x 442
+@ First we go through the list of replacement texts and copy the ones
+@y
+@ To write the named output files, we proceed as for the unnamed
+module.
+The only subtlety is that we have to open each one.
+@<Write all the named output files@>=
+for (;cur_file<end_output_files; cur_file++) {
+ strncpy(output_file_name,(*curfile)->byte_start, longest_name);
+ output_file_name[length(*curfile)]='\0';
+ fclose(C_file);
+ C_file=fopen(output_file_name,"w");
+ if (C_file <=0) {
+ fatal("! Cannot open output file:",output_file_name)
+ } else {
+ printf("(%s)",output_file_name); update_terminal;
+ }
+ stack_ptr=stack+1;
+ cur_name= (*cur_file);
+ cur_repl= cur_name->equiv_or_xref;
+ cur_byte=cur_repl->tok_start;
+ cur_end=(cur_repl+1)->tok_start;
+ /* cur_mod= it */;
+ while (stack_ptr > stack) get_output();
+ flush_buffer();
+}
+
+
+
+@ First we go through the list of replacement texts and copy the ones
+@z
+@x 575
+ ccode['&']=join; ccode['<']=module_name;
+@y
+ ccode['&']=join;
+ ccode['<']=ccode['(']=module_name;
+@z
+@x 778
+ case module_name: @<Scan the module name and make |cur_module|
+ point to it@>;
+@y
+ case module_name:
+ cur_module_char=*(loc-1);
+ @<Scan the module name and make |cur_module| point to it@>;
+@z
+@x 803
+ return(module_name);
+@y
+ if (cur_module_char=='(') {
+ @<Add |cur_module| to the output file stack, or complain we're out
+ of room@>
+ }
+ return(module_name);
+@z
diff --git a/web/spiderweb/src/master/newdefs.awk b/web/spiderweb/src/master/newdefs.awk
new file mode 100644
index 0000000000..1432a2e60f
--- /dev/null
+++ b/web/spiderweb/src/master/newdefs.awk
@@ -0,0 +1,11 @@
+## Used to convert old Levy CWEB style definitions to Spidery WEB definitions
+/#d|@d/ {
+ for (i=1; i<NF; i++) {
+ if ($i=="#d"||$i=="@d") {
+ $(i+1) = $(i+1) " ="
+ }
+ }
+ print
+ next
+}
+{print}
diff --git a/web/spiderweb/src/master/nodups.awk b/web/spiderweb/src/master/nodups.awk
new file mode 100644
index 0000000000..e22f1d3f65
--- /dev/null
+++ b/web/spiderweb/src/master/nodups.awk
@@ -0,0 +1,13 @@
+##161:
+
+#line 2529 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2530 "/u/norman/pu/web/dist/src/master/spider.web"
+{if($0==last){
+print"Error: duplicate name",$0,"on lines",NR-1"-"NR
+exit-1
+}
+last=$0
+}
+##:161
+
diff --git a/web/spiderweb/src/master/pathopen.c b/web/spiderweb/src/master/pathopen.c
new file mode 100644
index 0000000000..626a7117c5
--- /dev/null
+++ b/web/spiderweb/src/master/pathopen.c
@@ -0,0 +1,122 @@
+/*4:*/
+#line 88 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+/*9:*/
+#line 171 "/u/norman/pu/web/dist/src/master/pathopen.web"
+#include<stdio.h>
+
+/*:9*/
+#line 89 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+
+static int nextpath= 0;
+static char pathtexts[1024];
+static char *searchpath[64]= {pathtexts};
+static char *maxpathtexts= pathtexts+1024;
+
+void pathreset()
+{
+nextpath= 0;
+searchpath[nextpath]= pathtexts;
+}
+
+/*:4*//*5:*/
+#line 103 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+void pathaddname(name)
+char *name;
+{char *t= searchpath[nextpath];
+if(name==NULL)return;
+if(nextpath>=64)overflow("paths");
+while( *name){
+if(t>=maxpathtexts)overflow("path texts");
+ *t++= *name++;
+}
+/*6:*/
+#line 118 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+if(t==searchpath[nextpath])nextpath--;
+else if(t==searchpath[nextpath]+1&&
+ *searchpath[nextpath]=='/')
+t--;
+
+/*:6*/
+#line 114 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+searchpath[++nextpath]= t;
+}
+
+/*:5*//*7:*/
+#line 125 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+#line 126 "/u/norman/pu/web/dist/src/master/pathopen.web"
+void pathaddpath(path,path_separator)
+char *path;
+char path_separator;
+{
+char *t= searchpath[nextpath];
+if(path!=NULL){
+while( *path){
+if(nextpath>=64)overflow("paths");
+while( *path!=path_separator&& *path!='\0'){
+if(t>=maxpathtexts)overflow("path texts");
+ *t++= *path++;
+}
+/*6:*/
+#line 118 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+if(t==searchpath[nextpath])nextpath--;
+else if(t==searchpath[nextpath]+1&&
+ *searchpath[nextpath]=='/')
+t--;
+
+/*:6*/
+#line 138 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+searchpath[++nextpath]= t;
+if( *path)path++;
+}
+}
+}
+
+/*:7*//*8:*/
+#line 151 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+FILE *pathopen(name)
+char *name;
+{
+FILE *fp;
+char pathname[1024];
+char *s, *t;
+int i;
+
+if(( *(name)=='/'))
+return fopen(name,"r");
+else{
+if((fp= fopen(name,"r"))!=NULL)return fp;
+for(i= 0;i<nextpath;i++){
+/*10:*/
+#line 173 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+#line 174 "/u/norman/pu/web/dist/src/master/pathopen.web"
+for(s= pathname,t= searchpath[i];t<searchpath[i+1];){
+ *s++= *t++;
+if(s>pathname+1024)overflow("path name length");
+}
+ *s++= '/';
+if(s>pathname+1024)overflow("path name length");
+t= name;
+while( *s++= *t++)
+if(s>pathname+1024)overflow("path name length");
+if((fp= fopen(pathname,"r"))!=NULL)return fp;
+
+
+/*:10*/
+#line 165 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+}
+}
+return NULL;
+}
+
+/*:8*/
diff --git a/web/spiderweb/src/master/pathopen.h b/web/spiderweb/src/master/pathopen.h
new file mode 100644
index 0000000000..7fbb7cf479
--- /dev/null
+++ b/web/spiderweb/src/master/pathopen.h
@@ -0,0 +1,9 @@
+/*11:*/
+#line 187 "/u/norman/pu/web/dist/src/master/pathopen.web"
+
+#line 188 "/u/norman/pu/web/dist/src/master/pathopen.web"
+void pathaddname();
+void pathaddpath();
+void pathreset();
+FILE *pathopen();
+/*:11*/
diff --git a/web/spiderweb/src/master/pathopen.web b/web/spiderweb/src/master/pathopen.web
new file mode 100644
index 0000000000..6e374eacc5
--- /dev/null
+++ b/web/spiderweb/src/master/pathopen.web
@@ -0,0 +1,192 @@
+% Copyright 1989 by Norman Ramsey, Odyssey Research Associates
+% Not to be sold, but may be used freely for any purpose
+% For more information, see file COPYRIGHT in the parent directory
+
+\def\title{Finding a file on a path}
+@*Introduction.
+This module is intended to make it easy for applications to open
+a file that is not necessarily in the current directory,
+but is somewhere on a search path.
+The notion is to specify the path ahead of time, so that
+calls to |fopen(n,"r")| can be replaced with calls to |pathopen(n)|.
+{\em No ASCII conversions are done; everything is in the character set of the
+local machine.}
+
+@ The functions we export are:
+\medskip
+\noindent{\tabskip=0ptplus1fil
+\halign to \hsize{\vrule height 10pt depth3.5pt width0pt
+ \hfil#\tabskip=2em&\vtop{\hsize=4in
+ \noindent\ignorespaces#\par}
+ \tabskip=0ptplus1fil\cr
+\omit\hfil\strut{\bf Function name and signature}&
+\omit\bf What it does\hfil\cr
+\noalign{\smallskip}
+|pathreset()|&
+Initializes the system to search the empty path.
+This initialization is performed statically, so programs needn't call
+|pathreset|, but then they may not be serially reusable.
+\cr
+|pathaddname(char *)|&
+Adds the name to the search path (FIFO).
+If the first argument is |NULL| or points to the empty string,
+|pathaddname| is a no-op.
+\cr
+|pathaddpath(char *, char)|&
+Adds the given path to the search path (FIFO).
+The second argument is the character that is used to separate names on
+the path, e.g. |pathaddpath(getenv("PATH"),':')|.
+If the first argument is |NULL| or points to the empty string,
+|pathaddpath| is a no-op.
+\cr
+|FILE *pathopen(char *)|&
+Attempts to open (for read)
+the given name, first in the current directory, then,
+if the name is not absolute (i.e. on UNIX doesn't begin with |'/'|),
+tries to open the name in each directory on the search path.
+If some |fopen| succeeds, it returns a pointer to a stream, otherwise it
+returns |NULL|.
+\cr
+}}
+
+@ We expect to be able to import:
+\medskip
+\noindent{\tabskip=0ptplus1fil
+\halign to \hsize{\vrule height 10pt depth3.5pt width0pt
+ \hfil#\tabskip=2em&\vtop{\hsize=4in
+ \noindent\ignorespaces#\par}
+ \tabskip=0ptplus1fil\cr
+\omit\hfil\strut{\bf Function name and signature}&
+\omit\bf What it does\hfil\cr
+\noalign{\smallskip}
+|overflow(char *)|&
+The |pathopen| routines will call |overflow| if they run out of space,
+e.g.~|overflow("path texts")|.
+\cr
+}}
+
+@ Here we set up the search path.
+It is a list of directories; if |pathopen| is handed a relative file name,
+it will look for it first in the current directory, and then in each
+directory on the search path.
+We allow up to |maxpaths| directories to be on the path,
+and we set aside |pathtextlength| bytes for the names of those directories.
+We perform the initialization statically, so we don't {\em have} to call
+|pathreset|.
+
+We use Knuth's technique for storing the names: all the bytes are stored
+in |pathtexts|.
+Pointers to the directories are stored in |searchpath|; the name of
+the |i|th directory is found in $|*(searchpath[i])|\ldots|*(seachpath[i+1])|$,
+where we start counting |i| from zero.
+|nextpath| contains the number directories already stored, and
+|searchpath[nextpath]| points to the first free byte in |pathtexts|.
+This is all a little odd for those used to C's null-terminated strings, but
+it becomes second nature after a while.
+@d maxpaths = 64
+@d pathtextlength = 1024
+@u
+@<Included files@>@;
+
+static int nextpath=0;
+static char pathtexts[pathtextlength];
+static char *searchpath[maxpaths]={pathtexts};
+static char *maxpathtexts = pathtexts+pathtextlength;
+
+void pathreset()
+{
+ nextpath=0;
+ searchpath[nextpath]=pathtexts;
+}
+
+@ Here's how we add a name to the path
+@u
+void pathaddname(name)
+ char *name;
+{ char *t=searchpath[nextpath];
+ if (name==NULL) return;
+ if (nextpath>=maxpaths) overflow ("paths");
+ while (*name) {
+ if (t>=maxpathtexts) overflow("path texts");
+ *t++=*name++;
+ }
+ @<If path is null, decrement |nextpath| to cancel addition,
+ and if path is root, make it null@>@;
+ searchpath[++nextpath]=t;
+}
+
+@ @<If path is null...@>=
+if (t==searchpath[nextpath]) nextpath--;
+else if (t==searchpath[nextpath]+1 &&
+ *searchpath[nextpath]==directory_separator) /* path is root */
+ t--;
+
+@ Adding a path is slightly more complicated.
+@u
+void pathaddpath(path,path_separator)
+ char *path;
+ char path_separator;
+{
+ char *t=searchpath[nextpath];
+ if (path!=NULL) {
+ while (*path) {
+ if (nextpath>=maxpaths) overflow ("paths");
+ while(*path!=path_separator && *path!='\0') {
+ if (t>=maxpathtexts) overflow("path texts");
+ *t++=*path++;
+ }
+ @<If path is null...@>@;
+ searchpath[++nextpath]=t;
+ if (*path) path++; /* skip separator */
+ }
+ }
+}
+
+@ Now we define the function that attempts to open a file, searching
+on the path.
+We need to know whether a file name is absolute:
+@d directory_separator = '/' /* not converted to ASCII */
+@d absolute(n) = (*(n)==directory_separator)
+@d maxpathnamelength = 1024 /* longest path name we can create */
+@u
+FILE *pathopen(name)
+ char *name;
+{
+ FILE *fp; /* the stream we try to get */
+ char pathname[maxpathnamelength]; /* used to create pathnames */
+ char *s, *t; /* used to copy prefixes */
+ int i; /* used to search pathnames */
+
+ if (absolute(name))
+ return fopen(name,"r");
+ else {
+ if ((fp=fopen(name,"r"))!=NULL) return fp;
+ for (i=0; i<nextpath; i++) {
+ @<Try to open on path |i|, returning if not |NULL|@>@;
+ }
+ }
+ return NULL;
+}
+
+@ @<Included...@>=#include <stdio.h>
+
+@ @<Try to open on path |i|...@>=
+for(s=pathname,t=searchpath[i]; t<searchpath[i+1];) {
+ *s++ = *t++;
+ if (s>pathname+maxpathnamelength) overflow("path name length");
+}
+*s++=directory_separator;
+if (s>pathname+maxpathnamelength) overflow("path name length");
+t=name;
+while(*s++=*t++)
+ if (s>pathname+maxpathnamelength) overflow("path name length");
+if ((fp=fopen(pathname,"r"))!=NULL) return fp;
+
+
+@ As a final service, we write declarations on file {\tt pathopen.h}
+@(pathopen.h@>=
+void pathaddname();
+void pathaddpath();
+void pathreset();
+FILE *pathopen();
+@* Index.
diff --git a/web/spiderweb/src/master/pretty.awk b/web/spiderweb/src/master/pretty.awk
new file mode 100644
index 0000000000..39ec7a1a38
--- /dev/null
+++ b/web/spiderweb/src/master/pretty.awk
@@ -0,0 +1,2 @@
+/^\\Y/,/\\par$/ {print; next}
+{next}
diff --git a/web/spiderweb/src/master/spider.awk b/web/spiderweb/src/master/spider.awk
new file mode 100644
index 0000000000..d8d0d74a10
--- /dev/null
+++ b/web/spiderweb/src/master/spider.awk
@@ -0,0 +1,4799 @@
+##3:
+
+#line 53 "/u/norman/pu/web/dist/src/master/spider.web"
+
+BEGIN{
+##9:
+
+#line 181 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 182 "/u/norman/pu/web/dist/src/master/spider.web"
+logfile="spider.slog"
+
+##:9
+##10:
+
+#line 185 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 186 "/u/norman/pu/web/dist/src/master/spider.web"
+translationfile="trans_keys.unsorted"
+
+##:10
+##11:
+
+#line 190 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 191 "/u/norman/pu/web/dist/src/master/spider.web"
+scrapfile="scraps.web"
+print"@*Scrap code generated by {\\tt SPIDER}.">scrapfile
+ttokfile="outtoks.web"
+print"@*Token code generated by {\\tt SPIDER}.">ttokfile
+##:11
+##12:
+
+#line 197 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 198 "/u/norman/pu/web/dist/src/master/spider.web"
+reserved="scraps.web"
+
+##:12
+##13:
+
+#line 202 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 203 "/u/norman/pu/web/dist/src/master/spider.web"
+tokennamefile="names.unsorted"
+##:13
+##14:
+
+#line 206 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 207 "/u/norman/pu/web/dist/src/master/spider.web"
+ilkfile="names.unsorted"
+##:14
+##15:
+
+#line 210 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 211 "/u/norman/pu/web/dist/src/master/spider.web"
+categoryfile="names.unsorted"
+##:15
+##16:
+
+#line 213 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 214 "/u/norman/pu/web/dist/src/master/spider.web"
+grammarfile="grammar.web"
+print"@*Grammar code generated by {\\tt SPIDER}.">grammarfile
+##:16
+##17:
+
+#line 219 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 220 "/u/norman/pu/web/dist/src/master/spider.web"
+tlang="outtoks.web"
+wlang="scraps.web"
+
+##:17
+##18:
+
+#line 225 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 226 "/u/norman/pu/web/dist/src/master/spider.web"
+productions="productions.list"
+
+##:18
+##19:
+
+#line 229 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 230 "/u/norman/pu/web/dist/src/master/spider.web"
+cycles="cycle.test"
+
+
+
+##:19
+##37:
+
+#line 525 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 526 "/u/norman/pu/web/dist/src/master/spider.web"
+default_translation="<*>"
+default_mathness="maybe"
+
+
+##:37
+##46:
+
+#line 727 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 728 "/u/norman/pu/web/dist/src/master/spider.web"
+highesttoken=200
+tokennumber=highesttoken
+
+##:46
+##61:
+
+#line 943 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 944 "/u/norman/pu/web/dist/src/master/spider.web"
+tokennumbers["identifier"]=tokennumber;tokennumber--
+tokenname["identifier"]="identifier"
+
+##:61
+##63:
+
+#line 969 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 970 "/u/norman/pu/web/dist/src/master/spider.web"
+tokennumbers["constant"]=tokennumber;tokennumber--
+tokenname["constant"]="constant"
+tokennumbers["string"]=tokennumber;tokennumber--
+tokenname["string"]="string"
+
+
+##:63
+##138:
+
+#line 2246 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2247 "/u/norman/pu/web/dist/src/master/spider.web"
+at_sign="@@"
+
+
+##:138
+##146:
+
+#line 2397 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2398 "/u/norman/pu/web/dist/src/master/spider.web"
+texof["\\"]="\\BS"
+texof["{"]="\\{"
+texof["}"]="\\{"
+texof["$"]="\\$"
+texof["&"]="\\amp"
+texof["#"]="\\#"
+texof["^"]="\\H"
+texof["_"]="\\_"
+texof["~"]="\\TI"
+texof["%"]="\\%"
+
+##:146
+##150:
+
+#line 2475 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2476 "/u/norman/pu/web/dist/src/master/spider.web"
+sharp_line_open="\"#line\""
+sharp_line_close="\"\""
+
+##:150
+##152:
+
+#line 2495 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2496 "/u/norman/pu/web/dist/src/master/spider.web"
+month["Jan"]="January"
+month["Feb"]="February"
+month["Mar"]="March"
+month["Apr"]="April"
+month["May"]="May"
+month["Jun"]="June"
+month["Jul"]="July"
+month["Aug"]="August"
+month["Sep"]="September"
+month["Oct"]="October"
+month["Nov"]="November"
+month["Dec"]="December"
+
+##:152
+
+#line 55 "/u/norman/pu/web/dist/src/master/spider.web"
+
+exitcode=0
+}
+
+##6:
+
+#line 100 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 101 "/u/norman/pu/web/dist/src/master/spider.web"
+/^#|^ *$/{
+
+print $0>logfile
+next
+}
+
+##:6
+
+#line 59 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##35:
+
+#line 498 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 499 "/u/norman/pu/web/dist/src/master/spider.web"
+/^default /{
+print"Setting defaults...">logfile
+start_place=2
+##36:
+
+#line 518 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 519 "/u/norman/pu/web/dist/src/master/spider.web"
+this_translation=default_translation
+this_mathness=default_mathness
+this_name=""
+this_category=""
+this_tangleto=""
+##:36
+
+#line 503 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##29:
+
+#line 434 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 435 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i=start_place;i<NF;){
+if($i=="tangleto"){
+i++
+this_tangleto=$i
+i++
+}else if($i=="translation"){
+i++
+this_translation=$i
+i++
+}else if($i=="mathness"){
+i++
+this_mathness=$i
+i++
+}else if($i=="category"){
+i++
+this_category=$i
+categories[$i]=1
+i++
+}else if($i=="name"){
+i++
+this_name="SP_"$i
+i++
+}else{
+print"Error: unrecognized token description",$i,"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 459 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 462 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:29
+
+#line 506 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##30:
+
+#line 466 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 467 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_name!=""){
+print"Error: name doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 469 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:30
+
+#line 507 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##32:
+
+#line 476 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 477 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_category!=""){
+print"Error: category doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 479 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:32
+
+#line 508 "/u/norman/pu/web/dist/src/master/spider.web"
+
+default_translation=this_translation
+default_mathness=this_mathness
+print"\tdefault translation is",default_translation>logfile
+print"\tdefault mathness is",default_mathness>logfile
+next
+}
+
+##:35
+##39:
+
+#line 570 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 571 "/u/norman/pu/web/dist/src/master/spider.web"
+/^token /{
+print"Token",$2>logfile
+if($2=="identifier"){
+##40:
+
+#line 595 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 596 "/u/norman/pu/web/dist/src/master/spider.web"
+##36:
+
+#line 518 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 519 "/u/norman/pu/web/dist/src/master/spider.web"
+this_translation=default_translation
+this_mathness=default_mathness
+this_name=""
+this_category=""
+this_tangleto=""
+##:36
+
+#line 597 "/u/norman/pu/web/dist/src/master/spider.web"
+
+this_translation=""
+start_place=3
+##29:
+
+#line 434 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 435 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i=start_place;i<NF;){
+if($i=="tangleto"){
+i++
+this_tangleto=$i
+i++
+}else if($i=="translation"){
+i++
+this_translation=$i
+i++
+}else if($i=="mathness"){
+i++
+this_mathness=$i
+i++
+}else if($i=="category"){
+i++
+this_category=$i
+categories[$i]=1
+i++
+}else if($i=="name"){
+i++
+this_name="SP_"$i
+i++
+}else{
+print"Error: unrecognized token description",$i,"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 459 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 462 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:29
+
+#line 602 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##30:
+
+#line 466 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 467 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_name!=""){
+print"Error: name doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 469 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:30
+
+#line 603 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##31:
+
+#line 471 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 472 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_tangleto!=""){
+print"Error: tangleto doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 474 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:31
+
+#line 604 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##34:
+
+#line 486 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 487 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_category==""){
+print"Error: you must give a category on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 489 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:34
+
+#line 605 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##33:
+
+#line 481 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 482 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_translation!=""){
+print"Error: translation doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 484 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:33
+
+#line 606 "/u/norman/pu/web/dist/src/master/spider.web"
+
+id_category=this_category
+id_mathness=this_mathness
+
+##:40
+
+#line 574 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else if($2=="number"){
+##41:
+
+#line 611 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 612 "/u/norman/pu/web/dist/src/master/spider.web"
+print"Warning: numeric constants and strings are",\
+"identified in this WEAVE."
+print"Warning: numeric constants and strings are",\
+"identified in this WEAVE.">logfile
+##36:
+
+#line 518 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 519 "/u/norman/pu/web/dist/src/master/spider.web"
+this_translation=default_translation
+this_mathness=default_mathness
+this_name=""
+this_category=""
+this_tangleto=""
+##:36
+
+#line 617 "/u/norman/pu/web/dist/src/master/spider.web"
+
+this_translation=""
+start_place=3
+##29:
+
+#line 434 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 435 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i=start_place;i<NF;){
+if($i=="tangleto"){
+i++
+this_tangleto=$i
+i++
+}else if($i=="translation"){
+i++
+this_translation=$i
+i++
+}else if($i=="mathness"){
+i++
+this_mathness=$i
+i++
+}else if($i=="category"){
+i++
+this_category=$i
+categories[$i]=1
+i++
+}else if($i=="name"){
+i++
+this_name="SP_"$i
+i++
+}else{
+print"Error: unrecognized token description",$i,"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 459 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 462 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:29
+
+#line 622 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##30:
+
+#line 466 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 467 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_name!=""){
+print"Error: name doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 469 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:30
+
+#line 623 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##31:
+
+#line 471 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 472 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_tangleto!=""){
+print"Error: tangleto doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 474 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:31
+
+#line 624 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##34:
+
+#line 486 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 487 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_category==""){
+print"Error: you must give a category on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 489 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:34
+
+#line 625 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##33:
+
+#line 481 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 482 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_translation!=""){
+print"Error: translation doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 484 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:33
+
+#line 626 "/u/norman/pu/web/dist/src/master/spider.web"
+
+number_category=this_category
+number_mathness=this_mathness
+
+##:41
+
+#line 576 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else if($2=="newline"){
+##42:
+
+#line 631 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 632 "/u/norman/pu/web/dist/src/master/spider.web"
+##36:
+
+#line 518 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 519 "/u/norman/pu/web/dist/src/master/spider.web"
+this_translation=default_translation
+this_mathness=default_mathness
+this_name=""
+this_category=""
+this_tangleto=""
+##:36
+
+#line 633 "/u/norman/pu/web/dist/src/master/spider.web"
+
+start_place=3
+##29:
+
+#line 434 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 435 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i=start_place;i<NF;){
+if($i=="tangleto"){
+i++
+this_tangleto=$i
+i++
+}else if($i=="translation"){
+i++
+this_translation=$i
+i++
+}else if($i=="mathness"){
+i++
+this_mathness=$i
+i++
+}else if($i=="category"){
+i++
+this_category=$i
+categories[$i]=1
+i++
+}else if($i=="name"){
+i++
+this_name="SP_"$i
+i++
+}else{
+print"Error: unrecognized token description",$i,"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 459 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 462 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:29
+
+#line 637 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##30:
+
+#line 466 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 467 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_name!=""){
+print"Error: name doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 469 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:30
+
+#line 638 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##31:
+
+#line 471 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 472 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_tangleto!=""){
+print"Error: tangleto doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 474 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:31
+
+#line 639 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##34:
+
+#line 486 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 487 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_category==""){
+print"Error: you must give a category on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 489 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:34
+
+#line 640 "/u/norman/pu/web/dist/src/master/spider.web"
+
+newline_category=this_category
+newline_mathness=this_mathness
+newline_translation=this_translation
+
+##:42
+
+#line 578 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else if($2=="pseudo_semi"){
+##43:
+
+#line 646 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 647 "/u/norman/pu/web/dist/src/master/spider.web"
+##36:
+
+#line 518 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 519 "/u/norman/pu/web/dist/src/master/spider.web"
+this_translation=default_translation
+this_mathness=default_mathness
+this_name=""
+this_category=""
+this_tangleto=""
+##:36
+
+#line 648 "/u/norman/pu/web/dist/src/master/spider.web"
+
+start_place=3
+##29:
+
+#line 434 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 435 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i=start_place;i<NF;){
+if($i=="tangleto"){
+i++
+this_tangleto=$i
+i++
+}else if($i=="translation"){
+i++
+this_translation=$i
+i++
+}else if($i=="mathness"){
+i++
+this_mathness=$i
+i++
+}else if($i=="category"){
+i++
+this_category=$i
+categories[$i]=1
+i++
+}else if($i=="name"){
+i++
+this_name="SP_"$i
+i++
+}else{
+print"Error: unrecognized token description",$i,"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 459 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 462 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:29
+
+#line 652 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##30:
+
+#line 466 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 467 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_name!=""){
+print"Error: name doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 469 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:30
+
+#line 653 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##31:
+
+#line 471 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 472 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_tangleto!=""){
+print"Error: tangleto doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 474 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:31
+
+#line 654 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##34:
+
+#line 486 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 487 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_category==""){
+print"Error: you must give a category on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 489 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:34
+
+#line 655 "/u/norman/pu/web/dist/src/master/spider.web"
+
+pseudo_semi_category=this_category
+pseudo_semi_mathness=this_mathness
+pseudo_semi_translation=this_translation
+
+##:43
+
+#line 580 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else if($2~/[a-zA-ZA-Z0-9]+/){
+
+print"Error: unknown token species:",$2
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 584 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else{
+##44:
+
+#line 677 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 678 "/u/norman/pu/web/dist/src/master/spider.web"
+this_string=$2
+##45:
+
+#line 700 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 701 "/u/norman/pu/web/dist/src/master/spider.web"
+old_string=this_string
+this_string=""
+
+
+idx=index(old_string,"{space}")
+while(idx!=0){
+temp=substr(old_string,1,idx-1)
+this_string=this_string temp" "
+old_string=substr(old_string,idx+7)
+idx=index(old_string,"{space}")
+}
+this_string=this_string old_string
+
+##:45
+
+#line 679 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+$2=this_string
+
+##49:
+
+#line 767 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 768 "/u/norman/pu/web/dist/src/master/spider.web"
+if($2=="@"){
+$2="@@"
+tokenname[$2]="'@@'"
+tokenlength[$2]=1
+}else if($2=="'"||$2=="\\"){
+$2="\\"$2
+tokenname[$2]="'"$2"'"
+tokenlength[$2]=1
+}else if(length($2)>1){
+##50:
+
+#line 793 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 794 "/u/norman/pu/web/dist/src/master/spider.web"
+tokenname[$2]="SP_gen_token_"tokennumber
+tokennumbers[$2]=tokennumber
+tokennumber--
+
+temp=sprintf("strncmp(\"%s\",loc-1,%d)==0",$2,length($2))
+tokentest[$2]=temp
+tokenlength[$2]=length($2)
+
+
+##:50
+
+#line 777 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else{
+temp=sprintf("'%s'",$2)
+tokenname[$2]=temp
+tokenlength[$2]=1
+}
+
+##:49
+
+#line 684 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(tokens[$2]!=""){
+print"Warning: token",$2,"defined twice"
+}
+tokens[$2]=1
+##51:
+
+#line 804 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 805 "/u/norman/pu/web/dist/src/master/spider.web"
+##36:
+
+#line 518 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 519 "/u/norman/pu/web/dist/src/master/spider.web"
+this_translation=default_translation
+this_mathness=default_mathness
+this_name=""
+this_category=""
+this_tangleto=""
+##:36
+
+#line 806 "/u/norman/pu/web/dist/src/master/spider.web"
+
+this_name=tokenname[$2]
+start_place=3
+##29:
+
+#line 434 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 435 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i=start_place;i<NF;){
+if($i=="tangleto"){
+i++
+this_tangleto=$i
+i++
+}else if($i=="translation"){
+i++
+this_translation=$i
+i++
+}else if($i=="mathness"){
+i++
+this_mathness=$i
+i++
+}else if($i=="category"){
+i++
+this_category=$i
+categories[$i]=1
+i++
+}else if($i=="name"){
+i++
+this_name="SP_"$i
+i++
+}else{
+print"Error: unrecognized token description",$i,"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 459 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 462 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:29
+
+#line 811 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##34:
+
+#line 486 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 487 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_category==""){
+print"Error: you must give a category on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 489 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:34
+
+#line 812 "/u/norman/pu/web/dist/src/master/spider.web"
+
+tokencategory[$2]=this_category
+tokenmathness[$2]=this_mathness
+tokentranslation[$2]=this_translation
+tokenname[$2]=this_name
+tokentangleto[$2]=this_tangleto
+
+##:51
+
+#line 689 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##53:
+
+#line 828 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 829 "/u/norman/pu/web/dist/src/master/spider.web"
+if(tokenlength[$2]>1&&tokennumbers[$2]==""){
+print"This can't happen: token",$2,"is long",\
+"but has no number"
+exitcode=-1
+}
+
+
+##:53
+
+#line 690 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##52:
+
+#line 821 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 822 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=tokenlength[$2]
+if(temp>maxtokenlength){
+maxtokenlength=temp
+}
+
+##:52
+
+#line 691 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+
+##:44
+
+#line 586 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+categories[this_category]=1
+
+next
+}
+
+##:39
+##75:
+
+#line 1183 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1184 "/u/norman/pu/web/dist/src/master/spider.web"
+/^ilk /{
+print"Ilk",$2>logfile
+##36:
+
+#line 518 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 519 "/u/norman/pu/web/dist/src/master/spider.web"
+this_translation=default_translation
+this_mathness=default_mathness
+this_name=""
+this_category=""
+this_tangleto=""
+##:36
+
+#line 1187 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##76:
+
+#line 1206 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1207 "/u/norman/pu/web/dist/src/master/spider.web"
+if($2~/^[a-zA-Z_]+_like$/&&$0!~/ category /){
+
+this_category=substr($2,1,length($2)-5)
+categories[this_category]=1
+}
+
+##:76
+
+#line 1188 "/u/norman/pu/web/dist/src/master/spider.web"
+
+this_name=""
+start_place=3
+##29:
+
+#line 434 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 435 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i=start_place;i<NF;){
+if($i=="tangleto"){
+i++
+this_tangleto=$i
+i++
+}else if($i=="translation"){
+i++
+this_translation=$i
+i++
+}else if($i=="mathness"){
+i++
+this_mathness=$i
+i++
+}else if($i=="category"){
+i++
+this_category=$i
+categories[$i]=1
+i++
+}else if($i=="name"){
+i++
+this_name="SP_"$i
+i++
+}else{
+print"Error: unrecognized token description",$i,"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 459 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 462 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:29
+
+#line 1193 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##34:
+
+#line 486 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 487 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_category==""){
+print"Error: you must give a category on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 489 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:34
+
+#line 1194 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##30:
+
+#line 466 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 467 "/u/norman/pu/web/dist/src/master/spider.web"
+if(this_name!=""){
+print"Error: name doesn't apply on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 469 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+##:30
+
+#line 1195 "/u/norman/pu/web/dist/src/master/spider.web"
+
+ilk_category[$2]=this_category
+ilk_mathness[$2]=this_mathness
+ilk_translation[$2]=this_translation
+next
+}
+
+
+##:75
+##77:
+
+#line 1220 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1221 "/u/norman/pu/web/dist/src/master/spider.web"
+/^reserved /{
+print"Reserved word",$2>logfile
+if($0!~/ ilk /){
+##78:
+
+#line 1246 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1247 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=$2"_like"
+reservedilk[$2]=temp
+if(ilk_translation[temp]==""){
+ilk_translation[temp]=default_translation
+}
+has_reserved[temp]=1
+if(ilk_mathness[temp]==""){
+ilk_mathness[temp]=default_mathness
+}
+
+if(ilk_category[temp]==""){
+ilk_category[temp]=$2
+categories[$2]=1
+}
+ilk_is_made_up[temp]=1
+
+
+
+##:78
+
+#line 1224 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+for(i=3;i<=NF;){
+if($i=="ilk"){
+i++
+reservedilk[$2]=$i
+has_reserved[$i]=1
+i++
+}else{
+print"Error: bad reserved word attribute:",$i,\
+"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1235 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 1238 "/u/norman/pu/web/dist/src/master/spider.web"
+
+next
+}
+
+##:77
+##129:
+
+#line 2071 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2072 "/u/norman/pu/web/dist/src/master/spider.web"
+/^language /{
+language=$2
+extension=language
+for(i=3;i<NF;){
+if($i=="extension"){
+i++
+extension=$i
+i++
+}else if($i=="version"){
+i++
+version=$i
+i++
+}else{
+print"Error: unknown language property",$i,\
+"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2087 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 2090 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##131:
+
+#line 2125 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2126 "/u/norman/pu/web/dist/src/master/spider.web"
+macrofile=extension"web.tex"
+print"\\input webkernel.tex">macrofile
+
+
+##:131
+
+#line 2091 "/u/norman/pu/web/dist/src/master/spider.web"
+
+next
+}
+
+##:129
+##132:
+
+#line 2132 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2133 "/u/norman/pu/web/dist/src/master/spider.web"
+/^macros begin$/,/^macros end$/{
+if(begunmacs==0){
+begunmacs=1
+next
+}
+if($0~/^macros end$/){
+begunmacs=0
+next
+}
+if(macrofile==""){
+if(complained==0){
+print"Error: you must give \"language\"",\
+"before \"macros\""
+complained=1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2147 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}else{
+print $0>macrofile
+}
+next
+}
+
+
+
+##:132
+##133:
+
+#line 2163 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2164 "/u/norman/pu/web/dist/src/master/spider.web"
+/^module /{
+for(i=2;i<NF;){
+if($i=="definition"){
+i++
+mod_def_cat=$i
+categories[$i]=1
+print"Module definition category set to",$i>logfile
+i++
+}else if($i=="use"){
+i++
+mod_use_cat=$i
+categories[$i]=1
+print"Module use category set to",$i>logfile
+i++
+}else{
+print"Error: unknown module property",$i,\
+"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2181 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 2184 "/u/norman/pu/web/dist/src/master/spider.web"
+
+next
+}
+
+##:133
+##135:
+
+#line 2213 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2214 "/u/norman/pu/web/dist/src/master/spider.web"
+/^at_sign /{
+if(NF==2&&length($2)==1){
+if($2=="@"){
+at_sign="@@"
+}else{
+at_sign=$2
+}
+}else{
+print"Error: I can't understand",$0
+print" Give me an at sign of length 1"
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2224 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+next
+}
+
+##:135
+##139:
+
+#line 2254 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2255 "/u/norman/pu/web/dist/src/master/spider.web"
+/^comment /{
+print $0>logfile
+for(i=2;i<NF;){
+if($i=="begin"){
+i++
+if($i~/^<.*>$/){
+transstring=$i
+wherestring="in \"comment begin\" on line "NR-1
+##27:
+
+#line 389 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 390 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 395 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+print"Digit not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^[a-zA-Z_]+$/){
+print"Key word not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=substr(alternate,2,length(alternate)-2)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+print"<*> not allowed in restricted translation",wherestring
+exitcode=-1
+}else{
+print"Bogus restricted translation",wherestring
+exitcode=-1
+}
+}
+outstring="\""outstring"\""
+
+
+
+##:27
+
+#line 2264 "/u/norman/pu/web/dist/src/master/spider.web"
+
+begin_comment_string=outstring
+i++
+}else{
+print"Error: \"comment begin\" must have a restricted translation"
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2269 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}else if($i=="end"){
+i++
+if($i=="newline"){
+comments_end_with_newline=1
+end_comment_string="\"\\n\""
+}else if($i~/^<.*>$/){
+comments_end_with_newline=0
+transstring=$i
+wherestring="in \"comment end\" on line "NR-1
+##27:
+
+#line 389 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 390 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 395 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+print"Digit not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^[a-zA-Z_]+$/){
+print"Key word not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=substr(alternate,2,length(alternate)-2)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+print"<*> not allowed in restricted translation",wherestring
+exitcode=-1
+}else{
+print"Bogus restricted translation",wherestring
+exitcode=-1
+}
+}
+outstring="\""outstring"\""
+
+
+
+##:27
+
+#line 2282 "/u/norman/pu/web/dist/src/master/spider.web"
+
+end_comment_string=outstring
+}else{
+print"Error: \"comment end\" must have a restricted translation"
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2286 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+i++
+}else{
+print"Error: bad comment attribute:",$i
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2291 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 2294 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##145:
+
+#line 2376 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2377 "/u/norman/pu/web/dist/src/master/spider.web"
+if(macrofile!=""){
+this_string=substr(begin_comment_string,2,length(begin_comment_string)-2)
+##147:
+
+#line 2410 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2411 "/u/norman/pu/web/dist/src/master/spider.web"
+tex_string=""
+while(length(this_string)>0){
+c=substr(this_string,1,1)
+this_string=substr(this_string,2)
+cprime=texof[c]
+if(cprime==""){
+tex_string=tex_string c
+}else{
+tex_string=tex_string cprime
+}
+}
+
+##:147
+
+#line 2379 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"\\def\\commentbegin{%s}\n",tex_string>macrofile
+if(comments_end_with_newline==0){
+this_string=substr(end_comment_string,2,length(end_comment_string)-2)
+##147:
+
+#line 2410 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2411 "/u/norman/pu/web/dist/src/master/spider.web"
+tex_string=""
+while(length(this_string)>0){
+c=substr(this_string,1,1)
+this_string=substr(this_string,2)
+cprime=texof[c]
+if(cprime==""){
+tex_string=tex_string c
+}else{
+tex_string=tex_string cprime
+}
+}
+
+##:147
+
+#line 2383 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"\\def\\commentend{%s}\n",tex_string>macrofile
+}else{
+print"\\def\\commentend{\\relax}">macrofile
+}
+}else{
+print"Error: I can't write comment info to the macro file---"
+print" you haven't given me any \"language\" information"
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2391 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+
+##:145
+
+#line 2295 "/u/norman/pu/web/dist/src/master/spider.web"
+
+next
+}
+
+##:139
+##148:
+
+#line 2428 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2429 "/u/norman/pu/web/dist/src/master/spider.web"
+/^line /{
+print $0>logfile
+for(i=2;i<NF;){
+if($i=="begin"){
+i++
+if($i~/^<.*>$/){
+transstring=$i
+wherestring="in \"line begin\" on line "NR-1
+##27:
+
+#line 389 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 390 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 395 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+print"Digit not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^[a-zA-Z_]+$/){
+print"Key word not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=substr(alternate,2,length(alternate)-2)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+print"<*> not allowed in restricted translation",wherestring
+exitcode=-1
+}else{
+print"Bogus restricted translation",wherestring
+exitcode=-1
+}
+}
+outstring="\""outstring"\""
+
+
+
+##:27
+
+#line 2438 "/u/norman/pu/web/dist/src/master/spider.web"
+
+sharp_line_open=outstring
+i++
+}else{
+print"Error: \"line begin\" must have a restricted translation"
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2443 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}else if($i=="end"){
+i++
+if($i~/^<.*>$/){
+transstring=$i
+wherestring="in \"line end\" on line "NR-1
+##27:
+
+#line 389 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 390 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 395 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+print"Digit not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^[a-zA-Z_]+$/){
+print"Key word not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=substr(alternate,2,length(alternate)-2)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+print"<*> not allowed in restricted translation",wherestring
+exitcode=-1
+}else{
+print"Bogus restricted translation",wherestring
+exitcode=-1
+}
+}
+outstring="\""outstring"\""
+
+
+
+##:27
+
+#line 2452 "/u/norman/pu/web/dist/src/master/spider.web"
+
+sharp_line_close=outstring
+}else{
+print"Error: \"line end\" must have a restricted translation"
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2456 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+i++
+}else{
+print"Error: bad line attribute:",$i,"on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 2461 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+##5:
+
+#line 91 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 92 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: leftover fields",$i,"... on line",NR-1
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 94 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+
+##:5
+
+#line 2464 "/u/norman/pu/web/dist/src/master/spider.web"
+
+next
+}
+
+##:148
+##151:
+
+#line 2481 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2482 "/u/norman/pu/web/dist/src/master/spider.web"
+/^date /{
+
+mo=month[$3]
+day=$4
+year=$7
+time=$5
+##153:
+
+#line 2510 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2511 "/u/norman/pu/web/dist/src/master/spider.web"
+hour=substr(time,1,2)
+if(hour>=12)
+ampm="PM"
+else
+ampm="AM"
+
+if(hour==0){
+hour=12
+}else if(hour>12){
+hour=hour-12
+}
+minute=substr(time,4,2)
+
+
+##:153
+
+#line 2488 "/u/norman/pu/web/dist/src/master/spider.web"
+
+date=sprintf("(generated at %d:%s %s on %s %d, %d)",\
+hour,minute,ampm,mo,day,year)
+next
+}
+
+##:151
+
+#line 60 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##88:
+
+#line 1442 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1443 "/u/norman/pu/web/dist/src/master/spider.web"
+/-->/{
+##89:
+
+#line 1471 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1472 "/u/norman/pu/web/dist/src/master/spider.web"
+lowpos=0;highpos=0;pos=1
+prodnum=prodnum+1
+inputline[prodnum]=$0
+print"Parsing production",prodnum,$0>logfile
+
+
+##:89
+
+#line 1444 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##90:
+
+#line 1492 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1493 "/u/norman/pu/web/dist/src/master/spider.web"
+trans[pos]=""
+for(i=1;i<=NF;i++){
+if($i~/<.*>/){
+##92:
+
+#line 1525 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1526 "/u/norman/pu/web/dist/src/master/spider.web"
+transstring=$i
+selfstring=""
+wherestring=sprintf("in production on line %d",NR-1)
+append_keyword="app"
+##24:
+
+#line 336 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 342 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+temp=sprintf("\tapp_str(\"%s\");\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^[a-zA-Z_]+$/){
+translation_keywords[alternate]=1
+temp=sprintf("\t%s(%s);\n",append_keyword,alternate)
+
+outstring=outstring temp
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=sprintf("\tapp_str(%s);\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+##26:
+
+#line 377 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 378 "/u/norman/pu/web/dist/src/master/spider.web"
+if(selfstring==""){
+print"Translation \"<*>\" makes no sense",wherestring
+exitcode=-1
+}
+
+##:26
+
+#line 355 "/u/norman/pu/web/dist/src/master/spider.web"
+
+outstring=outstring selfstring
+}else{
+print"Bogus translation",wherestring
+exitcode=-1
+}
+}
+
+##:24
+
+#line 1531 "/u/norman/pu/web/dist/src/master/spider.web"
+
+trans[pos]=trans[pos]outstring
+
+##:92
+
+#line 1496 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else if($i~/^!?[a-zA-Z_]+(\*\*?)?$/){
+##93:
+
+#line 1543 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1544 "/u/norman/pu/web/dist/src/master/spider.web"
+field[pos]=$i
+##96:
+
+#line 1606 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1607 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=substr($i,1,1)
+if(temp=="!"){
+negation=1
+$i=substr($i,2)
+}else{
+negation=0
+}
+
+##:96
+
+#line 1545 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##98:
+
+#line 1638 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1639 "/u/norman/pu/web/dist/src/master/spider.web"
+if($i~/^([a-zA-Z_]+|\(([a-zA-Z_]+\|)*[a-zA-Z_]+\))\*\*$/){
+temp=sprintf("\tmake_underlined(pp+%d);\n",pos-1)
+trans[pos]=trans[pos]temp
+$i=substr($i,1,length($i)-2)
+}else if($i~/^([a-zA-Z_]+|\(([a-zA-Z_]+\|)*[a-zA-Z_]+\))\*$/){
+temp=sprintf("\tmake_underlined(pp+%d);\n",pos-1)
+trans[pos]=trans[pos]temp
+$i=substr($i,1,length($i)-1)
+}else if($i~/\*$/){
+print"Error: can't remove stars in production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1649 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:98
+
+#line 1547 "/u/norman/pu/web/dist/src/master/spider.web"
+
+cat=$i
+categories[cat]=1
+if(negation==0){
+test[pos]=sprintf("(pp+%d)->cat==SP_%s",pos-1,cat)
+}else{
+test[pos]=sprintf("((pp+%d)->cat!=SP_%s && (pp+%d)->cat != 0)",\
+pos-1,cat,pos-1)
+}
+##95:
+
+#line 1597 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1598 "/u/norman/pu/web/dist/src/master/spider.web"
+if(pos>highestpos[cat]){
+highestpos[cat]=pos
+}
+
+##:95
+
+#line 1556 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##97:
+
+#line 1619 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1620 "/u/norman/pu/web/dist/src/master/spider.web"
+pos=pos+1
+trans[pos]=""
+
+##:97
+
+#line 1557 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:93
+
+#line 1498 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else if($i~/^!?\(([a-zA-Z_]+\|)*[a-zA-Z_]+\)(\*\*?)?$/){
+##94:
+
+#line 1567 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1568 "/u/norman/pu/web/dist/src/master/spider.web"
+field[pos]=$i
+##96:
+
+#line 1606 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1607 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=substr($i,1,1)
+if(temp=="!"){
+negation=1
+$i=substr($i,2)
+}else{
+negation=0
+}
+
+##:96
+
+#line 1569 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(negation==0){
+test[pos]="("
+}else{
+temp=sprintf("(pp+%d)->cat==0",pos-1)
+test[pos]="!("temp"||"
+}
+##98:
+
+#line 1638 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1639 "/u/norman/pu/web/dist/src/master/spider.web"
+if($i~/^([a-zA-Z_]+|\(([a-zA-Z_]+\|)*[a-zA-Z_]+\))\*\*$/){
+temp=sprintf("\tmake_underlined(pp+%d);\n",pos-1)
+trans[pos]=trans[pos]temp
+$i=substr($i,1,length($i)-2)
+}else if($i~/^([a-zA-Z_]+|\(([a-zA-Z_]+\|)*[a-zA-Z_]+\))\*$/){
+temp=sprintf("\tmake_underlined(pp+%d);\n",pos-1)
+trans[pos]=trans[pos]temp
+$i=substr($i,1,length($i)-1)
+}else if($i~/\*$/){
+print"Error: can't remove stars in production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1649 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:98
+
+#line 1577 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=substr($i,2,length($i)-2)
+m=split(temp,tok,"|")
+for(j=1;j<=m;j++){
+cat=tok[j]
+categories[cat]=1
+##95:
+
+#line 1597 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1598 "/u/norman/pu/web/dist/src/master/spider.web"
+if(pos>highestpos[cat]){
+highestpos[cat]=pos
+}
+
+##:95
+
+#line 1584 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=sprintf("(pp+%d)->cat==SP_%s",pos-1,cat)
+test[pos]=test[pos]temp
+if(j!=m)
+test[pos]=test[pos]"||\n"
+}
+test[pos]=test[pos]")"
+##97:
+
+#line 1619 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1620 "/u/norman/pu/web/dist/src/master/spider.web"
+pos=pos+1
+trans[pos]=""
+
+##:97
+
+#line 1591 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+
+##:94
+
+#line 1500 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else if($i=="?"){
+##99:
+
+#line 1654 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1655 "/u/norman/pu/web/dist/src/master/spider.web"
+field[pos]=$i
+test[pos]=sprintf("(pp+%d)->cat!=0",pos-1)
+highwildcard=pos
+##97:
+
+#line 1619 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1620 "/u/norman/pu/web/dist/src/master/spider.web"
+pos=pos+1
+trans[pos]=""
+
+##:97
+
+#line 1658 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+
+
+##:99
+
+#line 1502 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}else if($i=="["){
+lowpos=pos
+}else if($i=="]"){
+highpos=pos
+}else if($i=="-->"){
+break
+}else{
+print"Error: bad field is",$i,"in production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1511 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+i++
+
+
+##:90
+
+#line 1445 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##100:
+
+#line 1679 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1680 "/u/norman/pu/web/dist/src/master/spider.web"
+arrowpos=pos
+if(lowpos==0&&highpos==0){
+lowpos=1
+highpos=arrowpos
+
+}else if(lowpos==0||highpos==0){
+print"Error: square brackets don't balance in",\
+"production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1688 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:100
+
+#line 1446 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##101:
+
+#line 1693 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1694 "/u/norman/pu/web/dist/src/master/spider.web"
+if(arrowpos-1>highestposoverall){
+highestposoverall=arrowpos-1
+}
+
+##:101
+
+#line 1447 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##102:
+
+#line 1708 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1709 "/u/norman/pu/web/dist/src/master/spider.web"
+for(hup=arrowpos-1;field[hup]=="?";){
+hup--
+}
+for(;hup>highestunknownpos;hup--){
+temp=field[hup]
+temp=substr(temp,1,1)
+if(temp=="?"||temp=="!"){
+highestunknownpos=hup
+break
+}
+}
+
+##:102
+
+#line 1448 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##103:
+
+#line 1722 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1723 "/u/norman/pu/web/dist/src/master/spider.web"
+for(pos=1;pos<lowpos;pos++){
+##105:
+
+#line 1733 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1734 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i>NF||$i!=field[pos]){
+print"Error: token mismatch is: found",$i,\
+"sought",field[pos],"on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1737 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:105
+
+#line 1724 "/u/norman/pu/web/dist/src/master/spider.web"
+
+i++
+}
+
+##:103
+
+#line 1449 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##106:
+
+#line 1746 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1747 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(i>NF){
+print"Error: no target category in production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1750 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+if($i~/#[0-9]+/){
+$i=substr($i,2)
+##117:
+
+#line 1867 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1868 "/u/norman/pu/web/dist/src/master/spider.web"
+if((0+$i)<1||(0+$i)>=0+arrowpos){
+print"Error: can't take token number",$i,"of",arrowpos-1,\
+"tokens","in production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1871 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:117
+
+#line 1754 "/u/norman/pu/web/dist/src/master/spider.web"
+
+targetcategory[prodnum]="Unnamed category"
+temp=sprintf("(pp+%d)->cat",$i-1)
+unnamed_cat[prodnum]=temp
+}else if($i~/[a-zA-Z][a-zA-Z_]*/){
+targetcategory[prodnum]=$i
+categories[$i]=1
+}else{
+print"Error: unrecognizable target token",$i,\
+"in production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1764 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+i++
+
+##:106
+
+#line 1450 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##104:
+
+#line 1728 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1729 "/u/norman/pu/web/dist/src/master/spider.web"
+for(pos=highpos;pos<arrowpos;pos++){
+##105:
+
+#line 1733 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1734 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i>NF||$i!=field[pos]){
+print"Error: token mismatch is: found",$i,\
+"sought",field[pos],"on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1737 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:105
+
+#line 1730 "/u/norman/pu/web/dist/src/master/spider.web"
+
+i++
+}
+##:104
+
+#line 1451 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##107:
+
+#line 1769 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1770 "/u/norman/pu/web/dist/src/master/spider.web"
+if(i<=NF){
+print"Error: used only "i-1" of "NF" tokens",\
+"in production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1773 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+
+##:107
+
+#line 1452 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##108:
+
+#line 1779 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1780 "/u/norman/pu/web/dist/src/master/spider.web"
+prodtest[prodnum]=""
+for(pos=1;pos<arrowpos;pos++){
+if(pos>1){
+prodtest[prodnum]=prodtest[prodnum]" &&\n\t\t"
+}
+prodtest[prodnum]=prodtest[prodnum]test[pos]
+}
+
+##:108
+
+#line 1454 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##109:
+
+#line 1789 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1790 "/u/norman/pu/web/dist/src/master/spider.web"
+prodtrans[prodnum]=""
+for(pos=lowpos;pos<highpos;pos++){
+prodtrans[prodnum]=prodtrans[prodnum]trans[pos]
+
+temp=sprintf("\tapp1(pp+%d);\n",pos-1)
+prodtrans[prodnum]=prodtrans[prodnum]temp
+##114:
+
+#line 1830 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1831 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=field[pos]
+tempa=substr(temp,1,1)
+if(tempa!="!"){
+if(temp~/^\(([a-zA-Z_]+\|)*[a-zA-Z_]+\)(\*\*?)?$/){
+
+##115:
+
+#line 1853 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1854 "/u/norman/pu/web/dist/src/master/spider.web"
+while(temp~/\*$/){
+temp=substr(temp,1,length(temp)-1)
+}
+
+##:115
+
+#line 1836 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=substr(temp,2,length(temp)-2)
+m=split(temp,tok,"|")
+for(j=1;j<=m;j++){
+alternate=tok[j]
+reduced[alternate]=1
+}
+}else if(temp~/^[a-zA-Z_]+(\*\*?)?$/){
+##115:
+
+#line 1853 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1854 "/u/norman/pu/web/dist/src/master/spider.web"
+while(temp~/\*$/){
+temp=substr(temp,1,length(temp)-1)
+}
+
+##:115
+
+#line 1844 "/u/norman/pu/web/dist/src/master/spider.web"
+
+reduced[temp]=1
+}else if(temp!="?"){
+print"Confusion: unintelligible field[pos]:",temp,\
+"in production on line",NR-1
+##91:
+
+#line 1519 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1520 "/u/norman/pu/web/dist/src/master/spider.web"
+prodnum--
+##4:
+
+#line 79 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 80 "/u/norman/pu/web/dist/src/master/spider.web"
+exitcode=-1
+next
+
+
+##:4
+
+#line 1521 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:91
+
+#line 1849 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+}
+
+##:114
+
+#line 1798 "/u/norman/pu/web/dist/src/master/spider.web"
+
+}
+prodtrans[prodnum]=prodtrans[prodnum]trans[highpos]
+
+##:109
+
+#line 1456 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##110:
+
+#line 1803 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1804 "/u/norman/pu/web/dist/src/master/spider.web"
+ppstart[prodnum]=lowpos-1
+tokensreduced[prodnum]=highpos-lowpos
+
+##:110
+
+#line 1458 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##111:
+
+#line 1808 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1809 "/u/norman/pu/web/dist/src/master/spider.web"
+if(highpos-lowpos==1){
+printf"%d: %s --> %s\n",prodnum,field[lowpos],\
+targetcategory[prodnum]>cycles
+wrotecycles=1
+}
+
+##:111
+
+#line 1460 "/u/norman/pu/web/dist/src/master/spider.web"
+
+next
+}
+
+##:88
+
+#line 61 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##7:
+
+#line 108 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 109 "/u/norman/pu/web/dist/src/master/spider.web"
+{
+print"Warning: I don't know what to do with this line:"
+print" ",$0
+print"Warning: I don't know what to do with this line:">logfile
+print" ",$0>logfile
+}
+
+
+##:7
+
+#line 62 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+END{
+##54:
+
+#line 847 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 848 "/u/norman/pu/web/dist/src/master/spider.web"
+tempfile=scrapfile
+##55:
+
+#line 854 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 855 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Here we input tokens of more than one character">tempfile
+print"@<Compress two-symbol operator@>=">tempfile
+##56:
+
+#line 863 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 864 "/u/norman/pu/web/dist/src/master/spider.web"
+for(len=maxtokenlength;len>=2;len--){
+printf"if (loc+%d<=limit) {\n",len-1>tempfile
+##57:
+
+#line 871 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 872 "/u/norman/pu/web/dist/src/master/spider.web"
+notfirst=0
+for(t in tokentest){
+if(tokenlength[t]==len){
+printf"\t">tempfile
+if(notfirst==1){
+printf"else ">tempfile
+}
+notfirst=1
+printf"if (%s) {\n",tokentest[t]>tempfile
+printf"\t\tloc += %d;\n",len-1>tempfile
+printf"\t\treturn %s;\n\t\t}\n",tokenname[t]>tempfile
+}
+}
+
+
+##:57
+
+#line 866 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"\t}\n">tempfile
+}
+##58:
+
+#line 887 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 888 "/u/norman/pu/web/dist/src/master/spider.web"
+for(t in tokentest){
+if(tokenlength[t]==1){
+print"This can't happen: token",t,"is of length 1 but",\
+"it has a test"
+exitcode=-1
+}
+}
+
+
+
+##:58
+
+#line 869 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:56
+
+#line 858 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:55
+
+#line 849 "/u/norman/pu/web/dist/src/master/spider.web"
+
+tempfile=ttokfile
+##55:
+
+#line 854 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 855 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Here we input tokens of more than one character">tempfile
+print"@<Compress two-symbol operator@>=">tempfile
+##56:
+
+#line 863 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 864 "/u/norman/pu/web/dist/src/master/spider.web"
+for(len=maxtokenlength;len>=2;len--){
+printf"if (loc+%d<=limit) {\n",len-1>tempfile
+##57:
+
+#line 871 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 872 "/u/norman/pu/web/dist/src/master/spider.web"
+notfirst=0
+for(t in tokentest){
+if(tokenlength[t]==len){
+printf"\t">tempfile
+if(notfirst==1){
+printf"else ">tempfile
+}
+notfirst=1
+printf"if (%s) {\n",tokentest[t]>tempfile
+printf"\t\tloc += %d;\n",len-1>tempfile
+printf"\t\treturn %s;\n\t\t}\n",tokenname[t]>tempfile
+}
+}
+
+
+##:57
+
+#line 866 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"\t}\n">tempfile
+}
+##58:
+
+#line 887 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 888 "/u/norman/pu/web/dist/src/master/spider.web"
+for(t in tokentest){
+if(tokenlength[t]==1){
+print"This can't happen: token",t,"is of length 1 but",\
+"it has a test"
+exitcode=-1
+}
+}
+
+
+
+##:58
+
+#line 869 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:56
+
+#line 858 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:55
+
+#line 851 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:54
+##59:
+
+#line 904 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 905 "/u/norman/pu/web/dist/src/master/spider.web"
+print"Writing out predefined scraps">logfile
+##60:
+
+#line 917 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 918 "/u/norman/pu/web/dist/src/master/spider.web"
+if(id_category!=""){
+print"@ @<Append an identifier scrap@>=">scrapfile
+print"p=id_lookup(id_first, id_loc,normal);">scrapfile
+print"if (p->ilk==normal) {">scrapfile
+print" small_app(id_flag+p-name_dir);">scrapfile
+printf" app_scrap(SP_%s,%s_math);",\
+id_category,id_mathness>scrapfile
+appended[id_category]=1
+print" /* not a reserved word */">scrapfile
+print"}">scrapfile
+print"else if reserved(p) {">scrapfile
+print"@<Decide on reserved word scraps@>;">scrapfile
+print"}">scrapfile
+print"else {">scrapfile
+print" err_print(\"! Identifier with unmentioned ilk\");">scrapfile
+print"@.Identifier with unmentioned ilk@>">scrapfile
+print"}">scrapfile
+}else{
+print"Error: I don't know what to do with an identifier"
+print" Please give me a \"token identifier ...\""
+exitcode=-1
+}
+
+##:60
+
+#line 906 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##62:
+
+#line 948 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 949 "/u/norman/pu/web/dist/src/master/spider.web"
+print"Warning: TeX strings have the same category as ",\
+"numeric constants in this WEAVE."
+print"Warning: TeX strings have the same category as ",\
+"numeric constants in this WEAVE.">logfile
+if(number_category!=""){
+print"@ For some reason strings, constants,",\
+" and \TeX\ strings are identified.">scrapfile
+print"That has to be fixed.">scrapfile
+print"@<Do the |app_scrap| for a string or constant@>=">scrapfile
+printf"app_scrap(SP_%s,%s_math);\n",number_category,\
+number_mathness>scrapfile
+appended[number_category]=1
+}else{
+print"Error: I don't know what to do with a numeric constant"
+print" Please give me a \"token number ...\""
+exitcode=-1
+}
+
+
+##:62
+
+#line 907 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##64:
+
+#line 977 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 978 "/u/norman/pu/web/dist/src/master/spider.web"
+if(newline_category!=""){
+print"@ @<Append a newline scrap@>=">scrapfile
+transstring=newline_translation
+selfstring="small_app(next_control);"
+wherestring="in translation of token newline"
+append_keyword="small_app"
+##24:
+
+#line 336 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 342 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+temp=sprintf("\tapp_str(\"%s\");\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^[a-zA-Z_]+$/){
+translation_keywords[alternate]=1
+temp=sprintf("\t%s(%s);\n",append_keyword,alternate)
+
+outstring=outstring temp
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=sprintf("\tapp_str(%s);\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+##26:
+
+#line 377 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 378 "/u/norman/pu/web/dist/src/master/spider.web"
+if(selfstring==""){
+print"Translation \"<*>\" makes no sense",wherestring
+exitcode=-1
+}
+
+##:26
+
+#line 355 "/u/norman/pu/web/dist/src/master/spider.web"
+
+outstring=outstring selfstring
+}else{
+print"Bogus translation",wherestring
+exitcode=-1
+}
+}
+
+##:24
+
+#line 985 "/u/norman/pu/web/dist/src/master/spider.web"
+
+print outstring>scrapfile
+printf" app_scrap(SP_%s,%s_math);\n",newline_category,\
+newline_mathness>scrapfile
+appended[newline_category]=1
+}else{
+print"Error: I don't know what to do with a newline"
+print" Please give me a \"token newline ...\""
+exitcode=-1
+}
+
+##:64
+
+#line 908 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##65:
+
+#line 997 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 998 "/u/norman/pu/web/dist/src/master/spider.web"
+if(pseudo_semi_category!=""){
+print"@ @<Append a |pseudo_semi| scrap@>=">scrapfile
+transstring=pseudo_semi_translation
+selfstring="small_app(next_control);"
+wherestring="in translation of token pseudo_semi"
+append_keyword="small_app"
+##24:
+
+#line 336 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 342 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+temp=sprintf("\tapp_str(\"%s\");\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^[a-zA-Z_]+$/){
+translation_keywords[alternate]=1
+temp=sprintf("\t%s(%s);\n",append_keyword,alternate)
+
+outstring=outstring temp
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=sprintf("\tapp_str(%s);\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+##26:
+
+#line 377 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 378 "/u/norman/pu/web/dist/src/master/spider.web"
+if(selfstring==""){
+print"Translation \"<*>\" makes no sense",wherestring
+exitcode=-1
+}
+
+##:26
+
+#line 355 "/u/norman/pu/web/dist/src/master/spider.web"
+
+outstring=outstring selfstring
+}else{
+print"Bogus translation",wherestring
+exitcode=-1
+}
+}
+
+##:24
+
+#line 1005 "/u/norman/pu/web/dist/src/master/spider.web"
+
+print outstring>scrapfile
+printf" app_scrap(SP_%s,%s_math);\n",pseudo_semi_category,\
+pseudo_semi_mathness>scrapfile
+appended[pseudo_semi_category]=1
+}else{
+printf"Error: I don't know what to do with a pseudo_semi (%s;)",\
+substr(at_sign,1,1)
+print" Please give me a \"token pseudo_semi ...\""
+exitcode=-1
+}
+
+##:65
+
+#line 909 "/u/norman/pu/web/dist/src/master/spider.web"
+
+print"Writing out token scraps">logfile
+##66:
+
+#line 1018 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1019 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ @<Cases for ordinary tokens@>=">scrapfile
+for(t in tokens){
+temp=tokenname[t]
+printf"case %s:\n",temp>scrapfile
+transstring=tokentranslation[t]
+selfstring="small_app(next_control);"
+wherestring=sprintf("in translation of token %s",t)
+append_keyword="small_app"
+##24:
+
+#line 336 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 342 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+temp=sprintf("\tapp_str(\"%s\");\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^[a-zA-Z_]+$/){
+translation_keywords[alternate]=1
+temp=sprintf("\t%s(%s);\n",append_keyword,alternate)
+
+outstring=outstring temp
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=sprintf("\tapp_str(%s);\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+##26:
+
+#line 377 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 378 "/u/norman/pu/web/dist/src/master/spider.web"
+if(selfstring==""){
+print"Translation \"<*>\" makes no sense",wherestring
+exitcode=-1
+}
+
+##:26
+
+#line 355 "/u/norman/pu/web/dist/src/master/spider.web"
+
+outstring=outstring selfstring
+}else{
+print"Bogus translation",wherestring
+exitcode=-1
+}
+}
+
+##:24
+
+#line 1028 "/u/norman/pu/web/dist/src/master/spider.web"
+
+print outstring>scrapfile
+printf"\tapp_scrap(SP_%s,%s_math);\n",tokencategory[t],\
+tokenmathness[t]>scrapfile
+temp=tokencategory[t]
+appended[temp]=1
+
+print"\tbreak;">scrapfile
+}
+
+##:66
+
+#line 911 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+
+
+
+##:59
+##67:
+
+#line 1047 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1048 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ @<Cases for tokens to be output@>=">ttokfile
+for(t in tokennumbers){
+##69:
+
+#line 1099 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1100 "/u/norman/pu/web/dist/src/master/spider.web"
+if(t=="string"||t=="constant"||t=="identifier")
+continue
+
+
+
+##:69
+
+#line 1050 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"case %s:\n",tokenname[t]>ttokfile
+this_tangleto=tokentangleto[t]
+if(this_tangleto==""){
+printf"\tC_printf(\"%%s\",\"%s\");\n",t>ttokfile
+}else{
+printf"\tif (out_state==verbatim) {\n">ttokfile
+printf"\t\tC_printf(\"%%s\",\"%s\");\n",t>ttokfile
+printf"\t} else {\n">ttokfile
+##70:
+
+#line 1106 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1107 "/u/norman/pu/web/dist/src/master/spider.web"
+oldwherestring=wherestring
+wherestring="for tangleto "wherestring
+
+transstring=this_tangleto
+##27:
+
+#line 389 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 390 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 395 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+print"Digit not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^[a-zA-Z_]+$/){
+print"Key word not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=substr(alternate,2,length(alternate)-2)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+print"<*> not allowed in restricted translation",wherestring
+exitcode=-1
+}else{
+print"Bogus restricted translation",wherestring
+exitcode=-1
+}
+}
+outstring="\""outstring"\""
+
+
+
+##:27
+
+#line 1112 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"\tC_printf(\"%%s\",%s);\n",outstring>ttokfile
+
+wherestring=oldwherestring
+
+
+
+##:70
+
+#line 1059 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"\t}\n">ttokfile
+}
+print"\tif (out_state!=verbatim) out_state=misc;">ttokfile
+print"break;">ttokfile
+}
+
+##:67
+##68:
+
+#line 1068 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1069 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ @<Cases for tokens to be output@>=">ttokfile
+for(t in tokentangleto){
+##69:
+
+#line 1099 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1100 "/u/norman/pu/web/dist/src/master/spider.web"
+if(t=="string"||t=="constant"||t=="identifier")
+continue
+
+
+
+##:69
+
+#line 1071 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(tokennumbers[t]!=""||tokentangleto[t]=="")
+continue
+if(t=="@"){
+thistangletokname="@@"
+}else if(t=="\\"||t=="'"){
+thistangletokname="\\"t
+}else{
+thistangletokname=t
+}
+printf"case '%s':\n",thistangletokname>ttokfile
+this_tangleto=tokentangleto[t]
+if(this_tangleto==""){
+print"This can't happen -- null tangleto for",t,wherestring
+exitcode=-1
+}else{
+printf"\tif (out_state==verbatim) {\n">ttokfile
+printf"\t\tC_printf(\"%%s\",\"%s\");\n",t>ttokfile
+printf"\t} else {\n">ttokfile
+##70:
+
+#line 1106 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1107 "/u/norman/pu/web/dist/src/master/spider.web"
+oldwherestring=wherestring
+wherestring="for tangleto "wherestring
+
+transstring=this_tangleto
+##27:
+
+#line 389 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 390 "/u/norman/pu/web/dist/src/master/spider.web"
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 395 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+print"Digit not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^[a-zA-Z_]+$/){
+print"Key word not allowed in restricted translation",wherestring
+exitcode=-1
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=substr(alternate,2,length(alternate)-2)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+print"<*> not allowed in restricted translation",wherestring
+exitcode=-1
+}else{
+print"Bogus restricted translation",wherestring
+exitcode=-1
+}
+}
+outstring="\""outstring"\""
+
+
+
+##:27
+
+#line 1112 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"\tC_printf(\"%%s\",%s);\n",outstring>ttokfile
+
+wherestring=oldwherestring
+
+
+
+##:70
+
+#line 1090 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"\t}\n">ttokfile
+}
+print"\tif (out_state!=verbatim) out_state=misc;">ttokfile
+print"break;">ttokfile
+}
+##:68
+##71:
+
+#line 1123 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1124 "/u/norman/pu/web/dist/src/master/spider.web"
+tempfile=scrapfile
+##72:
+
+#line 1131 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1132 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Here are the definitions of the token names">tempfile
+for(t in tokennumbers){
+temp=tokennumbers[t]
+if(temp==0)
+continue
+if(tempfile==ttokfile){
+##69:
+
+#line 1099 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1100 "/u/norman/pu/web/dist/src/master/spider.web"
+if(t=="string"||t=="constant"||t=="identifier")
+continue
+
+
+
+##:69
+
+#line 1139 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=temp+31+3-highesttoken
+
+}
+printf"@d %s = %s\n",tokenname[t],temp>tempfile
+}
+
+
+##:72
+
+#line 1125 "/u/norman/pu/web/dist/src/master/spider.web"
+
+tempfile=ttokfile
+##72:
+
+#line 1131 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1132 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Here are the definitions of the token names">tempfile
+for(t in tokennumbers){
+temp=tokennumbers[t]
+if(temp==0)
+continue
+if(tempfile==ttokfile){
+##69:
+
+#line 1099 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1100 "/u/norman/pu/web/dist/src/master/spider.web"
+if(t=="string"||t=="constant"||t=="identifier")
+continue
+
+
+
+##:69
+
+#line 1139 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=temp+31+3-highesttoken
+
+}
+printf"@d %s = %s\n",tokenname[t],temp>tempfile
+}
+
+
+##:72
+
+#line 1127 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:71
+##79:
+
+#line 1268 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1269 "/u/norman/pu/web/dist/src/master/spider.web"
+print"Writing out reserved words and ilks">logfile
+ilkno=64
+print"@ Here is a list of all the ilks">reserved
+for(i in ilk_translation){
+printf"@d SP_%s = %d\n",i,ilkno>reserved
+ilkno++
+}
+
+##:79
+##80:
+
+#line 1279 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1280 "/u/norman/pu/web/dist/src/master/spider.web"
+print" ">reserved
+print"@ Here are the scraps we get from the reserved words">reserved
+print"@d the_word = res_flag+p-name_dir">reserved
+print"@<Decide on reserved word scraps@>=">reserved
+print"switch (p->ilk) {">reserved
+for(t in ilk_translation){
+printf"\tcase SP_%s: \n\t\t",t>reserved
+transstring=ilk_translation[t]
+selfstring="small_app(the_word);"
+wherestring=sprintf("in translation of ilk %s",t)
+append_keyword="small_app"
+##24:
+
+#line 336 "/u/norman/pu/web/dist/src/master/spider.web"
+
+temp=substr(transstring,2,length(transstring)-2)
+trcnt=split(temp,trtok,"-")
+outstring=""
+for(tridx=1;tridx<=trcnt;tridx++){
+alternate=trtok[tridx]
+##25:
+
+#line 366 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 367 "/u/norman/pu/web/dist/src/master/spider.web"
+if(alternate=="space"){
+alternate="\" \""
+}else if(alternate=="dash"){
+alternate="\"-\""
+}
+
+
+##:25
+
+#line 342 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(alternate~/^[0-9]$/){
+temp=sprintf("\tapp_str(\"%s\");\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^[a-zA-Z_]+$/){
+translation_keywords[alternate]=1
+temp=sprintf("\t%s(%s);\n",append_keyword,alternate)
+
+outstring=outstring temp
+}else if(alternate~/^\"([^"]*\\\")*[^"]*\"$/){
+temp=sprintf("\tapp_str(%s);\n",alternate)
+outstring=outstring temp
+}else if(alternate~/^\*$/){
+##26:
+
+#line 377 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 378 "/u/norman/pu/web/dist/src/master/spider.web"
+if(selfstring==""){
+print"Translation \"<*>\" makes no sense",wherestring
+exitcode=-1
+}
+
+##:26
+
+#line 355 "/u/norman/pu/web/dist/src/master/spider.web"
+
+outstring=outstring selfstring
+}else{
+print"Bogus translation",wherestring
+exitcode=-1
+}
+}
+
+##:24
+
+#line 1293 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(trcnt>0)
+has_translation[t]=1
+print outstring>reserved
+printf"\tapp_scrap(SP_%s,%s_math);\n",ilk_category[t],\
+ilk_mathness[t]>reserved
+temp=ilk_category[t]
+appended[temp]=1
+
+printf"\t\tbreak;\n">reserved
+}
+print"}">reserved
+
+
+##:80
+##81:
+
+#line 1309 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1310 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ @<Store all the reserved words@>=">reserved
+for(i in reservedilk){
+printf"id_lookup(\"%s\",NULL,SP_%s);\n",i,reservedilk[i]>reserved
+}
+
+##:81
+##118:
+
+#line 1876 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1877 "/u/norman/pu/web/dist/src/master/spider.web"
+print"Writing out category codes">logfile
+print"@ Here is a list of category codes scraps can have">grammarfile
+i=1
+for(t in categories){
+printf"@d SP_%s = %d\n",t,i>grammarfile
+i++
+}
+print"@c">grammarfile
+##:118
+##119:
+
+#line 1887 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1888 "/u/norman/pu/web/dist/src/master/spider.web"
+print"#ifdef DEBUG">grammarfile
+print"#define PRINT_CAT(A,B) case A: printf(B); break">grammarfile
+print"print_cat(c) /* symbolic printout of a category */">grammarfile
+print"eight_bits c;">grammarfile
+print"{">grammarfile
+print" switch(c) {">grammarfile
+for(t in categories){
+printf"PRINT_CAT(SP_%s,\"%s\");\n",t,t>grammarfile
+}
+print" case 0: printf(\"zero\"); break;">grammarfile
+print" default: printf(\"UNKNOWN\"); break;">grammarfile
+print" }">grammarfile
+print"}">grammarfile
+print"#endif DEBUG">grammarfile
+print" ">grammarfile
+
+##:119
+##123:
+
+#line 1947 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1948 "/u/norman/pu/web/dist/src/master/spider.web"
+print"Writing out grammar">logfile
+print"@ Here is where we define |highestposoverall| and where we">grammarfile
+print"check the productions.">grammarfile
+print"@d highestposoverall =",highestposoverall>grammarfile
+print"@<Test for all of the productions@>=">grammarfile
+for(n=1;n<=prodnum;n++){
+if(n%5==0)
+print"@ @<Test for all of the productions@>="\
+>grammarfile
+##126:
+
+#line 2011 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2012 "/u/norman/pu/web/dist/src/master/spider.web"
+this_string=inputline[n]
+tempi=index(this_string,"|")
+while(tempi!=0){
+tempa=substr(this_string,1,tempi-1)
+tempb=substr(this_string,tempi+1)
+this_string=tempa"\\vert "tempb
+tempi=index(this_string,"|")
+}
+templ="";tempr=this_string
+tempi=index(tempr,"_")
+while(tempi!=0){
+tempa=substr(tempr,1,tempi-1)
+tempr=substr(tempr,tempi+1)
+templ=templ tempa"\\_"
+tempi=index(tempr,"_")
+}
+this_string=templ tempr
+templ="";tempr=this_string
+tempi=index(tempr,"#")
+while(tempi!=0){
+tempa=substr(tempr,1,tempi-1)
+tempr=substr(tempr,tempi+1)
+templ=templ tempa"\\#"
+tempi=index(tempr,"#")
+}
+this_string=templ tempr
+
+
+##:126
+
+#line 1957 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##127:
+
+#line 2041 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2042 "/u/norman/pu/web/dist/src/master/spider.web"
+toolong=this_string;this_string=""
+while(length(toolong)>60){
+idx=59
+idchar=substr(toolong,idx,1)
+while(idx>1&&idchar!=" "){
+idx--
+idchar=substr(toolong,idx,1)
+}
+if(idx==1)
+idx=59
+temp=substr(toolong,1,idx-1)
+toolong=substr(toolong,idx+1)
+this_string=this_string temp"\n"
+}
+this_string=this_string toolong
+
+
+
+##:127
+
+#line 1958 "/u/norman/pu/web/dist/src/master/spider.web"
+
+printf"if (%s) {\n\t/* %d: {\\tt %s} */\n%s",\
+prodtest[n],n,this_string,prodtrans[n]>grammarfile
+##124:
+
+#line 1970 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1971 "/u/norman/pu/web/dist/src/master/spider.web"
+ttk=targetcategory[n]
+if(ttk=="Unnamed category"){
+
+printf"\treduce(pp+%d,%d,%s,%d,%d);\n",ppstart[n],\
+tokensreduced[n],unnamed_cat[n],\
+1-highestposoverall,n>grammarfile
+}else{
+appended[ttk]=1
+
+reduction=highestpos[ttk]
+if(reduction<highestunknownpos){
+reduction=highestunknownpos
+}
+printf"\treduce(pp+%d,%d,SP_%s,%d,%d);\n",ppstart[n],\
+tokensreduced[n],targetcategory[n],\
+1-reduction,n>grammarfile
+}
+
+##:124
+
+#line 1962 "/u/norman/pu/web/dist/src/master/spider.web"
+
+print"\tgoto end_prods;">grammarfile
+printf"} ">grammarfile
+}
+printf"\n">grammarfile
+print"end_prods:">grammarfile
+
+##:123
+##130:
+
+#line 2095 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2096 "/u/norman/pu/web/dist/src/master/spider.web"
+if(language!=""){
+print"@ Here is the language-dependent stuff">tlang
+if(version!="")
+version=", Version "version
+printf"@d banner = \"This is %s TANGLE%s %s\\n\"\n",language,\
+version,date>tlang
+printf"@<Global...@>=char C_file_extension[]=\"%s\";\n",extension \
+>tlang
+
+print"@ Here is the language-dependent stuff">wlang
+if(version!="")
+version=", Version "version
+printf"@d banner = \"This is %s WEAVE%s %s\\n\"\n",language,\
+version,date>wlang
+print"@<Set |out_ptr| and do a |tex_printf| to read the macros@>="\
+>wlang
+printf"*out_ptr='x'; tex_printf(\"\\\\input %sweb.te\");\n",\
+extension>wlang
+printf"@ @<Global...@>=char C_file_extension[]=\"%s\";\n",extension \
+>wlang
+}else{
+print"Error: you haven't given me any \"language\" information"
+exitcode=-1
+}
+
+##:130
+##134:
+
+#line 2189 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2190 "/u/norman/pu/web/dist/src/master/spider.web"
+if(mod_def_cat!=""){
+print"@ @<Call |app_scrap| for a module definition@>=">scrapfile
+printf"app_scrap(SP_%s,no_math);\n",mod_def_cat>scrapfile
+appended[mod_def_cat]=1
+}else{
+print"Error: I don't know what to do with a module definition"
+print" Give me a \"module definition ...\""
+exitcode=-1
+}
+if(mod_use_cat!=""){
+print"@ @<Call |app_scrap| for a module use@>=">scrapfile
+printf"app_scrap(SP_%s,maybe_math);\n",mod_use_cat>scrapfile
+appended[mod_use_cat]=1
+}else{
+print"Error: I don't know what to do with a module use"
+print" Give me a \"module use ...\""
+exitcode=-1
+}
+
+
+##:134
+##136:
+
+#line 2230 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2231 "/u/norman/pu/web/dist/src/master/spider.web"
+tempfile=grammarfile
+##137:
+
+#line 2237 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2238 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Here is the |at_sign| for the new web">tempfile
+printf"@d at_sign = @`%s'\n",at_sign>tempfile
+print" ">tempfile
+print"@ Here is |the_at_sign| left for common">tempfile
+print"@<Global...@>=char the_at_sign = at_sign;">tempfile
+print" ">tempfile
+
+##:137
+
+#line 2232 "/u/norman/pu/web/dist/src/master/spider.web"
+
+tempfile=ttokfile
+##137:
+
+#line 2237 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2238 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Here is the |at_sign| for the new web">tempfile
+printf"@d at_sign = @`%s'\n",at_sign>tempfile
+print" ">tempfile
+print"@ Here is |the_at_sign| left for common">tempfile
+print"@<Global...@>=char the_at_sign = at_sign;">tempfile
+print" ">tempfile
+
+##:137
+
+#line 2234 "/u/norman/pu/web/dist/src/master/spider.web"
+
+
+##:136
+##140:
+
+#line 2304 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2305 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Here we recognize the comment start seqence">ttokfile
+print"@<See a comment starting at |loc| and skip it@>=">ttokfile
+printf"{int len; len=strlen(%s);\n",begin_comment_string>ttokfile
+printf"if (loc+len<=limit && !strncmp(loc,%s,len)) {\n",\
+begin_comment_string>ttokfile
+print"\tloc += len; /* a new thing */">ttokfile
+print"\tskip_comment(); /* scan to end of comment or newline */">ttokfile
+print"\tif (comment_continues || comments_end_with_newline)">ttokfile
+print"\t\treturn('\\n');">ttokfile
+print"\telse continue;\n}\n}">ttokfile
+
+
+##:140
+##141:
+
+#line 2318 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2319 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ @<See a comment starting at |loc-1| and return |begin_comment|@>="\
+>scrapfile
+printf"{int len; len=strlen(%s);\n",begin_comment_string>scrapfile
+printf"if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
+begin_comment_string>scrapfile
+print"\tloc += len-1;">scrapfile
+print"\t return (begin_comment); /* scan to end of comment or newline */">scrapfile
+print"}\n}">scrapfile
+
+
+
+
+##:141
+##142:
+
+#line 2332 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2333 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Here we deal with recognizing the end of comments">ttokfile
+printf"@d comments_end_with_newline = %d\n",comments_end_with_newline>ttokfile
+print"@<Recognize comment end starting at |loc-1|@>=">ttokfile
+if(comments_end_with_newline!=1){
+printf"{int len; len=strlen(%s);\n",end_comment_string>ttokfile
+printf"if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
+end_comment_string>ttokfile
+print"loc += len-1; return(comment_continues=0); }}">ttokfile
+}else{
+print"/* This code will never be executed */ ">ttokfile
+}
+
+##:142
+##143:
+
+#line 2348 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2349 "/u/norman/pu/web/dist/src/master/spider.web"
+printf"@ Here we recognize end of comments">scrapfile
+printf"@d comments_end_with_newline = %d\n",comments_end_with_newline>scrapfile
+print"@<Check for end of comment@>=">scrapfile
+printf"{int len; len=strlen(%s);\n",end_comment_string>scrapfile
+printf"if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
+end_comment_string>scrapfile
+print" loc++; if(bal==1) {if (phase==2) app_tok('}'); return(0);}">scrapfile
+print" else {">scrapfile
+print" err_print(\"! Braces don't balance in comment\");">scrapfile
+print"@.Braces don't balance in comment@>">scrapfile
+print" @<Clear |bal| and |return|@>;">scrapfile
+print" }">scrapfile
+print"}">scrapfile
+print"}">scrapfile
+
+
+##:143
+##144:
+
+#line 2367 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2368 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Important tokens:">ttokfile
+printf"@d begin_comment_string = %s\n",begin_comment_string>ttokfile
+printf"@d end_comment_string = %s\n",end_comment_string>ttokfile
+
+##:144
+##149:
+
+#line 2469 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2470 "/u/norman/pu/web/dist/src/master/spider.web"
+print"@ Important tokens:">ttokfile
+printf"@d sharp_line_open = %s\n",sharp_line_open>ttokfile
+printf"@d sharp_line_close = %s\n",sharp_line_close>ttokfile
+
+##:149
+
+#line 65 "/u/norman/pu/web/dist/src/master/spider.web"
+
+print"Writing out lists">logfile
+##21:
+
+#line 304 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 305 "/u/norman/pu/web/dist/src/master/spider.web"
+for(t in translation_keywords){
+print t>translationfile
+}
+
+##:21
+##73:
+
+#line 1149 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1150 "/u/norman/pu/web/dist/src/master/spider.web"
+for(t in tokenname){
+temp=tokenname[t]
+if(substr(temp,1,1)!="'"){
+##74:
+
+#line 1158 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1159 "/u/norman/pu/web/dist/src/master/spider.web"
+tempa=substr(temp,1,3)
+if(tempa=="SP_"){
+temp=substr(temp,4)
+}
+
+
+##:74
+
+#line 1153 "/u/norman/pu/web/dist/src/master/spider.web"
+
+print temp>tokennamefile
+}
+}
+
+##:73
+##83:
+
+#line 1330 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1331 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i in ilk_translation){
+print i>ilkfile
+}
+
+##:83
+##113:
+
+#line 1817 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1818 "/u/norman/pu/web/dist/src/master/spider.web"
+if(wrotecycles==0){
+print"0: dummy --> nodummy">cycles
+}
+
+##:113
+##120:
+
+#line 1905 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1906 "/u/norman/pu/web/dist/src/master/spider.web"
+for(c in categories){
+print c>categoryfile
+}
+
+##:120
+##122:
+
+#line 1929 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1930 "/u/norman/pu/web/dist/src/master/spider.web"
+for(n=1;n<=prodnum;n++){
+printf"%2d: %s\n",n,inputline[n]>productions
+}
+
+##:122
+
+#line 67 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##22:
+
+#line 309 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 310 "/u/norman/pu/web/dist/src/master/spider.web"
+for(t in translation_keywords){
+num_of_translation_keywords++
+}
+printf"You used %d translation keywords.\n",\
+num_of_translation_keywords>logfile
+printf"You used %d translation keywords.\n",num_of_translation_keywords
+
+##:22
+##84:
+
+#line 1335 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1336 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i in ilk_translation)number_of_ilks++
+for(i in reservedilk)number_of_reserved_words++
+printf"You defined %d reserved words of %d ilks.\n",\
+number_of_reserved_words,number_of_ilks
+printf"You defined %d reserved words of %d ilks.\n",\
+number_of_reserved_words,number_of_ilks>logfile
+
+##:84
+##121:
+
+#line 1910 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1911 "/u/norman/pu/web/dist/src/master/spider.web"
+for(c in categories){
+number_of_categories++
+}
+printf"You used %d different categories in %d productions.\n",\
+number_of_categories,prodnum
+printf"You used %d different categories in %d productions.\n",\
+number_of_categories,prodnum>logfile
+printf"The biggest production had %d scraps on its left-hand side.\n",\
+highestposoverall
+printf"The biggest production had %d scraps on its left-hand side.\n",\
+highestposoverall>logfile
+
+
+##:121
+
+#line 68 "/u/norman/pu/web/dist/src/master/spider.web"
+
+##47:
+
+#line 734 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 735 "/u/norman/pu/web/dist/src/master/spider.web"
+if(tokennumber<127){
+print"Error: too many token names for WEAVE --- over by",\
+127-tokennumber
+exitcode=-1
+}
+##:47
+##48:
+
+#line 743 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 744 "/u/norman/pu/web/dist/src/master/spider.web"
+if(highesttoken-tokennumber>31-(11-1)+3){\
+
+print"Error: too many token names for TANGLE --- over by",\
+highesttoken-tokennumber-(31-(11-1)+3)
+exitcode=-1
+}
+
+
+
+##:48
+##82:
+
+#line 1318 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1319 "/u/norman/pu/web/dist/src/master/spider.web"
+for(i in ilk_translation){
+if(has_reserved[i]!=1){
+print"Error: there is no reserved word of ilk",i
+exitcode=-1
+}
+if(has_translation[i]!=1){
+print"Error: ilk",i,"has no translation"
+exitcode=-1
+}
+}
+
+##:82
+##116:
+
+#line 1858 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1859 "/u/norman/pu/web/dist/src/master/spider.web"
+for(c in categories){
+if(reduced[c]!=1){
+print"Warning: category",c,"never reduced"
+}
+}
+
+
+##:116
+##125:
+
+#line 1992 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 1993 "/u/norman/pu/web/dist/src/master/spider.web"
+for(c in categories){
+if(appended[c]!=1){
+if(c=="ignore_scrap"){
+print"Warning: category",c,"never appended"
+}else{
+print"Error: category",c,"never appended"
+exitcode=-1
+}
+}
+}
+
+
+
+##:125
+
+#line 69 "/u/norman/pu/web/dist/src/master/spider.web"
+
+if(exitcode!=0){
+exit exitcode
+}
+}
+
+##:3
+
diff --git a/web/spiderweb/src/master/spider.web b/web/spiderweb/src/master/spider.web
new file mode 100644
index 0000000000..0aca8a4232
--- /dev/null
+++ b/web/spiderweb/src/master/spider.web
@@ -0,0 +1,2580 @@
+% Copyright 1989 by Norman Ramsey, Odyssey Research Associates
+% Not to be sold, but may be used freely for any purpose
+% For more information, see file COPYRIGHT in the parent directory
+
+\message{OK, entering \string\batchmode...}
+\batchmode
+
+\let\RA\rightarrow
+
+\def\vert{{\tt\char'174}}
+\def\pb{$\.|\ldots\.|$} % C brackets (|...|)
+
+\def\title{SPIDER}
+
+\def\topofcontents{\null\vfill
+ \titlefalse % include headline on the contents page
+ \def\rheader{\hfil}
+ \centerline{\titlefont The {\ttitlefont SPIDER} processor}
+ \vfill}
+
+
+\def\syntax##1{\leavevmode\hbox{$\langle\hbox{\sl ##1\/}\rangle$}}
+\def\produces{\leavevmode\hbox{${}::={}$}}
+\def\opt##1{$[$##1$]$}
+
+#*={\tt SPIDER} proper.
+#*Introduction.
+This is an AWK program designed to read a description of a programming
+language and to write out the language-dependent parts of WEB.
+In the main,
+the description of a programming language is
+ a list of all the tokens of the language
+(together with various facts about them) and a grammar for prettyprinting
+code fragments written in that language.
+The ``Spider User's Guide'' describes how to use {\tt SPIDER} to construct
+a {\tt WEB} system for the ALGOL-like language of your choice.
+({\tt SPIDER} may be able to handle LISP and Miranda and other strange
+languages; the experiment hasn't been tried.
+The unusual lexical requirements of FORTRAN are probably beyond it, at
+least until the lexical analysis is modernized.)
+
+# The outline of the program is fairly straightforward.
+We use |exitcode| throughout to monitor error status.
+If we were more Knuthlike, we would have a |history| variable with values of
+|spotless|, and so on.
+This will have to wait until we get macros back into \.{TANGLE}.
+
+We put the pattern-action statement for productions last, because in
+case of a conflict like \.{token~-->~...}, we want the interpretation
+as {\tt token} to win out over
+the interpretation as a prodution.
+
+#u#1
+BEGIN {
+#<Set initial values#>
+exitcode=0
+}
+#@
+#<Ignore comments and blank lines#>
+#<Pattern-action statements#>
+#<Production pattern-action statement#>
+#<Default action for patterns we don't recognize#>
+#@
+END {
+#<Write out all of the WEB code#>
+print "Writing out lists" > logfile
+#<Write lists of everything#>
+#<Write statistics for this \.{SPIDER}#>
+#<Check for errors at the very end#>
+if (exitcode != 0) {
+ exit exitcode
+ }
+}
+
+# There are a couple of actions we may want to perform with just
+about any command.
+If a command fails, we move on to the next, but we remember the fault
+so we can complain at the end.
+#<Punt this command#>=
+ exitcode=-1
+ next
+
+
+# Throughout \.{SPIDER} we always use the variable |i| to step through the
+fields of a command, so that |$i| is always the next field of interest.
+When we thinik we have finished a command,
+we will always want to check to make sure there are no unexamined
+fields left over.
+For error messages, the line number is really |NR-1|, since we put an
+extra line at the beginning with the date.
+#<Check that we used everything#>=
+ if (i<=NF) {
+ print "Error: leftover fields", $i, "... on line", NR-1
+ #<Punt...#>
+ }
+
+
+# To \.{SPIDER}, any line beginning with |"## "| is a comment.
+\.{SPIDER} also ignores blank lines.
+#<Ignore comments...#>=
+#=/^##|^ *$/#> {
+ ## comments, blank lines
+ print $0 > logfile
+ next
+ }
+
+# But, if \.{SPIDER} encounters a line we don't recognize, it complains.
+#<Default act...#>=
+ {
+ print "Warning: I don't know what to do with this line:"
+ print " ", $0
+ print "Warning: I don't know what to do with this line:" > logfile
+ print " ", $0 > logfile
+}
+
+
+#*1Files written by {\tt SPIDER}.
+ {\tt SPIDER} writes output to a number of files.
+Because 4.3~BSD AWK is limited in the number of files it can write at
+one time, there is substantial overlap.
+Here is a table:
+\noindent\halign{\vrule height10pt depth3.5pt width0pt
+\it##\hfil\tabskip=1em&\tt##\hfil&\tabskip=0pt
+ \hsize=4in\vtop{\noindent##\strut\par}\cr
+\noalign{\medskip}
+\bf Internal Name&\bf External Name&\omit\bf Description\hfil\cr
+\noalign{\smallskip}
+categoryfile&names.unsorted&
+names of categories, to be checked for duplicates by {\tt nodups.awk}
+\cr
+cycles&cycle.test&
+potential cycles, to be checked by {\tt cycle.web}
+\cr
+grammarfile&grammar.web&
+grammar; included in {\tt weave.web}
+\cr
+ilkfile&names.unsorted&
+names of ilks, to be checked for duplicates by {\tt nodups.awk}
+\cr
+logfile&spider.slog&
+log file, to be consulted when things go wrong
+\cr
+macrofile&*web.tex&
+language specific macro file, {\tt\string\input} by all \TeX{}
+ files created by {\tt weave.web}
+\cr
+productions&productions.list&
+list of the productions (numbered) used in debugging \.{WEAVE}
+\cr
+reserved&scraps.web&
+code for converting the reserved word to scraps.
+{\tt scraps.web} is included by {\tt weave.web}
+\cr
+scrapfile&scraps.web&
+code for converting tokens to scraps.
+{\tt scraps.web} is included by {\tt weave.web}
+\cr
+tlang&outtoks.web&
+Information about what language we're webbing.
+{\tt outtoks.web} is included by {\tt tangle.web}.
+\cr
+tokennamefile&names.unsorted&
+list of names of all the tokens, to be checked by {\tt nodups.awk}
+\cr
+translationfile&trans\_keys.unsorted&
+list of names of all the translation keywords.
+Checked for duplicates by {\tt nodups.awk}, and also for recognizability
+by {\tt transcheck.awk}.
+\cr
+ttokfile&outtoks.web&
+This is the tokenization code for {\tt TANGLE}.
+\cr
+wlang&scraps.web&
+Information about what language we're webbing,
+{\tt scraps.web} is included by {\tt weave.web}.
+\cr
+}
+
+# Every action writes information to a log file.
+This log file can be used to check up on what happened.
+#<Set initial...#>=
+ logfile = "spider.slog"
+
+# Here we write the names of the key words used in translations.
+#<Set initi...#>=
+ translationfile = "trans_keys.unsorted"
+
+# We write tokens out to two files: |scrapfile| for \.{WEAVE}, and
+|ttokfile| for \.{TANGLE}.
+#<Set init...#>=
+ scrapfile = "scraps.web"
+ print "@*Scrap code generated by {\\tt SPIDER}." > scrapfile
+ ttokfile = "outtoks.web"
+ print "@*Token code generated by {\\tt SPIDER}." > ttokfile
+# The reserved word stuff gets a file of its own, or it would in an ideal
+world.
+#<Set init...#>=
+ reserved = "scraps.web" ## use same file; not enough files
+
+# We'll also end up writing a list of token names, for name checking
+purposes.
+#<Set initial...#>=
+ tokennamefile = "names.unsorted" ## cut down on number of output files
+# We also write out every ilk, so we'll be able to look for name
+clashes with translations and so on.
+#<Set init...#>=
+ ilkfile = "names.unsorted" ## cut down on number of output files
+# We also write all the category names to a separate file, so we can
+check for duplicates later.
+#<Set init...#>=
+ categoryfile = "names.unsorted" ## cut down on number of output files
+# We use a special file to write grammar information:
+#<Set init...#>=
+ grammarfile = "grammar.web"
+ print "@*Grammar code generated by {\\tt SPIDER}." > grammarfile
+# We use the language information to write banners and macro information.
+We combine this with other stuff because AWK can't handle more than
+10 output files.
+#<Set initial...#>=
+ tlang = "outtoks.web" ## same as ttokfile
+ wlang = "scraps.web" ## same as scrapfile
+
+# We will write a list of the successfully parsed productions to a
+separate file.
+#<Set init...#>=
+ productions = "productions.list"
+
+# These productions will get fed to {\tt cycle.awk}, which looks for cycles.
+#<Set initial...#>=
+ cycles = "cycle.test"
+
+
+
+#*Processing translations.
+Translations tell \.{WEAVE} or \.{TANGLE} what to write out in
+particular circumstances (e.g.~after scanning a particular token, or
+when firing some production).
+They are described at some length in the ``\.{SPIDER} User's Guide.''
+Translations are enclosed in angle brackets and separated by dashes.
+They can contain key words, digits, the self marker~`{\tt*}',
+or quoted strings.
+Since we can't put a space or dash into strings, we allow the use of
+key words |space| and |dash| to stand for those symbols.
+#^space#>
+#^dash#>
+
+Other key words are interpreted by \.{WEAVE} as prettyprinting instructions:
+
+\yskip\hang |break_space| denotes an optional line break or an en space;
+
+\yskip\hang |force| denotes a line break;
+
+\yskip\hang |big_force| denotes a line break with additional vertical space;
+
+\yskip\hang |opt| denotes an optional line break (with the continuation
+line indented two ems with respect to the normal starting position)---this
+code is followed by an integer |n|, and the break will occur with penalty
+$10n$;
+
+\yskip\hang |backup| denotes a backspace of one em;
+
+\yskip\hang |cancel| obliterates any |break_space| or |force| or |big_force|
+tokens that immediately precede or follow it and also cancels any
+|backup| tokens that follow it;
+
+\yskip\hang |indent| causes future lines to be indented one more em;
+
+\yskip\hang |outdent| causes future lines to be indented one less em.
+
+\yskip\hang |math_rel|, |math_bin|, and |math_op| will be translated into
+\.{\\mathrel\{}, \.{\\mathbin\{}, and \.{\\mathop\{}, respectively.
+
+
+\yskip\noindent All of these tokens are removed from the \TeX\ output that
+comes from programming language text between \pb\ signs; |break_space|
+and |force| and
+|big_force| become single spaces in this mode.
+%The translation of other
+%program texts results in \TeX\
+%control sequences \.{\\1}, \.{\\2},
+%\.{\\3}, \.{\\4}, \.{\\5}, \.{\\6},
+%\.{\\7} corresponding respectively to
+%|indent|, |outdent|, |opt|,
+%|backup|, |break_space|, |force|, and
+%|big_force|. However,
+A sequence of consecutive `\.\ ', |break_space|,
+|force|, and/or |big_force| tokens is first replaced by a single token
+(the maximum of the given ones).
+
+%Some Other control sequences in the \TeX\ output will be
+%`\.{\\\\\{}$\,\ldots\,$\.\}'
+%surrounding identifiers, `\.{\\\&\{}$\,\ldots\,$\.\}' surrounding
+%reserved words, `\.{\\.\{}$\,\ldots\,$\.\}' surrounding strings,
+%`\.{\\C\{}$\,\ldots\,$\.\}$\,$|force|' surrounding comments, and
+%`\.{\\X$n$:}$\,\ldots\,$\.{\\X}' surrounding module names, where
+%|n| is the module number.
+
+# We write out the names of all the key words used translations,
+so we can check that
+\.{WEAVE} can be expected to recognize them.
+This helps us catch the problem early if a translation given is
+not one of the above
+(as opposed to, say, having the C~compiler fail to compile \.{WEAVE}).
+#<Write lists...#>=
+ for (t in translation_keywords) {
+ print t > translationfile
+ }
+
+# #<Write stat...#>=
+for (t in translation_keywords) {
+ num_of_translation_keywords++
+ }
+printf "You used %d translation keywords.\n", \
+ num_of_translation_keywords > logfile
+printf "You used %d translation keywords.\n", num_of_translation_keywords
+
+# If the macro facility worked right,
+we would use the following patterns to recognize items as they occur:
+#d cat_pattern = #=/[a-zA-Z][a-zA-Z_]*/#>
+#d trans_pattern = #=/<(([0-9]|[a-zA-Z][a-zA-Z_]*|"([^"]*\\")*[^"]*"|\*)-)*#>#&
+ #=([0-9]|[a-zA-Z][a-zA-Z_]*|"([^"]*\\")*[^"]*"|\*)>/#>
+
+# Here's where we swallow a translation and spit out the \.{WEAVE} code
+to handle that translation.
+Since AWK has no functions, we define this as a module.
+
+When we're appending a key word {\it in the process of creating a
+scrap from a token}, we use |small_app| in preference to |app|,
+because |app|'s cleverness about mathness and dollar signs only works when
+reducing existing scraps, not when creating scraps from tokens.
+We'll expect the variable |append_keyword| to be set to either
+|"small_app"| or |"app"|.
+
+
+#<Take translation from |transstring| and write corresponding \.{WEAVE} code to
+|outstring|, using |selfstring| as translation of |"<*>"|#>=
+temp = substr(transstring,2,length(transstring)-2) ## kills awk bug
+trcnt = split(temp,trtok,"-")
+outstring = ""
+for (tridx=1;tridx<=trcnt;tridx++) {
+ alternate=trtok[tridx]
+ #<Convert |"space"| and |"dash"|#>
+ if (alternate ~ #=/^[0-9]$/#>) { ## digit
+ temp = sprintf("\tapp_str(\"%s\");\n",alternate)
+ outstring=outstring temp
+ } else if (alternate ~ #=/^[a-zA-Z_]+$/#>) { ## key word
+ translation_keywords[alternate]=1 ## remember
+ temp = sprintf("\t%s(%s);\n",append_keyword,alternate)
+##Call |app| or |small_app| depending whether we're reducing or creating scraps
+ outstring=outstring temp
+ } else if (alternate ~ #=/^\"([^"]*\\\")*[^"]*\"$/#>) { ## string
+ temp = sprintf("\tapp_str(%s);\n",alternate)
+ outstring=outstring temp
+ } else if (alternate ~ #=/^\*$/#>) { ## self marker
+ #<If |selfstring==""|, complain loudly#>
+ outstring=outstring selfstring
+ } else {
+ print "Bogus translation", wherestring
+ exitcode = -1
+ }
+}
+
+# Here we convert the key words |space| and |dash| to strings.
+We quote the strings, to be sure that they are handled by the string
+mechanism.
+#<Convert |"space"|...#>=
+ if (alternate=="space") {
+ alternate="\" \""
+ } else if (alternate=="dash") {
+ alternate="\"-\""
+ }
+
+
+# There are some places (notably in productions) where the translation
+|"<*>"| makes no sense.
+In this case the caller sets |selfstring=""|, and we complain.
+#<If |selfstring==""|, complain...#>=
+if (selfstring=="") {
+ print "Translation \"<*>\" makes no sense", wherestring
+ exitcode = -1
+ }
+
+# There are times when we may want to convert a translation directly
+into a quoted string, usually for \.{TANGLE}'s benefit.
+Here, the only things allowed are quoted strings and |space| and |dash|.
+We peel off quote marks and concatenate things together, and then we
+put the quote marks back on at the end.
+#<Convert restricted translation in |transstring|
+ to quoted string in |outstring|#>=
+temp = substr(transstring,2,length(transstring)-2) ## kills awk bug
+trcnt = split(temp,trtok,"-")
+outstring = ""
+for (tridx=1;tridx<=trcnt;tridx++) {
+ alternate=trtok[tridx]
+ #<Convert |"space"| and |"dash"|#>
+ if (alternate ~ #=/^[0-9]$/#>) { ## digit
+ print "Digit not allowed in restricted translation", wherestring
+ exitcode = -1
+ } else if (alternate ~ #=/^[a-zA-Z_]+$/#>) { ## key word
+ print "Key word not allowed in restricted translation", wherestring
+ exitcode = -1
+ } else if (alternate ~ #=/^\"([^"]*\\\")*[^"]*\"$/#>) { ## string
+ temp = substr(alternate,2,length(alternate)-2) ## strip quotes
+ outstring=outstring temp
+ } else if (alternate ~ #=/^\*$/#>) { ## self marker
+ print "<*> not allowed in restricted translation", wherestring
+ exitcode = -1
+ } else {
+ print "Bogus restricted translation", wherestring
+ exitcode = -1
+ }
+}
+outstring = "\"" outstring "\"" ## put quotes back on |outstring|
+
+
+
+#*Tokens.
+
+Tokens are pretty complicated.
+Each token has a string by which we recognize it in the input.
+This string is what immediately follows the |token| command.
+Then, there's another string that tells \.{TANGLE} how to write out
+the token.
+Finally, it has a category and a translation (so we can make a scrap out
+of it), and a mathness (to tell us whether it has to be in math
+mode, horizontal mode, or either).
+The
+\.{translation} and \.{mathness} have defaults.
+
+#*2Scanning for token descriptions.
+This module is used everywhere we must scan a line for token descriptions.
+#<Scan this line from |start_place| to finish, looking for
+ \.{translation}$\ldots$ and putting results in
+ |this_translation|$\ldots$#>=
+for (i=start_place;i<NF;) {
+ if ($i=="tangleto") { ## for \.{TANGLE}
+ i++
+ this_tangleto=$i
+ i++
+ } else if ($i=="translation") { ## for \.{WEAVE}
+ i++
+ this_translation=$i
+ i++
+ } else if ($i=="mathness") { ## for \.{WEAVE}
+ i++
+ this_mathness=$i
+ i++
+ } else if ($i=="category") { ## for \.{WEAVE}
+ i++
+ this_category=$i
+ categories[$i]=1
+ i++
+ } else if ($i=="name") { ## for debugging
+ i++
+ this_name="SP_" $i ##OK, so it's hacking...
+ i++
+ } else {
+ print "Error: unrecognized token description", $i, "on line", NR-1
+ #<Punt...#>
+ }
+}
+#<Check that we used everything#>
+
+# We check for the presence or absence of certain empty strings after
+scanning.
+#<Make sure |this_name| is empty#>=
+if (this_name != "") {
+ print "Error: name doesn't apply on line", NR-1
+ #<Punt...#>
+ }
+# #<Make sure |this_tangleto| is empty#>=
+if (this_tangleto != "") {
+ print "Error: tangleto doesn't apply on line", NR-1
+ #<Punt...#>
+ }
+# #<Make sure |this_category| is empty#>=
+if (this_category != "") {
+ print "Error: category doesn't apply on line", NR-1
+ #<Punt...#>
+ }
+# #<Make sure |this_translation| is empty#>=
+if (this_translation != "") {
+ print "Error: translation doesn't apply on line", NR-1
+ #<Punt...#>
+ }
+# #<Make sure |this_category| is not empty#>=
+if (this_category == "") {
+ print "Error: you must give a category on line", NR-1
+ #<Punt...#>
+ }
+
+#*1Setting the default token descriptions.
+\.{SPIDER} maintains default information about {\em mathness}
+and {\em translation}, so these can be omitted from token descriptions.
+We can change the operative defaults at any time by using a
+|"default"| command.
+It too, scans for keywords, using the standard scanning module
+#<Pattern-action...#>=
+#=/^default /#> {
+ print "Setting defaults..." > logfile
+ start_place=2
+ #<Set |this_mathness| etcetera to the defaults and those with no
+ defaults to |""|#>
+ #<Scan this line from |start_place| to finish, looking for
+ \.{translation}$\ldots$ and putting results in
+ |this_translation|$\ldots$#>
+ #<Make sure |this_name| is empty#>
+ #<Make sure |this_category| is empty#>
+ default_translation=this_translation
+ default_mathness=this_mathness
+#@ print "\tdefault translation is", default_translation > logfile
+ print "\tdefault mathness is", default_mathness > logfile
+#@ next
+}
+
+# Normally, we will set all quantities to the defaults before scanning:
+#<Set |this_mathness| etcetera to the defaults and those with no
+defaults to |""|#>=
+ this_translation=default_translation
+ this_mathness=default_mathness
+ this_name=""
+ this_category=""
+ this_tangleto=""
+# When \.{SPIDER} starts up, the defaults are already set:
+#<Set initi...#>=
+ default_translation="<*>"
+ default_mathness="maybe"
+
+
+#*1Recognizing token designators.
+Let's begin by discussing the way \.{WEAVE} and \.{TANGLE} represent
+tokens internally.
+
+\.{WEAVE} and \.{TANGLE} process tokens in a two-step process.
+Both read the token from the input using |get_next|, which returns a
+unique eight-bit number representing the token.
+Generally printable ASCII characters represent themselves, and other
+tokens get numbers in the unprintable range.
+\.{TANGLE} assigns ranges to some tokens ahead of time: |string| is 2,
+|identifier| is #'202, and so on.
+Tokens that we introduce to \.{TANGLE} must have numbers between
+#'13 and #'37 inclusive.
+
+Rather than work with eight-bit numbers themselves, we use names for
+the tokens.
+This makes \.{WEAVE} and \.{TANGLE} easier to debug when things go wrong.
+
+In \.{WEAVE}, the category, mathness, and translation are all
+attached to a scrap based on the eight-bit number returned by
+|get_next|, and this is done at a later time.
+
+In \.{TANGLE}, characters are written to the output file(s) based on
+the token code, which can be either eight bits for simple tokens or
+sixteen for identifiers and things.
+
+Our mission in this section will be to read in all the token
+information from the {\tt token} command,
+and to create the names and numbers used by \.{WEAVE} and \.{TANGLE}
+to represent the tokens.
+In the next section we will
+write the code that processes the tokens for both \.{WEAVE} and
+\.{TANGLE} (lexical analysis in
+|get_next|, and subsequent processing elsewhere).
+You will pardon us if things get a bit tedious.
+
+# The {\tt token} command is used to specify tokens that are not
+reserved words.
+Reserved word tokens get special treatment all their own.
+
+#<Pattern...#>=
+#=/^token /#> {
+ print "Token", $2 > logfile
+ if ($2=="identifier") {
+ #<Process identifier token description#>
+ } else if ($2=="number") {
+ #<Process numeric token description#>
+ } else if ($2=="newline") {
+ #<Process newline token description#>
+ } else if ($2=="pseudo_semi") {
+ #<Process |pseudo_semi| token description#>
+ } else if ($2 ~ #=/[a-zA-ZA-Z0-9]+/#>) {
+ ## we recognize no other names
+ print "Error: unknown token species:", $2
+ #<Punt this command#>
+ } else {
+ #<Process a non-alphanumeric token description#>
+ }
+ categories[this_category]=1 ## is this right?
+#^questions#>
+ next
+}
+
+# Identifiers, numbers (and string literals), newlines, and the special
+token \.{pseduo\_semi} are predefined.
+#<Process identifier token description#>=
+ #<Set |this_mathness| etcetera to the defaults and those with no
+ defaults to |""| #>
+ this_translation=""
+ start_place=3
+ #<Scan this line from |start_place| to finish, looking for
+ \.{translation}$\ldots$ and putting results in
+ |this_translation|$\ldots$#>
+ #<Make sure |this_name| is empty#>
+ #<Make sure |this_tangleto| is empty#>
+ #<Make sure |this_category| is not empty#>
+ #<Make sure |this_translation| is empty#>
+ id_category=this_category
+ id_mathness=this_mathness
+
+# We have yet to implement a separate procedure for numerics and strings!
+#<Process numeric token description#>=
+ print "Warning: numeric constants and strings are",\
+ "identified in this WEAVE."
+ print "Warning: numeric constants and strings are",\
+ "identified in this WEAVE." > logfile
+ #<Set |this_mathness| etcetera to the defaults and those with no
+ defaults to |""| #>
+ this_translation=""
+ start_place=3
+ #<Scan this line from |start_place| to finish, looking for
+ \.{translation}$\ldots$ and putting results in
+ |this_translation|$\ldots$#>
+ #<Make sure |this_name| is empty#>
+ #<Make sure |this_tangleto| is empty#>
+ #<Make sure |this_category| is not empty#>
+ #<Make sure |this_translation| is empty#>
+ number_category=this_category
+ number_mathness=this_mathness
+
+#
+#<Process newline token description#>=
+ #<Set |this_mathness| etcetera to the defaults and those with no
+ defaults to |""| #>
+ start_place=3
+ #<Scan this line from |start_place| to finish, looking for
+ \.{translation}$\ldots$ and putting results in
+ |this_translation|$\ldots$#>
+ #<Make sure |this_name| is empty#>
+ #<Make sure |this_tangleto| is empty#>
+ #<Make sure |this_category| is not empty#>
+ newline_category=this_category
+ newline_mathness=this_mathness
+ newline_translation=this_translation
+
+#
+#<Process |pseudo_semi| token description#>=
+ #<Set |this_mathness| etcetera to the defaults and those with no
+ defaults to |""| #>
+ start_place=3
+ #<Scan this line from |start_place| to finish, looking for
+ \.{translation}$\ldots$ and putting results in
+ |this_translation|$\ldots$#>
+ #<Make sure |this_name| is empty#>
+ #<Make sure |this_tangleto| is empty#>
+ #<Make sure |this_category| is not empty#>
+ pseudo_semi_category=this_category
+ pseudo_semi_mathness=this_mathness
+ pseudo_semi_translation=this_translation
+
+# Here is where things get a bit more interesting; we have to
+consider all the other (non-reserved-word) tokens, and find a way to
+convert them to \.{WEAVE} and \.{TANGLE}'s internal form.
+We take single characters straight, except for those that must be
+escaped one way or another.
+For multicharacter tokens, we have to invent a name and a number,
+which process we will describe below.
+
+
+Tokens have a zillion attributes: not just category, translation, and
+their friends, but things like internal representations, the length of
+the input string, you name it.
+
+We remember the length of the longest token in the system,
+because when we go to
+recognize tokens we will look for the longest first and then on down.
+We maintain that length at the very end here.
+#<Process a non-alphanumeric token description#>=
+ this_string=$2
+ #<Translate |"{space}"| to |" "| in |this_string|#>
+## print NF, "fields on line", NR-1 ## caught a bug in DEC awk
+ $2 = this_string
+## print NF, "fields on line", NR-1
+ #<Set |tokenname[$2]|, |tokenlength[$2]|, and, for long
+ tokens, set |tokentest[$2]| and |tokennumbers[$2]|#>
+ if (tokens[$2]!="") {
+ print "Warning: token", $2, "defined twice"
+ }
+ tokens[$2]=1 ## remember this token
+ #<Set attributes of token |$2|#>
+ #<Make sure token |$2| has a number if it needs one#>
+ #<Update record of maximum token length#>
+
+
+# This code represents and undocumented feature.
+We should replace it by allowing restricted translations
+in |$2|, the then documenting it.
+When doing this, we'll have to match the full |trans_pattern|
+in all its glory; A mere |#=/<.*>/#>| won't do.
+
+#<Translate |"{space}"| to |" "| in |this_string|#>=
+old_string = this_string
+this_string = ""
+## Invariant: |this_string old_string| corresponds to result, and
+## |"{space}"| is translated in |this_string| but not |old_string|
+idx = index(old_string,"{space}")
+while (idx != 0) {
+ temp =substr(old_string,1,idx-1)
+ this_string = this_string temp " "
+ old_string = substr(old_string,idx+7)
+ idx = index(old_string,"{space}")
+}
+this_string = this_string old_string
+
+# Tokens need an internal eight-bit representation.
+For single characters (which are assumed to be printable), we use
+the ASCII code as the internal representation.
+Multicharacter tokens will be assigned a name and a number.
+(The names may be specified by the user or generated by \.{SPIDER}.)
+Unfortunately the numbers for \.{WEAVE} and \.{TANGLE} have to be
+different (the reasons will only depress you).
+We assign \.{WEAVE} numbers by starting numbering from |highesttoken|, and
+working our way down.
+At the moment |hisghesttoken==200|, and I can't remember whether 200 is
+a ``magic number'' or not, so you'd better assume that it is.
+We get the tpoken numbers for \.{TANGLE} by subtracting an offset,
+as you'll see later.
+#<Set initial...#>=
+ highesttoken=200 ## highest numbered token
+ tokennumber=highesttoken
+
+# At the end we check to make sure we haven't used up too many numbers
+for tokens.
+\.{WEAVE} token numbers must be |>=127|.
+#<Check for errors at the...#>=
+if (tokennumber<127) {
+ print "Error: too many token names for WEAVE --- over by",\
+ 127-tokennumber
+ exitcode=-1
+ }
+# \.{TANGLE} tokens must be between #'13 and #'37 inclusive.
+We add three to the number because \.{TANGLE} has special definitions for
+the three tokens taken off the top.
+#<Check for errors...#>=
+if (highesttoken-tokennumber > #'37-(#'13-1)+3) { \
+ ## number of tokens in |#'13|--|#'37|, plus 3
+ print "Error: too many token names for TANGLE --- over by",\
+ highesttoken-tokennumber - (#'37-(#'13-1)+3)
+ exitcode=-1
+ }
+
+
+
+# The token name is what \.{WEAVE} and \.{TANGLE} will use internally
+to refer to the token's internal representation as an eight-bit code.
+We use names instead of using the numbers directly in the vague hope that
+it will make \.{WEAVE} and \.{TANGLE} easier to debug when something goes
+wrong.
+For multi-character tokens, the name will be a \.{WEB} macro that is defined
+to be equal to the token's eight-bit code.
+If the token is a single character, its ``name'' will be that character,
+quoted with single quotes.
+The single-character tokens \.{@}, \.{\\}, and \.{'} require special
+handling, since they have to be escaped in some way to be quoted.
+
+Once we've computed the name, we put it in |tokenname[$2]|.
+#<Set |tokenname[$2]|, |tokenlength[$2]|, and, for long
+ tokens, set |tokentest[$2]| and |tokennumbers[$2]|#>=
+ if ($2=="@") {
+ $2="@@"
+ tokenname[$2]="'@@'"
+ tokenlength[$2]=1
+ } else if ($2=="'" || $2 == "\\") {
+ $2="\\" $2
+ tokenname[$2]="'" $2 "'"
+ tokenlength[$2]=1
+ } else if (length($2)>1) {
+ #<Handle multicharacter tokens#>
+ } else {
+ temp = sprintf("'%s'", $2)
+ tokenname[$2] = temp
+ tokenlength[$2]=1
+ }
+
+# For the long tokens, we generate a name by which we'll refer to the
+token.
+That name will actually be defined to be a number, which we'll take to
+be the current value of |tokennumber|.
+We'll write in |tokentest[$2]| the C~code used to recognize that token,
+and in |tokenlength[$2]| we'll leave that token's length.
+(The length is used both to find long tokens before short ones, and
+to avoid finding long ``tokens'' that
+actually go beyond the end of the line.)
+#<Handle multicharacter tokens#>=
+ tokenname[$2]="SP_gen_token_" tokennumber
+ tokennumbers[$2]=tokennumber
+ tokennumber--
+ ## figure out how to recognize the token
+ temp = sprintf( "strncmp(\"%s\",loc-1,%d)==0", $2, length($2))
+ tokentest[$2]=temp
+ tokenlength[$2]=length($2)
+
+
+# The setting of attributes is as for all tokens:
+#<Set attributes of token |$2|#>=
+ #<Set |this_mathness| etcetera to the defaults and those with no
+ defaults to |""| #>
+ this_name=tokenname[$2]
+ start_place=3
+ #<Scan this line from |start_place| to finish, looking for
+ \.{translation}$\ldots$ and putting results in
+ |this_translation|$\ldots$#>
+ #<Make sure |this_category| is not empty#>
+ tokencategory[$2]=this_category
+ tokenmathness[$2]=this_mathness
+ tokentranslation[$2]=this_translation
+ tokenname[$2]=this_name
+ tokentangleto[$2]=this_tangleto
+
+# We have to remember the length of the longest token so we can
+recognize long tokens before short ones.
+#<Update record of maximum token length#>=
+ temp = tokenlength[$2]
+ if (temp > maxtokenlength) {
+ maxtokenlength=temp
+ }
+
+# We're paranoid.
+#<Make sure token |$2| has a number if it needs one#>=
+ if (tokenlength[$2]>1 && tokennumbers[$2]=="") {
+ print "This can't happen: token", $2, "is long", \
+ "but has no number"
+ exitcode = -1
+ }
+
+
+#*1Writing {\tt WEB}'s lexical analysis code.
+The token recognition problem is the same for \.{WEAVE} and
+\.{TANGLE}.
+Both have routines called |get_next| that recognize the tokens on
+input.
+Most of |get_next| is prefabricated
+(and the same in both \.{WEAVE} and \.{TANGLE}),
+but we have to put in the part that recognizes multi-character
+non-alphanumeric tokens.
+
+We write the same code to both \.{WEAVE} and \.{TANGLE}.
+#<Write out...#>=
+ tempfile = scrapfile
+ #<Write token recognition code to |tempfile|#>
+ tempfile = ttokfile
+ #<Write token recognition code to |tempfile|#>
+
+# This is how we do it.
+#<Write token recognition code to |tempfile|#>=
+print "@ Here we input tokens of more than one character" > tempfile
+print "@<Compress two-symbol operator@>=" > tempfile
+#<Look for multicharacter tokens, starting with the longest,
+ and working down#>
+
+# We look for long tokens, then shorter, and so on.
+We have to make sure we don't look beyond the end of a line.
+#<Look for multicharacter tokens, starting with the longest,
+ and working down#>=
+ for (len=maxtokenlength; len>=2; len--) {
+ printf "if (loc+%d<=limit) {\n", len-1 > tempfile
+ #<Check for tokens in |tokentest| of length |len|#>
+ printf "\t}\n" > tempfile
+ }
+ #<Make sure there are no tokens of length 1 in |tokentest|#>
+
+# #<Check for tokens in |tokentest| of length |len|#>=
+notfirst=0
+for (t in tokentest) {
+ if (tokenlength[t]==len) {
+ printf "\t" > tempfile
+ if (notfirst==1) {
+ printf "else " > tempfile
+ }
+ notfirst=1
+ printf "if (%s) {\n", tokentest[t] > tempfile
+ printf "\t\tloc += %d;\n", len-1 > tempfile
+ printf "\t\treturn %s;\n\t\t}\n", tokenname[t] > tempfile
+ }
+ }
+
+
+# #<Make sure there are no tokens of length 1 in |tokentest|#>=
+for (t in tokentest) {
+ if (tokenlength[t]==1) {
+ print "This can't happen: token", t, "is of length 1 but", \
+ "it has a test"
+ exitcode=-1
+ }
+}
+
+
+
+#*1Writing out {\tt WEAVE}'s token-to-scrap code.
+Here is where we write the code that converts an already-recognized
+token (from |get_next|) into a scrap.
+There are several different kinds of tokens, and each requires a
+slightly different treatment.
+Will write out the code for the different species one at a time.
+#<Write out all...#>=
+ print "Writing out predefined scraps" > logfile
+ #<Write code for identifier scrap#>
+ #<Write code for string or constant scrap#>
+ #<Write code for newline scrap#>
+ #<Write code for |pseudo_semi| scrap#>
+ print "Writing out token scraps" > logfile
+ #<Write code for ordinary token scraps#>
+
+
+
+
+# This is how we write out the information for the identifier.
+#<Write code for identifier scrap#>=
+ if (id_category != "") {
+ print "@ @<Append an identifier scrap@>=" > scrapfile
+ print "p=id_lookup(id_first, id_loc,normal);" > scrapfile
+ print "if (p->ilk==normal) {" > scrapfile
+ print " small_app(id_flag+p-name_dir);" > scrapfile
+ printf " app_scrap(SP_%s,%s_math);", \
+ id_category, id_mathness > scrapfile
+ appended[id_category]=1
+ print " /* not a reserved word */" > scrapfile
+ print "}" > scrapfile
+ print "else if reserved(p) {" > scrapfile
+ print "@<Decide on reserved word scraps@>;" > scrapfile
+ print "}" > scrapfile
+ print "else {" > scrapfile
+ print " err_print(\"! Identifier with unmentioned ilk\");" > scrapfile
+ print "@.Identifier with unmentioned ilk@>" > scrapfile
+ print "}" > scrapfile
+ } else {
+ print "Error: I don't know what to do with an identifier"
+ print " Please give me a \"token identifier ...\""
+ exitcode = -1
+ }
+
+# We hold the name |"identifier"|, and we reserve a number for
+identifiers.
+#<Set initial...#>=
+ tokennumbers["identifier"]=tokennumber; tokennumber--
+ tokenname["identifier"]="identifier"
+
+# This is how we write out the string or constant scrap, at the end.
+#<Write code for string or constant scrap#>=
+print "Warning: TeX strings have the same category as ", \
+ "numeric constants in this WEAVE."
+print "Warning: TeX strings have the same category as ", \
+ "numeric constants in this WEAVE." > logfile
+if (number_category != "") {
+print "@ For some reason strings, constants,",\
+ " and \TeX\ strings are identified." > scrapfile
+print "That has to be fixed." > scrapfile
+print "@<Do the |app_scrap| for a string or constant@>=" > scrapfile
+printf "app_scrap(SP_%s,%s_math);\n", number_category,\
+ number_mathness > scrapfile
+appended[number_category]=1
+} else {
+ print "Error: I don't know what to do with a numeric constant"
+ print " Please give me a \"token number ...\""
+ exitcode = -1
+ }
+
+
+# We hold names and numbers for constants and strings, as well as identifiers.
+#<Set initial...#>=
+ tokennumbers["constant"]=tokennumber; tokennumber--
+ tokenname["constant"]="constant"
+ tokennumbers["string"]=tokennumber; tokennumber--
+ tokenname["string"]="string"
+
+
+#
+#<Write code for newline scrap#>=
+if (newline_category != "") {
+ print "@ @<Append a newline scrap@>=" > scrapfile
+ transstring=newline_translation
+ selfstring="small_app(next_control);"
+ wherestring="in translation of token newline"
+ append_keyword="small_app"
+ #<Take translation from |transstring| and write corresponding \.{WEAVE} code to
+ |outstring|, using |selfstring| as translation of |"<*>"|#>
+ print outstring > scrapfile
+ printf " app_scrap(SP_%s,%s_math);\n", newline_category,\
+ newline_mathness > scrapfile
+ appended[newline_category]=1
+} else {
+ print "Error: I don't know what to do with a newline"
+ print " Please give me a \"token newline ...\""
+ exitcode = -1
+ }
+
+#
+#<Write code for |pseudo_semi| scrap#>=
+if (pseudo_semi_category != "") {
+ print "@ @<Append a |pseudo_semi| scrap@>=" > scrapfile
+ transstring=pseudo_semi_translation
+ selfstring="small_app(next_control);"
+ wherestring="in translation of token pseudo_semi"
+ append_keyword="small_app"
+ #<Take translation from |transstring| and write corresponding \.{WEAVE} code to
+ |outstring|, using |selfstring| as translation of |"<*>"|#>
+ print outstring > scrapfile
+ printf " app_scrap(SP_%s,%s_math);\n", pseudo_semi_category,\
+ pseudo_semi_mathness > scrapfile
+ appended[pseudo_semi_category]=1
+} else {
+ printf "Error: I don't know what to do with a pseudo_semi (%s;)",\
+ substr(at_sign,1,1)
+ print " Please give me a \"token pseudo_semi ...\""
+ exitcode = -1
+ }
+
+# Here is how we write out the code that converts ordinary tokens to scraps:
+#<Write code for ordinary token scraps#>=
+print "@ @<Cases for ordinary tokens@>=" > scrapfile
+for (t in tokens) {
+ temp = tokenname[t]
+ printf "case %s:\n", temp > scrapfile
+ transstring=tokentranslation[t]
+ selfstring="small_app(next_control);"
+ wherestring= sprintf ("in translation of token %s", t)
+ append_keyword="small_app"
+ #<Take translation from |transstring| and write corresponding \.{WEAVE} code to
+ |outstring|, using |selfstring| as translation of |"<*>"|#>
+ print outstring > scrapfile
+ printf "\tapp_scrap(SP_%s,%s_math);\n", tokencategory[t], \
+ tokenmathness[t] > scrapfile
+ temp = tokencategory[t]
+ appended[temp]=1
+#^append check#>
+ print "\tbreak;" > scrapfile
+ }
+
+#*3{\tt TANGLE}'s token-to-output conversion.
+We have to write special cases for things appearing in |tokennumbers|.
+The output conventions for |string|, |constant| and |identifier| are
+fixed by \.{TANGLE}.
+
+One day we have to improve \.{TANGLE}'s treatment of spacing in the output;
+at the moment it just makes sure there are spaces between adjacent identifiers
+or numbers.
+#^future enhancements#>
+#<Write out...#>=
+print "@ @<Cases for tokens to be output@>=" > ttokfile
+for (t in tokennumbers) {
+ #<If |t| is |"string"|, |"constant"|, or |"identifier"|, just |continue|#>
+ printf "case %s:\n", tokenname[t] > ttokfile
+ this_tangleto = tokentangleto[t]
+ if (this_tangleto=="") {
+ printf "\tC_printf(\"%%s\",\"%s\");\n",t > ttokfile
+ } else {
+ printf "\tif (out_state==verbatim) {\n" > ttokfile
+ printf "\t\tC_printf(\"%%s\",\"%s\");\n",t > ttokfile
+ printf "\t} else {\n" > ttokfile
+ #<Write code to print |this_tangleto| onto |ttokfile|#>
+ printf "\t}\n" > ttokfile
+ }
+ print "\tif (out_state!=verbatim) out_state=misc;" > ttokfile
+ print "break;" > ttokfile
+ }
+
+# We also have to write something for the tokens that aren't in |tokennumbers|
+but which have a nonnull |tokentangleto| anyway.
+#<Write out...#>=
+print "@ @<Cases for tokens to be output@>=" > ttokfile
+for (t in tokentangleto) {
+ #<If |t| is |"string"|, |"constant"|, or |"identifier"|, just |continue|#>
+ if (tokennumbers[t]!="" || tokentangleto[t]=="")
+ continue
+ if (t=="@") {
+ thistangletokname = "@@"
+ } else if (t=="\\" || t=="'") {
+ thistangletokname = "\\" t
+ } else {
+ thistangletokname = t
+ }
+ printf "case '%s':\n", thistangletokname > ttokfile
+ this_tangleto = tokentangleto[t]
+ if (this_tangleto=="") {
+ print "This can't happen -- null tangleto for", t, wherestring
+ exitcode = -1
+ } else {
+ printf "\tif (out_state==verbatim) {\n" > ttokfile
+ printf "\t\tC_printf(\"%%s\",\"%s\");\n",t > ttokfile
+ printf "\t} else {\n" > ttokfile
+ #<Write code to print |this_tangleto| onto |ttokfile|#>
+ printf "\t}\n" > ttokfile
+ }
+ print "\tif (out_state!=verbatim) out_state=misc;" > ttokfile
+ print "break;" > ttokfile
+}
+# The tokens for |string|, |constant|, and |identifier| are treated
+specially by \.{TANGLE}; code to handle them already lives in \.{TANGLE}.web.
+Therefore, we don't gum up the works with our scheming.
+#<If |t| is |"string"|, |"constant"|, or |"identifier"|, just |continue|#>=
+if (t=="string"||t=="constant"||t=="identifier")
+ continue
+
+
+
+# This is somewhat like the translation code, but tuned for \.{TANGLE}
+#<Write code to print |this_tangleto| onto |ttokfile|#>=
+oldwherestring = wherestring
+wherestring = "for tangleto " wherestring
+#@
+transstring=this_tangleto
+#<Convert restricted translation in |transstring|
+ to quoted string in |outstring|#>
+printf "\tC_printf(\"%%s\",%s);\n",outstring > ttokfile
+#@
+wherestring=oldwherestring
+
+
+
+#*3Defining the token names.
+ At some point we'll have to define all these names, for both
+\.{TANGLE} and \.{WEAVE}. We may as well
+show how we do that now.
+#<Write out...#>=
+ tempfile = scrapfile
+ #<Write the definitions of the token names to |tempfile|#>
+ tempfile = ttokfile
+ #<Write the definitions of the token names to |tempfile|#>
+
+# We use an ugly trick to get the token numbers different for
+\.{WEAVE} and \.{TANGLE}:
+#<Write the definitions of the token names to |tempfile|#>=
+ print "@ Here are the definitions of the token names" > tempfile
+ for (t in tokennumbers) {
+ temp = tokennumbers[t]
+ if (temp==0)
+ continue ## don't know why we need this!!
+ if (tempfile==ttokfile) { ## output to \.{TANGLE}
+ #<If |t| is |"string"|, |"constant"|, or |"identifier"|,
+ just |continue|#> ## already defined in \.{TANGLE}
+ temp = temp + #'37 + 3 - highesttoken ## hackety hack!
+ ## +3 because three highest are already defined!
+ }
+ printf "@d %s = %s\n", tokenname[t], temp > tempfile
+ }
+
+
+# Some token names are just characters quoted with |'|. We write out
+all the others.
+#<Write lists...#>=
+ for (t in tokenname) {
+ temp = tokenname[t]
+ if (substr(temp,1,1) != "'") {
+ #<Strip opening |"SP_"| from |temp|, if it is there#>
+ print temp > tokennamefile
+ }
+ }
+
+# #<Strip opening |"SP_"| from |temp|, if it is there#>=
+tempa=substr(temp,1,3)
+if (tempa=="SP_") {
+ temp = substr(temp,4) ## remove |"SP_"|
+ }
+
+
+#*Reserved words and ilks.
+\.{TANGLE} doesn't even need the {\it idea} of
+ reserved words; it treats them like
+all other identifiers.
+\.{WEAVE}, however, needs to be able to recognize reserved words to do
+prettyprinting.
+\.{WEAVE} uses a two-tiered system for coping with reserved words.
+I think this system was really designed to make it easier to code
+\.{WEAVE} by hand, and is therefore not of much interest for
+\.{SPIDER}, but we retain it as a matter of least resistance.
+
+Every reserved word belongs to an ilk, and it is the ilks that, like
+tokens, have translations, categories, and so on.
+
+I have made a bewildering array of defaults that is probably full of
+bugs.
+We use a special convention to initialize the |this_| family.
+
+#<Pattern-act...#>=
+#=/^ilk /#> {
+ print "Ilk", $2 > logfile
+ #<Set |this_mathness| etcetera to the defaults and those with no
+ defaults to |""| #>
+ #<If no category is specified, invent a default if you can#>
+ this_name=""
+ start_place=3
+ #<Scan this line from |start_place| to finish, looking for
+ \.{translation}$\ldots$ and putting results in
+ |this_translation|$\ldots$#>
+ #<Make sure |this_category| is not empty#>
+ #<Make sure |this_name| is empty#>
+ ilk_category[$2]=this_category
+ ilk_mathness[$2]=this_mathness
+ ilk_translation[$2]=this_translation
+ next
+}
+
+
+# The pernicious option here is to be able to leave off the category, so
+that an item of ilk |fish_like| will get category |fish|.
+
+#<If no category is specified, invent a default if you can#>=
+ if ($2 ~ #=/^[a-zA-Z_]+_like$/#> && $0 !~ #=/ category /#>) {
+ ## give default category
+ this_category = substr($2,1,length($2)-5)
+ categories[this_category]=1
+ }
+
+# For the reserved words, our only option is to set an ilk.
+We go through wild and assuredly ill-advised gyrations attempting to
+set all the default properties of that ilk.
+If the ilk is omitted, we make a new ilk by attaching the string
+|"_like"| to the name of the reserved word.
+{\bf Don't use this feature; it embarrasses the author.}
+#^ill-advised#>
+#<Pattern-action...#>=
+#=/^reserved /#> {
+ print "Reserved word", $2 > logfile
+ if ($0 !~ #=/ ilk /#>) {
+ #<Attempt to make up an ilk, with all its defaults#>
+ }
+ for (i=3; i<=NF;) {
+ if ($i == "ilk") {
+ i++
+ reservedilk[$2]=$i
+ has_reserved[$i]=1 ## remember that ilk has some reserved word
+ i++
+ } else {
+ print "Error: bad reserved word attribute:", $i, \
+ "on line", NR-1
+ #<Punt...#>
+ }
+ }
+ #<Check that we used everything#>
+ next
+}
+
+# Here is our feeble attempt to make up an ilk for a reserved word for
+which no ilk is given.
+The default ilk for |"with"| is |"with_like"|, and so on.
+{\bf Please, please don't do this.}
+#<Attempt to make up an ilk, with all its defaults#>=
+ temp = $2 "_like"
+ reservedilk[$2]=temp
+ if (ilk_translation[temp]=="") {
+ ilk_translation[temp]=default_translation
+ }
+ has_reserved[temp]=1
+ if (ilk_mathness[temp]=="") {
+ ilk_mathness[temp]=default_mathness
+ }
+ ## and default category for that ilk is the resword itself
+ if (ilk_category[temp]=="") {
+ ilk_category[temp]=$2
+ categories[$2]=1
+ }
+ ilk_is_made_up[temp]=1 ## we really should do something with this
+#^mistakes#>
+
+
+#*1Telling {\tt WEAVE} how to recognize reserved words.
+At the end, we'll write out definitions for the ilk names, and we'll
+write translations of all the ilks.
+#<Write out all...#>=
+print "Writing out reserved words and ilks" > logfile
+ilkno=64
+print "@ Here is a list of all the ilks" > reserved
+for (i in ilk_translation) {
+ printf "@d SP_%s = %d\n", i, ilkno > reserved
+ ilkno++
+ }
+
+# Here is where we write the code that converts reserved word tokens
+into scraps.
+#<Write out all...#>=
+print " " > reserved
+print "@ Here are the scraps we get from the reserved words" > reserved
+print "@d the_word = res_flag+p-name_dir" > reserved
+print "@<Decide on reserved word scraps@>=" > reserved
+print "switch (p->ilk) {" > reserved
+for (t in ilk_translation) {
+ printf "\tcase SP_%s: \n\t\t", t > reserved
+ transstring=ilk_translation[t]
+ selfstring="small_app(the_word);"
+ wherestring= sprintf ("in translation of ilk %s", t)
+ append_keyword="small_app"
+ #<Take translation from |transstring| and
+ write corresponding \.{WEAVE} code to
+ |outstring|, using |selfstring| as translation of |"<*>"|#>
+ if (trcnt>0) ## at least one text in the translation
+ has_translation[t]=1
+ print outstring > reserved
+ printf "\tapp_scrap(SP_%s,%s_math);\n", ilk_category[t], \
+ ilk_mathness[t] > reserved
+ temp=ilk_category[t]
+ appended[temp]=1
+#^append check#>
+ printf "\t\tbreak;\n" > reserved
+ }
+print "}" > reserved
+
+
+# At the end, we'll have to enter each reserved word in the identifier
+table, along with its ilk.
+#<Write out all...#>=
+print "@ @<Store all the reserved words@>=" > reserved
+for (i in reservedilk) {
+ printf "id_lookup(\"%s\",NULL,SP_%s);\n", i, reservedilk[i] > reserved
+}
+
+# At the very end, we'll make sure every ilk has both a reserved word
+and some translation.
+{\bf Perhaps this could be cleaned up a bit?}
+#<Check for errors at...#>=
+ for (i in ilk_translation) {
+ if (has_reserved[i] != 1) {
+ print "Error: there is no reserved word of ilk", i
+ exitcode=-1
+ }
+ if (has_translation[i] != 1) {
+ print "Error: ilk", i, "has no translation"
+ exitcode=-1
+ }
+ }
+
+# #<Write lists...#>=
+ for (i in ilk_translation) {
+ print i > ilkfile
+ }
+
+# #<Write stat...#>=
+for (i in ilk_translation) number_of_ilks++
+for (i in reservedilk) number_of_reserved_words++
+printf "You defined %d reserved words of %d ilks.\n", \
+ number_of_reserved_words, number_of_ilks
+printf "You defined %d reserved words of %d ilks.\n", \
+ number_of_reserved_words, number_of_ilks > logfile
+
+#*The prettyprinting grammar.
+The most intricate part of \.{WEAVE} is its mechanism for converting
+programming language code into \TeX\ code.
+A ``bottom up'' approach is used to parse the
+programming language material, since \.{WEAVE} must deal with fragmentary
+constructions whose overall ``part of speech'' is not known.
+
+At the lowest level, the input is represented as a sequence of entities
+that we shall call {\it scraps}, where each scrap of information consists
+of two parts, its {\it category} and its {\it translation}. The category
+is essentially a syntactic class, and the translation is a token list that
+represents \TeX\ code. Rules of syntax and semantics tell us how to
+combine adjacent scraps into larger ones, and if we are lucky an entire
+program text that starts out as hundreds of small scraps will join
+together into one gigantic scrap whose translation is the desired \TeX\
+code. If we are unlucky, we will be left with several scraps that don't
+combine; their translations will simply be output, one by one.
+
+The combination rules are given as context-sensitive productions that are
+applied from left to right. Suppose that we are currently working on the
+sequence of scraps $s_1\,s_2\ldots s_n$. We try first to find the longest
+production that applies to an initial substring $s_1\,s_2\ldots\,$; but if
+no such productions exist, we find to find the longest production
+applicable to the next substring $s_2\,s_3\ldots\,$; and if that fails, we
+try to match $s_3\,s_4\ldots\,$, etc.
+
+A production applies if the category codes have a given pattern. For
+example, one of the productions is
+$$\hbox{\.{open [ math semi <\.{"\\\\,"}-opt-5> ] -->
+open math}}$$
+and it means that three consecutive scraps whose respective categories are
+|open|, |math|, and |semi| are con\-verted to two scraps whose categories
+are |open| and |math|.
+ The |open| scrap has not changed, while the string \.{<"\\\\,"-opt-5>}
+indicates that the new |math| scrap
+has a translation composed of the translation of the original
+|math| scrap followed by the translation of the |semi| scrap followed
+by `\.{\\,}' followed by `|opt|' followed by `\.5'. (In the \TeX\ file,
+this will specify an additional thin space after the semicolon, followed
+by an optional line break with penalty 50.)
+
+Their is an extensive discussion of the grammar, with examples, in the
+``Spider User's Guide.''
+Y'oughta read it.
+
+#*1Scanning a production.
+ A production in the grammar is written as a sequence of category
+names and translations, followed by a right arrow (\.{-->}), followed
+by a category name.
+When \.{WEAVE} is scanning the sequence of scraps that makes up a
+module, it checks to see whether the categories of those scraps match
+the categories given on the left side of the production.
+If so, the production fires, and the scraps and translations on the
+left side of the arrow are combined into a single, new scrap, and the
+new scrap is given the category from the right side of the arrow.
+The scraps which are combined are called the firing scraps,
+#^firing scraps#>
+and the category given to the combination is called the target category.
+
+Instead of a category name, e.g.~``\.{math},'' one can write a list of
+category names, e.g.~``\.{(open\vert lsquare)}'' instead.
+A scrap matches the list if and only if its category is one of the
+names listed.
+One can also use the wildcard ``\.?'', which any scrap matches.
+
+On the right-hand side, one can write a \## followed by a number in
+place of the target category name.
+If we specify the target category as ``\.{\##2}'', for example, it
+means ``give the new scrap the same category as the second scrap that
+matched the left side of the production.''
+
+# Here is the whole syntax as quoted from the ``Spider User's Guide''
+
+\begingroup\def\\{\par\noindent\ignorespaces}\tt
+\noindent\syntax{production} \produces\\\quad
+\syntax{left context} [ \syntax{firing instructions} ] \syntax{right context}
+--> \syntax{left context} \syntax{target category} \syntax{right
+context}\\
+\syntax{left context} \produces~\syntax{scrap designators}\\
+\syntax{right context} \produces~\syntax{scrap designators}\\
+\syntax{firing instruction} \produces \syntax{scrap designator}\\
+\syntax{firing instruction} \produces \syntax{translation}\\
+\syntax{scrap designator} \produces~?\\
+\syntax{scrap designator} \produces~\opt{!}\syntax{marked category}\\
+\syntax{scrap designator} \produces~\opt{!}\syntax{category alternatives}\\
+\syntax{category alternatives} \produces~(\syntax{optional
+alternatives}\syntax{marked category})\\
+\syntax{optional alternative} \produces~\syntax{marked category}\vert\\
+\syntax{marked category} \produces~\syntax{category name}\opt{*}\\
+\syntax{target category} \produces~\#\syntax{integer}\\
+\syntax{target category} \produces~\syntax{category name}\\
+\endgroup
+
+# Here is the pattern that reads productions.
+In most of the modules below, we read through some of the fields of the
+production.
+We use |i| to remember what field we are about to examine.
+When a module terminates, |$i| is left pointing to the first field of
+interest to the next module.
+#<Production patt...#>=
+#=/-->/#> {
+ #<Set up to parse this production#>
+ #<Read through the fields of the production, up to the arrow#>
+ #<Set |lowpos|, |highpos|, and |arrowpos| to their proper values#>
+ #<Update |highestposoverall|#>
+ #<Update |highestunknownpos|#>
+ #<Check to see that left context matches#>
+ #<Process scrap to which we are translating#>
+ #<Check to see that right context matches#>
+ #<Check to make sure we used all the fields of the production#>
+ #<Compute the appropriate test for this production and put it
+ in |prodtest[prodnum]|#>
+ #<Compute the code to write the new translation, should this
+ production fire, and put it in |prodtrans[prodnum]|#>
+ #<Write the start token in |ppstart[prodnum]| and the number
+ of tokens reduced in |tokensreduced[prodnum]|#>
+ #<If we only reduced one token, write the reduction
+ out to file |cycles| for later testing#>
+ next
+} ## \.{/-->/}
+
+# Each scrap in the production will be given a position |pos|,
+beginning with 1. (Using 1 and not 0 lets us make good use of the fact
+that uninitialized AWK variables will have value zero.)
+We will remember the positions of the scraps that get reduced; they
+will be from |lowpos| to |highpos-1|.
+We keep track of the production number in |prodnum|, and we save a
+copy of the input line in |inputline[prodnum]|.
+#<Set up to parse this production#>=
+ lowpos=0; highpos=0; pos=1
+ prodnum=prodnum+1
+ inputline[prodnum]=$0
+ print "Parsing production", prodnum, $0 > logfile
+
+
+# This is the guts of the parsing. We have to read each field in the
+production, determine whether it is category or translation
+information, and act accordingly.
+Each scrap will be given a position |pos|.
+We will write in |test[pos]| the code needed to decide whether a
+particular scrap matches the pattern given in the production.
+Scraps can match a single category by name, a list of categories, or
+|"?"|, which every scrap matches.
+Categories can be starred, in which case we underline the index entry
+of the first identifier in the scrap's translation.
+
+We also write in |trans[pos]| the code necessary to produce the
+translations preceding the scrap at |pos|.
+
+#<Read through the fields...#>=
+trans[pos]=""
+for (i=1; i<=NF; i++) {
+ if ($i ~ #=/<.*>/#>) { ## should be |trans_pattern|
+ #<Process a translation in |$i|#>
+ } else if ($i ~ #=/^!?[a-zA-Z_]+(\*\*?)?$/#>) { ## |cat_pattern|
+ #<Process a single category#>
+ } else if ($i ~ #=/^!?\(([a-zA-Z_]+\|)*[a-zA-Z_]+\)(\*\*?)?$/#>){
+ #<Process a list of alternative categories#>
+ } else if ($i == "?") {
+ #<Process a category wild card#>
+ } else if ($i == "[") {
+ lowpos=pos
+ } else if ($i == "]") {
+ highpos=pos
+ } else if ($i=="-->") {
+ break
+ } else { ## we don't recognize the field
+ print "Error: bad field is", $i, "in production on line", NR-1
+ #<Forget this production#>
+ }
+}
+i++
+
+
+# When we find a mistake, we just abandon the current production.
+Decrementing |prodnum| will make it as if this production never happened.
+#<Forget this production#>=
+ prodnum--
+ #<Punt this...#>
+
+# We process the translation and add the result to the current
+translation for |pos|.
+#<Process a translation...#>=
+transstring=$i
+selfstring="" ## senseless for productions
+wherestring= sprintf ("in production on line %d", NR-1)
+append_keyword="app"
+#<Take translation from |transstring| and write corresponding \.{WEAVE} code to
+|outstring|, using |selfstring| as translation of |"<*>"|#>
+trans[pos]=trans[pos] outstring
+
+# Here we'll set |test[pos]|.
+The phrase |test[pos]| will be a single C conjunct; if the test for
+each scrap is true, the whole production will fire.
+If we're called upon to make a scrap underlined or reserved, we'll add
+to |trans[pos]|.
+
+If a category is negated we add an extra clause to make
+sure nothing matches the zero category, since {\tt WEAVE} assumes
+no production ever matches a scrap with category zero.
+#<Process a single category#>=
+ field[pos]=$i ## save this field to compare RHS
+ #<Set |negation|, and remove leading |"!"| from |$i| if necessary#>
+ #<Strip stars from |$i| (if any) and add appropriate
+ translations to |trans[pos]|#>
+ cat = $i
+ categories[cat]=1 ## remember |cat| is a category
+ if (negation==0) {
+ test[pos]=sprintf("(pp+%d)->cat==SP_%s",pos-1,cat)
+ } else {
+ test[pos]=sprintf("((pp+%d)->cat!=SP_%s && (pp+%d)->cat != 0)",\
+ pos-1,cat,pos-1)
+ }
+ #<Update the record of the rightmost occurrence of category |cat|#>
+ #<Advance |pos|, making the new |trans[pos]| empty#>
+
+# The list of categories is enclosed in parentheses and the individual
+categories are separated by vertical bars.
+We have to make the test for these things a disjunction, but
+processing is more or less like the processing for a single category.
+
+If a list of alternatives is negated we add an extra clause to make
+sure nothing matches the zero category, since {\tt WEAVE} assumes
+no production ever matches a scrap with category zero.
+#<Process a list of alternative categories#>=
+ field[pos]=$i ## save this field to compare RHS
+ #<Set |negation|, and remove leading |"!"| from |$i| if necessary#>
+ if (negation==0) {
+ test[pos]="(" ## open for a list of good alternatives
+ } else {
+ temp=sprintf("(pp+%d)->cat==0",pos-1)
+ test[pos]="!(" temp "||" ## open for a list of bad alternatives
+ }
+ #<Strip stars from |$i| (if any) and add appropriate
+ translations to |trans[pos]|#>
+ temp = substr($i,2,length($i)-2) ## throw out parens
+ m = split(temp,tok,"|")
+ for (j=1;j<=m;j++) {
+ cat = tok[j]
+ categories[cat]=1 ## remember it's a category
+ #<Update the record of the rightmost occurrence of
+ category |cat|#>
+ temp=sprintf("(pp+%d)->cat==SP_%s",pos-1,cat)
+ test[pos]=test[pos] temp ## add alternative to test
+ if (j!=m)
+ test[pos]=test[pos] "||\n" ## line too long errors
+ }
+ test[pos]= test[pos] ")"
+ #<Advance |pos|, making the new |trans[pos]| empty#>
+
+
+# We keep track of the rightmost occurrence of each category.
+This enables us to backtrack by exactly the right amount when a
+production fires and creates a new scrap.
+#<Update the record of the rightmost occurrence of category |cat|#>=
+ if (pos > highestpos[cat]) {
+ highestpos[cat]=pos
+ }
+
+# If a category or lsit of alternatives is preceded by an exclamation
+point (|"!"|), we set |negation|, and we will test for scraps that are
+{\it not} of that category or are {\it not} of one of the categories
+listed.
+#<Set |negation|...#>=
+temp = substr($i,1,1)
+if (temp=="!") {
+ negation = 1
+ $i = substr($i,2)
+} else {
+ negation = 0
+}
+
+# Since both translations and tokens can add to |trans[pos]| we must
+make sure it is empty whenever we get a new |pos|.
+This device makes that easy.
+
+#<Advance |pos|, making the new |trans[pos]| empty#>=
+ pos=pos+1
+ trans[pos]=""
+
+# If a category is single-starred, we take this construct to be the
+{\it definition} of that item, and we underline the index entry for
+this module.
+The |make_underlined| routine finds the first identifier in the
+translation of the starred scrap, and underlines the index entry for
+that identifier in this module.
+
+If a category is double-starred, we used to try to change the ilk of the
+appropriate identifier to make it a reserved word.
+The only use this ever had was in handling C typedefs, and it should
+probably be removed.
+#^mistakes#>
+In the meanwhile, double starring is like single starring.
+
+#<Strip stars from |$i| (if any) and add appropriate
+ translations to |trans[pos]|#>=
+if ($i ~ #=/^([a-zA-Z_]+|\(([a-zA-Z_]+\|)*[a-zA-Z_]+\))\*\*$/#>) { ## it's double-starred
+ temp = sprintf("\tmake_underlined(pp+%d);\n",pos-1)
+ trans[pos] = trans[pos] temp
+ $i = substr($i,1,length($i)-2)
+} else if ($i ~ #=/^([a-zA-Z_]+|\(([a-zA-Z_]+\|)*[a-zA-Z_]+\))\*$/#>) { ## it's starred
+ temp = sprintf("\tmake_underlined(pp+%d);\n",pos-1)
+ trans[pos] = trans[pos] temp
+ $i = substr($i,1,length($i)-1)
+} else if ($i ~ #=/\*$/#>) { ## a bad star?
+ print "Error: can't remove stars in production on line", NR-1
+ #<Forget this production#>
+}
+
+# Wild cards are easy to process, but we do have to remember that
+not even a wild card matches a scrap of category zero.
+#<Process a category wild card#>=
+ field[pos]=$i ## save this field to compare RHS
+ test[pos]=sprintf("(pp+%d)->cat!=0",pos-1) ## anything nonzero matches
+ highwildcard=pos ## we don't really need this?
+ #<Advance |pos|, making the new |trans[pos]| empty#>
+
+
+
+#
+We reach this point in the program after we will have read the arrow
+into |$i|.
+
+This module establishes in what ranges of |pos| the contexts fall:
+$$\vbox{\halign{##\hfil\tabskip1em&\hfil##\hfil\cr
+\bf Items&\bf Range\cr
+\noalign{\vskip2pt}
+left context&|1..lowpos-1|\cr
+firing instructions&|lowpos..highpos-1|\cr
+right context&|highpos..arrowpos-1|\cr
+}}$$
+If |lowpos| and |highpos| haven't been set by the appearance of square
+brackets, we set them to make the contexts empty.
+None or both should be set.
+
+
+#<Set |lowpos|, |highpos|, and |arrowpos| to their proper values#>=
+ arrowpos=pos
+ if (lowpos==0 && highpos==0) {
+ lowpos=1 ## first transform position
+ highpos=arrowpos ## first token not reduced
+ ## (or one beyond last token position)
+ } else if (lowpos==0 || highpos==0) {
+ print "Error: square brackets don't balance in", \
+ "production on line", NR-1
+ #<Forget this production#>
+ }
+
+# Here is the efficient place to update the rightmost (highest)
+position of {\it any} category.
+#<Update |highestposoverall|#>=
+ if (arrowpos-1 > highestposoverall) {
+ highestposoverall=arrowpos-1
+ }
+
+# Dealing with grammars in which categories can be unnamed (using
+wildcards or negation) can be a pain in the ass.
+What we have to do, when reducing after firing a production, is move
+backwards enough so that we don't miss any earlier productions that
+are supposed to fire.
+This means we have to move back at least far enough so that the new
+scrap will match any unnamed category.
+{\bf But} we don't have to worry about wildcards (|"?"|) at the end of
+a production, because they would have matched anyway, even before the
+current production fired. Hence:
+#<Update |highestunknownpos|#>=
+for (hup=arrowpos-1; field[hup]=="?";) {
+ hup--
+ }
+for (;hup>highestunknownpos;hup--) {
+ temp=field[hup]
+ temp=substr(temp,1,1)
+ if (temp=="?" || temp =="!") {
+ highestunknownpos=hup ## we know |hup>highestunknownpos|
+ break ## redundant, since test will fail
+ }
+}
+
+# Here is the error checking for context sensitive productions.
+#<Check to see that left context matches#>=
+ for (pos=1;pos<lowpos;pos++) {
+ #<Check |$i| against |field[pos]|#>
+ i++
+ }
+
+# #<Check to see that right context matches#>=
+ for (pos=highpos;pos<arrowpos;pos++) {
+ #<Check |$i| against |field[pos]|#>
+ i++
+ }
+# #<Check |$i| against |field[pos]|#>=
+ if (i>NF || $i != field[pos]) {
+ print "Error: token mismatch is: found", $i, \
+ "sought", field[pos], "on line", NR-1
+ #<Forget this...#>
+ }
+
+# We process our target scrap in between checking the left and right
+contexts.
+This scrap can be the name of a category, or it can be ``$\##nnn$'',
+where $nnn$ refers to the number of a category on the left side of the
+arrow.
+In this way it is possible to match wildcards and lists of alternatives.
+#<Process scrap to which we are translating#>=
+ ## i points to the target category
+ if (i>NF) {
+ print "Error: no target category in production on line", NR-1
+ #<Forget this...#>
+ }
+ if ($i ~ #=/##[0-9]+/#>) { ## a number
+ $i = substr($i,2) ## peel off the \##
+ #<Make sure |1 <= $i < arrowpos|#>
+ targetcategory[prodnum]="Unnamed category"
+ temp = sprintf("(pp+%d)->cat", $i-1)
+ unnamed_cat[prodnum]=temp
+ } else if ($i ~ #=/[a-zA-Z][a-zA-Z_]*/#>) { ## a category
+ targetcategory[prodnum]=$i
+ categories[$i]=1 ## remember this is a category
+ } else {
+ print "Error: unrecognizable target token", $i, \
+ "in production on line", NR-1
+ #<Forget this...#>
+ }
+ i++
+
+# We call this at the end to make sure there aren't unused fields left over
+#<Check to make sure we used all the fields of the production#>=
+ if (i<=NF) {
+ print "Error: used only " i-1 " of " NF " tokens", \
+ "in production on line", NR-1
+ #<Forget this...#>
+ }
+
+# After having vetted the whole production, we combine the tests and
+translations for each |pos|.
+#<Compute the appropriate test for this production and put it
+ in |prodtest[prodnum]|#>=
+ prodtest[prodnum]=""
+ for (pos=1;pos<arrowpos;pos++) {
+ if (pos>1) {
+ prodtest[prodnum]=prodtest[prodnum] " &&\n\t\t"
+ }
+ prodtest[prodnum]=prodtest[prodnum] test[pos]
+ }
+
+# #<Compute the code to write the new translation, should this
+ production fire, and put it in |prodtrans[prodnum]|#>=
+ prodtrans[prodnum]=""
+ for (pos=lowpos;pos<highpos;pos++) {
+ prodtrans[prodnum]=prodtrans[prodnum] trans[pos]
+ ## add code to append this scrap
+ temp = sprintf("\tapp1(pp+%d);\n",pos-1)
+ prodtrans[prodnum]=prodtrans[prodnum] temp
+ #<If not negated, record the fact that a token of
+ category satisfying |test[pos]| could have
+ been reduced#>
+ }
+ prodtrans[prodnum]=prodtrans[prodnum] trans[highpos]
+
+# #<Write the start token in |ppstart[prodnum]| and the number
+ of tokens reduced in |tokensreduced[prodnum]|#>=
+ ppstart[prodnum]=lowpos-1
+ tokensreduced[prodnum]=highpos-lowpos
+
+# #<If we only reduced one token, write the reduction
+ out to file |cycles| for later testing#>=
+ if (highpos-lowpos==1) {
+ printf "%d: %s --> %s\n", prodnum, field[lowpos], \
+ targetcategory[prodnum] > cycles
+ wrotecycles = 1
+ }
+
+# If we never even had the possibility of a cycle, we still have to write
+out a dummy file so the cycle checker in the Makefile won't barf.
+# #<Write lists of everything#>=
+if(wrotecycles==0) {
+ print "0: dummy --> nodummy" > cycles
+ }
+
+# For error checking, we keep track of categories that get reduced in
+productions.
+We can't do this while scanning the production, because we don't know
+at the beginning what |lowpos| will be, since we might or might not
+ever see a left square bracket.
+
+If a particular category is never reduced, that merits a warning later on.
+#<If not negated, record the fact that a token of category satisfying
+ |test[pos]| could have been reduced#>=
+temp = field[pos]
+tempa = substr(temp,1,1)
+if (tempa != "!") {
+ if (temp ~ #=/^\(([a-zA-Z_]+\|)*[a-zA-Z_]+\)(\*\*?)?$/#>) {
+ ## list of alternatives
+ #<Remove trailing stars from |temp|#>
+ temp = substr(temp,2,length(temp)-2)
+ m = split(temp,tok,"|")
+ for (j=1;j<=m;j++) {
+ alternate = tok[j]
+ reduced[alternate]=1
+ }
+ } else if (temp ~ #=/^[a-zA-Z_]+(\*\*?)?$/#>) {
+ #<Remove trailing stars from |temp|#>
+ reduced[temp]=1
+ } else if (temp != "?") {
+ print "Confusion: unintelligible field[pos]:", temp, \
+ "in production on line", NR-1
+ #<Forget this...#>
+ }
+}
+
+# #<Remove trailing...#>=
+while (temp ~ #=/\*$/#>) {
+ temp = substr(temp,1,length(temp)-1)
+}
+
+# #<Check for err...#>=
+for (c in categories) {
+ if (reduced[c] != 1) {
+ print "Warning: category", c, "never reduced"
+ }
+}
+
+
+# Here's a check for the target token number
+#<Make sure |1 <= $i < arrowpos|#>=
+if ((0+$i)<1 || (0+$i)>=0+arrowpos) {
+ print "Error: can't take token number", $i, "of", arrowpos-1, \
+ "tokens", "in production on line", NR-1
+ #<Forget this...#>
+ }
+
+#*1Writing the scrap reduction code.
+Before writing the grammar, we want to define all of the category codes.
+#<Write out...#>=
+ print "Writing out category codes" > logfile
+ print "@ Here is a list of category codes scraps can have" > grammarfile
+ i=1
+ for (t in categories) {
+ printf "@d SP_%s = %d\n",t,i > grammarfile
+ i++
+ }
+ print "@c" > grammarfile
+# We also want to make sure we can print the names of categories in
+case we need to debug.
+#<Write out...#>=
+ print "##ifdef DEBUG" > grammarfile
+ print "##define PRINT_CAT(A,B) case A: printf(B); break" > grammarfile
+ print "print_cat(c) /* symbolic printout of a category */" > grammarfile
+ print "eight_bits c;" > grammarfile
+ print "{" > grammarfile
+ print " switch(c) {" > grammarfile
+ for (t in categories) {
+ printf "PRINT_CAT(SP_%s,\"%s\");\n",t,t > grammarfile
+ }
+ print " case 0: printf(\"zero\"); break;" > grammarfile
+ print " default: printf(\"UNKNOWN\"); break;" > grammarfile
+ print " }" > grammarfile
+ print "}" > grammarfile
+ print "##endif DEBUG" > grammarfile
+ print " " > grammarfile
+
+# And there goes the list...
+#<Write lists...#>=
+ for (c in categories) {
+ print c > categoryfile
+ }
+
+# #<Write stat...#>=
+for (c in categories) {
+ number_of_categories++
+}
+printf "You used %d different categories in %d productions.\n", \
+ number_of_categories, prodnum
+printf "You used %d different categories in %d productions.\n", \
+ number_of_categories, prodnum > logfile
+printf "The biggest production had %d scraps on its left-hand side.\n", \
+ highestposoverall
+printf "The biggest production had %d scraps on its left-hand side.\n", \
+ highestposoverall > logfile
+
+
+# We will write a list of the successfully parsed productions to a
+separate file.
+The list will include
+ production numbers, to which the user can refer
+when debugging.
+#<Write lists...#>=
+for (n=1; n<= prodnum; n++) {
+ printf "%2d: %s\n",n,inputline[n] > productions
+ }
+
+# Finally, we write out the code for all of the productions.
+Here is our first view of category checking: we want to make sure that
+each category can be appended, either by |app_scrap| or by |reduce|.
+We also want to make sure each category can be reduced by firing some
+production.
+We track these things using the arrays |appended| and |reduced|.
+
+We write the definition of |highestposoverall|, for safety.
+
+We used to write this code as a very deeply nested if-then-else,
+but that caused a yacc overflow in the generated code for C~{\tt WEAVE}.
+So now we write
+{\tt if (...) \LB...; goto end\_prods;\RB}
+#<Write out...#>=
+print "Writing out grammar" > logfile
+print "@ Here is where we define |highestposoverall| and where we" > grammarfile
+print "check the productions." > grammarfile
+print "@d highestposoverall =", highestposoverall > grammarfile
+print "@<Test for all of the productions@>=" > grammarfile
+for (n=1; n<=prodnum; n++) {
+ if (n%5==0)
+ print "@ @<Test for all of the productions@>=" \
+ > grammarfile ## avoids overflowing \.{WEAVE} of \.{WEAVE}
+ #<Change \vert,\_, and {\tt \##} in |inputline[n]|; put results in |this_string|#>
+ #<Make |this_string| no more than 60 characters wide#>
+ printf "if (%s) {\n\t/* %d: {\\tt %s} */\n%s",\
+ prodtest[n],n,this_string,prodtrans[n] > grammarfile
+ #<Write the |reduce| call, taking note of whether the
+ category is named#>
+ print "\tgoto end_prods;" > grammarfile
+ printf "} " > grammarfile
+ }
+printf "\n" > grammarfile
+print "end_prods:" > grammarfile
+
+# We do different things for a category that is unnamed.
+#<Write the |reduce| call, taking note of whether the category is named#>=
+ ttk=targetcategory[n]
+ if (ttk == "Unnamed category") {
+#^append check#>
+ printf "\treduce(pp+%d,%d,%s,%d,%d);\n",ppstart[n],\
+ tokensreduced[n],unnamed_cat[n],\
+ 1-highestposoverall,n > grammarfile
+ } else {
+ appended[ttk]=1 ## remember we appended this token
+#^append check#>
+ reduction=highestpos[ttk]
+ if (reduction<highestunknownpos) {
+ reduction = highestunknownpos
+ }
+ printf "\treduce(pp+%d,%d,SP_%s,%d,%d);\n",ppstart[n],\
+ tokensreduced[n],targetcategory[n],\
+ 1-reduction,n > grammarfile
+ }
+
+# This is the place we check for errors.
+#^append check#>
+#^reduce check#>
+#<Check for errors...#>=
+for (c in categories) {
+ if (appended[c] != 1) {
+ if (c=="ignore_scrap") { ## appended by \.{WEAVE}
+ print "Warning: category", c, "never appended"
+ } else {
+ print "Error: category", c, "never appended"
+ exitcode=-1
+ }
+ }
+ }
+
+
+
+# It's desirable to put the production in a comment, but we have to
+get rid of the confusing \vert, or \.{WEAVE} will think it introduces
+code.
+We also have to escape underscores and sharp signs, otherwise \TeX\ will
+think we want math mode.
+#<Change \vert,\_, and {\tt \##} in |inputline[n]|; put results in |this_string|#>=
+ this_string = inputline[n]
+ tempi = index(this_string,"|")
+ while (tempi != 0) {
+ tempa = substr(this_string,1,tempi-1)
+ tempb = substr(this_string,tempi+1)
+ this_string = tempa "\\vert " tempb
+ tempi = index(this_string,"|")
+ }
+ templ = ""; tempr = this_string
+ tempi = index(tempr,"_")
+ while (tempi != 0) {
+ tempa = substr(tempr,1,tempi-1)
+ tempr = substr(tempr,tempi+1)
+ templ = templ tempa "\\_"
+ tempi = index(tempr,"_")
+ }
+ this_string = templ tempr
+ templ = ""; tempr = this_string
+ tempi = index(tempr,"##")
+ while (tempi != 0) {
+ tempa = substr(tempr,1,tempi-1)
+ tempr = substr(tempr,tempi+1)
+ templ = templ tempa "\\##"
+ tempi = index(tempr,"##")
+ }
+ this_string = templ tempr
+
+
+# We have to keep these productions from making an input line too long.
+#<Make |this_string| no more than 60 characters wide#>=
+toolong=this_string; this_string=""
+while (length(toolong)>60) {
+ idx=59
+ idchar = substr(toolong,idx,1)
+ while (idx>1 && idchar!=" ") {
+ idx--
+ idchar = substr(toolong,idx,1)
+ }
+ if (idx==1)
+ idx=59
+ temp = substr(toolong,1,idx-1)
+ toolong = substr(toolong,idx+1)
+ this_string = this_string temp "\n"
+}
+this_string = this_string toolong
+
+
+
+#*The rest of {\tt SPIDER}.
+We present the remaining features of \.{SPIDER} in them order we
+used in the ``\.{SPIDER} User's Guide.''
+#*2 Naming the target language.
+\.{SPIDER} is designed to help you build a \.{WEB} system for any
+programming language.
+We need to know the name of the language, and what extension to
+use when writing the tangled unnamed module.
+We use this information to pick a name for the file that will hold
+this \.{WEB}'s special \TeX{} macros, and we write |"\\input webkernel"|
+on that file.
+#<Patt...#>=
+#=/^language /#> {
+ language = $2
+ extension=language
+ for (i=3; i<NF; ) {
+ if ($i=="extension") {
+ i++
+ extension=$i
+ i++
+ } else if ($i=="version") {
+ i++
+ version=$i
+ i++
+ } else {
+ print "Error: unknown language property", $i,\
+ "on line", NR-1
+ #<Punt...#>
+ }
+ }
+ #<Check that we used everything#>
+ #<Write the first line of the macro file#>
+ next
+}
+
+# #<Write out...#>=
+if (language != "") {
+ print "@ Here is the language-dependent stuff" > tlang
+ if (version!="")
+ version = ", Version " version
+ printf "@d banner = \"This is %s TANGLE%s %s\\n\"\n", language, \
+ version, date > tlang
+ printf "@<Global...@>=char C_file_extension[]=\"%s\";\n", extension \
+ > tlang
+#@
+ print "@ Here is the language-dependent stuff" > wlang
+ if (version!="")
+ version = ", Version " version
+ printf "@d banner = \"This is %s WEAVE%s %s\\n\"\n", language, \
+ version, date > wlang
+ print "@<Set |out_ptr| and do a |tex_printf| to read the macros@>=" \
+ > wlang
+ printf "*out_ptr='x'; tex_printf(\"\\\\input %sweb.te\");\n", \
+ extension > wlang
+ printf "@ @<Global...@>=char C_file_extension[]=\"%s\";\n", extension \
+ > wlang
+} else {
+ print "Error: you haven't given me any \"language\" information"
+ exitcode=-1
+}
+
+#*1Defining {\TeX} macros.
+The first thing we do after getting the language is write a line to
+the macro file.
+This makes sure the kernel \.{WEB} macros will be available.
+#<Write the first line of the macro file#>=
+ macrofile = extension "web.tex"
+ print "\\input webkernel.tex" > macrofile
+
+
+# Processing macros is straightforward: everything between \.{macros
+begin} and \.{macros end} gets copied into the macro file.
+#<Patt...#>=
+#=/^macros begin$/,/^macros end$/#> {
+ if (begunmacs==0) {
+ begunmacs=1
+ next
+ }
+ if ($0 ~ #=/^macros end$/#>) {
+ begunmacs=0
+ next
+ }
+ if (macrofile=="") {
+ if (complained==0) {
+ print "Error: you must give \"language\"",\
+ "before \"macros\""
+ complained=1
+ #<Punt...#>
+ }
+ } else {
+ print $0 > macrofile
+ }
+ next
+}
+
+
+
+#*1Handling modules.
+We need to give module names a category, both when we define modules
+and when we use them in other modules.
+
+We might conceivably fool around with mathness, but we don't
+really intend to do so.
+#<Pattern-action...#>=
+#=/^module /#> {
+ for (i=2;i<NF;) {
+ if ($i=="definition") {
+ i++
+ mod_def_cat=$i
+ categories[$i]=1
+ print "Module definition category set to", $i > logfile
+ i++
+ } else if ($i=="use") {
+ i++
+ mod_use_cat=$i
+ categories[$i]=1
+ print "Module use category set to", $i > logfile
+ i++
+ } else {
+ print "Error: unknown module property", $i, \
+ "on line", NR-1
+ #<Punt...#>
+ }
+ }
+ #<Check that we used everything#>
+ next
+}
+
+# Here's how we rig it:
+#<Write out...#>=
+if (mod_def_cat!="") {
+ print "@ @<Call |app_scrap| for a module definition@>=" > scrapfile
+ printf "app_scrap(SP_%s,no_math);\n", mod_def_cat > scrapfile
+ appended[mod_def_cat]=1
+} else {
+ print "Error: I don't know what to do with a module definition"
+ print " Give me a \"module definition ...\""
+ exitcode=-1
+}
+if (mod_use_cat!="") {
+ print "@ @<Call |app_scrap| for a module use@>=" > scrapfile
+ printf "app_scrap(SP_%s,maybe_math);\n", mod_use_cat > scrapfile
+ appended[mod_use_cat]=1
+} else {
+ print "Error: I don't know what to do with a module use"
+ print " Give me a \"module use ...\""
+ exitcode=-1
+}
+
+
+#*1At sign.
+With \.{SPIDER}, we can designate any character we like as the
+``magic at sign.''
+#<Pattern-act...#>=
+#=/^at_sign /#> {
+ if (NF==2 && length($2)==1) {
+ if ($2=="@") {
+ at_sign="@@"
+ } else {
+ at_sign=$2
+ }
+ } else {
+ print "Error: I can't understand", $0
+ print " Give me an at sign of length 1"
+ #<Punt...#>
+ }
+ next
+}
+
+# We write the at sign out to the grammar file and to \.{TANGLE}'s token file
+#<Write out all...#>=
+ tempfile = grammarfile
+ #<Write |at_sign| definion to |tempfile|#>
+ tempfile = ttokfile
+ #<Write |at_sign| definion to |tempfile|#>
+
+# It's trivially done
+#<Write |at_sign| definion to |tempfile|#>=
+ print "@ Here is the |at_sign| for the new web" > tempfile
+ printf "@d at_sign = @`%s'\n", at_sign > tempfile
+ print " " > tempfile
+ print "@ Here is |the_at_sign| left for common" > tempfile
+ print "@<Global...@>=char the_at_sign = at_sign;" > tempfile
+ print " " > tempfile
+
+# We provide a default at sign:
+#<Set init...#>=
+ at_sign="@@"
+
+
+#*1Comments.
+We have to explain how our programming language supports comments.
+We give the strings that initiate and terminate a comment.
+We can say comments are terminated by ``newline'' if that's the case.
+#<Pattern-act...#>=
+#=/^comment /#> {
+ print $0 > logfile
+ for (i=2; i<NF;) {
+ if ($i=="begin") {
+ i++
+ if ($i ~ #=/^<.*>$/#>) {
+ transstring = $i
+ wherestring = "in \"comment begin\" on line " NR-1
+ #<Convert restricted translation in |transstring|
+ to quoted string in |outstring|#>
+ begin_comment_string = outstring
+ i++
+ } else {
+ print "Error: \"comment begin\" must have a restricted translation"
+ #<Punt...#>
+ }
+ } else if ($i=="end") {
+ i++
+ if ($i=="newline") {
+ comments_end_with_newline = 1
+ end_comment_string = "\"\\n\""
+ } else if ($i ~ #=/^<.*>$/#>){
+ comments_end_with_newline = 0
+ transstring = $i
+ wherestring = "in \"comment end\" on line " NR-1
+ #<Convert restricted translation in
+ |transstring|
+ to quoted string in |outstring|#>
+ end_comment_string = outstring
+ } else {
+ print "Error: \"comment end\" must have a restricted translation"
+ #<Punt...#>
+ }
+ i++
+ } else {
+ print "Error: bad comment attribute:", $i
+ #<Punt...#>
+ }
+ }
+ #<Check that we used everything#>
+ #<Write the comment definitions to the macro file#>
+ next
+}
+
+# \.{WEAVE} and \.{TANGLE} must be able to recognize comments.
+Here we give \.{TANGLE}
+ quoted strings that show the beginning and end of a
+comment.
+
+#<Write out...#>=
+print "@ Here we recognize the comment start seqence" > ttokfile
+print "@<See a comment starting at |loc| and skip it@>=" > ttokfile
+ printf "{int len; len=strlen(%s);\n", begin_comment_string > ttokfile
+ printf "if (loc+len<=limit && !strncmp(loc,%s,len)) {\n",\
+ begin_comment_string > ttokfile
+ print "\tloc += len; /* a new thing */" > ttokfile
+ print "\tskip_comment(); /* scan to end of comment or newline */" > ttokfile
+ print "\tif (comment_continues || comments_end_with_newline)" > ttokfile
+ print "\t\treturn('\\n');" > ttokfile
+ print "\telse continue;\n}\n}" > ttokfile
+
+
+# Now this is \.{WEAVE} finding the start of a comment
+#<Write out...#>=
+print "@ @<See a comment starting at |loc-1| and return |begin_comment|@>=" \
+ > scrapfile
+ printf "{int len; len=strlen(%s);\n", begin_comment_string > scrapfile
+ printf "if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
+ begin_comment_string > scrapfile
+ print "\tloc += len-1;" > scrapfile
+ print "\t return (begin_comment); /* scan to end of comment or newline */" > scrapfile
+ print "}\n}" > scrapfile
+
+
+
+
+# Here \.{TANGLE} spots the end of a comment
+#<Write out...#>=
+print "@ Here we deal with recognizing the end of comments" > ttokfile
+printf "@d comments_end_with_newline = %d\n", comments_end_with_newline >ttokfile
+print "@<Recognize comment end starting at |loc-1|@>=" > ttokfile
+if (comments_end_with_newline != 1) {
+ printf "{int len; len=strlen(%s);\n", end_comment_string > ttokfile
+ printf "if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
+ end_comment_string > ttokfile
+ print "loc += len-1; return(comment_continues=0); }}" > ttokfile
+} else {
+ print "/* This code will never be executed */ " > ttokfile
+}
+
+# Now here is \.{WEAVE}.
+\.{WEAVE} copes elsewhere with the situation when
+|comments_end_with_newline| holds, so we don't need to consider it here.
+#<Write out...#>=
+printf "@ Here we recognize end of comments" > scrapfile
+printf "@d comments_end_with_newline = %d\n",comments_end_with_newline >scrapfile
+print "@<Check for end of comment@>=" > scrapfile
+ printf "{int len; len=strlen(%s);\n", end_comment_string > scrapfile
+ printf "if (loc+len-1<=limit && !strncmp(loc-1,%s,len)) {\n",\
+ end_comment_string > scrapfile
+print " loc++; if(bal==1) {if (phase==2) app_tok('}'); return(0);}" > scrapfile
+print " else {" > scrapfile
+print " err_print(\"! Braces don't balance in comment\");" > scrapfile
+print "@.Braces don't balance in comment@>" > scrapfile
+print " @<Clear |bal| and |return|@>;" > scrapfile
+print " }" > scrapfile
+print "}" > scrapfile
+print "}" > scrapfile
+
+
+# We have to give \.{TANGLE} the beginning and ending comment strings, so
+it can use thing in writing its own comments.
+#<Write out...#>=
+ print "@ Important tokens:" > ttokfile
+ printf "@d begin_comment_string = %s\n", begin_comment_string > ttokfile
+ printf "@d end_comment_string = %s\n", end_comment_string > ttokfile
+
+# We also have to write out the starting and ending comment strings to
+the macro file.
+We do this at the time of parsing |#=/^comment /#>|, so the user has a
+chance to override.
+#<Write the comment definitions to the macro file#>=
+if (macrofile!="") {
+ this_string=substr(begin_comment_string,2,length(begin_comment_string)-2)
+ #<Write |this_string| into |tex_string|, escaping \TeX's specials#>
+ printf "\\def\\commentbegin{%s}\n", tex_string > macrofile
+ if (comments_end_with_newline==0) {
+ this_string=substr(end_comment_string,2,length(end_comment_string)-2)
+ #<Write |this_string| into |tex_string|, escaping \TeX's specials#>
+ printf "\\def\\commentend{%s}\n", tex_string > macrofile
+ } else {
+ print "\\def\\commentend{\\relax}" > macrofile
+ }
+} else {
+ print "Error: I can't write comment info to the macro file---"
+ print " you haven't given me any \"language\" information"
+ #<Punt...#>
+ }
+
+
+
+# Escaping \TeX's specials is pretty easy:
+#<Set initial...#>=
+texof["\\"]="\\BS"
+texof["{"]="\\{"
+texof["}"]="\\{"
+texof["$"]="\\$"
+texof["&"]="\\amp"
+texof["##"]="\\##"
+texof["^"]="\\H"
+texof["_"]="\\_"
+texof["~"]="\\TI"
+texof["%"]="\\%"
+
+#
+#<Write |this_string| into |tex_string|, escaping \TeX's specials#>=
+tex_string=""
+while (length(this_string)>0) {
+ c = substr(this_string,1,1)
+ this_string = substr(this_string,2)
+ cprime = texof[c]
+ if (cprime=="") {
+ tex_string = tex_string c
+ } else {
+ tex_string = tex_string cprime
+ }
+}
+
+#*1Controlling line numbering.
+Here we fart around with line numbering for \.{TANGLE}.
+This lets \.{TANGLE} write an indication of the locations of things in
+the \.{WEB} source.
+The C preprocessor accepts these things as \.{\##line} directives.
+#<Pattern-act...#>=
+#=/^line /#> {
+ print $0 > logfile
+ for (i=2; i<NF;) {
+ if ($i=="begin") {
+ i++
+ if ($i ~ #=/^<.*>$/#>) {
+ transstring = $i
+ wherestring = "in \"line begin\" on line " NR-1
+ #<Convert restricted translation in |transstring|
+ to quoted string in |outstring|#>
+ sharp_line_open = outstring
+ i++
+ } else {
+ print "Error: \"line begin\" must have a restricted translation"
+ #<Punt...#>
+ }
+ } else if ($i=="end") {
+ i++
+ if ($i ~ #=/^<.*>$/#>){
+ transstring = $i
+ wherestring = "in \"line end\" on line " NR-1
+ #<Convert restricted translation in
+ |transstring|
+ to quoted string in |outstring|#>
+ sharp_line_close = outstring
+ } else {
+ print "Error: \"line end\" must have a restricted translation"
+ #<Punt...#>
+ }
+ i++
+ } else {
+ print "Error: bad line attribute:", $i, "on line", NR-1
+ #<Punt...#>
+ }
+ } ## |for|
+ #<Check that we used everything#>
+ next
+}
+
+# We have to give \.{TANGLE} the strings for \&{\##line} commands.
+#<Write out...#>=
+ print "@ Important tokens:" > ttokfile
+ printf "@d sharp_line_open = %s\n", sharp_line_open > ttokfile
+ printf "@d sharp_line_close = %s\n", sharp_line_close > ttokfile
+
+# We'll choose some innocuous defaults
+#<Set init...#>=
+sharp_line_open = "\"##line\""
+sharp_line_close = "\"\""
+
+#*1Tracking the generation date.
+We want to be able to note the date on which we generate files.
+#<Patt...#>=
+#=/^date /#> {
+ ## date returned as ``Fri Dec 11 11:31:18 EST 1987''
+ mo = month[$3]
+ day = $4
+ year = $7
+ time = $5
+ #<Set |hour|, |minute|, and |ampm| from |time|#>
+ date = sprintf ("(generated at %d:%s %s on %s %d, %d)",\
+ hour, minute, ampm, mo, day, year)
+ next
+}
+
+# We want the months to have their full names
+#<Set init...#>=
+month["Jan"]="January"
+month["Feb"]="February"
+month["Mar"]="March"
+month["Apr"]="April"
+month["May"]="May"
+month["Jun"]="June"
+month["Jul"]="July"
+month["Aug"]="August"
+month["Sep"]="September"
+month["Oct"]="October"
+month["Nov"]="November"
+month["Dec"]="December"
+
+# We make a ``friendly'' time from |time=="hh:mm:ss"|.
+#<Set |hour|, |minute|, and |ampm| from |time|#>=
+ hour = substr(time,1,2)
+ if (hour >=12)
+ ampm = "PM"
+ else
+ ampm="AM"
+
+ if (hour==0) {
+ hour =12
+ } else if (hour>12) {
+ hour = hour -12
+ }
+ minute = substr(time,4,2)
+
+
+#*=The {\tt SPIDER} tools.
+#i cycle.web
+#*Flagging duplicate names.
+Detects duplicate names in a sorted list.
+#(nodups.awk#>=
+{ if ($0==last) {
+ print "Error: duplicate name", $0, "on lines", NR-1"-"NR
+ exit -1
+ }
+ last = $0
+}
+#*Checking translation keywords for validity.
+#(transcheck.awk#>=
+#=/^good translations$/#>,#=/^test translations$/#> {
+ if ($0 !~ #=/^good translations$|^test translations$/#>) {
+ istranslation[$0]=1
+ }
+ next
+ }
+
+{ if (istranslation[$0]!=1) {
+ print "Error:", $0, "is not a valid translation"
+ exitcode = -1
+ }
+}
+
+END {
+ exit exitcode
+ }
+# This is a copy of {\tt transcheck.list}, which should be the first
+part of the input to {\tt transcheck.awk}.
+Since \.{TANGLE} will insert its own stuff, we can't use it.
+{\tt transcheck.awk} {\em could} be updated to work with the
+tangled output, though, if it seemed desirable.
+#(junk.list#>=
+good translations
+break_space
+force
+big_force
+opt
+backup
+big_cancel
+cancel
+indent
+outdent
+math_rel
+math_bin
+math_op
+test translations
+
+#*=Index.
+This is a combined index to {\tt SPIDER} and the {\tt SPIDER} tools.
+Since the {\tt SPIDER} tools are nearly trivial, it's really just
+{\tt SPIDER}.
+
+
diff --git a/web/spiderweb/src/master/tangle.web b/web/spiderweb/src/master/tangle.web
new file mode 100644
index 0000000000..26266034ca
--- /dev/null
+++ b/web/spiderweb/src/master/tangle.web
@@ -0,0 +1,1800 @@
+% Copyright 1989 by Norman Ramsey, Odyssey Research Associates
+% Not to be sold, but may be used freely for any purpose
+% For more information, see file COPYRIGHT in the parent directory
+
+% This file is part of Spidery WEB
+
+% This program by Norman Ramsey is based on programs Silvio Levy
+% and D. E. Knuth. Silvio Levy wrote most of the code.
+% It is distributed WITHOUT ANY WARRANTY, express or implied.
+% Dec 1987
+
+% Here is TeX material that gets inserted after \input webmac
+
+\message{OK, entering \string\batchmode...}
+\batchmode
+
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps
+\def\cee{C}
+\def\pb{$\.|\ldots\.|$} % C brackets (|...|)
+\def\v{\char'174} % vertical (|) in typewriter font
+\def\ceeref{{\it The C Reference Manual}}
+\mathchardef\RA="3221 % right arrow
+\mathchardef\BA="3224 % double arrow
+
+\def\title{Spidery TANGLE}
+\def\contentspagenumber{1} % should be odd
+\def\topofcontents{\null\vfill
+ \titlefalse % include headline on the contents page
+ \def\rheader{\hfil}
+ \centerline{\titlefont The {\ttitlefont Spidery TANGLE} processor}
+ \vfill
+}
+
+@* Introduction.
+\.{TANGLE} has a fairly straightforward outline. It operates in
+two phases: first it reads the source file, saving the \cee\ code in
+compressed form; then outputs the code, after shuffling it around.
+It can be compiled
+with certain optional flags, |DEBUG| and |STAT|, the latter being used
+to keep track of how much of \.{TANGLE}'s resources were actually used.
+
+@u
+@<Include files@>@;
+@<Common code for \.{WEAVE} and \.{TANGLE}@>@;
+@<Typedef declarations@>@;
+@<Global variables@>@;
+
+main (ac, av)
+char **av;
+{
+ argc=ac; argv=av;
+ program=tangle;
+ @<Set initial values@>;
+ common_init();
+ printf(banner); /* print a ``banner line'' */
+ phase_one(); /* read all the user's text and compress it into |tok_mem| */
+ phase_two(); /* output the contents of the compressed tables */
+ wrap_up();
+}
+
+@ The following parameters were sufficient in the original \.{TANGLE} to
+handle \TeX, so they should be sufficient for most applications of \.{TANGLE}.
+
+If you change |max_bytes|, |max_names| or |hash_size| you should also
+change them in the file |"common.web"|.
+
+@d max_bytes = 90000 /* the number of bytes in identifiers,
+ index entries, and module names; used in |"common.web"| */
+@d max_toks = 150000 /* number of bytes in compressed \cee\ code */
+@d max_names = 4000 /* number of identifiers, strings, module names;
+ must be less than 10240; used in |"common.web"| */
+@d max_texts = 2000 /* number of replacement texts, must be less than 10240 */
+@d hash_size = 353 /* should be prime; used in |"common.web"| */
+@d longest_name = 400 /* module names shouldn't be longer than this */
+@d stack_size = 50 /* number of simultaneous levels of macro expansion */
+@d buf_size = 100 /* for \.{WEAVE} and \.{TANGLE} */
+
+@i common.h
+
+@ Should include tlang.web but can't because AWK runs out of files.
+
+@* Data structures exclusive to {\tt TANGLE}.
+We've already seen that the |byte_mem| array holds the names of identifiers,
+strings, and modules;
+the |tok_mem| array holds the replacement texts
+for modules. Allocation is sequential, since things are deleted only
+during Phase II, and only in a last-in-first-out manner.
+
+A \&{text} variable is a structure containing a pointer into
+|tok_mem|, which tells where the corresponding text starts, and an
+integer |text_link|, which, as we shall see later, is used to connect
+pieces of text that have the same name. All the \&{text}s are stored in
+the array |text_info|, and we use a |text_pointer| variable to refer
+to them.
+
+The first position of |tok_mem| that is unoccupied by
+replacement text is called |tok_ptr|, and the first unused location of
+|text_info| is called |text_ptr|. Thus we usually have the identity
+|text_ptr->tok_start=tok_ptr|.
+
+If your machine does not support |char unsigned| you should change
+the definition of \&{eight\_bits} to |short unsigned|.
+@^system dependencies@>
+
+@<Typed...@>=
+typedef struct {
+ eight_bits *tok_start; /* pointer into |tok_mem| */
+ sixteen_bits text_link; /* relates replacement texts */
+} text;
+typedef text *text_pointer;
+
+@ @<Glob...@>=
+text text_info[max_texts];
+text_pointer text_info_end=text_info+max_texts-1;
+text_pointer text_ptr; /* first unused position in |text_info| */
+eight_bits tok_mem[max_toks];
+eight_bits *tok_mem_end=tok_mem+max_toks-1;
+eight_bits *tok_ptr; /* first unused position in |tok_mem| */
+
+@ @<Set init...@>=
+text_info->tok_start=tok_ptr=tok_mem;
+text_ptr=text_info+1; text_ptr->tok_start=tok_mem;
+ /* this makes replacement text 0 of length zero */
+
+@ If |p| is a pointer to a module name, |p->equiv| is a pointer to its
+replacement text, an element of the array |text_info|.
+
+@d equiv = equiv_or_xref /* info corresponding to names */
+
+@ @<Set init...@>=
+name_dir->equiv=(ASCII *)text_info; /* the undefined module has no replacement text */
+
+@ Here's the procedure that decides whether a name of length |l|
+starting at position |first| equals the identifier pointed to by |p|:
+
+@u
+names_match(p,first,l)
+name_pointer p; /* points to the proposed match */
+ASCII *first; /* position of first character of string */
+int l; /* length of identifier */
+{
+ if (length(p)!=l) return 0;
+ return !strncmp(first,p->byte_start,l);
+}
+
+@
+@u
+init_node(node)
+name_pointer node;
+{
+ node->equiv=(ASCII *)text_info;
+
+}
+init_p(p,t)
+name_pointer p;
+eight_bits t;
+{
+ p->ilk=t;
+}
+
+
+
+@*Tokens.
+ Replacement
+ texts, which represent \cee\ code in a compressed format,
+appear in |tok_mem| as mentioned above. The codes in
+these texts are called `tokens'; some tokens occupy two consecutive
+eight-bit byte positions, and the others take just one byte.
+
+If $p$ points to a replacement text, |p->tok_start| is the |tok_mem| position
+of the first eight-bit code of that text. If |p->text_link=0|,
+this is the replacement text for a macro, otherwise it is the replacement
+text for a module. In the latter case |p->text_link| is either equal to
+|module_flag|, which means that there is no further text for this module, or
+|p->text_link| points to a continuation of this replacement text; such
+links are created when several modules have \cee\ texts with the same
+name, and they also tie together all the \cee\ texts of unnamed modules.
+The replacement text pointer for the first unnamed module appears in
+|text_info->text_link|, and the most recent such pointer is |last_unnamed|.
+
+@d module_flag = max_texts /* final |text_link| in module replacement texts */
+
+@<Glob...@>=
+text_pointer last_unnamed; /* most recent replacement text of unnamed module */
+
+@ @<Set init...@>= last_unnamed=text_info; text_info->text_link=0;
+
+@ If the first byte of a token is less than |@'200|, the token occupies a
+single byte. Otherwise we make a sixteen-bit token by combining two consecutive
+bytes |a| and |b|. If |@'200<=a<@'250|, then |(a-@'200)@t${}\times2^8$@>+b|
+to an identifier; if |@'250<=a<@'320|, then
+|(a-@'250)@t${}\times2^8$@>+b| points to a module name; otherwise, i.e., if
+|@'320<=a<@'400|, then |(a-@'320)@t${}\times2^8$@>+b| is the number of the module
+in which the current replacement text appears.
+
+Codes less than @'200 are 7-bit ASCII codes that represent themselves.
+In particular, a single-character identifier like `|x|' will be a one-byte
+token, while all longer identifiers will occupy two bytes.
+
+Some of the 7-bit ASCII codes will not be present, however, so we can
+use them for special purposes. The following symbolic names are used:
+
+\yskip \hang |join| denotes the concatenation of adjacent items with no
+space or line breaks allowed between them (the \.{@@\&} operation of \.{WEB}).
+
+\hang |string| denotes the beginning or end of a string, verbatim
+construction or numerical constant.
+@^ASCII code@>
+
+@d string = @'2
+@d param = @'7
+@d join = @'177 /* ASCII delete will not appear */
+
+@ The following procedure is used to enter a two-byte value into
+|tok_mem| when a replacement text is being generated.
+
+@u store_two_bytes(x)
+sixteen_bits x;
+{
+ if (tok_ptr+2>tok_mem_end) overflow("token");
+ *tok_ptr++=x>>8; /* store high byte */
+ *tok_ptr++=x&@'377; /* store low byte */
+}
+
+
+@* Stacks for output. The output process uses a stack to keep track
+of what is going on at different ``levels'' as the modules are being
+written out. Entries on this stack have five parts:
+
+\yskip\hang |end_field| is the |tok_mem| location where the replacement
+text of a particular level will end;
+
+\hang |byte_field| is the |tok_mem| location from which the next token
+on a particular level will be read;
+
+\hang |name_field| points to the name corresponding to a particular level;
+
+\hang |repl_field| points to the replacement text currently being read
+at a particular level.
+
+\hang |mod_field| is the module number, or zero if this is a macro.
+
+\yskip\noindent The current values of these five quantities are referred to
+quite frequently, so they are stored in a separate place instead of in
+the |stack| array. We call the current values |cur_end|, |cur_byte|,
+|cur_name|, |cur_repl|, and |cur_mod|.
+
+The global variable |stack_ptr| tells how many levels of output are
+currently in progress. The end of all output occurs when the stack is
+empty, i.e., when |stack_ptr=stack|.
+
+@<Typed...@>=
+typedef struct {
+ eight_bits *end_field; /* ending location of replacement text */
+ eight_bits *byte_field; /* present location within replacement text */
+ name_pointer name_field; /* |byte_start| index for text being output */
+ text_pointer repl_field; /* |tok_start| index for text being output */
+ sixteen_bits mod_field; /* module number or zero if not a module */
+} output_state;
+typedef output_state *stack_pointer;
+
+@ @d cur_end = cur_state.end_field /* current ending location in |tok_mem| */
+@d cur_byte = cur_state.byte_field /* location of next output byte in |tok_mem|*/
+@d cur_name = cur_state.name_field /* pointer to current name being expanded */
+@d cur_repl = cur_state.repl_field /* pointer to current replacement text */
+@d cur_mod = cur_state.mod_field /* current module number being expanded */
+
+@<Global...@>=
+output_state cur_state; /* |cur_end|, |cur_byte|, |cur_name|, |cur_repl|
+ and |cur_mod| */
+output_state stack[stack_size+1]; /* info for non-current levels */
+stack_pointer stack_ptr; /* first unused location in the output state stack */
+stack_pointer stack_end=stack+stack_size; /* end of |stack| */
+
+@ To get the output process started, we will perform the following
+initialization steps. We may assume that |text_info->text_link| is nonzero,
+since it points to the \cee\ text in the first unnamed module that generates
+code; if there are no such modules, there is nothing to output, and an
+error message will have been generated before we do any of the initialization.
+
+@<Initialize the output stacks@>=
+stack_ptr=stack+1; cur_name=name_dir; cur_repl=text_info->text_link+text_info;
+cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start; cur_mod=0;
+
+@ When the replacement text for name |p| is to be inserted into the output,
+the following subroutine is called to save the old level of output and get
+the new one going.
+
+We assume that the C compiler can copy structures.
+@^system dependencies@>
+
+@u push_level(p) /* suspends the current level */
+name_pointer p;
+{
+ @<Trace the name of what's pushed (|#ifdef TRACE_MACROS|)@>;
+ if (stack_ptr==stack_end) overflow("stack");
+ *stack_ptr=cur_state;
+ stack_ptr++;
+ cur_name=p; cur_repl=(text_pointer)p->equiv;
+ cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start;
+ cur_mod=0;
+}
+
+@ @<Trace the name of what's pushed (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+ if (tracing>0) {
+ printf("\nPushing "); print_id(p);
+ printf(" =nd[%d]",p-name_dir);
+ }
+#endif TRACE_MACROS
+
+@ When we come to the end of a replacement text, the |pop_level| subroutine
+does the right thing:
+It either moves to the continuation of this replacement
+text or returns the state to the most recently stacked level.
+If this level is the end of a macro definition, it pops all the parameters
+off the stack.
+
+@u pop_level() /* do this when |cur_byte| reaches |cur_end| */
+{ int number_of_parameters;
+@<Trace the name of what's popped (|#ifdef TRACE_MACROS|)@>;
+ if (cur_repl->text_link==0) { /* macro definition */
+ if (cur_name->ilk==macro) {
+ @<Pop the parameters from the parameter stack@>;
+ }
+ } else if (cur_repl->text_link<module_flag) { /* link to a continuation */
+ cur_repl=cur_repl->text_link+text_info; /* stay on the same level */
+ cur_byte=cur_repl->tok_start; cur_end=(cur_repl+1)->tok_start;
+ return;
+ }
+ stack_ptr--; /* go down to the previous level */
+ if (stack_ptr>stack) cur_state=*stack_ptr;
+}
+
+
+@ @<Pop the parameters...@>=
+number_of_parameters=*(cur_repl->tok_start);
+@<Trace number of parms popped (|#ifdef TRACE_MACROS|)@>;
+while (number_of_parameters-->0) {
+ name_ptr--; text_ptr--;
+#ifdef TRACE_MACROS
+byte_ptr-=2;
+#endif TRACE_MACROS
+ }
+#ifdef STAT
+#ifdef STAT_HAS_BEEN_FIXED
+ if (tok_ptr>max_tok_ptr) max_tok_ptr = tok_ptr;
+ /* maximum value of |tok_ptr| occurs just before
+ parameter popping */
+#endif STAT_HAS_BEEN_FIXED
+#endif STAT
+tok_ptr = text_ptr->tok_start;
+
+
+@ @<Trace number of parms popped (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>1) {
+ printf("\nUnstacking %d parameters",number_of_parameters);
+ }
+#endif TRACE_MACROS
+@ @<Trace the name of what's popped (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+ if (tracing>0) {
+ printf("\nPopping "); print_id(cur_name);
+ printf(" =nd[%d]",cur_name-name_dir);
+ }
+
+#endif TRACE_MACROS
+
+
+
+@*1 Expanding macros.
+ The heart of the output procedure is the |get_output| routine, which produces
+the next token of output that is not a reference to a macro. This procedure
+handles all the stacking and unstacking that is necessary.
+
+@ Before we can get on to |get_output|,
+we have to talk about expanding macros.
+Macro parameters must be stacked.
+They are placed in |tok_mem| just
+above the other replacement texts, and dummy parameter ``names'' are
+placed in |byte_start| just after the other names.
+The variables |text_ptr| and |tok_ptr| essentially serve
+as parameter stack pointers during the output phase, so there is
+no need for a separate data structure to handle this problem.
+
+
+@ Here are our invariants (assuming $\#$ represents |param|):
+(1) in a replacement text, $\#n$ always refers to |*(name_ptr-n)|.
+(2) when a parameter is scanned, any $\#n$ are replaced with references
+to the appropriate name, so that the replacement text for a parameter
+never contains a $\#n$.
+(3) |name_ptr| is not changed until all the parameters are scanned;
+otherwise invariant (1) wouldn't work during parameter scanning.
+(4) when all parameters are scanned, |name_ptr| is moved
+so that $\#n$ in the replacement text will be interpreted correctly.
+(5) When we have finished scanning the replacement text for a macro, we
+have to pop |name_ptr| to maintain the invariant in (1).
+
+@ |get_output| returns the value |module_number|
+if the next output begins or ends the replacement text of some module,
+in which case |cur_val| is that module's number (if beginning) or the
+negative of that value (if ending). (A module number of 0 indicates
+not the beginning or ending of a module, but a \&{\#line} command.)
+And it returns the value |identifier|
+if the next output is an identifier of length two or more, in which case
+|cur_val| points to that identifier name.
+
+@d module_number = @'201 /* code returned by |get_output| for module numbers */
+@d identifier = @'202
+@<Global...@>=
+int cur_val; /* additional information corresponding to output token */
+
+
+@ If |get_output| finds that no more output remains, it returns the value zero.
+@u sixteen_bits
+get_output() /* returns next token after macro expansion */
+{
+ sixteen_bits a; /* value of current byte */
+ restart: if (stack_ptr==stack) return 0;
+ if (cur_byte==cur_end) {
+ cur_val=-((int)cur_mod); /* cast needed because of sign extension */
+ pop_level();
+ if (cur_val==0) goto restart;
+ out_char(module_number); return;
+ }
+ a=*cur_byte++;
+ if (a<@'200)
+ if(a==param) {
+ @<Trace parameter number (|#ifdef TRACE_MACROS|)@>;
+ @<Get the parameter number, set up to substitute
+ that parameter, and go to |restart|@>;
+ } else
+ out_char(a); /* one-byte token */
+ else {
+ a=(a-@'200)*@'400+*cur_byte++;
+ switch (a/@'24000) { /* |@'24000=(@'250-@'200)*@'400| */
+ case 0:
+ @<If |a| is a macro, set it up and go to |restart|@>;
+ cur_val=a; out_char(identifier); break;
+ case 1: @<Expand module |a-@'24000|, |goto restart|@>;
+ default: cur_val=a-@'50000; if (cur_val>0) cur_mod=cur_val;
+ out_char(module_number);
+ }
+ }
+ return 1;
+}
+
+
+@ @<Get the parameter number...@>=
+push_level(name_ptr-*cur_byte++); goto restart;
+@ @<Trace parameter number...@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ printf (" [#%d]",*cur_byte);
+ }
+#endif TRACE_MACROS
+
+@ @<If |a| is a macro...@>=
+if (name_dir[a].ilk==macro) {
+ @<Trace macro expansion (|#ifdef TRACE_MACROS|)@>;
+ @<Scan the parameters (if any) and stack them,
+ or go to |restart| if an error occurs@>;
+ push_level(name_dir+a);
+ cur_byte++; /* skip number of parameters */
+ goto restart;
+} else if (name_dir[a].ilk==simple) {
+ @<Trace parameter expansion (|#ifdef TRACE_MACROS|)@>;
+ push_level(name_dir+a);
+ goto restart;
+}
+
+@ @<Trace macro expansion (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ printf("\nExpanding macro ("); print_id(name_dir+a);
+ printf("=nd[%d])",a);
+ }
+#endif TRACE_MACROS
+
+@ @<Trace parameter expansion (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ printf("\nExpanding parameter ("); print_id(name_dir+a);
+ printf("=nd[%d])",a);
+ }
+#endif TRACE_MACROS
+
+
+@ @<Scan the parameters...@>=
+{int number_of_parameters;
+ while (cur_byte==cur_end && stack_ptr>stack) pop_level();
+ number_of_parameters = *(((text_pointer)name_dir[a].equiv)->tok_start);
+ if (number_of_parameters>0) {
+ if (stack_ptr==stack || *cur_byte!=@`(') {
+ printf("\n! No parameters given for ");
+ print_id(a+name_dir);
+ err_print("");
+ goto restart;
+ }
+ cur_byte++; /* skip left parenthesis */
+ parm_ptr = name_ptr; /* maintain |name_ptr| invariant */
+ while (number_of_parameters-->0) {
+ @<Stack a parameter ending in
+ comma or parenthesis@>;
+ }
+ name_ptr=parm_ptr; /* we are about to |push_level|,
+ so this maintains the |name_ptr| invariant */
+ }
+}
+
+@ In scanning parameter lists, we require that parameters be separated
+by commas, and that parentheses balance in parameters.
+The balancing is done with the array |balances|, and {\tt SPIDER}
+could easily be extended to make certain tokens {\tt balance left} and
+other tokens {\tt balance right}, so that {\tt left} and {\tt right}
+tokens balance in parameters.
+@ @<Global variables@>=
+short balances[@'200];
+@ @<Set initial values@>=
+{ int i;
+for (i=0;i<@'200;i++) balances[i]=0;
+@<Set special |balances|@>;
+}
+@ Eventually we will want {\tt SPIDER} to set balances.
+For now let's try just tossing in parenthesis, braces, and square brackets.
+@<Set special |balances|@>=
+balances[@`(']=1;
+balances[@`)']=-1;
+balances[@`[']=1;
+balances[@`]']=-1;
+balances[@`{']=1;
+balances[@`}']=-1;
+
+@ @<Stack a parameter...@>=
+{ int bal; /* used to balance parentheses in parameter lists */
+ eight_bits b, oldb;
+ sixteen_bits c;
+bal=0;
+@<Trace scanning for stack (|#ifdef TRACE_MACROS|)@>;
+while (1) {
+ b=*cur_byte++;
+ if (b==param) { /* convert to name reference */
+ b=*cur_byte++;
+ @<Trace parameter scansion (|#ifdef TRACE_MACROS|)@>;
+ c=name_ptr-name_dir-b; /* |name_ptr-b| points to parm */
+ app_repl(@'200 + (c/@'400));
+ app_repl(c % @'400);
+ } else {
+ if (b>=@'200) {
+ @<Trace scansion of token in |b| (|#ifdef TRACE_MACROS|)@>;
+ app_repl(b);
+ b=*cur_byte++;
+ } else if (bal==0 && ((number_of_parameters==0 && b==@`)')
+ || (number_of_parameters>0 && b==@`,'))) {
+ goto done;
+ } else if (@<|b| opens verbatim or string@>) {
+ @<Copy verbatim or string token list beginning with |b|@>;
+ } else {
+ @<Trace scansion of ordinary token in |b|@>;
+ bal += balances[b];
+ }
+ app_repl(b);
+ }
+}
+done:
+parm_ptr->equiv=(ASCII *)text_ptr;
+text_ptr->text_link=0; /* label a macro */
+parm_ptr->ilk=simple; /* treat parm \# like simple macro */
+#ifdef TRACE_MACROS
+if(byte_ptr+2>=byte_mem_end) overflow("byte memory");
+*byte_ptr++=@`#'; *byte_ptr++=number_of_parameters+1+@`0';
+#endif TRACE_MACROS
+if (parm_ptr>=name_dir_end) overflow ("name");
+(++parm_ptr)->byte_start=byte_ptr;
+ if (text_ptr>=text_info_end) overflow("text");
+ (++text_ptr)->tok_start=tok_ptr;
+@<Trace stacking the parameter (|#ifdef TRACE_MACROS|)@>;
+}
+
+@ @<Global variables@>=
+name_pointer parm_ptr; /* used while stacking parameters */
+
+@ @<|b| opens verbatim or string@>=(b==string||b==constant)
+@ @<Copy verbatim or string...@>=
+app_repl(b);
+oldb=b;
+@<Write |" <"| (|#ifdef TRACE_MACROS|)@>;
+while((b=*cur_byte++)!=oldb) {
+@<Trace scansion of character |b| (|#ifdef TRACE_MACROS|)@>;
+ app_repl(b);
+ if (cur_byte>=cur_end)
+ confusion("string or constant didn't end in token list");
+}
+@<Write |">"| (|#ifdef TRACE_MACROS|)@>;
+
+
+
+@ @<Write |" <"| (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if(tracing>2) printf(" <");
+#endif TRACE_MACROS
+@ @<Trace scansion of character |b| (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>2) printf("%c",b);
+#endif TRACE_MACROS
+@ @<Write |">"| (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if(tracing>2) printf(">");
+#endif TRACE_MACROS
+@ @<Trace parameter scansion (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ printf (" <#%d=",b); print_id(name_ptr-b); printf("=nd[%d]>",
+ name_ptr-b-name_dir);
+ }
+#endif TRACE_MACROS
+
+@ @<Trace scansion of token in |b| (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ int c;
+ printf (" <");
+ c=(b-@'200)*@'400+*cur_byte;
+ switch (c/@'24000) {
+ case 0: print_id(name_dir+c);
+ printf(" =nd[%d]",c);
+ break;
+ case 1: printf("MODULE"); break;
+ default: printf("CONTEXT"); break;
+ }
+ printf(">");
+}
+#endif TRACE_MACROS
+
+@ @<Trace scansion of ordinary token in |b|@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ printf (" <");
+ if (@'37<b && b<@'177) printf("%c",b);
+ printf(">");
+}
+#endif TRACE_MACROS
+
+@ @<Trace scanning for stack (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ printf ("\nScanning #%d...", number_of_parameters+1);
+ }
+#endif TRACE_MACROS
+@ @<Trace stacking the parameter (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>1) {
+ printf ("\nStacked "); print_id(parm_ptr-1);
+ printf ("=nd[%d]", parm_ptr-1-name_dir);
+ }
+#endif TRACE_MACROS
+
+@ The user may have forgotten to give any \cee\ text for a module name,
+or the \cee\ text may have been associated with a different name by mistake.
+
+@<Expand module |a-...@>=
+ a-=@'24000;
+ if ((a+name_dir)->equiv!=(ASCII *)text_info) push_level(a+name_dir);
+ else if (a!=0) {
+ printf("\n! Not present: <"); print_id(a+name_dir); err_print(">");
+@.Not present: <section name>@>
+ }
+ goto restart;
+
+@* Producing the output.
+The |get_output| routine above handles most of the complexity of output
+generation, but there is one further consideration that has a nontrivial
+effect on \.{TANGLE}'s algorithms. Namely,
+we want to make sure that the output has spaces and line breaks in
+the right places (e.g., not in the middle of a string or a constant or an
+identifier, not at a `\.{@@\&}' position
+where quantities are being joined together, and certainly after a \.=
+because the C compiler thinks \.{=-} is ambiguous).
+
+The output process can be in one of following states:
+
+\yskip\hang |num_or_id| means that the last item in the buffer is a number or
+identifier, hence a blank space or line break must be inserted if the next
+item is also a number or identifier.
+
+\yskip\hang |unbreakable| means that the last item in the buffer was followed
+by the \.{@@\&} operation that inhibits spaces between it and the next item.
+
+\yskip\hang |verbatim| means we're copying only character tokens, and
+that they are to be output exactly as stored. This is the case during
+strings, verbatim constructions and numerical constants.
+
+\yskip\hang |misc| means none of the above.
+
+\yskip Furthermore, if the variable |protect| is positive, new-lines
+are preceded by a `\.\\'.% note this for /*spider*/
+
+@d misc = 0 /* ``normal'' state */
+@d num_or_id = 1 /* state associated with numbers and identifiers */
+@d unbreakable = 3 /* state associated with \.{@@\&} */
+@d verbatim = 4 /* state in the middle of a string */
+
+@<Global...@>=
+eight_bits out_state; /* current status of partial output */
+boolean protect; /* current status of partial output */
+
+@ Here is a routine that is invoked when we want to output the current line.
+During the output process, |cur_line| equals the number of the next line
+to be output.
+
+@u flush_buffer() /* writes one line to output file */
+{
+ C_putc('\n');
+ if (cur_line % 100 == 0) {
+ printf(".");
+ if (cur_line % 500 == 0) printf("%d",cur_line);
+ update_terminal; /* progress report */
+ }
+ cur_line++;
+}
+
+@* The big output switch. Here then is the routine that does the
+output.
+We have made some modifications to \.{TANGLE} so it will write output
+on multiple files.
+We do this very simply: if a module name is introduced by \.{@@(}
+instead of \.{@@<}, we treat it as the name of a file.
+All these special modules are saved on a stack, |output_files|.
+We write them out after we've done the unnamed module.
+
+@d max_files = 256
+@<Glob...@>=
+name_pointer output_files[max_files];
+name_pointer *cur_out_file, *end_output_files, *an_output_file;
+char cur_module_char; /* is it |'<'| or |'('| */
+char output_file_name[longest_name]; /* name of the file */
+
+@ We make |end_output_files| point jsut beyond the end of
+|output_files|.
+|cur_out_file| starts out there. Every time we see a new file, we
+decrement |cur_out_file| and then write it in.
+@<Set initial...@>=
+cur_out_file=end_output_files=output_files+max_files;
+
+@ @<If it's not there, add |cur_module| to the output file stack, or
+complain we're out of room@>=
+{
+if (cur_out_file>output_files) {
+ for (an_output_file=cur_out_file;
+ an_output_file<end_output_files; an_output_file++)
+ if (*an_output_file==cur_module) break;
+ if (an_output_file==end_output_files)
+ *--cur_out_file=cur_module;
+} else {
+ overflow("output files");
+}
+}
+
+@ Here is the output switch, then...
+
+@u
+phase_two () {
+ cur_line=1;
+ if (text_info->text_link==0) {
+ if(end_output_files==cur_out_file) {
+ printf("\n! No program text was specified."); mark_harmless;
+@.No output was specified@>
+ }
+ } else {
+ printf("\nWriting the output files: (%s)",C_file_name); update_terminal;
+ @<Initialize the output stacks@>;
+ while (stack_ptr>stack) get_output();
+ flush_buffer();
+ }
+ if (end_output_files>cur_out_file) {
+ if(text_info->text_link==0) {
+ printf("\nWriting the output files: "); update_terminal;
+ }
+ @<Write all the named output files@>@;
+ }
+ printf("\nDone.");
+}
+
+@ To write the named output files, we proceed as for the unnamed
+module.
+The only subtlety is that we have to open each one.
+@<Write all the named output files@>=
+for (an_output_file=end_output_files; an_output_file>cur_out_file;) {
+ an_output_file--;
+ strncpy(output_file_name,(*an_output_file)->byte_start, longest_name);
+ output_file_name[length(*an_output_file)]='\0';
+ fclose(C_file);
+ C_file=fopen(output_file_name,"w");
+ if (C_file == NULL) {
+ fatal("! Cannot open output file:",output_file_name)@;
+ } else {
+ printf(" (%s)",output_file_name); update_terminal;
+ }
+ stack_ptr=stack+1;
+ cur_name= (*an_output_file);
+ cur_repl= (text_pointer) cur_name->equiv_or_xref;
+ cur_byte=cur_repl->tok_start;
+ cur_end=(cur_repl+1)->tok_start;
+ cur_mod=0;
+ while (stack_ptr > stack) get_output();
+ flush_buffer();
+}
+
+
+
+@ A many-way switch is used to send the output:
+
+@u out_char(cur_char)
+eight_bits cur_char;
+{
+ ASCII *j; /* pointer into |byte_mem| */
+ @<Trace |cur_char| (|#ifdef TRACE_MACROS|)@>;
+ switch (cur_char) {
+ case @`\n': if (protect) C_putc(' '); /*spider*/
+ if (protect || out_state==verbatim) C_putc('\\'); /*spider*/
+ /*spider*/ /*may need to escape newlines*/
+ flush_buffer(); if (out_state!=verbatim) out_state=misc; break;
+ @/@t\4@>@<Case of an identifier@>;
+ @/@t\4@>@<Case of a module number@>;
+ @<Cases for tokens to be output@>@;
+ case join: out_state=unbreakable; break;
+ case constant: if (out_state==verbatim) {
+ out_state=num_or_id; break;
+ }
+ if(out_state==num_or_id) C_putc(' '); out_state=verbatim; break;
+ case string: if (out_state==verbatim) out_state=misc;
+ else out_state=verbatim; break;
+ default: C_putc(cur_char); if (out_state!=verbatim) out_state=misc;
+ break;
+ }
+}
+
+@ @<Trace |cur_char| (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ switch(cur_char) {
+ case @`\n': printf(" [\\n]"); break;
+ case string:
+ printf( "[STRING]"); break;
+ case join:
+ printf( "[JOIN]"); break;
+ case constant:
+ printf( "[CONSTANT]"); break;
+ case identifier:
+ printf (" ["); print_id(cur_val+name_dir); printf("]");
+ break;
+ case module_number:
+ if (cur_val>0) {
+ printf(" [%d:]", cur_val);
+ } else if(cur_val<0) {
+ printf(" [:%d]", - cur_val);
+ } else {
+ printf( "[LINE NUMBER]");
+ }
+ break;
+
+ default:
+ if (@'37<cur_char && cur_char < @'177) {
+ printf (" [%c]", cur_char);
+ } else {
+ printf(" [\\%3o]",cur_char);
+ }
+ }
+}
+#endif TRACE_MACROS
+
+
+
+
+@ @<Case of an identifier@>=
+case identifier:
+ if (out_state==num_or_id) C_putc(' ');
+ for (j=(cur_val+name_dir)->byte_start; j<(name_dir+cur_val+1)->byte_start;
+ j++) C_putc(*j);
+ out_state=num_or_id; break;
+
+@ @<Case of a mod...@>=
+case module_number:
+ if (cur_val>0) {
+ C_printf("%s",begin_comment_string);
+ C_printf("%d:",cur_val);
+ C_printf("%s",end_comment_string);
+ } else if(cur_val<0) {
+ C_printf("%s",begin_comment_string);
+ C_printf(":%d",-cur_val);
+ C_printf("%s",end_comment_string);
+ } else {
+ sixteen_bits a;
+ a=@'400* *cur_byte++;
+ a+=*cur_byte++; /* gets the line number */
+ C_printf("\n%s",sharp_line_open);
+ C_printf(" %d \"",a);
+ cur_val=*cur_byte++;
+ cur_val=@'400*(cur_val-@'200)+ *cur_byte++; /* points to the file name */
+ for (j=(cur_val+name_dir)->byte_start; j<(name_dir+cur_val+1)->byte_start;
+ j++) C_putc(*j);
+ C_printf("\"%s\n",sharp_line_close);
+ }
+ break;
+
+@i outtoks.web
+@* Introduction to the input phase.
+We have now seen that \.{TANGLE} will be able to output the full
+\cee\ program, if we can only get that program into the byte memory in
+the proper format. The input process is something like the output process
+in reverse, since we compress the text as we read it in and we expand it
+as we write it out.
+
+There are three main input routines. The most interesting is the one that gets
+the next token of a \cee\ text; the other two are used to scan rapidly past
+\TeX\ text in the \.{WEB} source code. One of the latter routines will jump to
+the next token that starts with `\.{@@}', and the other skips to the end
+of a \cee\ comment.
+
+@ Control codes in \.{WEB} begin with `\.{@@}', and the next character
+identifies the code. Some of these are of interest only to \.{WEAVE},
+so \.{TANGLE} ignores them; the others are converted by \.{TANGLE} into
+internal code numbers by the |control_code| table below. The ordering
+of these internal code numbers has been chosen to simplify the program logic;
+larger numbers are given to the control codes that denote more significant
+milestones.
+
+@d ignore = 0 /* control code of no interest to \.{TANGLE} */
+@d octal = @'5
+@d hex = @'6
+@d trace = @'370
+@d ascii_constant = @'371 /* control code for `\.{@@`}' */
+@d control_text = @'372 /* control code for `\.{@@t}', `\.{@@\^}', etc. */
+@d format = @'373 /* control code for `\.{@@f}' */
+@d definition = @'374 /* control code for `\.{@@d}' */
+@d begin_unnamed = @'375 /* control code for `\.{@@u}' */
+@d module_name = @'376 /* control code for `\.{@@<}' */
+@d new_module = @'377 /* control code for `\.{@@\ }' and `\.{@@*}' */
+
+@<Global...@>=
+eight_bits ccode[128]; /* meaning of a char following \.{@@} */
+
+@ @<Set ini...@>= {
+ int c; /* must be |int| so the |for| loop will end */
+ for (c=0; c<=127; c++) ccode[c]=ignore;
+ ccode[' ']=ccode[tab_mark]=ccode['*']=new_module;
+ ccode ['@@'] = '@@';
+ ccode['=']=string;
+ ccode['d']=ccode['D']=definition;
+ ccode['f']=ccode['F']=format;
+ ccode['c']=ccode['C']=begin_unnamed;
+ ccode['u']=ccode['U']=begin_unnamed;
+ ccode['^']=ccode[':']=ccode['.']=ccode['t']=ccode['T']=control_text;
+ ccode['&']=join;
+ ccode['<']=ccode['(']=module_name;
+ ccode['`']=ascii_constant;
+ ccode['\'']=octal;
+ ccode['"']=hex;
+/*Now adjust for |at_sign|... if it is @@, we have no-op followed by quoting */
+/* ... but if it is other, say \#, then \#@@ replaces @@\#, and \#\#
+ quotes itself*/
+ccode['@@']=ccode[at_sign];
+ccode[at_sign]=at_sign;
+
+#ifdef DEBUG
+ccode['0']=ccode['1']=ccode['2']=ccode['3']=ccode['4']=trace;
+#endif DEBUG
+
+}
+
+@ We may want some sort of tracing facility:
+@<Global variables@>=short tracing;
+@ @<Set initial...@>=tracing=0;
+
+@ The |skip_ahead| procedure reads through the input at fairly high speed
+until finding the next non-ignorable control code, which it returns.
+
+@u eight_bits skip_ahead() /* skip to next control code */
+{
+ eight_bits c; /* control code found */
+ while (1) {
+ if (loc>limit && (get_line()==0)) return(new_module);
+ *(limit+1)=at_sign;
+ while (*loc!=at_sign) loc++;
+ if (loc<=limit) {
+ loc++; c=ccode[*loc];
+#ifdef DEBUG
+ if (c==trace) {
+ tracing=*loc-@`0'; c=ignore;
+ }
+#endif DEBUG
+ loc++;
+ if (c!=ignore || *(loc-1)=='>') return(c);
+ }
+ }
+}
+
+@ The |skip_comment| procedure reads through the input at somewhat high
+speed until finding the end-comment token \.{*/} or a new-line, in which
+case |skip_comment| will be called again by |get_next|, since the
+comment is not finished. This is done so that the each newline in the
+C part of a module is copied to the output; otherwise the \&{\#line}
+commands inserted into the C file by the output routines become useless.
+If it comes to the end of the module it prints an error message.
+
+@<Global...@>=
+boolean comment_continues=0; /* are we scanning a comment? */
+
+@ @u
+skip_comment() /* skips over comments */
+{
+ ASCII c; /* current character */
+ if (comments_end_with_newline) {
+ get_line();
+ return (comment_continues=0);
+ } else {
+ while (1) {/*spider*/ /* fix this to recognize end ok */
+ if (loc>limit)
+ if(get_line()) return(comment_continues=1);
+ else{
+ err_print("! Input ended in mid-comment");
+@.Input ended in mid-comment@>
+ return(comment_continues=0);
+ }
+ c=*(loc++);
+ @<Recognize comment end starting at |loc-1|@>@;
+ if (c==at_sign) {
+ if (ccode[*loc]==new_module) {
+ err_print("! Section name ended in mid-comment"); loc--;
+@.Section name ended in mid-comment@>
+ return(comment_continues=0);
+ }
+ else loc++;
+ }
+ }
+ }
+}
+@* Inputting the next token.
+@d constant = @'3
+@<Global...@>=
+name_pointer cur_module; /* name of module just scanned */
+
+@ @<Include...@>=
+#include "ctype.h" /* definition of |isalpha|, |isdigit| and so on */
+
+@ As one might expect, |get_next| consists mostly of a big switch
+that branches to the various special cases that can arise.
+
+@u eight_bits get_next() /* produces the next input token */
+{
+ eight_bits c; /* the current character */
+ while (1) {
+ if (loc>limit) {
+ if (get_line()==0) return(new_module);
+ else if (print_where) {
+ print_where=0;
+ @<Insert the line number into |tok_mem|@>;
+ }
+ else return (@`\n');
+ }
+ c=*loc;
+ if (comment_continues) {
+ skip_comment(); /* scan to end of comment or newline */
+ if (comment_continues || comments_end_with_newline) return(@`\n');
+ else continue;
+ }
+ @<See a comment starting at |loc| and skip it@>@;
+ loc++;
+ if (isdigit(c) || c=='\\' || c=='.') @<Get a constant@>@;/*spider*/
+ else if (isalpha(c) || c=='_' || c=='$') @<Get an identifier@>@;/*spider*/
+ else if (c=='\'' || c=='\"') @<Get a string@>@;/*spider*/
+ else if (c==at_sign) @<Get control code and possible module name@>@;
+ else if (c==' ' || c==tab_mark) {
+ continue; /* ignore spaces and tabs */
+ }
+ mistake: @<Compress two-symbol operator@>@;
+ return(c);
+ }
+}
+
+@ @<Get an identifier@>= {/*spider*/
+ id_first=--loc;
+ while (isalpha(*++loc) || isdigit(*loc) || *loc=='_');
+ if (*loc=='$') while (isdigit(*++loc)||*loc=='$');
+ /* make room for \$\$ and \$nnn suffixes */
+ id_loc=loc; return(identifier);
+}
+
+@ @<Get a constant@>= {/*spider*/
+ id_first=loc-1;
+ if (*id_first=='.' && !isdigit(*loc)) goto mistake; /* not a constant */
+ if (*id_first=='\\') while (isdigit(*loc)) loc++; /* octal constant */
+ else {
+ if (*id_first=='0') {
+ if (*loc=='x' || *loc=='X') { /* hex constant */
+ loc++; while (isxdigit(*loc)) loc++; goto found;
+ }
+ }
+ while (isdigit(*loc)) loc++;
+ if (*loc=='.') {
+ loc++;
+ while (isdigit(*loc)) loc++;
+ }
+ if (*loc=='e' || *loc=='E') { /* float constant */
+ if (*++loc=='+' || *loc=='-') loc++;
+ while (isdigit(*loc)) loc++;
+ }
+ }
+ found:
+ id_loc=loc;
+ return(constant);
+}
+
+@ \cee\ strings and character constants, delimited by double and single
+quotes, respectively, can contain newlines or instances of their own
+delimiters if they are protected by a backslash. We follow this
+convention, but do not allow the string to be longer than |longest_name|.
+
+@<Get a string@>= {/*spider*/
+ ASCII delim = c; /* what started the string */
+@#
+/* if it's not a single-character literal, it's a tick mark or an |at_sign| */
+ if (delim=='\'' && (loc+1>=limit ||
+ (*loc != '\\' && *loc!=at_sign && loc[1]!='\'') ||
+ (*loc=='\\' && (loc+2>=limit||loc[2]!='\'')) ||
+ (*loc==at_sign &&
+ (loc+2>=limit||loc[1]!=at_sign||loc[2]!='\''))
+ )) goto mistake;
+ id_first = mod_text+1;
+ id_loc = mod_text; *++id_loc=delim;
+ while (1) {
+ if (loc>=limit) {
+ if(*(limit-1)!='\\') {
+ err_print("! String didn't end"); loc=limit; break;
+@.String didn't end@>
+ }
+ if(get_line()==0) {
+ err_print("! Input ended in middle of string"); loc=buffer; break;
+@.Input ended in middle of string@>
+ }
+ else if (++id_loc<=mod_text_end) *id_loc=@`\n'; /* will print as
+ \.{"\\\\\\n"} */
+ }
+ if ((c=*loc++)==delim) {
+ if (++id_loc<=mod_text_end) *id_loc=c;
+ break;
+ }
+ if (c=='\\') {
+ if (loc>=limit) continue;
+ if (++id_loc<=mod_text_end) *id_loc = '\\';
+ c=*loc++;
+ }
+ if (++id_loc<=mod_text_end) *id_loc=c;
+ }
+ if (id_loc>=mod_text_end) {
+ printf("\n! String too long: ");
+@.String too long@>
+ ASCII_write(mod_text+1,25);
+ printf("..."); mark_error;
+ }
+ id_loc++;
+ return(string);
+}
+
+@ After an \.{@@} sign has been scanned, the next character tells us
+whether there is more work to do.
+
+@<Get control code and possible module name@>= {
+ c=ccode[*loc++];
+ switch(c) {
+ case ignore: continue;
+ case control_text: while ((c=skip_ahead())==at_sign);
+ /* only \.{@@@@} and \.{@@>} are expected */
+ if (*(loc-1)!='>') err_print("! Improper @@ within control text");
+@.Improper {\AT!} within control text@>
+ continue;
+ case module_name:
+ cur_module_char=*(loc-1);
+ @<Scan the module name and make |cur_module| point to it@>;
+ case string: @<Scan a verbatim string@>;
+#ifdef DEBUG
+ case trace: tracing=*(loc-1)-'0'; continue;
+#endif DEBUG
+ case ascii_constant: @<Scan an ASCII constant@>;
+ case octal: @<Scan an octal constant@>;
+ case hex: @<Scan a hex constant@>;
+ default: return(c);
+ }
+}
+
+@ @<Scan an ASCII...@>=/*spider*/
+ id_first=loc;
+ if (*loc=='\\') loc++;
+ while (*loc!='\'') {
+ loc++;
+ if (loc>limit) {
+ err_print("! String didn't end"); loc=limit-1; break;
+ }
+ }
+ loc++;
+ return(ascii_constant);
+
+@ @<Scan an octal constant@>= {
+ id_first=loc;
+ while ('0'<=*loc && *loc<'8') loc++;
+ id_loc=loc;
+ return(octal);
+}
+
+@ @<Scan a hex constant@>= {
+ id_first=loc;
+ while (isxdigit(*loc)) loc++;
+ id_loc=loc;
+ return(hex);
+}
+
+
+@ @<Scan the module name...@>= {
+ ASCII *k; /* pointer into |mod_text| */
+ @<Put module name into |mod_text|@>;
+ if (k-mod_text>3 && strncmp(k-2,"...",3)==0) cur_module=prefix_lookup(mod_text+1,k-3);
+ else cur_module=mod_lookup(mod_text+1,k);
+ if (cur_module_char=='(') {
+ @<If it's not there, add |cur_module| to the output file stack, or
+ complain we're out of room@>@;
+ }
+ return(module_name);
+}
+
+@ Module names are placed into the |mod_text| array with consecutive spaces,
+tabs, and carriage-returns replaced by single spaces. There will be no
+spaces at the beginning or the end. (We set |mod_text[0]=' '| to facilitate
+this, since the |mod_lookup| routine uses |mod_text[1]| as the first
+character of the name.)
+
+@<Set init...@>=mod_text[0]=' ';
+
+@ @<Put module name...@>=
+k=mod_text;
+while (1) {
+ if (loc>limit && get_line()==0) {
+ err_print("! Input ended in section name");
+@.Input ended in section name@>
+ loc=buffer+1; break;
+ }
+ c=*loc;
+ @<If end of name, |break|@>;
+ loc++; if (k<mod_text_end) k++;
+ if (c==' ' || c==tab_mark) {
+ c=' '; if (*(k-1)==' ') k--;
+ }
+*k=c;
+}
+if (k>=mod_text_end) {
+ printf("\n! Section name too long: ");
+@.Section name too long@>
+ ASCII_write(mod_text+1,25);
+ printf("..."); mark_harmless;
+}
+if (*k==' ' && k>mod_text) k--;
+
+@ @<If end of name,...@>=
+if (c==at_sign) {
+ c=*(loc+1);
+ if (c=='>') {
+ loc+=2; break;
+ }
+ if (ccode[c]==new_module) {
+ err_print("! Section name didn't end"); break;
+@.Section name didn't end@>
+ }
+ *(++k)=at_sign; loc++; /* now |c==*loc| again */
+}
+
+@ At the present point in the program we
+have |*(loc-1)=string|; we set |id_first| to the beginning
+of the string itself, and |id_loc| to its ending-plus-one location in the
+buffer. We also set |loc| to the position just after the ending delimiter.
+
+@<Scan a verbatim string@>= {
+ id_first=loc++; *(limit+1)=at_sign; *(limit+2)='>';
+ while (*loc!=at_sign || *(loc+1)!='>') loc++;
+ if (loc>=limit) err_print("! Verbatim string didn't end");
+@.Verbatim string didn't end@>
+ id_loc=loc; loc+=2;
+ return(string);
+}
+
+@* Scanning a macro definition.
+The rules for generating the replacement texts corresponding to macros and
+\cee\ texts of a module are almost identical; the only differences are that
+
+\yskip \item{a)}Module names are not allowed in macros;
+in fact, the appearance of a module name terminates such macros and denotes
+the name of the current module.
+
+\item{b)}The symbols \.{@@d} and \.{@@f} and \.{@@u} are not allowed after
+module names, while they terminate macro definitions.
+
+\yskip Therefore there is a single procedure |scan_repl| whose parameter
+|t| specifies either |macro| or |module_name|. After |scan_repl| has
+acted, |cur_text| will point to the replacement text just generated, and
+|next_control| will contain the control code that terminated the activity.
+
+@d app_repl(c) = {if (tok_ptr==tok_mem_end) overflow("token"); *tok_ptr++=c;}
+
+@<Global...@>=
+text_pointer cur_text; /* replacement text formed by |scan_repl| */
+eight_bits next_control;
+
+@ @u scan_repl(t) /* creates a replacement text */
+eight_bits t;
+{
+ sixteen_bits a; /* the current token */
+ int set_print_where;
+ if (t==module_name) {@<Insert the line number into |tok_mem|@>;}
+ /* avoid inserting line number in macro replacement texts */
+ /* |print_where| is both tested and set in |get_next| */
+ while (1) {
+ if (t==macro) {
+ print_where = 0;
+ }
+ a=get_next();
+ if (t==macro) {
+ set_print_where = print_where;
+ }
+ switch (a) {
+ @<In cases that |a| is a non-ASCII token (|identifier|,
+ |module_name|, etc.), either process it and change |a| to a byte
+ that should be stored, or |continue| if |a| should be ignored,
+ or |goto done| if |a| signals the end of this replacement text@>@;
+ default: app_repl(a); /* store |a| in |tok_mem| */
+ }
+ }
+ done: next_control=(eight_bits) a;
+ if (text_ptr>text_info_end) overflow("text");
+ if (t==macro) {
+ @<Strip trailing newlines from the replacement text@>;
+ }
+ cur_text=text_ptr; (++text_ptr)->tok_start=tok_ptr;
+ print_where = set_print_where;
+}
+
+@ We don't ever want a macro replacement text to end with newline,
+but for readability of the {\tt WEB} source
+we usually want the last token in a macro definition
+to be a newline token.
+We can't just look for |@`\n'| at the end of the token list, because
+that might be the second half of a two-byte token, in which case it
+certainly {\em wouldn't} be a newline!
+So we look for a two-byte token (|*tok_ptr>=@'200|); as soon as we find one
+we've eliminated all trailing newlines.
+As long as we keep finding one-byte tokens, we drop trailing newlines
+as they come along.
+@<Strip trailing newlines...@>=
+tok_ptr -= 2;
+while (*tok_ptr<@'200 && *(tok_ptr+1)==@`\n') tok_ptr--;
+tok_ptr += 2;
+
+@ We use macros with zero or more parameters, and we give the parameters names.
+In order to scan a macro definition, we need to be able to substitute
+special markers for the parameter names.
+We {\em don't} put the parameter names in the hash table, because they're
+strictly temporary.
+Instead we keep them in temporary storage:
+We allow macros to have up to 32 parameters using 256 text chars.
+
+@d max_param_name_texts = 256
+@d max_param_names = 32
+@<Global variables@>=
+ASCII param_name_texts[max_param_name_texts];
+ASCII *param_name_texts_end = param_name_texts+max_param_name_texts;
+ASCII * param_names[max_param_names]; /* pointers into |param_name_texts| */
+short next_param_name; /* first free spot in |param_names| */
+ASCII * next_param_name_text; /* first free spot in |param_name_texts| */
+
+@ @<Initialize the parameter name area@>=
+next_param_name=0;
+next_param_name_text=param_name_texts;
+param_names[next_param_name]=next_param_name_text;
+
+@ @<Set initial values@>=@<Initialize the parameter name area@>;
+
+@ @<Add identifier to parameter name list@>=
+@<Check for parameter name overflow@>;
+while (id_first<id_loc)
+ *next_param_name_text++=*id_first++;
+param_names[++next_param_name]=next_param_name_text;
+
+@ @<Check for parameter name overflow@>=
+if (next_param_name==max_param_names)
+ overflow ("parameter names");
+if (id_loc - id_first >
+ param_name_texts_end - next_param_name_text)
+ overflow ("parameter name texts");
+
+
+@ The function |parameter_number(first,loc)| returns |0| if the identifier
+is not a parameter, and the number of the parameter if it is a parameter.
+This is not the parameter number we'll store, because for storage we
+want the {\em last} parameter to be {\tt \#1}, the penultimate to
+be {\tt \#2}, and so on.
+This means we have to know the number of parameters only when constructing
+the replacement text, not when scanning the replacement text.
+@u
+int parameter_number(first, loc)
+ ASCII *first, *loc;
+{
+ ASCII *f, *p;
+ int n;
+ for (n=0;n<next_param_name;n++) {
+ if (loc-first==param_names[n+1]-param_names[n]) {
+ /* lengths match */
+ for(f=first,p=param_names[n];f<loc;)
+ if (*f++!=*p++) goto nomatch;
+ return n+1; /* matched parameter */
+ }
+ nomatch: continue; /* need |continue| to avoid syntax error! */
+ }
+ return 0; /* never matched anything */
+}
+
+
+@ While scanning the macro name and parameter list, we ignore newlines.
+We initialize the parameter name area at the beginning, and again
+after scanning each replacement text.
+This makes sure we have an empty parameter name area when scanning the
+replacement text for a module.
+
+@<Scan a macro definition@>=
+@<Set |next_control| to the first non-newline token@>@;
+@<Take the macro name in |next_control|, put it in the hash table.
+ and set |p| to its entry in |name_dir|@>;
+@<Scan the parameter list (if any),
+ and set |next_control| to the non-newline
+ token following the parameter list@>;
+if (next_control!= @`=') {
+ err_print("! You must put an = sign before the macro replacement text");
+ @<Quit scanning the macro definition@>;
+}
+@<Trace macro name (|#ifdef TRACE_MACROS|)@>;
+app_repl(next_param_name); /* store number of parameters in |tok_mem| */
+scan_repl(macro);
+p->equiv=(ASCII *)cur_text;
+@<Trace equivalent text (|#ifdef TRACE_MACROS|)@>;
+@<Initialize the parameter name area@>;
+
+@ @<Trace macro name (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>1) {
+ printf("\nScanning definition of macro "); print_id(p);
+ }
+#endif TRACE_MACROS
+
+@ @<Trace equivalent text (|#ifdef TRACE_MACROS|)@>=
+#ifdef TRACE_MACROS
+if (tracing>2) {
+ printf("\nMacro "); print_id(p);
+ printf("'s replacement text is in text_info[%d]", cur_text-text_info);
+ }
+#endif TRACE_MACROS
+
+
+@ @<Scan the parameter list (if any), and set |next_control| to the
+ non-newline token
+ following the parameter list@>=
+@<Set |next_control| to the first non-newline token@>@;
+if (next_control==@`(') {
+ do {
+ @<Get a parameter name@>;
+ @<Set |next_control| to the first non-newline token@>@;
+ } while (next_control==@`,');
+ if (next_control != @`)') {
+ err_print("! Macro parameter list must end with )");
+ @<Quit scanning the macro definition@>;
+ }
+ next_control=get_next(); /* first token following parameter list */
+}
+
+
+@ @<Take the macro name in |next_control|...@>=
+if (next_control!=identifier) {
+ err_print("! Macro name must be an identifier");
+@.Macro name must be an identifier@>
+ @<Quit scanning the macro definition@>;
+} else {
+ p = id_lookup(id_first,id_loc,macro);
+ if (p->ilk!=macro) {
+#ifdef WARN_USE_BEFORE_DEF
+ printf("\n! Warning: macro name ");
+ print_id(p);
+ printf(" was used before it was defined");
+ mark_harmless;
+#endif WARN_USE_BEFORE_DEF
+ p->ilk=macro;
+ } else if (p+1!=name_ptr) {
+ err_print ("! Macro name is multiply defined: ");
+ }
+}
+
+@ @<Get a parameter name@>=
+@<Set |next_control| to the first non-newline token@>;
+if (next_control!=identifier) {
+ err_print("! Macro parameter name not an identifier");
+@.Macro parameter name...@>
+ @<Quit scanning the macro definition@>;
+} else {
+ if (parameter_number(id_first,id_loc)!=0) {
+ err_print("! Duplicate parameters in macro definition");
+@.Duplicate parameters...@>
+ @<Quit scanning the macro definition@>;
+ } else {
+ @<Add identifier to parameter name list@>;
+ }
+}
+
+
+@ @<Set |next_control| to the first non-newline token@>=
+while ((next_control=get_next())==@`\n');
+
+@ We modify this from the old tangle:
+
+@<In cases that |a| is a non-ASCII token (|identifier|,
+ |module_name|, etc.), either process it and change |a| to a byte
+ that should be stored, or |continue| if |a| should be ignored,
+ or |goto done| if |a| signals the end of this replacement text@>=
+case identifier:
+ { short n;
+ if ((n=parameter_number(id_first,id_loc))!=0) {
+#ifdef TRACE_MACROS
+if(tracing>1) {
+ printf("\nIdentified "); ASCII_write(id_first,id_loc-id_first);
+ printf(" as parameter number %d (%d from back)",n,next_param_name-n+1);
+ }
+#endif TRACE_MACROS
+ app_repl(param);
+ app_repl(next_param_name-n+1); /* reverses numbering */
+ } else { /* not a parameter */
+ a=id_lookup(id_first,id_loc,normal)-name_dir;
+ app_repl((a / @'400)+@'200); app_repl(a % @'400);
+ }
+ }
+ break;
+case module_name: if (t!=module_name) goto done;
+ else {
+ @<Was an |at_sign| missed here?@>;
+ a=cur_module-name_dir;
+ app_repl((a / @'400)+@'250);
+ app_repl(a % @'400);
+ @<Insert the line number into |tok_mem|@>; break;
+ }
+case constant: case string:
+ @<Copy a string or verbatim construction or numerical constant@>;
+case ascii_constant:
+ @<Copy an ASCII constant@>;
+case octal:
+ @<Copy an octal constant@>; break;
+case hex:
+ @<Copy a hex constant@>; break;
+case @`\n':
+#ifdef NEWLINES_IN_MACROS
+ app_repl(a);
+#else
+ if (t==macro) continue;
+ else app_repl(a);
+#endif NEWLINES_IN_MACROS
+ break;
+case definition: case format: case begin_unnamed:
+ if (t!=module_name) goto done;
+ else {
+ err_print("! @@d, @@f and @@u are ignored in C text"); continue;
+@.{\AT!}d, {\AT!}f and {\AT!}u are ignored in C text@>
+ }
+case new_module: goto done;
+
+@ Here is the code for the line number: first a |sixteen_bits| equal
+to |@'150000|; then, if we're dealing with the change file, the line
+number plus |@'100000|; or, if we're dealing with the web file, the
+line number; or, if we're dealing with an include file, the number 0,
+then the line number, followed by the number of characters in the file
+name and the file name.
+
+@<Insert the line...@>=
+store_two_bytes(@'150000);
+if (changing) id_first=change_file_name;
+else id_first=cur_file_name;
+id_loc=id_first+strlen(id_first);
+if (changing) store_two_bytes((sixteen_bits)change_line);
+else store_two_bytes((sixteen_bits)cur_line);
+{int a=id_lookup(id_first,id_loc,normal)-name_dir; app_repl((a / @'400)+@'200);
+ app_repl(a % @'400);}
+
+@ @<Was an |at_sign|...@>= {
+ ASCII *try_loc=loc;
+ while (*try_loc==' ' && try_loc<limit) try_loc++;
+ if (*try_loc=='+' && try_loc<limit) try_loc++;
+ while (*try_loc==' ' && try_loc<limit) try_loc++;
+ if (*try_loc=='=') err_print ("! Nested named modules. Missing @@?");
+@.Nested named modules@>
+}
+
+@ @<Copy a string...@>=
+ app_repl(a); /* |string| or |constant| */
+ while (id_first < id_loc) { /* simplify \.{@@@@} pairs */
+ if (*id_first==at_sign) id_first++;
+ app_repl(*id_first++);
+ }
+ app_repl(a); break;
+
+@ @<Copy an ASCII constant@>= {
+ int c;
+ if (*id_first==at_sign) {
+ c=xchr[*id_first++];
+ if (*id_first!=at_sign) err_print("! Double @@ within string");
+ }
+ else if (*id_first=='\\') {
+ id_first++;
+ switch (*id_first) {
+ case 't':c=@`\t';break;
+ case 'n':c=@`\n';break;
+ case 'b':c=@`\b';break;
+ case '0':c=@`\0';break;
+ case '\\':c=@`\\';break;
+ default: err_print("! Unrecognized escape sequence");
+ }
+ }
+ else c=xchr[*id_first];
+ app_repl(constant);
+/* we don't want octal; we want decimal */
+ /* we know |c<=255| */
+ app_decimal((long)c);
+ app_repl(constant);
+}
+break;
+
+@ Paranoia to work on any 32 bit integer machine...
+@<Copy an octal constant@>= {
+long sum=0;
+while (id_first<id_loc) {
+ sum = 8*sum + *id_first++ - '0';
+ if (sum > @"04000000) err_print("! Octal constant exceeds @@\"04000000");
+ }
+app_repl(constant);
+app_decimal(sum);
+app_repl(constant);
+}
+
+@ @<Copy a hex constant@>= {
+long sum=0;
+while (id_first<id_loc) {
+ sum = 16*sum +
+ (isdigit(*id_first)
+ ? *id_first - '0'
+ : isupper(*id_first)
+ ? *id_first - 'A' + 10
+ : *id_first - 'a' + 10
+ );
+ id_first++;
+ if (sum > @"04000000) err_print("! Hex constant exceeds @@\"04000000");
+ }
+app_repl(constant);
+app_decimal(sum);
+app_repl(constant);
+}
+
+@ This function prints out a decimal constant using |app_repl|.
+@u
+app_decimal(c)
+ long c; /* on entry require |c>=0| */
+{long power;
+ if (c==0) {app_repl('0'); return;}
+ if (c<0) /* should never happen */ {app_repl('-'); c = - c;}
+ for (power=1; c>=power; power *=10);
+ /* now |power/10<=c<power| */
+ for (power /=10; power>=1; power /=10) {
+ app_repl('0'+c/power); /* leading digit $>0$ */
+ c%=power;
+ /* invariant: original c = this c + printed string*power */
+ }
+ }
+
+
+
+@* Scanning a module.
+The |scan_module| procedure starts when `\.{@@\ }' or `\.{@@*}' has been
+sensed in the input, and it proceeds until the end of that module. It
+uses |module_count| to keep track of the current module number; with luck,
+\.{WEAVE} and \.{TANGLE} will both assign the same numbers to modules.
+
+@<Global...@>=
+extern sixteen_bits module_count; /* the current module number */
+
+@ The top level of |scan_module| is trivial.
+@u scan_module()
+{
+ name_pointer p; /* module name for the current module */
+ text_pointer q; /* text for the current module */
+ sixteen_bits a; /* token for left-hand side of definition */
+ module_count++;
+ if (*(loc-1)=='*') /* starred module */
+ printf("*%d",module_count); fflush(stdout);
+ @<Scan the definition part of the current module@>;
+ @<Scan the \cee\ part of the current module@>;
+}
+
+@ We define two kinds of ilks for identifiers: |normal| for ordinary
+identifiers, and |macro| for macros.
+@d normal = 0
+@d macro = 1
+@d simple = 2
+@<Scan the definition part...@>=
+next_control=0;
+while (1) {
+done_scanning:
+ while (next_control<=format)
+ if ((next_control=skip_ahead())==module_name) {
+ /* scan the module name too */
+ loc-=2; next_control=get_next();
+ }
+ if (next_control!=definition) break;
+ @<Scan a macro definition@>;
+ cur_text->text_link=0; /* |text_link=0| characterizes a macro */
+}
+
+@ We initialize the parameter name area at the beginning, and we re-initialize
+any time we have to punt a macro definition.
+This makes sure we have an empty parameter name area when scanning the
+replacement text for a module.
+@<Quit scanning the macro definition@>=
+@<Initialize the parameter name area@>;
+p->ilk=normal; /* turn off macro replacement and hope for the best */
+goto done_scanning;
+
+
+@ @<Scan the \cee...@>=
+switch (next_control) {
+ case begin_unnamed: p=name_dir; break;
+ case module_name: p=cur_module;
+ @<Check that |=| follows this module name, otherwise |return|@>;
+ break;
+ default: return;
+}
+@<Insert the module number into |tok_mem|@>;
+scan_repl(module_name); /* now |cur_text| points to the replacement text */
+@<Update the data structure so that the replacement text is accessible@>;
+
+@ @<Check that |=|...@>=
+while ((next_control=get_next())=='+'); /* allow optional `\.{+=}" */
+if (next_control!='=') {
+ err_print("! C text flushed, = sign is missing");
+@.C text flushed...@>
+ while ((next_control=skip_ahead()) != new_module);
+ return;
+}
+
+@ @<Insert the module number...@>=
+store_two_bytes((sixteen_bits)(@'150000+module_count)); /* |@'150000==@'320*@'400| */
+
+@ @<Update the data...@>=
+if (p==name_dir||p==0) { /* unnamed module, or bad module name */
+ (last_unnamed)->text_link=cur_text-text_info; last_unnamed=cur_text;
+}
+else if (p->equiv==(ASCII *)text_info) p->equiv=(ASCII *)cur_text;
+ /* first module of this name */
+else {
+ q=(text_pointer)p->equiv;
+ while (q->text_link<module_flag) q=q->text_link+text_info; /* find end of list */
+ q->text_link=cur_text-text_info;
+}
+cur_text->text_link=module_flag; /* mark this replacement text as a nonmacro */
+
+@ @u phase_one() {
+ phase=1;
+ module_count=0;
+ reset_input();
+ while ((next_control=skip_ahead())!=new_module);
+ while (!input_has_ended) scan_module();
+ check_complete();
+ phase=2;
+}
+
+@ @u print_stats() {
+ printf("\nMemory usage statistics:\n");
+ printf("%d names (out of %d)\n",name_ptr-name_dir,max_names);
+ printf("%d replacement texts (out of %d)\n",text_ptr-text_info,max_texts);
+ printf("%d bytes (out of %d)\n",byte_ptr-byte_mem,max_bytes);
+ printf("%d tokens (out of %d)\n",tok_ptr-tok_mem,max_toks);
+}
+
+@* Index.
+Here is a cross-reference table for the \.{TANGLE} processor.
+All modules in which an identifier is
+used are listed with that identifier, except that reserved words are
+indexed only when they appear in format definitions, and the appearances
+of identifiers in module names are not indexed. Underlined entries
+correspond to where the identifier was declared. Error messages and
+a few other things like ``ASCII code'' are indexed here too.
diff --git a/web/spiderweb/src/master/transcheck.awk b/web/spiderweb/src/master/transcheck.awk
new file mode 100644
index 0000000000..ec02b62ba3
--- /dev/null
+++ b/web/spiderweb/src/master/transcheck.awk
@@ -0,0 +1,23 @@
+##162:
+
+#line 2537 "/u/norman/pu/web/dist/src/master/spider.web"
+
+#line 2538 "/u/norman/pu/web/dist/src/master/spider.web"
+/^good translations$/,/^test translations$/{
+if($0!~/^good translations$|^test translations$/){
+istranslation[$0]=1
+}
+next
+}
+
+{if(istranslation[$0]!=1){
+print"Error:",$0,"is not a valid translation"
+exitcode=-1
+}
+}
+
+END{
+exit exitcode
+}
+##:162
+
diff --git a/web/spiderweb/src/master/transcheck.list b/web/spiderweb/src/master/transcheck.list
new file mode 100644
index 0000000000..d0ed25c6cf
--- /dev/null
+++ b/web/spiderweb/src/master/transcheck.list
@@ -0,0 +1,14 @@
+good translations
+break_space
+force
+big_force
+opt
+backup
+big_cancel
+cancel
+indent
+outdent
+math_rel
+math_bin
+math_op
+test translations
diff --git a/web/spiderweb/src/master/weave.web b/web/spiderweb/src/master/weave.web
new file mode 100644
index 0000000000..81dfec3f78
--- /dev/null
+++ b/web/spiderweb/src/master/weave.web
@@ -0,0 +1,3250 @@
+% Copyright 1989 by Norman Ramsey, Odyssey Research Associates
+% Not to be sold, but may be used freely for any purpose
+% For more information, see file COPYRIGHT in the parent directory
+
+% This file is part of Spidery WEB
+% This program by Norman Ramsey is based on programs Silvio Levy
+% and D. E. Knuth. Silvio Levy wrote most of the code.
+% It is distributed WITHOUT ANY WARRANTY, express or implied.
+% Dec 1987
+
+% Here is TeX material that gets inserted after \input webmac
+
+\message{Entering \string\batchmode...}
+\batchmode
+
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps
+\def\cee{C}
+\def\Pascal{Pascal}
+\def\PASCAL{Ada}
+\def\pb{$\.|\ldots\.|$} % C brackets (|...|)
+\def\v{\.{\char'174}} % vertical (|) in typewriter font
+\def\dleft{[\![} \def\dright{]\!]} % double brackets
+\mathchardef\RA="3221 % right arrow
+\mathchardef\BA="3224 % double arrow
+\def\({} % kludge for alphabetizing certain module names
+
+\def\title{Spidery WEAVE}
+\def\contentspagenumber{1} % should be odd
+\def\topofcontents{\null\vfill
+ \titlefalse % include headline on the contents page
+ \def\rheader{\hfil}
+ \centerline{\titlefont The {\ttitlefont Spidery WEAVE} processor}
+ \vfill}
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iftrue
+
+@* Introduction.
+\.{CWEAVE} has a fairly straightforward outline. It operates in
+three phases: first it inputs the source file and stores cross-reference
+data, then it inputs the source once again and produces the \TeX\ output
+file, and finally it sorts and outputs the index. It can be compiled
+with certain optional flags, |DEBUG| and |STAT|, the latter being used
+to keep track of how much of \.{WEAVE}'s resources were actually used.
+
+@u
+@<Include files@>@;
+@<Common code for \.{WEAVE} and \.{TANGLE}@>@;
+@<Typedef declarations@>@;
+@<Global variables@>@;
+
+main (ac, av)
+char **av;
+{
+ argc=ac; argv=av;
+ program=weave;
+ common_init();
+ @<Set initial values@>;
+ printf(banner); /* print a ``banner line'' */
+ @<Store all the reserved words@>;
+ phase_one(); /* read all the user's text and store the cross-references */
+ phase_two(); /* read all the text again and translate it to \TeX\ form */
+ phase_three(); /* output the cross-reference index */
+#ifdef STAT
+ @<Print statistics about memory usage@>;
+#endif STAT
+ wrap_up();
+}
+
+@ The following parameters were sufficient in the original \.{WEAVE} to
+handle \TeX, so they should be sufficient for most applications of \.{CWEAVE}.
+
+@d max_bytes = 90000 /* the number of bytes in identifiers,
+ index entries, and module names */
+@d max_names = 4000 /* number of identifiers, strings, module names;
+ must be less than 10240 */
+@d max_modules = 2000 /* greater than the total number of modules */
+@d hash_size = 353 /* should be prime */
+@d buf_size = 100 /* maximum length of input line, plus one */
+@d longest_name = 400 /* module names and strings shouldn't be longer than this */
+@d long_buf_size = 500 /* |buf_size+longest_name| */
+@d line_length = 80 /* lines of \TeX\ output have at most this many characters;
+ should be less than 256 */
+@d max_refs = 20000 /* number of cross-references; must be less than 65536 */
+@d max_toks = 20000 /* number of symbols in \cee\ texts being parsed;
+ must be less than 65536 */
+@d max_texts = 2000 /* number of phrases in \cee\ texts being parsed;
+ must be less than 10240 */
+@d max_scraps = 1000 /* number of tokens in \cee\ texts being parsed */
+@d stack_size = 400 /* number of simultaneous output levels */
+
+@i common.h
+
+@ Should include wlang.web but can't because AWK runs out of files.
+
+@* Data structures exclusive to {\tt WEAVE}.
+As explained in \.{common.web}, the field of a |name_info| structure
+that contains the |rlink| of a module name is used for a completely
+different purpose in the case of identifiers. If is then called the
+|ilk| of the identifier, and it is used to
+distinguish between various types of identifiers, as follows:
+
+@ Several types of identifiers are distinguished by their |ilk|:
+
+\yskip\hang |normal| identifiers are part of the \PASCAL\ program and
+will appear in italic type.
+\yskip\hang |roman| identifiers are index entries that appear after
+\.{@@\^} in the \.{WEB} file.
+
+\yskip\hang |wildcard| identifiers are index entries that appear after
+\.{@@:} in the \.{WEB} file.
+
+\yskip\hang |typewriter| identifiers are index entries that appear after
+\.{@@.} in the \.{WEB} file.
+
+\yskip\hang |array_like|, |begin_like|, \dots, |var_like|
+identifiers are \PASCAL\ reserved words whose |ilk| explains how they are
+to be treated when \PASCAL\ code is being formatted.
+
+\yskip\hang Finally, if |c| is an ASCII code, an |ilk| equal to
+|char_like+c| denotes a reserved word that will be converted to character
+|c|.
+
+@d normal = 0 /*ordinary identifiers have |normal| ilk */
+@d roman = 1 /*normal index entries have |roman| ilk */
+@d wildcard = 2 /*user-formatted index entries have |wildcard| ilk */
+@d typewriter = 3 /*`typewriter type' entries have |typewriter| ilk */
+@d reserved(a) = (a->ilk>typewriter) /* tells if a name is a reserved word */
+/* begin with 64 */
+
+/* ilks are generated by spider into file grammar.web */
+
+@ We keep track of the current module number in |module_count|, which
+is the total number of modules that have started. Modules which have
+been altered by a change file entry have their |changed_module| flag
+turned on during the first phase.
+
+@<Global...@>=
+boolean change_exists; /* has any module changed? */
+
+@ The other large memory area in \.{CWEAVE} keeps the cross-reference data.
+All uses of the name |p| are recorded in a linked list beginning at
+|p->xref|, which points into the |xmem| array. The elements of |xmem|
+are structures consisting of an integer, |num|, and a pointer |xlink|
+to another element of |xmem|. If |x=p->xref| is a pointer into |xmem|,
+the value of |x->num| is either a module number where |p| is used,
+or it is |def_flag| plus a module number where |p| is defined,
+or it is |file_flag| plus a module number where the file |p| is defined;
+and |x->xlink| points to the next such cross-reference for |p|,
+if any. This list of cross-references is in decreasing order by
+module number. The next unused slot in |xmem| is |xref_ptr|.
+
+The global variable |xref_switch| is set either to |def_flag|, or to zero,
+depending on whether the next cross-reference to an identifier is to be
+underlined or not in the index. This switch is set to |def_flag| when
+\.{@@!} or \.{@@d} or \.{@@f} is scanned, and it is cleared to zero when
+the next identifier or index entry cross-reference has been made.
+Similarly,
+the global variable |mod_xref_switch| is either |def_flag|,
+|file_flag|, or zero, depending
+on whether a module name is being defined, defined as a file, or used.
+
+@<Type...@>=
+typedef struct xref_info {
+ sixteen_bits num; /* module number plus zero,|def_flag|, or |file_flag| */
+ struct xref_info *xlink; /* pointer to the previous cross-reference */
+} xref_info;
+typedef xref_info *xref_pointer;
+
+@ @<Global...@>=
+xref_info xmem[max_refs]; /* contains cross-reference information */
+xref_pointer xmem_end = xmem+max_refs-1;
+xref_pointer xref_ptr; /* the largest occupied position in |xmem| */
+sixteen_bits xref_switch,mod_xref_switch; /* either zero or |def_flag| */
+
+@ @d def_flag = 10240 /* must be strictly larger than |max_modules| */
+@d file_flag = 2*def_flag
+@d xref = equiv_or_xref
+
+@<Set init...@>=
+name_dir->xref=(ASCII *)xmem;
+xref_ptr=xmem;
+xref_switch=0;
+mod_xref_switch=0;
+xmem->num=0; /* cross-references to undefined modules */
+
+@ A new cross-reference for an identifier is formed by calling |new_xref|,
+which discards duplicate entries and ignores non-underlined references
+to one-letter identifiers or \cee's reserved words.
+
+If the user has sent the |no_xref| flag (the \.{-x} option of the command line),
+it is unnecessary to keep track of cross-references for identifers.
+If one were careful, one could probably make more changes around module
+100 to avoid a lot of identifier looking up.
+
+@d append_xref(c) = if (xref_ptr==xmem_end) stat_overflow("cross-reference");
+ else (++xref_ptr)->num=c;
+
+@u new_xref(p)
+name_pointer p;
+{
+ xref_pointer q; /* pointer to previous cross-reference */
+ sixteen_bits m, n; /* new and previous cross-reference value */
+ if (no_xref) return;
+ if ((reserved(p) || length(p)==1) && xref_switch==0) return;
+ m=module_count+xref_switch; xref_switch=0; q=(xref_pointer)p->xref;
+ if (q != xmem) {
+ n=q->num;
+ if (n==m || n==m+def_flag) return;
+ else if (m==n+def_flag) {
+ q->num=m; return;
+ }
+ }
+ append_xref(m); xref_ptr->xlink=q; p->xref=(ASCII *)xref_ptr;
+}
+
+@ The cross-reference lists for module names are slightly different. Suppose
+that a module name is defined in modules $m_1$, \dots, $m_k$ and used in
+modules $n_1$, \dots, $n_l$. Then its list will contain $m_1+|def_flag|{}$,
+$m_k+|def_flag|{}$, \dots, $m_2+|def_flag|{}$, $n_l$, \dots, $n_1$, in
+this order.
+If the module is a file module (\.{@@(}), read |file_flag| for
+|def_flag|.
+Since no module should ever have both |file_flag| and |def_flag|, we
+check that.
+ After Phase II, however, the order will be
+$m_1+|def_flag|{}$, \dots, $m_k+|def_flag|{}$, $n_1$, \dots, $n_l$.
+
+@u new_mod_xref(p)
+name_pointer p;
+{
+ xref_pointer q,r; /* pointers to previous cross-references */
+ q=(xref_pointer)p->xref; r=xmem;
+ if (q>xmem) {
+ if (mod_xref_switch==0) while (q->num>=def_flag) {
+ r=q; q=q->xlink;
+ }
+ else if (q->num>=def_flag) {
+ @<Make sure |mod_xref_switch| is consistent with |q->num|@>
+ r=q; q=q->xlink;
+ }
+ }
+ append_xref(module_count+mod_xref_switch);
+ xref_ptr->xlink=q; mod_xref_switch=0;
+ if (r==xmem) p->xref=(ASCII *)xref_ptr;
+ else r->xlink=xref_ptr;
+}
+
+@ Consistency check
+@<Make sure |mod_xref_switch| is consistent with |q->num|@>=
+{
+ if ((mod_xref_switch==def_flag && q->num >= file_flag) ||
+ (mod_xref_switch==file_flag && q->num < file_flag)) {
+ printf("\n! You can't use <"); print_id(p);
+ printf("> both as a file and as a named module"); mark_harmless;
+@.You can't use <section name> both as a file...@>
+
+ }
+}
+
+
+@ A third large area of memory is used for sixteen-bit `tokens', which appear
+in short lists similar to the strings of characters in |byte_mem|. Token lists
+are used to contain the result of \cee\ code translated into \TeX\ form;
+further details about them will be explained later. A |text_pointer| variable
+is an index into |tok_start|.
+
+@<Typed...@>=
+typedef sixteen_bits token;
+typedef token *token_pointer;
+typedef token_pointer *text_pointer;
+
+@ The first position of |tok_mem|
+that is unoccupied by replacement text is called |tok_ptr|, and the first
+unused location of |tok_start| is called |text_ptr|.
+Thus, we usually have |*text_ptr=tok_ptr|.
+
+@<Global...@>=
+token tok_mem[max_toks]; /* tokens */
+token_pointer tok_mem_end = tok_mem+max_toks-1; /* end of |tok_mem| */
+token_pointer tok_start[max_texts]; /* directory into |tok_mem| */
+token_pointer tok_ptr; /* first unused position in |tok_mem| */
+text_pointer text_ptr; /* first unused position in |tok_start| */
+text_pointer tok_start_end = tok_start+max_texts-1; /* end of |tok_start| */
+#ifdef STAT
+token_pointer max_tok_ptr; /* largest value of |tok_ptr| */
+text_pointer max_text_ptr; /* largest value of |text_ptr| */
+#endif STAT
+
+@ @<Set init...@>=
+tok_ptr=tok_mem+1; text_ptr=tok_start+1; tok_start[0]=tok_mem+1;
+tok_start[1]=tok_mem+1; /* |tok_start| is the empty token list,
+ and |*textptr==tok_mem+1==tok_ptr| */
+#ifdef STAT
+max_tok_ptr=tok_mem+1; max_text_ptr=tok_start+1;
+#endif STAT
+
+@ @u
+names_match(p,first,l,t)
+name_pointer p; /* points to the proposed match */
+ASCII *first; /* position of first character of string */
+int l; /* length of identifier */
+eight_bits t; /* desired ilk */
+{
+ if (length(p)!=l) return 0;
+ if (p->ilk!=t && !(t==normal && reserved(p))) return 0;
+ return !strncmp(first,p->byte_start,l);
+}
+
+init_p(p,t)
+name_pointer p;
+eight_bits t;
+{
+ p->ilk=t; p->xref=(ASCII*)xmem;
+}
+
+init_node(p)
+name_pointer p;
+{
+ p->xref=(ASCII*)xmem;
+}
+
+@ We have to get Ada's
+reserved words into the hash table, and the simplest way to do this is
+to insert them every time \.{CWEAVE} is run. Since there are relatively
+few reserved words, we use an ad hoc function to simplify the code.
+@^reserved words@>
+There's not enough room to include \.{reserved.web}, since AWK can't open
+enough files.
+We make do with \.{scraps.web}.
+
+
+
+@* Lexical scanning.
+Let us now consider the subroutines that read the \.{WEB} source file
+and break it into meaningful units. There are four such procedures:
+One simply skips to the next `\.{@@\ }' or `\.{@@*}' that begins a
+module; another passes over the \TeX\ text at the beginning of a
+module; the third passes over the \TeX\ text in a \cee\ comment;
+and the last, which is the most interesting, gets the next token of
+a \cee\ text. They all use the pointers |limit| and |loc| into
+the line of input currently being studied.
+
+@ Control codes in \.{WEB}, which begin with `\.{@@}', are converted
+into a numeric code designed to simplify \.{CWEAVE}'s logic; for example,
+larger numbers are given to the control codes that denote more significant
+milestones, and the code of |new_module| should be the largest of
+all. Some of these numeric control codes take the place of ASCII
+control codes that will not otherwise appear in the output of the
+scanning routines.
+@^ASCII code@>
+
+@d ignore = 0 /* control code of no interest to \.{CWEAVE} */
+@d verbatim = @'2 /* extended ASCII alpha will not appear */
+ /* extended ASCII beta will not appear */
+@d begin_comment = @'10 /* ASCII tab mark will not appear */
+@d octal = @'14 /* ASCII carriage return will not appear */
+@d hex = @'15 /* ASCII form feed will not appear */
+@d switch_math_flag = @'175 /* this code will be intercepted without confusion */
+@d underline = @'176 /* this code will be intercepted without confusion */
+@d param = @'177 /* ASCII delete will not appear */
+/* identifier =200 or octal @'310 */
+@#/* following three must be conseccutive for indexing to work */
+@d xref_roman = (identifier+roman) /* control code for `\.{@@\^}' */
+@d xref_wildcard = (identifier+wildcard) /* control code for `\.{@@:}' */
+@d xref_typewriter = (identifier+typewriter) /* control code for `\.{@@.}' */
+@d TeX_string = @'356 /* control code for `\.{@@t}' */
+@d ascii_constant = @'357 /* control code for `\.{@@`}' */
+@d join = @'360 /* control code for `\.{@@\&}' */
+@d thin_space = @'361 /* control code for `\.{@@,}' */
+@d math_break = @'362 /* control code for `\.{@@\char'174}' */
+@d line_force = @'363 /* control code for `\.{@@/}' */
+@d line_break = @'364 /* control code for `\.{@@-}' */
+@d big_line_break = @'365 /* control code for `\.{@@\#}' */
+@d no_line_break = @'366 /* control code for `\.{@@+}' */
+@d pseudo_semi = @'367 /* control code for `\.{@@;}' */
+@d vertical_bar = @'370 /* The `\v' used to mark Ada text */
+@d trace = @'371 /* control code for `\.{@@0}', `\.{@@1}' and `\.{@@2}' */
+@d format = @'373 /* control code for `\.{@@f}' */
+@d definition = @'374 /* control code for `\.{@@d}' */
+@d begin_unnamed = @'375 /* control code for `\.{@@u}' */
+@d module_name = @'376 /* control code for `\.{@@<}' */
+@d new_module = @'377 /* control code for `\.{@@\ }' and `\.{@@*}' */
+
+@ Control codes are converted from ASCII to \.{CWEAVE}'s internal
+representation by means of the table |ccode|.
+
+@<Global...@>=
+eight_bits ccode[128]; /* meaning of a char following \.{@@} */
+
+@ @<Set ini...@>=
+{int c; for (c=0; c<=127; c++) ccode[c]=0;}
+ccode[' ']=ccode[tab_mark]=ccode['*']=new_module;
+ccode['-']=line_break;
+ccode['#']=big_line_break;
+ccode['=']=verbatim;
+ccode['d']=ccode['D']=definition;
+ccode['f']=ccode['F']=format;
+ccode['c']=ccode['C']=begin_unnamed;
+ccode['u']=ccode['U']=begin_unnamed;
+ccode['t']=ccode['T']=TeX_string;
+ccode['&']=join;
+ccode['<']=ccode['(']=module_name;
+ccode['!']=underline; ccode['^']=xref_roman;
+ccode['$']=switch_math_flag;
+ccode[':']=xref_wildcard;
+ccode['.']=xref_typewriter;
+ccode[',']=thin_space;
+ccode['|']=math_break;
+ccode['/']=line_force;
+ccode['+']=no_line_break;
+ccode[';']=pseudo_semi;
+ccode['`']=ascii_constant;
+ccode['\'']=octal;
+ccode['"']=hex;
+@t\4@>@<Special control codes allowed only when debugging@>@;
+@#
+/*Now adjust for |at_sign|... if it is @@, we have no-op followed by quoting */
+/* ... but if it is other, say \#, then \#@@ replaces @@\#, and \#\#
+ quotes itself*/
+ccode['@@']=ccode[at_sign];
+ccode[at_sign]=at_sign;
+
+@ If \.{CWEAVE} is compiled with debugging commands, one can write
+\.{@@2}, \.{@@1}, and \.{@@0} to turn tracing fully on, partly on,
+and off, respectively.
+
+@<Special control codes...@>=
+#ifdef DEBUG
+ccode['0']=ccode['1']=ccode['2']=trace;
+#endif DEBUG
+
+@ The |skip_limbo| routine is used on the first pass to skip through
+portions of the input that are not in any modules, i.e., that precede
+the first module. After this procedure has been called, the value of
+|input_has_ended| will tell whether or not a module has actually been found.
+
+@u skip_limbo() {
+ while(1) {
+ if (loc>limit && get_line()==0) return;
+ *(limit+1)=at_sign;
+ while (*loc!=at_sign) loc++; /* look for |at_sign|, then skip two chars */
+ if (loc++ <=limit) if (ccode[*loc++]==new_module) return;
+ }
+}
+
+@ The |skip_TeX| routine is used on the first pass to skip through
+the \TeX\ code at the beginning of a module. It returns the next
+control code or `\v' found in the input. A |new_module| is
+assumed to exist at the very end of the file.
+
+@u unsigned skip_TeX() /* skip past pure \TeX\ code */
+{
+ while (1) {
+ if (loc>limit && get_line()==0) return(new_module);
+ *(limit+1)=at_sign;
+ while (*loc!=at_sign && *loc!=vertical_char) loc++;
+ if (*loc++ ==vertical_char) return(vertical_bar);
+ if (loc<=limit) return(ccode[*(loc++)]);
+ }
+}
+
+@* Inputting the next token.
+As stated above, \.{WEAVE}'s most interesting lexical scanning routine is the
+|get_next| function that inputs the next token of \cee\ input. However,
+|get_next| is not especially complicated.
+
+The result of |get_next| is either an ASCII code for some special character,
+or it is a special code representing a pair of characters (e.g., `\.{!=}'),
+or it is the numeric value computed by the |ccode|
+table, or it is one of the following special codes:
+
+\yskip\hang |identifier|: In this case the global variables |id_first| and
+|id_loc| will have been set to the beginning and ending-plus-one locations
+in the buffer, as required by the |id_lookup| routine.
+
+\yskip\hang |string|: The string will have been copied into the array
+|mod_text|; |id_first| and |id_loc| are set as above (now they are
+pointers into |mod_text|).
+
+\yskip\hang |constant|: The constant is copied into |mod_text|, with
+slight modifications; |id_first| and |id_loc| are set.
+
+\yskip\noindent Furthermore, some of the control codes cause
+|get_next| to take additional actions:
+
+\yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|,
+|verbatim|: The values of |id_first| and |id_loc| will have been set to
+the beginning and ending-plus-one locations in the buffer.
+
+\yskip\hang |module_name|: In this case the global variable |cur_module| will
+point to the |byte_start| entry for the module name that has just been scanned.
+
+\yskip\noindent If |get_next| sees `\.{@@!}'
+it sets |xref_switch| to |def_flag| and goes on to the next token.
+
+\yskip\noindent If |get_next| sees `\.{@@\$}'
+it sets |math_flag| to |!math_flag| and goes on to the next token.
+
+
+
+@<Global...@>=
+name_pointer cur_module; /* name of module just scanned */
+int math_flag;
+
+@ @<Include...@>=
+#include "ctype.h"
+
+@ As one might expect, |get_next| consists mostly of a big switch
+that branches to the various special cases that can arise.
+
+Get next takes one argument that determines whether |vertical_char| is a
+character or gets translated to a |vertical_bar|.
+(Normally, |vertical_char=='|'|.)
+If it does get translated, the following rules apply:
+
+\yskip\hang|'|'| as part of a string or as a noninitial character in a
+multicharacter token is not a |vertical_bar|.
+
+\yskip\hang An initial |"||"| is treated like a single |'|'|, and
+taken to be either a token itself or the initial |'|'| in a
+multicharacter token.
+
+@d vertical_char = @`|'
+@u eight_bits get_next(see_vertical) /* produces the next input token
+*/
+ char see_vertical;
+{
+ eight_bits c; /* the current character */
+ while (1) {
+ if (loc>limit) {
+ if (get_line()==0) return(new_module);
+ else { return (@`\n'); }
+ }
+ c=*(loc++);
+ @<See a comment starting at |loc-1| and return |begin_comment|@>@;
+ if (see_vertical && c==vertical_char) {
+ if (*loc==vertical_char && loc < limit) {
+ loc++;
+ } else {
+ return vertical_bar;
+ }
+ }
+ if (isdigit(c)) @<Get a constant@>@; /*spider*/
+ else if (isalpha(c) || c=='_') @<Get an identifier@>@;/*spider*/
+ else if (c=='\'' || c=='"') @<Get a string@>@;/*spider*/
+ else if (c==at_sign) @<Get control code and possible module name@>@;
+ else if (c==' ' || c==tab_mark) continue; /* ignore spaces and tabs */
+ mistake: @<Compress two-symbol operator@>@;
+ return(c);
+ }
+}
+
+@ @<Set |next_control| to the first non-newline token@>=
+while ((next_control=get_next(0))==@`\n');
+
+@ @<Get an identifier@>= {/*spider*/
+ id_first=--loc;
+ while (isalpha(*++loc) || isdigit(*loc) || *loc=='_');
+ id_loc=loc; return(identifier);
+}
+
+@ Notice that in this section and the next, |id_first| and |id_loc|
+are pointers into the array |mod_text|, not into |buffer|.
+
+@<Get a constant@>= {
+ id_first=id_loc=mod_text+1;
+
+ if (*(loc-1)=='.' && !isdigit(*loc)) goto mistake; /* not a constant */
+ *id_loc++=*(loc-1);
+ while (isdigit(*loc)) *id_loc++=*loc++;
+ if (*loc=='.') {
+ *id_loc++=*loc++;
+ while (isdigit(*loc)) *id_loc++=*loc++;
+ }
+#ifdef C_FLOATING_POINT
+/* no floating point --- it depends too much on C */
+ if (*loc=='e' || *loc=='E') { /* float constant */
+ *id_loc++='_'; loc++;
+ if (*loc=='+' || *loc=='-') *id_loc++=*loc++;
+ while (isdigit(*loc)) *id_loc++=*loc++;
+ }
+#endif C_FLOATING_POINT
+ return(constant);
+}
+
+
+
+@ Here we do octals, which I should say more about later...
+@<Get an octal constant@>= {
+ id_first=id_loc=mod_text+1;
+ *id_loc++='~'; /* marks octal constant */
+ while ('0'<=*loc && *loc<'8') *id_loc++=*loc++;
+ return(constant);
+}
+@ And hexes are even easier...
+@<Get a hex constant@>= {
+ id_first=id_loc=mod_text+1;
+ *id_loc++='^'; /* marks hex constant */
+ while (isxdigit(*loc)) {
+ *id_loc++=(islower(*loc)?toupper(*loc):*loc);
+ loc++;
+ }
+ return(constant);
+}
+
+@ \cee\ strings and character constants, delimited by double and single
+quotes, respectively, can contain newlines or instances of their own
+delimiters if they are protected by a backslash. We follow this
+convention, but do not allow the string to be longer than |longest_name|.
+
+@<Get a string@>= {/*spider*/
+ ASCII delim = c; /* what started the string */
+ id_first = mod_text+1;
+ id_loc = mod_text;
+ if (delim=='`' && *(loc-2)==at_sign) {
+ /* make string begin with |"@@`"| */
+ *++id_loc=at_sign;
+ *++id_loc=at_sign;
+ }
+ /* this is hack for ascii constant */
+@#
+/* if it's not a single-character literal, it's a tick mark or an |at_sign| */
+ if ((delim=='\'' || delim == '`') &&
+ (loc+1>=limit ||
+ (*loc != '\\' && *loc!=at_sign && loc[1]!='\'') ||
+ (*loc=='\\' && (loc+2>=limit||loc[2]!='\'')) ||
+ (*loc==at_sign &&
+ (loc+2>=limit||loc[1]!=at_sign||loc[2]!='\''))
+ )
+ ) goto mistake;
+ *++id_loc=delim;
+ if (delim=='`') delim='\''; /* for |ascii_constant|s */
+ while (1) {
+ if (loc>=limit) {
+ if(*(limit-1)!='\\') {
+ err_print("! String didn't end"); loc=limit; break;
+@.String didn't end@>
+ }
+ if(get_line()==0) {
+ err_print("! Input ended in middle of string"); loc=buffer; break;
+@.Input ended in middle of string@>
+ }
+ }
+ if ((c=*loc++)==delim) {
+ if (++id_loc<=mod_text_end) *id_loc=c;
+ break;
+ }
+ if (c=='\\') if (loc>=limit) continue;
+ else if (++id_loc<=mod_text_end) {
+ *id_loc = '\\'; c=*loc++;
+ }
+ if (++id_loc<=mod_text_end) *id_loc=c;
+ }
+ if (id_loc>=mod_text_end) {
+ printf("\n! String too long: ");
+@.String too long@>
+ ASCII_write(mod_text+1,25);
+ printf("..."); mark_error;
+ }
+ id_loc++;
+ return(string);
+}
+
+@ After an \.{@@} sign has been scanned, the next character tells us
+whether there is more work to do.
+
+@<Get control code and possible module name@>= {
+ c=*loc++;
+ switch(ccode[c]) {
+ case underline: xref_switch=def_flag; continue;
+ case switch_math_flag: math_flag=!math_flag; continue;
+#ifdef DEBUG
+ case trace: tracing=c-'0'; continue;
+#endif DEBUG
+ case xref_roman: case xref_wildcard: case xref_typewriter:
+ case TeX_string:
+ @<Scan to the next \.{@@>}@>@;
+ case module_name:
+ @<Scan the module name and make |cur_module| point to it@>@;
+ case verbatim: @<Scan a verbatim string@>@;
+ case ascii_constant: /* fake into looking like quoted char */
+ @<Get a string@>;
+ case octal: @<Get an octal constant@>;
+ case hex: @<Get a hex constant@>;
+ default: return(ccode[c]);
+ }
+}
+
+@ The occurrence of a module name sets |xref_switch| to zero,
+because the module name might (for example) follow \&{int}.
+
+@<Scan the module name...@>= {
+ ASCII *k; /* pointer into |mod_text| */
+ cur_module_char=c; /* remember |'<'| or |'('| */
+ @<Put module name into |mod_text|@>;
+ if (k-mod_text>3 && strncmp(k-2,"...",3)==0) cur_module=prefix_lookup(mod_text+1,k-3);
+ else cur_module=mod_lookup(mod_text+1,k);
+ xref_switch=0; return(module_name);
+}
+
+@ @<Global...@>=ASCII cur_module_char;
+
+@ Module names are placed into the |mod_text| array with consecutive spaces,
+tabs, and carriage-returns replaced by single spaces. There will be no
+spaces at the beginning or the end. (We set |mod_text[0]=' '| to facilitate
+this, since the |mod_lookup| routine uses |mod_text[1]| as the first
+character of the name.)
+
+@<Set init...@>=mod_text[0]=' ';
+
+@ @<Put module name...@>=
+k=mod_text;
+while (1) {
+ if (loc>limit && get_line()==0) {
+ err_print("! Input ended in section name");
+@.Input ended in section name@>
+ loc=buffer+1; break;
+ }
+ c=*loc;
+ @<If end of name, |break|@>;
+ loc++; if (k<mod_text_end) k++;
+ if (c==' ' || c==tab_mark) {
+ c=' '; if (*(k-1)==' ') k--;
+ }
+*k=c;
+}
+if (k>=mod_text_end) {
+ printf("\n! Section name too long: ");
+@.Section name too long@>
+ ASCII_write(mod_text+1,25);
+ printf("..."); mark_harmless;
+}
+if (*k==' ' && k>mod_text) k--;
+
+@ @<If end of name,...@>=
+if (c==at_sign) {
+ c=*(loc+1);
+ if (c=='>') {
+ loc+=2; break;
+ }
+ if (ccode[c]==new_module) {
+ err_print("! Section name didn't end"); break;
+@.Section name didn't end@>
+ }
+ *(++k)=at_sign; loc++; /* now |c==*loc| again */
+}
+
+@ @<Scan to the next...@>= {
+ c=ccode[*(loc-1)]; id_first=loc; *(limit+1)=at_sign;
+ while (*loc!=at_sign) loc++;
+ id_loc=loc;
+ if (loc++>limit) {
+ err_print("! Control text didn't end"); loc=limit; return(c);
+@.Control text didn't end@>
+ }
+ if (*loc++!='>') err_print("! Control codes are forbidden in control text");
+@.Control codes are forbidden...@>
+ return(c);
+}
+
+@ At the present point in the program we
+have |*(loc-1)=verbatim|; we set |id_first| to the beginning
+of the string itself, and |id_loc| to its ending-plus-one location in the
+buffer. We also set |loc| to the position just after the ending delimiter.
+
+@<Scan a verbatim string@>= {
+ id_first=loc++; *(limit+1)=at_sign; *(limit+2)='>';
+ while (*loc!=at_sign || *(loc+1)!='>') loc++;
+ if (loc>=limit) err_print("! Verbatim string didn't end");
+@.Verbatim string didn't end@>
+ id_loc=loc; loc+=2;
+ return (verbatim);
+}
+
+@* Phase one processing.
+We now have accumulated enough subroutines to make it possible to carry out
+\.{WEAVE}'s first pass over the source file. If everything works right,
+both phase one and phase two of \.{WEAVE} will assign the same numbers to
+modules, and these numbers will agree with what \.{TANGLE} does.
+
+The global variable |next_control| often contains the most recent output of
+|get_next|; in interesting cases, this will be the control code that
+ended a module or part of a module.
+
+@<Global...@>=
+eight_bits next_control; /* control code waiting to be acting upon */
+
+@ The overall processing strategy in phase one has the following
+straightforward outline.
+
+@u phase_one() {
+phase=1; reset_input(); module_count=0;
+skip_limbo(); change_exists=0;
+while (!input_has_ended)
+ @<Store cross-reference data for the current module@>;
+changed_module[module_count]=change_exists;
+ /* the index changes if anything does */
+phase=2; /* prepare for second phase */
+@<Print error messages about unused or undefined module names@>;
+}
+
+@ @<Store cross-reference data...@>=
+{
+ if (++module_count==max_modules) stat_overflow("section number");
+ changed_module[module_count]=0; /* it will become 1 if any line changes */
+ if (*(loc-1)=='*') {
+ printf("*%d",module_count);
+ update_terminal; /* print a progress report */
+ }
+ @<Store cross-references in the \TeX\ part of a module@>;
+ @<Store cross-references in the definition part of a module@>;
+ @<Store cross-references in the \cee\ part of a module@>;
+ if (changed_module[module_count]) change_exists=1;
+}
+
+@ The |C_xref| subroutine stores references to identifiers in
+\cee\ text material beginning with the current value of |next_control|
+and continuing until |next_control| is `\.\{' or `\v', or until the next
+``milestone'' is passed (i.e., |next_control>=format|). If
+|next_control>=format| when |C_xref| is called, nothing will happen;
+but if |next_control="|"| upon entry, the procedure assumes that this is
+the `\v' preceding \cee\ text that is to be processed.
+
+The program uses the fact that our internal code numbers satisfy
+the relations |xref_roman=identifier+roman| and |xref_wildcard=identifier
++wildcard| and |xref_typewriter=identifier+typewriter| and |normal=0|.
+
+@u C_xref(see_v) /* makes cross-references for \cee\ identifiers */
+ char see_v;
+{
+ name_pointer p; /* a referenced name */
+ while (next_control<format) {
+ if (next_control>=identifier && next_control<=xref_typewriter) {
+ p=id_lookup(id_first, id_loc,next_control-identifier); new_xref(p);
+ }
+ next_control=get_next(see_v);
+ if (next_control==vertical_bar || next_control==begin_comment) return;
+ }
+}
+
+@ The |outer_xref| subroutine is like |C_xref| but it begins
+with |next_control!=vertical_bar| and ends with |next_control>=format|. Thus, it
+handles \cee\ text with embedded comments.
+
+@u outer_xref() /* extension of |C_xref| */
+{
+ int bal; /* brace level in comment */
+ while (next_control<format)
+ if (next_control!=begin_comment) C_xref(0);
+ else {
+ bal=copy_comment(1); next_control=vertical_bar;
+ while (bal>0) {
+ C_xref(1);
+ if (next_control==vertical_bar) bal=copy_comment(bal);
+ else bal=0; /* an error message will occur in phase two */
+ }
+ }
+}
+
+@ In the \TeX\ part of a module, cross-reference entries are made only for
+the identifiers in \cee\ texts enclosed in \pb, or for control texts
+enclosed in \.{@@\^}$\,\ldots\,$\.{@@>} or \.{@@.}$\,\ldots\,$\.{@@>}
+or \.{@@:}$\,\ldots\,$\.{@@>}.
+
+@<Store cross-references in the \T...@>=
+while (1) {
+ switch (next_control=skip_TeX()) {
+ case underline: xref_switch=def_flag; continue;
+#ifdef DEBUG
+ case trace: tracing=next_control-'0'; continue;
+#endif DEBUG
+ case vertical_bar: C_xref(1); break;
+ case xref_roman: case xref_wildcard:
+ case xref_typewriter: case module_name:
+ loc-=2; next_control=get_next(1); /* scan to \.{@@>} */
+ if (next_control!=module_name) {
+/* |printf ("\nweave debugging: new xref: ");| */
+/* |{char *p; for (p=id_first;p<id_loc;p++) putchar(*p);}| */
+/* |putchar('\n');|
+ */
+ new_xref(id_lookup(id_first, id_loc,next_control-identifier));
+ }
+ break;
+ }
+ if (next_control>=format) break;
+}
+
+@ During the definition and \cee\ parts of a module, cross-references
+are made for all identifiers except reserved words; however, the
+identifiers in a format definition are referenced even if they are
+reserved. The \TeX\ code in comments is, of course, ignored, except for
+\cee\ portions enclosed in \pb; the text of a module name is skipped
+entirely, even if it contains \pb\ constructions.
+
+The variables |lhs| and |rhs| point to the respective identifiers involved
+in a format definition.
+
+@<Global...@>=
+name_pointer lhs, rhs; /* pointers to |byte_start| for format identifiers */
+
+@ When we get to the following code we have |next_control>=format|.
+
+@<Store cross-references in the d...@>=
+while (next_control<=definition) { /* |format| or |definition| */
+ xref_switch=def_flag; /* implied \.{@@!} */
+ if (next_control==definition) next_control=get_next(1);
+ else @<Process a format definition@>;
+ outer_xref();
+}
+
+@ Error messages for improper format definitions will be issued in phase
+two. Our job in phase one is to define the |ilk| of a properly formatted
+identifier, and to fool the |new_xref| routine into thinking that the
+identifier on the right-hand side of the format definition is not a
+reserved word.
+
+@<Process a form...@>= {
+ next_control=get_next(1);
+ if (next_control==identifier) {
+ lhs=id_lookup(id_first, id_loc,normal); lhs->ilk=normal; new_xref(lhs);
+ next_control=get_next(1);
+ if (next_control==identifier) {
+ rhs=id_lookup(id_first, id_loc,normal);
+ lhs->ilk=rhs->ilk; rhs->ilk=normal; new_xref(rhs);
+ rhs->ilk=lhs->ilk; next_control=get_next(1);
+ }
+ }
+}
+
+@ Finally, when the \TeX\ and definition parts have been treated, we have
+|next_control>=begin_unnamed|.
+
+@<Store cross-references in the \cee...@>=
+if (next_control<=module_name) { /* |begin_unnamed| or |module_name| */
+ if (next_control==begin_unnamed) mod_xref_switch=0;
+ else mod_xref_switch=(cur_module_char=='<' ? def_flag: file_flag);
+ do {
+ if (next_control==module_name && cur_module!=NULL) new_mod_xref(cur_module);
+ next_control=get_next(1); outer_xref();
+ } while ( next_control<=module_name);
+}
+
+@ After phase one has looked at everything, we want to check that each
+module name was both defined and used. The variable |cur_xref| will point
+to cross-references for the current module name of interest.
+
+@<Global...@>=
+xref_pointer cur_xref; /* temporary cross-reference pointer */
+
+@ The following recursive procedure
+walks through the tree of module names and prints out anomalies.
+@^recursion@>
+
+@u mod_check(p) name_pointer p; /* print anomalies in subtree |p| */
+{
+ int level; /* 0: use 1: definition 2: file definition */
+ if (p) {
+ mod_check(p->llink);
+ cur_xref=(xref_pointer)p->xref;
+ level=(cur_xref->num)/def_flag;
+ if (level==0) {
+ printf("\n! Never defined: <"); print_id(p); putchar('>'); mark_harmless;
+@.Never defined: <section name>@>
+ }
+ while (cur_xref->num >=def_flag) {
+ if ((cur_xref->num)/def_flag != level) {
+ printf("\n! You can't use <"); print_id(p);
+ printf("> both as a file and as a named module"); mark_harmless;
+@.You can't use <section name> both as a file...@>
+ }
+ cur_xref=cur_xref->xlink;
+ }
+ if (cur_xref==xmem && level<2) {
+ printf("\n! Never used: <"); print_id(p); putchar('>'); mark_harmless;
+@.Never used: <section name>@>
+ }
+ else if (cur_xref!=xmem && level==2) {
+ printf("\n! You can't use file module ("); print_id(p);
+ putchar(')'); mark_harmless;
+@.You can't use file module (file name)@>
+ }
+ mod_check(p->rlink);
+ }
+}
+
+@ @<Print error messages about un...@>=mod_check(root)
+
+@* Low-level output routines.
+The \TeX\ output is supposed to appear in lines at most |line_length|
+characters long, so we place it into an output buffer. During the output
+process, |out_line| will hold the current line number of the line about to
+be output.
+
+@<Global...@>=
+ASCII out_buf[line_length+1]; /* assembled characters */
+ASCII *out_ptr; /* just after last character in |out_buf| */
+ASCII *out_buf_end = out_buf+line_length; /* end of |out_buf| */
+int out_line; /* number of next line to be output */
+
+@ The |flush_buffer| routine empties the buffer up to a given breakpoint,
+and moves any remaining characters to the beginning of the next line.
+If the |per_cent| parameter is 1 a |'%'| is appended to the line
+that is being output; in this case the breakpoint |b| should be strictly
+less than |out_buf_end|. If the |per_cent| parameter is |0|,
+trailing blanks are suppressed.
+The characters emptied from the buffer form a new line of output.
+
+The same caveat that applies to |ASCII_write| applies to |c_line_write|.
+
+@d c_line_write(c) = fflush(tex_file),write(fileno(tex_file),out_buf+1,c)@;
+@d tex_putxchar(c) = putc(xchr[c],tex_file)@;
+@d tex_new_line = putc('\n',tex_file)@;
+@d tex_printf(c) = fprintf(tex_file,c)@;
+
+@u flush_buffer(b,per_cent)
+ASCII *b;
+boolean per_cent; /* outputs from |out_buf+1| to |b|,where |b<=out_ptr| */
+{
+ ASCII *j; j=b; /* pointer into |out_buffer| */
+ if (! per_cent) /* remove trailing blanks */
+ while (j>out_buf && *j==' ') j--;
+ c_line_write(j-out_buf);
+ if (per_cent) tex_putxchar('%');
+ tex_new_line; out_line++;
+ if (b<out_ptr) strncpy(out_buf+1,b+1,out_ptr-b);
+ out_ptr-=b-out_buf;
+}
+
+@ When we are copying \TeX\ source material, we retain line breaks
+that occur in the input, except that an empty line is not
+output when the \TeX\ source line was nonempty. For example, a line
+of the \TeX\ file that contains only an index cross-reference entry
+will not be copied. The |finish_line| routine is called just before
+|get_line| inputs a new line, and just after a line break token has
+been emitted during the output of translated \cee\ text.
+
+@u finish_line() /* do this at the end of a line */
+{
+ ASCII *k; /* pointer into |buffer| */
+ if (out_ptr>out_buf) flush_buffer(out_ptr,0);
+ else {
+ for (k=buffer; k<=limit; k++)
+ if (*k!=' ' && *k!=tab_mark) return;
+ flush_buffer(out_buf,0);
+ }
+}
+
+@ In particular, the |finish_line| procedure is called near the very
+beginning of phase two. We initialize the output variables in a slightly
+tricky way so that the first line of the output file will be a command
+to read in the macro file.
+
+@<Set init...@>=
+out_ptr=out_buf+1; out_line=1;
+@<Set |out_ptr| and do a |tex_printf| to read the macros@>
+
+@ When we wish to append one character |c| to the output buffer, we write
+`|out(c)|'; this will cause the buffer to be emptied if it was already
+full. If we want to append more than one character at once, we say
+|out_str(s)|, where |s| is a string containing the characters,
+or |out_str_del(s,t)|, where |s| and |t| point to the same array of
+characters; characters from |s| to |t-1|, inclusive, are output.
+
+A line break will occur at a space or after a single-nonletter
+\TeX\ control sequence.
+
+@d out(c) = {if (out_ptr>=out_buf_end) break_out(); *(++out_ptr)=c;}
+
+@u out_str_del(s,t) /* output characters from |s| to |t-1| */
+ASCII *s, *t;
+{
+ while (s<t) out(*s++);
+}
+
+out_str(s) /* output characters from |s| to end of string */
+ASCII *s;
+{
+ while (*s) out(*s++);
+}
+
+@ The |break_out| routine is called just before the output buffer is about
+to overflow. To make this routine a little faster, we initialize position
+0 of the output buffer to `\.\\'; this character isn't really output.
+
+@<Set init...@>=
+out_buf[0]='\\';
+
+@ A long line is broken at a blank space or just before a backslash that isn't
+preceded by another backslash. In the latter case, a |'%'| is output at
+the break.
+
+@u break_out() /* finds a way to break the output line */
+{
+ ASCII *k=out_ptr; /* pointer into |out_buf| */
+ while (1) {
+ if (k==out_buf) @<Print warning message, break the line, |return|@>;
+ if (*k==' ') {
+ flush_buffer(k,0); return;
+ }
+ if (*(k--)=='\\' && *k!='\\') { /* we've decreased |k| */
+ flush_buffer(k,1); return;
+ }
+ }
+}
+
+@ We get to this module only in unusual cases that the entire output line
+consists of a string of backslashes followed by a string of nonblank
+non-backslashes. In such cases it is almost always safe to break the
+line by putting a |'%'| just before the last character.
+
+@<Print warning message...@>=
+{
+ printf("\n! Line had to be broken (output l. %d):\n",out_line);
+@.Line had to be broken@>
+ ASCII_write(out_buf+1, out_ptr-out_buf-1);
+ new_line; mark_harmless;
+ flush_buffer(out_ptr-1,1); return;
+}
+
+@ Here is a macro that outputs a module number in decimal notation.
+The number to be converted by |out_mod| is known to be less than
+|def_flag|, so it cannot have more than five decimal digits. If
+the module is changed, we output `\.{\\*}' just after the number.
+
+@u out_mod(n) sixteen_bits n;
+{
+ ASCII s[6];
+ sprintf(s,"%d",n); out_str(s);
+ if(changed_module[n]) out_str ("\\*");
+}
+
+@ The |out_name| procedure is used to output an identifier or index
+entry, enclosing it in braces.
+
+@u out_name(p) name_pointer p; {
+ ASCII *k, *k_end=(p+1)->byte_start; /* pointers into |byte_mem| */
+ out('{');
+ for (k=p->byte_start; k<k_end; k++) {
+ if (*k=='$') {out('\\'); out('D'); out('O'); out(' ');}
+ else if (*k=='&') {out('\\'); out('a'); out('m'); out('p');}
+ else {
+ if (*k=='_' || *k=='%' || *k=='#' || *k=='^' || *k=='{' || *k=='}')
+ out('\\');
+ out(*k);
+ }
+ }
+ out('}');
+}
+
+@* Routines that copy \TeX\ material.
+During phase two, we use the subroutines |copy_limbo|, |copy_TeX|, and
+|copy_comment| in place of the analogous |skip_limbo|, |skip_TeX|, and
+|skip_comment| that were used in phase one.
+
+The |copy_limbo| routine, for example, takes \TeX\ material that is not
+part of any module and transcribes it almost verbatim to the output file.
+No `\.{@@}' signs should occur in such material except in `\.{@@@@}'
+pairs; such pairs are replaced by singletons.
+
+@u copy_limbo()
+{
+ ASCII c;
+ while (1) {
+ if (loc>limit && (finish_line(), get_line()==0)) return;
+ *(limit+1)=at_sign;
+ while (*loc!=at_sign) out(*(loc++));
+ if (loc++<=limit) {
+ c=*loc++;
+ if (ccode[c]==new_module) break;
+ if (c!='z' && c!='Z') {
+ out(at_sign);
+ if (c!=at_sign) err_print("! Double @@ required outside of sections");
+@.Double \AT! required...@>
+ }
+ }
+ }
+}
+
+
+@ The |copy_TeX| routine processes the \TeX\ code at the beginning of a
+module; for example, the words you are now reading were copied in this
+way. It returns the next control code or `\v' found in the input.
+We don't copy spaces or tab marks into the beginning of a line. This
+makes the test for empty lines in |finish_line| work.
+
+@<Global...@>= eight_bits next_control; /* control code found */
+
+@ @u eight_bits copy_TeX()
+{
+ ASCII c; /* current character being copied */
+ while (1) {
+ if (loc>limit && (finish_line(), get_line()==0)) return(new_module);
+ *(limit+1)=at_sign;
+ while ((c=*(loc++))!=vertical_char && c!=at_sign) {
+ out(c);
+ if (out_ptr==out_buf+1 && (c==' ' || c==tab_mark)) out_ptr--;
+ }
+ if (c==vertical_char) return(vertical_bar);
+ if (loc<=limit) return(ccode[*(loc++)]);
+ }
+}
+
+@ The |copy_comment| function issues a warning if more braces are opened than
+closed, and in the case of a more serious error it supplies enough
+braces to keep \TeX\ from complaining about unbalanced braces.
+Instead of copying the \TeX\ material
+into the output buffer, this function copies it into the token memory.
+The abbreviation |app_tok(t)| is used to append token |t| to the current
+token list, and it also makes sure that it is possible to append at least
+one further token without overflow.
+
+Copies to end and then follows end of comment with a right brace.
+
+@d app_tok(c) = {if (tok_ptr+2>tok_mem_end) stat_overflow("token"); *(tok_ptr++)=c;}
+
+@u
+copy_comment(bal) /* copies \TeX\ code in comments */
+int bal; /* brace balance */
+{
+ ASCII c; /* current character being copied */
+ while (1) {
+ if (loc>limit)
+ if (comments_end_with_newline) {
+ loc++; if(bal==1) {if (phase==2) app_tok('}'); return(0);}
+ else {
+ err_print("! Braces don't balance in comment");
+@.Braces don't balance in comment@>
+ @<Clear |bal| and |return|@>;
+ }
+ } else {
+ if (get_line()==0) {
+ err_print("! Input ended in mid-comment");
+@.Input ended in mid-comment@>
+ loc=buffer+1; @<Clear |bal| and |return|@>;
+ }
+ }
+
+ c=*(loc++);
+ if (c==vertical_char) return(bal);
+ @<Check for end of comment@>;
+ if (phase==2) app_tok(c);
+ @<Copy special things when |c==at_sign, '\\', '{', '}'|; |return| at end@>;
+ }
+}
+
+
+
+@ @<Copy special things when |c==at_sign...@>=
+if (c==at_sign) {
+ if (*(loc++)!=at_sign) {
+ err_print("! Illegal use of @@ in comment");
+@.Illegal use of \AT!...@>
+ loc-=2; if (phase==2) tok_ptr--; @<Clear |bal|...@>;
+ }
+}
+else if (c=='\\' && *loc!=at_sign && phase==2) app_tok(*(loc++))@;
+else if (c=='{') bal++;
+else if (c=='}') bal--;
+
+@ When the comment has terminated abruptly due to an error, we output
+enough right braces to keep \TeX\ happy.
+
+@<Clear |bal|...@>=
+app_tok(' '); /* this is done in case the previous character was `\.\\' */
+while (bal-- >0) app_tok('}');
+/* |if (see_end_of_line) next_control=end_of_line;| */
+return(0);
+
+@* Parsing.
+The most intricate part of \.{WEAVE} is its mechanism for converting
+\cee-like code into \TeX\ code, and we might as well plunge into this
+aspect of the program now. A ``bottom up'' approach is used to parse the
+\cee-like material, since \.{WEAVE} must deal with fragmentary
+constructions whose overall ``part of speech'' is not known.
+
+At the lowest level, the input is represented as a sequence of entities
+that we shall call {\it scraps}, where each scrap of information consists
+of two parts, its {\it category} and its {\it translation}. The category
+is essentially a syntactic class, and the translation is a token list that
+represents \TeX\ code. Rules of syntax and semantics tell us how to
+combine adjacent scraps into larger ones, and if we are lucky an entire
+\cee\ text that starts out as hundreds of small scraps will join
+together into one gigantic scrap whose translation is the desired \TeX\
+code. If we are unlucky, we will be left with several scraps that don't
+combine; their translations will simply be output, one by one.
+
+The combination rules are given as context-sensitive productions that are
+applied from left to right. Suppose that we are currently working on the
+sequence of scraps $s_1\,s_2\ldots s_n$. We try first to find the longest
+production that applies to an initial substring $s_1\,s_2\ldots\,$; but if
+no such productions exist, we find to find the longest production
+applicable to the next substring $s_2\,s_3\ldots\,$; and if that fails, we
+try to match $s_3\,s_4\ldots\,$, etc.
+
+A production applies if the category codes have a given pattern. For
+example, one of the productions is
+$$open\ math\ semi\ \RA\ open\ math$$
+and it means that three consecutive scraps whose respective categories are
+|open|, |math|, and |semi| are con\-verted to two scraps whose categories
+are |open| and |math|. This production also has an associated rule that
+tells how to combine the translation parts:
+$$\eqalign{O_2&=O_1\cr
+M_2&=M_1\,S\,\.{\\,}\,\hbox{|opt|\thinspace\tt5}\cr}$$
+This means that the |open| scrap has not changed, while the new |math| scrap
+has a translation $M_2$ composed of the translation $M_1$ of the original
+|math| scrap followed by the translation |S| of the |semi| scrap followed
+by `\.{\\,}' followed by `|opt|' followed by `\.5'. (In the \TeX\ file,
+this will specify an additional thin space after the semicolon, followed
+by an optional line break with penalty 50.) Translation rules use subscripts
+to distinguish between translations of scraps whose categories have the
+same initial letter; these subscripts are assigned from left to right.
+
+$\.{WEAVE}$ also has the production rule
+$$\hbox{|semi|$\;\RA\;$|terminator|}$$
+(meaning that a semicolon can terminate a \cee\ statement). Since
+productions are applied from left to right, this rule will be activated
+only if the |semi| is not preceded by scraps that match other productions;
+in particular, a |semi| that is preceded by `|open| |math|' will have
+disappeared because of the production above, and such semicolons do not
+act as statement terminators.
+
+The translation rule corresponding to $\hbox{|semi|$\;\RA\;$|terminator|}$ is
+$$T=S$$
+but we shall not mention translation rules in the common case that the
+translation of the new scrap on the right-hand side is simply the
+concatenation of the disappearing scraps on the left-hand side.
+
+
+
+@ The token lists for translated \TeX\ output contain some special control
+symbols as well as ordinary characters. These control symbols are
+interpreted by \.{WEAVE} before they are written to the output file.
+
+\yskip\hang |break_space| denotes an optional line break or an en space;
+
+\yskip\hang |force| denotes a line break;
+
+\yskip\hang |big_force| denotes a line break with additional vertical space;
+
+\yskip\hang |opt| denotes an optional line break (with the continuation
+line indented two ems with respect to the normal starting position)---this
+code is followed by an integer |n|, and the break will occur with penalty
+$10n$;
+
+\yskip\hang |backup| denotes a backspace of one em;
+
+\yskip\hang |cancel| obliterates any |break_space| or |force| or |big_force|
+tokens that immediately precede or follow it and also cancels any
+|backup| tokens that follow it;
+
+\yskip\hang |indent| causes future lines to be indented one more em;
+
+\yskip\hang |outdent| causes future lines to be indented one less em.
+
+\yskip\noindent All of these tokens are removed from the \TeX\ output that
+comes from \cee\ text between \pb\ signs; |break_space| and |force| and
+|big_force| become single spaces in this mode. The translation of other
+\cee\ texts results in \TeX\ control sequences \.{\\1}, \.{\\2},
+\.{\\3}, \.{\\4}, \.{\\5}, \.{\\6}, \.{\\7} corresponding respectively to
+|indent|, |outdent|, |opt|, |backup|, |break_space|, |force|, and
+|big_force|. However, a sequence of consecutive `\.\ ', |break_space|,
+|force|, and/or |big_force| tokens is first replaced by a single token
+(the maximum of the given ones).
+
+The tokens |math_rel| and |math_bin| will be translated into
+\.{\\mathrel\{} and \.{\\mathbin\{}, respectively.
+Also |math_op| to \.{\\mathop\{}.
+Other control sequences in the \TeX\ output will be `\.{\\\\\{}$\,\ldots\,$\.\}'
+surrounding identifiers, `\.{\\\&\{}$\,\ldots\,$\.\}' surrounding
+reserved words, `\.{\\.\{}$\,\ldots\,$\.\}' surrounding strings,
+`\.{\\cee\{}$\,\ldots\,$\.\}$\,$|force|' surrounding comments, and
+`\.{\\X$n$:}$\,\ldots\,$\.{\\X}' surrounding module names, where
+|n| is the module number.
+
+@d math_bin = @'205 /* should these be octal or decimal? */
+@d math_rel = @'206
+@d math_op = @'207
+@d big_cancel = @'210 /* like |cancel|, also overrides spaces */
+@d cancel = @'211 /* overrides |backup|, |break_space|, |force|, |big_force| */
+@d indent = cancel+1 /* one more tab (\.{\\1}) */
+@d outdent = cancel+2 /* one less tab (\.{\\2}) */
+@d opt = cancel+3 /* optional break in mid-statement (\.{\\3}) */
+@d backup = cancel+4 /* stick out one unit to the left (\.{\\4}) */
+@d break_space = cancel+5 /* optional break between statements (\.{\\5}) */
+@d force = cancel+6 /* forced break between statements (\.{\\6}) */
+@d big_force = cancel+7 /* forced break with additional space (\.{\\7}) */
+@d end_translation = big_force+1 /* special sentinel token at end of list */
+
+@ Here is a table of all the productions. The reader can best get a feel for
+@^productions, table of@>
+how they work by trying them out by hand on small examples; no amount of
+explanation will be as effective as watching the rules in action.
+Parsing can also be watched by debugging with `\.{@@2}'.
+
+@i grammar.web
+
+@* Implementing the productions.
+More specifically, a scrap is a structure consisting of a category
+|cat| and a |text_pointer| |trans|, which points to the translation in
+|tok_start|. When \cee\ text is to be processed with the grammar above,
+we form an array |scrap_info| containing the initial scraps.
+Our production rules have the nice property that the right-hand side is never
+longer than the left-hand side. Therefore it is convenient to use sequential
+allocation for the current sequence of scraps. Five pointers are used to
+manage the parsing:
+
+\yskip\hang |pp| is a pointer into |scrap_info|. We will try to match
+the category codes |pp->cat@,(pp+1)->cat|$\,\ldots\,$ to the left-hand sides
+of productions.
+
+\yskip\hang |scrap_base|, |lo_ptr|, |hi_ptr|, and |scrap_ptr| are such that
+the current sequence of scraps appears in positions |scrap_base| through
+|lo_ptr| and |hi_ptr| through |scrap_ptr|, inclusive, in the |cat| and
+|trans| arrays. Scraps located between |scrap_base| and |lo_ptr| have
+been examined, while those in positions |>=hi_ptr| have not yet been
+looked at by the parsing process.
+
+\yskip\noindent Initially |scrap_ptr| is set to the position of the final
+scrap to be parsed, and it doesn't change its value. The parsing process
+makes sure that |lo_ptr>=pp+3|, since productions have as many as four terms,
+by moving scraps from |hi_ptr| to |lo_ptr|. If there are
+fewer than |pp+3| scraps left, the positions up to |pp+3| are filled with
+blanks that will not match in any productions. Parsing stops when
+|pp=lo_ptr+1| and |hi_ptr=scrap_ptr+1|.
+
+Since the |scrap| structure will later be used for other purposes, we
+declare its second element as unions.
+
+@<Type...@>=
+typedef struct {
+ eight_bits cat;
+ eight_bits mathness;
+ union {
+ text_pointer Trans;
+ ===> @<Rest of |trans_plus| union@>@;
+ } trans_plus;
+} scrap;
+typedef scrap *scrap_pointer;
+
+@ @d trans = trans_plus.Trans /* translation texts of scraps */
+@d no_math = 2
+@d yes_math = 1
+@d maybe_math = 0
+@d left_math(A) = ((A)->mathness %4)
+@d right_math(A) = (((A)->mathness/4)%4)
+@d make_math(LM,RM) = ((eight_bits) (LM+4*(RM)))
+
+
+@<Global...@>=
+scrap scrap_info[max_scraps]; /* memory array for scraps */
+scrap_pointer scrap_info_end=scrap_info+max_scraps -1; /* end of |scrap_info| */
+scrap_pointer pp; /* current position for reducing productions */
+scrap_pointer scrap_base; /* beginning of the current scrap sequence */
+scrap_pointer scrap_ptr; /* ending of the current scrap sequence */
+scrap_pointer lo_ptr; /* last scrap that has been examined */
+scrap_pointer hi_ptr; /* first scrap that has not been examined */
+#ifdef STAT
+scrap_pointer max_scr_ptr; /* largest value assumed by |scrap_ptr| */
+#endif STAT
+
+@ @<Set init...@>=
+scrap_base=scrap_info+1;
+#ifdef STAT
+max_scr_ptr=
+#endif STAT
+scrap_ptr=scrap_info;
+
+@ Token lists in |@!tok_mem| are composed of the following kinds of
+items for \TeX\ output.
+
+\yskip\item{$\bullet$}ASCII codes and special codes like |force| and
+|math_rel| represent themselves;
+
+\item{$\bullet$}|id_flag+p| represents \.{\\\\\{{\rm identifier $p$}\}};
+
+\item{$\bullet$}|res_flag+p| represents \.{\\\&\{{\rm identifier $p$}\}};
+
+\item{$\bullet$}|mod_flag+p| represents module name |p|;
+
+\item{$\bullet$}|tok_flag+p| represents token list number |p|;
+
+\item{$\bullet$}|inner_tok_flag+p| represents token list number |p|, to be
+translated without line-break controls.
+
+@d id_flag = 10240 /* signifies an identifier */
+@d res_flag = 2*id_flag /* signifies a reserved word */
+@d mod_flag = 3*id_flag /* signifies a module name */
+@d tok_flag = 4*id_flag /* signifies a token list */
+@d inner_tok_flag = 5*id_flag /* signifies a token list in `\pb' */
+
+@u
+#ifdef DEBUG
+print_text(p) /* prints a token list */
+text_pointer p;
+{
+ token_pointer j; /* index into |tok_mem| */
+ sixteen_bits r; /* remainder of token after the flag has been stripped off */
+ if (p>=text_ptr) printf("BAD");
+ else for (j=*p; j<*(p+1); j++) {
+ r=*j%id_flag;
+ switch (*j/id_flag) {
+ case 1: printf("\\{"); print_id((name_dir+r)); printf("}"); break;
+ /* |id_flag| */
+ case 2: printf("\\&{"); print_id((name_dir+r)); printf("}"); break;
+ /* |res_flag| */
+ case 3: printf("<"); print_id((name_dir+r)); printf(">"); break;
+ /* |mod_flag| */
+ case 4: printf("[[%d]]",r); break; /* |tok_flag| */
+ case 5: printf("|[[%d]]|",r); break; /* |inner_tok_flag| */
+ default: @<Print token |r| in symbolic form@>;
+ }
+ }
+}
+#endif DEBUG
+
+@ @<Print token |r|...@>=
+switch (r) {
+ case math_bin: printf("\\mathbin{"); break;
+ case math_op: printf("\\mathop{"); break;
+ case math_rel: printf("\\mathrel{"); break;
+ case big_cancel: printf("[ccancel]"); break;
+ case cancel: printf("[cancel]"); break;
+ case indent: printf("[indent]"); break;
+ case outdent: printf("[outdent]"); break;
+ case backup: printf("[backup]"); break;
+ case opt: printf("[opt]"); break;
+ case break_space: printf("[break]"); break;
+ case force: printf("[force]"); break;
+ case big_force: printf("[fforce]"); break;
+ case end_translation: printf("[quit]"); break;
+ default: putxchar(r);
+}
+
+@ The production rules listed above are embedded directly into the \.{WEAVE}
+program, since it is easier to do this than to write an interpretive system
+that would handle production systems in general. Several macros are defined
+here so that the program for each production is fairly short.
+
+All of our productions conform to the general notion that some |k|
+consecutive scraps starting at some position |j| are to be replaced by a
+single scrap of some category |c| whose translations is composed from the
+translations of the disappearing scraps. After this production has been
+applied, the production pointer |pp| should change by an amount |d|. Such
+a production can be represented by the quadruple |(j,k,c,d)|. For example,
+the production `|simp@,math| $\RA$ |math|' would be represented by
+`|(pp,2,math,-1)|'; in this case the pointer |pp| should decrease by 1
+after the production has been applied, because some productions with
+|math| in their second positions might now match, but no productions have
+|math| in the third or fourth position of their left-hand sides. Note that
+the value of |d| is determined by the whole collection of productions, not
+by an individual one. Consider the further example
+`|var_head@,math@,colon| $\RA$ |var_head@,intro|', which is represented by
+`|(pp+1,2,intro,+1)|'; the $+1$ here is deduced by looking at the
+grammar and seeing that no matches could possibly occur at positions |<=pp|
+after this production has been applied. The determination of |d| has been
+done by hand in each case, based on the full set of productions but not on
+the grammar of \cee\ or on the rules for constructing the initial
+scraps.
+
+We also attach a serial number of each production, so that additional
+information is available when debugging. For example, the program below
+contains the statement `|reduce(pp+1,2,intro,+1,52)|' when it implements
+the production just mentioned.
+
+Before calling |reduce|, the program should have appended the tokens of
+the new translation to the |tok_mem| array. We commonly want to append
+copies of several existing translations, and macros are defined to
+simplify these common cases. For example, |small_app2(pp)| will append the
+translations of two consecutive scraps, |trans[pp]| and |trans[pp+1]|, to
+the current token list. If the entire new translation is formed in this
+way, we write `|squash(j,k,c,d)|' instead of `|reduce(j,k,c,d)|'. For
+example, `|squash(pp,2,math,-1)|' is an abbreviation for `|small_app2(pp);
+reduce(pp,2,math,-1)|'.
+
+The code below is an exact translation of the production rules into
+\cee, using such macros, and the reader should have no difficulty
+understanding the format by comparing the code with the symbolic
+productions as they were listed earlier.
+
+@d app2(a) = app1(a);app1(a+1)@;
+@d app3(a) = app2(a);app1(a+2)@;
+@d app4(a) = app3(a);app1(a+3)@;
+@d small_app(a) = *(tok_ptr++)=a@;
+@d small_app1(a) = *(tok_ptr++)=tok_flag+(a)->trans-tok_start@;
+
+@<Global...@>=
+int init_mathness, last_mathness;
+
+@ @u app_str(s)
+ASCII *s;
+{
+ while (*s) small_app(*(s++));
+}
+
+app(a)
+token a;
+{
+ if (a==' ' || a>=big_cancel && a<=big_force) /* non-math token */ {
+ if (last_mathness==maybe_math) init_mathness=no_math;
+ else if (last_mathness==yes_math) small_app('$');
+ last_mathness=no_math;
+ }
+ else {
+ if (last_mathness==maybe_math) init_mathness=yes_math;
+ else if (last_mathness==no_math) small_app('$');
+ last_mathness=last_mathness=yes_math;
+ }
+ small_app(a);
+}
+app1(a)
+scrap_pointer a;
+{
+ switch (left_math(a)) { /* left boundary */
+ case (no_math):
+ if (last_mathness==maybe_math) init_mathness=no_math;
+ if (last_mathness==yes_math) small_app('$');
+ last_mathness = right_math(a); /* right boundary */
+ break;
+ case (yes_math):
+ if (last_mathness==maybe_math) init_mathness=yes_math;
+ else if (last_mathness==no_math) small_app('$');
+ last_mathness = right_math(a); /* right boundary */
+ break;
+ case (maybe_math): /* no changes */
+ break;
+ }
+ small_app(tok_flag+(a)->trans-tok_start);
+}
+
+@ Let us consider the big switch for productions now, before looking
+at its context. We want to design the program so that this switch
+works, so we might as well not keep ourselves in suspense about exactly what
+code needs to be provided with a proper environment.
+
+@<Match a production at |pp|, or increase |pp| if there is no match@>= {
+ /* |ignore_scrap| becomes part of the grammar */
+ @<Test for all of the productions@>@;
+ pp++; /* if no match was found, we move to the right */
+}
+
+@ It may be that during phase two we discover from some arrangement
+of the scraps that an identifier should be treated as a defining instance,
+meaning its index entry should be underlined.
+Since we're in phase two, the identifier is buried inside some scrap,
+which may contain other things as well.
+Using Spider to {\em star} a scrap causes the first identifier in that
+scrap's translation to get an underlined index entry.
+The starring generates a call to |make_underlined|, which
+finds the first identifier with |first_id| and then underlines it
+with |underline_xref|.
+@u
+@<Definition of |first_id|@>@;
+make_underlined(p)
+ /* underline the entry for the first identifier in |p->trans| */
+scrap_pointer p;
+{
+ sixteen_bits tok_value; /* a token:
+ the name of this identifier, plus its flag */
+ /* Assume |p->trans < text_ptr| */
+
+ /* attempt to set |tok_value| to the first identifier in |p->trans| */
+ tok_value = first_id(p->trans);
+ if (tok_value==0) {
+#ifdef DEBUG
+ if (tracing>0) {
+ printf("\n! I couldn't find an identifier to underline.");
+ mark_harmless;
+ }
+#endif DEBUG
+ return;
+ }
+ if (tok_value<id_flag || tok_value>=res_flag)
+ fatal("", "! Internal error in first_id");
+@.Internal error in first_id@>
+ /* don't underline identifiers of length 1, even if starred --- force
+ the user to use |"@@!"| */
+ if (length(tok_value-id_flag+name_dir)>1)
+ underline_xref(tok_value-id_flag+name_dir);
+}
+
+@ |first_id| finds the first identifier in a translation.
+It is indefatigable.
+It returns a |token| value, or zero if it can't find an identifier.
+@<Definition of |first_id|@>=
+
+sixteen_bits first_id(p)
+ text_pointer p;
+{
+ token_pointer tp; /* used to search for the first identifier */
+ sixteen_bits r; /* remainder after modding out by |id_flag| */
+ sixteen_bits the_id; /* the id we find, or zero otherwise */
+
+ for (tp=*p; tp<*(p+1); tp++) {
+ r=*tp%id_flag;
+ switch (*tp/id_flag) {
+ case 1: /* |id_flag| --- found it */
+ return *tp;
+ break;
+ case 2: /* |res_flag| */
+ case 3: /* |mod_flag| */
+ goto next;
+ break;
+ case 4: /* |tok_flag| */
+ case 5: /* |inner_tok_flag| */
+ /* search the inner list */
+ if ((the_id = first_id(tok_start+r))!=0) return the_id;
+ goto next;
+ break;
+ default:
+ goto next;
+ break;
+ }
+ next: continue;
+ }
+ return 0;
+}
+
+@ We cannot use |new_xref| to underline a cross-reference at this point
+because this would just make a new cross-reference at the end of the list.
+We actually have to search through the list for the existing
+cross-reference.
+
+@u underline_xref(p)
+name_pointer p;
+{
+ xref_pointer q=(xref_pointer)p->xref;
+ /* pointer to cross-reference being examined */
+ xref_pointer r; /* temporary pointer for permuting cross-references */
+ sixteen_bits m; /* cross-reference value to be installed */
+ sixteen_bits n; /* cross-reference value being examined */
+ if (no_xref) return;
+ xref_switch=def_flag;
+ m=module_count+xref_switch;
+ while (q != xmem) {
+ n=q->num;
+ if (n==m) return;
+ else if (m==n+def_flag) {
+ q->num=m; return;
+ }
+ else if (n>=def_flag && n<m) break;
+ q=q->xlink;
+ }
+ @<Insert new cross-reference at |q|, not at beginning of list@>;
+}
+
+@ We get to this module only when the identifier is one letter long,
+so it didn't get a non-underlined entry during phase one. But it may
+have got some explicitly underlined entries in later modules, so in order
+to preserve the numerical order of the entries in the index, we have
+to insert the new cross-reference not at the beginning of the list
+(namely, at |p->xref|), but rather right before |q|.
+
+@<Insert new cross-reference at |q|...@>=
+ append_xref(0); /* this number doesn't matter */
+ xref_ptr->xlink=(xref_pointer)p->xref;
+ p->xref=(ASCII*)xref_ptr;
+ r=xref_ptr;
+ while (r->xlink!=q) {r->num=r->xlink->num; r=r->xlink;}
+ r->num=m; /* everything from |q| on is left undisturbed */
+
+
+
+@ The `|freeze_text|' macro is used to give official status to a token list.
+Before saying |freeze_text|, items are appended to the current token list,
+and we know that the eventual number of this token list will be the current
+value of |text_ptr|. But no list of that number really exists as yet,
+because no ending point for the current list has been
+stored in the |tok_start| array. After saying |freeze_text|, the
+old current token list becomes legitimate, and its number is the current
+value of |text_ptr-1| since |text_ptr| has been increased. The new
+current token list is empty and ready to be appended to.
+Note that |freeze_text| does not check to see that |text_ptr| hasn't gotten
+too large, since it is assumed that this test was done beforehand.
+
+@d freeze_text = *(++text_ptr)=tok_ptr@;
+
+@ @u reduce(j,k,c,d,n)
+scrap_pointer j;
+eight_bits c;
+short k, d, n;
+{
+ scrap_pointer i, i1; /* pointers into scrap memory */
+ j->cat=c; j->trans=text_ptr;
+ j->mathness=make_math(init_mathness,last_mathness);
+ freeze_text;
+ if (k>1) {
+ for (i=j+k, i1=j+1; i<=lo_ptr; i++, i1++) {
+ i1->cat=i->cat; i1->trans=i->trans;
+ i1->mathness=i->mathness;
+ }
+ lo_ptr=lo_ptr-k+1;
+ }
+ @<Change |pp| to $\max(|scrap_base|,|pp+d|)$@>;
+#ifdef DEBUG
+ @<Print a snapshot of the scrap list if debugging @>;
+#endif DEBUG
+ pp--; /* we next say |pp++| */
+}
+
+@ @<Change |pp| to $\max...@>=
+if (pp+d>=scrap_base) pp=pp+d;
+else pp=scrap_base;
+
+@ The `|squash|'
+procedure takes advantage of the simplification that occurs when |k=1|.
+{\bf `|squash|' isn't used in Spidery \.{WEB}.}
+@u squash(j,k,c,d,n)
+scrap_pointer j;
+eight_bits c;
+short k, d, n;
+{
+ scrap_pointer i; /* pointers into scrap memory */
+ if (k==1) {
+ j->cat=c; @<Change |pp|...@>;
+#ifdef DEBUG
+ @<Print a snapshot...@>;
+#endif DEBUG
+ pp--; /* we next say |pp++| */
+ return;
+ }
+ for (i=j; i<j+k; i++) app1(i);
+ reduce(j,k,c,d,n);
+}
+
+@ Here now is the code that applies productions as long as possible. It
+requires two local labels (|found| and |done|), as well as a local
+variable (|i|).
+
+@<Reduce the scraps using the productions until no more rules apply@>=
+while (1) {
+ @<Make sure the entries |pp| through |pp+highestposoverall-1|
+ of |cat| are defined@>;
+ if (tok_ptr+8>tok_mem_end || text_ptr+4>tok_start_end) {
+#ifdef STAT
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+#endif STAT
+ stat_overflow("token/text");
+ }
+ if (pp>lo_ptr) break;
+ init_mathness=last_mathness=maybe_math;
+ @<Match a production...@>;
+}
+
+@ If we get to the end of the scrap list, category codes equal to zero are
+stored, since zero does not match anything in a production.
+
+@<Make sure the entries...@>=
+if (lo_ptr<pp+highestposoverall-1) {
+ while (hi_ptr<=scrap_ptr && lo_ptr!=pp+highestposoverall-1) {
+ (++lo_ptr)->cat=hi_ptr->cat; lo_ptr->mathness=(hi_ptr)->mathness;
+ lo_ptr->trans=(hi_ptr++)->trans;
+ }
+ for (i=lo_ptr+1;i<=pp+highestposoverall-1;i++) i->cat=0;
+}
+
+@ If \.{WEAVE} is being run in debugging mode, the production numbers and
+current stack categories will be printed out when |tracing| is set to 2;
+a sequence of two or more irreducible scraps will be printed out when
+|tracing| is set to 1.
+
+@<Global...@>=
+#ifdef DEBUG
+int tracing; /* can be used to show parsing details */
+#endif DEBUG
+
+@ @<Print a snapshot...@>= {
+ scrap_pointer k; /* pointer into |scrap_info| */
+ if (tracing==2) {
+ printf("\n%d:",n);
+ for (k=scrap_base; k<=lo_ptr; k++) {
+ if (k==pp) putxchar('*'); else putxchar(' ');
+ if (left_math(k) == yes_math) putchar('+');
+ else if (left_math(k) == no_math) putchar('-');
+ print_cat(k->cat);
+ if (right_math(k)== yes_math) putchar('+');
+ else if (right_math(k) == no_math) putchar('-');
+ }
+ if (hi_ptr<=scrap_ptr) printf("..."); /* indicate that more is coming */
+ }
+}
+
+@ The |translate| function assumes that scraps have been stored in
+positions |scrap_base| through |scrap_ptr| of |cat| and |trans|. It
+appends a |terminator| scrap and begins to apply productions as much as
+possible. The result is a token list containing the translation of
+the given sequence of scraps.
+
+After calling |translate|, we will have |text_ptr+3<=max_texts| and
+|tok_ptr+6<=max_toks|, so it will be possible to create up to three token
+lists with up to six tokens without checking for overflow. Before calling
+|translate|, we should have |text_ptr<max_texts| and |scrap_ptr<max_scraps|,
+since |translate| might add a new text and a new scrap before it checks
+for overflow.
+
+@u text_pointer translate() /* converts a sequence of scraps */
+{
+ scrap_pointer i, /* index into |cat| */
+ j; /* runs through final scraps */
+ pp=scrap_base; lo_ptr=pp-1; hi_ptr=pp;
+ @<If tracing, print an indication of where we are@>;
+ @<Reduce the scraps...@>;
+ @<Combine the irreducible scraps that remain@>;
+}
+
+@ If the initial sequence of scraps does not reduce to a single scrap,
+we concatenate the translations of all remaining scraps, separated by
+blank spaces, with dollar signs
+placed according to the |mathness| of the scraps.
+
+@<Combine the irreducible...@>= {
+ @<If semi-tracing, show the irreducible scraps@>;
+ for (j=scrap_base; j<=lo_ptr; j++) {
+ if (j!=scrap_base) small_app(' ');
+ if ((left_math(j) == yes_math) && math_flag==0) small_app('$');
+ if ((left_math(j) == no_math) && math_flag==1) {
+ small_app(' '); small_app('$');}
+ small_app1(j);
+ if ((right_math(j) == yes_math) && math_flag==0) small_app('$');
+ if ((right_math(j) == no_math) && math_flag==1) {small_app('$');
+ small_app(' ');}
+ if (tok_ptr+6>tok_mem_end) stat_overflow("token");
+ }
+ freeze_text; return(text_ptr-1);
+}
+
+@ @<If semi-tracing, show the irreducible scraps@>=
+#ifdef DEBUG
+if (lo_ptr>scrap_base && tracing==1) {
+ printf("\nIrreducible scrap sequence in section %d:",module_count);
+ mark_harmless;
+ for (j=scrap_base; j<=lo_ptr; j++) {
+ printf(" "); print_cat(j->cat);
+ }
+}
+#endif DEBUG
+
+@ @<If tracing,...@>=
+#ifdef DEBUG
+if (tracing==2) {
+ printf("\nTracing after l. %d:\n",cur_line); mark_harmless;
+ if (loc>buffer+50) {
+ printf("...");
+ ASCII_write(loc-51,51);
+ }
+ else ASCII_write(buffer+1,loc-buffer);
+}
+#endif DEBUG
+
+@* Initializing the scraps.
+If we are going to use the powerful production mechanism just developed, we
+must get the scraps set up in the first place, given a \cee\ text.
+The raw input is converted into scraps according to the following table,
+which gives category codes followed by the translations. Sometimes a single
+item of input produces more than one scrap.
+\def\stars {\.{---}}%
+
+A comment in the input will be combined with the preceding
+|omega| or |semi| scrap, or with the following |terminator| scrap, if
+possible; otherwise it will be inserted as a separate |terminator| scrap.
+An additional ``comment'' is effectively appended at the end of the
+\PASCAL\ text, just before translation begins; this consists of a |cancel|
+token in the case of \PASCAL\ text in \pb, otherwise it consists of a
+|force| token.
+
+From this table it is evident that \.{WEAVE} will parse a lot of non-\PASCAL\
+programs. For example, the reserved words `\.{for}' and `\.{array}' are
+treated in an identical way by \.{WEAVE} from a syntactic standpoint,
+and semantically they are equivalent except that a forced line break occurs
+just before `\&{for}'; \PASCAL\ programmers may well be surprised at this
+similarity. The idea is to keep \.{WEAVE}'s rules as simple as possible,
+consistent with doing a reasonable job on syntactically correct \PASCAL\
+programs. The production rules below have been formulated in the same
+spirit of ``almost anything goes.''
+
+A table
+of the initial scraps corresponding to \cee\ tokens appeared above in the
+section on parsing; our goal now is to implement that table. We shall do this
+by implementing a subroutine called |C_parse| that is analogous to the
+|C_xref| routine used during phase one.
+
+Like |C_xref|, the |C_parse| procedure starts with the current
+value of |next_control| and it uses the operation |next_control=get_next|
+repeatedly to read \cee\ text until encountering the next `\v' or
+`\.\{' (begin comment symbol) , or until |next_control>=format|. The
+scraps corresponding to what
+it reads are appended into the |cat| and |trans| arrays, and |scrap_ptr|
+is advanced.
+
+|C_parse| should never be called with |next_control| equal to
+|begin_comment|, because the
+upper routines should be screening those out.
+
+@u C_parse(see_v) /* creates scraps from \cee\ tokens */
+ char see_v;
+{
+ name_pointer p; /* identifier designator */
+ while (next_control<format) {
+ @<Append the scrap appropriate to |next_control|@>;
+ next_control=get_next(see_v);
+ if (next_control==vertical_bar || next_control==begin_comment) return;
+ }
+}
+
+@ The following macro is used to append a scrap whose tokens have just
+been appended:
+
+@d app_scrap(c,M) = (++scrap_ptr)->cat=c; scrap_ptr->trans=text_ptr;
+scrap_ptr->mathness=make_math(M,M);
+freeze_text;
+
+@ @<Append the scr...@>=
+@<Make sure that there is room for at least four more scraps, six more
+tokens, and four more texts@>;
+switch (next_control) {
+ @<Cases for ordinary tokens@>@;
+ case string: case constant: case verbatim: @<Append a string or constant@>;
+ break;
+ case @`\n': @<Append a newline scrap@>; break;
+ case identifier: @<Append an identifier scrap@>; break;
+ case TeX_string: @<Append a \TeX\ string scrap@>; break;
+ case ignore: case vertical_bar:
+ break;
+case xref_roman: case xref_wildcard: case xref_typewriter:
+ break;
+ case thin_space: app_str("\\,"); app_scrap(SP_ignore_scrap,yes_math); break;
+ case math_break:
+ small_app(opt); app_str("0"); app_scrap(SP_ignore_scrap,yes_math); break;
+ case line_break:
+ app_str("\\0"); app_scrap(SP_ignore_scrap,yes_math); break;
+ case line_force:
+ small_app(force); app_scrap(SP_ignore_scrap,no_math); break;
+ case big_line_break:
+ small_app(big_force); app_scrap(SP_ignore_scrap,no_math); break;
+ case no_line_break:
+ small_app(big_cancel); small_app(' '); small_app(big_cancel);
+ app_scrap(SP_ignore_scrap,no_math); break;
+ case pseudo_semi:
+@<Append a |pseudo_semi| scrap@>
+break;
+ case join: app_str("\\J"); app_scrap(SP_ignore_scrap,no_math); break;
+ default: small_app(next_control); app_scrap(SP_ignore_scrap,no_math); break;
+}
+
+@ Since we haven't yet figured out how to compute the room required
+by looking at the productions, let's be paranoid.
+@d SCRAP_SLACK = 50
+@d TOK_SLACK = 50
+@d TEXT_SLACK = 50
+@<Make sure that there is room for at least four...@>=
+if (scrap_ptr+SCRAP_SLACK>scrap_info_end || tok_ptr+TOK_SLACK>tok_mem_end ||
+ text_ptr+TEXT_SLACK>tok_start_end) {
+#ifdef STAT
+ if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr;
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+#endif STAT
+ stat_overflow("scrap/token/text");
+}
+
+@ Some nonstandard ASCII characters may have entered \.{WEAVE} by means of
+standard ones. They are converted to \TeX\ control sequences so that it is
+possible to keep \.{WEAVE} from stepping beyond standard ASCII.
+
+@ The following code must use |app_tok| instead of |small_app| in order to
+protect against overflow. Note that |tok_ptr+1<=max_toks| after |app_tok|
+has been used, so another |small_app| is legitimate before testing again.
+
+Many of the special characters in a string must be prefixed by `\.\\' so that
+\TeX\ will print them properly.
+@^special string characters@>
+
+@<Append a string or...@>=
+if (next_control==constant) app_str("\\O{");
+@.\\O@>
+else if (next_control==string) app_str("\\.{");
+@.\\.@>
+else app_str("\\={");
+@.\\=@>
+while (id_first<id_loc) {
+ if (*id_first==at_sign) {
+ if (*(id_first+1)==at_sign) id_first++;
+ else err_print("! Double at_sign should be used in strings");
+ }
+@.Double at_sign should be used...@>
+ @<Cause \TeX's special characters to be preceded with |'\\'|@>@;
+ app_tok(*id_first++);
+}
+small_app('}');
+@<Do the |app_scrap| for a string or constant@>@;
+
+@ @<Cause \TeX's special characters to be preceded with |'\\'|@>=
+switch (*id_first) {
+ case ' ':case '\\':
+ case '%':case '$': case '^':case '`':
+ case '#':
+ case '{': case '}': case '~': case '&': case '_':
+ small_app('\\'); break;
+
+ }
+
+@ @<Append a \TeX\ string scrap@>=
+app_str("\\hbox{"); while (id_first<id_loc) app_tok(*id_first++);
+small_app('}');
+@<Do the |app_scrap| for a string or constant@>@;
+
+
+@ When the `\v' that introduces \cee\ text is sensed, a call on
+|C_translate| will return a pointer to the \TeX\ translation of
+that text. If scraps exist in |scrap_info|, they are
+unaffected by this translation process.
+
+@u text_pointer C_translate()
+{
+ text_pointer p; /* points to the translation */
+ scrap_pointer save_base; /* holds original value of |scrap_base| */
+ save_base=scrap_base; scrap_base=scrap_ptr+1;
+ C_parse(1); /* get the scraps together */
+ if (next_control!=vertical_bar) err_print("! Missing vertical_bar after C text");
+@.Missing vertical_bar...@>
+ app_tok(cancel); app_scrap(SP_ignore_scrap,no_math);
+ /* place a |cancel| token as a final ``comment'' */
+ p=translate(); /* make the translation */
+#ifdef STAT
+ if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr;
+#endif STAT
+ scrap_ptr=scrap_base-1; scrap_base=save_base; /* scrap the scraps */
+ return(p);
+}
+
+@ The |outer_parse| routine is to |C_parse| as |outer_xref|
+is to |C_xref|: it constructs a sequence of scraps for \cee\ text
+until |next_control>=format|. Thus, it takes care of embedded comments.
+It will also do annotation duty.
+
+
+@u outer_parse() /* makes scraps from \cee\ tokens and comments */
+{
+ int bal; /* brace level in comment */
+ text_pointer p, q; /* partial comments */
+ while (next_control<format) {
+ if (next_control==begin_comment) {
+ @<Make sure that there is room for at least seven more
+ tokens, three more texts, and one more scrap@>; /* spider */
+ @<Copy comment, including embedded program text@>;
+ }
+ else {
+ C_parse(0);
+ }
+}
+}
+
+@ @<Copy comment, including embedded program text@>=
+ small_app(break_space); app_str("\\C{");
+@.\\cee@>
+ bal=copy_comment(1); next_control=vertical_bar;
+ while (bal>0) {
+ p=text_ptr; freeze_text; q=C_translate();
+ /* at this point we have |tok_ptr+6<=max_toks| */ /* spider */
+ small_app(tok_flag+p-tok_start); small_app(inner_tok_flag+q-tok_start);
+ if (next_control==vertical_bar) bal=copy_comment(bal);
+ else bal=0; /* an error has been reported */
+ }
+ small_app(force);
+ app_scrap(SP_ignore_scrap,no_math);
+ /* the full comment becomes a scrap */
+
+
+@ @<Make sure that there is room for at least seven more...@>=
+if (tok_ptr+TOK_SLACK>tok_mem_end || text_ptr+TEXT_SLACK>tok_start_end
+ || scrap_ptr+SCRAP_SLACK>scrap_info_end) {
+#ifdef STAT
+ if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr;
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+#endif STAT
+ stat_overflow("token/text/scrap");
+}
+
+@i scraps.web
+
+@* Output of tokens.
+So far our programs have only built up multi-layered token lists in
+\.{WEAVE}'s internal memory; we have to figure out how to get them into
+the desired final form. The job of converting token lists to characters in
+the \TeX\ output file is not difficult, although it is an implicitly
+recursive process. Three main considerations had to be kept in mind when
+this part of \.{WEAVE} was designed: (a) There are two modes of output,
+|outer| mode that translates tokens like |force| into line-breaking
+control sequences, and |inner| mode that ignores them except that blank
+spaces take the place of line breaks. (b) The |cancel| instruction applies
+to adjacent token or tokens that are output, and this cuts across levels
+of recursion since `|cancel|' occurs at the beginning or end of a token
+list on one level. (c) The \TeX\ output file will be semi-readable if line
+breaks are inserted after the result of tokens like |break_space| and
+|force|. (d) The final line break should be suppressed, and there should
+be no |force| token output immediately after `\.{\\Y\\P}'.
+
+@ The output process uses a stack to keep track of what is going on at
+different ``levels'' as the token lists are being written out. Entries on
+this stack have three parts:
+
+\yskip\hang |end_field| is the |tok_mem| location where the token list of a
+particular level will end;
+
+\yskip\hang |tok_field| is the |tok_mem| location from which the next token
+on a particular level will be read;
+
+\yskip\hang |mode_field| is the current mode, either |inner| or |outer|.
+
+\yskip\noindent The current values of these quantities are referred to
+quite frequently, so they are stored in a separate place instead of in the
+|stack| array. We call the current values |cur_end|, |cur_tok|, and
+|cur_mode|.
+
+The global variable |stack_ptr| tells how many levels of output are
+currently in progress. The end of output occurs when an |end_translation|
+token is found, so the stack is never empty except when we first begin the
+output process.
+
+@d inner = 0 /* value of |mode| for \cee\ texts within \TeX\ texts */
+@d outer = 1 /* value of |mode| for \cee\ texts in modules */
+
+@<Typed...@>= typedef int mode;
+typedef struct {
+ token_pointer end_field; /* ending location of token list */
+ token_pointer tok_field; /* present location within token list */
+ boolean mode_field; /* interpretation of control tokens */
+} output_state;
+typedef output_state *stack_pointer;
+
+@ @d cur_end = cur_state.end_field /* current ending location in |tok_mem| */
+@d cur_tok = cur_state.tok_field /* location of next output token in |tok_mem| */
+@d cur_mode = cur_state.mode_field /* current mode of interpretation */
+@d init_stack = stack_ptr=stack;cur_mode=outer /* initialize the stack */
+
+@<Global...@>=
+output_state cur_state; /* |cur_end|, |cur_tok|, |cur_mode| */
+output_state stack[stack_size]; /* info for non-current levels */
+stack_pointer stack_ptr; /* first unused location in the output state stack */
+stack_pointer stack_end=stack+stack_size-1; /* end of |stack| */
+#ifdef STAT
+stack_pointer max_stack_ptr; /* largest value assumed by |stack_ptr| */
+#endif STAT
+
+@ @<Set init...@>=
+#ifdef STAT
+max_stack_ptr=stack;
+#endif STAT
+
+@ To insert token-list |p| into the output, the |push_level| subroutine
+is called; it saves the old level of output and gets a new one going.
+The value of |cur_mode| is not changed.
+
+@u push_level(p) /* suspends the current level */
+text_pointer p;
+{
+ if (stack_ptr==stack_end) stat_overflow("stack");
+ if (stack_ptr>stack) { /* save current state */
+ stack_ptr->end_field=cur_end;
+ stack_ptr->tok_field=cur_tok;
+ stack_ptr->mode_field=cur_mode;
+ }
+ stack_ptr++;
+#ifdef STAT
+ if (stack_ptr>max_stack_ptr) max_stack_ptr=stack_ptr;
+#endif STAT
+ cur_tok=*p; cur_end=*(p+1);
+}
+
+@ Conversely, the |pop_level| routine restores the conditions that were in
+force when the current level was begun. This subroutine will never be
+called when |stack_ptr=1|.
+
+@u pop_level()
+{
+ cur_end=(--stack_ptr)->end_field;
+ cur_tok=stack_ptr->tok_field; cur_mode=stack_ptr->mode_field;
+}
+
+@ The |get_output| function returns the next byte of output that is not a
+reference to a token list. It returns the values |identifier| or |res_word|
+or |mod_name| if the next token is to be an identifier (typeset in
+italics), a reserved word (typeset in boldface) or a module name (typeset
+by a complex routine that might generate additional levels of output).
+In these cases |cur_name| points to the identifier or module name in
+question.
+
+@<Global...@>=
+name_pointer cur_name;
+
+@ @d res_word = 0201 /* returned by |get_output| for reserved words */
+@d mod_name = 0200 /* returned by |get_output| for module names */
+
+@u eight_bits get_output() /* returns the next token of output */
+{
+ sixteen_bits a; /* current item read from |tok_mem| */
+ restart: while (cur_tok==cur_end) pop_level();
+ a=*(cur_tok++);
+ if (a>=0400) {
+ cur_name=a % id_flag + name_dir;
+ switch (a / id_flag) {
+ case 2: return(res_word); /* |a==res_flag+cur_name| */
+ case 3: return(mod_name); /* |a==mod_flag+cur_name| */
+ case 4: push_level(a % id_flag + tok_start); goto restart;
+ /* |a==tok_flag+cur_name| */
+ case 5: push_level(a % id_flag + tok_start); cur_mode=inner; goto restart;
+ /* |a==inner_tok_flag+cur_name| */
+ default: return(identifier); /* |a==id_flag+cur_name| */
+ }
+ }
+ return(a);
+}
+
+@ The real work associated with token output is done by |make_output|.
+This procedure appends an |end_translation| token to the current token list,
+and then it repeatedly calls |get_output| and feeds characters to the output
+buffer until reaching the |end_translation| sentinel. It is possible for
+|make_output| to be called recursively, since a module name may include
+embedded \cee\ text; however, the depth of recursion never exceeds one
+level, since module names cannot be inside of module names.
+
+A procedure called |output_C| does the scanning, translation, and
+output of \cee\ text within `\pb' brackets, and this procedure uses
+|make_output| to output the current token list. Thus, the recursive call
+of |make_output| actually occurs when |make_output| calls |output_C|
+while outputting the name of a module.
+@^recursion@>
+
+@u
+output_C() /* outputs the current token list */
+{
+ token_pointer save_tok_ptr;
+ text_pointer save_text_ptr;
+ sixteen_bits save_next_control; /* values to be restored */
+ text_pointer p; /* translation of the \cee\ text */
+ save_tok_ptr=tok_ptr; save_text_ptr=text_ptr;
+ save_next_control=next_control; next_control=vertical_bar; p=C_translate();
+ small_app(p-tok_start+inner_tok_flag);
+ make_output(); /* output the list */
+#ifdef STAT
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+#endif STAT
+ text_ptr=save_text_ptr; tok_ptr=save_tok_ptr; /* forget the tokens */
+ next_control=save_next_control; /* restore |next_control| to original state */
+}
+
+@ Here is \.{WEAVE}'s major output handler.
+
+@u make_output() /* outputs the equivalents of tokens */
+{
+ eight_bits a, /* current output byte */
+ b; /* next output byte */
+ int c; /* count of |indent| and |outdent| tokens */
+ ASCII *k, *k_limit; /* indices into |byte_mem| */
+ ASCII *j; /* index into |buffer| */
+ ASCII delim; /* first and last character of string being copied */
+ ASCII *save_loc, *save_limit; /* |loc| and |limit| to be restored */
+ name_pointer cur_mod_name; /* name of module being output */
+ boolean save_mode; /* value of |cur_mode| before a sequence of breaks */
+ small_app(end_translation); /* append a sentinel */
+ freeze_text; push_level(text_ptr-1);
+ while (1) {
+ a=get_output();
+ reswitch: switch(a) {
+ case end_translation: return;
+ case identifier: case res_word: @<Output an identifier@>; break;
+ case mod_name: @<Output a module name@>; break;
+ case math_bin: case math_rel: case math_op:
+ @<Output a \.{\\math} operator@>; break;
+ case cancel: c=0; while ((a=get_output())>=indent && a<=big_force) {
+ if (a==indent) c++; if (a==outdent) c--;
+ }
+ @<Output saved |indent| or |outdent| tokens@>;
+ goto reswitch;
+ case big_cancel: c=0;
+ while (((a=get_output())>=indent || a==' ') && a<=big_force) {
+ if (a==indent) c++; if (a==outdent) c--;
+ }
+ @<Output saved...@>;
+ goto reswitch;
+ case indent: case outdent: case opt: case backup: case break_space:
+ case force: case big_force: @<Output a control,
+ look ahead in case of line breaks, possibly |goto reswitch|@>; break;
+ default: out(a); /* otherwise |a| is an ASCII character */
+ }
+ }
+}
+
+@ An identifier of length one does not have to be enclosed in braces, and it
+looks slightly better if set in a math-italic font instead of a (slightly
+narrower) text-italic font. Thus we output `\.{\\\char'174a}' but
+`\.{\\\\\{aa\}}'.
+
+BUG FIX: use |out_name| even for identifiers of length 1, so that
+the single-character identifier |_| is properly escaped.
+@<Output an identifier@>=
+out('\\');
+if (a==identifier)
+ if (length(cur_name)==1) out('|')@;
+@.\\|@>
+ else out('\\')@;
+@.\\\\@>
+else out('&')@; /* |a==res_word| */
+@.\\\&@>
+out_name(cur_name);
+
+@ @<Output a \....@>=
+if (a==math_bin) out_str("\\mathbin{");
+else if (a==math_rel) out_str("\\mathrel{");
+else out_str("\\mathop{");
+
+@ The current mode does not affect the behavior of \.{WEAVE}'s output routine
+except when we are outputting control tokens.
+
+@<Output a control...@>=
+if (a<break_space) {
+ if (cur_mode==outer) {
+ out('\\'); out(a-cancel+'0');
+ if (a==opt) out(get_output()); /* |opt| is followed by a digit */
+ }
+ else if (a==opt) b=get_output(); /* ignore digit following |opt| */
+ }
+else @<Look ahead for strongest line break, |goto reswitch|@>@;
+
+@ If several of the tokens |break_space|, |force|, |big_force| occur in a
+row, possibly mixed with blank spaces (which are ignored),
+the largest one is used. A line break also occurs in the output file,
+except at the very end of the translation. The very first line break
+is suppressed (i.e., a line break that follows `\.{\\Y\\P}').
+
+@<Look ahead for st...@>= {
+ b=a; save_mode=cur_mode; c=0;
+ while (1) {
+ a=get_output();
+ if (a==cancel || a==big_cancel) {
+ @<Output saved |indent| or |outdent| tokens@>;
+ goto reswitch; /* |cancel| overrides everything */
+ }
+ if ((a!=' ' && a<indent) || a==backup || a>big_force) {
+ if (save_mode==outer) {
+ if (out_ptr>out_buf+3 && strncmp(out_ptr-3,"\\Y\\P",4)==0)
+ goto reswitch;
+ @<Output saved |indent| or |outdent| tokens@>;
+ out('\\'); out(b-cancel+'0');
+ if (a!=end_translation) finish_line();
+ }
+ else if (a!=end_translation && cur_mode==inner) out(' ');
+ goto reswitch;
+ }
+ if (a==indent) c++;
+ else if (a==outdent) c--;
+ else if (a>b) b=a; /* if |a==' '| we have |a<b| */
+ }
+}
+
+@ @<Output saved...@>=
+ for (;c>0;c--) out_str("\\1");
+ for (;c<0;c++) out_str("\\2");
+
+@ The remaining part of |make_output| is somewhat more complicated. When we
+output a module name, we may need to enter the parsing and translation
+routines, since the name may contain \cee\ code embedded in
+\pb\ constructions. This \cee\ code is placed at the end of the active
+input buffer and the translation process uses the end of the active
+|tok_mem| area.
+
+@<Output a module name@>= {
+ boolean is_file;
+ cur_xref=(xref_pointer)cur_name->xref;
+ is_file = cur_xref->num >= file_flag;
+
+ out_str((is_file? "\\XF":"\\X"));
+@.\\X@>
+ if (cur_xref->num>=def_flag) {
+ out_mod(cur_xref->num-(is_file ? file_flag : def_flag));
+ if (phase==3) {
+ cur_xref=cur_xref->xlink;
+ while (cur_xref->num>=def_flag) {
+ out_str(", ");
+ out_mod(cur_xref->num-(is_file ? file_flag : def_flag));
+ cur_xref=cur_xref->xlink;
+ }
+ }
+ }
+ else out('0'); /* output the module number, or zero if it was undefined */
+ out(':'); @<Output the text of the module name@>;
+ out_str((is_file? "\\XF":"\\X"));
+}
+
+@ @<Output the text...@>=
+k=cur_name->byte_start; k_limit=(cur_name+1)->byte_start;
+cur_mod_name=cur_name;
+while (k<k_limit) {
+ b=*(k++);
+ if (b==at_sign) @<Skip next character, give error if not `\.{@@}'@>@;
+ if (b!=vertical_char) out(b)@;
+ else {
+ @<Copy the \cee\ text into the |buffer| array@>@;
+ save_loc=loc; save_limit=limit; loc=limit+2; limit=j+1;
+ *limit=vertical_char; output_C();
+ loc=save_loc; limit=save_limit;
+ }
+}
+
+@ @<Skip next char...@>=
+if (*k++!=at_sign) {
+ printf("\n! Illegal control code in section name: <");
+@.Illegal control code...@>
+ print_id(cur_mod_name); printf("> "); mark_error;
+}
+
+@ The \cee\ text enclosed in \pb\ should not contain `\v' characters,
+except within strings. We put a `\v' at the front of the buffer, so that an
+error message that displays the whole buffer will look a little bit sensible.
+The variable |delim| is zero outside of strings, otherwise it
+equals the delimiter that began the string being copied.
+
+@<Copy the \cee\ text into...@>=
+j=limit+1; *j=vertical_char; delim=0;
+while (1) {
+ if (k>=k_limit) {
+ printf("\n! C text in section name didn't end: <");
+@.C text...didn't end@>
+ print_id(cur_mod_name); printf("> "); mark_error; break;
+ }
+ b=*(k++);
+ if (b==at_sign) @<Copy a control code into the buffer@>@;
+ else {
+ if (b=='\'' || b=='"')
+ if (delim==0) delim=b;
+ else if (delim==b) delim=0;
+ if (b!=vertical_char || delim!=0) {
+ if (j>buffer+long_buf_size-3) stat_overflow("buffer");
+ *(++j)=b;
+ }
+ else break;
+ }
+}
+
+@ @<Copy a control code into the buffer@>= {
+ if (j>buffer+long_buf_size-4) stat_overflow("buffer");
+ *(++j)=at_sign; *(++j)=*(k++);
+}
+
+@* Phase two processing.
+We have assembled enough pieces of the puzzle in order to be ready to specify
+the processing in \.{WEAVE}'s main pass over the source file. Phase two
+is analogous to phase one, except that more work is involved because we must
+actually output the \TeX\ material instead of merely looking at the
+\.{WEB} specifications.
+
+@u phase_two() {
+reset_input(); printf("\nWriting the output file...");
+module_count=0; copy_limbo();
+math_flag=0;
+finish_line(); flush_buffer(out_buf,0); /* insert a blank line, it looks nice */
+while (!input_has_ended) @<Translate the current module@>;
+}
+
+@ The output file will contain the control sequence \.{\\Y} between non-null
+sections of a module, e.g., between the \TeX\ and definition parts if both
+are nonempty. This puts a little white space between the parts when they are
+printed. However, we don't want \.{\\Y} to occur between two definitions
+within a single module. The variables |out_line| or |out_ptr| will
+change if a section is non-null, so the following macros `|save_position|'
+and `|emit_space_if_needed|' are able to handle the situation:
+
+@d save_position = save_line=out_line; save_place=out_ptr@;
+@d emit_space_if_needed = if (save_line!=out_line || save_place!=out_ptr)
+ out_str("\\Y");
+@.\\Y@>
+
+@<Global...@>=
+int save_line; /* former value of |out_line| */
+ASCII *save_place; /* former value of |out_ptr| */
+
+@ @<Translate the current module@>= {
+ module_count++;
+ @<Output the code for the beginning of a new module@>;
+ save_position;
+ @<Translate the \TeX\ part of the current module@>;
+ @<Translate the definition part of the current module@>;
+ @<Translate the \cee\ part of the current module@>;
+ @<Show cross-references to this module@>;
+ @<Output the code for the end of a module@>;
+}
+
+@ Modules beginning with the \.{WEB} control sequence `\.{@@\ }' start in the
+output with the \TeX\ control sequence `\.{\\M}', followed by the module
+number. Similarly, `\.{@@*}' modules lead to the control sequence `\.{\\N}'.
+If this is a changed module, we put \.{*} just before the module number.
+
+@<Output the code for the beginning...@>=
+if (*(loc-1)!='*') out_str("\\M");
+@.\\M@>
+else {
+ out_str("\\N");
+@.\\N@>
+ printf("*%d",module_count); update_terminal; /* print a progress report */
+}
+out_mod(module_count); out_str(". ");
+
+@ In the \TeX\ part of a module, we simply copy the source text, except that
+index entries are not copied and \cee\ text within \pb\ is translated.
+
+@<Translate the \T...@>= do {
+ next_control=copy_TeX();
+ switch (next_control) {
+ case vertical_bar: /* surround vertical bar with \.{\\CD...\\DC} */
+ out_str("\\CD{}");
+ init_stack; output_C();
+ out_str("\\DC{}");
+ break;
+ case at_sign: out(at_sign); break;
+ case octal: @<Translate an octal constant appearing in \TeX\ text@>; break;
+ case hex: @<Translate a hex constant appearing in \TeX\ text@>; break;
+ case TeX_string: case xref_roman: case xref_wildcard: case xref_typewriter:
+ case module_name: loc-=2; next_control=get_next(1); /* skip to \.{@@>} */
+ if (next_control==TeX_string)
+ err_print("! TeX string should be in C text only"); break;
+@.TeX string should be...@>
+ case thin_space: case math_break:
+ case line_break: case big_line_break: case no_line_break: case join:
+ case pseudo_semi: err_print("! You can't do that in TeX text"); break;
+@.You can't do that...@>
+ }
+} while (next_control<format);
+
+@ @<Translate an octal constant appearing in \TeX\ text@>= {
+out_str("\\O{\\~");
+ while ('0'<=*loc && *loc<'8') out(*loc++);
+out('}');
+}
+
+@ @<Translate a hex constant appearing in \TeX\ text@>= {
+out_str("\\O{\\^");
+ while (isxdigit(*loc)) {
+ out(islower(*loc) ? toupper(*loc):*loc);
+ loc++;
+ }
+out('}');
+}
+
+
+
+@ When we get to the following code we have |next_control>=format|, and
+the token memory is in its initial empty state.
+
+@<Translate the d...@>=
+if (next_control<=definition) { /* definition part non-empty */
+ emit_space_if_needed; save_position;
+}
+while (next_control<=definition) { /* |format| or |definition| */
+ init_stack;
+ if (next_control==definition) @<Start a macro definition@>@;
+ else @<Start a format definition@>;
+ outer_parse(); finish_C();
+}
+
+@ The |finish_C| procedure outputs the translation of the current
+scraps, preceded by the control sequence `\.{\\P}' and followed by the
+control sequence `\.{\\par}'. It also restores the token and scrap
+memories to their initial empty state.
+
+A |force| token is appended to the current scraps before translation
+takes place, so that the translation will normally end with \.{\\6} or
+\.{\\7} (the \TeX\ macros for |force| and |big_force|). This \.{\\6} or
+\.{\\7} is replaced by the concluding \.{\\par} or by \.{\\Y\\par}.
+
+@u finish_C() /* finishes a definition or a \cee\ part */
+{
+ text_pointer p; /* translation of the scraps */
+ out_str("\\P"); app_tok(force); app_scrap(SP_ignore_scrap,no_math);
+ p=translate();
+@.\\P@>
+ small_app(p-tok_start+tok_flag); make_output(); /* output the list */
+ if (out_ptr>out_buf+1)
+ if (*(out_ptr-1)=='\\')
+@.\\6@>
+@.\\7@>
+@.\\Y@>
+ if (*out_ptr=='6') out_ptr-=2; /* suppress ordinary force?! */
+ else if (*out_ptr=='7') *out_ptr='Y';
+ out_str("\\par"); finish_line();
+#ifdef STAT
+ if (text_ptr>max_text_ptr) max_text_ptr=text_ptr;
+ if (tok_ptr>max_tok_ptr) max_tok_ptr=tok_ptr;
+ if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr;
+#endif STAT
+ tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info;
+ /* forget the tokens and the scraps */
+}
+
+@
+@<Start a macro...@>= {
+ small_app(backup); app_str("\\D"); /* this will produce `\&{define}' */
+@.\\D@>
+@<Set |next_control| to the first non-newline token@>@;
+ if (next_control!=identifier)
+ err_print("! Improper macro definition");
+@.Improper macro definition@>
+ else {
+ small_app('$');
+ small_app(id_flag+id_lookup(id_first, id_loc,normal)-name_dir);
+ @<Scan the parameter list (if any),
+ and set |next_control| to the non-newline
+ token following the parameter list@>;
+ if (next_control==@`=') {
+ small_app('\\'); small_app('S'); /* equivalence sign */
+ @<Set |next_control| to the first non-newline token@>@;
+ } else {
+ err_print ("! Equals sign required in macro definition");
+@.Equals sign required...@>
+ }
+punt_the_definition:
+ small_app('$'); small_app(break_space);
+ app_scrap(SP_ignore_scrap,no_math);
+ /* scrap won't take part in the parsing */
+ }
+}
+
+@ @<Quit scanning the macro definition@>=goto punt_the_definition;
+
+@ @<Scan the parameter list (if any),
+ and set |next_control| to the non-newline
+ token following the parameter list@>=
+@<Set |next_control| to the first non-newline token@>@;
+if (next_control==@`(') {
+ small_app(@`(');
+ do {
+ @<Set |next_control| to the first non-newline token@>@;
+ if (next_control==identifier) {
+ small_app(id_flag+id_lookup(id_first, id_loc,normal)-name_dir);
+ @<Set |next_control| to the first non-newline token@>@;
+ } else {
+ err_print("! Improper macro definition");
+ @<Quit scanning the macro definition@>;
+ }
+ if (next_control==@`,' || next_control==@`)')
+ small_app(next_control);
+ } while (next_control==@`,');
+ if (next_control != @`)') {
+ err_print("! Macro parameter list must end with )");
+ @<Quit scanning the macro definition@>;
+ }
+ @<Set |next_control| to the first non-newline token@>@;
+ /* first token following parameter list */
+}
+
+
+
+
+@ @<Start a format...@>= {
+ app_str("\\F"); app_scrap(SP_ignore_scrap,no_math);
+ /* this will produce `\&{format}' */
+@.\\F@>
+@<Set |next_control| to the first non-newline token@>@;
+/* claim at this point |scrap_ptr==scrap_info+1| */
+ if (scrap_ptr!=scrap_info+1) {
+ err_print("! This can't happen -- bad scrap_ptr in format definition");
+ printf("\n\tscrap_ptr-scrap_info==%d\n",scrap_ptr-scrap_info);
+ }
+ if (next_control==identifier) {
+ small_app(id_flag+id_lookup(id_first, id_loc,normal)-name_dir);
+ app_str(" ");
+ app_scrap(SP_ignore_scrap,no_math); /*spider*/
+ /* this is syntactically separate from what follows */
+ @<Set |next_control| to the first non-newline token@>@;
+ if (next_control==identifier) {
+ small_app(id_flag+id_lookup(id_first, id_loc,normal)-name_dir);
+ small_app(@`\n');
+ app_scrap(SP_ignore_scrap,no_math);
+ @<Set |next_control| to the first non-newline token@>@;
+ }
+ }
+ /* if everything went well, we appended two scraps */
+ if (scrap_ptr!=scrap_info+3) err_print("! Improper format definition");
+@.Improper format definition@>
+}
+
+@ Finally, when the \TeX\ and definition parts have been treated, we have
+|next_control>=begin_unnamed|. We will make the global variable |this_module|
+point to the current module name, if it has a name.
+
+@<Global...@>=
+name_pointer this_module; /* the current module name, or zero */
+
+@ @<Translate the \cee...@>=
+this_module=name_dir;
+if (next_control<=module_name) {
+ emit_space_if_needed; init_stack;
+ if (next_control==begin_unnamed) next_control=get_next(0);
+ else {
+ this_module=cur_module;
+ @<Check that |=| follows this module name, and
+ emit the scraps to start the module definition@>;
+ }
+ while (next_control<=module_name) {
+ outer_parse();
+ @<Emit the scrap for a module name if present@>;
+ }
+ finish_C();
+}
+
+@ @<Check that |=|...@>=
+do next_control=get_next(0);
+ while (next_control=='+'); /* allow optional `\.{+=}' */
+if (next_control!='=')
+ err_print("! You need an = sign after the section name");
+@.You need an = sign...@>
+ else next_control=get_next(0);
+if (out_ptr>out_buf+1 && *out_ptr=='Y' && *(out_ptr-1)=='\\') small_app(backup);
+ /* the module name will be flush left */
+@.\\Y@>
+small_app(mod_flag+this_module-name_dir);
+cur_xref=(xref_pointer)this_module->xref;
+app_str("${}");
+if (cur_xref->num%def_flag!=module_count) {
+ app_str("+"); /*module name is multiply defined*/
+ this_module=name_dir; /*so we won't give cross-reference info here*/
+}
+app_str("\\S"); /* output an equivalence sign */
+@.\\S@>
+app_str("{}$");
+small_app(force);
+@<Call |app_scrap| for a module definition@>
+/* this forces a line break unless `\.{@@+}' follows */
+
+@ @<Emit the scrap...@>=
+if (next_control<module_name) {
+ err_print("! You can't do that in C text");
+@.You can"t do that...@>
+ next_control=get_next(1);
+}
+else if (next_control==module_name) {
+ if (cur_module_char!='<') {
+ err_print("! You can't use a file like a module");
+@.You can't use a file like a module@>
+ next_control=get_next(1);
+ } else {
+ small_app(mod_flag+cur_module-name_dir);
+ @<Call |app_scrap| for a module use@>
+ next_control=get_next(1);
+ }
+}
+
+@ Cross references relating to a named module are given after the module ends.
+
+@<Show cross...@>=
+if (this_module>name_dir) {
+ @<Rearrange the list pointed to by |cur_xref|@>;
+ footnote(((((xref_pointer)this_module->xref)->num >= file_flag)
+ ? file_flag : def_flag));
+ footnote(0);
+}
+
+@ To rearrange the order of the linked list of cross-references, we need
+four more variables that point to cross-reference entries. We'll end up
+with a list pointed to by |cur_xref|.
+
+@<Global...@>=
+xref_pointer next_xref, this_xref, first_xref, mid_xref;
+ /* pointer variables for rearranging a list */
+
+@ We want to rearrange the cross-reference list so that all the entries with
+|def_flag| come first, in ascending order; then come all the other
+entries, in ascending order. There may be no entries in either one or both
+of these categories.
+
+@<Rearrange the list...@>=
+ first_xref=(xref_pointer)this_module->xref;
+ this_xref=first_xref->xlink; /* bypass current module number */
+ if (this_xref->num>def_flag) {
+ mid_xref=this_xref; cur_xref=0; /* this value doesn't matter */
+ do {
+ next_xref=this_xref->xlink; this_xref->xlink=cur_xref;
+ cur_xref=this_xref; this_xref=next_xref;
+ } while (this_xref->num>def_flag);
+ first_xref->xlink=cur_xref;
+}
+else mid_xref=xmem; /* first list null */
+cur_xref=xmem;
+while (this_xref!=xmem) {
+ next_xref=this_xref->xlink; this_xref->xlink=cur_xref;
+ cur_xref=this_xref; this_xref=next_xref;
+}
+if (mid_xref>xmem) mid_xref->xlink=cur_xref;
+else first_xref->xlink=cur_xref;
+cur_xref=first_xref->xlink;
+
+@ The |footnote| procedure gives cross-reference information about
+multiply defined module names (if the |flag| parameter is |def_flag|), or about
+the uses of a module name (if the |flag| parameter is zero). It assumes that
+|cur_xref| points to the first cross-reference entry of interest, and it
+leaves |cur_xref| pointing to the first element not printed. Typical outputs:
+`\.{\\A\ section 101.}'; `\.{\\U\ sections 370 and 1009.}';
+`\.{\\A\ sections 8, 27\\*, and 64.}'.
+
+@u footnote(flag) /* outputs module cross-references */
+sixteen_bits flag;
+{
+ xref_pointer q; /* cross-reference pointer variable */
+ if (cur_xref->num<=flag) return;
+ finish_line(); out('\\');
+@.\\A@>
+@.\\U@>
+ if (flag==0) out('U')@;@+else out('A');
+ out_str(" section");
+ @<Output all the module numbers on the reference list |cur_xref|@>;
+ out('.');
+}
+
+@ The following code distinguishes three cases, according as the number
+of cross-references is one, two, or more than two. Variable |q| points
+to the first cross-reference, and the last link is a zero.
+
+@<Output all the module numbers...@>=
+q=cur_xref; if (q->xlink->num>flag) out('s'); /* plural */
+out('~');
+while (1) {
+ out_mod(cur_xref->num-flag);
+ cur_xref=cur_xref->xlink; /* point to the next cross-reference to output */
+ if (cur_xref->num<=flag) break;
+ if (cur_xref->xlink->num>flag || cur_xref!=q->xlink) out(',');
+ /* not the last of two */
+ out(' ');
+ if (cur_xref->xlink->num<=flag) out_str("and~"); /* the last */
+}
+
+@ @<Output the code for the end of a module@>=
+out_str("\\fi"); finish_line();
+@.\\fi@>
+flush_buffer(out_buf,0); /* insert a blank line, it looks nice */
+
+@* Phase three processing.
+We are nearly finished! \.{WEAVE}'s only remaining task is to write out the
+index, after sorting the identifiers and index entries.
+
+If the user has set the |no_xref| flag (the |-x| option on the command line),
+just finish off the page, omitting the index, module name list, and table of
+contents.
+
+@<Glob...@>=
+extern int no_xref;
+
+@ @u phase_three() {
+if (no_xref) {
+ finish_line();
+ out_str("\\vfill\\end");
+ finish_line();
+}
+else {
+ phase=3; printf("\nWriting the index...");
+ if (change_exists) {
+ finish_line(); @<Tell about changed modules@>;
+ }
+ finish_line(); out_str("\\inx"); finish_line();
+@.\\inx@>
+ @<Do the first pass of sorting@>;
+ @<Sort and output the index@>;
+ out_str("\\fin"); finish_line();
+@.\\fin@>
+ @<Output all the module names@>;
+ out_str("\\con"); finish_line();
+@.\\con@>
+}
+printf("Done.");
+check_complete(); /* was all of the change file used? */
+}
+
+@ Just before the index comes a list of all the changed modules, including
+the index module itself.
+
+@<Global...@>=
+sixteen_bits k_module; /* runs through the modules */
+
+@ @<Tell about changed modules@>= {
+ /* remember that the index is already marked as changed */
+ k_module=0;
+ while (!changed_module[++k_module]);
+ out_str("\\ch ");
+ out_mod(k_module);
+ while (1) {
+ while (!changed_module[++k_module]);
+ out_str(", "); out_mod(k_module);
+ if (k_module==module_count) break;
+ }
+ out('.');
+}
+
+@ A left-to-right radix sorting method is used, since this makes it easy to
+adjust the collating sequence and since the running time will be at worst
+proportional to the total length of all entries in the index. We put the
+identifiers into 102 different lists based on their first characters.
+(Uppercase letters are put into the same list as the corresponding lowercase
+letters, since we want to have `$t<\\{TeX}<\&{to}$'.) The
+list for character |c| begins at location |bucket[c]| and continues through
+the |blink| array.
+
+@<Global...@>=
+name_pointer bucket[128];
+name_pointer next_name; /* successor of |cur_name| when sorting */
+hash_pointer h; /* index into |hash| */
+name_pointer blink[max_names]; /* links in the buckets */
+
+@ To begin the sorting, we go through all the hash lists and put each entry
+having a nonempty cross-reference list into the proper bucket.
+
+@<Do the first pass...@>= {
+int c;
+for (c=0; c<=127; c++) bucket[c]=NULL;
+for (h=hash; h<=hash_end; h++) {
+ next_name=*h;
+ while (next_name) {
+ cur_name=next_name; next_name=cur_name->link;
+ if (((xref_pointer)cur_name->xref)!=xmem) {
+ c=(cur_name->byte_start)[0];
+ if (c<='Z' && c>='A') c=c+040;
+ blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name;
+ }
+ }
+}
+}
+
+@ During the sorting phase we shall use the |cat| and |trans| arrays from
+\.{WEAVE}'s parsing algorithm and rename them |depth| and |head|. They now
+represent a stack of identifier lists for all the index entries that have
+not yet been output. The variable |sort_ptr| tells how many such lists are
+present; the lists are output in reverse order (first |sort_ptr|, then
+|sort_ptr-1|, etc.). The |j|th list starts at |head[j]|, and if the first
+|k| characters of all entries on this list are known to be equal we have
+|depth[j]=k|.
+
+@ @<Rest of |trans_plus| union@>=
+name_pointer Head;
+
+@
+@d depth = cat /* reclaims memory that is no longer needed for parsing */
+@d head = trans_plus.Head /* ditto */
+@d sort_pointer = scrap_pointer /* ditto */
+@d sort_ptr = scrap_ptr /* ditto */
+@d max_sorts = max_scraps /* ditto */
+
+@<Global...@>=
+eight_bits cur_depth; /* depth of current buckets */
+ASCII *cur_byte; /* index into |byte_mem| */
+sixteen_bits cur_val; /* current cross-reference number */
+#ifdef STAT
+sort_pointer max_sort_ptr; /* largest value of |sort_ptr| */
+#endif STAT
+
+@ @<Set init...@>=
+#ifdef STAT
+max_sort_ptr=scrap_info;
+#endif STAT
+
+
+@ The desired alphabetic order is specified by the |collate| array; namely,
+|collate[0]<collate[1]<@t$\cdots$@><collate[100]|.
+
+@<Global...@>=
+ASCII collate[102]; /* collation order */
+
+@ We use the order $\hbox{null}<\.\ <\hbox{other characters}<\.\_<
+\.A=\.a<\cdots<\.Z=\.z<\.0<\cdots<\.9.$
+
+@<Set init...@>=
+collate[0]=0; strcpy(collate+1," \1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\
+\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\
+!\42#$%&'()*+,-./:;<=>?@@[\\]^`{|}~_\
+abcdefghijklmnopqrstuvwxyz0123456789");
+
+@ Procedure |unbucket| goes through the buckets and adds nonempty lists
+to the stack, using the collating sequence specified in the |collate| array.
+The parameter to |unbucket| tells the current depth in the buckets.
+Any two sequences that agree in their first 255 character positions are
+regarded as identical.
+
+@d infinity = 255 /* $\infty$ (approximately) */
+
+@u unbucket(d) /* empties buckets having depth |d| */
+eight_bits d;
+{
+ ASCII c; /* index into |bucket| */
+ for (c=100; c>= 0; c--) if (bucket[collate[c]]) {
+ if (sort_ptr>=scrap_info_end) stat_overflow("sorting");
+ sort_ptr++;
+#ifdef STAT
+ if (sort_ptr>max_sort_ptr) max_sort_ptr=sort_ptr;
+#endif STAT
+ if (c==0) sort_ptr->depth=infinity;
+ else sort_ptr->depth=d;
+ sort_ptr->head=bucket[collate[c]]; bucket[collate[c]]=NULL;
+ }
+}
+
+@ @<Sort and output...@>=
+sort_ptr=scrap_info; unbucket(1);
+while (sort_ptr>scrap_info) {
+ cur_depth=sort_ptr->depth;
+ if (blink[sort_ptr->head-name_dir]==0 || cur_depth==infinity)
+ @<Output index entries for the list at |sort_ptr|@>@;
+ else @<Split the list at |sort_ptr| into further lists@>;
+}
+
+@ @<Split the list...@>= {
+ ASCII c;
+ next_name=sort_ptr->head;
+ do {
+ cur_name=next_name; next_name=blink[cur_name-name_dir];
+ cur_byte=cur_name->byte_start+cur_depth;
+ if (cur_byte==(cur_name+1)->byte_start) c=0; /* hit end of the name */
+ else {
+ c=*cur_byte;
+ if (c<='Z' && c>='A') c=c+040;
+ }
+ blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name;
+ } while (next_name);
+ --sort_ptr; unbucket(cur_depth+1);
+}
+
+@ @<Output index...@>= {
+ cur_name=sort_ptr->head;
+ do {
+ out_str("\\:");
+@.\\:@>
+ @<Output the name at |cur_name|@>;
+ @<Output the cross-references at |cur_name|@>;
+ cur_name=blink[cur_name-name_dir];
+ } while (cur_name);
+ --sort_ptr;
+}
+
+@ @<Output the name...@>=
+switch (cur_name->ilk) {
+ case normal: if (length(cur_name)==1) out_str("\\|");
+ else out_str("\\\\"); break;
+@.\\|@>
+@.\\\\@>
+ case roman: break;
+ case wildcard: out_str("\\9"); break;
+@.\\9@>
+ case typewriter: out_str("\\."); break;
+@.\\.@>
+ default: out_str("\\&");
+@.\\\&@>
+}
+out_name(cur_name);
+
+@ Section numbers that are to be underlined are enclosed in
+`\.{\\[}$\,\ldots\,$\.]'.
+
+@<Output the cross-references...@>=
+@<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>;
+do {
+ out_str(", "); cur_val=cur_xref->num;
+ if (cur_val<def_flag) out_mod(cur_val);
+ else {out_str("\\["); out_mod(cur_val%def_flag); out(']');}
+@.\\[@>
+ cur_xref=cur_xref->xlink;
+} while (cur_xref!=xmem);
+out('.'); finish_line();
+
+@ List inversion is best thought of as popping elements off one stack and
+pushing them onto another. In this case |cur_xref| will be the head of
+the stack that we push things onto.
+
+@<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>=
+this_xref=(xref_pointer)cur_name->xref; cur_xref=xmem;
+do {
+ next_xref=this_xref->xlink; this_xref->xlink=cur_xref;
+ cur_xref=this_xref; this_xref=next_xref;
+} while (this_xref!=xmem);
+
+@ The following recursive procedure walks through the tree of module names and
+prints them.
+@^recursion@>
+
+@u mod_print(p) /* print all module names in subtree |p| */
+name_pointer p;
+{
+ boolean is_file;
+ if (p) {
+ mod_print(p->llink);
+ cur_xref=(xref_pointer)p->xref;
+ is_file=((cur_xref->num)>=file_flag);
+ if ((is_file && do_file)||(!is_file && !do_file)) { /* C has no xor */
+ out_str("\\:");
+@.\\:@>
+ tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info; init_stack;
+ small_app(p-name_dir+mod_flag); make_output();
+ footnote(0); /* |cur_xref| was set by |make_output| */
+ finish_line();
+ }
+ mod_print(p->rlink);
+ }
+}
+
+@ Here we list files, then modules.
+@<Global...@>=boolean do_file;
+@ @<Output all the module names@>=
+do_file=(1==1);
+mod_print(root);
+do_file=(1==0);
+mod_print(root);
+
+
+
+@ @<Print statistics about memory usage@>=
+printf(
+"\nMemory usage statistics: %d of %d names, %d of %d cross-references,\n",
+ name_ptr-name_dir, name_dir_end-name_dir,
+ xref_ptr-xmem, xmem_end-xmem);
+printf("\t %d of %d bytes;",byte_ptr-byte_mem,byte_mem_end-byte_mem);
+printf("\nParsing required %d of %d(%d) scraps, %d of %d(%d) texts,\n",
+ max_scr_ptr-scrap_info, max_scraps, max_scraps-SCRAP_SLACK,
+ max_text_ptr-tok_start, max_texts, max_texts-TEXT_SLACK
+ );
+printf("\t %d of %d(%d) tokens, %d of %d levels;\n",
+ max_tok_ptr-tok_mem, max_toks, max_toks-TOK_SLACK,
+ max_stack_ptr-stack, stack_end-stack
+ );
+printf("\nSorting required %d levels\n", max_sort_ptr-scrap_info);
+
+@ @u
+stat_overflow(s)
+ char *s;
+{
+ printf("\n! Sorry, capacity exceeded: %s",s);
+#ifdef STAT
+ @<Print statistics about memory usage@>;
+#endif STAT
+ history=fatal_message; wrap_up();
+}
+
+@* Index.
+If you have read and understood the code for Phase III above, you know what
+is in this index and how it got here. All modules in which an identifier is
+used are listed with that identifier, except that reserved words are
+indexed only when they appear in format definitions, and the appearances
+of identifiers in module names are not indexed. Underlined entries
+correspond to where the identifier was declared. Error messages, control
+sequences put into the output, and a few
+other things like ``recursion'' are indexed here too.
+
+
diff --git a/web/spiderweb/src/master/webhead b/web/spiderweb/src/master/webhead
new file mode 100644
index 0000000000..3dfb208171
--- /dev/null
+++ b/web/spiderweb/src/master/webhead
@@ -0,0 +1,2 @@
+@*Example.
+@c
diff --git a/web/spiderweb/src/master/webtail b/web/spiderweb/src/master/webtail
new file mode 100644
index 0000000000..3749661008
--- /dev/null
+++ b/web/spiderweb/src/master/webtail
@@ -0,0 +1 @@
+@*Index.