From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- web/spiderweb/src/master/Make.pretty | 18 + web/spiderweb/src/master/Makefile | 18 + web/spiderweb/src/master/WebMakefile | 146 + web/spiderweb/src/master/common.c | 1001 +++++++ web/spiderweb/src/master/common.h | 146 + web/spiderweb/src/master/common.web | 1198 ++++++++ web/spiderweb/src/master/cycle.awk | 86 + web/spiderweb/src/master/cycle.web | 81 + web/spiderweb/src/master/junk.list | 22 + web/spiderweb/src/master/multitang.ch | 107 + web/spiderweb/src/master/newdefs.awk | 11 + web/spiderweb/src/master/nodups.awk | 13 + web/spiderweb/src/master/pathopen.c | 122 + web/spiderweb/src/master/pathopen.h | 9 + web/spiderweb/src/master/pathopen.web | 192 ++ web/spiderweb/src/master/pretty.awk | 2 + web/spiderweb/src/master/spider.awk | 4799 ++++++++++++++++++++++++++++++ web/spiderweb/src/master/spider.web | 2580 ++++++++++++++++ web/spiderweb/src/master/tangle.web | 1800 +++++++++++ web/spiderweb/src/master/transcheck.awk | 23 + web/spiderweb/src/master/transcheck.list | 14 + web/spiderweb/src/master/weave.web | 3250 ++++++++++++++++++++ web/spiderweb/src/master/webhead | 2 + web/spiderweb/src/master/webtail | 1 + 24 files changed, 15641 insertions(+) create mode 100644 web/spiderweb/src/master/Make.pretty create mode 100644 web/spiderweb/src/master/Makefile create mode 100644 web/spiderweb/src/master/WebMakefile create mode 100644 web/spiderweb/src/master/common.c create mode 100644 web/spiderweb/src/master/common.h create mode 100644 web/spiderweb/src/master/common.web create mode 100644 web/spiderweb/src/master/cycle.awk create mode 100644 web/spiderweb/src/master/cycle.web create mode 100644 web/spiderweb/src/master/junk.list create mode 100644 web/spiderweb/src/master/multitang.ch create mode 100644 web/spiderweb/src/master/newdefs.awk create mode 100644 web/spiderweb/src/master/nodups.awk create mode 100644 web/spiderweb/src/master/pathopen.c create mode 100644 web/spiderweb/src/master/pathopen.h create mode 100644 web/spiderweb/src/master/pathopen.web create mode 100644 web/spiderweb/src/master/pretty.awk create mode 100644 web/spiderweb/src/master/spider.awk create mode 100644 web/spiderweb/src/master/spider.web create mode 100644 web/spiderweb/src/master/tangle.web create mode 100644 web/spiderweb/src/master/transcheck.awk create mode 100644 web/spiderweb/src/master/transcheck.list create mode 100644 web/spiderweb/src/master/weave.web create mode 100644 web/spiderweb/src/master/webhead create mode 100644 web/spiderweb/src/master/webtail (limited to 'web/spiderweb/src/master') 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 +#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 +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=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;k1)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(++ilink; +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(kbyte_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 + +@= +typedef short boolean; +typedef char unsigned eight_bits; +extern boolean program; /* \.{WEAVE} or \.{TANGLE}? */ +extern int phase; /* which phase are we in? */ + +@ @= +#include + +@ 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 */ + +@= +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. + +@= +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 */ + +@= +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); +} + +@= +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 */ + +@= +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: +@= +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: +@= +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) + +@= +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 +#ifdef MSDOS +#define index strchr +#endif + +@@; +@@; +@@; +@@; +@; + +@ 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 +@= +extern char the_at_sign; + + +@ @= +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. + +@= 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| */ + +@= +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. + +@= +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: + +@= +common_init() +{ + strncpy(xchr," !\"#$%&'()*+,-./0123456789\ +:;<=>?@@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ",@'200); + @; + @; + @; + setting_up=1; + @; + setting_up=0; +} + +@ We use this variable to tell us what to do in |err_print|. +@=char setting_up; + +@ The following system-independent code makes the |xord| array contain +a suitable inverse to the information in |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}' */ + +@= /* 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= +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@> + +@= +#include +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 */ + +@= +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)) + +@= +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. + +@= +prime_the_change_buffer() +{ + change_limit=0; /* this value will be used if the change file ends */ + @; + @; + @; +} + +@ 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). + +@= +while(1) { + change_line++; + if (!input_ln(change_file)) return; + if (limit; + @; + 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. + +@= +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. + +@= { + 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}. + +@= +do { + change_line++; + if (!input_ln(change_file)) { + err_print("! Change file ended after @@x"); +@.Change file ended...@> + return; + } +} while (limit==buffer); + +@ @= +{ + 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|. + +@= +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) + @; + @;@/ + @;@/ + 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 (limit>buffer+1 && buffer[0]==the_at_sign) { + @; + 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. + +@= +reset_input() +{ + limit=buffer; loc=buffer+1; buffer[0]=@` '; + @; + 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. +@= +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 */ + + + +@= +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 */ + +@ @= +get_line() /* inputs the next line */ +{ + restart: + if (changing) changed_module[module_count]=1; + else @; + if (changing) { + @; + if (! changing) { + changed_module[module_count]=1; goto restart; + } + } + loc=buffer; *limit=@` '; + if (*buffer==the_at_sign && (*(buffer+1)==@`i' || *(buffer+1)==@`I')) + @; + 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. +@= + pathaddpath(getenv("WEBPATH"),':'); +@ @= + pathaddname(*argv+2); +@ @= +@i pathopen.h +@ We need to define an |overflow| {\em function} for |pathopen| to call. +@ @= +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. + +@= { + 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 + } + else {cur_line=0; print_where=1;} + } + else { + include_depth--; + err_print("! Too many nested includes"); +@.Too many nested includes@> + } + } + goto restart; +} + +@ @= { + 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(); +} + +@ @= { + 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) { + @; + @; + 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|. + +@= +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 */ + +@= +typedef struct name_info { + ASCII *byte_start; /* beginning of the name in |byte_mem| */ + ===> @@; +} 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|. + +@= +name_pointer name_ptr; /* first unused position in |byte_start| */ +ASCII *byte_ptr; /* first unused position in |byte_mem| */ + +@ @= +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. + +@= +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 */ + +@= +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. + +@= +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 */ + @; + @; + if (p==name_ptr) @; + 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|.$$ + +@= +h=*i; while (++i= +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. + +@= { + 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 */ + +@= +union { + struct name_info *Rlink; /* right link in binary search tree for module + names */ + eight_bits Ilk; /* used by identifiers in \.{WEAVE} only */ +} dummy; + +@ @= +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 */ + +@= +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 (krlink| 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|. + +@= +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 + +@= +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. + +@= +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 { + @; + } + 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@> + +@= +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 + +@ 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@> + +@= +wrap_up() { + putchar('\n'); + @; + if (history > harmless_message) exit(1); + else exit(0); +} + +@ @= +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}. + +@= +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| +@=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@> + +@= +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) @@; + else if (!found_change) @@; + else @; + } + else @; + } + if (!found_web) @; + if (!found_change) @; +} + +@ 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. + +@= +{ + if (strlen(*argv) > max_file_name_length-5) + @; + if ((dot_pos=index(*argv,'.'))==NULL) + sprintf(web_file_name,"%s.web",*argv); + else { + sprintf(web_file_name,"%s",*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; +} + +@ @= +{ char *p; + p=dot_pos; + while (*++p != '\0') + if (*p=='.') dot_pos=p; +} + +@ @= +{ + if (strlen(*argv) > max_file_name_length-5) + @; + if ((dot_pos=index(*argv,'.'))==NULL) + sprintf(change_file_name,"%s.ch",*argv); + else sprintf(change_file_name,"%s",*argv); + found_change=1; +} + +@ @= +#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&% +Adds {\tt } to the directories searched for included files.\cr +} +@= +{ + switch((*argv)[1]) { + case 'x': + no_xref=1; + break; + case 'I': + @@; + break; + default: + @; + break; + } +} + +@ @= +{ +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); +} + +@ @= fatal("! Filename %s too long\n", *argv); + +@ @= { +printf("! Unknown option in argument %s\n", *argv); mark_harmless; +} + +@* Output. Here is the code that opens the output file: +@^system dependencies@> + +@= +FILE *C_file; /* where output of \.{TANGLE} goes */ +FILE *tex_file; /* where output of \.{WEAVE} goes */ + +@ @= +scan_args(); +@@; +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 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 { + # + n=0 + while (n + n++ + } + for (node in incoming) { + if (incoming[node]!=0) { + # + } + } + if (cycle==0) { + print "There can't possibly be a cycle in the graph" + } else { + exit 0 ## no error until we check context + } + } + +# #= +for (node in incoming) { + if (incoming[node]==0) { + work[high++]=node + } + } + +# #= +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 + } + } + +# #= +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 +@= +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. +@= +cur_file=end_output_files=output_files+max_files; + +@ @= +{ +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 + @ + 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. +@= +for (;cur_filebyte_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: @; +@y + case module_name: + cur_module_char=*(loc-1); + @; +@z +@x 803 + return(module_name); +@y + if (cur_module_char=='(') { + @ + } + 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 + +/*: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;ipathname+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 +@@; + +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++; + } + @@; + searchpath[++nextpath]=t; +} + +@ @= +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++; + } + @@; + 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@; + } + } + return NULL; +} + +@ @=#include + +@ @= +for(s=pathname,t=searchpath[i]; tpathname+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;ilogfile +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;ilogfile +##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;i1){ +##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;i1&&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;ilogfile +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;imacrofile + + +##: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;ilogfile +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$/){ +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$/){ +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;posNF||$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;posNF||$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;pos1){ +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 %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"@=">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"@=">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"@ @=">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"@;">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"@=">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"@ @=">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"@ @=">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"@ @=">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"@ @=">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"@ @=">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"@=">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"@ @=">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"@=">grammarfile +for(n=1;n<=prodnum;n++){ +if(n%5==0) +print"@ @="\ +>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(reductiongrammarfile +} + +##: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"@=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"@="\ +>wlang +printf"*out_ptr='x'; tex_printf(\"\\\\input %sweb.te\");\n",\ +extension>wlang +printf"@ @=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"@ @=">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"@ @=">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"@=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"@=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"@=">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"@ @="\ +>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"@=">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"@=">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" @;">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 { +# +exitcode=0 +} +#@ +# +# +# +# +#@ +END { +# +print "Writing out lists" > logfile +# +# +# +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. +#= + 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. +#= + if (i<=NF) { + print "Error: leftover fields", $i, "... on line", NR-1 + # + } + + +# To \.{SPIDER}, any line beginning with |"## "| is a comment. +\.{SPIDER} also ignores blank lines. +#= +#=/^##|^ *$/#> { + ## comments, blank lines + print $0 > logfile + next + } + +# But, if \.{SPIDER} encounters a line we don't recognize, it complains. +#= + { + 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. +#= + logfile = "spider.slog" + +# Here we write the names of the key words used in translations. +#= + translationfile = "trans_keys.unsorted" + +# We write tokens out to two files: |scrapfile| for \.{WEAVE}, and +|ttokfile| for \.{TANGLE}. +#= + 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. +#= + reserved = "scraps.web" ## use same file; not enough files + +# We'll also end up writing a list of token names, for name checking +purposes. +#= + 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. +#= + 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. +#= + categoryfile = "names.unsorted" ## cut down on number of output files +# We use a special file to write grammar information: +#= + 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. +#= + 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. +#= + productions = "productions.list" + +# These productions will get fed to {\tt cycle.awk}, which looks for cycles. +#= + 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}). +#= + for (t in translation_keywords) { + print t > translationfile + } + +# #= +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"|. + + +#"|#>= +temp = substr(transstring,2,length(transstring)-2) ## kills awk bug +trcnt = split(temp,trtok,"-") +outstring = "" +for (tridx=1;tridx<=trcnt;tridx++) { + alternate=trtok[tridx] + # + 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 + # + 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. +#= + 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=="") { + 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. +#= +temp = substr(transstring,2,length(transstring)-2) ## kills awk bug +trcnt = split(temp,trtok,"-") +outstring = "" +for (tridx=1;tridx<=trcnt;tridx++) { + alternate=trtok[tridx] + # + 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. +#= +for (i=start_place;i + } +} +# + +# We check for the presence or absence of certain empty strings after +scanning. +#= +if (this_name != "") { + print "Error: name doesn't apply on line", NR-1 + # + } +# #= +if (this_tangleto != "") { + print "Error: tangleto doesn't apply on line", NR-1 + # + } +# #= +if (this_category != "") { + print "Error: category doesn't apply on line", NR-1 + # + } +# #= +if (this_translation != "") { + print "Error: translation doesn't apply on line", NR-1 + # + } +# #= +if (this_category == "") { + print "Error: you must give a category on line", NR-1 + # + } + +#*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 +#= +#=/^default /#> { + print "Setting defaults..." > logfile + start_place=2 + # + # + # + # + 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: +#= + this_translation=default_translation + this_mathness=default_mathness + this_name="" + this_category="" + this_tangleto="" +# When \.{SPIDER} starts up, the defaults are already set: +#= + 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. + +#= +#=/^token /#> { + print "Token", $2 > logfile + if ($2=="identifier") { + # + } else if ($2=="number") { + # + } else if ($2=="newline") { + # + } else if ($2=="pseudo_semi") { + # + } else if ($2 ~ #=/[a-zA-ZA-Z0-9]+/#>) { + ## we recognize no other names + print "Error: unknown token species:", $2 + # + } else { + # + } + categories[this_category]=1 ## is this right? +#^questions#> + next +} + +# Identifiers, numbers (and string literals), newlines, and the special +token \.{pseduo\_semi} are predefined. +#= + # + this_translation="" + start_place=3 + # + # + # + # + # + id_category=this_category + id_mathness=this_mathness + +# We have yet to implement a separate procedure for numerics and strings! +#= + print "Warning: numeric constants and strings are",\ + "identified in this WEAVE." + print "Warning: numeric constants and strings are",\ + "identified in this WEAVE." > logfile + # + this_translation="" + start_place=3 + # + # + # + # + # + number_category=this_category + number_mathness=this_mathness + +# +#= + # + start_place=3 + # + # + # + # + newline_category=this_category + newline_mathness=this_mathness + newline_translation=this_translation + +# +#= + # + start_place=3 + # + # + # + # + 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. +#= + this_string=$2 + # +## print NF, "fields on line", NR-1 ## caught a bug in DEC awk + $2 = this_string +## print NF, "fields on line", NR-1 + # + if (tokens[$2]!="") { + print "Warning: token", $2, "defined twice" + } + tokens[$2]=1 ## remember this token + # + # + # + + +# 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. + +#= +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. +#= + 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|. +#= +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. +#= +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]|. +#= + 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) { + # + } 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.) +#= + 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: +#= + # + this_name=tokenname[$2] + start_place=3 + # + # + 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. +#= + temp = tokenlength[$2] + if (temp > maxtokenlength) { + maxtokenlength=temp + } + +# We're paranoid. +#= + 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}. +#= + tempfile = scrapfile + # + tempfile = ttokfile + # + +# This is how we do it. +#= +print "@ Here we input tokens of more than one character" > tempfile +print "@=" > tempfile +# + +# 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. +#= + for (len=maxtokenlength; len>=2; len--) { + printf "if (loc+%d<=limit) {\n", len-1 > tempfile + # + printf "\t}\n" > tempfile + } + # + +# #= +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 + } + } + + +# #= +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. +#= + print "Writing out predefined scraps" > logfile + # + # + # + # + print "Writing out token scraps" > logfile + # + + + + +# This is how we write out the information for the identifier. +#= + if (id_category != "") { + print "@ @=" > 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 "@;" > 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. +#= + tokennumbers["identifier"]=tokennumber; tokennumber-- + tokenname["identifier"]="identifier" + +# This is how we write out the string or constant scrap, at the end. +#= +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 "@=" > 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. +#= + tokennumbers["constant"]=tokennumber; tokennumber-- + tokenname["constant"]="constant" + tokennumbers["string"]=tokennumber; tokennumber-- + tokenname["string"]="string" + + +# +#= +if (newline_category != "") { + print "@ @=" > scrapfile + transstring=newline_translation + selfstring="small_app(next_control);" + wherestring="in translation of token newline" + append_keyword="small_app" + #"|#> + 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 + } + +# +#= +if (pseudo_semi_category != "") { + print "@ @=" > scrapfile + transstring=pseudo_semi_translation + selfstring="small_app(next_control);" + wherestring="in translation of token pseudo_semi" + append_keyword="small_app" + #"|#> + 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: +#= +print "@ @=" > 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" + #"|#> + 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#> +#= +print "@ @=" > ttokfile +for (t in tokennumbers) { + # + 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 + # + 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. +#= +print "@ @=" > ttokfile +for (t in tokentangleto) { + # + 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 + # + 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=="string"||t=="constant"||t=="identifier") + continue + + + +# This is somewhat like the translation code, but tuned for \.{TANGLE} +#= +oldwherestring = wherestring +wherestring = "for tangleto " wherestring +#@ +transstring=this_tangleto +# +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. +#= + tempfile = scrapfile + # + tempfile = ttokfile + # + +# We use an ugly trick to get the token numbers different for +\.{WEAVE} and \.{TANGLE}: +#= + 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} + # ## 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. +#= + for (t in tokenname) { + temp = tokenname[t] + if (substr(temp,1,1) != "'") { + # + print temp > tokennamefile + } + } + +# #= +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. + +#= +#=/^ilk /#> { + print "Ilk", $2 > logfile + # + # + this_name="" + start_place=3 + # + # + # + 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 ($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#> +#= +#=/^reserved /#> { + print "Reserved word", $2 > logfile + if ($0 !~ #=/ ilk /#>) { + # + } + 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 + # + } + } + # + 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.} +#= + 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. +#= +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. +#= +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 "@=" > 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" + #"|#> + 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. +#= +print "@ @=" > 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?} +#= + 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 + } + } + +# #= + for (i in ilk_translation) { + print i > ilkfile + } + +# #= +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. +#= +#=/-->/#> { + # + # + # + # + # + # + # + # + # + # + # + # + # + 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]|. +#= + 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|. + +#= +trans[pos]="" +for (i=1; i<=NF; i++) { + if ($i ~ #=/<.*>/#>) { ## should be |trans_pattern| + # + } else if ($i ~ #=/^!?[a-zA-Z_]+(\*\*?)?$/#>) { ## |cat_pattern| + # + } else if ($i ~ #=/^!?\(([a-zA-Z_]+\|)*[a-zA-Z_]+\)(\*\*?)?$/#>){ + # + } else if ($i == "?") { + # + } 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 + # + } +} +i++ + + +# When we find a mistake, we just abandon the current production. +Decrementing |prodnum| will make it as if this production never happened. +#= + prodnum-- + # + +# We process the translation and add the result to the current +translation for |pos|. +#= +transstring=$i +selfstring="" ## senseless for productions +wherestring= sprintf ("in production on line %d", NR-1) +append_keyword="app" +#"|#> +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. +#= + field[pos]=$i ## save this field to compare RHS + # + # + 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) + } + # + # + +# 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. +#= + field[pos]=$i ## save this field to compare RHS + # + 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 + } + # + 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 + # + 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] ")" + # + + +# 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. +#= + 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. +#= +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. + +#= + 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. + +#= +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 + # +} + +# Wild cards are easy to process, but we do have to remember that +not even a wild card matches a scrap of category zero. +#= + 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? + # + + + +# +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. + + +#= + 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 + # + } + +# Here is the efficient place to update the rightmost (highest) +position of {\it any} category. +#= + 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: +#= +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. +#= + for (pos=1;pos + i++ + } + +# #= + for (pos=highpos;pos + i++ + } +# #= + if (i>NF || $i != field[pos]) { + print "Error: token mismatch is: found", $i, \ + "sought", field[pos], "on line", NR-1 + # + } + +# 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. +#= + ## i points to the target category + if (i>NF) { + print "Error: no target category in production on line", NR-1 + # + } + if ($i ~ #=/##[0-9]+/#>) { ## a number + $i = substr($i,2) ## peel off the \## + # + 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 + # + } + i++ + +# We call this at the end to make sure there aren't unused fields left over +#= + if (i<=NF) { + print "Error: used only " i-1 " of " NF " tokens", \ + "in production on line", NR-1 + # + } + +# After having vetted the whole production, we combine the tests and +translations for each |pos|. +#= + prodtest[prodnum]="" + for (pos=1;pos1) { + prodtest[prodnum]=prodtest[prodnum] " &&\n\t\t" + } + prodtest[prodnum]=prodtest[prodnum] test[pos] + } + +# #= + prodtrans[prodnum]="" + for (pos=lowpos;pos + } + prodtrans[prodnum]=prodtrans[prodnum] trans[highpos] + +# #= + ppstart[prodnum]=lowpos-1 + tokensreduced[prodnum]=highpos-lowpos + +# #= + 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. +# #= +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. +#= +temp = field[pos] +tempa = substr(temp,1,1) +if (tempa != "!") { + if (temp ~ #=/^\(([a-zA-Z_]+\|)*[a-zA-Z_]+\)(\*\*?)?$/#>) { + ## list of alternatives + # + 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_]+(\*\*?)?$/#>) { + # + reduced[temp]=1 + } else if (temp != "?") { + print "Confusion: unintelligible field[pos]:", temp, \ + "in production on line", NR-1 + # + } +} + +# #= +while (temp ~ #=/\*$/#>) { + temp = substr(temp,1,length(temp)-1) +} + +# #= +for (c in categories) { + if (reduced[c] != 1) { + print "Warning: category", c, "never reduced" + } +} + + +# Here's a check for the target token number +#= +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 + # + } + +#*1Writing the scrap reduction code. +Before writing the grammar, we want to define all of the category codes. +#= + 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. +#= + 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... +#= + for (c in categories) { + print c > categoryfile + } + +# #= +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. +#= +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} +#= +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 "@=" > grammarfile +for (n=1; n<=prodnum; n++) { + if (n%5==0) + print "@ @=" \ + > grammarfile ## avoids overflowing \.{WEAVE} of \.{WEAVE} + # + # + printf "if (%s) {\n\t/* %d: {\\tt %s} */\n%s",\ + prodtest[n],n,this_string,prodtrans[n] > grammarfile + # + print "\tgoto end_prods;" > grammarfile + printf "} " > grammarfile + } +printf "\n" > grammarfile +print "end_prods:" > grammarfile + +# We do different things for a category that is unnamed. +#= + 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 grammarfile + } + +# This is the place we check for errors. +#^append check#> +#^reduce check#> +#= +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. +#= + 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. +#= +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. +#= +#=/^language /#> { + language = $2 + extension=language + for (i=3; i + } + } + # + # + next +} + +# #= +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 "@=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 "@=" \ + > wlang + printf "*out_ptr='x'; tex_printf(\"\\\\input %sweb.te\");\n", \ + extension > wlang + printf "@ @=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. +#= + 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. +#= +#=/^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 + # + } + } 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. +#= +#=/^module /#> { + for (i=2;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 + # + } + } + # + next +} + +# Here's how we rig it: +#= +if (mod_def_cat!="") { + print "@ @=" > 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 "@ @=" > 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.'' +#= +#=/^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" + # + } + next +} + +# We write the at sign out to the grammar file and to \.{TANGLE}'s token file +#= + tempfile = grammarfile + # + tempfile = ttokfile + # + +# It's trivially done +#= + 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 "@=char the_at_sign = at_sign;" > tempfile + print " " > tempfile + +# We provide a default at sign: +#= + 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. +#= +#=/^comment /#> { + print $0 > logfile + for (i=2; i$/#>) { + transstring = $i + wherestring = "in \"comment begin\" on line " NR-1 + # + begin_comment_string = outstring + i++ + } else { + print "Error: \"comment begin\" must have a restricted translation" + # + } + } 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 + # + end_comment_string = outstring + } else { + print "Error: \"comment end\" must have a restricted translation" + # + } + i++ + } else { + print "Error: bad comment attribute:", $i + # + } + } + # + # + 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. + +#= +print "@ Here we recognize the comment start seqence" > ttokfile +print "@=" > 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 +#= +print "@ @=" \ + > 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 +#= +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 "@=" > 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. +#= +printf "@ Here we recognize end of comments" > scrapfile +printf "@d comments_end_with_newline = %d\n",comments_end_with_newline >scrapfile +print "@=" > 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 " @;" > 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. +#= + 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. +#= +if (macrofile!="") { + this_string=substr(begin_comment_string,2,length(begin_comment_string)-2) + # + 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) + # + 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" + # + } + + + +# Escaping \TeX's specials is pretty easy: +#= +texof["\\"]="\\BS" +texof["{"]="\\{" +texof["}"]="\\{" +texof["$"]="\\$" +texof["&"]="\\amp" +texof["##"]="\\##" +texof["^"]="\\H" +texof["_"]="\\_" +texof["~"]="\\TI" +texof["%"]="\\%" + +# +#= +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. +#= +#=/^line /#> { + print $0 > logfile + for (i=2; i$/#>) { + transstring = $i + wherestring = "in \"line begin\" on line " NR-1 + # + sharp_line_open = outstring + i++ + } else { + print "Error: \"line begin\" must have a restricted translation" + # + } + } else if ($i=="end") { + i++ + if ($i ~ #=/^<.*>$/#>){ + transstring = $i + wherestring = "in \"line end\" on line " NR-1 + # + sharp_line_close = outstring + } else { + print "Error: \"line end\" must have a restricted translation" + # + } + i++ + } else { + print "Error: bad line attribute:", $i, "on line", NR-1 + # + } + } ## |for| + # + next +} + +# We have to give \.{TANGLE} the strings for \&{\##line} commands. +#= + 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 +#= +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. +#= +#=/^date /#> { + ## date returned as ``Fri Dec 11 11:31:18 EST 1987'' + mo = month[$3] + day = $4 + year = $7 + time = $5 + # + 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 +#= +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"|. +#= + 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 +@@; +@@; +@@; +@@; + +main (ac, av) +char **av; +{ + argc=ac; argv=av; + program=tangle; + @; + 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@> + +@= +typedef struct { + eight_bits *tok_start; /* pointer into |tok_mem| */ + sixteen_bits text_link; /* relates replacement texts */ +} text; +typedef text *text_pointer; + +@ @= +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| */ + +@ @= +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 */ + +@ @= +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 */ + +@= +text_pointer last_unnamed; /* most recent replacement text of unnamed module */ + +@ @= 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|. + +@= +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 */ + +@= +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. + +@= +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; +{ + @; + 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; +} + +@ @= +#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; +@; + if (cur_repl->text_link==0) { /* macro definition */ + if (cur_name->ilk==macro) { + @; + } + } else if (cur_repl->text_linktext_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; +} + + +@ @= +number_of_parameters=*(cur_repl->tok_start); +@; +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; + + +@ @= +#ifdef TRACE_MACROS +if (tracing>1) { + printf("\nUnstacking %d parameters",number_of_parameters); + } +#endif 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 +@= +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) { + @; + @; + } else + out_char(a); /* one-byte token */ + else { + a=(a-@'200)*@'400+*cur_byte++; + switch (a/@'24000) { /* |@'24000=(@'250-@'200)*@'400| */ + case 0: + @; + cur_val=a; out_char(identifier); break; + case 1: @; + default: cur_val=a-@'50000; if (cur_val>0) cur_mod=cur_val; + out_char(module_number); + } + } + return 1; +} + + +@ @= +push_level(name_ptr-*cur_byte++); goto restart; +@ @= +#ifdef TRACE_MACROS +if (tracing>2) { + printf (" [#%d]",*cur_byte); + } +#endif TRACE_MACROS + +@ @= +if (name_dir[a].ilk==macro) { + @; + @; + push_level(name_dir+a); + cur_byte++; /* skip number of parameters */ + goto restart; +} else if (name_dir[a].ilk==simple) { + @; + push_level(name_dir+a); + goto restart; +} + +@ @= +#ifdef TRACE_MACROS +if (tracing>2) { + printf("\nExpanding macro ("); print_id(name_dir+a); + printf("=nd[%d])",a); + } +#endif TRACE_MACROS + +@ @= +#ifdef TRACE_MACROS +if (tracing>2) { + printf("\nExpanding parameter ("); print_id(name_dir+a); + printf("=nd[%d])",a); + } +#endif TRACE_MACROS + + +@ @= +{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) { + @; + } + 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. +@ @= +short balances[@'200]; +@ @= +{ int i; +for (i=0;i<@'200;i++) balances[i]=0; +@; +} +@ Eventually we will want {\tt SPIDER} to set balances. +For now let's try just tossing in parenthesis, braces, and square brackets. +@= +balances[@`(']=1; +balances[@`)']=-1; +balances[@`[']=1; +balances[@`]']=-1; +balances[@`{']=1; +balances[@`}']=-1; + +@ @= +{ int bal; /* used to balance parentheses in parameter lists */ + eight_bits b, oldb; + sixteen_bits c; +bal=0; +@; +while (1) { + b=*cur_byte++; + if (b==param) { /* convert to name reference */ + b=*cur_byte++; + @; + 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) { + @; + 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@>) { + @; + } else { + @; + 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; +@; +} + +@ @= +name_pointer parm_ptr; /* used while stacking parameters */ + +@ @<|b| opens verbatim or string@>=(b==string||b==constant) +@ @= +app_repl(b); +oldb=b; +@; +while((b=*cur_byte++)!=oldb) { +@; + app_repl(b); + if (cur_byte>=cur_end) + confusion("string or constant didn't end in token list"); +} +@"| (|#ifdef TRACE_MACROS|)@>; + + + +@ @= +#ifdef TRACE_MACROS +if(tracing>2) printf(" <"); +#endif TRACE_MACROS +@ @= +#ifdef TRACE_MACROS +if (tracing>2) printf("%c",b); +#endif TRACE_MACROS +@ @"| (|#ifdef TRACE_MACROS|)@>= +#ifdef TRACE_MACROS +if(tracing>2) printf(">"); +#endif 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 + +@ @= +#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 + +@ @= +#ifdef TRACE_MACROS +if (tracing>2) { + printf (" <"); + if (@'37"); +} +#endif TRACE_MACROS + +@ @= +#ifdef TRACE_MACROS +if (tracing>2) { + printf ("\nScanning #%d...", number_of_parameters+1); + } +#endif 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. + +@= + 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:
@> + } + 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 */ + +@= +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 +@= +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. +@= +cur_out_file=end_output_files=output_files+max_files; + +@ @= +{ +if (cur_out_file>output_files) { + for (an_output_file=cur_out_file; + an_output_filetext_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; + @; + 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; + } + @@; + } + 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. +@= +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| */ + @; + 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@>@; + @/@t\4@>@; + @@; + 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; + } +} + +@ @= +#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= +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 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 `\.{@@*}' */ + +@= +eight_bits ccode[128]; /* meaning of a char following \.{@@} */ + +@ @= { + 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: +@=short tracing; +@ @=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. + +@= +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++); + @@; + 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 +@= +name_pointer cur_module; /* name of module just scanned */ + +@ @= +#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; + @; + } + 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; + } + @@; + loc++; + if (isdigit(c) || c=='\\' || c=='.') @@;/*spider*/ + else if (isalpha(c) || c=='_' || c=='$') @@;/*spider*/ + else if (c=='\'' || c=='\"') @@;/*spider*/ + else if (c==at_sign) @@; + else if (c==' ' || c==tab_mark) { + continue; /* ignore spaces and tabs */ + } + mistake: @@; + return(c); + } +} + +@ @= {/*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); +} + +@ @= {/*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|. + +@= {/*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. + +@= { + 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); + @; + case string: @; +#ifdef DEBUG + case trace: tracing=*(loc-1)-'0'; continue; +#endif DEBUG + case ascii_constant: @; + case octal: @; + case hex: @; + default: return(c); + } +} + +@ @=/*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); + +@ @= { + id_first=loc; + while ('0'<=*loc && *loc<'8') loc++; + id_loc=loc; + return(octal); +} + +@ @= { + id_first=loc; + while (isxdigit(*loc)) loc++; + id_loc=loc; + return(hex); +} + + +@ @= { + ASCII *k; /* pointer 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=='(') { + @@; + } + 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.) + +@=mod_text[0]=' '; + +@ @= +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; + @; + loc++; 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 (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. + +@= { + 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;} + +@= +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) {@;} + /* 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) { + @@; + 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) { + @; + } + 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. +@= +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 +@= +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| */ + +@ @= +next_param_name=0; +next_param_name_text=param_name_texts; +param_names[next_param_name]=next_param_name_text; + +@ @=@; + +@ @= +@; +while (id_first= +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= +@@; +@; +@; +if (next_control!= @`=') { + err_print("! You must put an = sign before the macro replacement text"); + @; +} +@; +app_repl(next_param_name); /* store number of parameters in |tok_mem| */ +scan_repl(macro); +p->equiv=(ASCII *)cur_text; +@; +@; + +@ @= +#ifdef TRACE_MACROS +if (tracing>1) { + printf("\nScanning definition of macro "); print_id(p); + } +#endif 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 + + +@ @= +@@; +if (next_control==@`(') { + do { + @; + @@; + } while (next_control==@`,'); + if (next_control != @`)') { + err_print("! Macro parameter list must end with )"); + @; + } + next_control=get_next(); /* first token following parameter list */ +} + + +@ @= +if (next_control!=identifier) { + err_print("! Macro name must be an identifier"); +@.Macro name must be an identifier@> + @; +} 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: "); + } +} + +@ @= +@; +if (next_control!=identifier) { + err_print("! Macro parameter name not an identifier"); +@.Macro parameter name...@> + @; +} else { + if (parameter_number(id_first,id_loc)!=0) { + err_print("! Duplicate parameters in macro definition"); +@.Duplicate parameters...@> + @; + } else { + @; + } +} + + +@ @= +while ((next_control=get_next())==@`\n'); + +@ We modify this from the old tangle: + +@= +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 { + @; + a=cur_module-name_dir; + app_repl((a / @'400)+@'250); + app_repl(a % @'400); + @; break; + } +case constant: case string: + @; +case ascii_constant: + @; +case octal: + @; break; +case hex: + @; 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. + +@= +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);} + +@ @= { + ASCII *try_loc=loc; + while (*try_loc==' ' && try_loc +} + +@ @= + 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; + +@ @= { + 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... +@= { +long sum=0; +while (id_first @"04000000) err_print("! Octal constant exceeds @@\"04000000"); + } +app_repl(constant); +app_decimal(sum); +app_repl(constant); +} + +@ @= { +long sum=0; +while (id_first @"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=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. + +@= +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); + @; + @; +} + +@ 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 +@= +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; + @; + 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. +@= +@; +p->ilk=normal; /* turn off macro replacement and hope for the best */ +goto done_scanning; + + +@ @= +switch (next_control) { + case begin_unnamed: p=name_dir; break; + case module_name: p=cur_module; + @; + break; + default: return; +} +@; +scan_repl(module_name); /* now |cur_text| points to the replacement text */ +@; + +@ @= +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; +} + +@ @= +store_two_bytes((sixteen_bits)(@'150000+module_count)); /* |@'150000==@'320*@'400| */ + +@ @= +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_linktext_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 +@@; +@@; +@@; +@@; + +main (ac, av) +char **av; +{ + argc=ac; argv=av; + program=weave; + common_init(); + @; + printf(banner); /* print a ``banner line'' */ + @; + 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 + @; +#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. + +@= +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. + +@= +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; + +@ @= +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 + +@= +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) { + @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 +@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
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|. + +@= +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|. + +@= +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 + +@ @= +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|. + +@= +eight_bits ccode[128]; /* meaning of a char following \.{@@} */ + +@ @= +{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@>@@; +@# +/*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. + +@= +#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. + + + +@= +name_pointer cur_module; /* name of module just scanned */ +int math_flag; + +@ @= +#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++); + @@; + if (see_vertical && c==vertical_char) { + if (*loc==vertical_char && loc < limit) { + loc++; + } else { + return vertical_bar; + } + } + if (isdigit(c)) @@; /*spider*/ + else if (isalpha(c) || c=='_') @@;/*spider*/ + else if (c=='\'' || c=='"') @@;/*spider*/ + else if (c==at_sign) @@; + else if (c==' ' || c==tab_mark) continue; /* ignore spaces and tabs */ + mistake: @@; + return(c); + } +} + +@ @= +while ((next_control=get_next(0))==@`\n'); + +@ @= {/*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|. + +@= { + 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... +@= { + 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... +@= { + 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|. + +@= {/*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. + +@= { + 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: + @}@>@; + case module_name: + @@; + case verbatim: @@; + case ascii_constant: /* fake into looking like quoted char */ + @; + case octal: @; + case hex: @; + default: return(ccode[c]); + } +} + +@ The occurrence of a module name sets |xref_switch| to zero, +because the module name might (for example) follow \&{int}. + +@= { + ASCII *k; /* pointer into |mod_text| */ + cur_module_char=c; /* remember |'<'| or |'('| */ + @; + 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); +} + +@ @=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.) + +@=mod_text[0]=' '; + +@ @= +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; + @; + loc++; 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 (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 */ +} + +@ @= { + 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. + +@= { + 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. + +@= +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) + @; +changed_module[module_count]=change_exists; + /* the index changes if anything does */ +phase=2; /* prepare for second phase */ +@; +} + +@ @= +{ + 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 */ + } + @; + @; + @; + 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=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_control0) { + 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\,$\.{@@>}. + +@= +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=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. + +@= +name_pointer lhs, rhs; /* pointers to |byte_start| for format identifiers */ + +@ When we get to the following code we have |next_control>=format|. + +@= +while (next_control<=definition) { /* |format| or |definition| */ + xref_switch=def_flag; /* implied \.{@@!} */ + if (next_control==definition) next_control=get_next(1); + else @; + 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. + +@= { + 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|. + +@= +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. + +@= +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:
@> + } + 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
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:
@> + } + 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); + } +} + +@ @=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. + +@= +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 (bout_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. + +@= +out_ptr=out_buf+1; out_line=1; +@ + +@ 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= +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) @; + 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. + +@= +{ + 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; klimit && (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. + +@= 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@> + @; + } + } else { + if (get_line()==0) { + err_print("! Input ended in mid-comment"); +@.Input ended in mid-comment@> + loc=buffer+1; @; + } + } + + c=*(loc++); + if (c==vertical_char) return(bal); + @; + if (phase==2) app_tok(c); + @; + } +} + + + +@ @= +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--; @; + } +} +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. + +@= +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. + +@= +typedef struct { + eight_bits cat; + eight_bits mathness; + union { + text_pointer Trans; + ===> @@; + } 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))) + + +@= +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 + +@ @= +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: @; + } + } +} +#endif DEBUG + +@ @= +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@; + +@= +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. + +@= { + /* |ignore_scrap| becomes part of the grammar */ + @@; + 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 +@@; +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=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. +@= + +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 && nxlink; + } + @; +} + +@ 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|. + +@= + 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; + } + @; +#ifdef DEBUG + @; +#endif DEBUG + pp--; /* we next say |pp++| */ +} + +@ @= +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; @; +#ifdef DEBUG + @; +#endif DEBUG + pp--; /* we next say |pp++| */ + return; + } + for (i=j; i= +while (1) { + @; + 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; + @; +} + +@ 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. + +@= +if (lo_ptrcat=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. + +@= +#ifdef DEBUG +int tracing; /* can be used to show parsing details */ +#endif DEBUG + +@ @= { + 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; + @; + @; +} + +@ 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. + +@= { + @; + 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); +} + +@ @= +#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 + +@ @= +#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; + 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; + +@ @= +@; +switch (next_control) { + @@; + case string: case constant: case verbatim: @; + break; + case @`\n': @; break; + case identifier: @; break; + case TeX_string: @; 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: +@ +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 +@= +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@> + +@= +if (next_control==constant) app_str("\\O{"); +@.\\O@> +else if (next_control==string) app_str("\\.{"); +@.\\.@> +else app_str("\\={"); +@.\\=@> +while (id_first + @@; + app_tok(*id_first++); +} +small_app('}'); +@@; + +@ @= +switch (*id_first) { + case ' ':case '\\': + case '%':case '$': case '^':case '`': + case '#': + case '{': case '}': case '~': case '&': case '_': + small_app('\\'); break; + + } + +@ @= +app_str("\\hbox{"); while (id_first@; + + +@ 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; /* spider */ + @; + } + else { + C_parse(0); + } +} +} + +@ @= + 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 */ + + +@ @= +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 */ + +@= 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 */ + +@= +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 + +@ @= +#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. + +@= +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: @; break; + case mod_name: @; break; + case math_bin: case math_rel: case math_op: + @; break; + case cancel: c=0; while ((a=get_output())>=indent && a<=big_force) { + if (a==indent) c++; if (a==outdent) c--; + } + @; + goto reswitch; + case big_cancel: c=0; + while (((a=get_output())>=indent || a==' ') && a<=big_force) { + if (a==indent) c++; if (a==outdent) c--; + } + @; + goto reswitch; + case indent: case outdent: case opt: case backup: case break_space: + case force: case big_force: @; 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. +@= +out('\\'); +if (a==identifier) + if (length(cur_name)==1) out('|')@; +@.\\|@> + else out('\\')@; +@.\\\\@> +else out('&')@; /* |a==res_word| */ +@.\\\&@> +out_name(cur_name); + +@ @= +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. + +@= +if (a@; + +@ 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}'). + +@= { + b=a; save_mode=cur_mode; c=0; + while (1) { + a=get_output(); + if (a==cancel || a==big_cancel) { + @; + goto reswitch; /* |cancel| overrides everything */ + } + if ((a!=' ' && abig_force) { + if (save_mode==outer) { + if (out_ptr>out_buf+3 && strncmp(out_ptr-3,"\\Y\\P",4)==0) + goto reswitch; + @; + 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= + 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. + +@= { + 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(':'); @; + out_str((is_file? "\\XF":"\\X")); +} + +@ @= +k=cur_name->byte_start; k_limit=(cur_name+1)->byte_start; +cur_mod_name=cur_name; +while (k@; + if (b!=vertical_char) out(b)@; + else { + @@; + save_loc=loc; save_limit=limit; loc=limit+2; limit=j+1; + *limit=vertical_char; output_C(); + loc=save_loc; limit=save_limit; + } +} + +@ @= +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. + +@= +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) @@; + 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; + } +} + +@ @= { + 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) @; +} + +@ 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@> + +@= +int save_line; /* former value of |out_line| */ +ASCII *save_place; /* former value of |out_ptr| */ + +@ @= { + module_count++; + @; + save_position; + @; + @; + @; + @; + @; +} + +@ 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. + +@= +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. + +@= 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: @; break; + case hex: @; 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= { +out_str("\\O{\\~"); + while ('0'<=*loc && *loc<'8') out(*loc++); +out('}'); +} + +@ @= { +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. + +@= +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) @@; + else @; + 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 */ +} + +@ +@= { + small_app(backup); app_str("\\D"); /* this will produce `\&{define}' */ +@.\\D@> +@@; + 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); + @; + if (next_control==@`=') { + small_app('\\'); small_app('S'); /* equivalence sign */ + @@; + } 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 */ + } +} + +@ @=goto punt_the_definition; + +@ @= +@@; +if (next_control==@`(') { + small_app(@`('); + do { + @@; + if (next_control==identifier) { + small_app(id_flag+id_lookup(id_first, id_loc,normal)-name_dir); + @@; + } else { + err_print("! Improper 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 )"); + @; + } + @@; + /* first token following parameter list */ +} + + + + +@ @= { + app_str("\\F"); app_scrap(SP_ignore_scrap,no_math); + /* this will produce `\&{format}' */ +@.\\F@> +@@; +/* 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 */ + @@; + 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); + @@; + } + } + /* 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. + +@= +name_pointer this_module; /* the current module name, or zero */ + +@ @= +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; + @; + } + while (next_control<=module_name) { + outer_parse(); + @; + } + finish_C(); +} + +@ @= +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); +@ +/* this forces a line break unless `\.{@@+}' follows */ + +@ @= +if (next_control + 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); + @ + next_control=get_next(1); + } +} + +@ Cross references relating to a named module are given after the module ends. + +@= +if (this_module>name_dir) { + @; + 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|. + +@= +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. + +@= + 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"); + @; + 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. + +@= +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 */ +} + +@ @= +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. + +@= +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(); @; + } + finish_line(); out_str("\\inx"); finish_line(); +@.\\inx@> + @; + @; + out_str("\\fin"); finish_line(); +@.\\fin@> + @; + 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. + +@= +sixteen_bits k_module; /* runs through the 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. + +@= +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. + +@= { +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|. + +@ @= +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 */ + +@= +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 + +@ @= +#ifdef STAT +max_sort_ptr=scrap_info; +#endif STAT + + +@ The desired alphabetic order is specified by the |collate| array; namely, +|collate[0]= +ASCII collate[102]; /* collation order */ + +@ We use the order $\hbox{null}<\.\ <\hbox{other characters}<\.\_< +\.A=\.a<\cdots<\.Z=\.z<\.0<\cdots<\.9.$ + +@= +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_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) + @@; + else @; +} + +@ @= { + 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); +} + +@ @= { + cur_name=sort_ptr->head; + do { + out_str("\\:"); +@.\\:@> + @; + @; + cur_name=blink[cur_name-name_dir]; + } while (cur_name); + --sort_ptr; +} + +@ @= +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\,$\.]'. + +@= +@; +do { + out_str(", "); cur_val=cur_xref->num; + if (cur_val + 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. + +@= +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. +@=boolean do_file; +@ @= +do_file=(1==1); +mod_print(root); +do_file=(1==0); +mod_print(root); + + + +@ @= +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 + @; +#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. -- cgit v1.2.3