diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/c_cpp/cwebx |
Initial commit
Diffstat (limited to 'web/c_cpp/cwebx')
26 files changed, 16479 insertions, 0 deletions
diff --git a/web/c_cpp/cwebx/Makefile b/web/c_cpp/cwebx/Makefile new file mode 100644 index 0000000000..a61bd6280a --- /dev/null +++ b/web/c_cpp/cwebx/Makefile @@ -0,0 +1,122 @@ +# This file is part of CWEB, version x3.0. + +# +# Read the README file, then edit this file to reflect local conditions +# + +# The C compiler should be ANSI compatible + +CC = gcc + +# We let CWEAVE report syntax errors by setting +d. +# The flag +m makes the TeX output slightly more compact. +# We use the +e flag since our printer is two-sided. + +CWFLAGS = +mde +CTFLAGS = + +# We keep debugging info around to enable the `+d' option of cweave +CFLAGS = -DDEBUG -DSTAT -g + +# RM and CP are used below in case rm and cp are aliased + +RM= /bin/rm +CP= /bin/cp +RENAME= /bin/mv + + +# Set CCHANGES to common-foo.ch if you need changes to common.w in another +# file than common.ch + +CCHANGES= + +# Set TCHANGES to ctangle-foo.ch if you need changes to ctangle.w in another +# file than ctangle.ch + +TCHANGES= + +# Set WCHANGES to cweave-foo.ch if you need changes to cweave.w in another +# file than cweave.ch + +WCHANGES= + +########## You shouldn't have to change anything after this point ####### + +CWEAVE = ./cweave $(CWFLAGS) +CTANGLE = ./ctangle $(CTFLAGS) + +.SUFFIXES: .tex .dvi .w + +.w.tex: + $(CWEAVE) $* + +.tex.dvi: + tex $*.tex + +.w.c: + $(CTANGLE) $* + +.w.dvi: + make $*.tex + make $*.dvi + +.w.o: + make $*.c + make $*.o + +all: cweb doc listings +cweb: ctangle cweave +manual doc: manual.dvi +listings: common.dvi ctangle.dvi cweave.dvi + +cautiously: + make common.c ctangle.c cweave.c + $(RENAME) ctangle SAVEctangle # save version in case things mess up + make ctangle + $(RENAME) common.c SAVEcommon.c + ./ctangle common $(CCHANGES) + diff common.c SAVEcommon.c + $(RENAME) SAVEcommon.c common.c # restore date + $(RENAME) ctangle.c SAVEctangle.c + ./ctangle ctangle $(TCHANGES) + diff ctangle.c SAVEctangle.c + $(RENAME) SAVEctangle.c ctangle.c # restore date + $(RENAME) cweave.c SAVEcweave.c + ./ctangle cweave $(WCHANGES) + diff cweave.c SAVEcweave.c + $(RENAME) SAVEcweave.c cweave.c # restore date + $(RM) SAVEctangle # succeeded, use new binary from now on + +SAVEctangle.c: + $(CP) ctangle.c SAVEctangle.c + +SAVEcommon.c: + $(CP) common.c SAVEcommon.c + +common.c: common.w $(CCHANGES) common.inc + $(CTANGLE) common $(CCHANGES) +common.h: common.w $(CCHANGES) + $(CTANGLE) common $(CCHANGES) + +common.tex: common.w common.inc + $(CWEAVE) common $(CCHANGES) + +ctangle: ctangle.o common.o + $(CC) $(CFLAGS) -o ctangle ctangle.o common.o + +ctangle.c: ctangle.w $(TCHANGES) common.inc common.h + $(CTANGLE) ctangle $(TCHANGES) + +ctangle.tex: ctangle.w $(TCHANGES) common.inc intro.inc + $(CWEAVE) ctangle $(TCHANGES) + +cweave: cweave.o common.o + $(CC) $(CFLAGS) -o cweave cweave.o common.o + +cweave.c: cweave.w $(WCHANGES) common.inc common.h parser.w rules.w + $(CTANGLE) cweave $(WCHANGES) + +cweave.tex: cweave.w $(WCHANGES) parser.w rules.w common.inc intro.inc + $(CWEAVE) cweave $(WCHANGES) + +manual.dvi: compare.tex diff --git a/web/c_cpp/cwebx/README b/web/c_cpp/cwebx/README new file mode 100644 index 0000000000..66fd57e171 --- /dev/null +++ b/web/c_cpp/cwebx/README @@ -0,0 +1,111 @@ +This file is part of CWEB version x3.04, and explains briefly what CWEB is, +and how to set it up, and what the various files are for. Here is a listing +of the other files constituting the release with a brief indication of their +purpose. + + +common.w % CWEB source for routines common to CTANGLE and CWEAVE +ctangle.w % CWEB source for CTANGLE +cweave.w % CWEB source for bulk of CWEAVE +parser.w % CWEB source for parser of CWEAVE +rules.w % CWEB source for grammar rules of CWEAVE +common.inc % CWEB source included by common.w, ctangle.w, and cweave.w +intro.inc % CWEB source included by ctangle.w and cweave.w + +common.c % C file produced (by CTANGLE) from common.w; for bootstrap +common.h % C file produced (by CTANGLE) from common.w; for bootstrap +ctangle.c % C file produced (by CTANGLE) from ctangle.w; for bootstrap + +cwebxmac.tex % TeX macro file needed to process files produced by CWEAVE +cwebcmac.tex % TeX macro file needed in addition for compatibility mode + +cwebhmac.tex % optional TeX macro file creating hyperlinks for xhdvi (e.g.) + +manual.tex % master file for the CWEB manual +compare.tex % file \input by manual.tex (present to facilitate bootstrap) + +Makefile % control file for UNIX make facility to build CWEB + +man1 % directory with UNIX manpage +man1/cweb.1 % UNIX manpage for CTANGLE and CWEAVE + +examples % directory with sample CWEB programs +examples/compare.w % Compare files; source for compare.tex above +examples/treeprint.w % Convert sorted file listings to a tree diagram +examples/wc.w % Count characters, words and lines in text files +examples/wmerge.w % Perform preprocessing of change files as filter +examples/Makefile + +cweb.el % EMACS file that might facilitate editing CWEB files + + +The CWEB system allows you to write C programs and their documentation at +the same time, in a way that optimises human readability. This version +fully supports ANSI/ISO C, and indeed requires such a compiler for its own +compilation. Like other WEB systems, CWEB allows you to decompose your +programs into small, logically related portions, to add any desired amount +of documentation to each of them, and to present them in any order that help +understanding. As TeX is used to obtain the printed source document, a high +typographic quality can be obtained while allowing for formatting of +complicated formulae and tables; formatting of C program fragments is simple +however, since it is done fully automatically. When used properly, CWEB can +be used to produce documents that combine the qualities of a scientific +publication about algorithms and of a well documented source listing for an +implementation of those algorithms. + +The CWEB system consists of two programs, CTANGLE and CWEAVE, that are to be +used in combination with a C compiler and TeX; both transform text files, +while they share the same input language. CWEB source files, that typically +have suffix `.w', can be converted by CTANGLE to C files (suffix `.c') that +can be compiled, and alternatively can be converted by CWEAVE to TeX files +(suffix `.tex') from which the typeset source listing can be produced by +TeX. More details can be found in the manual, which you obtain by running +TeX on the file manual.tex. Here the main interest is that since CWEB is +written using CWEB you need a bootstrap procedure to get things going. For +this purpose the derived files common.c, common.h and ctangle.c are supplied. +If you have the UNIX make facility, all you have to do is check that these +files are newer than the sources common.w and ctangle.w (they should be when +the archive is unpacked, but you can use "touch" to make them newer in case +this should be necessary), and that the Makefile macro CC refers to an ANSI +compiler (it is set to "cc"; you may need to select "gcc" instead or to +supply additional flags in CFLAGS) and then invoke "make all". Otherwise +here is the required sequence of commands. + +$ cc -c ctangle.c # compile main part of CTANGLE +$ cc -c common.c # compile common code +$ cc -o ctangle ctangle.o common.o # link CTANGLE together +$ ./ctangle cweave # create cweave.c from cweave.w +$ cc -DDEBUG -DSTAT -g -c cweave.c # compile main part of CWEAVE +$ cc -DDEBUG -DSTAT -g -o cweave cweave.o common.o # link CWEAVE together +$ tex manual.tex # typeset the manual + +$ # the rest is only needed to obtain source listings for the CWEB system +$ ./cweave common # produce common.tex +$ tex common # typeset listing of common code +$ ./cweave ctangle # produce ctangle.tex +$ tex ctangle # typeset listing of CTANGLE +$ ./cweave cweave # produce cweave.tex +$ tex cweave # typeset listing of CWEAVE + +By the time you have done all this successfully, you have already tested +CTANGLE and CWEAVE on a substantial amount of input, and you may be +confident that the system is working properly. + +Should you on the other hand encounter problems during bootstrapping, then +things are of course less pleasant, especially because you cannot print the +source listings yet. Few problems are expected though, since there are no +known system dependencies, and identical source code compiles on several +dissimilar systems, one of which is quite non-UNIX. It is expected though that +the compiler can handle large arrays (certainly larger than 64 Kb) and +programs that used command line arguments; if your compiler does not, you will +either have to replace it by a better one or do some tailoring of the +programs. If you encounter any other difficulties, please report them to me at +<maavl@zenon.univ-poitiers.fr>. If you do need to make changes to common.c, +common.h, or ctangle.c, don't forget to write them down, since they will have +to be applied at the proper places in common.w, ctangle.w, or common.inc in +order to persist after bootstrapping. Such patches are best incorporated into +a change file (e.g., ctangle.ch) as explained in the manual. + +Marc van Leeuwen +Universit\'e de Poitiers +http://wallis.univ-poitiers.fr/~maavl/ diff --git a/web/c_cpp/cwebx/common.c b/web/c_cpp/cwebx/common.c new file mode 100644 index 0000000000..bbd7c75d0a --- /dev/null +++ b/web/c_cpp/cwebx/common.c @@ -0,0 +1,885 @@ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include "common.h" +#define max_bytes 50000L +#define max_modules 1000 +#define max_idents 5000 +#define max_sections 4000 +#define hash_size 353 +#define buf_size 100 +#define longest_name 1000 +#define long_buf_size (buf_size+longest_name) +#define local static +#define array_size(a)((int)(sizeof(a)/sizeof(a[0]))) +#define false (boolean)0 +#define true (boolean)1 +#define ctangle 0 +#define cweave 1 +#define and_and 04 +#define lt_lt 020 +#define gt_gt 021 +#define plus_plus 013 +#define minus_minus 01 +#define minus_gt 031 +#define not_eq 032 +#define lt_eq 034 +#define gt_eq 035 +#define eq_eq 036 +#define or_or 037 +#define find_char()(loc<=limit||get_line()) +#define id_index(p)((sixteen_bits)((p)-id_table)) +#define id_at(i)(&id_table[i]) +#define mod_index(p)((sixteen_bits)((p)-mod_table)) +#define mod_at(i)(&mod_table[i]) +#define name_begin(p)((p)->byte_start) +#define length(p)((int)(strlen(name_begin(p)))) +#define name_end(p)(name_begin(p)+length(p)) +#define complete_name(p)((p)->byte_start[-1]=='\0') +#define print_mod(p) \ +printf(": <%s%s>",name_begin(p),complete_name(p)?"":"...") +#define spotless 0 +#define harmless_message 1 +#define error_message 2 +#define fatal_message 3 +#define mark_harmless() \ +if(history==spotless)history=harmless_message;else +#define mark_error()(history=error_message) +#define overflow(t)fatal("\n! Sorry, %s capacity exceeded",t) +#define confusion(s)fatal("\n! This can't happen: %s",s) +#define show_banner flags['b'] +#define show_happiness flags['h'] +#define show_progress flags['p'] +#define show_stats flags['s'] +#define C_plus_plus flags['+'] +#define compatibility_mode flags['c'] +#define update_terminal()fflush(stdout) +#define new_line()putchar('\n') +#define term_write(string,leng)printf("%.*s",(int)(leng),string) +#define buffer_end (&buffer[buf_size-2]) +#define max_include_depth 10 +#define max_include_paths 8 +#define max_path_length 80 +#define lines_match() \ +(change_limit-change_buffer==limit-buffer \ +&&strncmp(buffer,change_buffer,limit-buffer)==0) +#define byte_mem_end (&byte_mem[max_bytes]) +#define id_table_end (&id_table[max_idents]) +#define mod_table_end (&mod_table[max_modules]) +#define copy_char(c)if(id_loc<mod_text_end)*id_loc++=c;else(void)(c) +/*13:*//*8:*/ +#line 122 "common.inc" +boolean names_match(id_pointer,char*,int,int); +void init_id_name(id_pointer,int); +void init_module_name(mod_pointer);/*:8*//*17:*/ +#line 106 "common.w" +int program,phase;/*:17*//*21:*/ +#line 145 "common.w" +char buffer[long_buf_size]; +char*loc=buffer; + +char*limit=buffer;/*:21*//*24:*/ +#line 230 "common.w" +struct f file[max_include_depth]; +struct f change; +local char web_file_name[max_file_name_length] +,change_file_name[max_file_name_length] +,alt_web_file_name[max_file_name_length]; +int include_depth; +boolean input_has_ended; +boolean changing; +boolean web_file_open=false; +boolean print_where=false; +local struct{char*name;int length;} +at_h_path[max_include_paths],at_i_path; + +boolean including_header_file=false;/*:24*//*29:*/ +#line 382 "common.w" +local boolean saved_changing; +local char*saved_change_limit; +local int saved_include_depth=0;/*:29*//*32:*/ +#line 452 "common.w" +local char change_buffer[buf_size]; +local char*change_limit;/*:32*//*44:*/ +#line 632 "common.w" +sixteen_bits section_count; +eight_bits changed_section[(max_sections+7)/8];/*:44*//*53:*/ +#line 811 "common.w" +char byte_mem[max_bytes]; +char*byte_ptr= &byte_mem[0]; +id_info id_table[max_idents]; +id_pointer id_ptr= &id_table[0]; +mod_info mod_table[max_modules]; +mod_pointer mod_ptr= &mod_table[0];/*:53*//*59:*/ +#line 893 "common.w" +id_pointer hash[hash_size];/*:59*//*68:*/ +#line 1009 "common.w" +mod_pointer root=NULL;/*:68*//*81:*/ +#line 1276 "common.w" +char mod_text[longest_name+1]; +char*id_first; +char*id_loc;/*:81*//*89:*/ +#line 1439 "common.w" +int history=spotless;/*:89*//*96:*/ +#line 1535 "common.w" +boolean flags[UCHAR_MAX+1]; +char C_file_name[max_file_name_length]; +local char tex_file_name[max_file_name_length]; +char idx_file_name[max_file_name_length]; +char scn_file_name[max_file_name_length]; +local boolean change_file_explicit=false;/*:96*//*107:*/ +#line 1704 "common.w" +FILE*C_file; +FILE*tex_file;/*:107*//*110:*/ +#line 1730 "common.w" +local boolean term_line_empty=true;/*:110*//*69:*/ +#line 1021 "common.w" +enum mod_comparison +{less, + +equal, +greater, + +prefix, +extension +};/*:69*//*97:*/ +#line 1547 "common.w" +local void scan_args(int argc,char* *argv);/*:97*//*:13*//*19:*/ +#line 116 "common.w" +void common_init(int argc,char* *argv) +{/*28:*/ +#line 352 "common.w" +{char*cwebinputs=getenv("CWEBINPUTS"); +at_h_path[0].name=at_i_path.name=NULL; +#ifdef CWEBHEADERS +at_h_path[0].name=CWEBHEADERS; +at_h_path[0].length=(int)strlen(CWEBHEADERS); +#endif +if(cwebinputs!=NULL) +{at_i_path.length=(int)strlen(cwebinputs); +at_i_path.name=strcpy(byte_ptr,cwebinputs); +byte_ptr+=at_i_path.length+1; +} +else +{ +#ifdef CWEBINPUTS +at_i_path.name=CWEBINPUTS;at_i_path.length=(int)strlen(CWEBINPUTS); +#endif +} +}/*:28*//*60:*/ +#line 901 "common.w" +{int i=hash_size;do hash[--i]=NULL;while(i>0);}/*:60*//*66:*/ +#line 1000 "common.w" +*byte_ptr++='\0';/*:66*//*83:*/ +#line 1307 "common.w" +mod_text[0]=' ';/*:83*//*102:*/ +#line 1649 "common.w" +show_banner=show_happiness=show_progress=true;/*:102*/ +#line 119 "common.w" +scan_args(argc,argv); +}/*:19*//*22:*/ +#line 162 "common.w" +local boolean input_ln(FILE*f) + +{register int c; +register char*k=limit=buffer; +while((c=getc(f))!='\n'&&c!=EOF) +if(k<=buffer_end){*k++=c;if(!isspace(c))limit=k;} +if(k>buffer_end) +{loc= &buffer[0]; +err_print("! Input line too long"); +if(limit>buffer_end)limit=buffer_end; +} +if(buffer[0]=='@'&&limit> &buffer[1]&&strchr("IXYZ",buffer[1])!=NULL) +buffer[1]=tolower(buffer[1]); +return c!=EOF||limit>buffer; +}/*:22*//*25:*/ +#line 261 "common.w" +boolean push_input_file(boolean header,boolean suspend) +{boolean success=false; +char delim=' '; +while(loc<limit&&(isspace((eight_bits)*loc)))++loc; +if(*loc=='"')delim= *loc++; +else if(*loc=='<')delim='>',++loc; +if(loc>=limit)err_print("! Include file name not given"); + +else if(++include_depth>=max_include_depth) +{--include_depth; +err_print("! Too many nested includes"); +} +else +{/*26:*/ +#line 298 "common.w" +{char*k=cur_file_name; +while(loc<limit +&&(delim==' '?!isspace((eight_bits)*loc):*loc!=delim)) +if(k== &cur_file_name[max_file_name_length-1]) +{err_print("! Include file name truncated");break;} + +else*k++= *loc++; +*k='\0'; +}/*:26*/ +#line 275 "common.w" +if(delim!='>'&&(cur_file=fopen(cur_file_name,"r"))!=NULL) +success=true; +else/*27:*/ +#line 320 "common.w" +{char name_buf[max_path_length+max_file_name_length];int i; +if(header) +for(i=0;i<max_include_paths;++i) +if(at_h_path[i].name==NULL)break; +else +{strcpy(name_buf,at_h_path[i].name); +strcpy(&name_buf[at_h_path[i].length],cur_file_name); +if((cur_file=fopen(name_buf,"r"))!=NULL){success=true;break;} +} +else if(at_i_path.name!=NULL) +{strcpy(name_buf,at_i_path.name); +strcpy(&name_buf[at_i_path.length],cur_file_name); +success=(cur_file=fopen(name_buf,"r"))!=NULL; +} +}/*:27*/ +#line 278 "common.w" +if(success) +{cur_line=0;print_where=true;/*30:*/ +#line 392 "common.w" +if(suspend) +{saved_changing=changing;changing=false; +saved_change_limit=change_limit;change_limit=change_buffer; +saved_include_depth=include_depth; +}/*:30*/ +#line 281 "common.w" +} +else +{--include_depth; +if(delim!='>') +err_print("! Cannot open include file"); + +} +} +loc= &limit[1]; +return success; +}/*:25*//*31:*/ +#line 418 "common.w" +local boolean get_web_line(void) +{do +if(++cur_line,input_ln(cur_file)) +if(!compatibility_mode +&&limit> &buffer[1]&&buffer[0]=='@'&&buffer[1]=='i') +{loc= &buffer[2];print_where=true;push_input_file(false,false);} + +else return true; +else if(include_depth==0) +{input_has_ended=true;web_file_open=false;return false;} +else +{fclose(cur_file);print_where=true; +if(include_depth-- ==saved_include_depth) +{changing=saved_changing;change_limit=saved_change_limit; +saved_include_depth=0;including_header_file=false; +if(changing)return false; +} +} +while(true); +}/*:31*//*33:*/ +#line 464 "common.w" +local void prime_the_change_buffer(void) +{change_limit=change_buffer;/*34:*/ +#line 477 "common.w" +do +{if(++change_line,!input_ln(change_file))return; +if(limit> &buffer[1]&&buffer[0]=='@') +if(buffer[1]=='x')break; +else if(buffer[1]=='y'||buffer[1]=='z') +{loc= &buffer[2]; +err_print("! Where is the matching @x?"); +} +else/*35:*/ +#line 497 "common.w" +{if(buffer[1]=='i'&&!compatibility_mode) +{loc= &buffer[2];err_print("! No includes allowed in change file");} +}/*:35*/ +#line 486 "common.w" +}while(true);/*:34*//*36:*/ +#line 506 "common.w" +do +if(++change_line,!input_ln(change_file)) +{loc= &buffer[0];err_print("! Change file ended after @x");return;} +while(limit==buffer);/*:36*//*37:*/ +#line 512 "common.w" +{int n=(int)(limit-buffer);change_limit=change_buffer+n; +strncpy(change_buffer,buffer,n); +}/*:37*/ +#line 470 "common.w" +}/*:33*//*38:*/ +#line 529 "common.w" +local void check_change(void) + +{int n=0; +if(!lines_match())return; +print_where=true; +do +{changing=true;/*39:*/ +#line 560 "common.w" +{if(++change_line,!input_ln(change_file)) +{loc= &buffer[0];err_print("! Change file ended before @y"); + +change_limit=change_buffer;changing=false;return; +} +if(limit> &buffer[1]&&buffer[0]=='@') +if(buffer[1]=='y')break; +else if(buffer[1]=='x'||buffer[1]=='z') +{loc= &buffer[2];err_print("! Where is the matching @y?");} + +else/*35:*/ +#line 497 "common.w" +{if(buffer[1]=='i'&&!compatibility_mode) +{loc= &buffer[2];err_print("! No includes allowed in change file");} +}/*:35*//*37:*/ +#line 512 "common.w" +{int n=(int)(limit-buffer);change_limit=change_buffer+n; +strncpy(change_buffer,buffer,n); +}/*:37*/ +#line 572 "common.w" +}/*:39*/ +#line 538 "common.w" +changing=false; +if(!get_web_line()) +{loc= &buffer[0]; +err_print("! CWEB file ended during a change");return; + +} +if(!lines_match())++n; +}while(true); +if(n>0) +{loc= &buffer[2]; +print("\n! Hmm... %d of the preceding lines failed to match",n); + +err_print(""); +} +}/*:38*//*41:*/ +#line 585 "common.w" +void reset_input(void) +{boolean use_change_file=change_file_name[0]!='\0';/*42:*/ +#line 601 "common.w" +{if((web_file=fopen(web_file_name,"r"))!=NULL) +strcpy(file[0].name,web_file_name); +else if((web_file=fopen(alt_web_file_name,"r"))!=NULL) +strcpy(file[0].name,alt_web_file_name); +else fatal("! Cannot open \"%s\" as input file",web_file_name); + +web_file_open=true; +if(use_change_file) +if((change_file=fopen(change_file_name,"r"))!=NULL) +strcpy(change.name,change_file_name); +else if(!change_file_explicit) +use_change_file=false; +else fatal("! Cannot open \"%s\" as change file",change_file_name); + +}/*:42*/ +#line 588 "common.w" +cur_line=0;change_line=0;include_depth=0; +if(use_change_file){changing=true;prime_the_change_buffer();} + +else change_limit=change_buffer; + +limit=buffer;loc= &buffer[1]; +changing=false;input_has_ended=false; +}/*:41*//*45:*/ +#line 660 "common.w" +boolean get_line(void) +{ +restart: +if(changing)mark_section_as_changed(section_count); +else/*47:*/ +#line 715 "common.w" +{if(get_web_line() +&&change_limit>change_buffer +&&limit-buffer==change_limit-change_buffer +&&buffer[0]==change_buffer[0] +)check_change(); +}/*:47*/ +#line 665 "common.w" +if(changing) +{/*48:*/ +#line 728 "common.w" +{if(++change_line,!input_ln(change_file)) +{err_print("! Change file ended without @z"); +buffer[0]='@';buffer[1]='z';limit= &buffer[2]; +} +if(limit> &buffer[1]&&buffer[0]=='@') +if(buffer[1]=='z') +{prime_the_change_buffer();changing=false;print_where=true;} +else if(buffer[1]=='x'||buffer[1]=='y') +{loc= &buffer[2];err_print("! Where is the matching @z?");} + +else/*35:*/ +#line 497 "common.w" +{if(buffer[1]=='i'&&!compatibility_mode) +{loc= &buffer[2];err_print("! No includes allowed in change file");} +}/*:35*/ +#line 739 "common.w" +}/*:48*/ +#line 667 "common.w" +if(!changing) +{mark_section_as_changed(section_count);goto restart;} +} +loc= &buffer[0];*limit=' '; +if(compatibility_mode&&buffer[0]=='@'&&buffer[1]=='i') +{loc+=2;print_where=true;push_input_file(false,changing); +goto restart; +} +if(limit-buffer>5 +&&strncmp(buffer,"#line",5)==0&&isspace((eight_bits)buffer[5]))/*46:*/ +#line 686 "common.w" +{sixteen_bits line=0; +print_where=true; +loc= &buffer[6];while(loc<limit&&isspace((eight_bits)*loc))++loc; +if(isdigit((eight_bits)*loc)) +{do line=10*line+ *loc++ -'0';while(isdigit((eight_bits)*loc)); +while(loc<limit&&isspace((eight_bits)*loc))++loc; +if(*loc++ =='"') +{int i=0;while(&loc[i]<limit&&loc[i]!='"')++i; +if(loc[i]=='"'&&i<max_file_name_length) +{struct f*cur_f=changing? &change:&file[include_depth]; +cur_f->line=line-1; +strncpy(cur_f->name,loc,i);cur_f->name[i]='\0'; +goto restart; +} +} +} +err_print("! Improper #line directive");goto restart; + +}/*:46*/ +#line 679 "common.w" +return!input_has_ended; +}/*:45*//*50:*/ +#line 755 "common.w" +void check_complete(void) +{if(change_limit!=change_buffer) +{int l=(int)(change_limit-change_buffer); +strncpy(buffer,change_buffer,l);limit= &buffer[l]; +changing=true;loc=buffer;web_file_open=true; + +err_print("! Change file entry did not match"); + +} +}/*:50*//*54:*/ +#line 823 "common.w" +char*store_string(char*s,int l) +{char*dest=byte_ptr; +if(byte_mem_end-byte_ptr<=l)overflow("byte memory"); +byte_ptr+=l;*byte_ptr++='\0';return strncpy(dest,s,l); +}/*:54*//*61:*/ +#line 914 "common.w" +id_pointer id_lookup(char*first,char*last,int ilk) + +{int l,h; +if(last==NULL)last=first+(l=(int)strlen(first)); +else l=(int)(last-first);/*62:*/ +#line 932 "common.w" +{char*p=first; +h= *p;while(++p<last)h=((h<<1)+ *p)%hash_size; +}/*:62*//*63:*/ +#line 942 "common.w" +{id_pointer p=hash[h]; +while(p!=NULL&&!names_match(p,first,l,ilk))p=p->hash_link; +if(p==NULL)/*64:*/ +#line 955 "common.w" +{p=id_ptr; +if(id_ptr++ >=id_table_end)overflow("identifier"); +name_begin(p)=store_string(first,l); +if(program==cweave)init_id_name(p,ilk); +p->hash_link=hash[h];hash[h]=p; +}/*:64*/ +#line 947 "common.w" +return p; +}/*:63*/ +#line 922 "common.w" +}/*:61*//*70:*/ +#line 1036 "common.w" +local enum mod_comparison mod_name_cmp +(char*p,int l1,char*q,int l2) +{int l=l1<l2?l1:l2; +while(--l>=0)if(*p++ != *q++)return* --p< * --q?less:greater; +return l1<l2?prefix:l1>l2?extension:equal; +}/*:70*//*71:*/ +#line 1051 "common.w" +local mod_pointer make_mod_node(char*name) +{mod_pointer node=mod_ptr; +if(mod_ptr++ >=mod_table_end)overflow("module name"); +name_begin(node)=name; +node->llink=NULL;node->rlink=NULL; +init_module_name(node); +return node; +}/*:71*//*72:*/ +#line 1067 "common.w" +local mod_pointer mod_name_lookup(char*name,int l) + +{mod_pointer p; +mod_pointer*loc= &root; +while((p= *loc)!=NULL) +{int l0=p->key_length;char*key=name_begin(p); +switch(mod_name_cmp(name,l,key,l0)) +{case less:loc= &p->llink;break; +case greater:loc= &p->rlink;break; +case equal:case extension:/*74:*/ +#line 1109 "common.w" +{enum mod_comparison cmp= +mod_name_cmp(name+l0,l-l0,key+l0,(int)strlen(key+l0)); +switch(cmp) +{case less:case greater: +err_print("! Incompatible module name"); +print("\nName inconsistently extends <%.*s...>.\n",l0,key); + +return NULL; +case extension:case equal: +if(complete_name(p)) +if(cmp==equal)return p; +else +{err_print("! Incompatible module name"); +print("\nPrefix exists: <%s>.\n",key);return NULL; + +} +name_begin(p)=store_string(name,l);/*77:*/ +#line 1200 "common.w" +free(key-1);/*:77*/ +#line 1128 "common.w" +return p; +} +}/*:74*/ +#line 1081 "common.w" +case prefix: +err_print("! Incompatible module name"); +print("\nName is a prefix of <%s%s>.\n" +,key,complete_name(p)?"":"..."); +return NULL; +} +}/*73:*/ +#line 1098 "common.w" +{(p=make_mod_node(store_string(name,l)))->key_length=l; +return*loc=p; +}/*:73*/ +#line 1090 "common.w" +}/*:72*//*75:*/ +#line 1152 "common.w" +local mod_pointer prefix_lookup(char*name,int l) + +{mod_pointer p=root,*loc= &root; +mod_pointer match=NULL; +mod_pointer saved=NULL; +while(p!=NULL) +{int l0=p->key_length;char*key=name_begin(p); +switch(mod_name_cmp(name,l,key,l0)) +{case less:p= *(loc= &p->llink);break; +case greater:p= *(loc= &p->rlink);break; +case equal:return p; +case extension:/*78:*/ +#line 1209 "common.w" +{enum mod_comparison cmp= +mod_name_cmp(name+l0,l-l0,key+l0,(int)strlen(key+l0)); +switch(cmp) +{case less:case greater: +err_print("! Incompatible module name"); +print("\nName inconsistently extends <%.*s...>.\n",l0,key); + +return NULL; +case prefix:case equal:return p; +case extension: +if(complete_name(p)) +{err_print("! Incompatible module name"); +print("\nPrefix exists: <%s>.\n",key);return NULL; +}/*79:*/ +#line 1236 "common.w" +{/*77:*/ +#line 1200 "common.w" +free(key-1);/*:77*/ +#line 1237 "common.w" +if((key=(char*)malloc(l+2))==NULL)fatal("Out of dynamic memory!"); +*key++='\1'; +strncpy(key,name,l);key[l]='\0'; +name_begin(p)=key; +}/*:79*/ +#line 1224 "common.w" +return p; +} +}/*:78*/ +#line 1167 "common.w" +case prefix: +if(match!=NULL) +{err_print("! Ambiguous prefix");return NULL;} + +match=p;saved=p->rlink;p=p->llink; +} +if(p==NULL&&match!=NULL) +p=saved,saved=NULL; +} +if(match==NULL)/*76:*/ +#line 1190 "common.w" +{char*key=(char*)malloc(l+2); +if(key==NULL)fatal("Out of dynamic memory!"); +*key++='\1'; +strncpy(key,name,l);key[l]='\0'; +(p=make_mod_node(key))->key_length=l; +return*loc=p; +}/*:76*/ +#line 1179 "common.w" +match->key_length=l; +return match; +}/*:75*//*82:*/ +#line 1291 "common.w" +mod_pointer get_module_name(void) +{/*84:*/ +#line 1315 "common.w" +{eight_bits c;char*k=mod_text; +do +{if(!find_char()) +{err_print("! Input ended in module name");break;} + +c= *loc++;/*85:*/ +#line 1342 "common.w" +if(c=='@') +{if((c= *loc++)=='>')break; +if(isspace(c)||c=='*'||c=='~') +{err_print("! Module name didn't end");loc-=2;break;} + +if(k<mod_text_end-1)* ++k='@'; + +}/*:85*/ +#line 1322 "common.w" +if(isspace(c))c=' '; +if(k<mod_text_end-1&&!(c==' '&& *k==' '))* ++k=c; +}while(true); +id_first= &mod_text[1]; +if(k>=mod_text_end-1) +{print("\n! Module name too long: "); +term_write(id_first,25);err_print(".."); +} +id_loc= *k==' '&&k>mod_text?k:k+1; + +}/*:84*/ +#line 1294 "common.w" +{int l=(int)(id_loc-id_first); +return l>=3&&strncmp(id_loc-3,"...",3)==0 +?prefix_lookup(id_first,l-3):mod_name_lookup(id_first,l); +} +}/*:82*//*86:*/ +#line 1360 "common.w" +boolean get_control_text(void) +{char c,*k=id_first= &mod_text[1]; +do +if((*k++= *loc++)=='@') +if((c= *loc++)!='@') +{if(c!='>') +err_print("! Control codes are forbidden in control text"); + +return(id_loc=k-1)==id_first; +} +while(loc<=limit); +err_print("! Control text didn't end"); +return(id_loc=k)==id_first; +}/*:86*//*87:*/ +#line 1386 "common.w" +void get_string(void) +{char c,delim=loc[-1]; +id_loc=id_first= &mod_text[1];copy_char(delim); +if(delim=='L') +*id_loc++=delim= *loc++; +else if(delim=='<')delim='>'; +do +{if(loc>=limit) +{err_print("! String didn't end");loc=limit;break;} + +copy_char(c= *loc++); +if(c=='\\') +if(loc<limit)copy_char(*loc++); + +else if(get_line()) +if(program==cweave)--id_loc; +else copy_char('\n'); +else +{loc=buffer; +err_print("! Input ended in middle of string"); + +break; +} +else if(!including_header_file&&c=='@') +if(*loc=='@')++loc; +else err_print("! Double @ required in strings"); + +} +while(c!=delim); +if(id_loc>=mod_text_end) +{print("\n! String too long: "); +term_write(mod_text+1,25);err_print(".."); +} +}/*:87*//*90:*/ +#line 1449 "common.w" +void err_print(char*s) +{print(*s=='!'?"\n%s.":"%s.",s); +if(web_file_open)/*91:*/ +#line 1462 "common.w" +{char*k,*l=(loc<limit)?loc:limit; +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); +if(l>buffer) +{for(k=buffer;k<l;k++)putchar(*k=='\t'?' ':*k); +new_line(); +for(k=buffer;k<l;k++)putchar(' '); +} +for(k=l;k<limit;k++)putchar(*k); +}/*:91*/ +#line 1452 "common.w" +update_terminal();mark_error(); +}/*:90*//*92:*/ +#line 1483 "common.w" +void wrap_up(void) +{ +#ifdef STAT +if(show_stats)print_stats(); +#endif/*93:*/ +#line 1496 "common.w" +{static char*mess[]= +{"No errors were found.", +"Did you see the warning message above?", +"Pardon me, but I think I spotted something wrong", +"That was a fatal error, my friend." +}; +if(show_happiness||history>0)print("\n(%s)\n",mess[history]); +}/*:93*/ +#line 1489 "common.w" +exit(history>harmless_message); +}/*:92*//*94:*/ +#line 1510 "common.w" +void fatal(char*s,...) +{va_list p;va_start(p,s); +vprintf(s,p);va_end(p);err_print(""); + +history=fatal_message;wrap_up(); +}/*:94*//*98:*/ +#line 1563 "common.w" +local void scan_args(int argc,char* *argv) +{char*dot_pos; +int files_found=0,paths_found=at_h_path[0].name==NULL?0:1; +while(--argc>0) +if(((* ++argv)[0]=='+'||(*argv)[0]=='-')&&(*argv)[1]!='\0')/*103:*/ +#line 1656 "common.w" +{boolean flag_change=(* *argv=='+'); +char*p= &(*argv)[1];unsigned char c; +while((c= *p++)!='\0') +if((c=tolower(c))!='i')flags[c]=flag_change; +else/*104:*/ +#line 1668 "common.w" +{size_t l=strlen(p); +if(l==0)err_print("! Empty include path"); + +else if(l>max_path_length)err_print("! Include path too long"); + +else if(paths_found>=max_include_paths) +err_print("! Too many include paths"); + +else +{at_h_path[paths_found].length=(int)l; +at_h_path[paths_found++].name=strcpy(byte_ptr,p); +byte_ptr+=l+1; +} +break; +}/*:104*/ +#line 1661 "common.w" +}/*:103*/ +#line 1569 "common.w" +else +{if(strlen(*argv)+5>max_file_name_length) + +fatal("! Filename too long:\n%s",*argv); +dot_pos=strrchr(*argv,'.'); +switch(++files_found) +{case 1:/*99:*/ +#line 1597 "common.w" +#ifndef CPPEXT +#define CPPEXT "C" + +#endif +{if(dot_pos==NULL)sprintf(web_file_name,"%s.w",*argv); +else +{sprintf(web_file_name,"%s",*argv); +*dot_pos='\0'; +} +sprintf(alt_web_file_name,"%s.web",*argv); +sprintf(change_file_name,"%s.ch",*argv); +if(program==ctangle) +sprintf(C_file_name,"%s.%s",*argv,C_plus_plus?CPPEXT:"c"); +else +{sprintf(tex_file_name,"%s.tex",*argv); +sprintf(idx_file_name,"%s.idx",*argv); +sprintf(scn_file_name,"%s.scn",*argv); +} +}/*:99*/ +#line 1577 "common.w" + +break;case 2:/*100:*/ +#line 1624 "common.w" +if((*argv)[0]=='-')change_file_name[0]='\0'; +else if((*argv)[0]!='+') +{change_file_explicit=true; +sprintf(change_file_name,dot_pos==NULL?"%s.ch":"%s",*argv); +}/*:100*/ +#line 1578 "common.w" + +break;case 3:/*101:*/ +#line 1634 "common.w" +if(program==ctangle) +if(dot_pos!=NULL)sprintf(C_file_name,"%s",*argv); +else sprintf(C_file_name,"%s.%s",*argv,C_plus_plus?CPPEXT:"c"); +else +{if(dot_pos!=NULL) +{sprintf(tex_file_name,"%s",*argv);*dot_pos='\0';} +else sprintf(tex_file_name,"%s.tex",*argv); +sprintf(idx_file_name,"%s.idx",*argv); +sprintf(scn_file_name,"%s.scn",*argv); +}/*:101*/ +#line 1579 "common.w" + +break;default:/*105:*/ +#line 1688 "common.w" +fatal("! Usage:\n" +"c%se [(+|-)options] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.%s]]]" +,program==ctangle?"tangl":"weav" +,program==ctangle?"c":"tex");/*:105*/ +#line 1581 "common.w" +} +} +if(files_found==0)/*105:*/ +#line 1688 "common.w" +fatal("! Usage:\n" +"c%se [(+|-)options] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.%s]]]" +,program==ctangle?"tangl":"weav" +,program==ctangle?"c":"tex");/*:105*/ +#line 1584 "common.w" +if(paths_found<max_include_paths) +at_h_path[paths_found].name=NULL; +}/*:98*//*108:*/ +#line 1708 "common.w" +void open_output_file(void) +{char*name;FILE* *file; +if(program==ctangle){name=C_file_name;file= &C_file;} +else{name=tex_file_name;file= &tex_file;} +if((*file=fopen(name,"w"))==NULL) +fatal("! Cannot open \"%s\" as output file",name); + +}/*:108*//*111:*/ +#line 1744 "common.w" +void print(char*s,...) +{va_list p;va_start(p,s); +if(term_line_empty&& *s=='\n')++s; +vprintf(s,p);va_end(p); +term_line_empty=s[strlen(s)-1]=='\n';update_terminal(); +} + +void print_progress(char*s){if(show_progress)print(s);} + +void print_section_progress(void) +{if(show_progress)print("*%u",section_count);}/*:111*/ diff --git a/web/c_cpp/cwebx/common.h b/web/c_cpp/cwebx/common.h new file mode 100644 index 0000000000..6981a0bbcc --- /dev/null +++ b/web/c_cpp/cwebx/common.h @@ -0,0 +1,90 @@ +/*14:*//*15:*/ +#line 90 "common.w" +typedef char boolean; +typedef unsigned char eight_bits; +typedef unsigned short sixteen_bits;/*:15*//*51:*/ +#line 778 "common.w" +typedef struct id_info +{char*byte_start;/*55:*/ +#line 860 "common.w" +struct variant*equiv_or_xref;/*:55*//*57:*/ +#line 871 "common.w" +struct id_info*hash_link; +int ilk;/*:57*/ +#line 780 "common.w" + +}id_info,*id_pointer; + +typedef struct mod_info +{char*byte_start;/*56:*/ +#line 863 "common.w" +struct variant*equiv_or_xref;/*:56*//*65:*/ +#line 979 "common.w" +struct mod_info*llink,*rlink; + +int key_length;/*:65*/ +#line 785 "common.w" + +}mod_info,*mod_pointer;/*:51*//*16:*/ +#line 103 "common.w" +extern int program,phase;/*:16*//*18:*/ +#line 112 "common.w" +void common_init(int argc,char* *argv);/*:18*//*20:*/ +#line 133 "common.w" +extern char buffer[],*loc,*limit;/*:20*//*23:*/ +#line 201 "common.w" +#define max_file_name_length 60 +extern struct f +{FILE*file;char name[max_file_name_length];sixteen_bits line;} +file[],change; +extern int include_depth; +extern boolean input_has_ended,changing,web_file_open,print_where +,including_header_file; + +boolean push_input_file(boolean,boolean); +boolean get_line(void); + +#define cur_file file[include_depth].file +#define cur_file_name file[include_depth].name +#define cur_line file[include_depth].line + +#define web_file file[0].file +#define change_file change.file +#define change_line change.line/*:23*//*40:*/ +#line 579 "common.w" +void reset_input(void);/*:40*//*43:*/ +#line 625 "common.w" +extern sixteen_bits section_count; +extern eight_bits changed_section[]; +#define mark_section_as_changed(n) (changed_section[(n)>>3]|=1<<((n)&7)) +#define section_changed(n) ((changed_section[(n)>>3]&(1<<((n)&7)))!=0)/*:43*//*49:*/ +#line 745 "common.w" +extern void check_complete(void);/*:49*//*52:*/ +#line 796 "common.w" +extern char byte_mem[],*byte_ptr; +extern id_info id_table[],*id_ptr; +extern mod_info mod_table[],*mod_ptr;/*:52*//*58:*/ +#line 886 "common.w" +extern id_pointer hash[]; +#define hash_end (&hash[hash_size]) +id_pointer id_lookup(char*,char*,int);/*:58*//*67:*/ +#line 1005 "common.w" +extern mod_pointer root;/*:67*//*80:*/ +#line 1266 "common.w" +extern char mod_text[],*id_first,*id_loc; +#define mod_text_end (&mod_text[longest_name+1]) +mod_pointer get_module_name(void); +boolean get_control_text(void); +void get_string(void);/*:80*//*88:*/ +#line 1433 "common.w" +extern history; +extern void err_print(char*),wrap_up(void),print_stats(void), +fatal(char*,...);/*:88*//*95:*/ +#line 1531 "common.w" +extern boolean flags[]; +extern char C_file_name[],idx_file_name[],scn_file_name[];/*:95*//*106:*/ +#line 1697 "common.w" +extern FILE*C_file,*tex_file; +void open_output_file(void);/*:106*//*109:*/ +#line 1723 "common.w" +void print(char*,...),print_progress(char*),print_section_progress(void);/*:109*//*:14*/ diff --git a/web/c_cpp/cwebx/common.inc b/web/c_cpp/cwebx/common.inc new file mode 100644 index 0000000000..db117631a6 --- /dev/null +++ b/web/c_cpp/cwebx/common.inc @@ -0,0 +1,184 @@ +@ This is is the beginning of material included (by means of `\.{@@i}') from +the file \.{common.inc}, so that it appears in an identical way in the +programs for \.{CTANGLE}, \.{CWEAVE}, and in the code shared between them +(from the file \.{common.w}). There will therefore not be any code producing +material, just like in a header file. However, the major part of the +declarative information that is shared between all the compilation units is +contained in an actual header file \.{common.h}, which is written as an +auxiliary output file when \.{CTANGLE} processes \.{common.w}; in particular +that file contains all the typedefs, |extern| variable declarations and +function prototype declarations relating to code shared by \.{CTANGLE} and +\.{CWEAVE}. Therefore this file is almost exclusively devoted to some macro +definitions of general utility and a few function prototype declarations for +functions that are {\it used\/} by the shared code but defined separately in +\.{CTANGLE} and \.{CWEAVE}. + +@h <stdlib.h> +@h <stdio.h> +@h <string.h> +@h <ctype.h> +@h <limits.h> +@h "common.h" + +@ We start with some dimensioning parameters that are used both in +\.{CTANGLE} and \.{CWEAVE}. Some of them have been decreased with respect to +their earlier values which were sufficient in the original \.{WEB} to handle +\TeX. Nevertheless they should be sufficient for most applications of +\.{CWEB}, since in \Cee\ there is no need to generate large programs +entirely as a single file (and \TeX\ is not written in \.{CWEB}!). The +smaller values allow \.{CWEB} to run on rather moderate size computers. + +@d max_bytes 50000L + /* the number of bytes in identifier and module names */ +@d max_modules 1000 /* maximal number of module names */ +@d max_idents 5000 /* maximal number of identifiers */ +@d max_sections 4000 + /* greater than the total number of sections, and less than $10240$ */ +@d hash_size 353 /* hash modulus, preferably odd */ +@d buf_size 100 /* maximum length of input line, plus two */ +@d longest_name 1000 + /* module names and strings shouldn't be longer than this */ +@d long_buf_size (buf_size+longest_name) /* for \.{CWEAVE} */ + +@ Here are some macros of general utility. We use |local| in place of +|static| for functions and variables when we want to stress the property +of file-scope rather than static initialisation and permanence of value. +The macro |array_size| can be used to compute the number of elements in a +statically initialised array. For truth values we use the type |boolean| +and the values |true| and |false|. We also make the general convention that +whenever we mention a pointer to the ``end'' of a subsequence of a linear +array, we mean the address of first entry beyond the subsequence itself. +This is in keeping with general practice in~\Cee, and the language definition +in fact guarantees the existence of the address even if the subsequence +should contain the last entry of the array; this convention avoids repeated +use of turgid phrases such as ``pointing one place beyond the last entry +of~\dots''. + +@d local static +@f local static +@d array_size(a) ((int)(sizeof(a)/sizeof(a[0]))) +@d false (boolean) 0 +@d true (boolean) 1 +@d ctangle 0 +@d cweave 1 + +@ Although \.{CWEB} uses the conventions of \Cee\ programs found in +the standard \.{<ctype.h>} header file, it does assume that the character set +is the \caps{ASCII} code. This dependency is mild however, and limited to +the assumption that certain character codes below |040| are not occupied by +ordinary characters. To be able to use such vacant spots in a character +code independent way is possible (as is done in \TeX) by mapping all +characters on input to their \caps{ASCII} positions (thereby ensuring that +certain positions remain unused) and unmapping them on output; such an +approach was deemed too tedious for the \.{CWEB} system however. Rather, +the few places that have to be modified for non-\caps{ASCII} codes can be +located by looking up the entry named ``\caps{ASCII} code dependencies'' +in the index of each of the programs. + +@ A few character pairs are encoded internally as single characters, using +the definitions below. These definitions are consistent with an extension of +\caps{ASCII} code originally developed at \caps{MIT} and explained in +Appendix~C of {\sl The \TeX book\/}. Thus, users who have such a character +set can type things like `\.{\char'32}' and `\.{\char'4}' instead of +`\.{!=}' and `\.{\&\&}'; however, their files will not be too portable until +more people adopt the extended code. To be precise, when moved to other +installations their files can still be processed by \.{CTANGLE} and +\.{CWEAVE}, producing the required output, but users will have problems +reading the source files. Therefore it is advised to use the two-character +form in the \.{CWEB} files, which will be converted to single characters on +input. + +@^ASCII code dependencies@> @^system dependencies@> + +@d and_and 04 /* `\.{\&\&}'; this corresponds to \caps{MIT}'s \.{\char'4} */ +@d lt_lt 020 /* `\.{<<}'; this corresponds to \caps{MIT}'s \.{\char'20} */ +@d gt_gt 021 /* `\.{>>}'; this corresponds to \caps{MIT}'s \.{\char'21} */ +@d plus_plus 013 /* `\.{++}'; this corresponds to \caps{MIT}'s \.{\char'13} */ +@d minus_minus 01 /* `\.{--}'; this corresponds to \caps{MIT}'s \.{\char'1} */ +@d minus_gt 031 /* `\.{->}'; this corresponds to \caps{MIT}'s \.{\char'31} */ +@d not_eq 032 /* `\.{!=}'; this corresponds to \caps{MIT}'s \.{\char'32} */ +@d lt_eq 034 /* `\.{<=}'; this corresponds to \caps{MIT}'s \.{\char'34} */ +@d gt_eq 035 /* `\.{>=}'; this corresponds to \caps{MIT}'s \.{\char'35} */ +@d eq_eq 036 /* `\.{==}'; this corresponds to \caps{MIT}'s \.{\char'36} */ +@d or_or 037 /* `\.{\v\v}'; this corresponds to \caps{MIT}'s \.{\char'37} */ + +@ Invoking the macro |find_char| will fetch a new line if the current one +has been completely read, and it will either return |false| to indicate that +there is no input left, or otherwise ensure that it is safe to inspect +|*loc| (although it might be the line-ending space). + +@d find_char() (loc<=limit || get_line()) + +@ The following declarations give the interface to the searching algorithms. +They are defined separately in \.{CTANGLE} and \.{CWEAVE} and used by the +common code, rather than the other way around. The small number of such +functions does not seem to justify creating a separate header file for them, +and moreover, it would be difficult to decide whether such a header file +should be produced by the source file for \.{CTANGLE} or by the source file +for \.{CWEAVE}. + +@< Function prototypes used but not defined in the shared code @>= + +boolean names_match (id_pointer,char*,int,int); +void init_id_name (id_pointer,int); +void init_module_name (mod_pointer); + +@ Here are some macros associated with the storage of names. The first four +of them convert pointers to structures representing identifiers or module +names to smaller |sixteen_bits| indices into the arrays they are stored in, +and back again. The macros |name_begin|, |name_end| and |length| provide +the basic attributes of the actual string referred to by a name pointer. +The macro |complete_name| tells whether the complete name for a module has +been encountered so far, and |print_mod| prints a module name on the user's +terminal for error reporting, providing a colon, angle brackets and an +ellipsis if necessary. + +@d id_index(p) ((sixteen_bits)((p)-id_table)) +@d id_at(i) (&id_table[i]) +@d mod_index(p) ((sixteen_bits)((p)-mod_table)) +@d mod_at(i) (&mod_table[i]) +@d name_begin(p) ((p)->byte_start) +@d length(p) ((int)(strlen(name_begin(p)))) +@d name_end(p) (name_begin(p)+length(p)) +@d complete_name(p) ((p)->byte_start[-1]=='\0') +@d print_mod(p) + printf(": <%s%s>",name_begin(p), complete_name(p) ? "" : "..." ) + +@ Three levels of error severity are distinguished: informative, serious and +fatal. An overflow stop occurs if \.{CWEB}'s tables aren't large enough. +Sometimes the program will detect a violation of one of its supposed +invariants (at least this could happen if the program still contains some +errors), and \.{CWEB} then prints an error message that is really for the +\.{CWEB} maintenance person, not the user. In such cases the program says +|confusion("indication of where we are")|. + +@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; @+ else @; +@d mark_error() (history=error_message) +@d overflow(t) fatal("\n! Sorry, %s capacity exceeded",t) + @.Sorry,... capacity exceeded@> +@d confusion(s) fatal("\n! This can't happen: %s",s) @.This can't happen@> + +@ Command line flag settings are stored in an array |flags|. For some of +them we use symbolic names. + +@d show_banner flags['b'] /* should the banner line be printed? */ +@d show_happiness flags['h'] /* should lack of errors be announced? */ +@d show_progress flags['p'] /* should progress reports be printed? */ +@d show_stats flags['s'] /* should statistics be printed at end of run? */ +@d C_plus_plus flags['+'] /* is the language `\Cpp' rather than `\Cee'? */ +@d compatibility_mode flags['c'] /* emulate \LKC.? */ + +@ Here are some macros for terminal output. The macro |update_terminal| is +invoked 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. Note that the cast on the |leng| parameter of |term_write| is +necessary because a pointer difference need not be of type |int|. + +@d update_terminal() fflush(stdout) /* empty the terminal output buffer */ +@d new_line() putchar('\n') +@d term_write(string,leng) printf("%.*s",(int)(leng),string) /* write on the standard output */ diff --git a/web/c_cpp/cwebx/common.w b/web/c_cpp/cwebx/common.w new file mode 100644 index 0000000000..9d5176f559 --- /dev/null +++ b/web/c_cpp/cwebx/common.w @@ -0,0 +1,1757 @@ +% This file is part of CWEBx. +% This program by Marc van Leeuwen based on earlier versions by +% D. E. Knuth., Silvio Levy and Frank Jensen. +% It is distributed WITHOUT ANY WARRANTY, express or implied. +% CWEB (Revision: 2.0) % Don Knuth, July 1990 +% Version 3.x, Marc van Leeuwen, December 1993 +% CWEBx 2+1.0, Marc van Leeuwen, August 1994 +% CWEBx 3.0, Marc van Leeuwen, Januari 1995 +% CWEBx 3.02, Marc van Leeuwen, April 1996 + +% Copyright (C) 1987,1990 Silvio Levy and Donald E. Knuth +% Copyright 1994 Marc A. A. van Leeuwen + +% Permission is granted to make and distribute verbatim copies of this +% document provided that the copyright notice and this permission notice +% are preserved on all copies. + +% Permission is granted to copy and distribute modified versions of this +% document under the conditions for verbatim copying, provided that the +% entire resulting derived work is distributed under the terms of a +% permission notice identical to this one. + +\def\pb{$\.|\ldots\.|$} % C brackets (|...|) +\def\LKC.{Levy/Knuth \.{CWEB}} +\def\:#1{`\.{@@#1}'} + +\def\title{Common code for CTANGLE and CWEAVE (Version x3.0)} +\def\topofcontents +{\topglue 0pt plus .5 fill + \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and + {\ttitlefont CWEAVE}} + \vskip 15pt + \centerline{(\.{CWEB} version x3.0)} +} +\def\botofcontents +{\vfill\noindent + Copyright \copyright\ 1987,\thinspace1990 Silvio Levy and Donald E. Knuth + \par\noindent + Copyright 1994 Marc A. A. van Leeuwen + \bigskip\noindent + Permission is granted to make and distribute verbatim copies of this + document provided that the copyright notice and this permission notice + are preserved on all copies. + + \smallskip\noindent + Permission is granted to copy and distribute modified versions of this + document under the conditions for verbatim copying, provided that the + entire resulting derived work is distributed under the terms of a + permission notice identical to this one. +} + +@* Introduction. This file contains code common to both \.{CTANGLE} +and \.{CWEAVE}, that roughly concerns the following problems: input +routines, name table handling, error handling and handling of the command +line. + +@h <stdarg.h> + +@i common.inc + + +@* Generalities. +That completes the contents of \.{common.inc}. +In the texts below we will sometimes use \.{CWEB} to refer to either +of the two component programs, if no confusion can arise. + +Here is the overall appearance of this file, except for the function +definitions for (local and public) functions, which will follow in the +remaining unnamed sections. + +@c +@< Function prototypes used but not defined in the shared code @>@; +@< Definitions of variables common to \.{CTANGLE} and \.{CWEAVE} @>@; +@< Prototypes of local functions @>@; + +@ For all functions and variables defined here that are accessible to +\.{CTANGLE} and \.{CWEAVE}, prototype respectively |extern| declarations are +placed on the file \.{common.h} that is included by all three source files. +Typedef declarations that are publicly visible also appear in this file. + +@( common.h @>= +@< Public typedef declarations @>@; +@< Declarations of public variables and function prototypes @>@; + +@ Since a number of large arrays are used to store various kinds of data, we +wish to have some control over the number of bytes occupied by the basic +items. In such cases we use one of the following types rather than |int|. + +@< Public typedef declarations @>= +typedef char boolean; +typedef unsigned char eight_bits; +typedef unsigned short sixteen_bits; + +@ In certain cases \.{CTANGLE} and \.{CWEAVE} should do almost, but not +quite, the same thing. In these cases we've written common code for both, +differentiating between the two by means of the global variable |program|. +Furthermore, |CTANGLE| operates in two phases (input and output), and +similarly |CWEAVE| operates in three phases (cross-reference collection, +translation of the source, and output of the index); the global variable +|phase| tells which phase we are in. + +@< Declarations... @>= +extern int program, phase; + +@~@< Definitions... @>= +int program, phase; + +@ There's an initialisation function that gets both \.{CTANGLE} and +\.{CWEAVE} off to a good start. + +@< Declarations...@>= +void common_init (int argc,char** argv); + +@~We will fill in the details of this function later. +@c +void common_init (int argc,char** argv) +{ @< Initialise variables @> + @< Set the default options common to \.{CTANGLE} and \.{CWEAVE} @> + scan_args(argc,argv); +} + + +@* Input routines. +The lowest level of input to the \.{CWEB} programs is performed by +|input_ln|, which must be told which file to read from. The return value +of |input_ln| is |true| if the read is successful and |false| if not +(i.e., if file has ended). The conventions of \TeX\ are followed; i.e., +the characters of the next line of the file are copied into the |buffer| +array, and the global variable |limit| will point to the first unoccupied +position; trailing white space is ignored. + +@< Declarations... @>= +extern char buffer[], *loc, *limit; + +@~The value of |limit| must be less than or equal to |buffer_end|, so that +|*buffer_end| is never filled by |input_ln|. The characters |*limit| and +|limit[1]| are reserved for placing a sentinel at the end of the line. For +the convenience of |CWEAVE|, the buffer is extended by |longest_name| +characters, so that there is enough space to place any module name after +the input. + +@d buffer_end (&buffer[buf_size-2]) /* practical end of |buffer| */ + +@< Definitions... @>= +char buffer[long_buf_size]; /* where each line of input goes */ +char *loc=buffer; + /* points to the next character to be read from the buffer */ +char *limit=buffer; /* points to the end of the input line */ + +@ If a non-empty line follows the last newline in a file, we return it with +a success status when |EOF| is read; in this case the next call will read +|EOF| once again, and that time return failure. We are careful in case +|isspace| is a macro using its argument more than once. As a service to the +include and change file handling functions, |get_line| replaces any initial +\:I, \:X, \:Y, or \:Z by its lower-case counterpart. The value of |loc| is +usually irrelevant during the lower level input functions, and will later be +set properly by |get_line|; however, when error messages are given, the +value of |loc| will determine the way that the current line of input is +displayed, so we give it an appropriate value in those cases. + +@c +local boolean input_ln (FILE *f) + /* copies a line into |buffer| or returns |false| */ +{ register int c; /* the character read */ + register char* k=limit=buffer; /* where next character goes */ + while ((c=getc(f))!='\n' && c!=EOF) + if (k<=buffer_end) {@; *k++=c; @+ if (!isspace(c)) limit=k; } + if (k>buffer_end) + { loc=&buffer[0]; /* now |err_print| will display unbroken input line */ + err_print ("! Input line too long"); @.Input line too long@> + if (limit>buffer_end) limit=buffer_end; /* truncate line */ + } + if (buffer[0]=='@@' && limit>&buffer[1] && strchr("IXYZ",buffer[1])!=NULL) + buffer[1]=tolower(buffer[1]); + return c!=EOF || limit>buffer; /* whether anything new has been found */ +} + +@ Now comes the problem of deciding which file to read from next. Recall +that the actual text that \.{CWEB} should process comes from two streams: a +|web_file|, which can contain possibly nested include commands \:i, +and a |change_file|. +For each file we store its name and line number for error reporting and for +the production of \&{\#line} directives by |CTANGLE|. Information for the +|web_file| together with the currently open include files is kept on a stack +|file| with stack pointer |include_depth|, while the change file has its own +record. The boolean |changing| tells whether or not we're reading from the +|change_file|. Whenever we switch from the |cur_file| to the |change_file| +or vice versa, or if the |cur_file| has changed, we tell |CTANGLE| to print +this information by means of a \&{\#line} directive in the \Cee\ file, by +raising the |print_where| flag. This flag is handled like an interrupt +request, i.e., it remains raised until it is serviced at an appropriate time +(when a complete token has been scanned), whereupon it is cleared by the +service routine. In Phase~I of \.{CWEAVE} header files following \:h +are swiftly scanned for typedef declarations, which creates one or more +extra levels of input, for which we use the same stack as for~\:i. +Certain functions operate differently at such times, so we must be able +to distinguish these inclusions; this is achieved by maintaining a boolean +variable |including_header_file|. + +@< Declarations...@>= +#define max_file_name_length 60 +extern struct f +{ FILE *file; char name[max_file_name_length]; sixteen_bits line; } +file[], change; +extern int include_depth; +extern boolean input_has_ended, changing, web_file_open, print_where + , including_header_file; +@) +boolean push_input_file(boolean,boolean); /* start a new level of input */ +boolean get_line (void); /* get the next line of merged input */ + +#define cur_file file[include_depth].file /* current file */ +#define cur_file_name file[include_depth].name /* current file name */ +#define cur_line file[include_depth].line + /* number of current line in current file */ +#define web_file file[0].file +#define change_file change.file +#define change_line change.line + +@~We also keep an array |at_h_path| of alternative search paths for locating +\:h files, and a path |at_i_path| for locating \:i files. + +@d max_include_depth 10 + /* maximum nesting depth of source files, not counting the change file */ +@d max_include_paths 8 + /* maximum number of additional search paths for \:h files */ +@d max_path_length 80 /* maximal length of a search path */ + +@<Definitions...@>= +struct f file[max_include_depth]; /* stack of non-change files */ +struct f change; /* change file */ +local char web_file_name[max_file_name_length] + , change_file_name[max_file_name_length] + , alt_web_file_name[max_file_name_length]; +int include_depth; /* current level of nesting */ +boolean input_has_ended; /* whether there is no more input */ +boolean changing; /* whether the current line is from |change_file| */ +boolean web_file_open=false; /* whether the web file is being read */ +boolean print_where=false; /* should |CTANGLE| print line and file info? */ +local struct { char* name; int length; } + at_h_path[max_include_paths],at_i_path; + /* alternative search paths for \:h and \:i */ +boolean including_header_file=false; /* are we processing \:h? */ + +@ Before we consider merging the main input with the change file, we first +handle pushing and popping files in the main stream. The function +|push_input_file| opens a new level of input, e.g., when a \:i line is +found. It is used both from within the common code, namely to open files +included by~\:i, and by a direct call from |CWEAVE|, to open files included +by~\:h (and nested include files) during its first phase. When it is +called, the file name is supposed to start at the first non-blank character +from |loc|; it is delimited by either blank space, double quotes or angle +brackets. Once the file name is located and the file is opened, any further +input from the current line will be discarded. This function has two boolean +parameters, the first telling whether the file to be included is a header +file (rather than a \:i file), the second telling whether changes should be +suspended during the file inclusion; the boolean result tells whether a +file was actually opened. + +@c +boolean push_input_file(boolean header,boolean suspend) +{ boolean success=false; /* whether a file as been opened */ + char delim=' '; /* the character being used to delimit the file name */ + while (loc<limit && (isspace((eight_bits)*loc))) ++loc; + if (*loc=='"') delim=*loc++; /* file name in quotes */ + else if (*loc=='<') delim='>',++loc; /* file name in angle brackets */ + if (loc>=limit) err_print("! Include file name not given"); + @.Include file name not given@> + else if (++include_depth>=max_include_depth) + @/{@; --include_depth; + err_print("! Too many nested includes"); @.Too many nested includes@> + } + else + { @< Read file name into |cur_file_name| @> + if (delim!='>' && (cur_file=fopen(cur_file_name,"r"))!=NULL) + success=true; + else @< Try to open file in alternative directory, and set |success| @> + if (success) + {@; cur_line=0; print_where=true; + @< If necessary deactivate the change file @> + } + else + { --include_depth; + if (delim!='>') /* don't complain about system header files */ + err_print("! Cannot open include file"); + @.Cannot open include file@> + } + } + loc=&limit[1]; /* force |input_ln| before next character is read */ + return success; +} + +@ When the include file name is delimited by spaces, any white-space +character will end the name; in any case we accept the end of the line as +end of the file name. + +@< Read file name into |cur_file_name| @>= +{ char* k=cur_file_name; + while (loc<limit + &&(delim==' ' ? !isspace((eight_bits)*loc) : *loc!=delim)) + if (k==&cur_file_name[max_file_name_length-1]) + @/{@; err_print("! Include file name truncated"); break; } + @.Include file name truncated@> + else *k++=*loc++; + *k='\0'; +} + +@ At initialisation time, paths may have been stored in |at_i_path| and +|at_h_path|, that will be prefixed to given file name in an attempt to find +include files. For a file opened in this manner |cur_file_name| will not +contain the full path name but just the final component; this affects error +messages and \&{\#line} directives produced while reading the file. The only +problem that this might cause is that a debugger could be unable to locate +a source file for code included from a \:i file; however, it is not likely +that such a file outside the current directory will contain any code +producing material, and good debuggers have their own means to specify a +search path for source files. + +@< Try to open file in alternative directory... @>= +{ char name_buf[max_path_length+max_file_name_length]; int i; + if (header) + for (i=0; i<max_include_paths; ++i) + if (at_h_path[i].name==NULL) break; + else + { strcpy(name_buf,at_h_path[i].name); + strcpy(&name_buf[at_h_path[i].length],cur_file_name); + if ((cur_file=fopen(name_buf,"r"))!=NULL) {@; success=true; break; } + } + else if (at_i_path.name!=NULL) /* \:i include file */ + { strcpy(name_buf,at_i_path.name); + strcpy(&name_buf[at_i_path.length],cur_file_name); + success= (cur_file=fopen(name_buf,"r"))!=NULL; + } +} + +@ We allow values for |at_h_path[0]| and |at_i_path| to be built into the +program by defining the preprocessor symbols |CWEBHEADERS| respectively +|CWEBINPUTS| to be the appropriate strings; these should be complete +prefixes that can be attached to file names, so they probably have to end +with a pathname separator. For |CWEBHEADERS| one could take the system +include file area, although this should usually not be necessary since the +typedefs contained in system header files are already built into |CWEAVE|. +Defining |CWEBINPUTS| only makes sense if \.{CWEB} is compiled specifically +for one particular project, as long as there are no general purpose +\:i~files. Following \LKC. we also allow |CWEBINPUTS| +to be overridden by an environment variable. For specifying further values of +|at_h_path| we use the more practical method of command line arguments (just +like most \Cee~compilers use for specifying alternative include +directories), which are handled by |scan_args|. + +@< Init... @>= +{ char* cwebinputs=getenv("CWEBINPUTS"); + at_h_path[0].name=at_i_path.name=NULL; /* defaults */ +#ifdef CWEBHEADERS + at_h_path[0].name=CWEBHEADERS; + at_h_path[0].length=(int)strlen(CWEBHEADERS); +#endif + if (cwebinputs!=NULL) + { at_i_path.length=(int)strlen(cwebinputs); + at_i_path.name=strcpy(byte_ptr,cwebinputs); + byte_ptr+=at_i_path.length+1; + } + else + { +#ifdef CWEBINPUTS + at_i_path.name=CWEBINPUTS; at_i_path.length=(int)strlen(CWEBINPUTS); +#endif + } +} + +@ In some cases we wish to suspend changes during the inclusion of a file, +and any nested inclusions. This happens when during the first pass of +\.{CWEAVE} a header file specified after \:h is being read in, or when +in compatibility mode a \:i inclusion is issued under control of the +change file. In such cases we suspend the change file by setting +|changing=false| and |change_limit=change_buffer| as if the change file had +ended, after having saved the old values of |changing| and |change_limit|. +The change file will be reactivated when the included file ends, which +requires saving |include_depth| as well. + +@<Definitions...@>= +local boolean saved_changing; /* were we changing before it was suspended? */ +local char* saved_change_limit; /* end of suspended change line */ +local int saved_include_depth=0; /* depth after opening \:h file */ + +@~Although this could be easily changed, the current code relies on the fact +that suspension cannot be nested (as would be the case if \:h were +activated in a file included under control of the change file), since +compatibility mode does not support \:h file inclusion. + +@< If necessary deactivate the change file @>= +if (suspend) +{ saved_changing=changing; changing=false; +@/saved_change_limit=change_limit; change_limit=change_buffer; +@/saved_include_depth=include_depth; +} + +@ The function |get_web_line| fetches the next line from the main input +stream, taking care of the interpretation on \:i and of restoring input to +the parent file when an include file has ended. Like |input_ln| it returns a +boolean value telling whether a line could be found. When this is not the +case, it means that either the main input stream has dried up, or we have +come to the end of a header file included by an \:h code issued from the +change file, in which case |changing==true| afterwards, and we should not +read from the main input stream after all. + +In compatibility mode |get_web_line| operates differently, since in +\LKC. include files are expanded after matching against the change file +rather than before, although an expanded include file will be reconsidered +for matching against (the same line of) the change file, unless it was +included under control of the change file itself. This means that in +compatibility mode no include file should be opened while preparing the +main input stream for a match. On the other hand, if an include file ends, +there is no other sensible action but to close the file and read on at the +previous level. + +@c +local boolean get_web_line(void) +{ do + if (++cur_line,input_ln(cur_file)) /* then a line has been found */ + if (!compatibility_mode + && limit>&buffer[1] && buffer[0]=='@@' && buffer[1]=='i') + @/{@;loc=&buffer[2]; print_where=true; push_input_file(false,false); } + /* expand \:i */ + else return true; /* return the line without further action */ + else if (include_depth==0) /* then end of input has been reached */ + @/{@; input_has_ended=true; web_file_open=false; return false; } + else + { fclose(cur_file); print_where=true; + if (include_depth--==saved_include_depth) /* then restore |changing| */ + { changing=saved_changing; change_limit=saved_change_limit; + saved_include_depth=0; including_header_file=false; + if (changing) return false; /* fall back into change file */ + } + } + while (true); +} + +@ Now we come to merging the main input stream with the change file. +When |changing| is false, the first non-empty line of |change_file| after +the next \:x to be matched is kept in |change_buffer|, for purposes of +comparison with the next line of |cur_file|. The test |lines_match| is +used for equality between the two lines; it will never return |true| when +|change_limit==change_buffer| because it will not be invoked when +|limit==buffer|. + +@d lines_match() + (change_limit-change_buffer==limit-buffer + && strncmp(buffer, change_buffer, limit-buffer)==0) + +@<Definitions...@>= +local char change_buffer[buf_size]; /* next line of |change_file| */ +local char *change_limit; /* points to the effective end of |change_buffer| */ + +@ The function |prime_the_change_buffer| sets |change_buffer| in preparation +for the next matching operation. After the change file has been completely +input, we set |change_limit=change_buffer|, so that no further matches will +be made; since blank lines in the change file are not used for matching, we +have |(change_limit==change_buffer && !changing)| if and only if the change +file is exhausted (or suspended). This function is called only when +|changing| is true; hence error messages will be reported correctly. + +@c +local void prime_the_change_buffer (void) +{ change_limit=change_buffer; + /* this value is used if the change file ends */ + @<Skip over comment lines in the change file; |return| if end of file@> + @<Skip to the next non-blank line; |return| if end of file@> + @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@> +} + +@ While looking for a line that begins with \:x in the change file, we allow +lines that begin with `\.{@@}', as long as they don't begin with \:y or \:z +(which would probably indicate that the change file is fouled up). + +@<Skip over comment lines in the change file...@>= +do +{ if (++change_line,!input_ln(change_file)) return; + if (limit>&buffer[1] && buffer[0]=='@@') + if (buffer[1]=='x') break; + else if (buffer[1]=='y' || buffer[1]=='z') + { loc=&buffer[2]; /* point out error after \:y or \:z */ + err_print ("! Where is the matching @@x?"); @.Where is the match...@> + } + else @< Check for erroneous \:i @> +} while (true); + +@ When not in compatibility mode, \:i lines are expanded by |get_web_line|, +so it makes no sense to place such a code between \:x and~\:y or between \:y +and~\:z; to allow them outside of the changes they would only cause +confusion by suggesting the inclusion of a subsidiary change file. Therefore +we normally do not allow \:i at the beginning of any line of the change file; +in compatibility mode however, \:i can be used in both sides of a change, +and outside of the changes the code is allowed but ignored. + +@< Check for erron... @>= +{ if (buffer[1]=='i' && !compatibility_mode) + @/{@; loc=&buffer[2]; err_print ("! No includes allowed in change file"); } +} @.No includes allowed...@> + +@ After a \:x has been found, we ignore the rest of the line, as well +as any blank lines that follow it; since |input_ln| removes trailing blanks, +we can simply test for empty lines. + +@< Skip to the next non-blank line... @>= +do + if (++change_line,!input_ln(change_file)) + @/{@; loc=&buffer[0]; err_print("! Change file ended after @@x"); return; } +while (limit==buffer); @.Change file ended...@> + +@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>= +{@; int n=(int)(limit-buffer); change_limit=change_buffer+n; + strncpy(change_buffer,buffer,n); +} + +@ The function |check_change| is used to see if the next change entry should +go into effect; it is called only when |changing| is false. 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. When this happens, all of the text down to the \:y +is supposed to match, and an error message is issued if any discrepancy is +found; after finding \:y we have |changing==true|, so that subsequent lines +will be read from the change file. Since |check_change| is called only when +|change_limit>change_buffer|, i.e., when the change file is active, we don't +have to consider the case here that |get_web_line| returns |false| after +reactivating the suspended change file. + +@c +local void check_change (void) + /* switches to |change_file| if the buffers match */ +{ int n=0; /* the number of discrepancies found */ + if (!lines_match()) return; + print_where=true; /* indicate interrupted line sequencing */ + do + { changing=true; + @< Read a line from the change file into the change buffer; + if \:y is found, |break|; if the change file ends, |return| @> + changing=false; + if (!get_web_line()) + @/{@; loc=&buffer[0]; + err_print("! CWEB file ended during a change"); return; + @.CWEB file ended...@> + } + if (!lines_match()) ++n; + } while (true); + if (n>0) + { loc=&buffer[2]; + print("\n! Hmm... %d of the preceding lines failed to match",n); + @.Hmm... $n$ of the preceding...@> + err_print(""); + } +} + +@ Since we read a line from the change file before reading the line from the +main input stream that should match it, we can use |input_ln| to read the +line into |buffer| first, and move it to |change_buffer| afterwards. +When expecting \:y, we signal and ignore any \:x or \:z. + +@< Read a line from the change file into the change buffer... @>= +{ if (++change_line,!input_ln(change_file)) + { loc=&buffer[0]; err_print("! Change file ended before @@y"); + @.Change file ended...@> + change_limit=change_buffer; changing=false; return; + } + if (limit>&buffer[1] && buffer[0]=='@@') + if (buffer[1]=='y') break; + else if (buffer[1]=='x' || buffer[1]=='z') + @/{@; loc=&buffer[2]; err_print("! Where is the matching @@y?"); } + @.Where is the match...@> + else @< Check for erron... @> + @< Move |buffer| and |limit|... @> +} + +@ The function |reset_input|, which gets \.{CWEB} ready to read the \.{CWEB} +source file(s), is used at the beginning of Phase~I of |CTANGLE|, and +of Phases I~and~II of |CWEAVE|. + +@< Declarations... @>= +void reset_input (void); + +@~Although |reset_input| will not read anything from |web_file| after +opening it, it will move up to the first change line in |change_file|. + +@c +void reset_input (void) /* initialise to read the web file and change file */ +{ boolean use_change_file= change_file_name[0]!='\0'; + @<Open input files@> + cur_line=0; change_line=0; include_depth=0; + if (use_change_file) {@; changing=true; prime_the_change_buffer(); } + /* prepare change file */ + else change_limit=change_buffer; + /* emulate that change file that has ended */ + limit=buffer; loc=&buffer[1]; /* now |find_char()| will read a line */ + changing=false; input_has_ended=false; +} + +@ The following code opens the input files. We complain about a missing +change file only if it was explicitly mentioned as a command line argument. + +@<Open input files@>= +{ if ((web_file=fopen(web_file_name,"r"))!=NULL) + strcpy(file[0].name,web_file_name); + else if ((web_file=fopen(alt_web_file_name,"r"))!=NULL) + strcpy(file[0].name,alt_web_file_name); + else fatal("! Cannot open \"%s\" as input file", web_file_name); + @.Cannot open input file@> + web_file_open=true; + if (use_change_file) + if ((change_file=fopen(change_file_name,"r"))!=NULL) + strcpy(change.name,change_file_name); + else if (!change_file_explicit) + use_change_file=false; /* forget about the change file */ + else fatal("! Cannot open \"%s\" as change file", change_file_name); + @.Cannot open change file@> +} + +@ Here are some more variables relevant to the reading of input files. +Every time an input line is read coming from the change file, and on +returning to reading from |cur_file| (possibly after one or more lines of +the main input stream have been removed by the change file), we mark the +current section as having changed by setting a bit in the bitmap +|changed_section|. + +@< Declarations... @>= +extern sixteen_bits section_count; +extern eight_bits changed_section[]; +#define mark_section_as_changed(n) (changed_section[(n)>>3]|=1<<((n)&7)) +#define section_changed(n) ((changed_section[(n)>>3]&(1<<((n)&7)))!=0) + +@~ +@<Defin...@>= +sixteen_bits section_count; /* the current section number */ +eight_bits changed_section[(max_sections+7)/8]; /* is the section changed? */ + +@ The function |get_line| puts the next line of merged input into the +buffer and updates the variables appropriately. A space is placed at the +right end of the line (i.e., at |*limit|), serving many purposes, like +ensuring that a final `\.@@' on a line will be interpreted as \:\ , +and at other times allowing us to test for interesting characters without +first testing for line end. The function returns |!input_has_ended| because +we often want to check the value of that variable after calling the +function. Usually |get_line| is called after the space at |*limit| has been +processed; therefore a call often takes the form of the macro |find_char|, +which calls |get_line| if necessary, and returns whether it has succeeded +in making |loc| point to a valid character (possibly a line-ending space). + +The logic of marking sections as changed, which is implemented below, is a +bit subtle. The status of |changing| is noted for every line read, but since +the test is made at the start of |get_line|, it actually happens just before +the next line is read in. This means that if the first replacement line of a +change involves the start of a new section, then the new section is marked +as changed rather than the one before it, which is the right choice, +assuming that sections are started at the beginning of a line. It is +possible that |changing| is switched on and then right back off again (if +the line after \:y starts with \:z); in that case we mark the +current section as changed and restart |get_line|, since we still have found +no actual line. + +@c +boolean get_line (void) /* inputs the next line */ +{ +restart: + if (changing) mark_section_as_changed(section_count); + else @<Read from |cur_file| and maybe turn on |changing|@> + if (changing) + { @<Read from |change_file| and maybe turn off |changing|@> + if (!changing) + {@; mark_section_as_changed(section_count); goto restart; } + } + loc=&buffer[0]; *limit= ' '; /* place sentinel space */ + if (compatibility_mode && buffer[0]=='@@' && buffer[1]=='i') + @/{@; loc+=2; print_where=true; push_input_file(false,changing); + goto restart; + } + if (limit-buffer>5 + && strncmp(buffer,"#line",5)==0 && isspace((eight_bits)buffer[5])) + @< Set file name and line number according to \&{\#line} + directive and |goto restart| @> + return !input_has_ended; +} + +@ A \&{\#line} directive should have the form `\.{\#line 85 "common.w"}'. +The line number and file name simply override |cur_line| and |cur_file_name|. + +@< Set file name and line number... @>= +{ sixteen_bits line=0; + print_where=true; /* output a \&{\#line} directive soon */ + loc=&buffer[6]; @+ while (loc<limit && isspace((eight_bits)*loc)) ++loc; + if (isdigit((eight_bits)*loc)) + { do line=10*line + *loc++ -'0'; while (isdigit((eight_bits)*loc)); + while (loc<limit && isspace((eight_bits)*loc)) ++loc; + if (*loc++=='"') + { int i=0; @+ while (&loc[i]<limit && loc[i]!='"') ++i; + if (loc[i]=='"' && i<max_file_name_length) + { struct f* cur_f= changing ? &change : &file[include_depth]; + cur_f->line=line-1; /* directive applies to next line, not this one */ + strncpy(cur_f->name,loc,i); cur_f->name[i]='\0'; + goto restart; + } + } + } + err_print("! Improper #line directive"); goto restart; + @.Improper \#line directive@> +} + +@ After checking that a line could be obtained from the main input stream, +some quick tests are made that will avoid calling |check_change| in most +cases. The switch to |changing| mentioned in the module name is +usually brought about by |check_change|, but may also happen when the +suspension of the change file during inclusion of a header file is ended by +|get_web_line|. In either case further input lines should be taken from +the change file. + +@< Read from |cur_file|... @>= +{ if (get_web_line() + && change_limit>change_buffer + && limit-buffer==change_limit-change_buffer + && buffer[0]==change_buffer[0] + ) check_change(); +} + +@ Here we get a line of input from the change file, unless it starts +with~\:z. The statements `|loc=buffer; *limit=' ';|' were performed in +|get_web_line| for lines from the main input stream, but must be issued +explicitly for lines from the change file. + +@< Read from |change_file|... @>= +{ if (++change_line,!input_ln (change_file)) + { err_print("! Change file ended without @@z"); @.Change file ended...@> + @/buffer[0]='@@'; buffer[1]='z'; limit=&buffer[2]; + } + if (limit>&buffer[1] && buffer[0]=='@@') /* check if the change has ended */ + if (buffer[1]=='z') + @/{@; prime_the_change_buffer(); changing=false; print_where=true; } + else if (buffer[1]=='x' || buffer[1]=='y') + @/{@; loc=&buffer[2]; err_print("! Where is the matching @@z?"); } + @.Where is the match...@> + else @< Check for erron... @> +} + +@ The function |check_complete| will be called at the end of \.{CTANGLE} +and \.{CWEAVE} to check for an unfinished state of the change file. + +@< Declarations...@>= +extern void check_complete (void); + +@~When |check_complete| is called we have |input_has_ended|, which implies +|!changing|. The only thing to test for is that there is no change line still +waiting for a match. In order to get a decent display of the non-matching +line, we copy it from the |change_buffer| to the |buffer|, and set some other +variable appropriately. There is no need to restore any variables, since +|check_complete| is called at the very end of the run. + +@c +void check_complete (void) /* checks that all changes were picked up */ +{ if (change_limit!=change_buffer) + { int l=(int)(change_limit-change_buffer); + strncpy(buffer,change_buffer,l); limit=&buffer[l]; + changing=true; loc=buffer; web_file_open=true; + /* prepare unmatched line for display */ + err_print("! Change file entry did not match"); + @.Change file entry did not match@> + } +} + + +@* Storage of names and strings. +Both \.{CTANGLE} and \.{CWEAVE} store the strings representing identifiers, +module names and (in case of \.{CWEAVE}) index entries in a large array of +characters, called |byte_mem|. These strings are not accessed directly, +but via structures that collect further information about these objects. +These structures come in two kinds, depending on whether they correspond +to identifiers (or index entries) or to module names; these structures are +called |id_info| and |mod_info| respectively. + +@< Public typedef...@>= + +typedef struct id_info +{ char *byte_start; /* beginning of the name in |byte_mem| */ + @<More fields of |struct id_info|@>@; +} id_info, *id_pointer; +@) +typedef struct mod_info +{ char *byte_start; /* beginning of the name in |byte_mem| */ + @<More fields of |struct mod_info|@>@; +} mod_info, *mod_pointer; + +@ All |id_info| and |mod_info| structures are stored in one of two arrays, +called |id_table| and |mod_table| respectively, and hence all |id_pointer| +and |mod_pointer| values point into these arrays. Therefore we can freely +convert between such a pointer and an index into the appropriate array; +the macros |id_index|, |id_at|, |mod_index| and |mod_at| defined above +perform these conversions. + +@< Declarations... @>= +extern char byte_mem[], *byte_ptr; +extern id_info id_table[], *id_ptr; +extern mod_info mod_table[], *mod_ptr; + +@~The first unused position in |byte_mem| is kept in |byte_ptr|, and the +first unused positions in |id_table| and |mod_table| are similarly kept in +|id_ptr| and |mod_ptr|, respectively. We want to keep +|byte_ptr<=byte_mem_end|, |id_ptr<=id_table_end| and +|mod_ptr<=mod_table_end|. + +@d byte_mem_end (&byte_mem[max_bytes]) /* end of |byte_mem| */ +@d id_table_end (&id_table[max_idents]) /* end of |id_table| */ +@d mod_table_end (&mod_table[max_modules]) /* end of |mod_table| */ + +@<Definitions...@>= +char byte_mem[max_bytes]; /* characters of names */ +char *byte_ptr=&byte_mem[0]; /* first unused position in |byte_mem| */ +id_info id_table[max_idents]; /* information about identifiers */ +id_pointer id_ptr=&id_table[0]; /* first unused position in |id_table| */ +mod_info mod_table[max_modules]; /* information about module names */ +mod_pointer mod_ptr=&mod_table[0]; /* first unused position in |mod_table| */ + +@ Here is a simple function that copies a (not necessarily null-terminated) +string~|s| of length~|l| into |byte_mem| and returns a pointer to the copied +string. + +@c +char* store_string(char* s, int l) +{ char* dest=byte_ptr; + if (byte_mem_end-byte_ptr<=l) overflow ("byte memory"); + byte_ptr+=l; *byte_ptr++='\0'; return strncpy(dest,s,l); +} + +@ A component is present in both |id_info| and |mod_info| structures, +whose function is different for \.{CTANGLE} and \.{CWEAVE}. In |CTANGLE|, +it is used only in |mod_info| structures, and is a pointer to a replacement +text for the module name. In |CWEAVE| it is a pointer to a list of +cross-references for the identifier or module name. The precise nature of +these pointers is of no interest to the common code, and at this point we do +not have the types available to express this field as a |union|. However, +since in both cases it will be a pointer to a structure, we can use a little +trick by declaring it as a pointer to a |struct variant| where |variant| is +not further specified here. Then in \.{CTANGLE} and \.{CWEAVE} we define +|variant| as a macro for the appropriate |struct| specifier, while in the +common code we leave it undefined (the \:d defining |variant| is given +very early, so that it will precede the `\.{@@h "common.h"}' that will read +in the definition of |struct id_info| and |struct mod_info|). + +It is not quite proper that in the common code these structures contain a +pointer to a named but undefined structure, while in the other compilation +unit of \.{CTANGLE} and \.{CWEAVE} respectively, they contain a pointer to +a known structure with a {\it different\/} specifier (due to the macro +definition); in \Cee\ structures with different specifiers can never be the +same type. However, since the linker does not type-check, and the compiled +code is not likely to depend on the name of a structure specifier, this +should cause no problems, except possibly some mild confusion to a debugger. +An alternative solution of defining a structure of the same name to denote +entirely different types in \.{CTANGLE} and \.{CWEAVE} would be clearer to +the computer, but more confusing to humans. Of course we could also have +used a |(void*)| field instead of a pointer to a structure, but this would +require a lot of explicit or implicit casts, allowing possible type errors +to go undetected by the compiler. + +@< More fields of |struct id_info| @>= +struct variant* equiv_or_xref; /* extra information about this identifier */ +@~ +@< More fields of |struct mod_info| @>= +struct variant* equiv_or_xref; /* extra information about this module */ + +@ The |id_info| structures are linked together into (hopefully) short lists +sharing the same hash key by means of the |hash_link| field. In |CWEAVE|, +an additional field is used to store information about the syntactic r\^ole +of the identifier, its so-called |ilk|. + +@< More fields of |struct id_info| @>= +struct id_info *hash_link; /* links identifiers with same hash code */ +int ilk; /* syntactic information, used in |CWEAVE| only */ + +@ The pointers to the heads of the hash lists are stored in the +array~|hash|. Identifiers can be found by computing a hash code~|h| and then +looking at the identifiers represented by the |id_pointer|s |hash[h]|, +|hash[h]->hash_link|, |hash[h]->hash_link->hash_link|,~\dots, until either +finding the desired name or encountering the null pointer. Thus the hash +table consists of entries of type |id_pointer|; it is maintained by the +function |id_lookup|, which finds a given identifier and returns the +appropriate |id_pointer|. The basic matching is done by the function +|names_match|, which is slightly different in \.{CTANGLE} and \.{CWEAVE}. If +there is no match for the identifier, it is inserted into the table. + +@< Declarations... @>= +extern id_pointer hash[]; +#define hash_end (&hash[hash_size]) /* end of |hash| */ +id_pointer id_lookup(char*,char*,int); + +@~The |hash| table has size~|hash_size|. + +@<Definitions...@>= +id_pointer hash[hash_size]; /* heads of hash lists */ + +@ Initially all the hash lists are empty. Although strictly speaking the +array |hash| should be initialised to null pointers by the compiler, +we don't count too much on this in case such pointers are not represented +by null bit patterns. + +@<Init...@>= +{@; int i=hash_size; do hash[--i]=NULL; while(i>0); } + +@ Here is the main function for finding identifiers (and index entries). +The parameters |first| and |last| point to the beginning and end of the +string. The parameter |ilk| is used by |CWEAVE| only in the course of the +matching process; within |id_lookup| it is just passed on to the functions +|names_match| and~|init_id_name|, which are defined in \.{CTANGLE} and +\.{CWEAVE} separately. We facilitate the initialisation of the reserved +words in |CWEAVE| by allowing the use of null-terminated strings rather +than using a pointer to the end of the string: in this case |first| should +point to such a string, and |last==NULL|. + +@c +id_pointer id_lookup (char* first,char* last,int ilk) + /* look up an identifier */ +{ int l,h; /* length and hash code of the given identifier */ + if (last==NULL) last=first+(l=(int)strlen(first)); /* null-terminated string */ + else l=(int)(last-first); /* compute the length */ + @<Compute the hash code |h| of the string from |first| to |last| @> + @<Compute and |return| the name location, + possibly by entering a new identifier into the tables@> +} + +@ A simple hash code is used: If the sequence of character 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\hbox{|hash_size|}. $$ +The purist might worry about the empty string, which, although of course +impossible as identifier, might be flagged as an index entry; such an index +entry is however explicitly ruled out in |CWEAVE|. + +@<Compute the hash...@>= +{ char* p=first; + h=*p; while (++p<last) h=((h<<1)+*p)%hash_size; +} + +@ Here we either find an existing node for the identifier, or create one, +inserting it into the name table and the hash list. The maximal average +length |max_idents/hash_prime| of hash lists should be sufficiently small +that linearly searching them is quite fast. + +@<Compute and |return|...@>= +{ id_pointer p=hash[h]; /* the head of the hash list */ + while (p!=NULL && !names_match(p,first,l,ilk)) p=p->hash_link; + if (p==NULL) /* we haven't seen this identifier before */ + @< Make |p| point to a fresh |id_node| that refers to a string copied from + |first|, and prepend the node to hash list |h| @> + return p; +} + +@ The information associated with a new identifier must be initialised +in |CWEAVE| in a way that does not apply to~\.{CTANGLE}; hence the +function |init_id_name|. + +@< Make |p| point to a fresh |id_node|... @>= +{ p=id_ptr; /* this is where the new name entry will be created */ + if (id_ptr++>=id_table_end) overflow ("identifier"); + name_begin(p)=store_string(first,l); + if (program==cweave) init_id_name(p,ilk); + p->hash_link=hash[h]; hash[h]=p; /* insert |p| at beginning of hash list */ +} + +@ The names of modules are stored in |byte_mem| together with the identifier +names, but a hash table is not used for them because \.{CWEB} needs to be +able to recognise a module name when given a prefix of that name. To this +end the |mod_info| structures are linked into a binary search tree. The only +unconventional thing about this search tree is that the search key is not +necessarily equal to the full string stored but that a prefix is decisive +for matching purposes: once that prefix matches, not matching the remainder +of the string is considered to be an error. The reason for this is that the +module name may have been specified first by a short prefix and is later +extended; we need to be aware of the ambiguity when a second incompatible +extension of the original prefix is attempted. Therefore a field +|key_length| is included in |struct mod_info| that indicates the length of +the prefix that should determine a unique match; its value is the length of +the shortest partial specification of this module name that has been +encountered so far. + +@< More fields of |struct mod_info| @>= +struct mod_info *llink,*rlink; + /* left and right links in binary search tree */ +int key_length; /* number of characters decisive for match */ + +@ There is one more attribute that must be recorded for a module name, +namely whether the string accessed by the |byte_start| field is the full +module name, or just the longest prefix specified so far (i.e., all +occurrences so far ended with `\.{...}'). Rather than extending |struct +mod_info| with another (boolean) field or encoding this information in one +of the other fields we use a small trick to record this information. All +full module names will be stored in |byte_mem|, and since that is filled +with null-terminated strings, we will have for any |mod_pointer p@;| +representing a complete module name that |p->byte_start[-1]=='\0'|. The +macro |complete_name| defined above tests this condition, and we shall +store incomplete module names in such a way that the condition fails for +them. This method costs just one byte of temporary storage for each module +name that is incompletely specified on its first occurrence, except that a +one-time sacrifice of another byte is needed to guarantee that even the +first name is preceded by a null byte. + +@< Initialise... @>= +*byte_ptr++='\0'; /* prefix a null byte to the first string */ + +@~Like most trees, the search tree for module names starts at its |root|. + +@< Declarations... @>= +extern mod_pointer root; + +@~The search tree starts out empty. +@< Definitions... @>= +mod_pointer root=NULL; + /* the root of the binary search tree for module names */ + +@ The set of all possible strings can be conceptually arranged in an +infinite tree with the empty string at its root, where the direct descendents +of a string are those strings obtained by appending a single letter to it, +listed in alphabetical order. The position of one string relative to another +in this tree is one of five possibilities: to the left of, to the right of, +ancestor of, descendent of, or equal to. The type |enum mod_comparison| +encodes these five possibilities. + +@< Prototypes of local functions @>= +enum mod_comparison @/ +{ less, /* the first name is lexicographically less than, + but no prefix of the second */ + equal, /* the first name is equal to the second */ + greater, /* the first name is lexicographically greater than, + but no extension of the second */ + prefix, /* the first name is a proper prefix of the second */ + extension /* the first name is a proper extension of the second */ +}; + +@~The function |mod_name_cmp| will determine which one of the relations +described above holds for a given pair of strings, each represented by a +pointer to its beginning and its length. + +@c +local enum mod_comparison mod_name_cmp + (char* p, int l1, char* q, int l2) +{ int l= l1<l2 ? l1 : l2; + while (--l>=0) if (*p++!=*q++) return *--p<*--q ? less : greater; + return l1<l2 ? prefix : l1>l2 ? extension : equal; +} + +@ When a new module name is found and approved, we first store the string +representing it in an appropriate place, and then call |new_mod_node| to +install a node for the name in the |mod_table| array and prepare it for +inclusion in the search tree. The information associated with the new node +must be initialised in a slightly different way in |CWEAVE| than in +|CTANGLE|; hence the call of |init_module_name|. + +@c +local mod_pointer make_mod_node (char* name) +{ mod_pointer node=mod_ptr; /* allocate new node */ + if (mod_ptr++>=mod_table_end) overflow ("module name"); + name_begin(node)=name; + node->llink=NULL; node->rlink=NULL; + init_module_name(node); /* initialise new node */ + return node; +} + +@ The function |mod_name_lookup| is used to look up a complete module name +in the search tree, inserting it if necessary, and returns a pointer to +where it was found. Its parameters give the beginning and length of the +module name. The name might be illegal, for instance if it is a prefix of an +existing name, in which case |NULL| is returned rather than a pointer into +|mod_table|, after printing an error message. +@c +local mod_pointer mod_name_lookup (char* name, int l) + /* finds complete module name */ +{ mod_pointer p; /* current node of the search tree */ + mod_pointer* loc=&root; /* |p| will come from this location */ + while ((p=*loc)!=NULL) + { int l0=p->key_length; char* key=name_begin(p); + switch (mod_name_cmp(name,l,key,l0)) + { case less: loc=&p->llink; break; + case greater: loc=&p->rlink; break; + case equal: case extension: + @< Check that |name| matches the remainder of |key|; + if so, complete |p| if necessary and |return p|, + if |name| is a prefix of~|key| fall through, + and otherwise report an error and |return NULL| @> + case prefix: + err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nName is a prefix of <%s%s>.\n" @.Name is a prefix of <...>@> + ,key, complete_name(p) ? "" : "..."); + return NULL; /* dummy module name */ + } + } + @< Copy |name| into |byte_mem|, install a new node in the tree at |*loc|, + and |return| it @> +} + +@ When the name is not found in the tree, a new module has been specified +whose name is complete from the start, and can therefore be installed in +|byte_mem|. Its |key_length| field is set to the length~|l| of the name as +specified here, but it might be decreased later. + +@< Copy |name| into |byte_mem|... @>= +{ (p=make_mod_node(store_string(name,l)))->key_length=l; /* prepare new node */ + return *loc=p; /* install new node into tree */ +} + +@ When the module name |name| being looked up matches the string |key| in a +node~|p| of the search tree for the first |p->key_length| characters, then +this is guaranteed to be the only such match in the tree. It is required +that the rest of |key| matches the remainder of~|name| as well, and if +|complete_name(p)| holds then the match must exhaust~|name|. + +@< Check that |name| matches the remainder of |key|... @>= +{ enum mod_comparison cmp= + mod_name_cmp(name+l0,l-l0,key+l0,(int)strlen(key+l0)); + switch(cmp) + { case less: case greater: + err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nName inconsistently extends <%.*s...>.\n",l0,key); + @.Name inconsistently extends <...>@> + return NULL; + case extension: case equal: + if (complete_name(p)) + if (cmp==equal) return p; + else + { err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nPrefix exists: <%s>.\n",key); return NULL; + @.Prefix exists@> + } + name_begin(p)=store_string(name,l); + /* install |name| in place of |key| */ + @< Deallocate memory for |key| @> + return p; + } +} + +@ The function |prefix_lookup| is similar to |mod_name_lookup|, but it is +called when the specification of a module name ends with `\.{...}'. Here, +unlike in |mod_name_lookup|, there is the possibility that there is more +than one match. The function decides whether there are $0$,~$1$, or~$>1$ +matches present in the tree, but in the final case it need not find all +matches. There is a match of |name| with node~|p| if the first +|p->key_length| characters at |name_begin(p)| are equal, an extension or a +prefix of~|name| (in the last case we should not have |completed(p)|). It is +clear that if there is a match with two distinct nodes, then there is also a +match with their closest common ancestor in the search tree, and since we do +not allow inclusions among the significant parts of the keys in the tree, +this situation can only occur if |name| is a proper prefix for all its +matches. Hence, once a first match is found, any further match must be +among its descendents in the tree. + +The value of |loc| is maintained as in |mod_name_lookup| to allow insertion +of a new node if no match is found; however, once we have |match!=NULL|, the +value of~|loc| becomes irrelevant. + +@c +local mod_pointer prefix_lookup (char* name,int l) + /* finds module name given a prefix */ +{ mod_pointer p=root,* loc=&root; /* current node and where it comes from */ + mod_pointer match=NULL; /* the first matching node, if any */ + mod_pointer saved=NULL; /* another subtree that might have matches */ + while (p!=NULL) + { int l0=p->key_length; char* key=name_begin(p); + switch (mod_name_cmp(name,l,key,l0)) + { case less: p=*(loc=&p->llink); break; + case greater: p=*(loc=&p->rlink); break; + case equal: return p; /* a match, and no other matches are possible */ + case extension: + @< Check that |name| matches the remainder of~|key|; + if so, extend its string if necessary and |return p|, + otherwise report an error and |return NULL| @> + case prefix: + if (match!=NULL) + {@; err_print("! Ambiguous prefix"); return NULL; } + @.Ambiguous prefix@> + match=p; saved=p->rlink; p=p->llink; /* |loc| is irrelevant now */ + } + if (p==NULL && match!=NULL) + p=saved, saved=NULL; /* search other subtree */ + } + if (match==NULL) + @< Copy the incomplete |name| in a temporary place, install a new node + in the tree at |*loc|, and |return| it @> + match->key_length=l; /* |name| is a shorter prefix than used before */ + return match; +} + +@ Incomplete nodes are not stored in |byte_mem|, but temporarily set aside +in dynamic memory, which will be freed when the name is completed. A +non-zero byte is installed at the beginning to make the |complete_name| +macro function properly. Like for complete names, the initial |key_length| +is the full length of the string specified. + +@< Copy the incomplete |name|... @>= +{ char* key=(char*)malloc(l+2); + if (key==NULL) fatal("Out of dynamic memory!"); + *key++='\1'; /* ensure that |complete_name(p)| is false afterwards */ + strncpy(key,name,l); key[l]='\0'; /* store the incomplete name */ + (p=make_mod_node(key))->key_length=l; /* prepare new node */ + return *loc=p; /* install new node into tree */ +} + +@~When freeing the dynamic memory, we mustn't forget to subtract~1. + +@< Deallocate memory for |key| @>= @+ free(key-1); + +@ When we come here, we have found that |name| extends the first +|p->key_length| characters of the name stored in~|p|, and therefore cannot +similarly match any other nodes. + +@< Check that |name| matches the remainder of~|key|; + if so, extend its string if necessary and |return p|, + otherwise report an error and |return NULL| @>= +{ enum mod_comparison cmp= + mod_name_cmp(name+l0,l-l0,key+l0,(int)strlen(key+l0)); + switch(cmp) + { case less: case greater: + err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nName inconsistently extends <%.*s...>.\n",l0,key); + @.Name inconsistently extends <...>@> + return NULL; + case prefix: case equal: return p; + case extension: + if (complete_name(p)) + { err_print("! Incompatible module name"); @.Incompatible module name@> + print("\nPrefix exists: <%s>.\n",key); return NULL; @.Prefix exists@> + } + @< Replace name stored in |p| by the larger prefix |name| @> + return p; + } +} + +@ We come here in the rather unusual case that a second larger prefix of the +same module name is given before the full name is specified. We discard the +old memory for |key| and replace it by a fresh copy of |name|, which is +easier and probably more efficient than to try to reuse the old |key| by +using |realloc|. As for the initial dynamic allocation, we must not forget +the byte at~|key[-1]| here. + +@< Replace name stored in |p| by the larger prefix |name| @>= +{ @< Deallocate memory for |key| @> + if ((key=(char*)malloc(l+2))==NULL) fatal("Out of dynamic memory!"); + *key++='\1'; /* ensure that |complete_name(p)| is false afterwards */ + strncpy(key,name,l); key[l]='\0'; /* store the incomplete name */ + name_begin(p)=key; /* install new name in node |p| */ +} + +@* Lexical routines. +Much of the code of \.{CWEB} deals with lexical matters such as recognising +tokens, control texts, etc. This inevitably involves some complications +since the relevant set of lexical rules varies from place to place in the +source file, being sometimes that of \TeX, sometimes of \Cee, and sometimes +rules defined by \.{CWEB} itself. Furthermore the source file is read in on +three different occasions: once by \.{CTANGLE} and twice by \.{CWEAVE}; the +level of detail in which the source is inspected differs between these +passes. We can alleviate the task of lexical recognition by collecting in +the common code a few functions that will scan specific lexical items; we do +this for various classes of ``large'' lexical items: module names, control +texts and strings. + +In all cases we copy the characters of the lexical item to a separate place, +making minor modifications as necessary (for instance replacing \:@@ +by~`\.@@'). To this end there is a buffer |mod_text| used to copy the +characters into; its name is derived from its most important purpose of +storing module names, for which a separate buffer is definitely needed since +these names can be spread over several lines of input. There are also two +pointers |id_first| and~|id_loc| that are used to point to the beginning +and end of lexical entities. + +@< Declarations... @>= +extern char mod_text[], *id_first, *id_loc; +#define mod_text_end (&mod_text[longest_name+1]) /* end of |mod_text| */ +mod_pointer get_module_name (void); +boolean get_control_text(void); +void get_string(void); + +@~The character |mod_text[0]| will not be used to store any actual +characters, whence we make |mod_text| one slot longer than |longest_name|. + +@<Definitions...@>= +char mod_text[longest_name+1]; /* name being sought for */ +char *id_first; /* where the current identifier begins in the buffer */ +char *id_loc; /* just after the current identifier in the buffer */ + +@ The function for scanning and looking up module names is +|get_module_name|; it will read the module name from the input, and decide +how to search depending on the presence or absence of an ellipsis. This +function is called when \:< has just been scanned; it will scan the +module name and look it up by |mod_name_lookup| or |prefix_lookup| as +appropriate; it returns a |mod_pointer| to the name found, or |NULL| if the +name was found to be erroneous. Consequent to the limit, \.{CWEB} will treat +`\.{@@<...@@>}' as a valid module name if and only if if there exactly one +module name in the entire program. + +@c +mod_pointer get_module_name (void) +{ @< Put module name into |mod_text|, + set |id_first| and |id_loc| appropriately @> + { int l=(int)(id_loc-id_first); + return l>=3 && strncmp(id_loc-3,"...",3)==0 + @/ ? prefix_lookup(id_first,l-3) : mod_name_lookup(id_first,l); + } +} + +@ 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, and put |id_first=&mod_text[1]| for the calls to the lexical routines +that use |mod_text|. + +@<Init...@>= +mod_text[0]=' '; + +@~A module name of exactly |longest_name| characters will cause an error +message, even though all its characters are stored; this is because we +don't take the effort to distinguish a `full' state of the buffer from an +`overflowed' one. + +@< Put module name... @>= +{ eight_bits c; char* k=mod_text; /* points to last recorded character */ + do + { if (!find_char()) + {@; err_print("! Input ended in module name"); break; } + @.Input ended in module name@> + c=*loc++; + @<Handle |c=='@@'|; if end of name, |break|@> + if (isspace(c)) c=' '; /* convert tabs, newlines etc. */ + if (k<mod_text_end-1 && !(c==' ' && *k==' ') ) *++k=c; + } while(true); + id_first=&mod_text[1]; + if (k>=mod_text_end-1) +@/{@; print("\n! Module name too long: "); @.Module name too long@> + term_write(id_first,25); err_print(".."); + } + id_loc= *k==' ' && k>mod_text ? k : k+1; + /* point after last non-space character */ +} + +@ Except for the beginning of new sections we are not fussy about unexpected +control sequences within module names, since we might be within `\pb'; +any problems will be eventually be detected during the output of module +names by |CWEAVE|. So we are only looking for \:> (incidentally this +means that control texts like those introduced by \:t are forbidden +in module names). + +@< Handle |c=='@@'|... @>= +if (c=='@@') +{ if ((c=*loc++)=='>') break; + if (isspace(c) || c=='*' || c=='~') + @/{@; err_print("! Module name didn't end"); loc-=2; break; } + @.Module name didn't end@> + if (k<mod_text_end-1) *++k='@@'; + /* record the `\.{@@}', now |c==loc[-1]| again */ +} + +@ The function |get_control_text| handles control texts. Like +|get_module_name| it copies text until \:> into the |mod_text| array, +and sets |id_first| and |id_loc| appropriately. The function returns a +boolean value telling whether the control text was in fact empty, since that +case sometimes needs special treatment. Always using |get_control_text| +guarantees uniformity in the recognition of various types of control texts +(not counting module names). + +@c +boolean get_control_text(void) +{ char c,* k=id_first=&mod_text[1]; /* points after last recorded character */ + do + if ((*k++=*loc++)=='@@') + if ((c=*loc++)!='@@') + { if (c!='>') + err_print("! Control codes are forbidden in control text"); + @.Control codes are forbidden...@> + return (id_loc=k-1)==id_first; + } + while(loc<=limit); + err_print("! Control text didn't end"); @.Control text didn't end@> + return (id_loc=k)==id_first; +} + +@ And here is a similar function |get_string| that is used to scan strings +and character constants. These 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|. The macro +|copy_char| copies a character to |mod_text| without risking overflow; the +|else| branch of its definition serves only to ensure that any side effect +of its argument is always performed. + +@d copy_char(c) @+ if (id_loc<mod_text_end) *id_loc++=c; @+ else (void)(c) @; + +@c +void get_string(void) +{ char c, delim = loc[-1]; /* what started the string */ + id_loc=id_first = &mod_text[1]; copy_char(delim); + if (delim=='L') + *id_loc++=delim=*loc++; /* after `\.L' comes the real delimiter */ + else if (delim=='<') delim='>'; /* for file names in \&{\#include} lines */ + do + { if (loc>=limit) + {@; err_print("! String didn't end"); loc=limit; break; } + @.String didn't end@> + copy_char(c=*loc++); + if (c=='\\') + if (loc<limit) copy_char(*loc++); + /* record `\.\\|c|' with |c!='\n'| literally */ + else if (get_line()) + if (program==cweave) --id_loc; /* |CWEAVE| erases backslash */ + else copy_char('\n'); /* but |CTANGLE| copies escaped newline */ + else + {@; loc=buffer; + err_print("! Input ended in middle of string"); + @.Input ended in middle of string@> + break; + } + else if (!including_header_file && c=='@@') + if (*loc=='@@') ++loc; /* undouble \:@@ */ + else err_print("! Double @@ required in strings"); + @.Double @@ required...@> + } + while (c!=delim); + if (id_loc>=mod_text_end) + @/{@; print("\n! String too long: "); @.String too long@> + term_write(mod_text+1,25); err_print(".."); + } +} + + +@* 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 behaviour of the +program; it is simply computed for the convenience of systems that might +want to use such information. + +@< Declarations... @>= +extern history; /* indicates how bad this run was */ +extern void err_print (char*), wrap_up (void), print_stats (void), + fatal (char*,...); + +@~Despite the name |history|, each run starts in an unblemished state. +@< Definitions... @>= +int history=spotless; /* indicates how bad this run was */ + +@ The call |err_print("! Error message")| will report an error to the user, +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 the +string passed to |err_print| does not end with a period, since one will be +automatically supplied, as will an initial newline if the string begins +with~|"!"|. + +@c +void err_print (char *s) /* prints `\..' and location of error message */ +{ print(*s=='!' ? "\n%s." : "%s.",s); + if (web_file_open) @<Print error location based on input buffer@> + update_terminal(); 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|. + +@< Print error location based on input buffer @>= +{ char *k, *l=(loc<limit) ? loc : limit; /* pointers into |buffer| */ + if (changing) printf(" (l. %d of change file)\n", change_line); + else if (include_depth==0) printf(" (l. %d)\n", cur_line); + else printf(" (l. %d of include file %s)\n", cur_line, cur_file_name); + if (l>buffer) + { for (k=buffer; k<l; k++) putchar(*k=='\t' ? ' ': *k); + new_line(); + for (k=buffer; k<l; k++) putchar(' '); /* space out the next line */ + } + for (k=l; k<limit; k++) putchar(*k); /* print the part not yet read */ +} + +@ When a fatal error is encountered, or when the program comes to its normal +termination, the function |wrap_up| is called, which will not return to the +caller. 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 at worst only harmless messages were printed. +@^system dependencies@> + +@c +void wrap_up (void) +{ +#ifdef STAT + if (show_stats) print_stats(); /* print statistics about memory usage */ +#endif + @< Print the job |history| @> + exit(history>harmless_message); +} + +@ A spotless status is reported if |show_happiness| is true, any other +exit status is always reported. + +@< Print the job |history| @>= +{ static char* mess[]= + { "No errors were found.", + "Did you see the warning message above?", + "Pardon me, but I think I spotted something wrong", + "That was a fatal error, my friend." + }; + if (show_happiness || history>0) print("\n(%s)\n",mess[history]); +} + +@ When an error or overflow condition is detected for which no recovery is +possible, we call |fatal|, which aborts the program after pointing out the +source of trouble and wrapping up as graciously as possible. + +@c +void fatal(char* s,...) +{ va_list p; va_start(p,s); + vprintf(s,p); va_end(p); err_print(""); + /* print reason and location of fatal stop */ + history=fatal_message; wrap_up(); +} + + +@* Command line arguments. +The user calls \.{CWEAVE} and \.{CTANGLE} with one or more arguments on the +command line. These are either file names or sets of flags to be turned on +(beginning with |"+"|) or off (beginning with |"-"|); in case the special +flag |'i'| occurs, the remainder of that argument is interpreted as a string +rather than as a set of flags. + +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. Most of the flags are undefined but available for +future extensions. + +@< Declarations... @>= +extern boolean flags[]; +extern char C_file_name[],idx_file_name[],scn_file_name[]; + +@~@< Definitions... @>= +boolean flags[UCHAR_MAX+1]; /* an option for each character code */ +char C_file_name[max_file_name_length]; /* name of |C_file| */ +local char tex_file_name[max_file_name_length]; /* name of |tex_file| */ +char idx_file_name[max_file_name_length]; /* name of index file */ +char scn_file_name[max_file_name_length]; /* name of module names file */ +local boolean change_file_explicit=false; + /* was a change file argument specified? */ + +@ We now must look at the command line arguments and set the file names +accordingly. + +@< Prototypes of local functions @>= +local void scan_args (int argc,char** argv); + +@~At least one file name must be present: the \.{CWEB} file. It may have an +extension, or it may omit the extension to get |".w"| or |".web"| added. +The \TeX\ output file name is formed by replacing the \.{CWEB} file name +extension by |".tex"|, and the \Cee\ file name by replacing the extension +by~|".c"|. + +If a second file name is given, it is the change file, again either with an +extension or without one to get |".ch"|. An omitted change file argument +means that the change file name is that of the \.{CWEB} file with the +extension replaced by |".ch"| if that file exists, or none at all otherwise. +If present, a third file name replaces the default output file name, +possibly including the extension. + +@c +local void scan_args (int argc,char** argv) +{ char *dot_pos; /* position of rightmost |'.'| in the argument */ + int files_found=0, paths_found= at_h_path[0].name==NULL ? 0 : 1; + while (--argc>0) /* first ``argument'' (program name) is irrelevant */ + if (((*++argv)[0]=='+' || (*argv)[0]=='-') && (*argv)[1]!='\0') + @<Handle flag argument@> + else + { if (strlen(*argv)+5>max_file_name_length) + /* we need room to add things like `\.{.web}' */ + fatal("! Filename too long:\n%s", *argv); + dot_pos=strrchr(*argv,'.'); + switch (++files_found) + { case 1: + @< Make |web_file_name|, and defaults for other file names, + from~|*argv| @> @+ + break; case 2: @< Make |change_file_name| from |*argv| @> @+ + break; case 3: @< Make output file names from |*argv| @> @+ + break; default: @< Print usage error message and quit @> + } + } + if (files_found==0) @< Print usage error message and quit @> + if (paths_found<max_include_paths) + at_h_path[paths_found].name=NULL; /* mark end of list */ +} + +@ We use |*argv| for the |web_file_name| if there is a |'.'| in it, +otherwise we add |".w"|. We prepare an alternative name |alt_web_file_name| +by adding |"web"| after the dot. The other file names come from adding other +things after the dot. Since there is no general agreement about the proper +extension for \Cpp\ file names, we use a macro |CPPEXT| for it, which +defaults to |"C"| but can be overridden by predefining this preprocessor +symbol when \.{common.c} is compiled. + +@< Make |web_file_name|... @>= +#ifndef CPPEXT +#define CPPEXT "C" + /* extension for \Cpp\ file names; should not exceed 3 characters */ +#endif +{ if (dot_pos==NULL) sprintf(web_file_name,"%s.w",*argv); + else + { sprintf(web_file_name,"%s",*argv); /* use file name and extension */ + *dot_pos='\0'; /* truncate the name before the dot */ + } + sprintf(alt_web_file_name,"%s.web",*argv); + sprintf(change_file_name,"%s.ch",*argv); + if (program==ctangle) + sprintf(C_file_name,"%s.%s",*argv, C_plus_plus ? CPPEXT : "c"); + else + { sprintf(tex_file_name,"%s.tex",*argv); + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); + } +} + +@ If in place of the change file name a `\.-' is specified, we clear the +|change_file_name|, and if a `\.+' is specified we retain the default +(but |files_found| is increased so that the next file name will be the +output file); otherwise, |change_file_explicit| is raised and the default +change file name is replaced by the given one. + +@< Make |change_file_name|... @>= +if ((*argv)[0]=='-') change_file_name[0]='\0'; +else if ((*argv)[0]!='+') +@/{@; change_file_explicit=true; + sprintf(change_file_name,dot_pos==NULL ? "%s.ch" : "%s", *argv); +} + +@ If an output file name is given when calling \.{CWEAVE}, its base name is +also used for |idx_file_name| and |scn_file_name|. + +@< Make output file names... @>= +if (program==ctangle) + if (dot_pos!=NULL) sprintf(C_file_name, "%s", *argv); + else sprintf(C_file_name,"%s.%s", *argv, C_plus_plus ? CPPEXT : "c"); +else +{ if (dot_pos!=NULL) + {@; sprintf(tex_file_name, "%s", *argv); *dot_pos='\0'; } + else sprintf(tex_file_name,"%s.tex", *argv); + sprintf(idx_file_name,"%s.idx",*argv); + sprintf(scn_file_name,"%s.scn",*argv); +} + +@ The |flags| are turned off initially; any flags that are on by default are +set before calling |common_init|. + +@< Set the default options common to \.{CTANGLE} and \.{CWEAVE} @>= +show_banner=show_happiness=show_progress=true; + +@~Flags are made case-insensitive, since some operating systems do not +allow passing of both lower and upper case arguments. The |'i'| flag gets +a special treatment. + +@< Handle flag... @>= +{ boolean flag_change=(**argv == '+'); + char* p=&(*argv)[1]; unsigned char c; + while ((c=*p++)!='\0') + if ((c=tolower(c))!='i') flags[c]=flag_change; + else @< Store string |p| as new include path and |break| @> +} + +@ We copy include paths into |byte_mem|; since we are at the very beginning +of the run, there is no chance that we overflow |byte_mem| already, so that +we need not include a fourth error message below. + +@< Store string |p| as new include path and |break| @>= +{ size_t l=strlen(p); + if (l==0) err_print("! Empty include path"); + @.Empty include path@> + else if (l>max_path_length) err_print("! Include path too long"); + @.Include path too long@> + else if (paths_found>=max_include_paths) + err_print("! Too many include paths"); + @.Too many include paths@> + else + { at_h_path[paths_found].length=(int)l; + at_h_path[paths_found++].name=strcpy(byte_ptr,p); + byte_ptr+=l+1; + } + break; +} + +@ The usage message gives only a sketch of the proper way to call the +\.{CWEB}. programs; more details can be found in the manual. + +@< Print usage error message and quit @>= +fatal("! Usage:\n" @+ +"c%se [(+|-)options] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.%s]]]" + , program==ctangle ? "tangl" : "weav" + , program==ctangle ? "c" : "tex"); + +@* Output. The only thing done in the common code for the output files is +declaring the variables through which they are accessed, and opening them. + +@< Declarations... @>= +extern FILE *C_file, *tex_file; +void open_output_file(void); + +@~Having separate variables for the output files of \.{CTANGLE} and +\.{CWEAVE} only serves to give them more meaningful names. + +@<Definitions...@>= +FILE *C_file; /* where output of \.{CTANGLE} goes */ +FILE *tex_file; /* where output of \.{CWEAVE} goes */ + +@~@c +void open_output_file(void) +{ char* name; FILE** file; + if (program==ctangle) {@; name=C_file_name; file=&C_file; } + else {@; name=tex_file_name; file=&tex_file; } + if ((*file=fopen(name,"w"))==NULL) + fatal("! Cannot open \"%s\" as output file",name); + @.Cannot open output file@> +} + +@ All regular terminal output passes through the function |print|, which +takes the place of |printf|; it suppresses initial newlines if the line is +already empty. The function |print_progress| and |print_section_progress| +are used to display progress reports on the screen. + +@< Declarations...@>= +void print(char*,...), print_progress(char*), print_section_progress(void); + +@~A variable |term_line_empty| keeps track of whether we are at the +beginning of an output line, so that we can force certain terminal output +to start on a new line without producing empty lines. + +@<Definitions...@>= +local boolean term_line_empty=true; + /* has anything been written to the current line? */ + +@~Any initial or final newline produced by~|print| is caused by the format +string, not by any of the arguments following it; this makes it easy to +maintain |term_line_empty|. + +If a function produces terminal output otherwise than through one of the +three functions below, it should take care of |term_line_empty| itself; +for instance |err_print| produces the error context using |printf| and +|putchar|, knowing that |term_line_empty==false| at that time, which is +also the correct value afterwards. + +@c +void print(char* s,...) +{ va_list p; va_start(p,s); + if (term_line_empty && *s=='\n') ++s; /* avoid printing empty line */ + vprintf(s,p); va_end(p); /* print formatted value */ + term_line_empty= s[strlen(s)-1]=='\n'; update_terminal(); +} + +void print_progress (char* s) @+{@; if (show_progress) print(s); } + +void print_section_progress (void) +@+{@; if (show_progress) print("*%u",section_count); } + + +@* Index. % CWEAVE will add the index to this section. diff --git a/web/c_cpp/cwebx/compare.tex b/web/c_cpp/cwebx/compare.tex new file mode 100644 index 0000000000..8d8c03f490 --- /dev/null +++ b/web/c_cpp/cwebx/compare.tex @@ -0,0 +1,202 @@ +\input cwebxmac + +\N0 1. Comparing text files. +This is an entirely trivial program, that tests whether two text files are +equal, and if not so, points out the first point of difference. + +\Y\B\h$\.{<stdio.h>}$\par +\B\h$\.{<stdlib.h>}$\par +\Y\B$\&{typedef}~\&{char}~\&{bool};$\par +\fi + +\M2. The outline of the program is simple. We read characters from both input +files into $ c_1$ and~$ c_2$ until the comparison is complete. Line and column +counts are maintained in $\\{line}$ and~$\\{col}$. + +\Y\B\D$\\{left\_margin}$\5 +$\T{1}$\C{ leftmost column number; change to 0 if you prefer }\par +\Y\B$\X5:Functions\X$\7 +\&{int} $\\{main}$\5 +$(\1\1\1\&{int}~ n,\31~\&{char}~\m*\m*\\{arg}\2\2\2)$\6 +$\a\{\1\&{FILE}~\m* f_1,\31~\m* f_2;$\C{ the two input files }\6 +$\&{int}~ c_1,\31~ c_2,\31~\\{col}\K\\{left\_margin};$\6 +$\&{long}~\\{line}\K\T{1};$\7 +$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the command line +or from the terminal; in case of an error for which no recovery is possible, +call $\\{exit}(\T{1})$\X$\6 +$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a +difference was found\X$\6 +$\X4:Report the outcome of the comparison\X$\6 +$\&{return}~\T{0};$\C{ successful completion }\2\6 +$\}$\par +\fi + +\M3. The heart of the program is this simple loop. When we reach the end of +one of the files, the files match if and only if the other file has also +reached its end. For this reason the test $ c_1\E c_2$, which requires +characters to be read from both files, must precede the test for file end; +when only one file ends, it is the former test which breaks the loop. + +\Y\B\4$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a +difference was found\X\EQ{}$\6 +\&{while}~$(( c_1\K\\{getc}( f_1))\E( c_2\K\\{getc}( f_2))\W c_1\I\.{EOF})\1$\6 +\&{if}~$( c_1\E\.{'\\n'})\1$\5 +$\{\1$\5 +$\PP\\{line};$\5 +$\\{col}\K\\{left\_margin};\2$\5 +$\}$\5 +\2\&{else}\1\5 +$\PP\\{col};\2$\2\par +\U 2.\fi + +\M4. When the first difference occurs at the end of one of the files, or at the +end of a line, we give a message indicating this fact. + +\Y\B\4$\X4:Report the outcome of the comparison\X\EQ{}$\6 +\&{if}~$( c_1\E c_2)\1$\5 +$\\{printf}(\.{"Files\ match.\\n"});\2$\6 +\&{else}\6 +$\a\{\1\\{printf}(\.{"Files\ differ.\\n"});$\6 +\&{if}~$( c_1\E\.{EOF}\V c_2\E\.{EOF})$\1\6 +$\{\1$\5 +$\\{the\_file}( c_1\E\.{EOF});$\5 +$\\{printf}(\.{"is\ contained\ in\ the\)\ other\ as\ initial\ seg\)ment.\\n"});% +\2$\5 +$\}\2$\6 +\&{else}~\&{if}~$( c_1\E\.{'\\n'}\V c_2\E\.{'\\n'})$\1\6 +$\{\1$\5 +$\\{the\_file}( c_1\E\.{'\\n'});$\5 +$\\{printf}(\.{"has\ a\ shorter\ line\ \)number\ \%ld\ than\ the\ o\)ther.% +\\n"},\31\\{line});\2$\5 +$\}\2$\6 +\&{else}\1\5 +$\\{printf}(\.{"First\ difference\ at\)\ line\ \%ld,\ column\ \%d.\)\\n"},\31% +\\{line},\31\\{col});\2$\2\6 +$\}$\par +\U 2.\fi + +\M5. The function $\\{the\_file}$ starts a sentence about the first or second +file, +depending on its boolean argument. + +\Y\B\4$\X5:Functions\X\EQ{}$\6 +\&{void} $\\{the\_file}$\5 +$(\1\1\1\&{bool}~\\{is\_first}\2\2\2)$\5 +$\{\1$\5 +$\\{printf}(\.{"The\ \%s\ file\ "},\31\\{is\_first}\?\.{"first"}:\.{"second"});% +\2$\5 +$\}$\par +\A 7. +\U 2.\fi + +\M6. There can be be zero, one or two command line arguments. If there are +none, +the user is prompted to supply them, and if there are two these are taken as +the file names, prompting the user only in case a file could not be opened. +In case just one argument is present, the first file is assumed to be the +standard input, which does not have to be opened; in this case however we +will not read a file name from terminal in case the second file cannot be +opened. + +\Y\B\D$\\{read\_mode}$\5 +$\.{"r"}$\par +\Y\B\4$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the +command line or from the terminal; in case of an error for which no recovery is +possible, call $\\{exit}(\T{1})$\X\EQ{}$\6 +$\MM n;$\5 +$\PP\\{arg};$\C{ ignore ``argument'' 0, which is the program name }\6 +\&{if}~$( n\E\T{0})$\1\6 +$\{\1$\5 +$\\{open\_file}(\m\AND f_1,\31\.{"First\ file\ to\ compa\)re"},\31\NULL);$\5 +$\\{open\_file}(\m\AND f_2,\31\.{"Second\ file\ to\ comp\)are"},\31\NULL);\2$\5 +$\}\2$\6 +\&{else}~\&{if}~$( n\E\T{1})$\6 +$\a\{\1 f_1\K\\{stdin};$\6 +\&{if}~$(( f_2\K\\{fopen}(\m*\\{arg},\31\\{read\_mode}))\E\NULL)\1$\5 +$\{\1$\5 +$\\{printf}(\.{"Could\ not\ open\ file\)\ \%s.\\n"},\31\m*\\{arg});$\5 +$\\{exit}(\T{1});\2$\5 +$\}\2$\2\6 +$\}$\6 +\&{else}~\&{if}~$( n\E\T{2})$\6 +$\a\{\1\\{open\_file}(\m\AND f_1,\31\.{"Give\ another\ first\ \)file"},\31\m*% +\\{arg}\PP);$\5 +$\\{open\_file}(\m\AND f_2,\31\.{"Give\ another\ second\)\ file"},\31\m*% +\\{arg});\2$\6 +$\}$\6 +\&{else}\1\5 +$\{\1$\5 +$\\{printf}(\.{"No\ more\ than\ two\ co\)mmand\ line\ arguments\ \)are\ +allowed.\\n"});$\5 +$\\{exit}(\T{1});\2$\5 +$\}\2$\par +\U 2.\fi + +\M7. The function $\\{open\_file}$ will try to open the file $\\{name}$ for +reading, and +if this fails it will prompt for another file name until it has success. If +called with $\\{name}\E\NULL$, the function starts with prompting right away. + +\Y\B\4$\X5:Functions\X\PE{}$\6 +\&{void} $\\{open\_file}$\5 +$(\1\1\1\&{FILE}~\m*\m* f,\31~\&{char}~\m*\\{prompt},\31~\&{char}~\m*\\{name}\2% +\2\2)$\6 +$\a\{\1\&{char}~\\{buf}[\T{80}];$\7 +\&{if}~$(\\{name}\E\NULL\V(\m* f\K\\{fopen}(\\{name},\31\\{read\_mode}))\E% +\NULL)\1$\6 +\&{do}\1\5 +$\{\1$\5 +$\\{printf}(\.{"\%s:\ "},\31\\{prompt});$\5 +$\\{fflush}(\\{stdout});$\5 +$\\{scanf}(\.{"\%79s"},\31\\{buf});\2$\5 +$\}$\2\6 +\&{while}~$((\m* f\K\\{fopen}(\\{buf},\31\\{read\_mode}))\E\NULL);$\2\2\6 +$\}$\par +\fi + +\N0 8. Index. +\fi + + +\inx +\@m\\{arg}, \[2], 6. +\@h\&{bool}, \[1], 5. +\@m\\{buf}, \[7]. +\@m\\{col}, \[2], 3, 4. +\@m c_1, \[2], 3, 4. +\@m c_2, \[2], 3, 4. +\@m\.{EOF}, 3, 4. +\@m\\{exit}, 6. +\@m f, \[7]. +\@m\\{fflush}, 7. +\@m\\{fopen}, 6, 7. +\@m f_1, \[2], 3, 6. +\@m f_2, \[2], 3, 6. +\@m\\{getc}, 3. +\@m\\{is\_first}, \[5]. +\@m\\{left\_margin}, \[2], 3. +\@m\\{line}, \[2], 3, 4. +\@m\\{main}, \[2]. +\@m n, \[2]. +\@m\\{name}, \[7]. +\@m\\{open\_file}, 6, \[7]. +\@m\\{printf}, 4, 5, 6, 7. +\@m\\{prompt}, \[7]. +\@m\\{read\_mode}, \[6], 7. +\@m\\{scanf}, 7. +\@m\\{stdin}, 6. +\@m\\{stdout}, 7. +\@m\\{the\_file}, 4, \[5]. +\fin +\@$\X5, 7:Functions\X$ +\U 2. +\@$\X6:Open the files $ f_1$ and~$ f_2$, taking their names from the command +line or from the terminal; in case of an error for which no recovery is +possible, call $\\{exit}(\T{1})$\X$ +\U 2. +\@$\X4:Report the outcome of the comparison\X$ +\U 2. +\@$\X3:Search for first difference, leaving $ c_1\I c_2$ if and only if a +difference was found\X$ +\U 2. +\con diff --git a/web/c_cpp/cwebx/ctangle.c b/web/c_cpp/cwebx/ctangle.c new file mode 100644 index 0000000000..2ce302a9ef --- /dev/null +++ b/web/c_cpp/cwebx/ctangle.c @@ -0,0 +1,996 @@ +#define version_string "3.04" +#define banner "This is CTANGLE (Version x" version_string ")\n" +#define max_toks 150000L +#define max_texts 2500 +#define max_files 50 +#define stack_size_max 50 +#define max_indent 1000 +#define variant text +#define line_output flags['l'] +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include "common.h" +#define max_bytes 50000L +#define max_modules 1000 +#define max_idents 5000 +#define max_sections 4000 +#define hash_size 353 +#define buf_size 100 +#define longest_name 1000 +#define long_buf_size (buf_size+longest_name) +#define local static +#define array_size(a)((int)(sizeof(a)/sizeof(a[0]))) +#define false (boolean)0 +#define true (boolean)1 +#define ctangle 0 +#define cweave 1 +#define and_and 04 +#define lt_lt 020 +#define gt_gt 021 +#define plus_plus 013 +#define minus_minus 01 +#define minus_gt 031 +#define not_eq 032 +#define lt_eq 034 +#define gt_eq 035 +#define eq_eq 036 +#define or_or 037 +#define find_char()(loc<=limit||get_line()) +#define id_index(p)((sixteen_bits)((p)-id_table)) +#define id_at(i)(&id_table[i]) +#define mod_index(p)((sixteen_bits)((p)-mod_table)) +#define mod_at(i)(&mod_table[i]) +#define name_begin(p)((p)->byte_start) +#define length(p)((int)(strlen(name_begin(p)))) +#define name_end(p)(name_begin(p)+length(p)) +#define complete_name(p)((p)->byte_start[-1]=='\0') +#define print_mod(p) \ +printf(": <%s%s>",name_begin(p),complete_name(p)?"":"...") +#define spotless 0 +#define harmless_message 1 +#define error_message 2 +#define fatal_message 3 +#define mark_harmless() \ +if(history==spotless)history=harmless_message;else +#define mark_error()(history=error_message) +#define overflow(t)fatal("\n! Sorry, %s capacity exceeded",t) +#define confusion(s)fatal("\n! This can't happen: %s",s) +#define show_banner flags['b'] +#define show_happiness flags['h'] +#define show_progress flags['p'] +#define show_stats flags['s'] +#define C_plus_plus flags['+'] +#define compatibility_mode flags['c'] +#define update_terminal()fflush(stdout) +#define new_line()putchar('\n') +#define term_write(string,leng)printf("%.*s",(int)(leng),string) +#define tok_begin(p)(p)->tok_start +#define tok_end(p)((p)+1)->tok_start +#define text_table_end (&text_table[max_texts]) +#define tok_mem_end (&tok_mem[max_toks]) +#define store_byte(c) \ +if(tok_ptr==tok_mem_end)overflow("token");else*tok_ptr++=c +#define macro_flag (text_table_end-1) +#define header_flag text_table_end +#define next_sec(m)((m)->text_link) +#define equiv equiv_or_xref +#define verb_quote 0x2 +#define join 0x3 +#define cur_repl cur_state.repl_field +#define cur_byte cur_state.byte_field +#define cur_end cur_state.end_field +#define cur_sec cur_state.sec_nr_field +#define cur_ind cur_state.indent_field +#define cur_state stack[0] +#define stack_end (&stack[stack_size_max]) +#define stack_empty()(stack_ptr== &stack[0]) +#define C_printf(format,x)fprintf(C_file,format,x) +#define C_putc(c)putc(c,C_file) +#define put_indent() \ +(indent_buffer[ind_i=cur_ind]='\0',C_printf("%s",indent_buffer)) +#define append_white(c) \ +if(ind_i>=max_indent)overflow("indent buffer"); \ +else indent_buffer[ind_i++]=isspace(c)?c:' ' +#define trans_limit 9 +#define trans_of(c)c_trans[(unsigned char)(c)-0x80] +#define translation_exists(c)(trans_of(c)[0]!='\0') +#define comp_op(op) \ +(C_printf(out_state==operator&&line_output?" %s":"%s",op) \ +,out_state=operator) +#define code_of(c)ccode[(unsigned char)(c)] +#define compress(char2,code) \ +if(*loc==char2)return++loc,code +#define preproc_directive 0 +#define section_body 1 +#define report(k,c,m) \ +printf("%lu %ss (out of %lu)\n",(unsigned long)(c),k,(unsigned long)(m)) +/*3:*//*10:*/ +#line 122 "common.inc" +boolean names_match(id_pointer,char*,int,int); +void init_id_name(id_pointer,int); +void init_module_name(mod_pointer);/*:10*//*15:*/ +#line 116 "ctangle.w" +typedef struct text +{eight_bits*tok_start; +struct text*text_link; +}text,*text_pointer;/*:15*//*30:*/ +#line 442 "ctangle.w" +typedef struct +{text_pointer repl_field; +eight_bits*byte_field; +eight_bits*end_field; +sixteen_bits sec_nr_field; +sixteen_bits indent_field; +}output_state,*stack_pointer;/*:30*//*36:*/ +#line 561 "ctangle.w" +enum{identifier=0x80,section_start,section_end,line_mark};/*:36*//*44:*/ +#line 747 "ctangle.w" +enum{no_space,num_or_id,operator,literal};/*:44*//*54:*/ +#line 936 "ctangle.w" +enum +{ignore=0x80, +id_code,constant, +verbatim, +at_sign_image, +join_code, +ord, +control_text, +include_preproc, +char_trans, +format, +definition, +header, +begin_C, +module_name, +new_section +};/*:54*//*24:*/ +#line 301 "ctangle.w" +void phase_two(void); +void output(text_pointer); +void output_preproc_directives(void); +void C_newline(void); +void out_char(eight_bits);/*:24*//*80:*/ +#line 1604 "ctangle.w" +boolean mark_line(void);/*:80*//*89:*/ +#line 1770 "ctangle.w" +void phase_one(void);/*:89*//*16:*/ +#line 136 "ctangle.w" +text text_table[max_texts]; +text_pointer text_ptr= &text_table[0]; +eight_bits tok_mem[max_toks]; +eight_bits*tok_ptr= &tok_mem[0];/*:16*//*20:*/ +#line 197 "ctangle.w" +text_pointer text_root=NULL; +text_pointer*last_unnamed= &text_root;/*:20*//*23:*/ +#line 291 "ctangle.w" +boolean atp_seen=false;/*:23*//*26:*/ +#line 343 "ctangle.w" +mod_pointer output_file[max_files]; +int output_file_count=0;/*:26*//*31:*/ +#line 466 "ctangle.w" +output_state stack[stack_size_max]; +stack_pointer stack_ptr;/*:31*//*32:*/ +#line 486 "ctangle.w" +char indent_buffer[max_indent]; + +sixteen_bits ind_i;/*:32*//*35:*/ +#line 543 "ctangle.w" +int cur_val;/*:35*//*43:*/ +#line 723 "ctangle.w" +eight_bits out_state; +boolean protect;/*:43*//*47:*/ +#line 819 "ctangle.w" +char c_trans[UCHAR_MAX+1-0x80][trans_limit+1];/*:47*//*55:*/ +#line 962 "ctangle.w" +eight_bits ccode[UCHAR_MAX+1];/*:55*//*60:*/ +#line 1100 "ctangle.w" +id_pointer cur_id; +mod_pointer cur_mod;/*:60*//*70:*/ +#line 1342 "ctangle.w" +text_pointer cur_text; +eight_bits next_control;/*:70*/ +#line 79 "ctangle.w" + + +int main(int argc,char* *argv) +{program=ctangle; +line_output=true; +common_init(argc,argv);/*17:*/ +#line 144 "ctangle.w" +tok_begin(text_ptr)=tok_ptr;/*:17*//*48:*/ +#line 827 "ctangle.w" +if(compatibility_mode) +{unsigned char c=UCHAR_MAX; +do sprintf(trans_of(c),"X%X",c);while(--c>=0x80); +}/*:48*//*56:*/ +#line 970 "ctangle.w" +{unsigned char c=0; +do ccode[c]=isspace(c)?new_section:ignore;while(c++ !=UCHAR_MAX); +ccode['v']=ccode['V']='|'; +ccode['=']=verbatim; +ccode['@']=at_sign_image; +ccode['&']=join_code; +ccode['\'']=ord; +ccode['^']=ccode['?']=ccode['.']=ccode[':']=ccode['#']= +ccode['t']=ccode['T']=ccode['q']=ccode['Q']=control_text; +ccode['p']=ccode['P']=include_preproc; +ccode['l']=ccode['L']=char_trans; +ccode['f']=ccode['F']=ccode['s']=ccode['S']=format; +ccode['d']=ccode['D']=definition; +ccode['h']=ccode['H']=header; +ccode['c']=ccode['C']=begin_C; +ccode['<']=ccode['(']=module_name; +ccode['~']=ccode['*']=new_section; +if(compatibility_mode)/*57:*/ +#line 1000 "ctangle.w" +{ccode['h']=ccode['H']=include_preproc; +ccode['p']=ccode['P']=begin_C; +ccode['#']=ignore; +}/*:57*/ +#line 989 "ctangle.w" +}/*:56*/ +#line 86 "ctangle.w" +if(show_banner)print(banner); +phase_one(); +phase_two(); +wrap_up(); +return 0; +}/*:3*//*18:*/ +#line 153 "ctangle.w" +void store_two_bytes(sixteen_bits x) +{if(tok_ptr+2>tok_mem_end)overflow("token"); +*tok_ptr++=x>>8; +*tok_ptr++=x&0xFF; +}/*:18*//*21:*/ +#line 211 "ctangle.w" +boolean names_match(id_pointer x,char*q,int l,int dummy) +{char*p=name_begin(x);while(--l>=0)if(*p++ != *q++)return false; +return*p=='\0'; +} + +void init_module_name(mod_pointer node) +{node->equiv=NULL;} + +void init_id_name(id_pointer dummy,int ilk){}/*:21*//*25:*/ +#line 314 "ctangle.w" +void phase_two(void) +{phase=2; +if(text_root==NULL&&output_file_count==0) +{print("\n! No program text was specified.");mark_harmless();} + +else +{if(show_progress) +{print("\nWriting the output file%s" +,(text_root!=NULL)+output_file_count>1?"s":""); +if(text_root!=NULL)printf(" (%s):",C_file_name); +update_terminal(); +} +if(text_root==NULL)C_file=NULL; +else +{open_output_file();cur_line=1; +if(!atp_seen)output_preproc_directives(); +output(text_root); +}/*27:*/ +#line 355 "ctangle.w" +{int i; +char output_file_name[longest_name+1]; +for(i=0;i<output_file_count;i++) +{mod_pointer output_module=output_file[i];/*28:*/ +#line 384 "ctangle.w" +{char*p=output_file_name,*q=name_begin(output_module); +while(*q!='\0') +if((*p++= *q++)=='@') +if(*q++ !='@') +{print("\n! Illegal control code in file name"); + +print_mod(output_module);err_print(""); +} +*p='\0'; +}/*:28*/ +#line 360 "ctangle.w" +if(C_file!=NULL)fclose(C_file); +if(output_module->equiv==NULL) +{print("\n! Module not present"); + +print_mod(output_module);err_print(""); +} +else if((C_file=fopen(output_file_name,"w"))==NULL) +{print("\n! Cannot open \"%s\" as output file",output_file_name); + +err_print(""); +} +else +{if(show_progress)print("\n(%s):",output_file_name); +cur_line=1; +output(output_module->equiv); +} +} +}/*:27*/ +#line 333 "ctangle.w" +print_progress("\nDone.\n"); +} +}/*:25*//*33:*/ +#line 499 "ctangle.w" +void push_level(mod_pointer p) +{if(stack_ptr==stack_end)overflow("output stack"); +*stack_ptr++=cur_state; +cur_repl=p->equiv; +cur_byte=tok_begin(cur_repl);cur_end=tok_end(cur_repl); +cur_ind=ind_i; +}/*:33*//*34:*/ +#line 516 "ctangle.w" +void continue_or_pop_level(void) +{if(cur_repl->text_link!=NULL) + +{cur_repl=next_sec(cur_repl); +cur_byte=tok_begin(cur_repl);cur_end=tok_end(cur_repl); +} +else if(--stack_ptr> &stack[0])cur_state= *stack_ptr; + +ind_i=cur_ind; +}/*:34*//*37:*/ +#line 576 "ctangle.w" +void output(text_pointer repl) +{stack_ptr= &stack[1]; +cur_repl=repl;cur_byte=tok_begin(cur_repl);cur_end=tok_end(cur_repl); +cur_ind=ind_i=0; +do +if(cur_byte==cur_end) +{cur_val=cur_sec; +continue_or_pop_level(); +out_char(section_end); +} +else/*38:*/ +#line 601 "ctangle.w" +{int a= *cur_byte++; +if(a<0x80)out_char(a); +else if(a>=0xF8) +if(a<0xFA)out_char(a==0xF8? *cur_byte++:line_mark); +else{C_newline();output_preproc_directives();} +else +{cur_val=(((a-=0x80)%0x28)<<8)+ *cur_byte++; +switch(a/0x28) +{case 0:out_char(identifier);break; +case 1:/*39:*/ +#line 623 "ctangle.w" +{mod_pointer mod_name=mod_at(cur_val); +if(mod_name->equiv!=NULL)push_level(mod_name); +else +{print("\n! Module not present");print_mod(mod_name);err_print(""); + +} +}/*:39*/ +#line 610 "ctangle.w" +break; +case 2:cur_sec=cur_val;out_char(section_start); + +} +} +}/*:38*/ +#line 589 "ctangle.w" +while(!stack_empty()); +C_newline(); +}/*:37*//*40:*/ +#line 644 "ctangle.w" +void output_preproc_directives(void) +{text_pointer repl,l; +eight_bits*p,*end; +protect=true; +for(repl= &text_table[0];repl<text_ptr;repl++) +if((l=repl->text_link)==macro_flag||l==header_flag) +{p=tok_begin(repl);end=tok_end(repl); +C_printf("#%se ",l==macro_flag?"defin":"includ"); +out_state=no_space; +while(p<end)/*41:*/ +#line 670 "ctangle.w" +{int a= *p++; +if(a<0x80)out_char(a); +else if(a>=0xF8) +if(a==0xF8)out_char(*p++); +else confusion("`@p' within macro"); +else +{cur_val=(((a-=0x80)%0x28)<<8)+ *p++; +if(a<0x28)out_char(identifier); +else confusion("module within macro"); +} +}/*:41*/ +#line 656 "ctangle.w" +C_newline(); +} +protect=false; +}/*:40*//*42:*/ +#line 692 "ctangle.w" +void C_newline(void) +{C_putc('\n'); +if(!line_output)put_indent(); +if(cur_line%100==0&&show_progress) +{if(cur_line%500!=0)print("."); +else print(cur_line%2500==0?"%u\n":"%u",cur_line); +update_terminal(); +} +++cur_line; +}/*:42*//*45:*/ +#line 764 "ctangle.w" +void out_char(eight_bits c) +{if(out_state==literal) +if(c==verb_quote)out_state=num_or_id; +else C_putc(c); +else if(isalnum(c)&&c<0x80||c=='_') + +{if(out_state==num_or_id&&line_output)C_putc(' '); +C_putc(c);out_state=num_or_id; +if(!line_output)append_white(c); +} +else switch(c) +{case verb_quote: +if(out_state==num_or_id&&line_output)C_putc(' '); +out_state=literal;break; +case join:out_state=no_space;break; +case '\n':/*46:*/ +#line 803 "ctangle.w" +{if(protect){C_putc(' ');C_putc('\\');} +C_newline(); +if(out_state!=literal)out_state=no_space; +}/*:46*/ +#line 779 "ctangle.w" +break; +case identifier:/*49:*/ +#line 838 "ctangle.w" +{char*p=name_begin(id_at(cur_val));int l=0; +if(out_state==num_or_id&&line_output)C_putc(' '); +do +if((unsigned char)(*p)<0x80){C_putc(*p);++l;} +else +{char*q=trans_of(*p);do{C_putc(*q);++l;}while(* ++q!='\0');} +while(* ++p!='\0'); +out_state=num_or_id; +if(!line_output)do append_white(' ');while(--l>0); +}/*:49*/ +#line 780 "ctangle.w" +break; +case section_start: +if(line_output)C_printf("/*%d:*/",cur_val);else C_newline(); +out_state=no_space;break; +case section_end: +if(line_output)C_printf("/*:%d*/",cur_val);else C_newline(); +out_state=no_space;break; +case line_mark:/*50:*/ +#line 854 "ctangle.w" +{sixteen_bits a; +a=(*cur_byte++)<<8;a+= *cur_byte++; +C_newline();C_printf("#line %u \"",a); +a=(*cur_byte++)<<8;a+= *cur_byte++; +C_printf("%s\"",name_begin(id_at(a))); +C_newline();out_state=no_space; +}/*:50*/ +#line 787 "ctangle.w" +break;/*51:*/ +#line 877 "ctangle.w" +case '+':case '-':case '*':case '/':case '%':case '?': +case '<':case '>':case '&':case '|': +if(out_state==operator&&line_output)C_putc(' '); +case '=':C_putc(c);out_state=operator;break;/*:51*//*52:*/ +#line 892 "ctangle.w" +case plus_plus:comp_op("++");break; +case minus_minus:comp_op("--");break; +case minus_gt:comp_op("->");break; +case gt_gt:comp_op(">>");break; +case eq_eq:comp_op("==");break; +case lt_lt:comp_op("<<");break; +case gt_eq:comp_op(">=");break; +case lt_eq:comp_op("<=");break; +case not_eq:comp_op("!=");break; +case and_and:comp_op("&&");break; +case or_or:comp_op("||");break;/*:52*/ +#line 790 "ctangle.w" +default:C_putc(c);out_state=no_space; +if(!line_output)append_white(c); +} +}/*:45*//*58:*/ +#line 1012 "ctangle.w" +eight_bits skip_ahead(void) +{eight_bits c; +while(find_char()) +{limit[1]='@'; +while(*loc++ !='@'){} +if(loc<=limit&&(c=code_of(*loc++))!=ignore)return c; +} +return new_section; +}/*:58*//*59:*/ +#line 1051 "ctangle.w" +boolean skip_comment(boolean one_liner) +{char c; +do +{if(loc>=limit) +if(one_liner)return false; +else if(get_line())return true; +else +{err_print("! Input ended in mid-comment");return false;} + +if((c= *loc++)=='/'&& *loc=='*') +err_print("! `/*' inside comment, did you forget `*/' before? "); + +if(c=='@') +{eight_bits cc=code_of(*loc++); +if(cc==new_section) +{err_print("! Section ended in mid-comment");loc-=2;return false;} + +if(cc==module_name){/*68:*/ +#line 1291 "ctangle.w" +{boolean file_module=loc[-1]=='('; + +cur_mod=get_module_name(); +if(file_module&&cur_mod!=NULL)/*29:*/ +#line 398 "ctangle.w" +{int i=0; +while(i<output_file_count) +if(output_file[i]==cur_mod)break;else++i; +if(i==output_file_count) +if(output_file_count==max_files)overflow("output files"); +else output_file[output_file_count++]=cur_mod; +}/*:29*/ +#line 1296 "ctangle.w" +}/*:68*/ +#line 1068 "ctangle.w" +continue;} +} +if(!line_output) +{store_byte(c); +if(c=='@'&&(c=loc[-1])!='@')store_byte(c); +} +}while(c!='*'|| *loc!='/'||one_liner); +++loc;if(!line_output)store_byte('/'); +return false; +}/*:59*//*61:*/ +#line 1115 "ctangle.w" +eight_bits get_next(void) +{static boolean preprocessing=false; +static boolean comment_continues=false; +eight_bits c; +restart:/*62:*/ +#line 1152 "ctangle.w" +{if(loc>=limit) +{if(preprocessing&&limit>buffer&&limit[-1]!='\\') +preprocessing=false; +return get_line()?'\n':new_section; +} +if(comment_continues +||(c= *loc++)=='/'&&(*loc=='*'||C_plus_plus&& *loc=='/'))/*63:*/ +#line 1184 "ctangle.w" +{boolean one_liner=false; +if(!comment_continues) +{if(!line_output){store_byte('/');store_byte(*loc);} +one_liner= *loc++ =='/'; + +} +else if(preprocessing) +{print("\nWarning: Multi-line comment in preprocessor line"); + +preprocessing=false;mark_harmless(); +} +if(comment_continues=skip_comment(one_liner)) + +return '\n'; +else goto restart; +}/*:63*/ +#line 1162 "ctangle.w" +if(isspace(c)) +if(line_output) +if(preprocessing)return ' ';else goto restart; + +else return c; + +if(c=='#'&&loc==buffer+1)preprocessing=true; +}/*:62*/ +#line 1123 "ctangle.w" +if(c=='L'&&(*loc=='\''|| *loc=='\"')) +{get_string();return constant;} +if(c<0x80?isalpha(c)||c=='_':translation_exists(c)) +{/*65:*/ +#line 1233 "ctangle.w" +{id_first= --loc; +do c= * ++loc; +while(c<0x80?isalnum(c)||c=='_':translation_exists(c)); +cur_id= +loc==id_first+1&&(eight_bits)(*id_first)<0x80 +?NULL:id_lookup(id_first,loc,0); +}/*:65*/ +#line 1126 "ctangle.w" +return id_code;} +if(c>=0x80){err_print("! Illegal 8-bit character");goto restart;} + +if(isdigit(c)||c=='.'&&isdigit((eight_bits)*loc)) +{/*66:*/ +#line 1250 "ctangle.w" +{if(*(id_first=loc-1)=='0'&&tolower((eight_bits)*loc)=='x') + +do c= * ++loc;while(isxdigit(c)); +else +{while(isdigit(c))c= *loc++; +if(c=='.')do c= *loc++;while(isdigit(c)); +if(tolower(c)=='e') +{if((c= *loc)=='+'||c=='-')c= * ++loc; +while(isdigit(c))c= * ++loc; +} +else--loc; +} +while(isalpha(c))c= * ++loc; + +id_loc=loc; +}/*:66*/ +#line 1130 "ctangle.w" +return constant;} +switch(c) +{case '\'':case '"':get_string();return constant; +case '@':/*67:*/ +#line 1275 "ctangle.w" +{eight_bits cc=code_of(*loc++); +switch(cc) +{case ignore:goto restart; +case control_text:get_control_text();goto restart; +case verbatim:if(get_control_text())goto restart;else break; +case ord:/*69:*/ +#line 1302 "ctangle.w" +id_first=loc; +while(*loc!='\'') +{if(*loc++ =='\\')loc++; +if(loc>=limit){err_print("! ASCII constant didn't end");break;} + +} +id_loc=loc++;/*:69*/ +#line 1280 "ctangle.w" +break; +case module_name:/*68:*/ +#line 1291 "ctangle.w" +{boolean file_module=loc[-1]=='('; + +cur_mod=get_module_name(); +if(file_module&&cur_mod!=NULL)/*29:*/ +#line 398 "ctangle.w" +{int i=0; +while(i<output_file_count) +if(output_file[i]==cur_mod)break;else++i; +if(i==output_file_count) +if(output_file_count==max_files)overflow("output files"); +else output_file[output_file_count++]=cur_mod; +}/*:29*/ +#line 1296 "ctangle.w" +}/*:68*/ +#line 1283 "ctangle.w" +} +return cc; +}/*:67*//*64:*/ +#line 1217 "ctangle.w" +case '+':compress('+',plus_plus);break; +case '-':compress('-',minus_minus);compress('>',minus_gt);break; +case '=':compress('=',eq_eq);break; +case '>':compress('=',gt_eq);compress('>',gt_gt);break; +case '<':compress('=',lt_eq);compress('<',lt_lt);break; +case '&':compress('&',and_and);break; +case '|':compress('|',or_or);break; +case '!':compress('=',not_eq);break;/*:64*/ +#line 1137 "ctangle.w" +} +return c; +}/*:61*//*71:*/ +#line 1367 "ctangle.w" +void scan_repl(eight_bits context) +{eight_bits a; +eight_bits*keep=tok_ptr; +int brace_level=0,par_level=0; +if(context==section_body)/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1373 "ctangle.w" +do/*72:*/ +#line 1388 "ctangle.w" +{switch(a=get_next()) +{/*75:*/ +#line 1459 "ctangle.w" +case id_code:/*76:*/ +#line 1496 "ctangle.w" +if(cur_id==NULL)store_byte(*id_first); +else store_two_bytes(0x8000+id_index(cur_id));/*:76*/ +#line 1460 "ctangle.w" +keep=tok_ptr;continue; +case module_name:if(context==preproc_directive)goto done; +if(cur_mod!=NULL) +{sixteen_bits n=mod_index(cur_mod);/*77:*/ +#line 1508 "ctangle.w" +{char*p=loc; +while(*p==' '&&p<limit)++p; +if(*p=='+')++p; +if(*p=='=') +err_print +("! Illegal defining occurrence of module name; did you forget `@ '?"); + +}/*:77*/ +#line 1465 "ctangle.w" +if(line_output)tok_ptr=keep; +store_two_bytes(0xA800+n);keep=tok_ptr;/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1469 "ctangle.w" + +} +continue; +case constant:case verbatim:/*78:*/ +#line 1530 "ctangle.w" +if(id_loc==id_first+1)store_byte(*id_first); +else +{store_byte(verb_quote); +do +if(*id_first==verb_quote) +{++id_first;store_byte('\\');store_byte('0'+(verb_quote>>6)); +store_byte('0'+((verb_quote>>3)&7));store_byte('0'+(verb_quote&7)); +} +else +{if((eight_bits)(*id_first)>=0x80)store_byte(0xF8); + +store_byte(*id_first++); +} +while(id_first<id_loc); +store_byte(verb_quote); +if(context==section_body&&print_where)/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1547 "ctangle.w" +}/*:78*/ +#line 1473 "ctangle.w" +keep=tok_ptr; +continue; +case ord:/*79:*/ +#line 1559 "ctangle.w" +{int c= *id_first++; +if(c=='@'&& *id_first++ !='@') +{err_print("! Double `@' should be used in ASCII constant"); + +--id_first; +} +if(c=='\\') +{c= *id_first++; +switch(c) +{case 't':c='\011';break; +case 'n':c='\012';break; +case 'b':c='\010';break; +case 'f':c='\014';break; +case 'v':c='\013';break; +case 'r':c='\015';break; +case '0':c='\0';break; +case '\\':c='\134';break; +case '\'':c='\047';break; +case '\"':c='\042';break; +default:err_print("! Unrecognised escape sequence"); + +} +} +else +{ +} +if(id_first!=id_loc) +if(id_loc>id_first) +err_print("! ASCII constant should be single character"); + +else{err_print("! Empty ASCII constant");c=0;} + +store_byte(verb_quote); +if(c>=100)store_byte('0'+c/100);if(c>=10)store_byte('0'+(c/10)%10); +store_byte('0'+c%10); +store_byte(verb_quote); +}/*:79*/ +#line 1476 "ctangle.w" +keep=tok_ptr;continue; +case include_preproc: +if(context==preproc_directive) +err_print("! `@p' is forbidden in preprocessor directive"); + +else +{if(line_output)tok_ptr=keep; +store_byte(0xFA);atp_seen=true;keep=tok_ptr;/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1485 "ctangle.w" +} +continue; +case char_trans: +err_print("! `@l' is only allowed in limbo"); + +continue;/*:75*/ +#line 1394 "ctangle.w" +case format:case definition:case header:case begin_C: +if(context==preproc_directive)goto done; +err_print +("! `@f', `@d', `@h', and `@c' are ignored in section body"); + +continue; +case new_section:goto done;/*73:*/ +#line 1420 "ctangle.w" +case '(':++par_level;break; +case ')': +if(par_level<=0) +{err_print("! Unmatched closing parenthesis");continue;} + +--par_level;break; +case '{':++brace_level;break; +case '}': +if(brace_level<=0) +{err_print("! Unmatched closing brace");continue;} +--brace_level;break;/*:73*/ +#line 1402 "ctangle.w" +case '\n':store_byte('\n'); +if(context==section_body&&print_where) +{tok_ptr=keep;/*81:*/ +#line 1612 "ctangle.w" +{if(mark_line()){a=new_section;goto done;}}/*:81*/ +#line 1406 "ctangle.w" +} +continue; +case join_code:a=join; +} +store_byte(a);keep=tok_ptr; +}/*:72*/ +#line 1375 "ctangle.w" +while(true); +done:tok_ptr=keep; +next_control=a; +if(par_level>0||brace_level>0)/*74:*/ +#line 1439 "ctangle.w" +{char*p,*s;int l; +if(par_level>0)l=par_level,s="parenthes",p=l>1?"es":"is"; +else l=brace_level,s="brace",p=l>1?"s":""; +print("\n! There %s %d unclosed %s%s" +,par_level+brace_level>1?"are":"is",l,s,p); + +if(par_level>0&&brace_level>0) +print(" and %d unclosed brace%s" +,brace_level,brace_level>1?"s":""); +print(" in the previous "); +err_print(context==preproc_directive?"macro":"section"); +while(--par_level>=0)store_byte(')'); +while(--brace_level>=0)store_byte('}'); +}/*:74*//*19:*/ +#line 166 "ctangle.w" +{cur_text=text_ptr++; +if(text_ptr>=text_table_end)overflow("text"); +tok_begin(text_ptr)=tok_ptr; +}/*:19*/ +#line 1381 "ctangle.w" +}/*:71*//*82:*/ +#line 1627 "ctangle.w" +boolean mark_line(void) +{while(loc>=limit) +if(!get_line())return true; +print_where=false; +if(line_output) +{store_byte(0xF9); +id_first=changing?change.name:cur_file_name; +store_two_bytes(changing?change_line:cur_line); +store_two_bytes(id_index(id_lookup(id_first,NULL,0))); +} +return false; +}/*:82*//*83:*/ +#line 1648 "ctangle.w" +void scan_section(void) +{++section_count; +if(loc[-1]=='*')print_section_progress();/*84:*/ +#line 1668 "ctangle.w" +{next_control=ignore; +do +{if(next_control<definition) +{if((next_control=skip_ahead())==module_name) +{loc-=2;get_next();} + +} +else if(next_control==definition) +{/*85:*/ +#line 1694 "ctangle.w" +{do next_control=get_next(); +while(line_output?next_control=='\n' +:next_control<0x80&&isspace(next_control)); +if(next_control!=id_code) +{err_print("! Macro definition ignored, must start with identifier"); + +continue; +}/*76:*/ +#line 1496 "ctangle.w" +if(cur_id==NULL)store_byte(*id_first); +else store_two_bytes(0x8000+id_index(cur_id));/*:76*/ +#line 1703 "ctangle.w" +if(isspace((eight_bits)*loc))store_byte(' '); + +}/*:85*/ +#line 1677 "ctangle.w" +scan_repl(preproc_directive); +cur_text->text_link=macro_flag; +} +else +{scan_repl(preproc_directive);cur_text->text_link=header_flag;} +if(next_control==module_name)/*86:*/ +#line 1718 "ctangle.w" +{eight_bits t=get_next(); +if(t=='+')t=get_next(); +if(t!='='&&t!=eq_eq) +{next_control=ignore; +if(t!='|'&&!compatibility_mode) +err_print("! `=' sign missing, module name ignored"); + +} +}/*:86*/ +#line 1684 "ctangle.w" +}while(next_control<begin_C); +}/*:84*//*87:*/ +#line 1733 "ctangle.w" +{mod_pointer p; +switch(next_control) +{default:return; +case begin_C:p=NULL;break; +case module_name:p=cur_mod; +} +store_two_bytes(0xD000+section_count); +scan_repl(section_body);/*88:*/ +#line 1753 "ctangle.w" +{if(p==NULL) +{*last_unnamed=cur_text;last_unnamed= &cur_text->text_link;} +else +{text_pointer*q= &p->equiv; +while(*q!=NULL)q= &(*q)->text_link; +*q=cur_text; +} +cur_text->text_link=NULL; + +}/*:88*/ +#line 1744 "ctangle.w" +}/*:87*/ +#line 1653 "ctangle.w" +}/*:83*//*90:*/ +#line 1777 "ctangle.w" +void phase_one(void) +{phase=1;section_count=0;reset_input(); +while((next_control=skip_ahead())!=new_section) +if(next_control==char_trans)/*91:*/ +#line 1791 "ctangle.w" +{int c; +while(loc<limit&&isspace((eight_bits)*loc))++loc; +if(!(isxdigit((eight_bits)loc[0])&& +isxdigit((eight_bits)loc[1])&& +isspace((eight_bits)loc[2]))) +err_print("! Two-digit hex number and space should follow `@l'"); + +else if(sscanf(loc,"%x",&c),c<0x80) +err_print("! You cannot translate characters < 0x80"); + +else +{char*p=trans_of(c);int i=0; +loc+=3; +while(find_char()&&isspace((eight_bits)*loc))++loc; +if(!input_has_ended) +while(isalnum((eight_bits)*loc)|| *loc=='_') +if(++i<=trans_limit)*p++= *loc++;else break; +if(i>0)*p='\0';/*92:*/ +#line 1824 "ctangle.w" +if(i==0)err_print("! Translation string absent after `@l'"); + +else if(i>trans_limit)err_print("! Translation string too long"); +else if(!isspace((eight_bits)*loc)) +err_print("! Translation string not terminated by space");/*:92*/ +#line 1810 "ctangle.w" +} +}/*:91*/ +#line 1781 "ctangle.w" +while(!input_has_ended)scan_section(); +check_complete(); +}/*:90*//*93:*/ +#line 1838 "ctangle.w" +#ifdef STAT +void print_stats(void) +{print("\nMemory usage statistics:\n"); +report("identifier",id_index(id_ptr),max_idents); +report("module name",mod_index(mod_ptr),max_modules); +report("byte",byte_ptr-byte_mem,max_bytes); +report("replacement text",text_ptr-text_table,max_texts); +report("token",tok_ptr-tok_mem,max_toks); +} +#endif/*:93*/ diff --git a/web/c_cpp/cwebx/ctangle.w b/web/c_cpp/cwebx/ctangle.w new file mode 100644 index 0000000000..5c0eb7a813 --- /dev/null +++ b/web/c_cpp/cwebx/ctangle.w @@ -0,0 +1,1856 @@ +% This file is part of CWEBx. +% This program by Marc van Leeuwen based on earlier versions by +% D. E. Knuth., Silvio Levy and Frank Jensen. +% It is distributed WITHOUT ANY WARRANTY, express or implied. +% CWEB (Revision: 2.0) % Don Knuth, July 1990 +% Version 3.x, Marc van Leeuwen, December 1993 +% CWEBx 2+1.0, Marc van Leeuwen, August 1994 +% CWEBx 3.0, Marc van Leeuwen, Januari 1995 +% CWEBx 3.02, Marc van Leeuwen, April 1996 +% CWEBx 3.02a, Marc van Leeuwen, September 1996 +% CWEBx 3.03, Marc van Leeuwen, January 1998 (ctangle unchanged) + +% Copyright (C) 1987,1990 Silvio Levy and Donald E. Knuth +% Copyright 1994 Marc A. A. van Leeuwen + +% Permission is granted to make and distribute verbatim copies of this +% document provided that the copyright notice and this permission notice +% are preserved on all copies. + +% Permission is granted to copy and distribute modified versions of this +% document under the conditions for verbatim copying, provided that the +% entire resulting derived work is distributed under the terms of a +% permission notice identical to this one. + +\def\me.{CTANGLE} \def\myroots{\.{TANGLE}} + +% Here is TeX material that gets inserted after \input cwebxmac +\def\ASCII.{\caps{ASCII}} + +@i intro.inc % Here is some text that matches the start of CWEAVE + +@d banner "This is CTANGLE (Version x"@+version_string@+")\n" + +@ The following parameters are specific to \.{CTANGLE}; those which are +common to \.{CTANGLE} and \.{CWEAVE} are defined in the file \.{common.inc} +and appear below. Some of these values have been decreased with respect to +their earlier values which were sufficient in the original \.{WEB} to handle +\TeX; a motivation is given at the common declarations. The macro |variant| +is used in the include file \.{common.h}, and should therefore be defined +early; it refers to the |struct text| that will be declared later. + +@d max_toks 150000L /* number of bytes in compressed \Cee~code */ +@d max_texts 2500 /* number of replacement texts, must be less than 10240 */ +@d max_files 50 /* number of auxiliary output files */ +@d stack_size_max 50 /* module nesting level during output */ +@d max_indent 1000 /* size of buffer for save indentation characters */ +@) +@d variant @;text + +@ The program is built from two compilation units, one with source file +\.{common.w}, which contains a collection of routines and data shared +between \.{CTANGLE} and \.{CWEAVE}, and a second with source file +\.{ctangle.w} containing all code specific to \.{\me.}, and whose +typeset version you are now reading. All compilation units of the \.{CWEB} +system incorporate the file \.{common.inc} containing common declarations. + +\.{\me.} 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 optionally be compiled with the preprocessor symbol |STAT| defined, +in which case it will keep track of how much of \.{\me.}'s resources +were actually used. One command line argument is specifically designated to +modify the behaviour of \.{\me.}. Normally \.{\me.} will produce +machine-oriented output that supplies \&{\#line} directives (to allow +compilers and debuggers to locate the original source lines of statements), +while preserving line breaks but otherwise ignoring the lay-out and comments +in the source text. When the flag `\.{-l}' is supplied on the command line +however, the output cannot be traced back easily to the source lines, but +will be easier for humans to read: no \&{\#line} directives are produced, +but original lay-out and comments are preserved as well as possible. The +flag controlling this behaviour is called |line_output|; in the standard +mode of operation we have |line_output==true|. + +@d line_output flags['l'] +@c +@< Function prototypes used but not defined in the shared code @>@; +@< Typedef and enumeration declarations @>@; +@< Prototypes @>@; +@< Global variables @>@; + +int main (int argc, char** argv) +{ program=ctangle; + line_output=true; + common_init(argc,argv); + @<Set initial values@> + if (show_banner) print(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(); /* and exit gracefully */ + return 0; +} + +@i common.inc + + +@* Data structures exclusive to {\tt \me.}. +The basic mission of \.{\me.} is a relatively simple one, namely to +separate the \Cee~code from the commentary, to forget about the latter, and +to output the former almost verbatim, but plugging in the program text +corresponding to a module each time a reference is made. To this end the +bodies of all the sections are collected during Phase~I, and the proper +links are laid, after which the code is linearised for output in a +straightforward manner during Phase~II. + +The \Cee~code itself will be stored as a sequence of bytes (more precisely, +of |eight_bits|) in a single large array |tok_mem|, much in the same way as +the characters for identifiers and module names are stored in |byte_mem|; +the encoding used will be described below. The individual sections are +represented by structures of type |text|, which contain pointers to the +|tok_mem| array as well as links by which they may be connected into lists; +the same structures are also used to represent the replacement texts of +macros. All |text| structures reside in an array |text_table|, which is +comparable to |id_table| and |mod_table|. + +@<Typedef...@>= +typedef struct text +{ eight_bits* tok_start; /* pointer into |tok_mem| */ + struct text* text_link; /* relates replacement texts */ +} text,* text_pointer; + +@ Since all allocation is during Phase~I and all deallocation during +Phase~II, there is no reason for reclaiming memory when it is no longer +needed. Hence allocation is strictly linear, and a single pointer into +|tok_mem| for each |text| suffices, since a texts ends where the next one +starts. The first position of |tok_mem| that is unoccupied by a replacement +text is called |tok_ptr|, and the first unused location of |text_table| is +called |text_ptr|. Whenever we are not in the process of adding tokens to +|tok_mem|, we have the identity |tok_begin(text_ptr)==tok_ptr|. + +@d tok_begin(p) (p)->tok_start +@d tok_end(p) ((p)+1)->tok_start +@d text_table_end (&text_table[max_texts]) +@d tok_mem_end (&tok_mem[max_toks]) + +@<Glob...@>= +text text_table[max_texts]; +text_pointer text_ptr=&text_table[0]; /* first unused position in |text_table| */ +eight_bits tok_mem[max_toks]; +eight_bits *tok_ptr=&tok_mem[0]; /* first unused position in |tok_mem| */ + +@ Invariants must be initialised. + +@<Set init...@>= +tok_begin(text_ptr)=tok_ptr; + +@ The following macro and function are used to enter one- and two-byte +tokens into |tok_mem| when a replacement text is being generated. + +@d store_byte(c) @+ +if (tok_ptr==tok_mem_end) overflow("token"); @+ else *tok_ptr++=c@; + +@c +void store_two_bytes (sixteen_bits x) +{ if (tok_ptr+2>tok_mem_end) overflow("token"); + *tok_ptr++ = x >> 8; /* store high byte */ + *tok_ptr++ = x & 0xFF; /* store low byte */ +} + +@ When by successive calls of |store_byte| and |store_two_bytes| a complete +replacement text has been stored, the following code makes it into an +official text object by advancing |text_ptr| and storing the starting +location of the next text to be generated into the |text_table| array. + +@< Wrap up the accumulated bytes into a completed text, + pointed to by |cur_text| @>= +{ cur_text=text_ptr++; /* consolidate the replacement text */ + if (text_ptr>=text_table_end) overflow("text"); + tok_begin(text_ptr)=tok_ptr; /* mark end of replacement text */ +} + +@ The |text_link| field links a section to a possible continuation of it (the +next section with the same name, or for unnamed sections the next unnamed +section). The set of all sections linked together in such a way will be termed +a `module'. Apart from the value |NULL| used to indicate the end of such a +list, we need two other distinguished values: |macro_flag|, which indicates +that the text is a macro replacement text rather than a section body, and +|header_flag| similarly indicating that the text is the (quoted) file name +of a header file that is included. By a fortunate coincidence, there are two +unused pointer values near the end of |text_table|: since the macro |tok_end| +should work for all pointers to a replacement text, the last entry of +|text_table| is never used to store a replacement text; its address can be +used as one reserved pointer value, and the definition of~\Cee\ generously +offers us the address of the non-existent entry following it as another +valid pointer value. The pointer to the first section of the unnamed module +appears in |text_root|, and the address of the final link of this module is +recorded in |last_unnamed|. The named modules are accessed by the extra +pointer in the |mod_info| structure for a module name, which is a +|text_pointer| pointing to the body of the first section of that module; +in~\.{\me.} this field will be called |equiv|. + +@d macro_flag (text_table_end-1) /* address of unused entry */ +@d header_flag text_table_end /* address just beyond |text_table| */ +@d next_sec(m) ((m)->text_link) /* next section of the same module, if any */ +@d equiv equiv_or_xref /* info corresponding to names */ + +@<Glob...@>= +text_pointer text_root=NULL; +text_pointer* last_unnamed=&text_root; /* where to link new unnamed sections */ + +@ Here are the functions specific to \.{\me.}, which are used by the +common lookup routines. The function |names_match| decides whether a name of +length~|l| starting at position |q| equals the identifier represented +by~|x|. The parameter |dummy| is present only for compatibility with +\.{CWEAVE}, where it contains the |ilk| code. The common lookup routines +refer to separate routines |init_module_name| and |init_id_name| when the +data structure grows. Actually |init_id_name| is called only when +|program==cweave|, but we need to declare a dummy version so that the linker +won't complain about its absence. + +@c +boolean names_match (id_pointer x,char* q,int l,int dummy) +{@; char* p=name_begin(x); while (--l>=0) if (*p++!=*q++) return false; + return *p=='\0'; +} + +void init_module_name(mod_pointer node) +@+{@; node->equiv=NULL; } + +void init_id_name (id_pointer dummy,int ilk) @+ {} + + +@* Tokens. +Clearly, \.{\me.} must hold all the code for a complete program file at +the end of Phase~I (not counting the possible multiple use of some modules, +which will probably not make a great difference), and so it is worth while +to use a compact representation for the internal storage of the \Cee~code. +Using full-fledged data compression techniques would probably be overdoing +things a bit, but a few simple methods will make a great difference. There +are likely to be many identical occurrences of identifiers and keywords of +the language, so it is efficient to collect them into a table and replace +them in the internal format by references to the table; module names have +to be entered in a table anyway for the purpose of associating occurrences +of the same name with each other. Furthermore white space and comments can +be removed upon storing the texts; any necessary white space can be +reinserted during the output process (but note that newlines must be +represented internally, in order to be able to match output lines with +input lines). + +@ These compressed texts representing \Cee~code, while physically +represented as a sequence of eight-bit bytes in |tok_mem|, logically consist +of streams of `tokens', some of which occupy two or more consecutive byte +positions, while others take just one byte. If the first byte is |a| and, +in case |a>=0x80|, the next byte is~|b|, then the interpretation of the +token is as follows. + +\Y +\item{$\bullet$} |0<=a<0x80|: the token represents the character~|a|; +\item{$\bullet$} |0x80<=a<0xA8|: the two-byte token represents the + identifier with name |id_at((a-0x80)*@t$2^8$@>+b)|; +\item{$\bullet$} |0xA8<=a<0xD0|: the two-byte token represents the module + with name |mod_at((a-0xA8)*@t$2^8$@>+b)|; +\item{$\bullet$} |0xD0<=a<0xF8|: this two-byte token marks the beginning of + (a part of) the repacement text for the current module, defined in section + number |(a-0xD0)*@t$2^8$@>+b|; +\item{$\bullet$} |a==0xF8|: the two-byte token represents the (8-bit) + character~|b|. +\item{$\bullet$} |a==0xF9|: the token consists of 5 bytes, say |a|,~|b|, + |c|, |d| and~|e|, and represents a \&{\#line} directive for line number + |b*@t$2^8$@>+c| in the file |id_at(d*@t$2^8$@>+e)|. +\item{$\bullet$} |a==0xFA|: the token consists of a single byte, and + represents the location, indicated by the \:p control codes in the source + file, where the preprocessor directives produced by \:d and \:h will be + included in the output; this location will be marked by the presence of + the virtual module called `|@p|' in the typeset output. +\Y + +Any \Cee~token that consists of a single 7-bit character is represented by +that character itself; 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 codes will not be present however, since they are +control codes and they do not represent a symbol useful for \Cee\ in the +\caps{MIT} extension of \ASCII., 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 \.{CWEB}). + +\hang |verb_quote| denotes the beginning or end of a sequence of bytes that + is to be copied verbatim to the output, i.e., no spaces should be inserted. + Such sequences are used to transmit numerical constants and strings (in + which case the ordinary string quotes appear within the |verb_quote| + bytes), and to implement the \:= operation. + +@^ASCII code dependencies@> + +@d verb_quote 0x2 /* takes the place of extended \ASCII. \.{\char2} */ +@d join 0x3 /* takes the place of extended \ASCII. \.{\char3} */ + +@< Global var... @>= +boolean atp_seen=false; /* whether \:p occurs anywhere */ + + +@* Phase II processing. +We will start to explain the structure of the tokens lists built up in +memory by considering how they are output during Phase~II; this is the +simpler part of the program. We will proceed in top-down fashion for this +part. + +@<Prototypes@>= +void phase_two (void); /* output the contents of the compressed tables */ +void output (text_pointer); /* recursively write out modules */ +void output_preproc_directives(void); /* write out all \:d and \:h stuff */ +void C_newline (void); /* send a newline to the \Cee~file */ +void out_char (eight_bits); /* write a single token */ + +@ Here is the general control routine. After Phase~I, the quantity +|output_file_count| will be equal to the number of extra output files to be +written. There should at least be either an unnamed section or one that +produces an additional output file, for otherwise there will be no output at +all; if the is no unnamed section, \:d~and~\:h commands will only have +effect if \:p occurs somewhere. + +@c void phase_two (void) +{ phase=2; + if (text_root==NULL && output_file_count==0) + @/{@; print("\n! No program text was specified."); mark_harmless(); } + @.No program text...@> + else + { if (show_progress) + { print("\nWriting the output file%s" @.Writing the output...@> + ,(text_root!=NULL)+output_file_count>1 ? "s" : ""); + if (text_root!=NULL) printf(" (%s):",C_file_name); + update_terminal(); + } + if (text_root==NULL) C_file=NULL; + else + { open_output_file(); cur_line=1; + if (!atp_seen) output_preproc_directives(); + output(text_root); + } + @<Write all the named output files@> + print_progress("\nDone.\n"); + } +} + +@ \.{\me.} can write output on multiple files. If a module name is +introduced in at least one place by \:( instead of \:<, it is treated as the +name of a file. All these special module names are saved in the array +|output_files|. + +@<Glob...@>= +mod_pointer output_file[max_files]; +int output_file_count=0; + +@ To write the named output files, we proceed as for the unnamed module, the +only subtlety is that we have to open each one after closing the previous +one. However, if there is no main file, there is no file to close the first +time, and this situation can be recognised because we have set |C_file=NULL| +in that case. The length of a module name can be at most |longest_name| +(which is rather more generous than allowed elsewhere for file names), so we +need no test for overflowing |output_file_name|. + +@<Write all the named output files@>= +{ int i; + char output_file_name[longest_name+1]; /* name of the file */ + for (i=0; i<output_file_count; i++) + { mod_pointer output_module=output_file[i]; + @< Copy name of |output_module| into |output_file_name| @> + if (C_file!=NULL) fclose(C_file); + if (output_module->equiv==NULL) + @/{@; print("\n! Module not present"); + @.Module not present@> + print_mod(output_module); err_print(""); + } + else if ((C_file=fopen(output_file_name,"w"))==NULL) + @/{@; print("\n! Cannot open \"%s\" as output file",output_file_name); + @.Cannot open output file@> + err_print(""); + } + else + { if (show_progress) print("\n(%s):",output_file_name); + cur_line=1; + output(output_module->equiv); + } + } +} + +@ If the module name defining the output file contains any occurrences of +`\.{@@@@}', they are undoubled in forming the actual file name; apart from +this should be no control codes in the module name. + +@< Copy name of |output_module| into |output_file_name| @>= +{ char* p=output_file_name, *q=name_begin(output_module); + while (*q!='\0') + if ((*p++=*q++)=='@@') + if (*q++!='@@') + { print("\n! Illegal control code in file name"); + @.Illegal control code in file name@> + print_mod(output_module); err_print(""); + } + *p='\0'; +} + +@ Here is how a file name gets added to the list of output files. + +@<Adjoin |cur_mod| to the set of output files@>= +{ int i=0; + while(i<output_file_count) + if (output_file[i]==cur_mod) break; @+ else ++i; + if (i==output_file_count) /* if not present, add |cur_mod| */ + if (output_file_count==max_files) overflow ("output files"); + else output_file[output_file_count++]=cur_mod; +} + + +@* Writing replacement texts. +We now come to the functions |output| and |output_preproc_directives|; the +task of the former is a recursive one, and it may also call the latter if +\:p occurs. The function |output| is not recursive, however, but it uses a +stack to keep track of what is going on at different ``levels'' as the +sections are being written out. Entries on this stack have five parts: + +\yskip + +\hang |repl_field| points to replacement text of the active section; + +\hang |byte_field| is the |tok_mem| location from which the next token + on a particular level will be read; + +\hang |end_field| is the |tok_mem| location where the replacement + text of a particular level will end; + +\hang |sec_nr_field| is the section number. + +\hang |indent_field| determines the amount of indentation, for in case + |line_output==false| + +\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_byte|, |cur_end|, |cur_repl|, |cur_sec| and +|cur_ind|. + +@d cur_repl cur_state.repl_field +@d cur_byte cur_state.byte_field +@d cur_end cur_state.end_field +@d cur_sec cur_state.sec_nr_field +@d cur_ind cur_state.indent_field + +@<Typedef...@>= +typedef struct +{ text_pointer repl_field; /* replacement text of active section */ + eight_bits *byte_field; /* present location within replacement text */ + eight_bits *end_field; /* ending location of replacement text */ + sixteen_bits sec_nr_field; /* section number */ + sixteen_bits indent_field; /* amount of indentation */ +} output_state, * stack_pointer; + +@ The global variable |stack_ptr| tells how many levels of output are +currently in progress; the stack grows upwards with |stack_ptr| pointing to +the first vacant location above the top of the stack. The entry |stack[0]| +at the bottom of the stack is not used; when |stack_ptr| points to it then +|cur_state| itself has become invalid and the output process is completed; +this condition can be tested as |stack_empty()|. This somewhat strange use of +the stack is forced upon us because the \Cee\ language does not guarantee +that a pointer value at offset~$-1$ of an allocated array exists. Since the +location |stack[0]| is vacant we might as well use it to store |cur_state| +in. + +@d cur_state stack[0] +@d stack_end (&stack[stack_size_max]) +@d stack_empty() (stack_ptr==&stack[0]) + +@<Global...@>= +output_state stack[stack_size_max]; /* info for non-current levels */ +stack_pointer stack_ptr; /* points above top of output state stack */ + +@ When |line_output==false| we keep track of the current indentation level +in an array of white space characters |indent_buffer|. Every time a newline +is output, a sequence of |cur_ind| characters from |indent_buffer| are also +output by invoking |put_indent|. When relevant non-newline characters are +output, a white space counterpart of the character is appended to +|indent_buffer| by invoking the macro |append_white|, whose argument should +not involve any side effects. The current position of writing the characters +is maintained in |ind_i|, and reset to |cur_ind| on output of a newline. + +@d C_printf(format,x) fprintf(C_file,format,x) +@d C_putc(c) putc(c,C_file) +@d put_indent() + (indent_buffer[ind_i=cur_ind]='\0',C_printf("%s",indent_buffer)) +@d append_white(c) + if (ind_i>=max_indent) overflow("indent buffer"); + else indent_buffer[ind_i++]= isspace(c) ? c : ' ' @; +@< Global var... @>= +char indent_buffer[max_indent]; + /* white space characters to produce indentation */ +sixteen_bits ind_i; + /* number of those characters needed to reach current position */ + +@ When the replacement text for name |p| is to be inserted into the output, +the following function is called to save the old level of output and get +the new one going. The value of |cur_sec| is not set since its value is +recorded in the first token to be read after this function is called. It is +by calling |push_level| that the current value of |ind_i| becomes +significant, by being copied to |cur_ind|. + +@c +void push_level (mod_pointer p) /* suspends the current level */ +{ if (stack_ptr==stack_end) overflow("output stack"); + *stack_ptr++=cur_state; +@/cur_repl=p->equiv; + cur_byte=tok_begin(cur_repl); cur_end=tok_end(cur_repl); +@/cur_ind=ind_i; +} + +@ When we come to the end of a replacement text, the function +|continue_or_pop_level| does what its name suggests: it either moves to the +continuation of this replacement text (i.e., to the next section of the same +module) or returns the state to the most recently stacked level when a +module is completed. In the latter case all the fields of |cur_state| are +restored; in the former case, like for |push_level|, the value of |cur_sec| +is not yet set, but will be adjusted immediately afterwards. + +@c +void continue_or_pop_level (void) +{ if (cur_repl->text_link != NULL) + /* then link to a continuation, staying on the same level */ + {@; cur_repl=next_sec(cur_repl); + cur_byte=tok_begin(cur_repl); cur_end=tok_end(cur_repl); + } + else if (--stack_ptr > &stack[0]) cur_state=*stack_ptr; + /* otherwise |stack_empty()| holds */ + ind_i=cur_ind; /* reset indentation to base of current level */ +} + +@ The function |output| handles output of tokens by sending them to a lower +level function |out_char|, until the condition |stack_empty()| holds. The +task of |output| is to isolate and decode tokens, to handle stacking and +unstacking as necessary, and to issue special calls to |out_char| to mark +the beginning and end of replacements texts and to produce appropriate +\&{\#line} directives. As its name suggests, |out_char| usually handles the +output of single characters, but some special codes will make it perform a +few other operations. Some of these, like |verb_quote| and compressed +operator codes occur, as bytes with values below |0x80| inside the +replacements texts, and these get no special treatment by |output|. A few +other ones are generated explicitly by |output|, and these have values from +|0x80| upwards, defined in the enumeration below. Information about the +precise value of two-byte tokens is passed to |out_char| via the global +variable |cur_val|. + +@<Global...@>= +int cur_val; /* additional information corresponding to output token */ + +@~The code |identifier| is used for identifiers of length two or more, in +which case |cur_val| indicates the identifier name. The codes +|section_start| or |section_end| are sent at the beginning and end of parts +of the replacement text defined in a one section, in which case |cur_val| is +the section number; this number will be recorded in a comment to aid human +readers of the \Cee~file produced. When a \&{\#line} directive is to be +output, |line_mark| is sent to |out_char|, which will fetch the following +4~bytes (specifying the line number and file name) from the replacement text +itself; this is the only occasion where |output| does not read a complete +token before calling |out_char|. Any 8-bit characters that were escaped by a +byte `|0xF8|' are sent directly to |out_char|; they do not interfere with +the codes below since the input routine guarantees that they only occur +between |verb_quote| tokens, at which times |out_char| does not honour any +special codes. + +@< Typedef and enumeration... @>= +enum {identifier=0x80, section_start, section_end, line_mark }; + +@ Due to the output stack operations, the function |output| can be written +iteratively rather than recursively. A user who is not afraid of recursion +may however easily rewrite this code into a recursive form, and dispose of +the output stack and its routines altogether; the recursion depth will be +equal to the level of nesting of modules, which is not likely to cause any +problems in practice. We have retained the iterative version out of +reverence for the author of the original |WEB| system; it has the +additional advantage that, should there be a cycle in the directed graph of +module references, a reasonable overflow message will be produced by +|push_level|, whereas otherwise system stack overflow would occur, and it +would depend on the \Cee~runtime support whether it is properly detected. + +@c +void output (text_pointer repl) /* sends tokens to |out_char| */ +{ stack_ptr=&stack[1]; +@/cur_repl=repl; cur_byte=tok_begin(cur_repl); cur_end=tok_end(cur_repl); + cur_ind=ind_i=0; + do + if (cur_byte==cur_end) + { cur_val=cur_sec; + continue_or_pop_level(); + out_char(section_end); /* output ending section number comment */ + } + else + @< Output the token starting at |*cur_byte|, advancing |cur_byte| + correspondingly @> + while(!stack_empty()); + C_newline(); +} + +@ Most tokens lead to calling |out_char| with an appropriate value, after +possibly setting |cur_val| and |cur_sec|. Exceptions are module names, which +will push their replacement text on the stack but not produce any direct +output, and tokens |0xFA| which will produce the collected preprocessor +directives by calling |output_preproc_directives|, after ensuring that this +will start on a fresh line of output. + +@< Output the token starting at |*cur_byte|... @>= +{ int a = *cur_byte++; + if (a<0x80) out_char(a); /* single byte token */ + else if (a>=0xF8) + if (a<0xFA) out_char(a==0xF8 ? *cur_byte++ : line_mark); + else {@; C_newline(); output_preproc_directives(); } /* |a==0xFA| */ + else + { cur_val=(((a-=0x80)%0x28)<<8)+*cur_byte++; + switch (a/0x28) + { case 0: out_char(identifier); break; + case 1: @<Expand module name |cur_val| @> @+ break; + case 2: cur_sec=cur_val; out_char(section_start); + /* set the correct section number and output comment */ + } + } +} + +@ If any defining occurrence of a module name was encountered during the +Phase~I, its replacement text will have been be linked into to |equiv| field +of that name; otherwise that field will contain a null pointer and we must +report an error. + +@<Expand module name...@>= +{ mod_pointer mod_name=mod_at(cur_val); + if (mod_name->equiv!=NULL) push_level(mod_name); + else + {@; print("\n! Module not present"); print_mod(mod_name); err_print(""); + @.Module not present@> + } +} + +@ Output of macro definitions and inclusions of header files is sufficiently +different from ordinary output that we use a special function +|output_preproc_directives| for it. We bypass |output| and call |out_char| +directly, since in these cases there can be no nesting of replacement texts. +We go through the list of all replacement texts and copy the ones that refer +to macros or header files, preceded respectively by \&{\#define} and +\&{\#include}. During the output of a single directive any line breaks +present in the source file will be protected by backslashes. We use local +variables |p| and |end| instead of |cur_byte| and |cur_end|, since the +current level of the stack is already in use if this function is called from +|output|. + +@c +void output_preproc_directives (void) +{ text_pointer repl,l; + eight_bits* p, *end; + protect=true; /* newlines should be preceded by |'\\'| */ + for (repl=&text_table[0]; repl<text_ptr; repl++) + if ((l=repl->text_link)==macro_flag || l==header_flag) + { p=tok_begin(repl); end=tok_end(repl); + C_printf ("#%se ",l==macro_flag ? "defin" : "includ"); + out_state=no_space; + while (p<end) + @< Output preprocessor token starting at |p|, advancing |p| + correspondingly @> + C_newline(); /* this newline is not escaped */ + } + protect=false; +} + +@ The situation here is simpler than in |output|, since module names and \:p +control codes are forbidden in the replacement texts handled here, nor will +codes for \.{\#line} directives or section number indications have been +included in them during input. Should any such token nevertheless turn up, +then one of the two calls of |confusion| below is invoked; the diagnostic is +not quite exhaustive as to the possible cause in either case, but we do not +wish to elaborate on code that will never be executed. + +@< Output preprocessor token... @>= +{ int a=*p++; + if (a<0x80) out_char(a); /* single byte token */ + else if (a>=0xF8) + if (a==0xF8) out_char(*p++); @+ + else confusion("`@@p' within macro"); @.`@@p' within macro@> + else + { cur_val=(((a-=0x80)%0x28)<<8)+*p++; + if (a<0x28) out_char(identifier); @+ + else confusion("module within macro"); @.module within macro@> + } +} + + +@* Writing characters. The |output| routine above handles the global +structure of output generation; we now present the routines that transform +the lexical items produced by |output| into characters. +First we give a function that is called whenever we want to finish off a +line sent to the \Cee~file. +It keeps |cur_line| equal to the number of the next line to be output, +and displays a progress report every 100 lines. + +@c +void C_newline (void) /* writes one line to output file */ +{ C_putc('\n'); + if (!line_output) put_indent(); + if (cur_line%100==0 && show_progress) + { if (cur_line%500!=0) print("."); /* progress report */ + else print(cur_line%2500==0 ? "%u\n" : "%u",cur_line); + update_terminal(); + } + ++cur_line; +} + +@ The function |out_char| must make sure that the output has the proper +``surface structure''. When |line_output==false| everything should +essentially be written as it is stored, but when |line_output==true| only +the essential tokens have been stored, and white space has to be inserted +where appropriate. Spaces should not occur at certain 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), while in other places +they are required (e.g., between identifiers, which for \.{\me.} includes +reserved words, and between certain operators that might otherwise be +considered as a single token). Such surface structure can very nicely be +obtained by attaching a small finite state machine to the output generator, +recording information about the most recent token (as a matter of fact, even +\TeX's fabulous formatting of math formulae is largely based on such a simple +device). In our present case, the state of the output process is recorded in +the global variable |out_state|. Furthermore there is a variable |protect| +that is explicitly managed by the function calling |out_char| (notably by +|output_preproc_directives|), being set to |true| whenever newlines are to +be escaped by a backslash. + +@<Global...@>= +eight_bits out_state; /* current status of partial output */ +boolean protect; /* should newline characters be quoted? */ + +@ The output process can be in one of following states: + +\yskip\hang |no_space| means that no space will precede the following item. +This state is set by \:\&, and after the output of anything that could not +possibly combine into a larger token, such as punctuation. + +\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 |operator| means that the last item in the buffer is an operator, +hence a blank space or line break must be inserted if the next item is also +an operator. + +\yskip\hang |literal| 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; the |verb_quote| +character serves as a quoting mechanism which switches this state +on~and~off. + +@< Typedef and enum... @>= +enum { no_space, num_or_id, operator, literal}; + +@ The function |out_char| is a many-way switch on the kind of character or +token transmitted to it. The |verb_quote| mechanism is used amongst others +to encapsulate numeric constants, so we make the whole construction behave +as an identifier with respect to spacing (the user can still defeat any +spaces around it by means of \:\&). The code |line_mark| will only be stored +(and output) if |line_output==true|. We do not anticipate the need of +indentation across literals, operators and such, and assume that module +names are preceded on the same line only by white space, identifiers +(keywords) and things like braces; apart from identifiers all of these come +through the |default| case below. Therefore we only include an invocation of +|append_white| in that case and for identifiers; to handle all possible +cases the assiduous user could add such invocations to all other calls of +|C_putc| and |C_printf|. + +@c +void out_char (eight_bits c) +{ if (out_state==literal) + if (c==verb_quote) out_state=num_or_id; /* close literal mode */ + else C_putc(c); /* write literal character, possibly 8-bit */ + else if (isalnum(c)&&c<0x80 || c=='_') + /* single character identifier or number */ + { if (out_state==num_or_id && line_output) C_putc(' '); + C_putc(c); out_state=num_or_id; + if (!line_output) append_white(c); + } + else switch (c) + { case verb_quote: + if (out_state==num_or_id && line_output) C_putc(' '); + out_state=literal; break; + case join: out_state=no_space; break; + case '\n': @< Output a newline @> @+ break; + case identifier: @< Output an identifier @> @+ break; + case section_start: + if (line_output) C_printf("/*%d:*/",cur_val); @+ else C_newline(); + out_state=no_space; break; + case section_end: + if (line_output) C_printf("/*:%d*/",cur_val); @+ else C_newline(); + out_state=no_space; break; + case line_mark: @< Output a \.{\#line} directive @> @+ break; + @\@<Cases of operator symbols@> + @\@<Cases of compressed operators@> + default: C_putc(c); out_state=no_space; + if (!line_output) append_white(c); + } +} + +@ Newlines are escaped whenever |protect| holds; a space is prepended so +that the newline is effectively replaced by a space. This also means that +the user who cannot break the habit of escaping newlines in multi-line +preprocessor directives, even though this is unnecessary after \:d and \:h, +is not punished for this by the creation of the sequence `\.{\\\\}' at the +end of the lines, but rather gets `\.{\\\ \\}', which is harmless. + +@< Output a newline @>= +{ if (protect) {@; C_putc(' '); C_putc('\\'); } + C_newline(); + if (out_state!=literal) out_state=no_space; +} + +@ Apart from alphanumeric characters and underscores, we allow identifiers +to contain characters in the range |0x80<=c<=UCHAR_MAX|, if a translation +into a string of legal characters is known to \.{\me.}; such translations +can be specified by \:l directives in limbo. These translations are stored +in an array |c_trans|. + +@d trans_limit 9 /* maximal length of a translation string */ +@d trans_of(c) c_trans[(unsigned char)(c)-0x80] +@d translation_exists(c) (trans_of(c)[0]!='\0') /* non-empty translation */ + +@< Global var... @>= +char c_trans[UCHAR_MAX+1-0x80][trans_limit+1]; + +@~In compatibility mode, default translations exist of the form `\.X\\{NN}', +where \\{NN} is the (two digit) upper case hexadecimal representation of the +character in question. Otherwise the static initialisation will have made all +translation strings empty. + +@< Set init... @>= +if (compatibility_mode) +@/{@; unsigned char c=UCHAR_MAX; + do sprintf(trans_of(c),"X%X",c); while (--c>=0x80); +} + +@ In case of identifiers the name indexed by |cur_val| is written out. The +translation of characters in the range from |0x80| upwards is performed +here, rather than on input of such characters, since this is slightly more +convenient. The check that |translations_exists| was done on input, however. + +@< Output an identifier @>= +{ char* p=name_begin(id_at(cur_val)); int l=0; + if (out_state==num_or_id && line_output) C_putc(' '); + do + if ((unsigned char)(*p)<0x80) {@; C_putc(*p);++l; } + else + {@; char* q=trans_of(*p); do {@; C_putc(*q); ++l; } while (*++q!='\0'); } + while (*++p!='\0'); + out_state=num_or_id; + if (!line_output) do append_white(' '); while (--l>0); +} + +@ As indicated above, a call |out_char(line_mark)| causes four further bytes +to be fetched directly from the token memory, increasing |cur_byte| so that +|output| will resume at the correct byte. + +@< Output a \.{\#line} directive @>= +{ sixteen_bits a; + a=(*cur_byte++)<<8; a+=*cur_byte++; /* get the line number */ + C_newline(); C_printf("#line %u \"",a); @/ + a=(*cur_byte++)<<8; a+=*cur_byte++; /* get the file name index */ + C_printf("%s\"",name_begin(id_at(a))); + C_newline(); out_state=no_space; +} + +@ In some cases when two operator symbols are adjacent, a space is required +in between to avoid interpretation as a compound symbol. The language +definition is not explicit about when such a space is mandatory, but +examples are the somewhat unusual constructions | x /@, *p |, | a++ + b != a ++ ++b| and |sum = a - -b|; certain backward \Cee~compilers even think |x=-x| +is ambiguous without a space before the minus sign. Rather than trying to +detect precisely the problematic cases we take the conservative approach of +always putting in a space, unless the second operator is~`\.='; this +exception is made because we store operators like `\.{+=}' as two separate +tokens, but they form a single token according to the \caps{ANSI/ISO} +standard. Note that other multi-character operators like `\.{\&\&}' stay +intact because they will be compressed on input, and therefore do not involve +the present section. + +@<Cases of operator symbols@>= +case '+': case '-': case '*': case '/': case '%': case'?': +case '<': case '>': case '&': case '|': + if (out_state==operator && line_output) C_putc(' '); /* fall through */ +case '=': C_putc(c); out_state=operator; break; + +@ Compilers don't mind doing repetitive work, so we use a macro to produce +several almost identical cases. Had the cases been much longer or more +numerous however, then combining the cases and using an array of strings +would have been preferable. + +@d comp_op(op) + (C_printf(out_state==operator && line_output ? " %s" : "%s",op) + ,out_state=operator) + +@<Cases of compressed operators@>= +case plus_plus: comp_op("++"); break; +case minus_minus: comp_op("--"); break; +case minus_gt: comp_op("->"); break; +case gt_gt: comp_op(">>"); break; +case eq_eq: comp_op("=="); break; +case lt_lt: comp_op("<<"); break; +case gt_eq: comp_op(">="); break; +case lt_eq: comp_op("<="); break; +case not_eq: comp_op("!="); break; +case and_and: comp_op("&&"); break; +case or_or: comp_op("||"); break; + + +@* Introduction to the phase I. We have now seen that \.{\me.} 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 (but of course, as any programmer knows, +input requires a bit more work than the corresponding output). To preserve +the symmetry we shall proceed in a bottom-up fashion in presenting this part +of the program. + +The basic character input routines are defined in the code shared with +\.{CWEAVE}. At the next higher level 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 \.{CWEB} +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 \.{CWEB} begin with `\.{@@}', and the next character +identifies the code. Some of these are of interest only to \.{CWEAVE}, so +\.{\me.} ignores them; the others are converted by \.{\me.} into +internal code numbers. The code numbers have been chosen such that they are +distinct from ordinary characters (so that a function can return either a +character or a control code), and their ordering is such as to simplify the +program logic; larger numbers are given to the control codes that denote +more significant milestones. This order is only relevant starting from +|format| however. The code \:> is treated as ignored because it should not +occur in places where we are not searching for it. The following enumeration +lists the non-character values that may be returned from~|get_next|; +therefore the two values |id_code| and |constant| are included although they +do not belong to any control code. + +@<Typedef and enum...@>= +enum @/ +{ ignore=0x80, /* control code of no interest to \.{\me.}, or \:> */ + id_code, constant, /* token codes but not control codes */ + verbatim, /* control code for \:= */ + at_sign_image, /* control code for \:@@ */ + join_code, /* control code for \:\& */ + ord, /* control code for \:' */ + control_text, /* control code for \:t, \:\^, etc. */ + include_preproc, /* control code for \:p */ + char_trans, /* control code for \:l */ + format, /* control code for \:f */ + definition, /* control code for \:d */ + header, /* control code for \:h */ + begin_C, /* control code for \:c */ + module_name, /* control code for \:< or \:( */ + new_section /* control code for \:\ , \:\~, or \:* */ +}; + +@ The conversion from the character following `\.@@' to the corresponding +numeric code is performed by means of the table~|ccode|. Since we don't know +whether characters are signed or not, we always access |ccode| via the macro +|code_of|. + +@d code_of(c) ccode[(unsigned char)(c)] + +@<Global...@>= +eight_bits ccode[UCHAR_MAX+1]; /* meaning of a char following `\.@@' */ + +@~Here we initialise |ccode|. The code code for~\:v is the only case where a +value stored in the |ccode| table is an ordinary character; this is done so +that it may be used in the \Cee~part of a section to stand for~`|@v|', even +though there is no need for using it there. + +@<Set ini...@>= +{ unsigned char c=0; + do ccode[c] = isspace (c) ? new_section : ignore; while(c++!=UCHAR_MAX); +@/ccode['v']=ccode['V']='|'; +@/ccode['=']=verbatim; +@/ccode['@@']=at_sign_image; +@/ccode['&']=join_code; +@/ccode['\'']=ord; +@/ccode['^']=ccode['?']=ccode['.']=ccode[':']=ccode['#']= + ccode['t']=ccode['T']=ccode['q']=ccode['Q']=control_text; +@/ccode['p']=ccode['P']=include_preproc; +@/ccode['l']=ccode['L']=char_trans; +@/ccode['f']=ccode['F']=ccode['s']=ccode['S']=format; +@/ccode['d']=ccode['D']=definition; +@/ccode['h']=ccode['H']=header; +@/ccode['c']=ccode['C']=begin_C; +@/ccode['<']=ccode['(']=module_name; +@/ccode['~']=ccode['*']=new_section; +if (compatibility_mode) + @< Reset some control codes to match Levy/Knuth \.{CWEB} @> +} + +@ In \.{CWEBx} there are a few control codes that also exist in \LKC. but +have a different meaning. In compatibility mode we reassign the meaning of +these codes to that of \LKC., making their usual function inaccessible, +since it is not intended that hybrid programs should be written using the +codes of \LKC. together with features particular to \.{CWEBx}. For +\.{\me.} the change of meaning of \:: (which is used instead of \:? in +\LKC.) has no effect since it introduces a control text anyway. + +@< Reset some control codes... @>= +{ ccode['h']=ccode['H']=include_preproc; /* \:h means \:p */ + ccode['p']=ccode['P']=begin_C; /* \:p means \:c */ + ccode['#']=ignore; /* \:\# means \:) */ +} + +@ The function |skip_ahead| reads through the input at fairly high speed +until finding the next non-ignorable control code, which it returns. It is +used to skip over \TeX~text, and text in limbo. It uses the fact that +|get_line| places a~|' '| at~|*limit|, so that placing the sentinel cannot +inadvertently create a token \:@@. + +@c +eight_bits skip_ahead (void) /* skip to next control code */ +{ eight_bits c; /* control code found */ + while (find_char()) + { limit[1]='@@'; /* place a sentinel */ + while (*loc++!='@@') {} + if (loc<=limit && (c=code_of(*loc++))!=ignore) return c; + } + return new_section; +} + +@ The function |skip_comment| reads through the input until finding the +end-comment token `\.{*/}' or a newline, or in case of a \Cpp\ one-line +comment (starting with `$/\!/$') just until finding a newline. The |one_liner| +parameter tells whether the latter applies, and a boolean result is returned +telling whether a newline in the middle of a comment was scanned (which can +only happen if |one_liner==false|). Returning such a newline is necessary so +that the each newline in the \Cee\ part of a section may be copied to the +output; otherwise the \&{\#line} commands inserted into the \Cee\ file by +the output routines become useless. When |line_output==false|, all characters +encountered are also stored, so that they can be output along with the +\Cee~code. + +The function gives an error message if it encounters a sequence `\.{/*}', +since this almost certainly means the user has forgotten to close the +comment. In this way we avoid the most pernicious consequence of \Cee's rule +that nested comments are forbidden, namely that such an omission often leads +to a syntactically correct, but unintended program. This means that one +cannot write either `\.{/*}' or `\.{*/}' in a comment; this difficulty can +usually be circumvented by inserting something like `\.{\{\}}' between the +two characters. + +As a safety measure, |skip_comment| comes to an end if it runs into the next +section, and prints an error message; it is slightly non-trivial that this +test does not interfere with searching for the end of the comment. If a +module name occurs in a comment (presumably in `\pb'), it is scanned by the +normal routines that will be given later: this could be the only place where +the name is spelled out in full, and/or using \:(. + +@c +boolean skip_comment (boolean one_liner) /* skips over comments */ +{ char c; /* current character */ + do + { if (loc>=limit) + if (one_liner) return false; + else if (get_line ()) return true; + else + {@; err_print("! Input ended in mid-comment"); return false; } + @.Input ended in mid-comment@> + if ((c=*loc++)=='/' && *loc=='*') + err_print("! `/*' inside comment, did you forget `*/' before? "); + @.`/*' inside comment...@> + if (c=='@@') + { eight_bits cc=code_of(*loc++); + if (cc==new_section) /* watch out for \:\ , \:\~, and \:* */ + @/{@; err_print("! Section ended in mid-comment"); loc-=2; return false; } + @.Section ended in mid-comment@> + if (cc==module_name) @/{@; @< Scan the module name... @> continue; } + } + if (!line_output) + {@; store_byte(c); + if (c=='@@' && (c=loc[-1])!='@@') store_byte(c); + } + } while (c!='*' || *loc!='/' || one_liner); + ++loc; @+ if (!line_output) store_byte('/'); + return false; +} + + +@* Getting the next token. +We now come to the function |get_next|, which does more extensive scanning. +It will move the input pointer over a single token of input, and if that +token consists of a single character that is not an identifier or a number, +it will return that character; certain two-character operators are also +returned as a single character representing the operator. In the case of +longer tokens, it will return one of the codes of the enumeration given +earlier, after possibly setting some global variables in such a way that the +complete token can be retrieved. For numeric constants |id_first| and +|id_loc| will be made to point to the beginning and end of the token +within~|buffer|. For string constants, which need not reside in a single +line of input, the characters are copied into the separate buffer |mod_text| +and this is where |id_first| and |id_loc| will then point. Identifiers and +module names are even looked up, and |cur_id| or |cur_mod| is made to point +to the name found. In the case of single-character identifiers or bad module +names, which are not entered into the name tables, |cur_id| respectively +|cur_mod| is set to~|NULL|; in the former case |id_first| is made to point +to the character that forms the identifier. + +@<Global...@>= +id_pointer cur_id; /* identifier just scanned */ +mod_pointer cur_mod; /* module name just scanned */ + +@ As one might expect, |get_next| consists mostly of a big switch that +branches to the various special cases that can arise. The static flag +|preprocessing| is raised while we are scanning an explicit preprocessor +directive (i.e., not one introduced by \:d or \:h). The static flag +|comment_continues| is raised when |get_next| must return a newline while +scanning a comment, so that it will remember what it was doing the next time +it is called. Although we allow 8-bit character input, any characters +|c>=0x80| should be contained in a comment or a character or string constant, +or in case |translation_exists(c)| an identifier, since they cannot +otherwise be part of a valid \Cee~symbol. + +@c +eight_bits get_next (void) /* produces the next input token */ +{ static boolean preprocessing=false; /* did this line start with `\.\#'? */ + static boolean comment_continues=false; /* were we scanning a comment? */ + eight_bits c; /* the current character */ +restart: + @< Handle cases involving newlines and comments; + if a token is found |return| it, or |goto restart| if it is ignored; + otherwise read the next character into |c| @> + if (c=='L' && (*loc=='\'' || *loc=='\"')) + {@; get_string(); return constant; } + if (c<0x80 ? isalpha(c) || c=='_' : translation_exists(c)) + @/{@; @< Get an identifier @> return id_code; } + if (c>=0x80) {@; err_print("! Illegal 8-bit character"); goto restart; } + @.Illegal 8-bit character@> + if (isdigit(c) || c=='.' && isdigit((eight_bits)*loc)) + @/{@; @< Get a numeric constant @> return constant; } + switch(c) + { case '\'': case '"': get_string(); return constant; + case '@@': @< Get a control code and possibly a complete module name, + and either |return| it, + or |goto restart| if control code is |ignore| @> + @\@< In applicable cases |return| compressed two-symbol operator @> + } + return c; +} + +@ The flag |preprocessing| raised when the first character of a line +is~`\.\#', and is lowered at a non-escaped newline; while it is raised spaces +are not ignored. The test whether a newline is escaped is not entirely +sound, since a final `\.{\\}' might be part of a \:\\ control code or of a +\Cpp\ one-line comment (after `$/\!/$'); a completely correct check would be +more difficult, and leaving |preprocessing| raised a bit too long does no +real harm. Contrary to |preprocessing|, the flag |comment_continues| will +always be given a new value on the first call to |get_next| after it is set, +and therefore can remain set only if |skip_comment| finds another newline. + +@< Handle cases involving newlines... @>= +{ if (loc>=limit) + { if (preprocessing && limit>buffer && limit[-1]!='\\') + preprocessing=false; + return get_line() ? '\n' : new_section; + } + if (comment_continues + ||(c=*loc++)=='/' && (*loc=='*' || C_plus_plus && *loc=='/')) + @< Scan to the end of the comment or of the current line, + setting |comment_continues| to indicate which case applies; + in the former case |goto restart|, in the latter |return '\n'| @> + if (isspace(c)) + if (line_output) + if (preprocessing) return ' '; @+ else goto restart; + /* ignore other spaces */ + else return c; + /* when |line_output==false| preserve white space faithfully */ + if (c=='#' && loc==buffer+1) preprocessing=true; +} + +@ When we come to this code |loc| either points to the beginning of a +multi-line comment or to the second character of `\.{//}' or `\.{/*}'; in +the latter case we must advance |loc| before looking for `\.{*/}', lest +`\.{/*/}' would be scanned as a complete comment. Another subtlety arises +when both |preprocessing| and |comment_continues| are raised, which means +that a preprocessor line has ended inside a comment; although in \Cee\ this +means that the preprocessor line continues, \.{\me.} will terminate it +because the comment is removed and a non-escaped newline remains. The +warning message below can be ignored by the user unless something follows +the comment, but the proper way to resolve this is to start the comment on +the line after the preprocessor line, which avoids all confusion. + +@< Scan to the end of the comment... @>= +{ boolean one_liner=false; + if (!comment_continues) + { if (!line_output) {@; store_byte('/'); store_byte(*loc); } + one_liner=*loc++=='/'; + /*/ record kind of comment, and advance to comment proper {/}*/ + } + else if (preprocessing) + { print("\nWarning: Multi-line comment in preprocessor line"); + @.Multi-line comment...@> + preprocessing=false; mark_harmless(); + } + if (comment_continues=skip_comment(one_liner)) + /* scan to end of comment or newline */ + return '\n'; /* comment contains a newline; |get_line| has been called */ + else goto restart; /* comment complete, get next token */ +} + +@ The following code assigns values to the combinations `\.{++}', `\.{--}', +`\.{->}', `\.{>=}', `\.{<=}', `\.{==}', `\.{<<}', `\.{>>}', `\.{!=}', +`\.{\v\v}' and `\.{\&\&}'. The compound assignment operators (e.g., `\.{+=}') +are separate tokens, according to {\sl The C Reference Manual\/} (so there may +be white space and even comments in between), but according to the +\caps{ANSI/ISO} standard they are single tokens. We have not allocated +single-byte codes for them, so we scan them as two separate symbols; these +will be adjacent on output. Therefore, as far as \.{\me.} is concerned, one +may write a separation between the two characters even if the \Cee-compiler +does not accept this; however, this practice should still be avoided because +it will confuse the parser of |CWEAVE|, leading to ill-formatted output. + +@d compress(char2,code) @+ + if (*loc==char2) return ++loc,code @; + +@< In applicable cases... @>= +case '+': compress('+',plus_plus); break; +case '-': compress('-',minus_minus); compress('>',minus_gt); break; +case '=': compress('=',eq_eq); break; +case '>': compress('=',gt_eq); compress('>',gt_gt); break; +case '<': compress('=',lt_eq); compress('<',lt_lt); break; +case '&': compress('&',and_and); break; +case '|': compress('|',or_or); break; +case '!': compress('=',not_eq); break; + +@ Identifiers are looked up unless they consist of a single character +|c<0x80|, in which case that character can be found as |*id_first|; there is +no need to set |id_loc| in either case. In case any 8-bit characters appear in +identifiers, they are only stored in the name table, and cannot interfere with +any of the special codes we use. + +@< Get an identifier @>= +{ id_first=--loc; /* mark beginning of identifier */ + do c=*++loc; + while (c<0x80 ? isalnum(c) || c=='_' : translation_exists(c)); + cur_id= + loc==id_first+1 && (eight_bits)(*id_first)<0x80 + ? NULL : id_lookup(id_first,loc,0); +} + +@ Scanning numeric constants is straightforward. The only subtle point is +that we must not call functions like |isdigit| with an argument of type +|char|, like |*loc|, since that may fail for 8-bits characters if |char| is +a signed type; therefore we copy characters into |c|, which has type +|unsigned char|, before applying such functions. We don't mind \.{8}'s and +\.{9}'s appearing in octal constants (although the \Cee~compiler will), so +there is no need here to distinguish between octal and decimal constants. + +@< Get a numeric constant @>= +{ if (*(id_first=loc-1)=='0' && tolower((eight_bits)*loc)=='x') + /* hex constant */ + do c=*++loc; while (isxdigit(c)); + else /* octal, decimal or float constant */ + { while (isdigit(c)) c=*loc++; + if (c=='.') @+ do c=*loc++; while (isdigit(c)); + if (tolower(c)=='e') /* floating point constant with exponent */ + { if ((c=*loc)=='+' || c=='-') c=*++loc; + while (isdigit(c)) c=*++loc; + } + else --loc; /* back up to first character after constant */ + } + while (isalpha(c)) c=*++loc; + /* incorporate any `\.{U}', `\.{L}', or `\.{F}' suffixes */ + id_loc=loc; +} + +@ After an `\.@@' sign has been scanned, the next character tells us whether +there is more work to do. Although control codes like \:\^ and \:t are +ignored by \.{\me.}, their control text has to be skipped, which is +performed by |get_control_text|. Verbatim constructions also use +|get_control_text|, but then return the |verbatim| control code, unless their +control text was empty, in which case they are ignored. + +@< Get a control code... @>= +{ eight_bits cc=code_of(*loc++); + switch(cc) + { case ignore: goto restart; + case control_text: get_control_text(); goto restart; + case verbatim: if (get_control_text()) goto restart; @+ else break; + case ord: @<Scan an \ASCII. constant@> @+ break; + case module_name: + @<Scan the module name and make |cur_mod| point to it@> + } + return cc; +} + +@ Before we scan a module name, we record whether it started with \:(, so +that we may record it afterwards as an auxiliary file name if necessary. + +@<Scan the module name...@>= +{ boolean file_module= loc[-1]=='('; + /* does this module define an output file? */ + cur_mod=get_module_name(); + if (file_module && cur_mod!=NULL) + @<Adjoin |cur_mod| to the set of output files@> +} + +@ There is no reason why we should allow a newline within an \ASCII. +constant, even if it is escaped. + +@<Scan an \ASCII. constant@>= +id_first=loc; /* first character after opening quote */ +while (*loc!='\'') +{ if (*loc++=='\\') loc++; /* accept any character following backslash */ + if (loc>=limit) {@; err_print("! ASCII constant didn't end"); break; } + @.ASCII constant didn't end@> +} +id_loc=loc++; /* move past closing quote */ + + +@* Scanning a piece of \Cee\ text. +Having acquired the skills of skipping over text and isolating tokens that +really matter, we are now ready to tackle complete pieces of \Cee~text. The +rules for generating the replacement texts for preprocessor directives +following \:d and \:h and for bodies of sections are almost identical; the +only differences are that + +\yskip \item{a)} + Module names are not possible in preprocessor directives; indeed, the + appearance of a module name terminates such directives and starts the + \Cee~part of the section. + +\item{b)} + The codes \:d, \:h, \:f, and \:c are not allowed in a \Cee~part, while + they terminate preprocessor directives. + +\item{c)} + The code \:p is not allowed inside preprocessor directives. +\yskip + +\noindent Therefore there is a single function |scan_repl| with a parameter +|context| indicating which of the two kinds of replacement texts is being +scanned; its value is either |preproc_directive| or |section_body|. 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 preproc_directive 0 +@d section_body 1 + +@<Global...@>= +text_pointer cur_text; /* replacement text formed by |scan_repl| */ +eight_bits next_control; /* control code which has already been scanned */ + +@ We will try to reduce storage requirements by discarding any trailing +newlines both after preprocessor directives and section bodies. This does not +affect the line sequencing indicated by \&{\#line} directives in either case, +since in the former case such directives are not issued in the first place, +and in the latter case a new \&{\#line} directive will be issued before any +further code is added. Note that we cannot reliably recognise newlines in a +right-to-left motion when the end of a replacement text is reached, since +there is a mixture of one-byte, two-byte and five-byte tokens. Therefore, +instead of tracing backwards, we mark the position of the last non-discardable +token every time one is appended, and back up to the last position recorded +when reaching the end of a replacement text. Given this mechanism, it is easy +to discard other things than newlines, such as \&{\#line} directives that +apply to no lines at all, and to back up at other occasions where the line +sequencing is interrupted as well, like before applied module names. + +This is also a good place to add a simple but useful check, namely that in +each section and macro replacement text the parentheses and braces should be +matched (failure in this respect can lead to syntax errors that are very +hard to locate in the \.{CWEB} source, since the real problem may be buried +inside a module name or macro). + +@c +void scan_repl (eight_bits context) /* creates a replacement text */ +{ eight_bits a; /* the current token */ + eight_bits* keep=tok_ptr; /* non-discardable stuff up to this point */ + int brace_level=0, par_level=0; + if (context==section_body) + @< Insert the line number into |tok_mem| @> + do @< Scan and store a token, setting |keep=tok_ptr| afterwards unless it + is discardable; when the replacement text has ended |goto done| @> + while (true); +done: tok_ptr=keep; /* backup over trailing newlines and \&{\#line} marks */ + next_control=a; /* this control code must be reconsidered */ + if (par_level>0 || brace_level>0) @< Report unmatched opening symbols @> + @< Wrap up the accumulated bytes into a completed text, + pointed to by |cur_text| @> +} + +@ When the function |scan_repl| is called we have either just scanned \:h or +the identifier following \:d or a defining occurrence of a module name (with +the following `\.='); in all cases we start by calling |get_next|. + +@< Scan and store a token... @>= +{ switch (a=get_next()) + { + @\@< Cases where |a| is a special token (|id_code|, + |module_name|, etc.): either store the corresponding bytes and + |continue|, or |goto done| if |a| signals the end of this + replacement text@> + case format: case definition: case header: case begin_C: + if (context==preproc_directive) goto done; + err_print + ("! `@@f', `@@d', `@@h', and `@@c' are ignored in section body"); + @.`@@f', `@@d', ... are ignored...@> + continue; + case new_section: goto done; + @\@< Cases that keep track of |par_level| and |brace_level| @> + case '\n': store_byte('\n'); + if (context==section_body && print_where) /* input file was switched */ + { tok_ptr=keep; /* back up over discardable items */ + @< Insert the line number into |tok_mem| @> + } + continue; + case join_code: a=join; + } + store_byte(a); keep=tok_ptr; /* mark as non-discardable */ +} + +@ We test matching parentheses and braces separately because it is easier, +and in the unlikely case that a mismatch should go undetected the compiler +will surely report an error that is easy to locate. All parentheses and +braces that are returned from |get_next| are real ones, i.e., not part of a +string or comment. + +@< Cases that keep track of |par_level| and |brace_level| @>= +case '(': ++par_level; break; +case ')': + if (par_level<=0) + {@; err_print("! Unmatched closing parenthesis"); continue; } + @.Unmatched closing...@> + --par_level; break; +case '{': ++brace_level; break; +case '}': + if (brace_level<=0) + {@; err_print("! Unmatched closing brace"); continue; } + --brace_level; break; + +@ The most difficult part of matching parentheses and braces is reporting any +errors at the end of the macro or section in proper English. We supply the +missing closing symbols so that there is a slightly larger chance that the +produced \Cee~code will compile. Note that there is no need to update |keep| +here, since no backing up will follow. + +@< Report unmatched opening symbols @>= +{ char *p, *s; int l; + if (par_level>0) l=par_level, s="parenthes", p= l>1 ? "es" : "is"; + else l=brace_level, s="brace", p=l>1 ? "s" : ""; + print("\n! There %s %d unclosed %s%s" + ,par_level+brace_level>1 ? "are" : "is", l, s, p ); + @.There are unclosed...@> + if (par_level>0 && brace_level>0) + print(" and %d unclosed brace%s" + , brace_level, brace_level>1 ? "s" : ""); + print(" in the previous "); + err_print(context==preproc_directive ? "macro" : "section"); + while (--par_level>=0) store_byte(')'); + while (--brace_level>=0) store_byte('}'); +} + +@ When a module reference is inserted the line sequencing is interrupted, +so we must restore it afterwards; on the other hand newlines before the +insertion can be discarded. + +@< Cases where |a| is...@>= +case id_code: + @< Store the identifier just scanned @> @+ keep=tok_ptr; continue; +case module_name: if (context==preproc_directive) goto done; + if (cur_mod!=NULL) /* don't record bad module name */ + { sixteen_bits n = mod_index(cur_mod); /* index of module name */ + @< If this looks like a defining occurrence, report a runaway section @> + if (line_output) tok_ptr=keep; /* back up */ + store_two_bytes(0xA800+n); keep=tok_ptr; + /* store reference to module name */ + @< Insert the line number into |tok_mem| @> + /* to get in phase after module insertion */ + } + continue; +case constant: case verbatim: + @/@< Copy a constant or verbatim construction @> @+ keep=tok_ptr; + continue; +case ord: + @<Translate an \ASCII. constant@> @+ keep=tok_ptr; continue; +case include_preproc: + if (context==preproc_directive) + err_print("! `@@p' is forbidden in preprocessor directive"); + @.`@@p' is forbidden...@> + else + { if (line_output) tok_ptr=keep; + store_byte(0xFA); atp_seen=true; keep=tok_ptr; + @< Insert the line number... @> + } + continue; +case char_trans: + err_print("! `@@l' is only allowed in limbo"); + @.`@@l' is only allowed in limbo@> + continue; + +@ Identifiers of length~$1$ have no name pointer, but |id_first| still +points at the relevant character. + +@< Store the identifier... @>= +if (cur_id==NULL) store_byte(*id_first); /* single-character identifier */ +else store_two_bytes(0x8000+id_index(cur_id)); + +@ Here we look to see whether a module name is followed by~`\.{=}' +or~`\.{+=}' (whether another `\.{=}' follows is irrelevant at this point), +in which case we assume that introduces the \Cee-part of a new section, of +which we somehow missed the opening \:\ . Note that this need not limit the +use of module names standing for single expressions, even if followed by the +`\.{=}' or the `\.{+=}' operator, since such names should be followed by +`\.{@@;@@;}' for proper treatment by \.{CWEAVE}. + +@< If this looks like a defining occurrence, report... @>= +{ char *p=loc; + while (*p==' ' && p<limit) ++p; + if (*p=='+') ++p; /* an optional `\.+' is allowed */ + if (*p=='=') + err_print + ("! Illegal defining occurrence of module name; did you forget `@@ '?"); + @.Illegal defining occurrence...@> +} + +@ Constants of length one can only be single digit numbers, so they can +be stored in a single (unquoted) byte like single letter identifiers. + +Inside strings (and verbatim constructions) {\it any\/} character may in +principle show up, including the character |verb_quote| used to delimit the +verbatim storage of the string. Even if this is hardly a sign of good +programming, the effect on output would be so disastrous that some counter +measure is called for: we replace |verb_quote| by its three-digit octal +escape code, which the user should have written to begin with. Because a \:i +line or a change file match can occur inside a string, we must test +|print_where| at the end of a string. + +@< Copy a constant... @>= +if (id_loc==id_first+1) store_byte(*id_first); /* single digit number */ +else +{ store_byte(verb_quote); /* opening */ + do + if (*id_first==verb_quote) + { ++id_first; store_byte('\\'); store_byte('0'+(verb_quote>>6)); + store_byte('0'+((verb_quote>>3)&7)); store_byte('0'+(verb_quote&7)); + } + else + { if ((eight_bits)(*id_first)>=0x80) store_byte(0xF8); + /* quote 8-bit characters */ + store_byte(*id_first++); + } + while (id_first<id_loc); + store_byte(verb_quote); /* closing */ + if (context==section_body && print_where) + @< Insert the line number into |tok_mem| @> +} + +@ This section should be rewritten on machines that don't use \ASCII. +code internally. It is probably best to use a table indexed by characters +and containing \ASCII. codes for this purpose (like the |xord| array in +\TeX). Incidentally, it would be tempting to write statements like +`|case'\\': c=@'\\';|' here, which would in principle be correct in any +other place, but which would cause circularity in a bootstrapped system like +this. +@^ASCII code dependencies@> + +@< Translate an \ASCII. constant @>= +{ int c=*id_first++; + if (c=='@@' && *id_first++ !='@@') + @/{@; err_print("! Double `@@' should be used in ASCII constant"); + @.Double `@@' should be used...@> + --id_first; + } + if (c=='\\') + { c=*id_first++; + switch (c) + { case 't':c='\011';break; + case 'n':c='\012';break; + case 'b':c='\010';break; + case 'f':c='\014';break; + case 'v':c='\013';break; + case 'r':c='\015';break; + case '0':c='\0';break; + case '\\':c='\134';break; + case '\'':c='\047'; break; + case '\"':c='\042'; break; + default: err_print("! Unrecognised escape sequence"); + @.Unrecognised escape sequence@> + } + } + else + { /* at this point |c| should be converted to its \ASCII. code number */ + @+} + if (id_first!=id_loc) + if (id_loc>id_first) + err_print("! ASCII constant should be single character"); + @.ASCII constant should be...@> + else {@; err_print("! Empty ASCII constant"); c=0; } + @.Empty ASCII constant@> + store_byte(verb_quote); + if (c>=100) store_byte('0'+c/100); @+ if (c>=10) store_byte('0'+(c/10)%10); + store_byte('0'+c%10); @/ + store_byte(verb_quote); +} + +@ Line number marks, which will lead to \&{\#line} directives on output, are +inserted in several places, so we shall call a function to do the necessary +work. For reasons explained below this function scans ahead a bit through +the input, and in doing so it may hit the end of the input; when this +happens it must be reported back to |scan_repl|. Therefore |mark_line| +returns a boolean value which tells whether |input_has_ended|. + +@< Prototypes @>= boolean mark_line(void); + +@~When |mark_line| returns |true|, we should terminate |scan_repl| with +|next_control==new_section|; this is achieved by jumping to |done|, where +some exitialisations are done, and where |next_control| is assigned from the +variable~|a|. + +@< Insert the line... @>= +{@; if (mark_line()) {@; a=new_section; goto done; } } + +@~If |line_output==true|, the following items are stored for recording the +line number: first a byte equal to |0xF9|, then the numeric line number, +and finally the |id_index| of the file name, which is looked up as if it were +an identifier (except that we use the feature of |id_lookup| to accept +null-terminated strings as first argument if the second argument is~|NULL|). +Note that |keep| is not increased; if nothing essential follows this line +number indication then it need not be recorded. We improve compactness of +storage and output a bit by discarding any newlines that are immediately +ahead; the inserted line number will automatically be increased properly. +While reading we might find there is no input left, in which case no line +number is recorded and |true| is returned. + +@c +boolean mark_line(void) +{ while (loc>=limit) + @+ if (!get_line()) return true; /* skip over newlines */ + print_where=false; /* request is being serviced */ + if (line_output) + { store_byte(0xF9);@/ + id_first=changing ? change.name : cur_file_name; + store_two_bytes(changing ? change_line : cur_line); + store_two_bytes(id_index(id_lookup(id_first,NULL,0))); + } + return false; +} + + +@* Scanning a section. +The function |scan_section| starts when \:\ , \:\~, or \:* has been sensed +in the input, and it proceeds until the end of that section. It uses +|section_count| to keep track of the current section number; hopefully +\.{CWEAVE} and \.{\me.} will both assign the same numbers to sections. + +@c +void scan_section (void) +{ ++section_count; + if (loc[-1]=='*') print_section_progress(); + @<Scan the \TeX\ and definition parts of the current section@> + @<Scan the \Cee\ part of the current section@> +} + +@ For \.{\me.} there is no real difference between the \TeX\ and +definition parts of a section; after all, the latter can contain \&{format} +commands that are ignored just like \TeX~text is. So we set up a loop that +stops when |next_control>=begin_C|, and handles the cases |definition| and +|header|, while ignoring anything else. The price we pay for our haste in +skipping the \TeX~part of a section (and \:f commands) is that we may have +to back up after finding \:< in order to scan the whole module name and set +|cur_mod|, which requires calling~|get_next|. In case \:< follows a +|definition| or |header| code, it is scanned by |scan_repl|, and no backing +up is required; therefore the code for backing up and rescanning cannot be +moved outside the loop. + +@<Scan the \TeX\ and definition part...@>= +{ next_control=ignore; /* clear |new_section| */ + do + { if (next_control<definition) + { if ((next_control=skip_ahead())==module_name) + @/{@; loc -= 2; get_next(); } + /* back up and scan the module name itself */ + } + else if (next_control==definition) + { @< Scan and store the identifier after \:d, if not found |continue| @> + scan_repl(preproc_directive); + cur_text->text_link=macro_flag; /* characterise as macro */ + } + else /* |next_control==header| */ + {@; scan_repl(preproc_directive); cur_text->text_link=header_flag; } + if (next_control==module_name) + @< If this is not a defining occurrence, set |next_control=ignore| @> + } while (next_control<begin_C); +} + +@ We retain the strange lexical rule of the \Cee\ preprocessor that a space +after the identifier defined as a macro is quite significant, especially +when followed by `\.{(}'. Together with the spaces in preprocessor lines +contained in a \Cee~part, these are the only kind of unquoted spaces that +are ever stored as tokens, when |line_output| holds. + +@< Scan and store the id... @>= +{ do next_control=get_next(); /* allow white space before identifier */ + while (line_output ? next_control=='\n' + : next_control<0x80 && isspace(next_control)); + if (next_control!=id_code) + @/{@; err_print("! Macro definition ignored, must start with identifier"); + @.Macro definition ignored...@> + continue; + } + @< Store the id... @> + if (isspace((eight_bits)*loc)) store_byte(' '); + /* separate the replacement text from a parameterless macro */ +} + +@ Due to the fact that module names are allowed enclosed in `\pb', it is not +certain that the occurrence of a module name signals the start of the +\Cee~part of the section. The decisive factor will be what follows the +module name: if this is `\.=' or `\.{==}', possibly preceded by `\.+', then +this is really the name of the module defined in this section. If not, then +we require that the next token is a `\.\v' (the one closing `\pb'), unless +we are in compatibility mode, in which case anything goes. We use the fact +that scanning the `\.+' and `\.=' symbols will not destroy the value in +|cur_mod|. + +@< If this is not a defining occurrence, set |next_control=ignore| @>= +{ eight_bits t=get_next(); + if (t=='+') t=get_next(); /* skip an optional `\.+' */ + if (t!='=' && t!=eq_eq) /* then apparently a cited occurrence, ignore it */ + { next_control=ignore; + if (t!='|' && !compatibility_mode) + err_print("! `=' sign missing, module name ignored"); + @.`=' sign missing, module name ignored@> + } +} + +@ If we come to this module with |next_control==module_name|, then the code +of the previous section has just removed the `\.=' or `\.{==}' and optional +`\.+' following the module name. + +@<Scan the \Cee...@>= +{ mod_pointer p; /* module name for the current section */ + switch (next_control) + { default: return; /* no \Cee\ part present */ + case begin_C: p=NULL; break; /* section contributing to unnamed module */ + case module_name: p=cur_mod; /* section contributing to named module */ + } + store_two_bytes(0xD000+section_count); /* record section number */ + scan_repl(section_body); + /* now |cur_text| points to the replacement text */ + @< Link the section body at |cur_text| to the module named |p|, + or to the unnamed module if |p==NULL| @> +} + +@ Sections whose body starts with a bad name (e.g., an ambiguous prefix) will +be treated as if they were unnamed, so the user had better not ignore the +error message. Unnamed sections get a slightly special treatment, which makes +accumulating many of them a bit more efficient than building up a named module +that is defined in many sections. + +@<Link the section...@>= +{ if (p == NULL) /* unnamed section, or bad module name */ + {@; *last_unnamed = cur_text; last_unnamed = &cur_text->text_link; } + else + { text_pointer* q=&p->equiv; /* text for the current module */ + while(*q!=NULL) q=&(*q)->text_link; /* find end of list */ + *q=cur_text; /* add section to module */ + } + cur_text->text_link=NULL; + /* end list, also marking replacement text as a non-macro */ +} + + +@* Phase I processing. +Finally we can wrap everything up and define the global structure +of~Phase~I. + +@<Proto...@>= +void phase_one (void); + /* read all the user's text and compress it into |tok_mem| */ + +@~The only thing left to do is picking up character translations in the limbo +part. + +@c +void phase_one (void) +{ phase=1; section_count=0; reset_input(); + while ((next_control=skip_ahead())!=new_section) + if (next_control==char_trans) @< Store a character translation @> + while (!input_has_ended) scan_section(); /* load all sections */ + check_complete(); /* verify that change file hasn't got out of sync */ +} + +@ The code \:l should be followed (after optional space) by a two-digit hex +number~|c| with |c>=0x80|, white space, and a translation string of at most +|trans_limit| characters, either alphanumeric or underscores, terminated by +another space. + +@< Store a character translation @>= +{ int c; + while (loc<limit && isspace((eight_bits)*loc)) ++loc; /* skip space */ + if (!(isxdigit((eight_bits)loc[0])&& + isxdigit((eight_bits)loc[1])&& + isspace ((eight_bits)loc[2]))) + err_print("! Two-digit hex number and space should follow `@@l'"); + @.Two-digit hex number...@> + else if (sscanf(loc,"%x",&c),c<0x80) + err_print("! You cannot translate characters < 0x80"); + @.You cannot translate...@> + else + { char* p=trans_of(c); int i=0; + loc+=3; @+ + while (find_char() && isspace((eight_bits)*loc)) ++loc; /* skip space */ + if (!input_has_ended) + while (isalnum((eight_bits)*loc) || *loc=='_') + if (++i<=trans_limit) *p++=*loc++; @+ else break; + if (i>0) *p='\0'; /* terminate translation unless none was given */ + @< Report any problems with the translation string @> + } +} + +@ The main contribution of the \:l feature to \.{CWEB} is new syntax, +leading to a variety of new error messages. It is possible to give an empty +translation for a character by terminating the translation string by a +non-space character that is not allowed in identifiers; this is forbidden +however, since an empty translation for~|c| would just make +|translation_exists(c)| fail, making~|c| useless anyway. Even if the +translation string is not empty, terminating it by a non-space character is +considered an error, since this would make the \TeX\ macro used for +formatting the corresponding line of the printed document fail. + +@< Report any problems... @>= +if (i==0) err_print("! Translation string absent after `@@l'"); + @.Translation string...@> +else if (i>trans_limit) err_print("! Translation string too long"); +else if (!isspace((eight_bits)*loc)) + err_print("! Translation string not terminated by space"); + + +@ At the end of the run, if |STAT| was defined and the `\.{+s}' flag +present, we report how much of all the arrays was actually needed. + +@d report(k,c,m) + printf("%lu %ss (out of %lu)\n",(unsigned long)(c),k,(unsigned long)(m)) + +@c +#ifdef STAT +void print_stats (void) +{ print("\nMemory usage statistics:\n"); +@/report("identifier", id_index(id_ptr), max_idents); +@/report("module name", mod_index(mod_ptr), max_modules); +@/report("byte", byte_ptr-byte_mem, max_bytes); +@/report("replacement text", text_ptr-text_table, max_texts); +@/report("token", tok_ptr-tok_mem, max_toks); +} +#endif + + +@* Index. Here is a cross-reference table for the \.{\me.} +processor. All sections 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 dependencies'' are indexed here too. diff --git a/web/c_cpp/cwebx/cweave.w b/web/c_cpp/cwebx/cweave.w new file mode 100644 index 0000000000..037ebdff3d --- /dev/null +++ b/web/c_cpp/cwebx/cweave.w @@ -0,0 +1,3588 @@ +% This file is part of CWEBx. +% This program by Marc van Leeuwen based on earlier versions by +% D. E. Knuth., Silvio Levy and Frank Jensen. +% It is distributed WITHOUT ANY WARRANTY, express or implied. +% CWEB (Revision: 2.0) % Don Knuth, July 1990 +% Version 3.x, Marc van Leeuwen, December 1993 +% CWEBx 2+1.0, Marc van Leeuwen, August 1994 +% CWEBx 3.0, Marc van Leeuwen, Januari 1995 +% CWEBx 3.02, Marc van Leeuwen, April 1996 +% CWEBx 3.03, Marc van Leeuwen, January 1998 + +% Copyright (C) 1987,1990 Silvio Levy and Donald E. Knuth +% Copyright 1994 Marc A. A. van Leeuwen + +% Permission is granted to make and distribute verbatim copies of this +% document provided that the copyright notice and this permission notice +% are preserved on all copies. + +% Permission is granted to copy and distribute modified versions of this +% document under the conditions for verbatim copying, provided that the +% entire resulting derived work is distributed under the terms of a +% permission notice identical to this one. + +\def\me.{CWEAVE} \def\myroots{\.{WEAVE}} + +% Here is TeX material that gets inserted after \input cwebxmac +\def\xr.{cross-reference} +\def\Cpp{\Cee\PP} +\def\TeXxstring{\TeX\_string} +\def\TeXxlike{\TeX\_like} +\def\skipxTeX{skip\_\TeX} +\def\copyxTeX{copy\_\TeX} + +@i intro.inc % Here is some text that matches the start of CWEAVE + +@d banner "This is CWEAVE (Version x"@+version_string@+")\n" + +@ The following parameters are specific to \.{\me.}; those which are +common to \.{CTANGLE} and \.{CWEAVE} are defined in the file \.{common.inc} +and appear below. Some of these values have been decreased with respect to +their earlier values which were sufficient in the original |WEB| to handle +\TeX; a motivation is given at the common declarations. + +@d max_refs 10000 /* number of \xr.s; must be less than 65536 */ +@d max_toks 10000 /* number of symbols in \Cee\ texts being parsed; + must be less than |65536| */ +@d max_texts 2500 /* number of phrases in \Cee\ texts being parsed; + must be less than |10240| */ +@d max_scraps 4000 /* number of tokens in \Cee\ texts being parsed */ +@d max_no_of_nodes 215 /* number of nodes in search trie for grammar rules, + must be at most |256| */ +@d line_length 80 /* maximal line length for \TeX\ output; + should be less than |256| */ +@d stack_size 400 /* number of simultaneous output levels */ +@d sort_stack_size 500 /* number of identifier lists during sorting */ + +@ The program is built from two compilation units, one with source file +\.{common.w}, which contains a collection of routines and data shared +between \.{CTANGLE} and \.{CWEAVE}, and a second with master source file +\.{cweave.w} containing all code specific to \.{\me.}, and whose +typeset version you are now reading. All compilation units of the \.{CWEB} +system incorporate the file \.{common.inc} containing common declarations. + +\.{\me.} has the following outline. It operates in three phases: first +it reads the source file once for collecting \xr. data, then it +reads the source file for a second time, meanwhile producing the bulk of +the \TeX\ output file, and finally it outputs the information in its tables +from which \TeX\ will produce the list of module names, index and table +of contents. Syntax errors would often be reported identically in the first +two phases, and problems in printing module names similarly in the second +and third phase; to avoid this we check after each phase if any serious +errors were found, and if so, we stop prematurely. \.{\me.} can optionally +be compiled with preprocessor symbols |DEBUG| and/or |STAT| defined; the +former is useful for the user who requires detailed information about the +parsing process, the latter if one wishes to keep track of how much of the +static resources were actually used. + +@c +@< Function prototypes used but not defined in the shared code @>@; +@< Typedef and enumeration declarations @>@; +@< Prototypes @>@; +@< Global variables @>@; + +int main (int argc,char** argv) +{ program=cweave; + make_xrefs=true; + common_init(argc,argv); + @< Set initial values @> + if (show_banner) print(banner); /* print a ``banner line'' */ + @< Store all the reserved words @> + phase_one (); /* read all the user's text and store the \xr.s */ + if (history>harmless_message) wrap_up(); /* stop in case of trouble */ + open_output_file(); + phase_two (); /* read all the text again and translate it to \TeX\ form */ + if (history>harmless_message) wrap_up(); /* stop in case of trouble */ + phase_three (); /* output the \xr. index */ + wrap_up (); /* and exit gracefully */ + return 0; /* for completeness---not reached */ +} + +@ The macro |variant| needs to be defined before we include the file +\.{common.h}, in order to affect the definition of |id_info| and |mod_info| +(see \.{common.w} for an explanation); it refers to the |struct xref_info| +that will be declared later. + +@d variant @;xref_info +@< Prototypes @>= +void phase_one (void); + /* read all the user's text and store the \xr.s */ +void phase_two (void); + /* read all the text again and translate it to \TeX\ form */ +void phase_three (void); /* output the \xr. index */ + +@i common.inc + + +@* Data structures exclusive to {\tt \me.}. @:title@> +Although the eventual typeset output produced after running \.{\me.} +is meant to closely resemble the \.{CWEB} source from which it was created +(or maybe it is the other way around), the task of \.{\me.} is more +complicated than that of \.{CTANGLE}, because it involves a much more +detailed ``understanding'' of the program fragments present in the source. +Therefore the bulk of the program text is concerned with the parsing +process during Phase~II. Detailed information about these matters shall be +given at more appropriate places below; here we only discuss how individual +tokens are tagged on input, and how \xr. infromation is stored. + +The |ilk| field of an |id_info| structure is used to distinguish between +various types of identifiers, as follows: + +\yskip\hang |normal| identifiers are part of the \Cee\ program and +will usually appear in italic type. + +\yskip\hang |roman| identifiers are index entries that appear after \:\^ in +the \.{CWEB} file. + +\yskip\hang |wildcard| identifiers are index entries that appear after \:? +in the \.{CWEB} file. + +\yskip\hang |typewriter| identifiers are index entries that appear after \:. +in the \.{CWEB} file. + +@:section number example@> +\yskip\hang |reference| identifiers are used for explicit \xr.s by the user +within pieces of \TeX~text, allowing us for instance to remark that this is +section@#section number example@> out of a total of@#index@> in this file, +and that the sections having titles are number@#title@>. These \xr.s marks +are set by~\:: and referred to by~\:\#. + +\yskip\hang |type_defined| identifiers have occurred in a |typedef| +declaration. They will parse as types like |int|, but \xr.s to +them will be collected. + +\yskip\hang |TeX_like| identifiers like |TeX| are typeset as user-definable +control sequences. + +\yskip\hang |NULL_like| identifiers like `|NULL|' (\.{NULL}) are also +typeset as (\TeX) control sequences, but in addition are treated like +reserved words for indexing purposes, i.e., only underlined references are +generated. + +\yskip\hang |case_like|, \dots, |while_like| identifiers are \Cee\ +reserved words that will be typeset in boldface. The further distinction +between these |ilk| values determines their distinguished behaviour in +parsing (and consequently in the formatted output), since the values +are also used as category codes when these reserved words are converted +into scraps. Their values, which all exceed |NULL_like|, shall be defined +together with the other category codes. Two special cases of these are +given here, whose distinction is only needed in the lexical analysis, and +which will get a category different from their |ilk|: |const_like| and +|typedef_like| will become |int_like| in parsing (and the same holds for +|type_defined| identifiers). + +@f TeX_like TeX + +@d reserved(a) (a->ilk>=type_defined) + /* whether identifier automatically gets this |ilk| */ +@d unindexed(a) (a->ilk>=NULL_like) + /* whether cross-referencing is suppressed */ + +@< Typedef and enum... @>= +enum @/ +{ normal, /* |ilk| of ordinary identifiers */ + roman, /* |ilk| of roman type index entries */ + wildcard, /* |ilk| of user-formatted index entries */ + typewriter, /* |ilk| of typewriter type entries */ + reference, /* |ilk| of identifiers used for explicit \xr.s */ + type_defined, /* |ilk| of identifiers that are defined by |typedef| */ + TeX_like, /* |ilk| of identifiers with user-given control sequences */ + NULL_like, /* |ilk| of \.{NULL} */ + const_like, typedef_like /* special reserved words */ +}; + +@ Besides the memory used for storing names, another large memory area is +used in \.{\me.} for keeping the \xr. data. All uses of the name~|p| are +recorded in a linked list beginning at |p->xref|, which is an |xref_pointer| +pointing into the |xmem| array (here we use the pointer field for additional +information in |id_info| and |mod_info| structures, which was called |equiv| +in~\.{CTANGLE}). The elements of |xmem| are structures consisting of an +integer, |num|, and the index |next| of another element of |xmem|. If +|x==p->xref|, the value of |x->num| is a section number where |p| occurs, +plus a multiple of |cite_flag| that indicates the nature of the occurrence, +or it is |file_flag| marking the fact that |p| is a module name which is the +name of an auxiliary output file (for \.{CTANGLE}). The next such \xr. +for~|p|, if any, is |xmem[x->next]|. + +Since the entries of |xmem| are small but numerous, and the \xr. lists are +not traversed very frequently, a significant amount of space is saved by +storing a |sixteen_bits| index as a link to the next node rather than an +|xref_pointer|, and at very little cost in performance. The main price paid +is in terms of elegance, as somewhat different methods of traversal of the +lists are appropriate depending on whether the traversal may leed to the +insertion of a new node into the list or not. If no such insertion is +needed, then we can work with |xref_pointer| values as if we were dealing +with an ordinary linked list, provided only that the pointer to a successor +node is obtained by invoking a macro |next_xref| instead of selecting the +link field. If however we may need to insert a new node into the list, the +the most convenient method is to use a pointer to a link field, which then +must have type |(sixteen_bits*)|; for that case the macros |xnum| and +|xlink|, which select the |num| and |next| fields respectively of a node +specified by its index, and which yield expressions that one can assign to +or take the address of, are more useful than |next_xref|. Since the +beginning of the list is indicated by a |xref_pointer| rather than by its +index, we need to convert that pointer into an index (for which the macro +|xref_index| is supplied) when using the second method, and store this +index in a local variable, not forgetting to update the original +|xref_pointer| in case an insertion occurs at the front of the list. + +During collection of \xr.s, the lists are basically in decreasing order by +section number, as a result of the fact that new nodes are added at the +beginning of the list (for module names the situation is actually a bit more +complicated, as described below), so that in that phase the |next| field +really refers to the previous \xr. for the same name; at the end of Phase~I, +however, the lists are reversed. + +The global variable |xref_switch| is set either to |def_flag| or to zero, +depending on whether the next \xr. 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 +\xr. has been made. Similarly, the global variable |mod_xref_switch| is +either |def_flag|, |cite_flag|, or zero, depending on whether a module name +is being defined, cited, or used. During Phase~II a number of \xr.s for +identifiers will be changed into underlined ones, when the identifier is +found to occur in a declaration or function definition, since these +occurrences cannot be reliably recognised in Phase~I. + +@d xref equiv_or_xref +@d next_xref(x) (&xmem[(x)->next]) +@d xnum(i) (xmem[i].num) +@d xlink(i) (xmem[i].next) +@d xref_index(p) ((sixteen_bits)((p)-xmem)) +@d cite_flag 0x4000 + /* a |sixteen_bits| power of 2 that is at least |max_sections+2| */ +@d def_flag 0x8000 /* twice that */ +@d num_mask (cite_flag-1) /* a bit-mask for working modulo |cite_flag| */ + +@<Typedef...@>= +typedef struct xref_info +{ sixteen_bits num; /* section number plus a multiple of |cite_flag| */ + sixteen_bits next; /* index of the next \xr. in the list */ +} xref_info, *xref_pointer; + +@ We use an allocation pointer~|xref_ptr| that indicates how much of |xmem| +is already in use. Unlike in the other cases of sequential allocation, where +the allocation pointer points to the first free position, |xref_ptr| points +to the last occupied position in |xmem|; the first node |xmem[0]| is already +in use when \.{\me.} starts. All allocation of |xref_info| nodes is +performed by calling |make_xref(n,i)|, where |n| is the |num| field of the +new node, and |i| is the index of its successor in the list; after +|make_xref| has been invoked, |xref_ptr| points to the new node. + +@d make_xref(n,i) + /* create \xr. node with |num==n| and successor |xmem[i]| */ + if (++xref_ptr >= &xmem[max_refs]) + overflow ("cross-reference"); @.cross-reference capacity exceeded@> + else xref_ptr->num=n, xref_ptr->next=i @; + +@<Global...@>= +xref_info xmem[max_refs]; /* contains \xr. information */ +xref_pointer xref_ptr = &xmem[0]; /* the last used position in |xmem| */ +sixteen_bits xref_switch = 0, mod_xref_switch = 0; + /* either zero or |def_flag| */ + +@ A first node is initially allocated, with its |num==0|; the node is used +to initialise \xr. lists (see |init_id_name| and |init_module_name| below), +and serves as a sentinel at the end of those lists. + +@<Set init...@>= +xnum(0)=0; /* sentinel node terminating \xr. lists */ + +@ A new \xr. for an identifier~|p| is formed by calling |new_id_xref(p)| +with |section_count| and |xref_switch| set to the appropriate value. +Multiple references to the same section are merged (with underlining +activated if any reference requires so) while non-underlined references to +one-letter identifiers, reserved words or identifiers with |ilk==NULL_like| +(like `\.{NULL}') are ignored. Explicit \xr.s set by~\:: are never +underlined, so |xref_switch| is ignored in this case. If the user has set +the |no_xref| flag (the \.{-x} option of the command line), |new_id_xref| is +active only for explicit \xr.s; when are reading a header file included +by~\:h, it is inactive altogether (but since \:: is disabled in such header +files, this case is treated together with the |no_xref|~case). + +@d no_xref (!flags['x']) +@d make_xrefs flags['x'] /* should cross references be output? */ + +@c +void new_id_xref (id_pointer p) +{ sixteen_bits f=xref_switch; xref_switch=0; + if (p->ilk==reference) f=0; + else if (f==0 && (unindexed(p) || length(p)==1) + || no_xref || including_header_file) return; + if ((p->xref->num&num_mask)==section_count) p->xref->num|=f; + else + {@; make_xref(section_count|f,xref_index(p->xref)); p->xref=xref_ptr; } +} + +@ The \xr. lists for module names are slightly different. Suppose that a +module name is defined in sections $d_1$,~\dots,~$d_k$, cited in sections +$c_1$,~\dots,~$c_l$, and used in sections $u_1$,~\dots,~$u_m$, where the +sequences of $d$'s, $c$'s and $u$'s each are in increasing order. Then its +list will contain |@t$d_k$@>+def_flag|, \dots, |d1+def_flag|, +|@t$c_l$@>+cite_flag|, \dots, |c1+cite_flag|, $u_m$, \dots, $u_1$,~$0$, in +this order; the final~$0$ is the sentinel node, which allows the loops below +to be a little faster. If the module name specifies an output file (with the +\:( feature) then a node containing the special value |file_flag| is +prepended to this list. The special ordering described here only serves for +efficiency of insertion, and after Phase~II the order will be adjusted to +the more natural sequence |d1+def_flag|, \dots, |@t$d_k$@>+def_flag|, +|c1+cite_flag|, \dots, |@t$c_l$@>+cite_flag|, $u_1$, \dots,~$u_m$,~0. + +There can be multiple applied or cited occurrences of some module name +within one section, but only one defining occurrence. Therefore, in the +former cases we perform a test to avoid duplicate references. + +@d file_flag (cite_flag-1) + /* a distinguished value, not divisible by |cite_flag| */ + +@c +void new_mod_xref (mod_pointer p) +{ sixteen_bits head, *q, m=section_count+mod_xref_switch; + if (p->xref->num==file_flag) q=&p->xref->next; /* skip |file_flag| */ + else head=xref_index(p->xref),q=&head; + if (mod_xref_switch!=def_flag) + { while (xnum(*q)>m) + q=&xlink(*q); /* skip the $d_i$'s and possibly $c_i$'s */ + if (xnum(*q)==m) return; /* don't duplicate */ + } + make_xref(m,*q); mod_xref_switch=0; + if (q==&head) p->xref=xref_ptr; @+ else *q=xref_index(xref_ptr); +} + +@ When a module name starts with \:(, we will call |set_file_flag|. + +@c +void set_file_flag (mod_pointer p) +{@; if (p->xref->num!=file_flag) + {@; make_xref(file_flag,xref_index(p->xref)); p->xref=xref_ptr; } +} + +@ 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. Sequences of +tokens which have been delimited as such are called texts, and can be +accessed via elements of the array |text_mem|. Only a pointer to the first +token of the sequence is stored, so in order to be able to compute the +extent of a token list one needs a pointer into |text_mem|, which is called +a |text_pointer|. + +@<Typedef...@>= +typedef sixteen_bits token, * 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 |text_mem| is called +|text_ptr|. Since we already know that the next text to be stored will start +at |tok_ptr|, we make sure that |*text_ptr==tok_ptr| whenever we are not in +the process of appending tokens. In this way we can also always find the end +of any stored text as the beginning of the next one, which is computed by +the macro |text_end|. + +@d tok_mem_end (&tok_mem[max_toks]) /* end of |tok_mem| */ +@d text_mem_end (&text_mem[max_texts]) /* end of |text_mem| */ +@d text_index(p) ((sixteen_bits)((p)-text_mem)) +@d text_at(i) (&text_mem[i]) +@d text_begin(p) (*(p)) +@d text_end(p) (*(p+1)) + +@<Global...@>= +token tok_mem[max_toks]; /* tokens */ +token_pointer text_mem[max_texts]; /* directory into |tok_mem| */ +token_pointer tok_ptr = tok_mem; /* first unused position in |tok_mem| */ +text_pointer text_ptr = text_mem; /* first unused position in |text_mem| */ +#ifdef STAT +token_pointer max_tok_ptr = tok_mem; /* largest value of |tok_ptr| */ +text_pointer max_text_ptr = text_mem; /* largest value of |text_ptr| */ +#endif + +@ We initialise our invariant. + +@<Set init...@>= +*text_ptr=tok_ptr; + +@ Here are the three functions needed to complete |id_lookup|. For a +name~|x| stored in the table to match a string of length~|l| starting at~|q| +and of requested ilk~|ilk|, it is required that the names match exactly, and +either |x->ilk==ilk|, or |ilk==normal| and |x->ilk| specifies some reserved +word (where the ilks |TeX_like| and |NULL_like| are considered as reserved). +This rule means that if we look up an ``identifier'' whose name matches +reserved word stored in the table, the reserved word rather than an +identifier is returned, so that recognition of reserved words is automatic +when the tables are properly initialised. The other two functions install +the sentinel node |xmem[0]| at the end of each \xr. list as it is created. + +@c +boolean names_match (id_pointer x, char* q, int l, int ilk) +{ char* p=name_begin(x); + if ((x->ilk==ilk || ilk==normal && reserved(x))) + @/{@; while (--l>=0) if (*p++!=*q++) return false; return *p=='\0'; } + else return false; +} + +void init_id_name (id_pointer p, int t) +{@; p->ilk = t; p->xref = &xmem[0]; } + +void init_module_name (mod_pointer p) {@; p->xref=&xmem[0]; } + + +@* Skipping and copying \TeX\ material. @:title@> +The source file consists roughly speaking of two sorts of input, namely +\TeX\ material and pieces of \Cee~text. The limbo part is entirely the +realm of \TeX, and the sections are divided (possibly quite unevenly) +into a \TeX~part, and the remainder which is~\Cee, although the distinction +is not quite as pure here due to `\pb' interjections and comments. +The meddling of \.{\me.} (and indeed of all of \.{CWEB}) in the \TeX\ +parts is extremely superficial: it is restricted to skipping or copying it, +only replacing \:@@ by~`\.{@@}', and the main interest in these parts +of the source is to find out where they end. By contrast the \Cee\ fragments +have to be broken up into meaningful parts by \.{\me.} in order to perform +the required operations of collecting \xr.s and formatting for +pretty-printing output. Before we study that more complicated process of +lexical scanning, let us study the easier question of what happens with the +\TeX~parts. + +The three tasks of passing over limbo material, over the \TeX~part of a +section, and over a comment embedded in the \Cee~part are sufficiently +different that they merit separate routines. Moreover, the entire source is +scanned twice by \.{\me.}, with different purposes, and so the scanning +routines have two flavours. We could choose either to use distinct scanning +routines on both passes, or to have a single all-purpose scanning routine. +The first option is more attractive if the functions are simple and small, +and can perform tasks specific to their pass on-the-fly, while the second +option is to be favoured if the scanning task becomes complicated, to avoid +near-duplication of substantial pieces of code. We have chosen the first +option for the routines that pass over ordinary \TeX\ material, both in +limbo and in sections, and the second option for the function passing over +comments; for the lexical scanning of \Cee~text we shall also write a single +function for both phases. Where distinct routines are used we must of +course make sure that they are sufficiently similar that they will make the +same decisions about where these parts of the source text end. + +@ Although the functions to be described here have a quite trivial task, +they will eventually run into a control code that terminates their action, +and so we have to be aware of tokens that do not belong in the \TeX~part. +The relevant tokens form only a small subset of the tokens that can be +recognised by the function |get_next| that scans \Cee~text, so we defer +an enumeration of possible tokens to a later point. At this point it +is sufficient to know there is an array |ccode| which translates characters +that may follow `\.{@@}' in a control code into a numeric value greater +than any (unsigned) character, which values have symbolic names and are +arranged in order of increasing significance. In particular any value +greater than or equal to |format| terminates the \TeX~part of a section, +and the largest value of all is |new_section|. By using the same encoding, +the \TeX~text scanning functions can return a value directly usable for +continuing the scan into \Cee~territory. Tokens consisting of a single +character are represented as that character itself by |get_next|, and at +this point the relevant case is the character~`\.\v' which interrupts +\TeX~text. + +Like in |CTANGLE|, we always access |ccode| by means of the macro |code_of|. + +@d code_of(c) ccode[(unsigned char)(c)] + +@<Global...@>= +int ccode[UCHAR_MAX + 1]; /* meaning of a character following `\.{@@}' */ + +@ This section performs the simplest of all scanning operations, namely to +skip through portions of the input that are not in any sections, i.e., that +precede the first section, on the first pass. It uses the fact that +|get_line| places a~|' '| at~|*limit|, so that placing the sentinel cannot +inadvertently create a token \.{@@@@}. Although a few control codes like \:q +are allowed in limbo, they can be ignored here, since they do not affect the +recognition of the end of the limbo part. An exception is the format code +\:s, which must be obeyed in this first phase; the code doing this will be +given together with the processing of other format codes. After the code +below is executed, the value of |input_has_ended| will tell whether or not a +section has actually been found. + +@< Skip the limbo part @>= +while (find_char()) +{ limit[1]='@@'; /* place a sentinel */ + while (*loc++!='@@') {} + if (loc<=limit) + { int c=code_of(*loc++); + if (c==new_section) break; + if (c==format) @< Process a format code in limbo @> + } +} + +@ In Phase~II, the corresponding task is slightly less trivial, as the limbo +material must also be copied to the output; it is performed by the +|copy_limbo| function. Output is generated by calling |out| for ordinary +characters, and |finish_line| when a completed line is to be sent out. No +spaces or tab marks are copied by |copy_limbo| into the beginning of a line +(indicated by the macro |output_line_empty|), nor by the function |copy_TeX| +below. As a consequence a line with only spaces and removed items (like \:q, +\:s, or for |copy_TeX| \xr. entries for the index) will produce a completely +empty line of output; such lines will only be actually written out by +|finish_line| if they come from a completely blank input line. Any pair \:@@ +is replaced by `\.{@@}', and apart from this only the control codes \:q, +\:s, and \:l are allowed to be present before the first section is +encountered. Note that we have chosen to detect violations of this rule in +Phase~II; on other occasions however, errors were checked in Phase~I, and we +can then assume that errors are absent in Phase~II, as we would not even get +there otherwise. + +@c +void copy_limbo (void) /* copy \TeX\ code until the next section begins */ +{ while (loc<=limit || (finish_line(),get_line())) + { eight_bits c; + limit[1]='@@'; /* place a sentinel */ + while ((c=*loc++)!='@@') + if (!(output_line_empty() && isspace(c))) out(c); + if (loc<=limit) + switch(code_of(*loc++)) + { + case new_section: return; + case ignored_text: get_control_text(); break; + case format: get_next(); get_next(); break; /* skip two identifiers */ + case char_trans: out_str("\\ATL "); break; @.\\ATL@> + default: err_print("! Double @@ required in limbo part"); + @.Double @@ required...@> + /* fall through */ + case at_sign_image: out('@@'); + } + } +} + +@ The function |skip_TeX| is used on the first pass to skip through the +\TeX\ code at the beginning of a section. It returns the next control code +or `\.\v' found in the input. A |new_section| is assumed to exist at the +very end of the file. Any comment character `\.{\%}' that is not escaped +(with a backslash) will disable recognition of `\.\v' and cross-referencing +control codes for the remainder of the line, so that no spurious \xr.s +will be created to commented-out text. Recognition of control codes that +terminate the \TeX~part of the section will still be enabled however, to +maintain synchronisation with the activities of |CTANGLE|. + +@f skip_TeX TeX + +@c +int skip_TeX (void) /* skip past pure \TeX\ code */ +{ char c; + while (find_char()) + { limit[1]='@@'; + while ((c=*loc++)!='@@' && c!='%') + if (c=='|') return c; + else if (c=='\\' && *loc!='@@') ++loc; + /* ignore `\.{\\\%}' and `\.{\\\v}' */ + if (loc<=limit) + if (c=='@@') return code_of(*loc++); + else /* ignore remainder of line unless a major control code occurs */ + do @+ + if ((c=*loc++)=='@@' && code_of(*loc++)>=format) + return code_of(loc[-1]); + while (loc<limit); + } + return new_section; +} + +@ During Phase~II, the function |copy_TeX| processes the \TeX\ code at the +beginning of a section; for example, the words you are now reading were +copied in this way. Like |skip_TeX|, it returns the next control code or +`\.\v' found in the input, and ignores anything that is commented out, +except control codes that terminate the \TeX~part; the characters after the +`\.{\%}' are not even copied to the output. (Note that in the limbo part +comments {\sl are\/} copied, allowing any commented-out header information +such as copyright notices to be passed on to the \TeX~file.) + +@f copy_TeX TeX + +@c +int copy_TeX (void) /* copy pure \TeX\ material */ +{ eight_bits c; /* current character being copied */ + while (loc<=limit || (finish_line(),get_line())) + { limit[1]='@@'; + while((c=*loc++)!='@@') + { if (c=='|') return '|'; + if (!(output_line_empty() && isspace(c))) out(c); + if (c=='%') break; + if (c=='\\' && *loc!='@@') out(*loc++); + /* copy `\.{\\\%}' and `\.{\\\v}' */ + } + if (loc<=limit) + if (c=='@@') return code_of(*loc++); + else /* ignore remainder of line unless a major control code occurs */ + do + if ((c=*loc++)=='@@' && code_of(*loc++)>=format) + return finish_line(),code_of(loc[-1]); + while(loc<limit); + } + return new_section; +} + +@ The final \TeX~scanning function is |scan_comment| which scans the +\TeX~text in comments. It is used both in Phase~I and in Phase~II, but +stores the characters it sees only if |phase==2|. The function returns the +token that terminated its action, which is one of |end_comment|, `\.\v' or +(in erroneous cases) |new_module|. + +@< Prototypes @>= +int scan_comment(int* bal, boolean one_liner); + /* skip or copy \TeX\ text in comments */ + +@~The function |scan_comment| counts the braces it encounters to see if they +are balanced; the parameter |bal| points to an integer variable keeping +track of the brace level (it can be positive initially if |scan_comment| is +called after `\pb' occurring within braces in a comment). This feature is +mainly a remnant from \.{CTANGLE}'s Pascal origins, since there comments are +closed by `\.\}', and counting is necessary to establish which braces are +for \TeX\ and which are not. Although there is no such need in \Cee, the +feature is retained. The parameter |one_liner| tells whether we are dealing +with a \Cpp\ one-line comment. + +@c +int scan_comment (int* bal, boolean one_liner) +{ char c; boolean forced_out=false; /* prematurely terminated? */ + while (one_liner ? loc<limit + : find_char() && (*loc!='*' || loc[1]!='/' )) + @< Handle next character; if |'|'|, |return| it, + if a new section starts, set |forced_out| and |goto done| @> + if (input_has_ended) + forced_out=true,err_print("! Input ended in mid-comment"); + @.Input ended in mid-comment@> + else if (!one_liner) loc+=2; /* move past `\.{*{}/}' */ +done: + if (*bal>0) err_print("! Too few closing braces in comment"); + @.Too few closing braces...@> + return forced_out ? new_section : end_comment; +} + +@ Like elsewhere, `\.@@' should be doubled in comments; |scan_comment| +replaces them by a single `\.@@'. In Phase~II, instead of copying the \TeX\ +material into the output buffer like |copy_TeX|, |scan_comment| copies it +into the token memory, since comments will be output together with the rest +of the formatted \Cee~code. To this end it calls the macro |app_char_tok(c)| +rather than |out(c)|. When including header files comments should be skipped +with no processing at all, so this module is almost completely disabled in +that case. + +@< Handle next character... @>= +if (including_header_file) ++loc; /* don't process characters here */ +else +{ switch(c=*loc++) + { + case '|': return '|'; /* beginning of `\pb' inside comment */ + case '@@': + if (*loc++!='@@') + if (code_of(loc[-1])!=new_section) + err_print("! Double @@ required in comment"); + @.Double @@ required...@> + else + {@; err_print("! Section ended in mid-comment"); + @.Section ended in mid-comment@> + forced_out=true; goto done; + } + break; + case '\\': + if (*loc!='@@') {@; if (phase==2) app_char_tok(c); c=*loc++; } + break; + case '{': ++*bal; break; + case '}': + @+if (*bal>0) --*bal; + @+else err_print("! Extra } in comment"); + @.Extra \} in comment@> + @+break; + case '/': @+ if (*loc=='*') err_print("! Nested comment"); + @.Nested comment@> + + } + if (phase==2) app_char_tok(c); +} + + +@* Getting the next token. @:title@> +We now come to the most important lexical scanning function, |get_next|, +which locates and classifies the next token of \Cee~text. Before we give the +function itself, we shall first specify which kind of values it can return. +The result value is an integer, which can either be at most |UCHAR_MAX|, in +which case the character code represents that character, or possibly a +compressed multi-character symbol as defined by the macros +in~\.{common.inc}, or it is one of the values greater than |UCHAR_MAX| +defined in the enumeration below, which indicates a particular token or +class of tokens or a control code. (Actually, |underline| and +|trace0|,~\dots,~|trace3| are never returned by |get_next| because they are +treated within the scanner; they are included however because they are +distinguished control codes.) The ordering of this enumeration is designed +to simplify \.{\me.}'s logic; for example, from |format| on, larger +numbers are given to the control codes that denote more significant +milestones, and the code of |new_section| is the largest of all. Also, the +sequence |identifier|,~\dots,~|xref_mark| runs parallel to the first five +|ilk|~codes. For efficiency all codes that produce a fixed scrap are placed +at the beginning; these are all codes preceding |ignore|. The code \:> is +treated as ignored because it should not occur in places where we are not +searching for it. + +@f TeX_string TeX + +@<Typedef and enum...@>= +enum @/ +{ at_sign_image = UCHAR_MAX+1, /* quoted `\.{@@}' */ + or, /* \:v */ + mul_assign, div_assign, mod_assign, plus_assign, minus_assign, + left_assign, right_assign, and_assign, xor_assign, or_assign, + sh_sh, ellipsis, colon_colon, @/ + start_preproc, end_preproc, /* begin and end of a preprocessor directive */ + join, /* \:\& */ + thin_space, /* \:, */ + math_break, /* \:\v */ + line_break, /* \:/ */ + big_line_break, /* \:) */ + no_line_break, /* \:+ */ + backup_line, /* \:\\ */ + pseudo_semi, /* \:; */ + force_expr_open, force_expr_close, /* \:[, \ \:] */ + include_preproc, /* \:p */ +@) + ignore, /* control code of no interest to \.{\me.} */ + constant, string, /* the next five codes should remain in this order */ + identifier, /* any (possibly reserved) word found in \Cee\ text */ + xref_roman, xref_wildcard, xref_typewriter, xref_mark, + /* \:\^, \ \:?, \ \:., \ \:: */ + refer, /* \:\# */ + TeX_string, /* \:t */ + verbatim, /* \:= */ + ignored_text, /* \:q */ + char_trans, /* \:l */ + ASCII_code, /* \:' */ + begin_comment, end_comment, + underline, /* \:! */ +#ifdef DEBUG + trace0, trace1, trace2, trace3, /* \:0, \dots, \:3 */ +#endif + format, /* \:f */ + definition, /* \:d */ + header, /* \:h */ + begin_C, /* \:c */ + module_name, /* \:< and \:( */ + new_section /* \:\ , \:\~ and \:* */ +}; + +@ Here we initialise the |ccode| table in accordance with the comments given +in the enumeration above. + +@<Set ini...@>= +{ unsigned char c=0; + do ccode[c] = isspace(c) ? new_section : ignore; while(c++!=UCHAR_MAX); + ccode['@@'] = at_sign_image; +@/ccode['v'] = ccode['V'] = or; +@/ccode['!'] = underline; /* set definition flag */ +@/ccode['^'] = xref_roman; /* index entry to be typeset normally */ +@/ccode['?'] = xref_wildcard; /* index entry to be in user format */ +@/ccode['.'] = xref_typewriter; /* index entry to be in typewriter type */ +@/ccode[':'] = xref_mark; ccode['#']=refer; /* explicit \xr.s */ +@/ccode['t'] = ccode['T'] = TeX_string; /* \TeX\ box within \Cee\ text */ +@/ccode['='] = verbatim; +@/ccode['q'] = ccode['Q'] = ignored_text; +@/ccode['l'] = ccode['L'] = char_trans; +@/ccode['\''] = ASCII_code; +@/ccode['&'] = join; /* concatenate two tokens */ +@/ccode[','] = thin_space; +@/ccode['|'] = math_break; +@/ccode['/'] = line_break; +@/ccode[')'] = big_line_break; +@/ccode['\\']= backup_line; +@/ccode['+'] = no_line_break; +@/ccode[';'] = pseudo_semi; +@/ccode['['] = force_expr_open; ccode[']'] = force_expr_close; +ccode['p'] = ccode['P'] = include_preproc; +#ifdef DEBUG + ccode['0'] = trace0; ccode['1'] = trace1; + ccode['2'] = trace2; ccode['3'] = trace3; +#endif +@/ccode['f'] = ccode['F'] = ccode['s'] = ccode['S'] = format; +@/ccode['d'] = ccode['D'] = definition; +@/ccode['h'] = ccode['H'] = header; +@/ccode['c'] = ccode['C'] = begin_C; + /* \Cee\ text in unnamed module */ +@/ccode['<'] = ccode['('] = module_name; /* beginning of a module name */ +@/ccode['~'] = ccode['*'] = new_section; /* beginning of a new section */ + if (compatibility_mode) + @< Reset some control codes to match \LKC. @> +} + +@ In \.{CWEBx} there are a few control codes that also exist in Levy/Knuth +\.{CWEB} but have a different meaning. In compatibility mode we reassign the +meaning of these codes to that of \LKC., making their usual function +inaccessible, since it is not intended that hybrid programs should be +written using the codes of \LKC. together with features particular to +\.{CWEBx}. +@^Levy/Knuth \.{CWEB}@> + +@< Reset some control codes... @>= +{ ccode['h']=ccode['H']=include_preproc; /* \:h means \:p */ + ccode['p']=ccode['P']=begin_C; /* \:p means \:c */ + ccode['#']=big_line_break; /* \:\# means \:) */ + ccode[':']=xref_wildcard; /* \:: means \:? */ +} + +@ We come now to the definition of |get_next| itself. When returning certain +values it will have performed some additional actions, as follows. + +\yskip\hang |constant|, |string|, |TeX_string|, |verbatim|: The token is + copied into |mod_text|, with slight modifications; the global variables + |id_first| and |id_loc| are set to the beginning and ending-plus-one + locations in |mod_text|. + +\yskip\hang |identifier|, |xref_roman|, |xref_wildcard|, |xref_typewriter|, + |xref_mark|, |refer|, |module_name|: The global variable |cur_id| or + |cur_mod| will point to the identifier, control text or module name that + has just been scanned (for the |xref_roman|, \dots, |xref_mark| this is + only true if |phase==1|). + +\yskip\hang |underline|: this value is not even returned. If |get_next| sees + \:!, it sets |xref_switch| to |def_flag| and goes on to the next token. +\yskip + +Preprocessing directives complicate scanning in two ways: first, their +lexical structure is different from that of ordinary text, and second, a +preprocessor directive can occur at any place in a \Cee~text, so +syntactically it should be treated like a comment. The first issue is +resolved by maintaining a static variable |preprocessing| which is~$0$ in +ordinary \Cee~text, $2$ in \&{\#include} directives, and~$1$ in other +preprocessor directives. The second issue must be dealt with during parsing, +and in order to be able to do so, |get_next| emits special tokens +|start_preproc| and |end_preproc| when it has sensed the boundaries of a +preprocessor directive. + +@<Global...@>= +id_pointer cur_id; /* identifier or index entry just scanned */ +mod_pointer cur_mod; /* module name just scanned */ +int preprocessing=0; + +@ As one might expect, |get_next| consists mostly of a big switch that +branches to the various cases that can arise. Any character |c>=0x80| that +is not contained in a string or comment is assumed to belong to an +identifier. + +@< Prototypes @>= int get_next (void); +@~@c +int get_next (void) /* produces the next input token */ +{ eight_bits c; /* the current character */ +restart: + if (!find_char()) {@; preprocessing=0; return new_section; } + @< If a preprocessor line has ended, handle it and |return end_preproc| @> + if ((c=*loc++)=='@@') + @< Get control code and possibly module name, and either |return| it, + or |goto restart| if ignored or handled within |get_next| @> + if (isspace(c)) + if (preprocessing>0) return ' '; /* keep spaces in preprocessor lines */ + else goto restart; /* ignore other white space */ + if (c=='L' && (*loc=='\'' || *loc=='"')) + {@; get_string(); return string; } + if (isalpha(c) || c=='_' || c>=0x80) + {@; @< Get an identifier @> return identifier; } + if (isdigit(c) || c=='.' && isdigit((eight_bits)*loc)) + @/{@; @< Get a numeric constant @> return constant; } + if (c=='\'' || c=='"' || (c=='<' && preprocessing==2)) + {@; get_string(); return string; } + if (c=='#' && loc==&buffer[1]) + @/{@; @< Handle start of preprocessor directive; maybe |goto restart| @> + return start_preproc; } + if (c=='\\' && preprocessing>0 && loc==limit) + { ++loc; /* move past |limit|, so |get_line| will be called */ + goto restart; + } + @< Compress multi-character tokens @> + return c; +} + +@ When a `\.\#' is seen as the first character of a line, |get_next| returns +a special code |start_preproc| and sets |preprocessing| to a non-zero value. +Because of the freakish use of `\.<' and `\.>' to delimit a file name in +lines that start with `\.{\#include}', those lines get an extra-special +treatment, and |preprocessing| is set to~$2$ rather than to~$1$. If however +we encounter a `\.{\#include}' directive when we are already busy reading a +header file due to \:h in Phase~I, then we actually execute the directive, +and start reading the nested header file; this action will be transparent to +the stream of tokens produced, so we |goto restart| to fetch the first token +from the newly opened file. + +@<Handle start of prep...@>= +{ while (loc<limit && isspace((eight_bits)*loc)) ++loc; + /* allow spaces after `\.\#' */ + if (limit-loc>=7 && strncmp(loc,"include",7)==0) /* `\.{\#include}' line */ + if (including_header_file) + @/{@; loc+=7; push_input_file(true,false); goto restart; @+} + /* start nested header file */ + else preprocessing=2; + else preprocessing=1; +} + +@ When we get to the end of a preprocessor line, we lower the flag and +send a code |end_preproc| (unless the last character was a `\.\\', but that +case has already been taken out and never comes here). + +@<If a prep...@>= +if (preprocessing>0 && loc==limit) +{@; preprocessing=0; return end_preproc; } + +@ The following code assigns values to the compound operators `\.{++}', +`\.{--}', `\.{->}', `\.{>=}', `\.{<=}', `\.{==}', `\.{<<}', `\.{>>}', +`\.{!=}', `\.{\v\v}', and `\.{\&\&}', to the special symbols `\.{/*}', +`\.{*/}', `\.{//}', `\.{...}', `\.{::}', and `\.{\#\#}', and moreover, if not +in compatibility mode, to the assignment operators `\.{*=}', `\.{/=}', +`\.{+=}', `\.{-=}', `\.{>>=}', `\.{<<=}', `\.{\&=}', `\.{\^=}', `\.{\v=}'. +Although the comment ending token `\.{*/}' should never occur when we are +scanning \Cee~text, we must recognise it in order to detect unclosed `\pb' +constructions within comments (fortunately no legal combination of operators +causes an adjacent sequence `\.{*/}'; only `\.*' immediately followed by a +comment could cause it, and the user should simply not do this). The reason +that in compatibility mode we are forced to follow suit with \LKC. +@^Levy/Knuth \.{CWEB}@> in not combining assignment operators is a truly +stupid one: @:truly stupid@> the Stanford GraphBase @^Stanford GraphBase@> +contains fragments like `\.{\$\v n1\v=n\_1\$}' which will cause trouble if +`\.{\v=}' is parsed as a single symbol (of course the fragments should have +been written as `\.{\v n1\v\$\{\}=n\_1\$}', but we have set the goal to handle +the GraphBase as it is, and moreover its files cannot be changed). For +splitting up the assignment operators here, we shall have to pay the price of +including syntax rules (in compatibility mode) for recombining them. + +The macros defined below are for strictly local use, so we don't mind that +they could mess things up if used in the |if|-part of an |if|-|else| +statement. Also, there is no need to test whether |*loc| or |loc[1]| lie +beyond |limit|, since if they do, they are not evaluated anyway because we +know |*limit==' '|. Note that the three-symbol assignment operators +`\.{>>=}' and~`\.{<<=}' must be tested before their non-assignment +counterparts `\.{>>}' and~`\.{<<}'. + +@d compress2(char2,code) if (*loc==char2) return ++loc, code @; +@d compress3(char2,char3,code) + if (*loc==char2 && loc[1]==char3) return loc+=2, code @; +@d comp_ass_op2(code) + if (*loc=='=' && !compatibility_mode) return ++loc, code @; +@d comp_ass_op3(char2,code) + if (*loc==char2 && loc[1]=='=' && !compatibility_mode) return loc+=2,code @; + +@<Compress multi...@>= +switch (c) { +case '/': compress2('*',begin_comment); @+ + if (C_plus_plus) compress2('/',begin_comment); + comp_ass_op2(div_assign); break; +case '*': compress2('/',end_comment); comp_ass_op2(mul_assign); break; +case '%': comp_ass_op2(mod_assign); break; +case '+': compress2('+',plus_plus); comp_ass_op2(plus_assign); break; +case '-': compress2('-',minus_minus); compress2 ('>', minus_gt); + comp_ass_op2(minus_assign); break; +case '=': compress2('=',eq_eq); break; +case '>': compress2('=',gt_eq); comp_ass_op3('>',right_assign); + compress2 ('>',gt_gt); break; +case '<': compress2('=', lt_eq); comp_ass_op3('<',left_assign); + compress2 ('<', lt_lt); break; +case '&': compress2('&',and_and); comp_ass_op2(and_assign); break; +case '^': comp_ass_op2(xor_assign); break; +case '|': compress2('|',or_or); comp_ass_op2(or_assign); break; +case '!': compress2('=',not_eq); break; +case '.': compress3('.','.', ellipsis); break; +case '#': compress2 ('#', sh_sh); break; +case ':': @+ if (C_plus_plus) compress2 (':',colon_colon); +} + +@ The code below is almost identical to the corresponding module in +|CTANGLE|; the difference is that we accept characters |c>=0x80| without +testing whether a translation is defined for them (since we have not +recorded such information) and that we always look up identifiers, even if +they have length~$1$. + +@< Get an identifier @>= +{ id_first=--loc; /* mark beginning of identifier */ + do c=*++loc; while (isalnum(c) || c=='_' || c>=0x80); + cur_id= id_lookup(id_first,loc,normal); +} + +@ In \Cee~text, numeric constants are specified in the ordinary \Cee~manner: +octals start with `\.0', hexadecimals with `\.{0x}', and anything starting +with a non-zero digit is a decimal constant; however for octal and +hexadecimal constants \.{\me.} will produce output using italics or +typewriter font, respectively, and introduced by a raised circle or hash +mark. Forgivably in contradiction with the definition of~\Cee, we treat the +ubiquitous constant~`0' as decimal rather than as octal. When the kind of +constant we are dealing with has been recognised, we represent this +information internally by special marker characters, which replace the marks +used in the \Cee~source (like `\.{0x}' for hexadecimal or `\.E' for the +exponent of a floating point constant). These markers are characters like +`\.\^' that will get a backslash prepended by the output routine for +constants, so that formatting of the constants can be controlled by defining +the corresponding \TeX\ control words (like `\.{\\\^}' in the case +mentioned) appropriately. + +@d shift_and_store(ch) (*id_loc++=ch,c=*++loc) + +@<Get a numeric constant@>= +{ id_first=id_loc=&mod_text[1]; + + if (c=='0' && (isdigit(c=*loc) || tolower(c)=='x')) /* octal or hex */ + { if (isdigit(c)) /* octal constant with at least two digits */ + { *id_loc++ = '~'; /* store `\.\~' in place of leading `\.0' */ + do shift_and_store(c); while (isdigit(c)); + /* copy second and following digits */ + } + else /* hex constant */ + { shift_and_store('^'); /* replace `\.{0x}' by `\.\^' */ + while (isxdigit(c)) shift_and_store(c); + } + } + else /* decimal constant */ + { c=*--loc; /* recover first digit or decimal point */ + while (isdigit(c)) shift_and_store(c); + if (c=='.') @+ do shift_and_store(c); while (isdigit(c)); + if (tolower(c)== 'e') /* floating point constant with exponent */ + { shift_and_store('_'); /* replace `\.e' by `\.\_' */ + if (c=='+' || c=='-') {@; *id_loc++ = c; c=*++loc; } + while (isdigit(c)) shift_and_store(c); /* exponent */ + } + } + if (isalpha(c)) /* `\.{U}', `\.{L}', and/or `\.{F}' suffix */ + @/{@; *id_loc++ = '$'; do shift_and_store(c); while (isalpha(c)); } +} + +@ After an `\.{@@}' sign has been scanned, the next character tells us +whether there is more work to do. This code uses the fact that our internal +code numbers |xref_roman|, |xref_wildcard|, |xref_typewriter|, and +|xref_mark| are consecutive in the stated order, as are the |ilk| codes +|roman|, |wildcard|, |typewriter|, and |reference|. We silently eliminate the +possibility of indexing the empty string, since it would cause anomalous +situations in hashing and sorting of the index, and it would look rather +silly anyway. + +@<Get control code and possibly module name...@>= +if (including_header_file) goto restart; /* ignore `\.@@' in header files */ +else +{ int cc=code_of(*loc++); + switch (cc) + { case ignore: goto restart; + case underline: xref_switch=def_flag; goto restart; +#ifdef DEBUG + case trace0: case trace1: case trace2: case trace3: @+ + if (phase==2) tracing=cc; @+ goto restart; +#endif + case char_trans: + err_print("! `@@l' only allowed in limbo"); goto restart; + @.`@@l' only allowed in limbo@> + case ASCII_code: @< Scan an \caps{ASCII} constant @> @+ return string; + case module_name: + @< Scan the module name and make |cur_mod| point to it @> @+ break; + case ignored_text: get_control_text(); goto restart; + case verbatim: case TeX_string: get_control_text(); break; + case xref_roman: case xref_wildcard: case xref_typewriter: + case xref_mark: case refer: + if (get_control_text()) goto restart; /* don't index empty strings */ + if (cc==refer) cur_id=id_lookup(id_first,id_loc,reference); + else if (phase==1) + cur_id=id_lookup(id_first,id_loc,cc-xref_roman+roman); + } + return cc; +} + +@ There is no reason why we should allow a newline within an \caps{ASCII} +constant, even if it is escaped. + +@< Scan an \caps{ASCII} constant @>= +{ id_first=&mod_text[1]; strncpy(id_first,"@@'",2); id_loc=&id_first[2]; + while ((*id_loc++=c=*loc++)!='\'') + { if (c=='\\') + *id_loc++=*loc++; /* copy any character following backslash */ + else if (c=='@@' && *loc++!='@@') + {@; err_print("! Double @@ required in strings"); --loc; } + @.Double @@ required...@> + if (loc>=limit) {@; err_print("! ASCII constant didn't end"); break; } + @.ASCII constant didn't end@> + } +} + +@ Here |get_module_name| does nearly all the work; we only need to recognise +when \:( is used rather than~\:<, and if so insert |file_flag| in the +appropriate \xr. list. + +@< Scan the module name... @>= +{ boolean file_module=loc[-1]=='('; + cur_mod=get_module_name(); + if (file_module && phase==1 && cur_mod!=NULL) set_file_flag(cur_mod); +} + + +@* Phase I processing. @:title@> +We now have accumulated enough functions to make it possible to carry out +\.{\me.}'s first pass over the source file. If everything works right, +both Phase~I and Phase~II of \.{\me.} will assign the same numbers to +sections, and these numbers will agree with what \.{CTANGLE} does. + +We keep track of the current section number in |section_count|, which +is the total number of sections that have started. Sections which have +been altered by a change file entry have their |changed_section| flag +turned on during the first phase. Meanwhile we also keep track using +|change_exists| of whether any change was made at all, which will tell us +whether the index has changed. 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 section or part of a section. + +@d shift() (next_control=get_next()) + +@<Global...@>= +boolean change_exists=false; /* has any section changed? */ +int next_control; /* control code waiting to be acted upon */ + +@ The overall processing strategy in Phase~I has the following +straightforward outline. + +@c +void phase_one (void) + /* read all the user's text and store the \xr.s */ +{ phase=1; reset_input(); section_count=0; + @< Skip the limbo part @> + while (!input_has_ended) + @< Store cross-reference data for the current section @> + if (change_exists) mark_section_as_changed(section_count); + /* the index changes if anything does */ + @< Print error messages about unused or undefined module names @> + @< Reverse the \xr. lists for identifiers @> +} + +@ The outline for each section is equally straightforward. + +@< Store cross-reference data... @>= +{ if (++section_count==max_sections) + overflow("section number"); @.section number capacity exceeded@> + if (loc[-1]=='*') print_section_progress (); + @< Store cross-references in the \TeX~part of a section @> + @< Store cross-references in the definition part of a section @> + @< Store cross-references in the \Cee~part of a section @> + if (section_changed(section_count)) change_exists=true; + } + +@ We interrupt our refinement of |phase_one| temporarily for some auxiliary +functions that are used in its various parts. + +@< Prototypes @>= +void C_xref (boolean); /* make \xr.s within in straight \Cee~text */ +void outer_xref (void); /* make \xr.s in \Cee~text with comments */ +void mod_check (mod_pointer); /* check \xr.s for module names */ + +@ The function |C_xref| stores references to identifiers in \Cee~text, +either for an entire fragment enclosed in `\pb', or for a portion of a macro +or section body delimited by comments or module names. The boolean parameter +|inner| tells whether the former is the case; if so |C_xref| should stop +with |next_control=='|'|, and otherwise it should stop with either +|next_control==begin_comment| or |next_control>=format|. In fact, setting +|inner| will make |C_xref| stop at |'|'| but proceed past module names, +while comment delimiters and major control codes will make |C_xref| stop +regardless of |inner|. If |next_control>=format| when |C_xref| is called, +nothing will happen, but it is safe to call the function when +|next_control=='|'| or |next_control==end_comment|, which will be stepped +over rather than considered as termination condition. Thus we can avoid +saying |shift()| immediately before calling |C_xref| on several occasions. +After a `\.\#' that starts a preprocessor directive, an identifier must +follow, which must not be \xr.d; this is achieved by performing an extra +|shift()|. If no identifier follows, we report an error, but do not perform +the extra |shift()|. The code below uses the fact that our internal code +numbers |identifier|, |xref_roman|, |xref_wildcard|, |xref_typewriter|, and +|xref_mark| are consecutive. + +The other task of Phase~I is to collect all unusual |ilk| assignments, +and the subtler part of this is the processing of |typedef| declarations. +The details of this process will be explained later, but this is where +it is hooked into the other actions, since all relevant tokens (which do +not include module names or comments) pass here one by one. + +@c +void C_xref (boolean inner) +{ while (next_control<format || next_control==module_name && inner) + { @< Keep track of tokens relevant to |typedef| declarations @> + if (next_control>=identifier && next_control<=xref_mark) + new_id_xref(cur_id); + else if (next_control==module_name && cur_mod!=NULL) + mod_xref_switch=cite_flag,new_mod_xref(cur_mod); + if (next_control==start_preproc && shift()!=end_preproc + &&next_control!=identifier) + err_print("! Identifier should follow `#'"); + @.Identifier should follow `\#'@> + else shift(); + if (next_control=='|' && inner + || next_control==begin_comment || next_control==end_comment) + return; + } +} + +@ The function |outer_xref| is like |C_xref|, but is used to scan an entire +macro body, or a portion of a section body delimited by module names; it +handles \Cee~text with embedded comments. It is called after \:d, a format +definition, \:c, or a module name (either defining or applied) has been +scanned, and after \:h has fired up its header file; in all cases +|next_control| is already processed, and we start with |shift()|. (There is +also one call that can occur during Phase~II, namely if illegal items +following a \:s format definition were found; its purpose then is merely to +ensure |next_control>=format| without producing any output.) + +While a comment is being scanned, tokens that pass |C_xref| should not be +considered as part of a possible |typedef| that is in progress; this is +achieved by invoking the macro |typedef_tracking| with the proper boolean +value at the beginning and end of the comment. + +@c +void outer_xref (void) /* extension of |C_xref| */ +{ shift(); /* move past previously processed token */ + while (next_control<format) + if (next_control!=begin_comment) C_xref(false); + else + { boolean one_liner=loc[-1]=='/'; int bal=0; /* brace level in comment */ + typedef_tracking(false); + while ((next_control=scan_comment(&bal,one_liner))=='|') + @/{@; C_xref(true); if (next_control!='|') break; } + typedef_tracking(true); + } +} + +@ In the \TeX~part of a section, \xr. entries are made only for the +identifiers in \Cee\ texts enclosed in `\pb', or for control texts +introduced by \:\^, \:., \:?, or \::. + +@< Store cross-references in the \TeX... @>= +do + switch (next_control=skip_TeX()) + { case underline: xref_switch=def_flag; break; + case '|': C_xref(true); break; + case module_name: case refer: loc-=2; get_next(); break; + case ignored_text: get_control_text(); break; + case char_trans: err_print("! `@@l' only allowed in limbo"); break; + @.`@@l' only allowed in limbo@> + case xref_roman: case xref_wildcard: case xref_typewriter: + case xref_mark: loc-=2; get_next(); new_id_xref(cur_id); + } +while (next_control<format); + +@ During the definition and \Cee~parts of a section, \xr.s are made for all +identifiers except reserved words and the right hand sides of format +definitions. 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. + +@ When we get to the following code we have |next_control>=format|. + +@< Store cross-references in the def... @>= +while (next_control<begin_C) /* |format|, |definition| or |header| */ + if(next_control!=header) + { xref_switch=def_flag; /* implied \:! for first identifier */ + if (next_control==format) @< Process a format code in a section @> + outer_xref(); /* macro definition or comment after format definition */ + } + else @< Read a header file, scanning it for |typedef| declarations @> + +@ Before we handle format codes occurring in a section, let us consider +their treatment in limbo. Here the code must be a non-printing \:s rather +than \:f, since we are not prepared to emit formatted output in limbo. The +syntax and semantics are simple: two identifiers must follow, and the |ilk| +of the latter is is assigned to the former. + +@< Process a format code in limbo @>= +if (tolower((eight_bits)loc[-1])=='f') + err_print("! Double @@ required in limbo part"); + @.Double @@ required...@> +else +{ id_pointer lhs; + if (shift()==identifier && (lhs=cur_id,shift()==identifier)) + lhs->ilk=cur_id->ilk; + else err_print("! Improper format definition"); + @.Improper format definition@> +} + +@ In a section \:s is processed in the same way; for \:f we additionally +produce a defining \xr. for the left hand side. We do not call |shift| since +this will be done by |outer_xref|, which is called after this code to +process any comments that follow the format definition. + +@< Process a format code in a section @>= +{ boolean f= tolower((eight_bits)loc[-1])=='f'; + id_pointer lhs; + if (shift()==identifier && (lhs=cur_id,shift()==identifier)) + { if (f) new_id_xref(lhs); @+ else xref_switch=0; + lhs->ilk=cur_id->ilk; + } + else err_print("! Improper format definition"); + @.Improper format definition@> +} + +@ After \:h a file name follows, enclosed in white space, double quotes, or +angle brackets. We open the header file by the same routine used to open \:i +files, but here we do suspend reading from the change file. If all is well +the whole file will contain pure \Cee~text without any control codes, so +|outer_xref| will come to a halt shortly after returning from that file. + +@< Read a header file... @>= +{ if (push_input_file(true,true)) /* prepare for reading header file */ + including_header_file=true; /* will be reset on closing the file */ + typedef_tracking(true); /* this is what we are doing it for */ + outer_xref(); + /* |shift()| and collect typedefs until |next_control>=format| */ + typedef_tracking(false); +} + + +@ Finally, when the \TeX\ and definition parts have been treated, we have +|next_control>=begin_C|. The loop repeatedly marks a module name +\xr. and then calls |outer_xref| to scan everything up to the +next module name; if |next_control==module_name| initially, we raise +|mod_xref_switch| which will cause that first module name to be marked as +defining. + +@< Store cross-references in the \Cee... @>= +{ if (next_control<new_section) /* |begin_C| or |module_name| */ + { typedef_tracking(true); + mod_xref_switch= next_control==module_name ? def_flag : 0; + do + { if (next_control==module_name && cur_mod!=NULL) + new_mod_xref(cur_mod); + outer_xref(); + } while (next_control<new_section); + typedef_tracking(false); + } +} + +@ After we have seen everything, we want to check that each module name was +both defined and used. The following recursive function walks through the +tree of module names alphabetically and prints out anomalies. It also +reverses the sublists of \xr.s to sections where a module is being defined, +cited, and used. In this section we use the fact that \xr. lists end with a +sentinel with |num==0|. +@^recursion@> + +@c +void mod_check (mod_pointer p) /* print anomalies in subtree |p| */ +{ if (p != NULL) + { mod_check (p->llink); /* traverse left subtree */ + { boolean file_module = p->xref->num==file_flag; + sixteen_bits head, *q, threshold; + /* lower limit of |num| values of current interest */ + if (file_module) q=&p->xref->next; @+ + else head=xref_index(p->xref),q=&head; + if (!complete_name(p)) + @/{@; print("\n! Never completed"); print_mod(p); mark_harmless(); } + @.Never completed: <module name>@> + if (xnum(*q)<=(threshold=def_flag)) + @/{@; print("\n! Never defined"); print_mod(p); mark_harmless(); } + @.Never defined: <module name>@> + else + @< Reverse sublist after |*q| with entries |num>threshold|; + make |q| point to final |next| field @> + if (xnum(*q)>(threshold=cite_flag)) @/@< Reverse sublist... @> + if (xnum(*q)==(threshold=0)) + @/{@; if(!file_module) + {@; print("\n! Never used"); print_mod(p); mark_harmless(); } + @.Never used: <module name>@> + } + else @< Reverse sublist... @> + if (!file_module) p->xref=&xmem[head]; + /* set pointer to possibly modified value */ + } + mod_check (p->rlink); /* traverse right subtree */ + } +} + +@~@<Print error messages about un...@>= +mod_check(root); + +@ We now come to the reversal of the \xr. lists, which is necessary because +by repeatedly prepending elements to these lists, or in the case of module +names to one of three sublists, these (sub)lists have obtained reverse +ordering. The method of traversal of the set of all identifiers is different +from that for the set of all module names, whence these tasks are linked +into the program at different points, but the reversal routines themselves +are quite similar. As we already traverse the module names in |check_root|, +the reversal code for module \xr.s was simply inserted at the proper place +in that function; for traversal of the set of identifiers we use the |hash| +table, by following all non-empty hash lists. + +@< Reverse the \xr. lists... @>= +{ id_pointer name; id_pointer *h; /* pointer into |hash| */ + for (h=hash; h<hash_end; h++) + for (name=*h; name!=NULL; name=name->hash_link) + /* traverse all hash lists */ + @< Reverse the list |name->xref| @> +} + +@ As Knuth keeps reminding us, list reversal can be thought of as a process +of repeatedly popping values off one list~|x| and pushing them onto the +reversed list~|y| (or you may read ``stack'' for ``list'' if you like). It +can also be useful to remember that the basic action can be performed by a +four-stroke engine, where the left hand side of each assignment equals the +right hand side of the previous one. The basic cycle can actually take +different forms, each using an auxiliary variable~|t|. One way is to +use~|t| to hold the entry moved, repeating |{ t=x; x=t->next; t->next=y; +y=t;}| until |x==NULL|; another way is to use~|t| to hold the remainder of +the list to be reversed, repeating |{ t=x->next; x->next=y; y=x; x=t;}|, +again until |x==NULL|. For reversing the \xr. lists of identifiers, we use +the first form. + +@< Reverse the list |name->xref| @>= +{ sixteen_bits x=xref_index(name->xref),t,y=0; + /* index of the sentinel node */ + while (xnum(x)!=0) {@; t=x; x=xlink(t); xlink(t)=y; y=t; } + name->xref=&xmem[y]; /* don't forget to link in the reversed list */ +} + +@ The reversal of sublists of the \xr. list attached to module names is only +slightly more complicated. At the three places where the code below is used, +things have been set up so that |q| points to the location of the link +pointing to the start of the sublist (since this link is going to be +changed, we need a pointer to it) and the end of the list is implicitly +indicated by |threshold|: the sublist ends before the first entry with +|num<=threshold| (which always exists because of the sentinel with +|num==0|). It has also been ensured that the code is only invoked when the +indicated sublist is not empty, so that we can use a |do|-|while| loop; we +have chosen the alternative order of assignments with respect to the +previous section, mainly to demonstrate the possibility, although it also +allows the code to be slightly shorter here. + +After the sublist has been reversed, some links must be redirected to +install it in its proper place. The link |*q| must be pointed to the head of +the reversed list, which is in |y|, while the link at the end of the sublist +must be pointed to the unaffected remainder of the list (this remainder +should ideally have been assigned to |y| initially, but it is only located +once we have arrived at the first entry with |num<=threshold|). Fortunately +the final node of the sublist is not only pointed to by its predecessor, but +also by |*q| (before it is changed) since it used to be the first node of +the sublist; therefore a small sequence of carefully ordered assignments +will do the trick. It is instructive to check that if the sublist to be +reversed has length~$1$, then all variables will eventually return to their +original state. The initialisation of~|y| is only present to keep certain +compilers from complaining that its value is used before it is first +assigned to; the initial value is irrelevant since it will be overwritten. + +@< Reverse sublist... @>= +{ sixteen_bits x=*q,y=0,t; + do {@; t=xlink(x); xlink(x)=y; y=x; } while (xnum(x=t)>threshold); + xlink(t=*q)=x; *q=y; q=&xlink(t); +} + + +@* Outline of Phase II processing. @:title@> +With the description of Phase~I still fresh in our memory, let us look at +the general outline of Phase~II, which is analogous, although it is more +complicated. The extra complication is due to the fact that much more has to +be done during Phase~II, notably the \Cee~texts have to be parsed in order +to determine their proper formatting, and the resulting token lists have to +be written to a file in a form that \TeX\ will be able to process. Most of +the actual work however is localised in a few powerful functions that will +be defined in detail later on, so that the definition of |phase_two| can be +given here without much problems. + +There are three stages in the processing of a piece of \Cee~text during +Phase~II: first it is scanned lexically (using |get_next|), and the +resulting tokens are collected in the form of `scraps' that form the input +for the second stage, the parsing algorithm, which transforms the scraps into +a recursively nested token list, which is converted in the third stage to +textual output. +For the small parts of \Cee~text enclosed in `\pb', a function~|do_C| is +available which handles all three stages of processing: it is called when an +opening `\.\v' is seen, and when it is completed one has arrived at the +closing `\.\v', and the required output is written on the \TeX~file. +For the larger parts of \Cee~text, the function |outer_read| will read in, +and convert to scraps, chunks of \Cee~text delimited by control codes +|c>=format| (so like |outer_xref| it handles comments, but it will not +incorporate module names), and when enough of these have been accumulated, +a call on |finish_C| will invoke the parsing algorithm and send the +resulting tokens to the output file. + +A number of simple functions for producing output are also called explicitly +at certain points. We have already seen |out| and~|finish_line| for +character-based output; there is also |out_str| for writing a string, +|out_sec_nr| for a section number, |list_refs| for generating the text for +\:\#, and |footnote| for producing the module \xr. information at the end of +sections. Invoking the macro |tex_new_line| immediately after |finish_line| +was called will produce an empty line on the output. Furthermore certain +small pieces of code which have been scanned directly rather than via |do_C| +or |outer_read| (for instance after format or macro definitions) are +converted into scraps: first a number of tokens are appended by means of +|app| or~|app_str| and then the whole sequence is converted to a scrap by +calling |pack_scrap|; |app| and~|pack_scrap| are macros. + +@< Prototypes @>= +void do_C (void); /* handle \Cee~text enclosed in `\pb' */ +void outer_read (void); /* transform input into scraps */ +void finish_C (void); /* finishes a definition or a \Cee~part */ +void finish_line(void); /* send out a line of output */ +void out_str (char*); /* write multiple characters */ +void out_sec_nr (int); /* output a section number */ +xref_pointer list_refs (xref_pointer,sixteen_bits); + /* output module \xr.s */ +void footnote(xref_pointer*,sixteen_bits); /* same with heading text */ +void app_str(char*); /* append a sequence of character tokens */ + +@ Like in |phase_one|, we loop over the sections after passing over the limbo +part. + +@c +void phase_two (void) + /* read all the text again and translate it to \TeX\ form */ +{ phase=2; reset_input (); + print_progress("\nWriting the output file..."); + @.Writing the output file...@> + section_count=0; copy_limbo(); finish_line(); + tex_new_line(); /* insert a blank line, it looks nice */ + while (!input_has_ended) @<Translate the current section@> +} + +@ The output file will contain the control sequence `\.{\\Y}' before a +non-empty definition portion of a section, and before a non-empty +\Cee~portion (the \TeX~portion is always considered to be non-empty, since +it contains at least the section number). This puts a little white space +between adjacent portions when they are printed. + +@d emit_space() out_str ("\\Y"); @.\\Y@> + +@< Translate the current section @>= +{ section_count++; + @< Output the code for the beginning of a new section @> + @< Translate the \TeX~part of the current section @> + if (next_control<begin_C) + @/{@; emit_space(); + @< Translate the definition part of the current section @> + } + if (next_control<new_section) + { mod_pointer this_module=NULL; /* the current module name */ + emit_space(); @< Translate the \Cee~part of the current section @> + @< Show cross-references to this section @> + } + @< Output the code for the end of a section @> +} + +@ Sections beginning with the \.{CWEB} control sequence \:{\ } start in the +output with the \TeX\ control sequence `\.{\\M}', followed by the section +number. Similarly, \:* sections lead to the control sequence `\.{\\N}', and +\:\~ sections to `\.{\\n}'. If this is a changed section, we put `\.*' just +before the section number. + +@< Output the code for the beginning... @>= +{ out('\\'); out(loc[-1]=='*' ? 'N' : loc[-1]=='~' ? 'n' : 'M' ); + @.\\N@> @.\\n@> @.\\M@> + if (loc[-1]=='*') + {@; print_section_progress(); @< Handle title level @>@+ } + out_sec_nr(section_count); out_str(". "); +} + +@ Between \:* and the title that follows, a level can be specified in the +form of another `\.*', or a decimal number; the absence of a number will be +interpreted as level~0. The level will be written out after `\.{\\N}' as a +first argument, delimited by a space, after which the second argument is the +section number and the third specifies the title. + +@< Handle title level @>= +{ if (*loc=='*') ++loc,out_str("-1"); + else if (!isdigit((eight_bits)*loc)) out('0'); + else do out(*loc++); while (isdigit((eight_bits)*loc)); + out(' '); /* terminate level by a space */ +} + +@ In the \TeX~part of a section, we simply copy the source text, except that +index entries are not copied and \Cee\ text within `\pb' is translated; +during this translation we track typedef definitions so that any complete +typedef declaration within `\pb' will be parsed correctly, as will be +explained below. + +@< Translate the \TeX... @>= +do + switch (next_control=copy_TeX()) + { case '|': typedef_master=0; do_C(); break; + case at_sign_image: out('@@'); break; + case thin_space: case math_break: case ASCII_code: case line_break: + case big_line_break: case no_line_break: case join: case pseudo_semi: + case force_expr_open: case force_expr_close: + err_print("! You can't do that in TeX text"); + @.You can't do that...@> + break; +#ifdef DEBUG + case trace0: case trace1: case trace2: case trace3: tracing=next_control; + break; +#endif + case module_name: loc-=2; get_next(); break; /* get module name */ + case refer: loc-=2; get_next(); /* get name referred to */ + if (cur_id->xref->num==0) err_print("! Undefined reference"); + else list_refs(cur_id->xref,0); + break; + case TeX_string: err_print("! TeX string should be in C text only"); + @.TeX string should be...@> + /* fall through */ + case xref_roman: case xref_wildcard: case xref_typewriter: + case xref_mark: case ignored_text: + get_control_text(); /* skip to \:> */ + } +while (next_control<format); + +@ When we get to the following code we have |format<=next_control<begin_C|. +The first conditional statement below processes the first few tokens of a +preprocessor directive, which do not follow the ordinary syntax rules; the +remainder is then treated by |outer_read| using the regular parsing +mechanism, although in the case of a format definition this should only +involve possible comments and formatting controls, and in case of \:s even +these should be absent. + +@< Translate the def... @>= +{ typedef_tracking(false); + do + { boolean suppressed=false; /* whether output suppressed by \:s */ + if (next_control==format) @< Start a format definition @> + else if (next_control==definition) @< Start a macro definition @> + else @< Start a header file inclusion @> + if (!suppressed) outer_read(), finish_C(); + else if (next_control<format) + { err_print("! Improper stuff after `@@s' format definition"); + @.Improper stuff after `@@s'...@> + if (next_control==begin_comment) loc-=2; /* try to get back in phase */ + outer_xref(); /* skip illegal stuff */ + } + } while (next_control<begin_C); /* |format|, |definition|, or |header| */ +} + +@ The syntax of a format definition has already been checked. It suffices to +build scraps that will produce the desired output. The trickiest point is +formatting the identifiers in a way that is least confusing to readers. We +try to keep analogy to lines for macro definitions in case these define one +identifier to stand for another: the left hand side is set in italics +regardless of its |ilk|, while the right hand side has its normal +appearance, prescribed by its |ilk|. If the left hand side is a |TeX_like| or +|NULL_like| identifier, we stick to the representation in italics, even though +this costs some extra work, and is not what would happen for a macro +definition; in addition we append the usual formatted form of the identifier +in parentheses, to show the correspondence of name and printed symbol. + +@< Start a format... @>= +if (tolower((eight_bits)loc[-1])=='s') +@/{@; suppressed=true; shift(); shift(); shift(); } + /* skip format definition */ +else +{ int saved_code=0,saved_mathness; + app_str("\\F"); shift(); /* this will produce `\&{format}' */ @.\\F@> + if (cur_id->ilk!=TeX_like && cur_id->ilk!=NULL_like) + app(id_flag+id_index(cur_id)); + else @< Expand identifier and set |saved_code| and |saved_mathness| @> + app('~'); pack_scrap(insert,yes_math); shift(); + app((cur_id->ilk==normal || cur_id->ilk==TeX_like || cur_id->ilk==NULL_like + ? id_flag : res_flag + )+id_index(cur_id)); +@/check_scrap(); + pack_scrap(insert,cur_id->ilk==TeX_like ? no_math : yes_math); + shift(); + if (saved_code!=0) + { app_str("\\quad("); app(saved_code); app(')'); + @/check_scrap(); pack_scrap(insert,saved_mathness); + } +} + +@ Since conversion of an identifier into a \TeX\ control sequence is +performed by the output routine, we need to circumvent this to force italic +type; this is done by expanding the name into characters directly rather +than leaving this to the output routine. + +@< Expand identifier... @>= +{ char* p=name_begin(cur_id); + saved_mathness=cur_id->ilk==TeX_like ? no_math : yes_math; + saved_code=id_flag+id_index(cur_id);/* save to print afterwards */ + app_str("\\\\{"); @.\\\\@> + do {@; if (*p=='_') app('\\'); app_tok(*p); } while (*++p!='\0'); + app('}'); check_toks(10); +} + +@ Keeping in line with the conventions of the \Cee\ preprocessor (and +otherwise contrary to the rules of \.{CWEB}) we distinguish here between the +cases that a `\.(' immediately follows the identifier being defined, and the +case that anything else (possibly a space) does. In the latter case, the +replacement text starts immediately after the identifier, in the former +case, it starts after we scan the matching `\.)', which must be simply the +first `\.)' that follows. + +@<Start a macro...@>= +{ if (shift()!=identifier) + err_print("! Improper macro definition"); + @.Improper macro definition@> + else + { app_str("\\D$"); /* this will produce \&{\#define} */ @.\\D@> + app(id_flag+id_index(cur_id)); + if (*loc=='(') + { shift(); + do + { app_char_tok(next_control); + if (shift()!=identifier) break; + app(id_flag+id_index(cur_id)); + } while(shift()==','); + check_toks(2); + if (next_control==')') {@; app(')'); shift(); } + else err_print("! Improper macro definition"); + } + else shift(); + app('$'); app(break_space); pack_scrap(insert,no_math); + } +} + +@ For scanning the token following \:h we temporarily set |preprocessing=2|, +so that angle brackets will be recognised as string quotes. + +@<Start a header file...@>= +{ app_str("\\h"); /* this will produce \&{\#include} */ @.\\h@> + pack_scrap(insert,no_math); + { int save=preprocessing; preprocessing=2; /* emulate `\.{\#include}' */ + while (shift()==' ') {} /* skip spaces and read file name as string */ + preprocessing=save; + } +} + +@ Finally, when the \TeX\ and definition parts have been treated, we have +|next_control>=begin_C|. If the section defines a module name, we assign the +name to the variable |this_module|, so that the proper \xr. information can +be listed at the end of the section. Like in Phase~I it is necessary to pay +special attention to |typedef| declarations, and this time tracking is +enabled both within `\pb' in \TeX~text and in the \Cee~part of a section; +since the former may involve incomplete pieces of syntax like a sole +`|typedef|', we reset the master counter to its neutral state at the +beginning of a \Cee~part. After the heading of the \Cee~text has been +processed we alternatively read ordinary pieces of \Cee~text and module +names until the module has ended; we start with calling |outer_read| before +testing termination, in order to ensure that the overflow tests contained in +|outer_read| will be executed even in case of a section with a \Cee~part +consisting only of a heading. + +@<Translate the \Cee...@>= +{ typedef_master=0; + if (next_control==begin_C) shift(); + else + { this_module=cur_mod; /* register the name for this module */ + @< Check that `\.{=}' or `\.{==}' follows this module name, and + emit the scraps to start the module definition @> + } + do + { outer_read(); + if (next_control==new_section) break; + if (next_control==module_name) @< Append a module name scrap @> + else err_print("! You can't do that in C text"); + @.You can't do that...@> + /* |format|, |definition| or |begin_C| */ + shift(); + } while (true); + finish_C(); +} + +@ Despite the name of this module, we allow `\.+' to precede the `\.{=}' or +`\.{==}', just as |CTANGLE| does. Note however that, unlike in |CTANGLE|, +the `\.+' will be scanned as part of an `\.{+=}' compound operator (whence +in fact `\.{+= =}' is allowed here whereas `\.{+ ==}' is not; we hope +nobody minds this). Note also that if for whatever reason the current +section number should fail to appear in the \xr. list for the +module name (e.g., if section numbers have inadvertently got out of +synchronisation with respect to Phase~I), then listing the +\xr. information at the end of the section is suppressed by setting +|this_module=NULL|. + +@< Check that `\.{=}' ... @>= +{ if (shift()=='=' || next_control==eq_eq || next_control==plus_assign) + @/{@; if (next_control!=plus_assign || shift()=='=') shift(); } + /* accept `\.=', `\.{==}', `\.{+=}' or `\.{+==}' */ + else err_print("! You need an = sign after the module name"); + @.You need an = sign...@> + if (this_module!=NULL) /* i.e., unless module name was bad */ + { xref_pointer x=this_module->xref; + if (x->num==file_flag) x=next_xref(x); + app_str("\\4$"); /* module name will be flush left */ @.\\4@> + app(mod_flag+mod_index(this_module)); + if (x->num != section_count+def_flag) + { app_str("\\PE"); /* module has also been defined before */ @.\\PE@> + this_module = NULL; /* so we won't give \xr. info here */ + } + else app_str("\\EQ"); /* output a module definition sign */ @.\\EQ@> + app_str("{}$"); app(force); pack_scrap(insert,no_math); + /* this forces a line break unless \:+ follows */ + } +} + +@ Cross references relating to a named module are given after its first +defining section ends (for further defining sections of this name we will +have put |this_module=NULL|). + +@< Show cross-references... @>= +{ if (this_module != NULL) + { xref_pointer foot_ref=this_module->xref; + if (foot_ref->num==file_flag) foot_ref=next_xref(foot_ref); + foot_ref=next_xref(foot_ref); /* don't \xr. to yourself */ + footnote(&foot_ref,def_flag); + /* display further defining sections; advance |foot_ref| */ + footnote(&foot_ref,cite_flag); /* display any citations */ + footnote(&foot_ref,0); /* display uses */ + } +} + +@ The `\.{\\fi}' closes a \TeX~conditional that was initiated by the macro +that started off the section; this allows printing of only the changed +sections in a simple way. + +@<Output the code for the end of a section@>= +{@; out_str ("\\fi"); finish_line (); tex_new_line(); } +@.\\fi@> /* insert a blank line, it looks nice */ + + +@* Auxiliary functions used in Phase~II. @:title@> +We now define the functions that do the actual processing of \Cee~code +during Phase~II, but without going into the details of parsing and output. +We explain the functions |do_C|, |outer_read|, |finish_C|, |list_refs|, +and |footnote| used above, and also two further auxiliaries |C_read| +and~|C_translate|. + +@< Prototypes @>= +text_pointer translate(void); + /* build formatted text from collected scraps */ +void make_output(text_pointer,mode); + /* output text in |inner| or |outer| mode */ + +@ Before we discuss these functions, we must first discuss what scraps are. +Scraps are the objects manipulated during parsing, and they have two main +attributes: a syntactic category |cat|, that determines the way they will be +treated by the parser, and a translation |trans|, which is a pointer +into~|text_mem| denoting a (possibly recursively nested) sequence of tokens, +that determines the representation of the scrap upon output. Since some +parts of the output are to be processed in \TeX's math mode, and other parts +in horizontal mode, an additional field |mathness| tells which mode is +required at each end of the translation of the scrap. + +@<Typedef...@>= +typedef struct +{ eight_bits cat; /* category code */ + eight_bits mathness; /* whether in math mode at left and right boundary */ + text_pointer trans; /* translation text */ +} scrap, *scrap_pointer; + +@ When \Cee\ text is converted into scraps for parsing, the resulting scraps +are placed in an array |scrap_info|, between the locations pointed to by +|scrap_base| and~|scrap_ptr|. Actually, |scrap_info| is one field of a +|union|, since the same memory is used for a different purpose during +Phase~III. + +Basic scraps are created by invoking |app|, |app_tok| or |app_char_tok| a +number of times creating the constituent tokens, and then consolidating the +text by means of |freeze_text|; the resulting text is accessible as +|text_ptr| before, and as |text_ptr-1| after the call of |freeze_text|. In +the common case that the text forms the translation of a new scrap that is +to be added to the scrap sequence, |pack_scrap| can be used in place of +|freeze_text|; a category and `mathness' should be supplied in this case. +The latter can take one of three values as explained later, and by +multiplying it by~5 (binary~$0101$) it is duplicated into the two least +significant pairs of bits, because for elementary scraps the value is the +same at its left and right boundaries. Note that none of |app|, |freeze_text| +and |pack_scrap| do bound checks, since it is assumed that these have been +done beforehand; |app_tok| and |app_char_tok| however can be called at more +uncertain times. + +@d scrap_info scrap_union.scrap_field +@d scrap_info_end (&scrap_info[max_scraps]) /* end of |scrap_info| */ +@) +@d app(a) (*tok_ptr++ = a) +@d app_tok(a) @+ + if (tok_ptr>tok_mem_end-2) overflow("token"); @.token capacity exceeded@> + @+ else app(a) @; +@d app_char_tok(c) app_tok((unsigned char)(c)) +@d freeze_text() (*++text_ptr = tok_ptr) +@d pack_scrap(c,m) + ( scrap_ptr->cat = c, scrap_ptr->trans = text_ptr, freeze_text(), + (scrap_ptr++)->mathness = 5*(m) ) + +@<Global...@>= +union +{ scrap scrap_field[max_scraps]; /* memory array for scraps */ + @< Alternative use of |scrap_union| @>@; +} scrap_union; +scrap_pointer scrap_base=scrap_info; + /* beginning of the current scrap sequence */ +scrap_pointer scrap_ptr = scrap_info; + /* points to end of the current scrap sequence */ +#ifdef STAT +scrap_pointer max_scr_ptr = scrap_info; + /* largest value assumed by |scrap_ptr| */ +#endif + +@ Token lists are stored in |tok_mem| and represent text to be output to the +\TeX~file. Because during parsing token lists will often be formed by +concatenation of existing ones, a representation is chosen where this can be +done easily; in particular a token can be a reference to a token list stored +elsewhere. Also identifiers, reserved words and module names are represented +by a reference to the name table rather than by their constituent +characters. All other items are stored as list of characters, which have +been widened to fill a 16-bit |token|, and special layout codes that will be +explained below. More precisely, a |token t@;| is interpreted as follows. +\yskip + +\item{$\bullet$} |t<=UCHAR_MAX|: the character~|t|, which possibly is + a compressed operator like `\.{\&\&}'; +\item{$\bullet$} |UCHAR_MAX<t<id_flag|: a special layout feature such as + |indent|, +\item{$\bullet$}|id_flag<=t<res_flag|: the identifier with name + |id_at(t-id_flag)|; +\item{$\bullet$}|res_flag<=t<mod_flag|: the reserved word + |id_at(t-res_flag)|; +\item{$\bullet$}|mod_flag<=t<text_flag|: the module named + |mod_at(t-mod_flag)|; +\item{$\bullet$}|text_flag<=t<inner_text_flag|: a reference to the token list + |text_at(t-text_flag)|; +\item{$\bullet$}|inner_text_flag<=t|: a reference to the token list + |text_at(t-inner_text_flag)|, which is to be translated without + line-break controls. + +@d id_flag 10240U /* signifies an identifier */ +@d res_flag (2*id_flag) /* signifies a reserved word */ +@d mod_flag (3*id_flag) /* signifies a module name */ +@d text_flag (4*id_flag) /* signifies a token list */ +@d inner_text_flag (5*id_flag) /* signifies a token list in `\pb' */ + +@~The special layout tokens are the following: +\yskip + +\item{$\bullet$} |indent| causes future lines to be indented one more unit; +\item{$\bullet$} |outdent| causes future lines to be indented one less unit; +\item{$\bullet$} |opt| denotes an optional line break, it is followed by an + integer~|n|, and the break will normally occur with penalty~$10n$ (in + fact \TeX\ raises some penalties, but this remains invisible to + \.{\me.}); +\item{$\bullet$} |flush_left| denotes that the line will be printed flush + left, regardless of the current indentation level; +\item{$\bullet$} |break_space| denotes an optional line break or an ``en'' + space; +\item{$\bullet$} |force| denotes a forced line break; +\item{$\bullet$} |big_force| denotes a forced line break with additional + vertical space; +\item{$\bullet$} |backup| denotes a forced line break followed by a + backspace of one indentation unit; +\item{$\bullet$} |big_backup| denotes a forced line break with additional + vertical space, followed by a backspace of one indentation unit; +\item{$\bullet$} |cancel| obliterates any space, |break_space|, |force|, + |big_force|, |backup|, or |big_backup| tokens that immediately precede + or follow it and also cancels any |opt| tokens that follow it; +\item{$\bullet$} |relax| does nothing, but serves as a ``stopper''. + +\yskip\noindent The character tokens |' '|~and~|'~'|, together with the +sequence of tokens from |break_space| to |big_backup| form a hierarchy, in +the sense that on output any consecutive sequence of such tokens is +equivalent to their maximum, except that |big_force| and |backup| combine to +|big_backup|. To facilitate the computation we added |space|~and~|tilde| to +the enumeration below, that overlay |opt|~and~|flush_left| respectively +(this causes no problems, since |space|~and~|tilde| are not used as token +values). Formally, we define a partial ordering on the set |{' ', '~', +break_space, force, big_force, backup, big_backup }| that differs from the +linear order in which they were listed only by the fact that |big_force| and +|backup| are incomparable; then any consecutive list of such tokens is +replaced by their least upper bound. + +@< Typedef and enum... @>= +enum @/ +{ cancel=UCHAR_MAX+1,/* the following 9 items should remain in this order */ + indent, outdent, opt, flush_left, break_space, force, big_force, + backup, big_backup, @/ + relax, @/ + space=opt, tilde=flush_left @/ +}; + +@ The memory management for tokens follows a simple block regime. +The source file is divided into blocks, and whenever a block is entered, +the current states of the |text_mem| and~|tok_mem| are marked, after +which they will gradually get filled up further; at the end of the block +all memory used during the block is released by resetting the pointers +into these arrays to the values they had on block entry. There are three +kinds of blocks: the global block, which is filled during initialisation and +is never released, the section blocks, which correspond to each individual +section with a non-empty \Cee~part (or to a macro or format definition), +and the inner blocks, which correspond to each `\pb' contained in the +\TeX~part of a section or in a module name (but not in a comment). Because +nesting is at most three blocks deep (for `\pb' inside module names) a +two-element stack will suffice to hold the saved markers (nothing needs to +be saved for the global block), and we can address these elements directly +without a stack pointer since we know at which level we are. In fact the +section blocks butt together, and nothing is added to the global block +except at initialisation time, so that each section block starts in the same +state. Therefore, if we call |enter_block(0)| after initialisation to record +this state, then there is no need to call it any more, and it suffices to +call |leave_block(0)| each time upon leaving a section. + +Scraps follow a different regime than texts and tokens, since they must be +assembled into a single contiguous sequence before each translation, and can +be discarded when the translation is over. In particular the scraps used to +parse `\pb' within a comment can and must be released before reading in the +\Cee~text following the comment, but the tokens and texts formed while +translating the comment must remain until they are output. + +@d enter_block(i) save[i].txt=text_ptr, save[i].tok=tok_ptr; +@d leave_block(i) text_ptr=save[i].txt, tok_ptr=save[i].tok; + +@< Global variables @>= +struct {@; text_pointer txt; token_pointer tok; } save[2]; + +@ The conversion of input tokens as obtained by |get_next| into scraps +that can be processed by the parser is mainly handled by the function +|C_read|, which is analogous to the |C_xref| routine used during Phase~I. +Like |C_xref|, the function |C_read| takes a boolean argument telling +whether it is processing `\pb'; it starts with the current value of +|next_control| and it uses the operation |shift| repeatedly to read +\Cee~text until encountering a terminating token. Also like |C_ref|, the +initial conditions |next_control=='|'| or |next_control==end_comment| will +not lead to immediate termination; in these cases the first time through the +|while| loop will have no effect. + +@c +void C_read (boolean inner) /* creates scraps from \Cee\ tokens */ +{ while (next_control<format || next_control==module_name && inner) + { @<Append the scrap appropriate to |next_control|@> + if (shift()=='|' && inner + || next_control==begin_comment || next_control==end_comment) return; + } +} + +@ Many input tokens are completely determined by the value returned from +|get_next|; we have arranged it that such a value is always less than +|ignore|. For those tokens we will install corresponding scrap at initialisation +time in an array |token_trans|, so that when such a token comes along, we +can simply copy the scrap from |token_trans| into scrap memory. + +@< Global variables @>= +scrap token_trans[ignore]; + +@~Since the scraps in |token_trans| contain pointers into |tok_mem|, that +array cannot be initialised statically; rather we do the initialisation +dynamically based on information stored statically. For each token three +kinds of information are supplied: the category of its translation, a +string of characters giving the translation itself, and an indication for +its the |mathness| (whether the translation must or must not occur in math +mode, or whether both are allowed). The actual initialisation values will +be given later. + +@< Set initial values @>= +{ static struct {@; short tok; eight_bits cat, mathness; char* tr; } + trans_ini [] = @/{ @< Initialiser for |trans_ini| @>@;@; };@/ + int i,n=array_size(trans_ini); + scrap o={ insert, 5*maybe_math, NULL }; /* completely inert scrap */ + + for (i=0; i<n; ++i) + { scrap* p=&token_trans[trans_ini[i].tok]; + p->cat=trans_ini[i].cat; p->mathness=5*trans_ini[i].mathness; + app_str(trans_ini[i].tr); p->trans=text_ptr; freeze_text(); + } + @< Install the translations of tokens involving line breaks @> + + o.trans=text_ptr; freeze_text(); /* empty translation */ + for (i=0; i<=UCHAR_MAX; ++i) /* clear all remaining tokens */ + if (token_trans[i].cat==0) token_trans[i]=o; + enter_block(0); /* fix tokens; will be restored after each section */ +} + +@ Having installed |token_trans|, the number of cases in the +switch statement below is greatly reduced. + +@< Append the scr... @>= +{ check_scrap(); check_toks(6); /* `\.{\\hbox\{}' */ + switch (next_control) + { case string: case constant: case verbatim: + @< Append a string or constant @> @+ goto done; + case TeX_string: @< Append a \TeX\ string scrap @> @+ goto done; + case identifier: @< Append an identifier scrap @> @+ goto done; + case module_name: @< Append a module name scrap @> @+ goto done; + case start_preproc: + @< Append a scrap starting a preprocessing directive @> @+ goto done; + case refer: + err_print("! You can't use `@@#' in C text"); /*fall through */ + case ignore: case begin_comment: case end_comment: + case xref_roman: case xref_wildcard: case xref_typewriter: + case xref_mark: goto done; + @\@< Cases necessary for proper parsing of typedefs @> + case '|': @+ if (inner) goto done; /* skip initial `\.\v' of `\pb' */ + } + *scrap_ptr=token_trans[next_control]; /* fixed scrap for this input token */ + @< Possibly scoop up some dangling output tokens in compatibility mode @> + ++scrap_ptr; /* incorporate the scrap */ + done: {} +} + +@ When we need to be sure there is enough space to store |n|~more tokens, +we say |check_toks(n)|; when a scrap has to be appended we invoke +|check_scrap|, and when a text is to be formed otherwise than by +|pack_scrap|, we invoke |check_text|. The key points in the program where +we make such checks is when reading in \Cee~text in the functions |C_read| +and |outer_read|, and when translating the text in |reduce|. At these points +we make sure that there is enough room to spare so that in cases where +explicit tokens occasionally need to be appended (e.g., by |app_str|) no +test is necessary. When we cannot be sure about this, for instance because +an indefinite number of tokens is appended, we use |app_tok| or +|app_char_tok| instead of |app|; after this it will in fact be safe to do +one additional |app|. + +@d check_toks(n) @+ if (tok_ptr>tok_mem_end-n) + overflow("token"); @.token capacity exceeded@> @+ else @; +@d check_text() @+ if (text_ptr>=text_mem_end-1) + overflow("text"); @.text capacity exceeded@> @+ else @; +@d check_scrap() @+ if (scrap_ptr>=scrap_info_end) + overflow("scrap"); @.scrap capacity exceeded@> @+ else check_text() @; + +@ As was just explained, we can use |app| rather than |app_char_tok| here. + +@c +void app_str(char* s) @+{@; while(*s!='\0') app(*s++); } + +@ In long strings we insert a discretionary break every 20~characters, so +that \TeX\ is less likely to run into problems, especially if the string +occurs in the \TeX~part of a section; if we are directly after a backslash +however, we postpone the break since otherwise the quote escaping the line +break would appear to be escaped itself. Many of the special characters in +a string must be prefixed by `\.\\' so that \TeX\ will print them properly. +In the case of constants however, this `\.\\' converts marker characters +that were inserted during scanning into control sequences used in formatting +the constant. +@^special string characters@> + +@< Append a string or... @>= +{ int count = -1; /* characters remaining before string break */ + + if (next_control==constant) app_str("\\T{"); @.\\T@> + else if (next_control==string) {@; count=20; app_str("\\.{"); } @.\\.@> + else app_str("\\vb{"); @.\\vb@> + + while (id_first<id_loc) + { if (count--==0) /* insert a discretionary break in a long string */ + if (id_first[-1]=='\\') count=0; /* no break after backslash */ + else {@; check_toks(2); app_str("\\)"); count = 20; } @.\\)@> + if (strchr(" \\#%$^{}~&_",*id_first)!=NULL) app('\\'); +@.\\\ @> @.\\\\@> @.\\\#@> @.\\\%@> @.\\\$@> @.\\\^@> +@.\\\{@> @.\\\}@> @.\\\~@> @.\\\~@> @.\\\&@> @.\\\_@> + app_char_tok(*id_first++); + } + app('}'); + if (next_control==verbatim) pack_scrap(insert,maybe_math); + else pack_scrap(expression,yes_math); +} + +@ A \TeX~string is boxed and copied without further ado; undoubling of any +\:@@ has already been done by |get_control_text|. In compatibility mode we +do not however produce a scrap, but rather leave the output tokens produced +to be picked up by the next scrap appended; this makes +|dangling_tokens()| hold. In order to make sure that this next scrap +exists, we will append a dummy scrap in |translate| if necessary. +@:TeX string@> + +@d dangling_tokens() (compatibility_mode && tok_ptr>*text_ptr) + +@< Append a \TeX\ string scrap @>= +{ app_str("\\hbox{"); + while (id_first<id_loc) app_char_tok(*id_first++); + app('}'); + if (!compatibility_mode) pack_scrap(expression,maybe_math); +} + +@ Input tokens that have prefabricated scraps will not automatically incorporate +any output tokens left behind by the code in section@#TeX string@> since they do +not call |freeze_text|, so we must take some action to avoid that +\TeX~strings will appear too late in the output. When the code below is +encountered, the fixed scrap has been copied to |*scrap_ptr|. Its category +and mathness are correct, but we may need to prepend tokens to its +translation; this is done by appending that translation to the dangling +tokens, wrapping them up together, and replacing the original translation by +a pointer to the combined result. + +@< Possibly scoop up... @>= + +{@; if (dangling_tokens()) + {@; app_trans(scrap_ptr); scrap_ptr->trans=text_ptr; freeze_text(); } +} + +@ It is during the conversion of identifiers to scraps that their |ilk| +plays a crucial r\^ole. Ordinary identifiers, and those with |ilk| equal to +|TeX_like| or |NULL_like|, will get the category |expression|, and are set in +math mode except in the case of |TeX_like| identifiers. If the |ilk| specifies +some reserved word on the other hand, that |ilk| becomes the category of the +identifier, determining its behaviour during parsing. The translation of +these reserved words is done using |res_flag| rather than |id_flag|, as a +result of which they will be printed in boldface; they get mathness +|maybe_math|, indicating that they can be set equally well inside and +outside math mode. Identifiers whose |ilk| is |type_defined|, |const_like|, +or |typedef_like| will become reserved words with category |int_like|. These +special |ilk| values have served their purpose during the scanning of +typedef declarations; this involves subtle manoeuvres that will be explained +later. + +@< Append an identifier scrap @>= +{ id_pointer p=cur_id; int cat=p->ilk; + @< Track identifiers relevant to typedef; + maybe change |cat| from |int_like| to |expression| @> + if (cat==normal || cat==TeX_like || cat==NULL_like) + { app(id_flag+id_index(p)); + pack_scrap(expression + , cat==TeX_like && !compatibility_mode ? no_math : yes_math); + } + else + { if (cat==type_defined || cat==const_like || cat==typedef_like) + cat=int_like; + app(res_flag+id_index(p)); pack_scrap(cat,maybe_math); + } +} + +@ For bad module names (e.g., an ambiguous prefix) an error has already +been reported, and they are silently suppressed from the output. + +@< Append a module name scrap @>= +{@; if (cur_mod!=NULL) + app(mod_flag+mod_index(cur_mod)), pack_scrap(mod_scrap,yes_math); +} + +@ We tested in Phase~I that `\.\#' is followed by an identifier or by a +newline (which will cause |get_next| to return |end_preproc|). In the former +case we incorporate the identifier into the scrap for the preprocessor +directive; in the latter case the |lproc| scrap will just contain the +`\.\#', but since we already scanned the following |end_preproc|, we append +the corresponding |rproc| scrap as well. + +@< Append a scrap starting a preprocessing directive @>= +{ app(force); app(flush_left); app_str("\\&\\#"); + if (shift()==identifier) + {@; app(res_flag+id_index(cur_id)); pack_scrap(lproc,no_math); } + else if (next_control==end_preproc) + @/{@; pack_scrap(lproc,no_math); + check_scrap(); *scrap_ptr++=token_trans[end_preproc]; + } + else confusion("no identifier after `#'"); + @.no identifier after `\#'@> + +} + +@ 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, which is useful since we convert comments to single +scraps with help of |C_translate| while building the scrap sequence for the +surrounding piece of \Cee~text. + +@c +text_pointer C_translate(void) +{ text_pointer p; scrap_pointer save_base=scrap_base; + scrap_base=scrap_ptr; + C_read(true); /* get the scraps together */ + if (next_control != '|') err_print("! Missing `|' after C text"); + @.Missing `|'...@> + p=translate(); /* make the translation */ +#ifdef STAT + if (scrap_ptr>max_scr_ptr) max_scr_ptr=scrap_ptr; +#endif + scrap_ptr=scrap_base; scrap_base=save_base; return p; +} + +@ The function |outer_read| is to |C_read| as |outer_xref| is to |C_xref|: +it constructs a sequence of scraps for \Cee~text until +|next_control>=format|, taking care of embedded comments. It is called +between each occurrence of \:d, \:f, \:h, or a module name, which makes it a +convenient place to test whether the memory arrays have enough spare room to +cater for the stuff that could be needed for processing the next such token; +the most demanding requirements are for a module name heading the \Cee~part +of a section. These tests must be made outside the main loop of |outer_read|. + +We use that if |next_control==end_comment| when |C_read| is called, +this value is effectively ignored. + +@c +void outer_read (void) /* makes scraps from \Cee\ tokens and comments */ +{ while (next_control<format) + if (next_control!=begin_comment) C_read(false); + else @< Read a comment, and convert it into a scrap @> + check_scrap(); check_toks(11); /* `\.{\$\\4$m$\\PE\{\}\$$f$}' */ +} + +@ Since the call on |C_translate| used to process `\pb' inside comments will +itself create tokens, the token sequence for the comment under construction +must be wrapped up each time this happens, and a token referring to that +initial segment contributed as first new item after the translation is made. +Tests on the availability of two more tokens and a text must be made inside +the loop that incorporates successive `\pb' fragments; the space for tokens +outside these fragments is tested within |scan_comment|. + +@< Read a comment, and convert it into a scrap @>= +{ boolean one_liner=loc[-1]=='/'; int bal=0; /* brace level in comment */ + typedef_tracking(false); + check_scrap(); check_toks(4); + app(cancel); app_str(one_liner ? "\\SHC{" : "\\C{"); @.\\C@> @.\\SHC@> + while ((next_control=scan_comment(&bal,one_liner))=='|') + { text_pointer p=text_ptr, q=(freeze_text(), C_translate()); + check_toks(7); + app_tok(text_flag+text_index(p)); + if (compatibility_mode) app_str("\\PB{"); @.\\PB@> + app(inner_text_flag+text_index(q)); + if (compatibility_mode) app('}'); + check_text(); + } + app_char_tok('}'); app(force); pack_scrap(insert, no_math); + /* the full comment becomes a scrap */ + typedef_tracking(true); +} + +@ The function |do_C| does the scanning, translation, and output of +\Cee~text within `\pb' brackets. It is called during the scanning of the +\TeX~part of a section and during the output of module names. As we have +seen, this function is not called when processing comments, where +|C_translate| is used instead, because no direct output should be produced +at such times. + +@c +void do_C (void) /* read, translate, and output \Cee~text in `\pb' */ +{ enter_block(1); + if (compatibility_mode) out_str("\\PB{"); @.\\PB@> + make_output(C_translate(),inner); /* output the list */ + if (compatibility_mode) out('}'); +#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 + leave_block(1); /* forget the tokens */ +} + +@ The function |finish_C| outputs the translation of the current scraps, +preceded by the control sequence `\.{\\B}' and followed by the control +sequence `\.{\\par}'. It also restores the token and scrap memories to their +state as immediately after initialisation. + +@c +void finish_C (void) +{ out_str ("\\B"); @.\\B@> + make_output(translate(),outer); + 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 + leave_block(0); scrap_ptr=scrap_info; + /* forget the tokens and the scraps */ +} + +@ The function |footnote| gives \xr. information about +further definitions of a module name (if |flag==def_flag|), +about citations of a module name (if |flag==cite_flag|), +or about the uses of a module name (if |flag==0|). +It assumes that |*p| points to the first \xr. entry +of interest, and it leaves |*p| pointing to the first element +not printed (possibly the sentinel with |num==0|). +Typical outputs are `\hbox{\.{\\Q 2001.}}', +`\hbox{\.{\\Us 370\\ET1009.}}'\ETs `\hbox{\.{\\As 8, 27\\*, 51\\ETs64.}}'. + +@c +void footnote (xref_pointer* p,sixteen_bits flag) +{ if ((*p)->num<=flag) return; + finish_line(); out('\\'); + out(flag==0 ? 'U' : flag==cite_flag ? 'Q' : 'A'); @.\\A@> @.\\Q@> @.\\U@> + *p=list_refs(*p,flag); + out('.'); +} + +@~The function |list_refs|, which does the main work for |footnote| and is +also used to produce the text replacing \:\#, distinguishes three cases, +according as the number of relevant \xr.s is one, two, or more than two. The +function always produces at least one \xr.: it is never called with +|x->num<=flag|. The value of |x| after traversing the references is +returned, for the benefit of |footnote|. + +@c +xref_pointer list_refs (xref_pointer x,sixteen_bits flag) +{ xref_pointer q=next_xref(x); /* second element in \xr. list */ + if (q->num>flag) out('s'); + /* use `\.{\\As}', `\.{\\Qs}' or `\.{\\Us}' */ + @.\\As@> @.\\Qs@> @.\\Us@> + out(' '); + while (out_sec_nr(x->num&num_mask),x=next_xref(x),x->num>flag) + if (next_xref(x)->num>flag) out_str(", "); /* |x| is not the last */ + else + { out_str("\\ET"); /* next number printed will be the last */ + if (x!=q) out('s'); /* `\.{\\ETs}' for the last of more than two */ + } @.\\ET@> @.\\ETs@> + return x; +} + + +@* The problem of typedef declarations. @:title@> +We now consider how |typedef| declarations are processed. It is a slightly +problematic matter, since it involves the syntactic structure of the +program, but we are doing only lexical analysis during Phase~I. +Nevertheless, we don't want to delay recognition of |typedef| declarations to +Phase~II, since this would restrict the user's freedom of ordering the +sections so that all typedef declarations remain before any of their uses, +and it would make it cumbersome to mention a typedef identifier in the +commentary directly before its definition. Our approach will be to specify +simple rules that pinpoint the identifiers which are subject to a |typedef| +definition in any syntactically correct program; we don't care too much +about strange behaviour in the presence of syntax errors, and also assume +some basic decency on the part of the user, e.g., the identifier should +occur in the same section as the |typedef| token. + +Obviously there is little to care about until a |typedef| token +(recognisable by its |ilk|) comes along, but then it becomes tricky. +Ordinarily the identifier being defined is the first non-reserved word that +follows, but |struct| (and |union| and |enum|) tokens complicate the +situation; also there may be more than one identifier subject to the +|typedef|, separated by commas from each other. Because of |struct| we must +keep track of the nesting of braces, and because the mentioned commas should +be distinguished from those occurring in the parameter specifications of +functions, we should also keep track of parenthesis nesting. A semicolon at +the proper level of brace nesting signals the end of a |typedef| declaration. + +Fortunately |typedef| declarations cannot be nested inside each other, +so we can use global variables to keep the proper counts. Three integer +counters are used, two for the nesting levels of braces and parentheses, +which are set to~0 whenever a |typedef| is scanned and properly maintained +thereafter, and a master counter which determines if we are paying +attention at all. + +@d typedef_tracking(b) (typedef_master += b ? 5 : -5) + +@< Global... @>= +local int typedef_master=-5; /* tracking disabled outside \Cee~parts */ +local int brace_level, par_level; + +@ The master counter is ordinarily equal to~0 when tracking is enabled, and +negative if it is disabled. When it is~0 and a |typedef| is seen, it is +raised to~2, after which an |int_like| or |type_defined| identifier will +further raise it to~4, indicating that any |normal| identifier coming along +at the same brace level will be made |type_defined|. When that happens the +master counter drops to~1, indicating that it can still be rekindled by a +comma at the proper parenthesis level. We must be prepared to see more than +one typedef for the same identifier, as we may have scanned a header file +before seeing the source that produced that file, so we also let the master +counter drop to~1 if it was~4 and an identifier that is already +|type_defined| is seen. If however following the |typedef|, when the master +counter is~2, a |struct_like| identifier is seen, the master counter is +raised only to~3, so that a following identifier will not be made +|type_defined|, but rather pass the honour on by setting the master counter +to~4; this also happens when instead of an identifier a left brace is seen. +Finally, a semicolon at the right brace level will return a positive value +of the master counter to~0. + +@< Keep track of tokens relevant to |typedef| declarations @>= +{ if (typedef_master==0 && + next_control==identifier && cur_id->ilk==typedef_like) + @/{@; typedef_master=2; brace_level=par_level=0; } + else if (typedef_master>0) switch(next_control) + { case identifier: + if (brace_level==0) + if (typedef_master==2) + { if (cur_id->ilk==int_like || cur_id->ilk==type_defined) + typedef_master=4; + else if (cur_id->ilk==struct_like) typedef_master=3; + } + else if (typedef_master==4) + { if(cur_id->ilk==normal||cur_id->ilk==type_defined) /* this is it */ + cur_id->ilk=type_defined, typedef_master=1; + } + else if (typedef_master==3) typedef_master=4; + break; + case '{': @+ + if (brace_level++==0 && typedef_master==3) typedef_master=4; @+ break; + case '}': --brace_level; break; + case ',': @+ + if (typedef_master==1 && par_level==0) typedef_master=4; @+ break; + case '(': ++par_level; break; + case ')': --par_level; break; + case ';': @+ if (brace_level==0) typedef_master=0; + } + if (C_plus_plus) + @< Take action to mark identifiers following \&{class} as |type_defined| @> +} + +@ In \Cpp, any identifier that follows \&{class} (or |struct| of |union|) is +considered as a typedef identifier, i.e., every time some \&{class~x} is +encountered, an implicit `|typedef| \&{class~x~x}' is assumed. + +@< Take action to mark identifiers following \&{class}... @>= +{ static boolean class_seen=false; + if (class_seen) + { if (next_control==identifier && cur_id->ilk==normal) + cur_id->ilk=type_defined; + class_seen=false; + } + else if (next_control==identifier && cur_id->ilk==struct_like) + class_seen=true; +} + +@ We now consider |typedef| declarations in Phase~II. In Phase~I we have set +the |ilk| of the defined identifiers to~|type_defined|, which will make them +behave as |int_like|; although this works fine everywhere else, it thwarts a +correct parse of their |typedef| declaration itself during Phase~II. In +fact, according to the \caps{ANSI}/\caps{ISO}~\Cee\ syntax, the identifier +being declared in a typedef declaration should not be considered to be a +\\{typedef-name}, since that would make the declaration unsyntactical; this +is justified by the fact that the typedef identifier only comes into scope +after the declaration is completed. This is not a problem for single-pass +compilers, but it is for us: we have recorded the typedef declaration on the +first pass, and it will be active during the entire second pass, since we +cannot delimit the declaration to its proper range (we are not processing +the code in the same order that the \Cee~compiler will, and for the +commentary parts of sections, the concept of range does not even apply). To +attempt to write the grammar in such a way that it will accept typedef +declarations in which the defined identifier is |int_like| would be very +difficult, since without the help of some rather remote context, a +declarator of this kind can not always be distinguished form an abstract +declarator; compare the declaration `\hbox{|typedef char *(example[4]);|}', +which declares |example| to specify the type ``array of 4 pointers to +character'', with a valid declaration `|void f(example[3]);|' that might +follow it, declaring |f| as a function that takes as an argument an array of +3 such |example| objects (in fact the argument will be passed as a pointer to +|example|, of course). + +So, rather than solving the problem in a syntactic way, we stoop down to +emulating a one-pass system by setting the |category| of the defining +occurrence of an identifier in a typedef declaration explicitly to +|expression|, despite the fact that its |ilk| is |type_defined|. The defining +occurrence is located by the same lexical means used in Phase~I, in fact, by +using the same intricate succession of states of |typedef_master| that was +used there. A difference is that at the {\sl moment supr\`eme\/} the +identifier found is now |type_defined| rather than |normal|. The fact that +this identifier is not necessarily the only or first |type_defined| +identifier in the declaration, and that is has to be recognised in a +left-to-right pass, may explain some of the details of our code, for +instance why |const_like| had to be distinguished from both |int_like| and +|type_defined|; one may compare `\hbox{|typedef unsigned long int * const +example;|}' with `\hbox{|typedef const example * examp2;|}'. + +With respect to the code for Phase~I, there is also a slight difference in +the way the code is hooked into the program, since the function |C_read| +already contains a |switch| on the value of |next_control|, which has a +special case for identifiers. So let us first consider the identifier cases +(including reserved words). + +@f example scrap /* pretend |example| is |type_defined| */ +@f examp2 example /* and |examp2| is another such identifier */ + +@< Track identifiers... @>= +{ if (typedef_master==0 && cat==typedef_like) + typedef_master=2, brace_level=par_level=0; + else if (typedef_master>0 && brace_level==0) + if (typedef_master==2) + { if (cat==int_like || cat==type_defined) typedef_master=4; + else if (cat==struct_like) typedef_master=3; + } + else if (typedef_master==4 && cat==type_defined) /* this is it */ + cat=expression, typedef_master=1; + else if (typedef_master==3) typedef_master=4; +} + +@ And here are the relevant cases of non-identifiers. + +@< Cases necessary for proper parsing of typedefs @>= + +case '{': @+ + if (typedef_master>0 && brace_level++==0 && typedef_master==3) + typedef_master=4; + @+break; +case '}':@+ if (typedef_master>0) --brace_level; @+break; +case ',':@+ if (typedef_master==1 && par_level==0) typedef_master=4; @+break; +case '(':@+ if (typedef_master>0) ++par_level; @+break; +case ')':@+ if (typedef_master>0) --par_level; @+break; +case ';':@+ if (typedef_master>0 && brace_level==0) typedef_master=0; @+break; + + +@i parser.w +@i rules.w + + +@* Output of tokens. @:title@> +Now that we have treated the highest level of processing by \.{\me.}, we +shall have to descend again to the level of character strings, which +eventually have to written to the output file. Our first concern is to +linearise the multi-layered token lists into a sequence of output tokens. +The output of special layout tokens is affected by whether or not they were +generated from within `\pb', so there are two modes of output: during output +of `\pb' we are in |inner| mode, and otherwise in |outer| mode. +A switch from |outer| to |inner| mode can occur in the middle of a text, +namely for `\pb' fragments inside comments; this is indicated by the fact +that the reference to the translation of the fragment is tagged with +|inner_text_flag|. Apart from the fact that the mode is set to |inner| +during the output of such subtrees, no traces of the tree structure of the +internal representation of texts are left after linearisation. + +Linearising texts is therefore a straightforward process, that is easy to +implement using a stack. In the linearised stream of tokens certain +sequences of tokens, particularly layout tokens, have to be considered +together, e.g., |cancel| will remove any adjacent line-breaking tokens. No +line-breaking tokens will occur at either end of the output of a complete +text either; if a line break is required there, the caller of the output +routines will supply it explicitly. For identifiers, reserved words and +module names, the transformation of a single token into a sequence of +characters is handled by separate functions to be discussed later. For +module names the transformation may result in a recursive call of the output +routines via the processing of `\pb' fragments by~|do_C|, but this recursion +in never more than one level deep, since such fragments cannot contain +module names. +@^recursion@> + +@< Prototypes @>= +void out_identifier (id_pointer); +void out_keyword (id_pointer); +xref_pointer out_module_name(mod_pointer); + +@ The stack that is used to keep track of token lists at different levels +of output is similar to the one used in |CTANGLE|. Entries have three parts: +for the token list at each level |tok_field| and |end_field| record where we +are respectively where we should stop, and |mode_field| records the output +mode. The current values of these quantities are referred to quite +frequently, so they are stored in a separate place, and are called +|cur_tok|, |cur_end|, and |cur_mode|. + +@d cur_tok cur_state.tok_field /* location of next output token in |tok_mem| */ +@d cur_end cur_state.end_field /* current ending location in |tok_mem| */ +@d cur_mode cur_state.mode_field /* current mode of interpretation */ + +@<Typedef...@>= +typedef enum { inner, outer } mode; +typedef struct +{ token_pointer tok_field; /* present location within token list */ + token_pointer end_field; /* ending location of token list */ + mode mode_field; /* interpretation of control tokens */ +} output_stack_element, *stack_pointer; + +@ The stack grows upwards with the global variable |stack_ptr| pointing to +the first vacant location above the top of the stack. The entry |stack[0]| +at the bottom of the stack is not used; when |stack_ptr| points to it then +|cur_state| itself has become invalid and the output process is completed. +Therefore we can use |stack[0]| to store |cur_state| in. + +@d cur_state stack[0] /* the currently active state variables */ +@d stack_end (&stack[stack_size]) /* end of |stack| */ + +@<Global...@>= +output_stack_element stack[stack_size]; /* info for non-current levels */ +stack_pointer stack_ptr=&stack[0]; + /* first unused location in the output state stack */ +#ifdef STAT +stack_pointer max_stack_ptr = stack; /* largest value assumed by |stack_ptr| */ +#endif + +@ To insert token list |p| into the output, the function |push_level| is +called; it saves the old level of output and gets a new one going. +Conversely, the macro |pop_level| restores the conditions that were in force +when the current level was begun. The value of |cur_mode| is not changed by +|push_level|, but it might be changed explicitly directly after the call; if +so this setting will remain in effect until the matching invocation of +|pop_level|. At the beginning of |make_output|, a call to |push_level| is +made to put the root text into the output stream. If this is not a recursive +call to |make_output|, the old (undefined) value of |cur_state| will be +``saved'' into |stack[0]|, which is |cur_state| itself; although this is +redundant, no harm is done. + +@d pop_level() cur_state = *--stack_ptr +@c +void push_level (text_pointer p) /* suspends the current level */ +{ if (stack_ptr==stack_end) overflow("stack"); @.stack capacity exceeded@> + *stack_ptr++=cur_state; +#ifdef STAT + if (stack_ptr>max_stack_ptr) max_stack_ptr=stack_ptr; +#endif + cur_tok=text_begin(p); cur_end=text_end(p); +} + +@ The function |make_output| traverses the nested token lists using the +stack, and producing the corresponding output. Since it can be called +recursively, the initial value of |stack_ptr| is saved in |stack_bot|; we +return from |make_output| when |stack_ptr| drops back again to this level. +When we encounter tokens marked with |id_flag|, |res_flag| and |mod_flag|, we +respectively call |out_identifier|, |out_keyword| and |out_module_name|. +As mentioned above, the last of these may result in a recursive call to +|make_output| via |do_C|; because calls of |do_C| also involve +lexical scanning, the values of |next_control|, |cur_id| are saved, and +restored when |make_output| is complete. +@^recursion@> + +@c +void make_output(text_pointer t,mode m) /* output a complete text */ +{ int save_next_control=next_control; id_pointer save_cur_id=cur_id; + stack_pointer stack_bot=stack_ptr; token state=cancel; + push_level(t); cur_mode=m; + do + if (cur_tok==cur_end) pop_level(); + else + { token a= *cur_tok % id_flag; + switch (*cur_tok++/id_flag) + { + @\@< Cases 1, 2, 3: identifiers, reserved words and module names @> + case 4: push_level(text_at(a)); break; + case 5: push_level(text_at(a)); cur_mode=inner; break; + case 0: @< Output the character or format control |a| @> + } + } + while(stack_ptr>stack_bot); + @< Complete any unfinished work at the end of the translation @> + next_control=save_next_control; cur_id=save_cur_id; +} + +@ To handle the proper interaction of adjacent output tokens, we keep track +of an integer |state| variable to record that a possibly unfinished sequence +is being processed. The state is mainly affected by the output of format +control tokens like |break_space|, so for convenience we often set the state +equal to the value of such a token. If |state==0| the most recent token was +an ordinary one, and no special action is called for. If |state| has one of +the values |space|, |tilde|, |break_space|, |force|, |big_force|, |backup|, +or |big_backup|, it is the upper bound in the sense mentioned before of a +sequence of such tokens that has recently passed (where |space|~and~|tilde| +represent the tokens |' '|~and~|'~'|, respectively). We call such states +`white-space states'; when an ordinary character comes along in a +white-space state, the token recorded in |state| is first output. + +@< Output white space if specified by |state| @>= +{ if (state>=space) + if (state<break_space) out(state==space ? ' ' : '~'); + else + { out('\\'); + if (state<backup) out(state-break_space+'5'); + /* `\.{\\5}', `\.{\\6}', or `\.{\\7}' */ @.\\5@> @.\\6@> @.\\7@> + else out(state-backup+'6'),out_str("\\4"); + /* `\.{\\6\\4}' or `\.{\\7\\4}' */ @.\\6@> @.\\7@> @.\\4@> + finish_line(); + } + state=0; +} + +@ The following three cases now become easy. +@< Cases 1, 2, 3... @>= +case 1: @< Output white space... @>@+ out_identifier(id_at(a)); break; +case 2: @< Output white space... @>@+ out_keyword(id_at(a)); break; +case 3: @< Output white space... @>@+ out_module_name(mod_at(a)); break; + +@ Whether or not a forced break is required at the end of the translation is +determined by the function calling |make_output|, not by the code being +translated; therefore we do not invoke |@< Output white space... @>| at the +end, and if a white-space state was set, it will sink silently into +oblivion. An exception is made for |big_force| or |big_backup|, for which we +output `\.{\\Y}' so that the extra vertical white space (which must have been +inserted explicitly by a \:) control code) will not vanish, for instance +when it occurs between macro definitions. + +@< Complete any unfinished work... @>= +{@; if (cur_mode==outer && (state==big_force || state==big_backup)) + out_str("\\Y"); +} + +@ White-space format controls operate by modifying |state| when appropriate, +as described above. The same holds basically for |' '| and |'~'|, but these +characters can also occur in ordinary text inside comments, where they must +not be contracted, so we take some care that in such a position (where +|cur_mode==outer| holds), they are output as ordinary characters. In +white-space states |opt| and the digit following it are ignored. Like the +white-space format controls, the token |cancel| also sets |state| to its own +value, but it ignores the value it previously had. In this state any spaces +and format control tokens except |indent|, |outdent| and |flush_left| are +ignored. We also have set |state==cancel| at the beginning of |make_output|, +so that there will be no forced break or white space at the beginning of the +translation. + +The tokens |indent| and |outdent| are output directly without altering +|state|; effectively this means that they will be moved in front of any +white-space format controls that preceded them. This is important because +the format controls that are explicitly inserted by codes like \:/ will +stick to the token to their left, so that any indentation changes generated +by the syntax rules at the same point will necessarily come {\it after\/} +them, while in fact these indentation changes must be allowed to affect the +indentation at explicitly inserted line breaks. With the exceptions noted +above, |opt| and |flush_left| are transmitted like normal tokens. + +When |cur_mode==inner| things are a bit different. The tokens |indent|, +|outdent|, and |flush_left| are completely ignored, and all white-space +tokens except |'~'| are treated like |' '|, i.e., they set |state=space|. +Optional breaks are produced using `\.{\\0}' rather than `\.{\\3}' to avoid +a ragged right margin within a paragraph. + +@< Output the character or format control |a| @>= +{ switch (a) + { case relax: @< Output white space... @>@+ break; + case cancel: state=cancel; break; + case indent: case outdent: + if (cur_mode==outer) {@; out('\\'); out(a-indent+'1'); } + break; @.\\1@> @.\\2@> + case opt: + { int digit=*cur_tok++; + if (state==0) + {@; out('\\'); out(cur_mode==outer ? '3' : '0'); out(digit); } + @.\\3@> @.\\0@> + break; + } + case flush_left: + if (cur_mode==outer) + {@; @< Output white space... @> out_str("\\8"); } @.\\8@> + break; + case big_force: case backup: + if (a+state==big_force+backup) a=big_backup; /* fall through */ + case break_space: case force: case big_backup: + if (cur_mode==inner) a=space; + up_state: + if (state!=cancel && state<a) state=a; + break; + case ' ': case '~': + if (cur_mode==inner) {@; a= a==' ' ? space : tilde; goto up_state; } + if (state==cancel || state>=break_space) break; + /* else fall through */ + default: @< Output white space... @>@+ out(a); + } +} + +@* Low-level output routines. @:title@> +We now come to the functions that write individual tokens and characters. +We start with a simple function to output a section number in decimal +notation. The number to be converted by |out_sec_nr| is known to be less +than |def_flag|, so it cannot have more than five decimal digits. If the +section was changed, we output `\.{\\*}' just after the number. + +@c +void out_sec_nr (int n) /* output a section number */ +{ char s[6]; + sprintf(s,"%d",n); out_str(s); + if (section_changed(n)) out_str ("\\*"); @.\\*@> +} + +@ Since we want to typeset identifiers such as \.{catch22} as |catch22|, which +is achieved by writing the string `\.{\$\\\\\{catch\}\_\{22\}\$}' to the +\TeX~file, it is useful to have an auxiliary function |out_id_part| that +will output a substring of an identifier (or reserved word), enclosing it in +braces if it contains more than one character. The substring is specified by +a pointer and a length count. A call to |out_id_part| for the full name +referred to by an |id_pointer p@;| is achieved by invoking |out_id_full(p)|. + + +@d out_id_full(p) out_id_part(name_begin(p),length(p)) + +@c +void out_id_part (char* s, int l) +{ boolean b=l!=1; + if (b) out ('{'); + while (--l>=0) {@; if (*s=='_') out ('\\'); out (*s++); } + if (b) out('}'); +} + +@ For index entries we do not wish to give underscores a special treatment, +so we define a function |out_index| to use in place of |out_id_full|. Index +entries of length~0 are discarded on input, so they never come here; for +index entries of length~1 we omit braces. + +In \LKC., there is no function like |out_index|, and the equivalent of +|out_id_part| is used instead, and as a consequence, underscores are +automatically escaped in index entries, but other special characters are +not. While there is no particular rhyme or reason to such a behaviour, user +have learned to adapt to it, and so, in an attempt at bug-to-bug +compatibility, we transfer control to |out_id_part| in compatibility mode. + +@c +void out_index (id_pointer p) +{ char* s=name_begin(p); boolean b= s[1]!='\0'; + if (compatibility_mode) {@; out_id_full(p); return; } + if (b) out ('{'); + out_str(s); + if (b) out('}'); +} + +@ The function |out_keyword| simply prefixes `\.{\\\&}' to the output of +|out_id_full|. + +@c +void out_keyword(id_pointer p) @+{@; out_str("\\&"); out_id_full(p); } +@.\\\&@> + +@ The function |out_identifier| formats ordinary identifiers (those that +were stored with |id_flag|). Yet there is some variation in the way these +are formatted. Identifiers whose |ilk| is |TeX_like| or |NULL_like| are +converted into control sequences. If only one alphabetic character is +present, it is set in math (rather than text) italic, otherwise if all +characters are either upper case letters or digits, the identifier is set in +typewriter type, and in the remaining, most common, case, it is set in text +italic. If math or text italic are used, a trailing sequence of digits in +the name, if present, is set as a subscript to the rest of the identifier. + +@c +void out_identifier (id_pointer p) +{ int k=length(p); eight_bits* ch=(eight_bits*)name_begin(p); +@/enum { ord, indexed, caps, single, indexed_single } kind; + if (p->ilk==TeX_like || p->ilk==NULL_like) @.\\NULL@> @.\\TeX@> + @/{@; + @< Output the identifier |p| in the form of a \TeX\ control sequence @> + return; + } + if (!compatibility_mode) + { do --k; while (isdigit((eight_bits)ch[k])); /* terminates because |!isdigit(ch[0])| */ + ++k; /* point to end of identifier without its index (if any) */ + } + @< Determine the |kind| of |p|, and possibly output some + leading characters @> + if (kind==indexed || kind==indexed_single) + { out_id_part(name_begin(p),k); /* main part */ + out ('_'); out_id_part(name_begin(p)+k,length(p)-k); /* subscript */ + } + else out_id_full (p); +} + +@ When an identifier is output in the form of a control sequence, +underscores are replaced by `\.x' so that they will become part of the +control sequence. If the |ilk| of the identifier was |TeX_like|, the control +sequence will be processed in horizontal mode with italic type selected; if +the |ilk| was |NULL_like|, it will be processed in math mode. + +@< Output the identifier |p| in the form of a \TeX\ control sequence @>= +{ if (p->ilk==TeX_like) out_str("\\\\{"); + out('\\'); @+ do out(*ch=='_' ? 'x' : *ch); while (*++ch!='\0'); + if (p->ilk==TeX_like) out('}'); +} + +@ For identifiers that consist of a single character, not counting any +trailing digits, that character is written in unadorned form, since it will +be processed in math mode, so that the math italic font will be used. We do +output a space before the character however, to eliminate the possibility +that it is captured by a control word at the end of the previous output. + +@< Determine the |kind|... @>= +if (k==1) {@; out(' '); kind= length(p)==1 ? single : indexed_single; } +else +{ int i=k; @+ + while (--i>=0) @+ if (!isupper(ch[i])&&!isdigit(ch[i])&&ch[i]!='_') break; + kind= i<0 ? caps : k<length(p) ? indexed : ord; + out('\\'); out(kind==caps ? '.' : '\\'); @.\\\\@> @.\\.@> +} + +@ The last function for output of tokens, |out_module_name|, represents the +most complicated aspect of such output, since up to here we have not paid +any attention to `\pb' constructions in module names. When these are present +they will need a complete treatment by the scanning, parsing and general +output routines described above. Now |out_module_name| does not involve +itself directly in such actions---they are contained in a call to +|do_C|---but we do have to be aware that we are in the midst of scanning and +output operations going on at a different level, and be careful not to +disturb them. The text between `\pb' will be placed at the end of the active +input buffer (this is why the buffer has size |long_buf_size| rather than +|buf_size|) and the translation process uses the end of the active |tok_mem| +area. + +On the other hand |out_module_name| is also used during Phase~III while +writing the list of module names (which implies that scanning and parsing is +not over at the end of Phase~II). We want |out_module_name| to behave +slightly differently in Phase~III however, by printing a full list of +section numbers in which the module is defined rather than just the first +one. Also, immediately after calling |out_module_name| we will need a +pointer to the sublist of \xr.s to sections in which the module is used, and +since this sublist starts at the end of the list of defining sections, we +might as well return it as result from |out_module_name|. + +@c +xref_pointer out_module_name(mod_pointer name) +{ xref_pointer x=name->xref; boolean file_module= x->num==file_flag; + if (file_module) x=next_xref(x); + out_str ("\\X"); @.\\X@> + if (x->num>=def_flag) + { out_sec_nr(x->num-def_flag); /* output the defining section number */ + if (phase==3) /* all of them in Phase III */ + while (x=next_xref(x), x->num>=def_flag) + out_str (", "), out_sec_nr(x->num-def_flag); + } + else out ('0'); /* section number `0' means `nowhere defined' */ + out (':'); if (file_module) out_str("\\.{"); @.\\.@> + @<Output the text of the module name@> + if (file_module) out_str ("}"); + out_str ("\\X"); + return x; +} + +@ Copying \TeX\ text from a module name to the output is fairly simple, but +we shouldn't forget to escape special characters if this is actually a file +name (which is typeset verbatim), even though they are probably quite rare. + +@< Output the text... @>= +{ char* k=name_begin(name),c; + while ((c=*k++)!='\0') + { if (file_module) @+{@; if (strchr(" \\#%$^{}~&_",c)!=NULL) out ('\\'); } + if (c=='@@' && *k++!='@@') @< Report illegal control code in module name @> + if (file_module || c!='|') out(c); + else + { char* save_loc=loc, *save_limit=limit; + @< Copy the \Cee\ text into the |buffer| array @> + do_C(); loc=save_loc; *(limit=save_limit)=' '; + } + } +} + +@ We haven't checked for illegal control codes in module names yet, so +we should report an error if we encounter one. + +@< Report illegal control... @>= +{@; print("\n! Illegal control code in module name"); print_mod(name); + @.Illegal control code...@> + mark_error(); +} + +@ Within `\pb' we should be aware of character and string constants, since +any `\.\v' occurring there is not the closing one. The variable |delimiter| +is zero outside such constants, otherwise it equals the delimiter that began +the constant. We copy the opening and closing `\.\v' into the buffer, so +that an error message that displays the whole buffer will look a little bit +sensible. We also add a space at the end (just like |get_line| does) so that +the closing `\.\v' cannot by accident be parsed as part of an operator such +as `\.{\v=}'. Putting |next_control='|'| completes the proper initial +conditions for calling |do_C|. We need not test for overflow of |buffer| +here, since we start filling it at position |limit| which is at most +|&buffer[buf_size-2]|, and add at most |longest_name| characters from the +module name, so that if worst comes to worst, the final space stored at the +end is at position |&buffer[long_buf_size-2]| leaving one more place after +|limit|, which is occasionally needed by |get_next|. + +@< Copy the \Cee\ text into... @>= +{ char delimiter='\0'; /* |'"'| or |'\''|, or |'\0'| outside of strings */ + next_control=*limit++='|'; loc=limit; + do + if ((c=*k++)=='\0') @< Report a runaway \Cee~text @> + else + { *limit++=c; + @/@< In special cases copy the character after |c|, + or change |delimiter| @> + } + while (c!='|' || delimiter!='\0'); + *limit=' '; +} + +@ Here cases like `\.{1+@@\v2}', `\.{@@'\v'}' and `\.{'\\''}' within `\pb' +in module names are handled properly, as well as the more ordinary cases of +character constants and strings. In case of a control code, the character +following the `\.@@' cannot be the null character that terminates the module +name, because it is impossible to enter a module name that ends with a +single `\.@@'. If the user is being really devious by saying `\.{@@< Sudden +\v"death\\ @@>}', then the final `\.\\' is silently removed to avoid +disaster to \.{\me.}. + +@< In special cases... @>= +{ if (c=='@@') /* control code; now |*k!='\0'| */ + { if ((*limit++=*k++)=='\'' && delimiter=='\0') /* copy code, test for \:' */ + delimiter='\''; /* which behaves like `\.'' */ + } + else if (c=='\\' && delimiter!='\0') + { char d=*limit++=*k++; /* escaped character, possibly |delimiter| */ + if (d=='\0') --k,limit-=2; /* remove backslash, error is issued anyway */ + } + else if (c=='\'' || c=='"') + if (delimiter=='\0') delimiter=c; + else if (delimiter==c) delimiter='\0'; +} + +@ If the module name ends before the \Cee~text does, we add an |'|'|, and if +a string or character constant was left unclosed we prepend the closing +delimiter to it: we must prevent that |do_C| will ever call |get_line|, even +in erroneous situations. In these cases there is a slight chance that we +might overflow |buffer|, in which case we quit since the user is just trying +to break \.{\me.} anyway. + +@< Report a runaway \Cee~text @>= +{ print("\n! C text in module name didn't end"); print_mod(name); + @.C text...didn't end@> + mark_error(); + if (delimiter!='\0') *limit++=delimiter; + *limit++='|'; + if (limit>&buffer[long_buf_size-2]) + fatal("fix that first, you sneaky devil"); + @.fix that first...@> + break; +} + +@ Finally we come to the point where the characters produced are sent off to +the \TeX~file. The \TeX\ output is supposed to appear in lines at most +|line_length| characters long, so we place it into an output buffer +|out_buf|. The first character of the output buffer is used as a sentinel, +and is not actually written to the output file, so we usually refer to the +output buffer via |out_line|. The pointer |out_ptr| indicates the next +position to be used in the output buffer. During the output process, +|out_line_nr| will hold the current line number of the line about to be +output; it is only used for (rather unlikely) diagnostic messages. + +@d out_line (&out_buf[1]) /* start of actual output line */ +@d out_buf_end (&out_line[line_length]) /* end of |out_buf| */ + +@<Global...@>= +char out_buf[line_length+1]; /* assembled characters */ +char* out_ptr; /* first unused position in |out_buf| */ +int out_line_nr=1; /* number of next line to be output */ + +@ The auxiliary function |flush_buffer| empties the buffer up to a given +breakpoint~|b|, and moves any remaining characters to the beginning of the +buffer, so that they will appear on the next line. If the |percent| +parameter is |true| 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 |percent| parameter is |false|, trailing blanks are suppressed. The +characters emptied from the buffer form a new line of output. + +@d tex_putc(c) putc (c, tex_file) +@d tex_new_line() (putc('\n', tex_file),++out_line_nr) +@d tex_printf(format) fprintf(tex_file, format) + +@c +void flush_buffer(char* b, boolean percent) + /* output from |out_line| to |b|, where |b<=out_ptr| */ +{ int j=(int)(b-out_line); /* number of characters to be output */ + if (!percent) @+ while (j>0 && out_line[j-1]==' ') --j; + /* remove trailing blanks */ + fprintf(tex_file, "%.*s",j,out_line); + if (percent) tex_putc('%'); + tex_new_line(); + { char* p=out_line; + while (b<out_ptr) *p++=*b++; /* shift back remainder of line */ + out_ptr=p; /* adjust to end of (possibly empty) shifted part */ + } +} + +@ The usual way to send a completed line to the output is to call +|finish_line|. When we are copying \TeX\ source material, we retain line +breaks that occur in the input, except that an empty line is not output when +the \TeX\ source line was not entirely blank. For example, a line of the +\TeX\ file that contains only an index \xr. entry will not be copied. Since +|get_line| has removed trailing blanks, the input line is blank if and only +if it is empty; similarly the emptiness of the output line is tested by a +simple comparison because |copy_limbo| and |copy_TeX| have not copied spaces +into an empty output line. The |finish_line| routine is called just before +|get_line| inputs a new line, and just after a line break token has been +emitted during the output of translated \Cee~text. + +@d output_line_empty() (out_ptr==out_line) + +@c +void finish_line(void) /* do this at the end of a line */ +{ if (!output_line_empty()) flush_buffer(out_ptr, false); + else if (limit==buffer) tex_new_line(); /* copy blank input line */ +} + +@ In particular, the function |finish_line| is called near the very +beginning of Phase~II. We initialize the output buffer in such a way that +the first line of the output file will be `\.{\\input cwebxmac}', or +`\.{\\input cwebcmac}' in compatibility mode. At position~0 of the output +buffer, which is never really output, we put a space that will help make the +function |break_out| a little faster. + +@< Set initial... @>= +{ char* line1=" \\input cwebxmac"; + out_ptr=&out_buf[0]; @+ do *out_ptr++=*line1++; while (*line1!='\0'); + if (compatibility_mode) out_ptr[-4]='c'; /* change to \.{cwebcmac} */ +} + +@ When we wish to append one character |c| to the output buffer, we write +`|out(c)|'; this will cause the buffer to be broken at a sensible place and +flushed, 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. + +@d out(c) + *(out_ptr>=out_buf_end ? (break_out(),out_ptr++) : out_ptr++)=c + +@c +void out_str (char* s) @+{@; while (*s!='\0') out (*s++); } + +@ The function |break_out| will determine a good break point in the output +buffer when it is about to overflow. + +@< Prototypes @>= +void break_out (void); + +@~For speed we search from right to left for a proper break point, although +in this direction we cannot know whether we are inside a control sequence. +Nevertheless any blank space is a safe break point, as is the position just +before a backslash that isn't preceded by another backslash. If the break is +not at a space, a |'%'| is output at the break. + +@c +void break_out (void) /* finds a way to break the output line */ +{ char* k=out_ptr,c; int count=0; /* number of backslashes seen */ + do @+ if ((c=*--k)==' ') goto found; @+ while (c!='\\'); + do ++count; while ((c=*--k)=='\\'); +found: + if (++k>out_line) flush_buffer(k,c!=' '); @+ else @< Break peculiar line @> +} + +@ We get to this section only in the unusual case that the entire output line +consists of a string of backslashes followed by a string of characters that +contains no spaces or backslashes. Depending on the number |count| of +backslashes we handle this case as well as possible. If the |count>=2| we +split off a maximal even number of initial backslashes, and if |count==0| we +break the line by putting a |'%'| at the position of the last character. In +the latter case we do not place the |'%'| after the last character, since +that would make the output line one character longer than we promised; +similarly we are cautious in the former case not to place a |'%'| after a +buffer completely filled with backslashes. If |count==1| the line is +probably one enormous control word, and in this unlikely case we simply +output the whole line as is stands, after issuing a warning message. + +@< Break peculiar line @>= +if (count==0) flush_buffer(out_ptr-1,true); +else if (count>=2) flush_buffer + (&out_line[count&=~1]==out_buf_end ? out_buf_end-2 : &out_line[count],true); +else +{ print("\n! Line had to be broken (output l.%d):\n",out_line_nr); + @.Line had to be broken@> + term_write(out_line,out_ptr-out_line); new_line(); mark_harmless(); + flush_buffer(out_ptr,false); +} + + +@* Phase III processing. @:title@> +After Phase~II is completed, \.{\me.}'s only remaining task is writing out +the index, after sorting the identifiers and index entries, and a list of +module names. If the user has set the |no_xref| flag (the \.{-x} option on +the command line), we just finish off the page, omitting the index, module +name list, and table of contents. + +@d triple_file_output flags['t'] +@d even_out_pages flags['e'] + +@c +void phase_three (void) /* output the \xr. index */ +{ finish_line(); + if (no_xref) out_str("\\endcodemode\\vfill\\end"); + else + { phase=3; print_progress("\nWriting the index..."); + @.Writing the index...@> + typedef_tracking(false); /* during parse of `\pb' in module names */ + if (change_exists) + {@; @<Tell about changed sections @> finish_line(); } + if (triple_file_output) + @< Finish the \TeX~file and open the index file @> + else {@; out_str("\\inx"); finish_line(); } @.\\inx@> /* index */ + @<Sort and output the index@> + if (triple_file_output) + @< Finish the index file and open the module listing file @> + else {@; out_str("\\fin"), finish_line(); } @.\\fin@> /* end of index */ + @<Output all the module names@> + if (!triple_file_output) + { out_str("\\con"); @.\\con@> /* table of contents */ + if (even_out_pages) out_str("even"); @.\\coneven@> + } + } + finish_line(); fclose(tex_file); + print_progress("\nDone.\n"); + check_complete(); /* was all of the change file used? */ +} + +@ When output is to be distributed over three files, we can already finish +off the main output file, since it ends in a standard way: with `\.{\\input}' +commands for the two other files (that still have to be written) and either +`\.{\\con}' or `\.{\\coneven}' to produce the table of contents. Since the +three output files are written one after the other, we can reuse the file +pointer |tex_file| for all of them. + +@< Finish the \TeX~file and open the index file @>= +{ out_str("\\inx \\input \\jobname.idx\n" @+ + "\\fin \\input \\jobname.scn\n" @+ + "\\con"); + if (even_out_pages) out_str("even"); + finish_line(); fclose(tex_file); + if ((tex_file=fopen(idx_file_name,"w"))==NULL) + fatal("! Cannot open \"%s\" as output file",idx_file_name); +} + +@~The switch from the index file to the module listing file is similar, but +simpler. + +@< Finish the index file and open the module listing file @>= +{ finish_line(); fclose(tex_file); + if ((tex_file=fopen(scn_file_name,"w"))==NULL) + fatal("! Cannot open \"%s\" as output file",scn_file_name); +} + +@ Just before the index comes a list of all the changed sections, including +the index section itself. The outer |while| loop will terminate without +overstepping the bounds of |changed_section| in the inner loop since +|section_changed(section_count)| holds. + +@<Tell about changed sections@>= +{ int k=0; boolean first=true; + out_str("\\ch "); @.\\ch@> /* changes */ + while (k<section_count) + { do ++k; while (!section_changed(k)); + if (first) first=false; @+ else out_str(", "); + out_sec_nr(k); + } + out('.'); +} + +@ A left-to-right radix sorting method is used, since this makes it easy to +adjust the collating sequence, and the running time will be at worst +proportional to the total length of all entries in the index. +During the sorting phase the partially sorted index entries that have not +yet been output are held on a stack of identifier lists called |sort_info|. +Each of these lists is unsorted, but between elements of different lists the +ordering is completely known: for any pair of lists, all elements of the +list nearer to the top of the stack precede all elements of the other list +(this ordering is chosen because elements are output at the top of the +stack). The entries of |sort_info| are structures of type |sort_node|; they +have a |head| field that points to the first name of the list, and a +|depth| field that tells how many initial characters are known to be equal +among the entries of the list. + +@<Typedef...@>= +typedef struct {@; id_pointer head; int depth; } sort_node, * sort_pointer; + +@~In fact |sort_info| shares its memory with the |scrap_info| array that +serves no purpose at this moment (although it will be used again during +the output of module names). + +@d sort_info scrap_union.id_list_field +@d sort_info_end (&sort_info[sort_stack_size]) +@< Alternative use... @>= +sort_node id_list_field[sort_stack_size]; + +@ The variable |sort_ptr| points to the first unused slot above the top of +the stack in~|sort_info|. + +@<Global...@>= +sort_pointer sort_ptr=sort_info; +#ifdef STAT +sort_pointer max_sort_ptr=sort_info; /* largest value of |sort_ptr| */ +#endif + +@ The desired alphabetic order is specified by the |collate| array; namely, +|collate[0] < collate[1] < @t\dots@> < collate[end_collate-1]|. Upper case +letters are treated like the corresponding lower case letters, since we want +to have `|t<TeX<@[token@]|'. Therefore the collation array should have length +|UCHAR_MAX+1-26|. We are a bit extra cautious however, in case there are +implementations in which the verdict |isalpha(c)| is given (incorrectly) to +certain values |c>=128|, which would cause the part of |collate| that is +actually used to be shorter than that; therefore we compute the actual +length in |end_collate|. + +@<Global...@>= +eight_bits collate[UCHAR_MAX-25]; /* collation order */ +int end_collate; /* length of the part of |collate| actually used */ + +@ We use the order |'\0' < ' ' < @tother characters@> < '_' < 'A'=='a' < +@t\dots@> < 'Z'=='z' < '0' <@t\dots@> < '9'|. If there should be any +characters~|c| for which |tolower(c)| does not occur in |collate| (this can +only happen if |isalnum(c)| incorrectly holds), then entries containing the +character~|c| will not appear in the index. By computing |end_collate| +rather than using a constant |UCHAR_MAX-25|, we avoid however that the mere +existence of such characters would disrupt the proper order of the +index entries that do appear, due to spurious bytes |'\0'| at the end +of~|collate|. + +@<Set init...@>= +{ char *p="_abcdefghijklmnopqrstuvwxyz0123456789"; + int c='\1', k=2; + collate[0]='\0'; collate[1]=' '; + do @+ if (!isalnum(c) && c!='_' && c!=' ') collate[k++]=c; @+ + while (++c<=UCHAR_MAX); + while ((c=*p++)!='\0') collate[k++]=c; + end_collate=k; /* record the length */ +} + +@ The lists of identifiers used for sorting the index cannot be linked +together using |hash_link|, since we still need to be able to look up +identifiers when writing the list of module names. Therefore we declare a +separate array |index_link| that will provide the necessary links, and a +macro |ilink| used to access it: |ilink(p)| points to the successor of~|p| +(or is~|NULL|) for any |id_pointer p@;|. + +At each sorting step we partition a list of identifiers into at most +|UCHAR_MAX-25| sublists, based on the first character position where the +entries of the list are not known to be equal. After the partitioning step +the sublist for character~|c| is pointed to by |bucket[tolower(c)]|. + +@d ilink(p) index_link[id_index(p)] + +@<Global...@>= +id_pointer index_link[max_idents]; /* links identifiers during sorting */ +id_pointer bucket[UCHAR_MAX+1]; + +@ The basic sorting step is to pop the list from the top of the stack, and +either output it, if needs no further refinement, or to split it up into the +buckets, after which the function |unbucket| collects the non-empty buckets +and pushes them back on the stack. At the very first step however, the +identifiers do not come from the stack but directly from the identifier +table. In the remaining cases, the |depth| field of the element popped off +the stack is used to determine the character position used for splitting, +and is passed to |unbucket| so that it can set the proper depth when it +pushes lists back on the stack. When |depth| has risen to~|255| (or more +likely, has been set explicitly to that value by a previous |unbucket|), no +attempt is made to split up a list, even if it is not reduced to a +singleton. + +@d infinity 255 /* $\infty$ (approximately) */ + +@< Sort and output... @>= +{ @<Do the first pass of sorting@> + /* the first time, entries do not come from |sort_info| */ + unbucket(1); /* pick up first-order bucketed lists */ + while (sort_ptr>sort_info) /* i.e., the stack is not empty */ + { eight_bits depth=(--sort_ptr)->depth; + id_pointer name=sort_ptr->head; + if (ilink(name)==NULL || depth==infinity) + /* singleton or set of look-alikes */ + @< Output index entries for the list starting at |name| @> + else + {@; @< Split the list starting at |name| into further lists @> + unbucket(depth+1); + } + } +} + +@ To begin the sorting, we go through all the hash lists and put each entry +having a non-empty \xr. list into the proper bucket. The buckets are emptied +initially; they will also be emptied by each call to~|unbucket|. The entries +of the |index_link| array are initialised when their corresponding +identifiers are prepended to the list of some bucket. + +@<Do the first pass...@>= +{ id_pointer name; + eight_bits c=UCHAR_MAX; + id_pointer *h; /* pointer into |hash| */ + + do bucket[c]=NULL; while (c--!=0); + for (h=hash; h<hash_end; h++) + for (name=*h; name!=NULL; name=name->hash_link) + /* traverse all hash lists */ + if (name->ilk!=reference && name->xref->num!=0) + /* leave out unreferenced names */ + {@; c=name_begin(name)[0]; c=tolower(c); + ilink(name)=bucket[c]; bucket[c]=name; + } +} + +@ Here we split the list at the top of the stack into buckets. Entries of +length |depth| will bet sent to |bucket['\0']| due to the null character +stored at the end of identifier names; shorter entries do not come here +since they will already have been output. Since we are changing the |ilink| +of the front node of the list when putting it into its bucket, we must be a +bit more cautious then usual in traversing the list. + +@< Split the list... @>= +do +{ eight_bits c= tolower((eight_bits)name_begin(name)[depth]); + id_pointer next_name=ilink(name); /* save link */ + ilink(name)=bucket[c]; bucket[c]=name; name=next_name; + /* put into bucket */ +} +while (name!=NULL); + +@ The function |unbucket| goes through the buckets in the reverse order of +the collating sequence specified in the |collate| array, and adds non-empty +lists to the stack. The parameter |d| to |unbucket| tells the current depth +in the buckets; it will be recorded in all lists pushed on the stack during +this call, except that the contents of the bucket containing the identifiers +of length~|d-1| will be given depth |infinity| so that they will be output +directly afterwards. Any two sequences that agree up to the case of their +characters (with any characters beyond position~255 being ignored) are +regarded as identical and will be output in a random order. It is only +because of this case-merging that there could possibly be more than one +identifier of length~|d-1|, which makes setting the depth to |infinity| +necessary; in other cases the special attention is superfluous, since +singleton lists will be output directly anyway. + +@< Prototypes @>= +void unbucket (eight_bits); + +@~@c +void unbucket (eight_bits d) /* empties buckets having depth |d| */ +{ int i=end_collate; /* index into |collate| */ + while(--i>=0) @+ if (bucket[collate[i]]!=NULL) + { if (sort_ptr>=sort_info_end) + overflow("sorting"); @.sorting capacity exceeded@> + sort_ptr->depth= i==0 ? infinity : d; + /* |infinity| means there is nothing left to compare */ + sort_ptr++->head=bucket[collate[i]]; + bucket[collate[i]]=NULL; /* push and empty bucket */ +#ifdef STAT + if (sort_ptr>max_sort_ptr) max_sort_ptr=sort_ptr; +#endif + } +} + +@ Each line in the index file starts with the macro `\.{\\@@}', which will +be properly defined when it is encountered. + +@< Output index... @>= +do +{ out_str("\\@@"); @.\\@@@> + @<Output the index entry |name|@> + @<Output the \xr.s of |name|@> +} while ((name=ilink(name))!= NULL); + +@ In this section the distinction between |xref_roman|, |xref_wildcard|, and +|xref_typewriter| finally becomes effective. As first character (immediately +after `\.{\\@@}') we output a character `\.h' or `\.m',indicating whether the +index entry should be processed in horizontal or in math mode. +@.\\NULL@> @.\\TeX@> + +@<Output the index entry...@>= +switch (name->ilk) +{ case normal: case NULL_like: out('m'); out_identifier(name); break; + case TeX_like: out('h'); out_identifier(name); break; + case roman: out('h'); out_index(name); break; + case wildcard: out_str("h\\9"); out_index(name); break; @.\\9@> + case typewriter: out_str("h\\."); out_index(name); break; @.\\.@> + default: out('h'); out_keyword(name); +} + +@ Section numbers that are to be underlined are enclosed in +`\.{\\[}\dots\.]'. The first `\.{,\ }' will be scooped up by the macro +`\.{\\@@}'. + +@<Output the \xr.s...@>= +{ xref_pointer x=name->xref; + do + { sixteen_bits n=x->num; + out_str(", "); + if (n<def_flag) out_sec_nr(n); + else {@; out_str("\\["); out_sec_nr(n-def_flag); out(']'); } @.\\[@> + } while ((x=next_xref(x))->num!=0); + out('.'); finish_line(); +} + +@ The following recursive function traverses the tree of module names and +prints them. We use the order already present in the tree, which means that +we do not use the collation sequence that was employed for sorting the +index. If the user starts all module names with capital letters however, the +difference should hardly be noticeable. +@^recursion@> + +@< Prototypes @>= +void list_modules (mod_pointer); + +@~The macro `\.{\\@@}' is redefined to serve for lines in the list of module +names as well. Since module names are to be processed in math mode, we +enclose them in dollar signs. + +@c +void list_modules (mod_pointer p) /* print all module names in subtree |p| */ +{ if (p != NULL) + { list_modules(p->llink); +@/ out_str("\\@@$"); @.\\@@@> + leave_block(0); scrap_ptr=scrap_info; /* get ready for parsing */ + {@; xref_pointer x=out_module_name(p); out('$'); + footnote(&x,cite_flag); footnote(&x,0); + }@/ + finish_line(); +@/ list_modules(p->rlink); + } +} + +@~Initially |list_modules| is called for the root of the tree, of course. + +@<Output all the module names@>= list_modules(root); + +@ At the end of the run, if |STAT| was defined and the `\.{+s}' flag +present, we report how much of all the arrays was actually needed. + +@d report(k,c,m) + printf("\t%lu %ss (out of %lu)\n",(unsigned long)(c),k,(unsigned long)(m)) +@c +#ifdef STAT +void print_stats() +{ print("\nMemory usage statistics:\n"); @.Memory usage statistics@> +@/report("identifier", id_index(id_ptr), max_idents); +@/report("module name", mod_index(mod_ptr), max_modules); +@/report("byte", byte_ptr-byte_mem, max_bytes); +@/report("cross-reference", xref_ptr-xmem, max_refs-1); +@)printf("Parsing:\n"); +@/report("scrap", max_scr_ptr-scrap_info, max_scraps); +@/report("text", max_text_ptr-text_mem, max_texts); +@/report("token", max_tok_ptr-tok_mem, max_toks); +@/report("trie node", node_no, max_no_of_nodes); +@/report("level", max_stack_ptr-stack, stack_size); +@)printf("Sorting:\n"); +@/report("level", max_sort_ptr-sort_info, sort_stack_size); +} +#endif + + +@* Index. @:index@> @:title@> +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 sections 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/c_cpp/cwebx/cwebcmac.tex b/web/c_cpp/cwebx/cwebcmac.tex new file mode 100644 index 0000000000..42d76748ec --- /dev/null +++ b/web/c_cpp/cwebx/cwebcmac.tex @@ -0,0 +1,77 @@ +% Compatibility mode macros for CWEBx (in addition to cwebxmac.tex) +% File: cwebcmac.tex, Author: Marc van Leeuwen, Date: November 1994 + +% This file gives definitions to macros defined in cwebmac.tex that are +% undefined in cwebxmac.tex, in order to simulate the cwebmac macro +% environment as much as possible in compatibility mode of CWEBx. +% In addition \PB is defined so as to get decent behaviour of |...| when used +% inside math mode (which one should't do, but some do it anyway). + +\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros + +\input cwebxmac % that's the standard stuff +\xdef\fmtversion{\fmtversion c} + +\let\mc=\ninerm % medium caps +\let\sc=\eightrm % small caps (NOT a caps-and-small-caps font) +%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font + +\def\CEE/{\Cee} +\def\UNIX/{\caps{UNIX}} +\def\TEX/{\TeX} +\def\CPLUSPLUS/{\Cpp} +\def\9#1{} +\let\cmntfont\tenrm + +\def\|#1{\leavevmode\hbox{$#1$}} % in case this is used directly +\def\ATP{\X\kern-.5em:Preprocessor definitions\X} +\let\ATH=\ATP % in case the user refers to \ATH directly +\def\PB#1{\ifmmode\hbox{#1}\else#1\fi} % make |...| safe in math mode + +\def\DC{\kern.1em{::}\kern.1em} % symbol for :: +\def\PA{\mathbin{.*}} % symbol for .* +\def\MGA{\mathbin{\MG*}} % symbol for ->* + +% Revert to Levy/Knuth representations of operators + +\def\K{=\Penalty2} % assignment operator +\def\E{\Penalty7\equiv} % equality test +\newbox\MGbox % symbol for -> +\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt} +\def\MG{\copy\MGbox} + +\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1 +\let\SHC\C % "// short comments" treated like "/* ordinary comments */" +\outer\def\N#1 #2. #3. % start of `@*' section + {\global\gdepth=#1\global\advance\gdepth1 \global\gtitle={#3}\MN#2.% + \ifon \ifnum#1<\secpagedepth \vfil\eject + \else \vskip 0pt plus .5 \vsize \penalty-1000\vskip 0pt plus -.5 \vsize + \vskip\intersecskip + \fi + \fi + \message{*\secno} % progress report + \edef\next{\write\cont % to contents file + {\ZZ{#3}{\number\gdepth}{\secno}{\noexpand\number\pageno}}} + \next + \ifon\startsection{\bf#3.\quad}% + } + +\def\contentsline#1#2#3#4% #1==title, #2=depth+1, #3=secno, #4=pageno + {\ifnum#2=0 \smallbreak\fi + \line{\consetup{#2}#1 \rm\leaders\hbox to .5em{.\hfil}\hfil + \ #3\hbox to3em{\hss#4}% + }% + } +\def\consetup#1% #1=depth+1 + {\ifcase#1 \bf % @** + \or % @* + \or \hskip2em % @*1 + \or \hskip4em % @*2 + \or \hskip6em % @*3 + \or \hskip8em % @*4 + \or \hskip10em % @*5 + \else \hskip12em % depth >=6 + \fi + } + +\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi} diff --git a/web/c_cpp/cwebx/cwebhmac.tex b/web/c_cpp/cwebx/cwebhmac.tex new file mode 100644 index 0000000000..3b55c34e59 --- /dev/null +++ b/web/c_cpp/cwebx/cwebhmac.tex @@ -0,0 +1,143 @@ +% This is file cwebhmac.tex, part of the CWEBx distribution. + +% This file can be loaded after cwebxmac.tex, in order to automatically +% insert \specials for use by the xhdvi previewer, or any other HyperTeX +% dvi-file processing tool. Current version is alpha release. + +% Author: Marc van Leeuwen +% Last modified: 8 november 1996. + +% The following macros are redefined +% \stsec +% \note<text><section list>. +% \finnote<text><section list>. +% \X<number>:<module name>\X + +\ifx\hyperloaded\undefined \let\hyperloaded\relax \else \endinput\fi + + +% Low level interface to hyperlinks and targets + +% \hyperanchor<attributes><anchor text> +% general mechanism to define <achor text> as anchor with <attributes> +\long\def\hyperanchor#1#2{\special{html:<A #1>}{#2}\special{html:</A>}} + +% \hyperlink<URL><active text> +% makes <active text> an active link referring to <URL> +\def\hyperlink#1{\hyperanchor{href="#1"}} + +% \hypertarget<tag><target text> +% defines a local target for hyperlinks, that can be referenced by <tag> +\def\hypertarget#1{\hyperanchor{name="#1"}} + +% \hyperdef<kind><number><target text> +% defines a hypertarget classified by <kind> and sequence <number> +\def\hyperdef#1#2{\hypertarget{#1.#2}} + +% \hyperref<assignments><kind><number><active text> +% creates a hyperlink to local target created by \hyperdef<kind><number> +% <assignments> are optional assignments for expansion of <kind> and <number> +{\catcode`\#=12 \gdef\sharp{#}} % to access neutralised '#' character +\def\hyperref#1#2#3% +{{#1\xdef\next{{\sharp#2.#3}}}\expandafter\hyperlink\next} + + +% High level interface attached to CWEAVE-produced macros (or to those +% accessed by them via the definition of cwebxmac.tex) + +% Target attached to beginning of each section. <kind>=sec, <number>=\secno. +\def\stsec + {\endcodemode\noindent + \hyperdef{sec}\secno{\let\*=\lapstar\bf\secstar.}\quad + } +\let\startsection=\stsec % this one is used; provides hook for extra actions + +% List of links attached to end of some sections. +\def\note#1#2.% +{\Y\noindent{\hangindent2\indentunit\baselineskip10pt\eightrm + #1~\hypersecrefs#2, .\par}} + +% Same, but for references from the list of module names. +\def\finnote#1#2.{\quad{\eightrm#1~\hypersecrefs#2, .}} + +% the following two are auxiliary macros that implement reference lists. + +% \hypersecrefs<number>, <number>, ..., <number><ETcode><number>, . +% or \hypersecrefs<number>, . +% make list of hyperlinks to each of then mentioned sections +\def\hypersecrefs#1, #2.% +{\ifx|#2|\def\next{\finalsecrefs#1\ET\ET.#1\ETs\ETs}% final `.' follows below + \else\hyperref{\let\*=\empty}{sec}{#1}{#1}, \let\next=\hypersecrefs + \fi\next#2.% +} + +% \finalsecref<n><ETcode><m>\ET\ET.<n><ETcode><m>\ETs\ETs. +% or \finalsecref<n>\ET\ET.<n>\ETs\ETs. +\def\finalsecrefs#1\ET#2\ET#3.#4\ETs#5\ETs#6.% +{\ifx|#2#5|\hyperref{\let\*=\empty}{sec}{#1}{#1}% just 1 reference + \else % <n>\ET<m> or <n>\ETs<m> + \edef\next{\ifx|#5|#1\else#4\fi}% <n> + \toks0={\let\*=\empty}% + \edef\next{{\the\toks0}{sec}{\next}{\next}}% + \expandafter\hyperref\next% link to sec.<n> with text <n> + #3#6% one is empty, the other is \ET or \ETs, as appropriate + \hyperref{\let\*=\empty}{sec}{#2#5}{#2#5}% similar trick, link to sec.<m> + \fi +} + +% we must change \ATP slightly, so that \X can recognise it and avoid a link +\def\ATP{\X\ATP\kern-.5em:Preprocessor directives\X} + +\def\X#1:#2\X % module name +{\langle\,${#2\eightrm\enspace\ifx\ATP#1\else\hypersecrefs#1, .\fi}$\,\rangle} + +{\catcode`_=12 \gdef\usplain{\def\_{_}}} + +\let\plainCident=\Cident + +\def\Cident#1{\def\idstart{#1}\futurelet\next\testid} +\let\\=\Cident % cwebx only; a \hbox would prevent us from seeing "subscript" + +\def\testid +{\ifx_\next + \def\next_##1% + {\hyperref\usplain{id}{\idstart##1}{\plainCident\idstart_{##1}}}% + \else \hyperref\usplain{id}\idstart{\plainCident\idstart}% + \let\next=\empty + \fi\next +} + +\def\indextestid +{\ifx_\next + \def\next_##1% + {{\usplain\xdef\next{{id}{\idstart##1}}}% + \expandafter\hyperdef\next{\plainCident\idstart_{##1}}% + }% + \else + {\usplain\xdef\next{{id}{\idstart}}}% + \expandafter\hyperdef\next{\plainCident\idstart}% + \let\next=\empty + \fi\next +} + +{\let \inxentry=\relax % remove \outer-ness + \toks0=\expandafter{\inx \let\testid=\indextestid}\xdef\inx{\the\toks0 } +} + +\outer\def\inxentry#1#2, #3.% #1 is `h' or `m' for horiz/math mode + {\par\hangindent2em\noindent + \if#1m$#2$\else#2\relax\fi % \relax avoids fatal \next=\fi in \indextestid + :\kern1em\indexrefs#3, .% + } + +\def\indexrefs#1, #2.% +{\hyperref{\let\*=\empty\def\[##1]{##1}}{sec}{#1}{#1}% + \ifx|#2|\let\next=\empty\else, \let\next=\indexrefs\fi\next#2.% +} + +\def\contentsline#1#2#3#4% #1=depth, #2=title, #3=secno, #4=pageno + {\line{\hyperref{}{sec}{#3}{\ignorespaces#2} + \leaders\hbox to .5em{.\hfil}\hfil + \ \hyperref{}{sec}{#3}{#3 \hbox to3em{\hss#4}}% + }% + } diff --git a/web/c_cpp/cwebx/cwebxmac.tex b/web/c_cpp/cwebx/cwebxmac.tex new file mode 100644 index 0000000000..47157c8aba --- /dev/null +++ b/web/c_cpp/cwebx/cwebxmac.tex @@ -0,0 +1,442 @@ +% Standard macros for CWEBx listings (in addition to plain.tex) +% File: cwebxmac.tex, Author: Marc van Leeuwen, Date: November 1994 + +\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros +\ifx \cwebxmacloaded\undefined \let\cwebxmacloaded=\relax \else \endinput \fi +\xdef\fmtversion{\fmtversion+CWEBx3.0} +\let\:=\. % preserve a way to get the dot accent + +\font\ninerm=cmr9 +\font\eightrm=cmr8 +\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\font\tentex=cmtex10 % TeX extended character set (used in strings) +\fontdimen7\tentex=0pt % no double space after sentences +\hyphenchar\tentex=-1 % no automatic hyphenation within strings + +\newdimen\indentunit \indentunit 1em +\parskip 0pt % no stretch between paragraphs +\parindent\indentunit % for paragraphs and for the first line of C text + +\chardef\v=`| % vertical (|) +\def\caps#1{\hbox{\ninerm #1}} +\def\Cee{\caps C} \def\Cpp{\Cee\PP} % \def\UNIX{\caps{UNIX}} etc. +\let\Sec=\S % section mark +\def\Secs{\Sec\Sec} % as in \Sec@#label@> + +\let\mainfont=\tenrm + +\def\Cident#1{{\it#1\/\kern.05em}} % italic type for identifiers +\def\\#1{\leavevmode\hbox\Cident{#1}} % robust version +\def\Cbold#1{{\bf + \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}% + #1\/\kern.05em}} % boldface type for reserved words +\def\{\leavevmode\hbox\Cbold{#1}} % robust version +\def\Cstring#1{\ifmmode {}$\typewriter{#1}${}\else\typewriter{#1}\fi} +\def\typewriter#1{{\tentex % typewriter type for strings + \let\\=\BS % backslash in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + \let\^=\CF % circumflex in a string + #1\kern.05em}} +\def\.#1{\leavevmode\hbox\typewriter{#1}} +\def\){\discretionary{"}{"}{}} % discretionary string break +\def\AT{@} % at sign for control text (no longer needed since CWEBx2+1.0) +\def\a#1{\mathopen{\hbox to \indentunit{$#1$\hss}}} % '{' in tab space +\def\m#1{\mathord{#1}} % braces required here if #1 generates a penalty + +\chardef\AM=`\& % ampersand character in a string +\chardef\BS=`\\ % backslash in a string +\chardef\LB=`\{ % left brace in a string +\chardef\RB=`\} % right brace in a string +\def\SP{{\tt\char`\ }} % (visible) space in a string +\chardef\TL=`\~ % tilde in a string +\chardef\UL=`\_ % underline character in a string +\chardef\CF=`\^ % circumflex character in a string + +\newbox\PPbox % symbol for ++ +\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt} +\def\PP{\copy\PPbox} +\newbox\MMbox +\setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0 + \kern-1pt\char0}\kern.5pt} +\def\MM{\copy\MMbox} +\newbox\SSbox % symbol for ## +\setbox\SSbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm\#\kern-1pt\#}\kern.5pt} +\def\SS{\mathbin{\copy\SSbox}} +\def\MG{{\rightarrow}} % symbol for `->' +\let\GG=\gg +\let\LL=\ll +\let\NULL=\odot +\mathchardef\AND="2026 % bitwise and; also \& (unary operator) +\def\OR{\Penalty6\mid} % bitwise or +\let\XOR=\oplus % bitwise exclusive or +\def\CM{{\sim}} % bitwise complement +\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%} +\def\MOD{\mathbin{\copy\MODbox}} +\def\CC{::} + +\newbox\bak \setbox\bak=\hbox to -\indentunit{} % backspace one em +\newbox\bakk\setbox\bakk=\hbox to -2\indentunit{} % backspace two ems + +\newcount\ind % current indentation level + 2 (for hanging indentation) +\newcount\pl %level of parentheses +\mathchardef\lpar=\mathcode`( \mathchardef\rpar=\mathcode`) +\mathchardef\lbrac=\mathcode`[ \mathchardef\rbrac=\mathcode`] +\mathchardef\plus=\mathcode`+ \mathchardef\minus=\mathcode`- +\mathchardef\mcolon=\mathcode`: +{\catcode`(=\active \catcode`)=\active + \catcode`[=\active \catcode`]=\active + \gdef({\global\advance\pl 1\lpar} + \gdef){\ifnum\pl>0\global\advance\pl-1\fi\rpar} + \gdef[{\global\advance\pl 2\lbrac} + \gdef]{\ifnum\pl>0\global\advance\pl-2\fi\rbrac} + \catcode`+=\active \catcode`-=\active \catcode`*=\active + \gdef+{\Penalty8\plus} \gdef-{\Penalty8\minus} \gdef*{\Penalty9\ast} + \catcode`:=\active \gdef:{\Penalty3\mcolon} +} +\def\cweblbrace{\global\advance\pl 1\lbrace} +\def\cwebrbrace{\ifnum\pl>0\global\advance\pl-1\fi\rbrace} + +% breaking within parentheses will be unattractive, +% and within brackets even more so + +\def\0#1{\penalty\number\pl#10 } % optional break in inner mode +\def\1{\global\advance\ind by1\global\hangindent\ind\indentunit} + % indent one more notch +\def\2{\global\advance\ind by-1} % indent one less notch +\def\3#1{\hfil\0#1\hfilneg} % optional break in outer mode +\def\4{\copy\bak\ignorespaces} % backspace one notch +\def\5{\hfil\penalty-1\hfilneg\enspace\kern2\indentunit\copy\bakk + \ignorespaces}% break space +\def\6{\ifmmode\else\par % forced break + \hangindent\ind\indentunit\noindent\kern\ind\indentunit + \copy\bakk\ignorespaces\fi} +\def\7{\Y\6} % forced break and a little extra space +\def\8{\hskip-\ind\indentunit\hskip 2\indentunit} % cancel full indentation + +\let\yskip=\smallskip % amount of space between subsections, and at `@)' +\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections +\newif\ifcodemode \codemodefalse % whether we are in \Cee part of section +\newif\ifon % whether a section will produce any printed output +\let\maybe=\iftrue % \if-like macro; governs printing of unchanged sections +\def\changesonly{\let\maybe=\iffalse} % the user might set this in limbo +\def\onmaybe{\let\ifon=\maybe} % this is executed for each unchanged section + +\def\Penalty#1{\relax\ifcodemode\3#1\fi} % optional break if in code mode + +% Summary of use of \Penalty +%\def*{\Penalty9\ast} +%\def+{\Penalty8\plus} \gdef-{\Penalty8\minus} +%\def\I{\Penalty7\neq} % inequality test +%\def\S{\Penalty7=} % equality test +%\def\OR{\Penalty6\mid} % bitwise OR +%\def\W{\Penalty5\land} % logical AND +%\def\V{\Penalty4\lor} % logical OR +%\def\?{\Penalty3\mathrel?} % `?' operator +%\def:{\Penalty3\mcolon} % colon in math mode matches `?' operator +%\def\K{\Leftarrow\Penalty2} % assignment operator, +% comma operator gets \Penalty1 by CWEAVE + +\def\note#1#2.% for cross-referencing notes, as at the end of a section +{\Y\noindent{\hangindent2\indentunit\baselineskip10pt\eightrm#1~#2.\par}} +\def\defin#1{\global\advance\ind by 2 + \1\&{#1}\quad} % begin `#define' or `format' of `#include' +\def\lapstar{\rlap{*}} +\def\stsec % start up setion + {\endcodemode\noindent{\let\*=\lapstar\bf\secstar.\quad}} +\let\startsection=\stsec % this one is used; provides hook for extra actions +\newcount\gdepth % depth of last starred section +\newcount\secpagedepth \secpagedepth=2 % depth where no page break is forced +\newtoks\gtitle % title of last starred section + +\def\A{\note{See also section}} % xref for doubly defined module name +\def\As{\note{See also sections}} % xref for multiply defined module name +\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo +\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}} + tangles as \tentex "#2"\egroup\par} +\def\noATL#1 #2 {} +\def\noatl{\let\ATL=\noATL} % suppress output from @l +\def\ATP{\X\kern-.5em:Preprocessor directives\X} +\def\B{\leavevmode % go into C mode + \ifcodemode\else + \begingroup\codemodetrue + \rightskip=0pt plus 300pt + \pretolerance 10000 + \hyphenpenalty 9999 % so strings can be broken (with string break inserted) + \exhyphenpenalty 10000 + \binoppenalty 10000 + \relpenalty 10000 + \mathcode`(="8000 \mathcode`)="8000 \mathcode`[="8000 \mathcode`]="8000 + \mathcode`+="8000 \mathcode`-="8000 \mathcode`*="8000 \mathcode`:="8000 + \let\{=\cweblbrace \let\}=\cwebrbrace + \let\.=\Cstring \let\\=\Cident \let\&=\Cbold + \fi + \global\ind=2 + \global\pl=0 + \1} +\def\endcodemode + {\ifcodemode + \endgroup % get out of C mode + \global\hangindent=0pt % counteract \global\hangindent in \1 + \fi + } +\def\C#1% ordinary C comment; try to allow a break if little room is left + {\hfil\penalty0\hfilneg\kern4\indentunit\copy\bakk$/\ast\,$#1$\,\ast/$} +\def\SHC#1% C++ one-line comment; force onto the current line + {\nobreak\kern2\indentunit\hbox{$/\!/\,$#1\unskip}}% +\def\D{\8\defin{\#define}} % macro definition +\def\E{\Penalty7=} % equality test +\let\EQ=\equiv % after defining module name +\def\PE{\mathrel\plus\EQ} % further defining section of a module +\def\ET{ and~} % conjunction between two section numbers +\def\ETs{, and~} % conjunction between the last two of several section numbers +\def\F{\defin{format}} % format definition +\let\G=\ge % greater than or equal sign +% \H is long Hungarian umlaut accent +\def\h{\8\defin{\#include}} % header file inclusion +\def\I{\Penalty7\neq} % inequality test +\def\J{\.{@\&}} % TANGLE's join operation +\def\K{\Leftarrow\Penalty2} % assignment operator, + % can be changed to `=' (if \E is redefined) or `\leftarrow', if desired +\def\KK#1{\mathrel{#1}\K} % composite assignment operators + % braces are required for same reason as for \m +\def\MRL#1{\mathrel{#1}} % for first such operator in compatibility mode +% \L is Polish letter suppressed-L +\outer\def\M#1. % start of `@ ' section + {\MN#1.\ifon + \vskip 0pt plus .5 \vsize \penalty-1000\vskip 0pt plus -.5 \vsize + \vskip\intersecskip\startsection + } +\outer\def\n#1. % start of `@~' section + {\MN#1.\ifon\vskip\intersecskip\startsection + } +\outer\def\N#1 #2. #3. %start of `@*' section + {\global\gdepth=#1\global\gtitle={#3}\MN#2.% + \ifon \ifnum\gdepth<\secpagedepth \vfil\eject + \else \vskip 0pt plus .5 \vsize \penalty-1000\vskip 0pt plus -.5 \vsize + \vskip\intersecskip + \fi + \fi + \message{*\secno}% progress report + \edef\next{\write\cont % to contents file + {\ZZ{#1}{#3}{\secno}{\noexpand\the\pageno}}} + \next + \ifon\startsection{\bf#3.\quad}% + } +\def\MN#1.% common code for \M, \N, \n + {\par{\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% + \ifx\secno\secstar \onmaybe % print unchanged section if \maybe=\iftrue + \else\ontrue % changed sections are always printed + \fi \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}% + }% each \mark is {section reference}{depth}{title} +% \O is Scandinavian letter O-with-slash +% \P is paragraph sign +\def\Q{\note{This code is cited in section}} % xref for citation of a module +\def\Qs{\note{This code is cited in sections}} % xref for citations of a module +\let\R=\lnot % logical not +% \S is section sign +\def\T#1{% + {\let\~=\oct \let\^=\hex \let\_=\timestentothepower \let\$=\withsuffix #1}} +\def\oct{{}^\circ\kern-.2em\it\aftergroup\afteroct} +\def\afteroct{\kern.2em } +\def\hex{{}^{\scriptscriptstyle\#}\tt} +\def\timestentothepower{\cdot 10^{\aftergroup}} +\def\withsuffix{_{\rm\,\aftergroup}} +\def\U{\note{This code is used in section}} % xref for use of a module +\def\Us{\note{This code is used in sections}} % xref for uses of a module +\def\V{\Penalty4\lor} % logical or +\def\W{\Penalty5\land} % logical and +\def\X#1:#2\X{\langle\,${#2\eightrm\enspace#1}$\,\rangle} % module name +\def\Y{\par\yskip} +\let\Z=\le % less than or equal sign +\let\ZZ=\relax % now you can \send the control sequence \ZZ +\def\?{\Penalty3\mathrel?} % `?' operator +\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule + \hbox{\strut\kern2pt\.{#1}\kern2pt}} + \hrule}\vrule\kern2pt}} % verbatim string +\let\*=* + + +% output routines + +\newif\iftitle % if true suppresses first running head +\def\lheader{\mainfont\the\pageno\eightrm\qquad\grouptitle\hfill\title\qquad + \mainfont\topsecno} % top line on left-hand pages +\def\rheader{\mainfont\topsecno\eightrm\qquad\title\hfill\grouptitle + \qquad\mainfont\the\pageno} % top line on right-hand pages +\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter + \takethree\topmark}} +\def\topsecno{\expandafter\takeone\topmark} +\def\takeone#1#2#3{#1} +\def\taketwo#1#2#3{#2} +\def\takethree#1#2#3{#3} +\def\nullsec{{\eightrm\kern-2em }} % the \kern-2em cancels \qquad in headers + +\let\page=\pagebody \normalbottom +% \def\page{\box255 }% faster, but loses plain TeX footnotes +\def\normaloutput#1#2#3% #1=page, #2=running head if even, #3 idem if odd +{\ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox + {\vbox to\fullpageheight + {\iftitle\global\titlefalse % no running head, but reset for next pages + \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}% + \fi + \vfill#1% parameter #1 is the page itself + }} + \global\advance\pageno by1 +} + +\def\title{\uppercase\expandafter{\jobname}} % default title +\def\topofcontents{\centerline{\titlefont\title}\vskip.7in + \vfill} % this material will start the table of contents page +\def\botofcontents{\vfill\vfill + \centerline{\covernote}} % this material will end the table of contents page +\def\covernote{} +\def\contentspagenumber{0} % default page number for table of contents + +\newdimen\pagewidth \pagewidth=6.5in % the width of each page +\newdimen\pageheight \pageheight=8.7in % the height of each page +\newdimen\fullpageheight \fullpageheight=9in % page height including headlines +\newdimen\pageshift \pageshift=0pt % shift righthand pages wrt lefthand ones +\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein + \fullpageheight=9truein\setpage} +\def\setpage{\hsize=\pagewidth \vsize=\pageheight} % use after changing page size +\def\contentsfile{\jobname.toc } % file that gets table of contents info +\def\readcontents{\input\contentsfile} + +\newwrite\cont +\output= % temporary for first page, which is emtpy so as to define \topmark +{\setbox0=\box255 % throw away empty page + \openout\cont=\contentsfile % gets written when first real page is shipped + \write\cont{\catcode `\noexpand\@=11 } % first line makes `@' letter + \global\output={\normaloutput\page\lheader\rheader}% the real \output +} +\setpage +\vbox to 2\vsize{} % dummy page, but the first \topmark won't be null +\gtitle={\.{CWEB} output} % this running head is reset by starred sections +\mark{\noexpand\nullsec0{\the\gtitle}} +% page must be oversized so even a very early \inx won't get the chance +% to reassign \output before this page is sent off. + +\newbox\sbox % one-page buffer for delayed output of page before index +\newif\ifpagesaved % whether buffer has been filled +\def\bufferedoutput +{{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi % empty buffer + \global\setbox\sbox=\page \global\pagesavedtrue % fill buffer +}} + +\def\ch#1.% +{{\let\*=\relax + \note{The following sections were changed by the change file:}{#1}.% +}} + +\newbox\lbox % lefthand column in the index +\def\inx + {\par\vskip6pt plus 1fil \endcodemode % we are beginning the index + \def\page{\box255 } \normalbottom + \write\cont{\catcode `\noexpand\@=12\relax} % make `@' other char + \closeout\cont % the contents information has been fully gathered + \output=\bufferedoutput\pagesavedfalse\eject % eject pages, keeping last + \setbox\sbox=\vbox{\unvbox\sbox} % allow its glue to reset + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output= + {\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else + \normaloutput + {\vbox to\pageheight + {\box\sbox \vss \hbox to\pagewidth{\box\lbox \hfil \page}}% + }% page argument to \normaloutput + \lheader\rheader % other two arguments to \normaloutput + \global\vsize\pageheight \gdef\lr{L} \global\pagesavedfalse + \fi + }% + \message{Index:} + \parskip 0pt plus .5pt + \let\@=\inxentry + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + } + +\outer\def\inxentry#1#2, % index entry; #1 is `h' or `m' for horiz/math mode + {\par\hangindent2em\noindent\if#1m$#2$\else#2\fi:\kern1em} + +\def\fin + {\par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % needed in case index is empty + \if R\lr \null\vfill\eject\fi % finish the current page + \parfillskip 0pt plus 1fil % restore normal paragraph end + \setpage % restore page shape + \output={\normaloutput\page\lheader\rheader}% restore output routine + \message{Module names:} + \def\grouptitle{NAMES OF THE MODULES} + \let\topsecno=\nullsec + \let\note=\finnote + \def\Q{\note{Cited in section}}% crossref for mention of a section + \def\Qs{\note{Cited in sections}}% crossref for mentions of a section + \def\U{\note{Used in section}}% crossref for use of a module + \def\Us{\note{Used in sections}}% crossref for uses of a module + \def\@{\par\hangindent 2em}\let\*=* + } + +\def\finnote#1#2.{\quad{\eightrm#1~#2.}} + +\def\con + {\par\vfill\eject % finish the module names + \rightskip 0pt \hyphenpenalty 50 \tolerance 200 + \setpage + \output={\normaloutput\page\lheader\rheader} + \titletrue % prepare to output the table of contents + \pageno=\contentspagenumber + \def\grouptitle{TABLE OF CONTENTS} + \message{Table of contents:} + \topofcontents + \line{\hfil Section\hbox to3em{\hss Page}} + \let\ZZ=\contentsline + \readcontents % read the contents info + \botofcontents \eject\end % print the contents page(s) and terminate + } +\def\contentsline#1#2#3#4% #1=depth, #2=title, #3=secno, #4=pageno + {\line{\ignorespaces#2 \leaders\hbox to .5em{.\hfil}\hfil + \ #3\hbox to3em{\hss#4}% + }% + } +\def\coneven % force even number of pages before contents +{\par\vfill\eject\ifodd\pageno\else\shipout\hbox{}\advancepageno\fi\con} +\def\noinx{\def\inx{\endcodemode\end}} % no indexes or table of contents +\def\nomods{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}} + % no index of module names or table of contents +\def\nocon{\let\con=\end} % no table of contents +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} +\newcount\twodigits +\def\hours{\twodigits=\time \divide\twodigits by 60 \printtwodigits + \multiply\twodigits by-60 \advance\twodigits by\time \printtwodigits} +\def\gobbleone1{} +\def\printtwodigits{\advance\twodigits100 + \expandafter\gobbleone\number\twodigits + \advance\twodigits-100 } +\def\now{{\eightrm\today\ at \hours}} +\def\datethis % say `\datethis' in limbo, to get your listing timestamped + {\def\startsection{\leftline\now\bigskip\let\startsection=\stsec\stsec}} +\def\datecontentspage % timestamps the contents page + {\def\topofcontents + {\leftline\now\bigskip\centerline{\titlefont\title}\vfill}% + } + +\def\indentation#1{\indentunit#1\relax + \parindent\indentunit + \setbox\bak\hbox to-\indentunit{}% backspace one unit + \setbox\bakk\hbox to-2\indentunit{}% backspace two units +} diff --git a/web/c_cpp/cwebx/examples/Makefile b/web/c_cpp/cwebx/examples/Makefile new file mode 100644 index 0000000000..7fcdba0d84 --- /dev/null +++ b/web/c_cpp/cwebx/examples/Makefile @@ -0,0 +1,45 @@ +CFLAGS = +CWEAVE = cweave +d +CTANGLE = ctangle +.SUFFIXES: .tex .dvi .w + +.w.tex: + $(CWEAVE) $* + +.tex.dvi: + tex $* + + +.w.dvi: + $(CWEAVE) $* + tex $* + +.w.c: + $(CTANGLE) $* + +.w.o: + $(CTANGLE) $* + $(CC) $(CFLAGS) -c $*.c + +.w.dvi: + make $*.tex + make $*.dvi + +.w.o: + make $*.c + make $*.o + +treeprint: treeprint.w + make treeprint.c + make treeprint + +wc: wc.w + make wc.c + make wc + +wmerge: wmerge.w + make wmerge.o + $(CC) $(CFLAGS) -o wmerge wmerge.o ../common.o + + + diff --git a/web/c_cpp/cwebx/examples/compare.w b/web/c_cpp/cwebx/examples/compare.w new file mode 100644 index 0000000000..7a52d9b000 --- /dev/null +++ b/web/c_cpp/cwebx/examples/compare.w @@ -0,0 +1,108 @@ +@* Comparing text files. +This is an entirely trivial program, that tests whether two text files are +equal, and if not so, points out the first point of difference. + +@h <stdio.h> +@h <stdlib.h> +@c typedef char bool; + +@ The outline of the program is simple. We read characters from both input +files into |c1| and~|c2| until the comparison is complete. Line and column +counts are maintained in |line| and~|col|. + +@d left_margin 1 /* leftmost column number; change to 0 if you prefer */ +@c +@<Functions@>@; +int main(int n, char** arg) +{ FILE *f1,*f2; /* the two input files */ + int c1,c2,col=left_margin; long line=1; + @< Open the files |f1| and~|f2|, + taking their names from the command line or from the terminal; + in case of an error for which no recovery is possible, + call |exit(1)| @> + @< Search for first difference, + leaving |c1!=c2| if and only if a difference was found @> + @< Report the outcome of the comparison @> + return 0; /* successful completion */ +} + +@ The heart of the program is this simple loop. When we reach the end of +one of the files, the files match if and only if the other file has also +reached its end. For this reason the test |c1==c2|, which requires +characters to be read from both files, must precede the test for file end; +when only one file ends, it is the former test which breaks the loop. + +@< Search for first difference... @>= +while ((c1=getc(f1))==(c2=getc(f2)) && c1!=EOF) + if (c1=='\n') {@; ++line; col=left_margin; } @+ else ++col; + +@ When the first difference occurs at the end of one of the files, or at the +end of a line, we give a message indicating this fact. + +@< Report... @>= +if (c1==c2) printf("Files match.\n"); +else +{ printf("Files differ.\n"); + if (c1==EOF || c2==EOF) + @/{@; + the_file(c1==EOF); + printf("is contained in the other as initial segment.\n"); + } + else if (c1=='\n' || c2=='\n') + @/{@; + the_file(c1=='\n'); + printf("has a shorter line number %ld than the other.\n",line); + } + else printf("First difference at line %ld, column %d.\n",line,col); +} + +@ The function |the_file| starts a sentence about the first or second file, +depending on its boolean argument. + +@<Functions@>= +void the_file(bool is_first) +@+{@; printf("The %s file ", is_first ? "first" : "second" ); } + +@ There can be be zero, one or two command line arguments. If there are none, +the user is prompted to supply them, and if there are two these are taken as +the file names, prompting the user only in case a file could not be opened. +In case just one argument is present, the first file is assumed to be the +standard input, which does not have to be opened; in this case however we +will not read a file name from terminal in case the second file cannot be +opened. + +@d read_mode "r" +@< Open... @>= +--n; ++arg; /* ignore ``argument'' 0, which is the program name */ +if (n==0) +@/{@; + open_file(&f1,"First file to compare", NULL); + open_file(&f2,"Second file to compare", NULL); +} +else if (n==1) +{ f1=stdin; + if ((f2=fopen(*arg,read_mode))==NULL) + {@; printf("Could not open file %s.\n",*arg); exit(1); } +} +else if (n==2) +{ open_file(&f1,"Give another first file", *arg++); + open_file(&f2,"Give another second file", *arg); +} +else +{@; printf("No more than two command line arguments are allowed.\n"); + exit(1); +} + +@ The function |open_file| will try to open the file |name| for reading, and +if this fails it will prompt for another file name until it has success. If +called with |name==NULL|, the function starts with prompting right away. + +@<Functions@>= +void open_file(FILE** f,char* prompt,char* name) +{ char buf[80]; + if (name==NULL || (*f=fopen(name,read_mode))==NULL) + do {@; printf("%s: ",prompt); fflush(stdout); scanf("%79s",buf); }@/ + while ((*f=fopen(buf,read_mode))==NULL); +} + +@* Index. diff --git a/web/c_cpp/cwebx/examples/treeprint.w b/web/c_cpp/cwebx/examples/treeprint.w new file mode 100644 index 0000000000..ae9dc239d1 --- /dev/null +++ b/web/c_cpp/cwebx/examples/treeprint.w @@ -0,0 +1,229 @@ +% Copyright 1987 Norman Ramsey -- Rutgers University +% Adapted to CWEB version 3.0 by Marc van Leeuwen -- CWI Amsterdam + +@*Directory Trees. +The object is to print out a directory hierarchy in some pleasant way. +The program takes output from \.{find * -type d -print {\v} sort} +@^system dependencies@> +and produces a nicer-looking listing. +For those of you who may not have \.{find} or \.{sort}, the output is a +list of fully +qualified directory names (parent and child separated by slashes |'/'|), +and everything is already nicely sorted in lexicographic order. + +|treeprint| takes one option, |"-p"|, which tells it to use the printer's +line-drawing set, rather than the terminal's. +@h <stdio.h> + +@c +@< Global declarations @>@; + +main(int argc, char** argv) +{ +@< Variable declaration for |main| @> @; +@< Search for options and set special characters on |"-p"| @> +@< Read output from find and enter into tree @> +@< Write tree on standard output @> +exit(0); +} + +@ +We make all the siblings of a directory a linked list off of its left child, +and the offspring a linked list off the right side. +Data are just directory names. +@d sibling left +@d child right + +@< Global decl... @>= +typedef struct tnode { + struct tnode *left, *right; + char *data; +} TNODE; +@ @< Variable declaration for |main| @>= +struct tnode *root; + + + +@*Input. +Reading the tree is simple---we read one line at a time, and call on the +recursive |add_tree| procedure. +@c +read_tree (FILE* fp,struct tnode** rootptr) +{ + char buf[255], *p; + + while ((fgets(buf, 255, fp))!=NULL) { + @< If |buf| contains a newline, make it end there @> + add_tree(rootptr, buf); + } + } + +@ Depending what system you're on, you may or may not get a newline in |buf|. +@< If |buf| contains a newline... @>= + p=buf; while (*p!='\0'&&*p!='\n') p++; +@^system dependencies@> + *p='\0'; + +@ +To add a string, we split off the first part of the name and insert it into +the sibling list. We then do the rest of the string as a child of the new node. +@c +add_tree(struct tnode** rootptr, char* p) +{ + char *s; + int slashed; + + if (*p=='\0') return; + +@< Break up the string so |p| is the first word, + |s| points at null-begun remainder, + and |slashed| tells whether |*s=='/'| on entry @> + + if (*rootptr==NULL) { +@< Allocate new node to hold string of size |strlen(p)| @> + strcpy((*rootptr)->data,p); + } + if (strcmp((*rootptr)->data,p)==0) { + if (slashed) ++s; + add_tree(&((*rootptr)->child),s); + } + else { + if (slashed) *s='/'; + add_tree(&((*rootptr)->sibling),p); + } + } + +@ We perform some nonsense to cut off the string |p| so that |p| just holds +the first word of a multiword name. |s| points at what was either the end +of |p| or a slash delimiting names. In either case |*s| is made |'\0'|. +Later depending on wether we want to pass the whole string or the last piece, +we will restore the slash or advance |s| one character to the right. + +@< Break up... @>= + for (s=p;*s!='\0'&&*s!='/';) s++; + if (*s=='/') slashed=1, *s='\0'; + else slashed=0; + +@ Node allocation is perfectly standard\dots +@< Allocate new node... @>= + *rootptr=(struct tnode *) malloc (sizeof(struct tnode)); + (*rootptr)->left = (*rootptr)->right = NULL; + (*rootptr)->data = malloc (strlen(p)+1); + +@ +@< Global decl... @>= char *malloc(); + +@ In this simple implementation, we just read from standard input. +@< Read... @>= read_tree(stdin,&root); + +@*Output. +We begin by defining some lines, tees, and corners. +The |s| stands for screen and the |p| for printer. +You will have to change this for your line-drawing set. +@^system dependencies@> + +@d svert '|' +@d shoriz '-' +@d scross '+' +@d scorner '\\' /* lower left corner */ + +@d pvert '|' +@d phoriz '-' +@d pcross '+' +@d pcorner '\\' /* lower left corner */ + +@ The default is to use the terminal's line drawing set. +@< Global declarations @>= +char vert=svert; +char horiz=shoriz; +char cross=scross; +char corner=scorner; + +@ With option |"-p"| use the printer character set. +@< Search for options... @>= +while (--argc>0) { + if (**++argv=='-') { + switch (*++(*argv)) { + case 'p': + vert=pvert; + horiz=phoriz; + cross=pcross; + corner=pcorner; + break; + default: + fprintf(stderr,"treeprint: bad option -%c\n",**argv); + break; + } + } +} + +@ We play games with a character stack to figure out when to put in vertical +bars. +A vertical bar connects every sibling with its successor, but the last sibling +in a list is followed by blanks, not by vertical bars. The state of +bar-ness or space-ness for each preceding sibling is recorded in the +|indent_string| variable, one character (bar or blank) per sibling. + +@< Global decl... @>= +char indent_string[100]=""; + +@ Children get printed +before siblings. +We don't bother trying to bring children up to the same line as their parents, +because the \caps{UNIX} filenames are so long. + +We define a predicate telling us when a sibling is the last in a series. +@d is_last(S) (S->sibling==NULL) + +@c +print_node(FILE* fp, char* indent_string, struct tnode* node) +{ + char string[255]; + int i; + char *p, *is; + + if (node==NULL) { + } + else { + *string='\0'; + for (i=strlen(indent_string); i>0; i--) + strcat(string,@, " | "); + strcat(string,@t\ \ @> " +--"); +@< Replace chars in |string| with chars from + line-drawing set and from |indent_string| @> + fprintf(fp,"%s%s\n",string,node->data); + +@) + /* Add vertical bar or space for this sibling (claim |*is=='\0'|) */ + *is++ = (is_last(node) ? ' ' : vert); + *is=='\0'; + + print_node(fp, indent_string, node->child); /* extended |indent_string| */ + *--is='\0'; + print_node(fp, indent_string, node->sibling); /* original |indent_string| */ + } + +} +@ For simplicity, we originally wrote connecting lines with |'|'|, |'+'|, and +|'-'|. +Now we replace those characters with appropriate characters from the +line-drawing set. +We take the early vertical bars and replace them with characters from +|indent_string|, and we replace the other characters appropriately. +We are sure to put a |corner|, not a |cross|, on the last sibling in +a group. +@< Replace chars... @>= + is=indent_string; + for (p=string; *p!='\0'; p++) switch(*p) { + case '|': *p=*is++; break; + case '+': *p=(is_last(node) ? corner : cross); break; + case '-': *p=horiz; break; + default: break; + } + + +@ For this simple implementation, we just write on standard output. + +@< Write... @>= print_node(stdout, indent_string, root); + +@*Index. diff --git a/web/c_cpp/cwebx/examples/wc.w b/web/c_cpp/cwebx/examples/wc.w new file mode 100644 index 0000000000..ce741c5f3c --- /dev/null +++ b/web/c_cpp/cwebx/examples/wc.w @@ -0,0 +1,226 @@ +% wc: An example of CWEB by Silvio Levy and Donald E. Knuth +% Adapted to CWEB version 3.0 by Marc van Leeuwen -- CWI Amsterdam + +\nocon % omit table of contents +\datethis % print date on listing + +@* An example of \.{CWEB}. This example presents the ``word count'' +program from \caps{UNIX}, rewritten in \.{CWEB} to demonstrate +literate programming in \Cee. The level of detail is intentionally +high, for didactic purposes; many of the things spelled out here don't +need to be explained in other programs. + +The purpose of \.{wc} is to count lines, words, and/or characters in a list +of files. The number of lines is the number of newline characters in the +file. The number of characters is the file length in bytes. A ``word'' is a +maximal sequence of consecutive characters other than white space, +containing at least one visible character. + +@ Most \.{CWEB} programs share a common structure. It's probably a good +idea to have one module that states this structure explicitly, even though +the elements could all be introduced in sections contributing to of the +unnamed module if they don't need to appear in any special order. + +@c + +@< Global variables @>@; +@< Functions @>@; +@< The main program @>@; + +@ We must include the standard I/O definitions, since we want to send +formatted output to |stdout| and |stderr|. We also use the character +classification macro |isgraph| to detect visible characters. + +@h <stdio.h> +@h <ctype.h> +@ The |status| variable tells the operating system if the run was +successful or not, and |prog_name| is used in case there's an error message to +be printed. + +@d OK 0 /* |status| code for successful run */ +@d usage_error 1 /* |status| code for improper syntax */ +@d cannot_open_file 2 /* |status| code for file access error */ + +@< Global variables @>= +int status=OK; /* exit status of command, initially |OK| */ +char *prog_name; /* who we are */ + +@ Now we come to the general layout of the |main| function. + +@< The main... @>= +main (int argc,char** argv) +{ + @< Variables local to |main| @>@; + prog_name=*argv++; --argc; /* process program name */ + @< Set up option selection @> + @< Process all the files @> + @< Print the grand totals if there were multiple files @> + exit(status); +} + +@ If the first argument begins with a `\.-' the user is choosing +the desired counts and specifying the order in which they should be +displayed. Each selection is given by the initial character +(lines, words, or characters). For example, `\.{-cl}' would cause +just the number of characters and the number of lines to be printed. + +We do not process this string now. It will be used to control the +formatting at output time. + +@< Var... @>= +int file_count; /* how many files there are */ +char *which; /* which counts to print */ + +@ @< Set up o... @>= +which="lwc"; /* if no option is given, print all three values */ +if (argc>0 && (*argv)[0] == '-') {@; which=&(*argv++)[1]; --argc; } +file_count=argc; + +@ Now we scan the remaining arguments and try to open a file, if +possible. The file is processed and its statistics are given. +We use a |do|~\dots~|while| loop because we should read from the +standard input if no file name is given. + +@< Process... @>= +do { + @< If a file is given try to open |*argv|; |continue| if unsuccesful @> + @< Initialize pointers and counters @> + @< Scan file @> + @< Write statistics for file @> + @< Close file @> + @< Update grand totals @> /* even if there is only one file */ +} while (++argv,--argc>0); + +@ Here's the code to open the file. We use the low-level functions |open|, +|read|, and |close| that operate work file descriptors rather than with +|FILE|s. A special trick allows us to handle input from |stdin| when no name +is given. Recall that the file descriptor to |stdin| is 0; that's what we +initialize our file descriptor to. + +@< Variabl... @>= +int fd=0; /* file descriptor, initialized to |stdin| */ + +@~@d READ_ONLY 0 /* read access code for system |open| routine */ + +@< If a fi... @>= +if (file_count>0 && (fd=open(*argv,READ_ONLY))<0) { + fprintf (stderr, "%s: cannot open file %s\n", prog_name, *argv); +@.cannot open file@> + status|=cannot_open_file; + --file_count; + continue; +} + +@ @< Close file @>= +close(fd); + +@ We will do some homemade buffering in order to speed things up: Characters +will be read into the |buffer| array before we process them. +To do this we set up appropriate pointers and counters. + +@d buf_size BUFSIZ /* \.{stdio.h}'s |BUFSIZ| is chosen for efficiency*/ + +@< Var... @>= +char buffer[buf_size]; /* we read the input into this array */ +register char *ptr; /* the first unprocessed character in |buffer| */ +register char *buf_end; /* the first unused position in |buffer| */ +register int c; /* current character, or number of characters just read */ +int in_word; /* are we within a word? */ +long word_count, line_count, char_count; /* number of words, lines, + and characters found in the file so far */ + +@ @< Init... @>= +ptr=buf_end=buffer; line_count=word_count=char_count=0; in_word=0; + +@ The grand totals must be initialized to zero at the beginning of the +program. If we made these variables local to |main|, we would have to +do this initialization explicitly; however, \Cee's globals are automatically +zeroed. (Or rather, ``statically zeroed.'') (Get it?) +@^Joke@> + +@< Global var... @>= +long tot_word_count, tot_line_count, tot_char_count; /* total number of words, lines and chars */ + +@ The present module, which does the counting that is \.{wc}'s {\it raison +d'\^etre}, was actually one of the simplest to write. We look at each +character and change state if it begins or ends a word. + +@< Scan... @>= +while (1) { + @< Fill |buffer| if it is empty; |break| at end of file @> + c=*ptr++; + if (isgraph(c)) /* visible character */ + {@; if (!in_word) ++word_count, in_word=1; } + else if (isspace(c)) + { in_word=0; /* |c| white space */ + if (c=='\n') ++line_count; + } +} + +@ Buffered I/O allows us to count the number of characters almost for free. + +@< Fill |buff... @>= +if (ptr>=buf_end) { + ptr=buffer; c=read(fd,ptr,buf_size); + if (c<=0) break; + char_count+=c; buf_end=buffer+c; +} + +@ It's convenient to output the statistics by defining a new function +|wc_print|; then the same function can be used for the totals. +Additionally we must decide here if we know the name of the file +we have processed or if it was just |stdin|. + +@< Write... @>= +wc_print(which, char_count, word_count, line_count); +if (file_count) printf (" %s\n", *argv); /* not |stdin| */ +else printf ("\n"); /* |stdin| */ + +@ @< Upda... @>= +tot_line_count+=line_count; +tot_word_count+=word_count; +tot_char_count+=char_count; + +@ We might as well improve a bit on \caps{UNIX}'s \.{wc} by counting the +files too. + +@< Print the... @>= +if (file_count>1) { + wc_print(which, tot_char_count, tot_word_count, tot_line_count); + printf(" total in %d files\n",file_count); +} + +@ Here now is the function that prints the values according to the +specified options. The calling routine is supposed to supply a +newline. If an invalid option character is found we inform +the user about proper usage of the command. Counts are printed in +10-digit fields so that they will line up in columns. + +@d print_count(n) printf("%10ld",n) + +@< Fun... @>= +wc_print(char* which, long char_count, long word_count, long line_count) +{ + while (*which) + switch (*which++) { + case 'l': print_count(line_count); break; + case 'w': print_count(word_count); break; + case 'c': print_count(char_count); break; + default: if ((status & usage_error)==0) { + fprintf (stderr, "\nUsage: %s [-lwc] [filename ...]\n", prog_name); +@.Usage: ...@> + status|=usage_error; + } + } +} + +@ Incidentally, a test of this program against the system \.{wc} command +on a SPARCstation showed that the ``official'' \.{wc} was slower. Furthermore, +although that \.{wc} gave an appropriate error message for the options +`\.{-abc}', it made no complaints about the options `\.{-labc}'! +Perhaps the system routine would have been better if its programmer had +been more literate? + +@* Index. +Here is a list of the identifiers used, and where they appear. Underlined +entries indicate the place of definition. Error messages are also shown. diff --git a/web/c_cpp/cwebx/examples/wmerge.w b/web/c_cpp/cwebx/examples/wmerge.w new file mode 100644 index 0000000000..4596e97d54 --- /dev/null +++ b/web/c_cpp/cwebx/examples/wmerge.w @@ -0,0 +1,52 @@ +% Adapted to CWEB version 3.0 by Marc van Leeuwen -- CWI Amsterdam +\noinx + +@* Introduction. This file contains the program |wmerge|, which takes two +or more files and merges them according to the conventions of \.{CWEB}. We +use the routines of \.{CWEB} itself. The function |common_init| takes care of +processing command line arguments. Since the result of the merge will be +produced on the standard output, we prevent distraction as much as possible +by clearing flags |'h'| and |'p'| (for |'b'| it is not required since no +banner is produced anyway. + +@h <stdio.h> +@h "../common.h" /* the header file for \.{CWEB}'s \.{common.w} */ +@c +@< Prototype @>@; +main (int argc,char** argv) +{ common_init(argc,argv); + flags['h']=flags['p']=0; + reset_input(); + while (get_line()) + put_line(); + wrap_up(); +} + +@ This file should be linked together with the object file produced from +|"common.w"|, which is also used in both |CTANGLE| and |CWEAVE|. +That file defines the functions |common_init|, |reset_input|, |get_line|, and +|wrap_up|. There are however a number of functions that are required by that +compilation unit although they are not actually used; we define them with +trivial function bodies. Since the linker doesn't check types anyway we +don't specify any here either. + +@c +void print_stats() @+ {} +void names_match () @+ {} +void init_module_name() @+ {} +void init_id_name () @+ {} + + +@ All that remains is to define |put_line| which is trivial. The external +variable |buffer| holds the characters read by |get_line|, up to |limit|, +and |loc| points to the next character to be read, i.e., after calling +|get_line| it points to |buffer[0]|. + +@< Prototype @>= void put_line(void); +@~@c + +void put_line(void) +{ + while (loc<limit) putchar(*loc++); + putchar('\n'); +} diff --git a/web/c_cpp/cwebx/intro.inc b/web/c_cpp/cwebx/intro.inc new file mode 100644 index 0000000000..49d0376e18 --- /dev/null +++ b/web/c_cpp/cwebx/intro.inc @@ -0,0 +1,88 @@ +\def\hang{\hangindent 3em\indent\ignorespaces} +\def\pb{$\.|\ldots\.|$} % C brackets (|...|) +\def\LKC.{Levy/Knuth \.{CWEB}} +\def\:#1{`\.{@@#1}'} + +\def\title{\me. (Version x3.04)} +\def\topofcontents +{\topglue 0pt plus .5 fill + \centerline{\titlefont The {\ttitlefont \me.} program} + \vskip 15pt + \centerline{(\.{CWEB} version x3.04)} + \vfill +} +\def\botofcontents +{\vfill\noindent + Copyright \copyright\ 1987,\thinspace1990 Silvio Levy and Donald E. Knuth + \par\noindent + Copyright 1994 Marc A. A. van Leeuwen + \bigskip\noindent + Permission is granted to make and distribute verbatim copies of this + document provided that the copyright notice and this permission notice + are preserved on all copies. + + \smallskip\noindent + Permission is granted to copy and distribute modified versions of this + document under the conditions for verbatim copying, provided that the + entire resulting derived work is distributed under the terms of a + permission notice identical to this one. +} + + +@* Introduction. @:title@> +This is the main source text for the program \.{\me.}, one of the two text +processing tools that constitute the \.{CWEB} system. This version of the +system, identified as \.{CWEB}x3.0, was written by Marc van Leeuwen. The +history of this system is somewhat complicated, and starts with the +\.{WEB} system created by D.~E. Knuth (for the programming language Pascal); +the program corresponding to the current program in that system was +called~\myroots. That program was converted into \.{\me.}, written in and +for the \Cee~language, by Silvio Levy, and further developed under joint +responsibility with Knuth. Based on \.{CWEB}~2.1 by Levy and Knuth, a +version adapted to \caps{ANSI~C} was made by Frank Jensen. That version has +served as the starting point for the current author; an intermediate version +between it and the current version was made public under the name +\.{CWEB}~3.x. Helpful comments and suggestions by Gareth McCaughan, Werner +Lemberg, and Andreas Scherer were greatly appreciated. + +The name \.{CWEB}~3.x was an unfortunate choice, since the Levy/Knuth +version of \.{CWEB} had also been independently developed further, and it is +currently being distributed as \.{CWEB}~3.4f. Both these branches have +retained the basic functionality of the version of \.{CWEB} they were both +derived from, making only minor (but different) additions and changes. +Realising that this divergence is undesirable and confusing, the development +of this branch from \.{CWEB}~3.x to the current version was mainly concerned +with improving compatibility and peaceful coexistence with the other branch. +First of all the name was changed by moving the character~`x' to the front +of the version number, so that this branch of the system can be referred to +as `\.{CWEBx}' without mentioning a complete version number; for clarity we +shall refer to the other branch as `\LKC.'. @^Levy/Knuth \.{CWEB}@> +Secondly, all the extensions of \LKC. were included, although in a few cases +in a slightly different form to avoid name conflicts with features already +present in \.{CWEB}~3.x. Thirdly, in order to accomodate existing programs +written for \LKC., among which are those of Knuth's impressive collection +called the Stanford GraphBase, a ``compatibility mode'' was added, +selectable by a command line option (\.{+c}), in which the remaining, mostly +trivial, differences in syntax and semantics are removed, and the system +attempts to be an alternative implementation for exactly the same language +as accepted by \LKC., at the price of losing a few possibilities particular +to~\.{CWEBx}. + +The major version number `3' now corresponds to the same version number of +\LKC., and since no further extension of the possibilities of that system +are anticipated, we do not expect that compatibility will ever require any +further increase of this major version number. The Stanford GraphBase +@^Stanford GraphBase@> states that it requires \.{CWEB} version 3.0 or +greater; this version of \.{CWEBx} can fully process it in a satisfactory +way, using compatibility mode. + +The ``banner line'' defined here should be changed whenever changes to +\.{\me.} are made publicly available. Users who are discontent with some +aspect of the program are encouraged to make the necessary modifications in +their copy of the source files; if this involves an improvement of the +implementation, they are kindly requested to inform the author responsible +for the current version. If they choose to make the resulting program +available under the name \.{\me.}, they should modify the non-numeric +prefix of the version specification. + +@d version_string "3.04" diff --git a/web/c_cpp/cwebx/man1/cweb.1 b/web/c_cpp/cwebx/man1/cweb.1 new file mode 100644 index 0000000000..5f67708b44 --- /dev/null +++ b/web/c_cpp/cwebx/man1/cweb.1 @@ -0,0 +1,165 @@ +.TH WEB 1L 7/12/90 +.SH NAME +ctangle, cweave \- translate CWEB to C (or C++) and/or TeX +.SH SYNOPSIS +.B ctangle +[ +.B \-bhpl +] [ +.B \+cs+ +] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.c]]] +.br +.B cweave +[ +.B \-bhpx +] [ +.B \+cs+dtefauwm +] [ +.B +isearchpath +] cwebfile[.w] [(changefile[.ch]|+|-) [outputfile[.tex]]] + +.SH DESCRIPTION +The +.I ctangle +program converts a CWEB +source document into a C program that may be compiled in the usual way. +The output file includes #line specifications so that error reporting and +debugging can be done in terms of the CWEB source file. +.PP +The +.I cweave +program converts the same CWEB file into a TeX file that may be +formatted and printed in the usual way. +It takes appropriate care of typographic details like page layout +and the use of indentation, italics, boldface, etc., and it supplies +extensive cross-reference information that it gathers automatically. +.PP +CWEB allows you to prepare a single +document containing all the information that is needed both to produce +a compilable C program and to produce a well-formatted document +describing the program in as much detail as the writer may desire. The +user of CWEB ought to have some familiarity with TeX as well as knowledge +of C. +.PP +The command line should have one, two, or three names on it. The first is +taken as the CWEB file (and `.w' is added if there is no extension). If that +file cannot be opened, the extension `.web' is tried instead. If there is a +second name, it is a change file (and `.ch' is added if there is no +extension), otherwise the name of the CWEB file with its extension replaced +by `.ch' is tried as change file name; if there no such file, no change file +is used. Instead of a change file one may specify `-' or `+'; `-' means do +not use a change file, and `+' means use a change file only if a file of the +default name exists (just as if only one name was given). The change file +overrides parts of the CWEB file, as described in the documentation. A third +name, if present, specifies the output file name explicitly. The default +output file name is that of the CWEB file with extension `.c' (for +.I ctangle +) or `.tex' (for +.I cweave +); the default extension is also applied if a third +name is given without extension. +.PP +Options in the command line may be either turned on with `+' +and off with `-'; several characters may follow in the same argument, which +indicate several options that are turned on respectively off simultaneously. +In fact, the options are processed from left to right, +so a sequence like `-b +b' is equivalent to `+b' (which is the default). +.PP +The +.B \+b +option shows a banner line on your terminal. The +.B \+h +option shows a happy message if the processing was successful. The +.B \+p +option shows progress reports (starred module numbers) as the processing +takes place. If you say `-bhp', you get no terminal output but error messages. +.PP +The +.B \+c +option emulates Levy/Knuth CWEB. +The +.B \+s +option prints statistics about memory usage at the end of a run +(assuming that the programs have been compiled with the -DSTAT switch). +By setting the option +.B \++ +one can select the C++ language instead of C. +.PP +The option +.B \-l +is applicable to +.I ctangle +only; when selected, the C output is made more readable for humans by omitting +.B #line +directives and preserving layout and comments. +.PP +There are several other options applicable to +.I cweave +only. The option +.B \-x +means omit the index and table of contents. By setting the option +.B \+d +one causes +.I cweave +to report problems encountered in parsing the program fragments. With +.B \+t +the the index and module names will be written to separate files. With +.B \+e +the table of contents is forced to start on a new sheet when using a +two-sided printer. +The options +.B \+f +, +.B \+a +, +.B \+u +, +.B \+w +, and +.B \+m +change the style in which programs are +formatted: +.B \+f +means force a newline after every simple statement in the formatted output, +and +.B \+a +means also force a newline before every simple statement, even if it follows +a label or a condition; the option +.B \+u +will change the style so that a line break goes after `{' instead of before, +thus preventing them from aligning with their matching `}' (this is the +style of Levy/Knuth CWEB), while the option +.B \+w +puts a line break both before and after `{', producing a more symmetric +layout at the expense of some extra paper. Finally, the option +.B \+m +avoids forcing line breaks between successive declarations in a local block. +With the option +.B \+isearchpath +one can specify a place to look for header files, when they are not found in +the current directory. The string following the `+i' will be stored and +preprended to a header file name if it cannot be found otherwise (so the +string should probably end with a pathname separator '/'). More than one +such search path can be specified; they will be tried in order from left to +right. +.PP +.SH FILES +/usr/local/lib/tex/macros/cwebxmac.tex TeX macros used by cweave output. +.br +/usr/local/lib/tex/macros/cwebcmac.tex TeX macros used in compatibility +mode. +.br +/usr/local/doc/cweb/manual.tex The user manual. +.SH "SEE ALSO" +.I Literate Programming +by D. E. Knuth. (CSLI, Stanford, 1992) +.br +tex(1), cc(1), web(1) +.SH "AUTHORS" +Don Knuth wrote WEB for TeX and Pascal. Silvio Levy designed and developed +CWEB by adapting the WEB conventions to C and by recoding everything in CWEB. +Frank Jensen adapted the programs to ANSI/ISO C, and Marc van Leeuwen made +numerous changes for version x3.0, and wrote a new manual. +.SH "BUGS" +The syntactic support for C++ does not cover that language completely, so +formatting of complex C++ code may be bad. diff --git a/web/c_cpp/cwebx/manual.dvi b/web/c_cpp/cwebx/manual.dvi Binary files differnew file mode 100644 index 0000000000..f8c4a007af --- /dev/null +++ b/web/c_cpp/cwebx/manual.dvi diff --git a/web/c_cpp/cwebx/manual.pdf b/web/c_cpp/cwebx/manual.pdf Binary files differnew file mode 100644 index 0000000000..781e87873e --- /dev/null +++ b/web/c_cpp/cwebx/manual.pdf diff --git a/web/c_cpp/cwebx/manual.tex b/web/c_cpp/cwebx/manual.tex new file mode 100644 index 0000000000..421542ad4c --- /dev/null +++ b/web/c_cpp/cwebx/manual.tex @@ -0,0 +1,2558 @@ +\input cwebxmac + +\def\title{\CWEBx. MANUAL}\titletrue +\parskip=0pt plus 2pt + +\font\boldit=cmbxti10 +\font\twelverm=cmr12 +\font\twelvett=cmtt12 + +\def\lheader{\mainfont\the\pageno\eightrm\qquad\firstmark\hfill\title} +\def\rheader{\eightrm\title\hfill\firstmark\qquad\mainfont\the\pageno} + +\countdef\secno=1 +\let\oldbeginsection=\beginsection +{\let\oldbeginsection=\relax % temporarily remove outerness + \outer\gdef\beginsection #1 \par + {\global\advance\secno by 1 + \oldbeginsection{\number\secno\space\space#1}\par + \edef\next{\write\cont{\Z{\number\secno}{#1}{\noexpand\the\pageno}}}\next + \uppercase{\mark{#1}}% +}} + + +\let\z\relax +\def\subsection#1 \par +{\smallskip\noindent{\boldit #1}\quad + {\def\.{\noexpand\noexpand\noexpand\.}% + \def\do##1{\def##1{\noexpand\noexpand\noexpand##1}}\dospecials + \edef\next{\write\cont{\z{#1}{\noexpand\the\pageno}}}\next +}} + +\def\pb{\.{|...|}} +\def\<#1>{\hbox{\rm\def\ { }$\langle\,$#1$\,\rangle$}} +\def\sp.{structured programming} +\def\lp.{literate programming} +\def\Lp.{Literate programming} +\def\LP.{Literate Programming} +\def\WEB.{\caps{WEB}} +\def\ASCII.{\caps{ASCII}} +\def\CWEB.{\.{CWEB}} +\def\CWEBx.{\.{CWEBx}} +\def\LKC.{Levy/Knuth \CWEB.} +\def\:#1{`\/\.{@#1}'} +\def\atspace.{\:{ }} + +\outer\def\N#1 #2. #3. %start of `@*' section + {\MN#2.% + \ifon \vskip 0pt plus .5 \vsize \penalty-1000\vskip 0pt plus -.5 \vsize + \vskip\intersecskip + \startsection{\bf#3.\quad}% + } +\def\MN#1.% common code for \M, \N, \n + {\par\xdef\secstar{#1}\ontrue}% no \mark, no starred sections +\def\inx + {\par\vskip6pt plus 1fil \endcodemode % we are beginning the index + \output=\bufferedoutput\pagesavedfalse\eject % eject pages, keeping last + \setbox\sbox=\vbox{\unvbox\sbox} % allow its glue to reset + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output= + {\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else + \normaloutput + {\vbox to\pageheight + {\box\sbox \vss \hbox to\pagewidth{\box\lbox \hfil \page}}% + }% page argument to \normaloutput + \lheader\rheader % other two arguments to \normaloutput + \global\vsize\pageheight \gdef\lr{L} \global\pagesavedfalse + \fi + }% + \parskip 0pt plus .5pt + \outer\def\@##1##2, % index entry; ##1 is `h' or `m' for horiz/math mode + {\par\hangindent2em\noindent\if##1m$##2$\else##2\fi:\kern1em} + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + } +\def\fin{\let\@\relax\con}% make these innocuous before scanning argument: +\def\con#1\con{% ignore stuff after index + \par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % needed in case index is empty + \if R\lr \null\vfill\eject\fi % finish the current page + \parfillskip 0pt plus 1fil % restore normal paragraph end + \setpage % restore page shape + \output={\normaloutput\page\lheader\rheader} % restore output routine +} + +\centerline{\titlefont Literate Programming in C} +\medskip +\centerline{\twelverm The {\twelvett CWEB} System + of Structured Software Documentation} +\medskip +\centerline{Manual for \CWEB.x3.0} +\bigskip +\centerline{Marc A. A. van Leeuwen} + +\bigskip\bigskip + + +\beginsection Overview + +This document describes \CWEB.x3.0, a particular implementation of \CWEB., a +system that supports the concept of ``\lp.'' for programs written in the +language~\Cee\ (more particularly for this version, in \caps{ANSI/ISO} \Cee). +As this manual aims to supply all information possibly relevant to a wide +variety of users, it is necessarily rather extensive. However, \CWEB. is not +a complicated system, and just a few simple commands suffice for practical +programming purposes; these are discussed in the section~4 (the remainder +serves mainly to allow fine-tuning of the presentation of the printed +documents describing literate programs). + +As the somewhat contrived name of the system indicates, \CWEBx. is not the +only version of \CWEB.; indeed it is based on an older version of \CWEB. by +Silvio Levy, which is an adaptation to~\Cee\ of the \.{WEB} system (for +Pascal) written by Donald~E. Knuth, the founder of \lp.. That \CWEB. system +has independently evolved into a system that is currently distributed, under +joint responsibility of Levy and Knuth, as \CWEB.~3.4. Those with experience +using \LKC. will already be acquainted with most aspects of \CWEBx., and may +turn to section~9 for a summary of the differences between the two systems; +however, \CWEBx. also provides a compatibility mode (selectable by specifying +`\.{+c}' on the command line) in which it should be able to process +\Cee~programs written using \LKC. without any modification. + +The structure of this manual is as follows. In section~2 an exposition of +the ideas underlying the concept of \lp. is given, and a description of how +systems of the \.{WEB} family provide concrete tools to support this +programming methodology. This section is directed particularly to those who +are new to \lp.; it explains the purpose of \CWEB. and the logical +connection between the various elements that \lp. adds with respect to +traditional programming. As an illustration we then give a small example +program using \CWEB. in section~3. The main commands of \CWEB., which define +the structure of the source text and tell the \CWEB. tools what to do with +the various pieces of that text, are discussed in section~4. In section~5 we +discuss how the behaviour of \CWEB. tools can be further affected by means +of command line options and file name arguments supplied when the programs +are invoked. In section~6 we discuss some facilities for distributing the +source text over several input files, including a ``change file'' that +allows applying local patches without affecting the original source files; +this is not directly related to \lp., but can be quite useful in larger +projects. In section~7 the remaining minor \CWEB. commands are explained, +and section~8 discusses some features of the \TeX\ format employed by +\CWEB., which can be used to affect the appearance of \CWEB. documents. +Section~9 is devoted to a comparison with \LKC.; finally section~10 +summarises all \CWEB. codes recognised in the source text. + + +\beginsection About literate programming + +\Lp. is a concept that was developed, implemented, and propagated by D.~E. +Knuth in the early 1980's, as a natural sequel to the concept of ``\sp.'' +that had caused a revolution in the world of software development about a +decade earlier. (At this moment, another decade further, one may conclude +that \lp. has not caused a similar revolution, since many programmers +practicing illiterate programming do not feel at all as guilty about this as +they would if they were to be found practicing unstructured programming.) So +let us first consider the idea of \sp. more closely. + +\subsection Structured programming + +Without attempting a definition of the term, it seems fair to say that \sp. +involves designing a program by hierarchical decomposition of the task at +hand, and constructing a program that has a similar decomposition into parts, +where each part ``solves'' the corresponding subtask. The subdivision of the +program manifests itself in its division into subroutines (procedures, +functions), and at the more detailed level in the syntactic composition of +those routines (control structures, blocks); this explains why much emphasis +is placed on the use of subroutines, and why languages with a linear program +model and (conditional) jumps as their main control structure (like assembly +language) do not form a natural vehicle for \sp.. The question as to which +criteria should be used in subdividing problems into smaller ones is +difficult to answer in general, but a good rule of thumb is that at the +level of abstraction at which a task is defined it should be possible to +give a reasonably simple informal description of its subtasks, in which a +new level of detail about the method used to perform the task is given. (It +would be nice if the subtasks also had simple formal specifications, but in +general this is too much to ask, since informal descriptions do not only +abstract from implementation details, they may also conceal numerous obvious +specification details). + +It can be said that the idea of \sp., undone of any dogmatism that has been +associated with it, has been rather universally accepted, and has proven to +be an effective methodology in software development. Other methodologies +have been put forward, such as data encapsulation, but this has been in +addition to rather than as an alternative for \sp.. \Lp. however is related +in a different way to \sp., as it concerns not the contents of the program +itself, but rather the way it is presented. \Lp. presupposes \sp., but is +independent of other programming paradigms; any program that has been +designed in a structured way can in principle be rendered as a literate +program, without requiring a change of the program text itself. + +\subsection Limitations of traditional \sp. + +Although the composition of a structured program should reflect the design +decisions that led to its construction, the traditional way of presenting +such programs lacks appropriate facilities for communicating this +information effectively to readers of the program, seriously limiting the +readability, especially to people other than the programmer of the code. Yet +readability is of vital importance, because it is only by careful reading +that we can verify that the design of a program is sound and +well-implemented, and to understand where and how changes can be made when +such a need arises. Of course the code may be documented by adding an +arbitrary amount of comments, but there are various reasons why this has a +limited effectiveness, so that in practice the level of documentation is +nearly always (much) lower than would be desirable from the point of view of +maintainability. + +The syntactic decomposition of a piece of a structured program into a +hierarchy of control structures, compound statements, etc., is crucial to +understanding the way it functions, yet the human eye is much less capable +of performing this task then a parser is, even when proper indentation is +applied. The difficulty rapidly increases with the size of the program +fragment, and can become a serious factor when this size exceeds a dozen or +two lines (depending on the complexity of the structure). And even when we +can recognise the structure, the meaning of the individual parts cannot be +immediately perceived, but must be derived from close inspection of the +code, or from the comments. However, as comments are localised in the code, +and it is hard to attach a comment clearly to a construct of some +considerable extent. Also, adding too many comments may actually decrease the +readability of the program, by making the structure recognition problem +worse. Even indentation, useful as it may be, can be more of a nuisance than +of any help when it becomes so deep that it forces code and comments to be +squeezed together into (or fall off) the right margin. Finally the fact that +program sources are usually plain text files, represented in a single rather +crude font, does not improve human readability. The limitations of the +character set hamper formulation of comments, where special symbols, +formulae, tables or illustrations might convey the information much more +effectively; in the program itself the eye has to do without visual clues +marking the distinction between various types of program elements +(identifiers, keywords, constants, literal strings, operators, comments, +etc.). As a consequence of all this, few people will find much pleasure in +reading source listings, even if the program is well designed and +documented, and possibly even contains some interesting and subtle +algorithms. + +Many of these problems would not be too grave if all subroutines were +severely limited in size and complexity. However, although it might be +possible to live up to such a restriction for certain kinds of programs (in +particular if the task involves mostly simple actions of administrative +nature rather than any really complicated algorithms), it would be a very +impractical requirement in general, certainly for procedural programming +languages. First of all, having to break up a subroutine using auxiliary +subroutines solely because of the size of the code, violates the basic +principle that such a decomposition should be the result of design +decisions. Furthermore, there are several technical reasons why such a +decomposition could be either impossible, or might involve a large amount of +additional code that has little to do with the actual task being performed, +and that might deteriorate performance unacceptably. Examples of such +reasons are for instance the need to perform a large multi-way branching, to +have local variables that are visible throughout the execution of a complex +algorithm, or to have the possibility to jump out of nested structures on +certain (error) conditions (while the language might not allow jumps out of +a subroutine into the routine calling~it). Finally, introducing many small +subroutines, for reasons that cannot be described easily outside the +immediate context in which they will be used, creates a serious problem of +giving them sensible names and remembering the tasks they perform. + +Concluding we may say that \sp. in its traditional form does not encourage +or even allow the level of documentation that would be desirable for +maintenance and intelligibility by people other than the author of the +program. + + +\subsection Requirements for \lp. + +The basic idea of \lp. is to take a fundamentally different starting point +for the presentation of programs to human readers, without any direct effect +on the program as seen by the computer. Rather than to present the program +in the form in which it will be compiled (or executed), and to intercalate +comments to help humans understand what is going on (and which the compiler +will kindly ignore), the presentation focuses on explaining to humans the +design and construction of the program, while pieces of actual program code +are inserted to make the description precise and to tell the computer what +it should do. The program description should describe parts of the algorithm +as they occur in the design process, rather than in the completed program +text. For reasons of maintainability it is essential however that the +program description defines the actual program text; if this were defined in +a separate source document, then inconsistencies would be almost impossible +to prevent. If programs are written in a way that concentrates on explaining +their design to human readers, then they can be considered as works of +(technical) literature; it is for this reason that Knuth has named this +style of software construction and description ``\lp.''. More background +information about this concept and its history concept can be found in +Knuths book ``\LP.'', CSLI Lecture notes~\#27, Leland Stanford Junior +University, 1992 (ISBN 0-937073-81-4). + +From the discussion above it will be clear that traditional programming +languages are not directly suitable for \lp.. We shall now try to formulate +requirements for a system that supports \lp.. Doing so we shall especially +keep in mind programs that evolve after their original design, possibly +altering certain parts of that design, and possibly being realised by +different persons, since it is in such cases that the benefits of \lp. are +particularly crucial. + +The documentation parts of the program description should allow for the same +freedom of expression that one would have in an ordinary technical paper. +This means that the document describing the program should consist of +formatted text, rather than being a plain text file. This does not exclude +the possibility that the source is written as a plain text file, but then it +should undergo some form of processing to produce the actual program +description. The document should moreover contain fragments of a program +written in some traditional (structured) programming language, in such a way +that they can be mechanically extracted and arranged into a complete +program; in the formatted document on the other hand layout and choice of +fonts for these program fragments should be so as to maximise readability. + +Parts of the program that belong together logically should appear near to +each other in the description, so that they are visible from the part of the +documentation that discusses their function. This means that it should be +possible to rearrange program text with respect to the order in which it +will be presented to the computer, for otherwise the parts that deal with +the actions at the outer level of a subroutine will be pushed apart by the +pieces specifying the details of inner levels. The most obvious and natural +way to do this is to suppress the program text for those inner levels, +leaving an outline of the outer level, while the inner levels may be +specified and documented elsewhere; this is a bit like introducing +subroutines for the inner levels, but without the semantic implications that +that would have. There should be no restrictions on the order in which the +program fragments resulting from this decomposition are presented, so that +this order can be chosen so as to obtain an optimal exposition; this may +even involve bringing together fragments whose location in the actual +program is quite unrelated, but which have some logical connection. + +Obviously there should be a clear indication of where pieces of program have +been suppressed, and which other program fragments give the detailed +specifications of those pieces. From the programming language point of view +the most obvious method of identification would be to use identifiers, +resulting in a simple system of parameterless macros, with as only unusual +aspect that uses of the macro are allowed to precede the definition, and +indeed do so more often than not. Actually, \lp. uses a method that differs +from this only trivially from a formal standpoint, but has a great advantage +in practical terms: identification is by means of a more or less elaborate +phrase or sentence, marked in a special way to indicate that it is a +reference to a program fragment. This description both stands for the +fragment that is being specified elsewhere, and also serves as a comment +describing the function of that fragment at a level of detail that is +appropriate for understanding the part of the program containing it. In this +way several purposes are served at once: a clear identification between use +and definition is established, the code at the place of use is readable +because irrelevant detail is suppressed, with a relevant description of what +is being done replacing it, and at the place of definition a reminder is +given of the task that the piece of code presented is to perform. The +documenting power of such a simple device is remarkable. In some cases the +result is so clear that there is hardly any need to supply further +documentation; also it can sometimes be useful to use this method to replace +small pieces of somewhat cryptic code by a description that is actually +longer than the code itself. It is hard to give a sharp limit on the length +of the description for a program fragment, but if substantially more than a +sentence is needed, say a full paragraph, then the fragment probably does +not represent a well chosen abstraction, which might be an indication that +the design of the program has some room for improvement. On the other hand, +it is good practice to explicitly mention any unusual control flow that +might be caused by executing the abstracted fragment, like jumping to a +label outside the fragment, since such information is vital for a proper +understanding of the program at the place where the fragment is used. + +\subsection WEB systems for \lp. + +Until now we have discussed \lp. as a general concept, independent of any +particular implementation; this was done to stress the generality of the +idea. We shall now indicate how these ideas are realised by ``\WEB. +systems'', a family of systems that were modelled after Knuth's original +\.{WEB}, and of which \CWEB. is a member. In these systems the program +source is written as a plain text file, and a pair of programs is provided, +which transform this source into other text files suitable for processing by +a compiler respectively by a typesetting program. Other kinds of \lp. tools +are conceivable (e.g., ones that would provide the programmer with a direct +graphical representation of the typeset document while editing, possibly +with hypertext facilities), but this approach has the advantage of being +fairly simple and portable across many platforms. The approach is not the +simplest possible however, as a substantial part of the work done by the +tools deals with transforming the program fragments from their plain text +form into typeset text with proper fonts and layout (i.e., with +pretty-printing); this part of the task also depends essentially on the +programming language being used. By reverting to verbatim representation of +program fragments one could make simple tools that support \lp. in a +language independent way---and indeed such tools exist---but then a price is +paid in terms of readability. We should also note that \WEB. systems support +writing documents whose purpose is to simultaneously specify and document a +program; if one is primarily writing a theoretic paper, in which only +occasionally pieces of program text are mentioned, then one might prefer a +slightly different kind of system that does not impose as much global +structure on the document as \WEB. systems do. + +Not surprisingly, \WEB. systems satisfy all the requirements for \lp. +formulated above, and they do so in a fairly straightforward manner. A +typeset \WEB. document consists of a sequence of consecutively numbered {\sl +sections}, whose size is typically less than half a page. Each section may +contain a program fragment, called a {\sl module}, preceded by a commentary +in ordinary text, although in some cases either one of these parts might be +absent. (The programming language used for the program fragments depends on +the particular \WEB. system used, as does the typesetting system that +eventually produces the printed document; for the \CWEB. system described +here these are respectively \Cee\ and \TeX.) In most cases a module is +headed by a text in angle brackets called its {\sl module name}, which gives +a description of the task it performs. This name is followed by `$\EQ$' and +the program code that constitutes the module itself; this is called a +defining occurrence of the module name. (We make a distinction between the +words `section' and `module', using the former for a numbered portion of the +\WEB. document, and the latter for a named portion of the program described +by it.) A module name can also be used in the body of some other module, +either before or after its definition in the document, to represent the +corresponding piece of program text. \WEB. facilitates locating the +definition of a module from the place where it is used by automatically +incorporating the number of the defining section in the module name. The +actual program text is then constructed by recursively replacing module +names by the text specified in their definition (this should of course +follow the grammatical structure of the program, lest the most basic +principles of \lp. be violated). A program fragment occurring at the +outermost level is distinguished by the fact that it is not headed by a +module name. + +Most module names will have just one defining occurrence, and will also be +referenced just once; in both cases there may however be exceptions, where a +module name has more than one occurrence of the indicated kind. If a module +is multiply referenced, this simply means that the corresponding part of the +program text is repeated identically in more than one place. If a module +name has more than one defining occurrence, then the text of the +corresponding module is obtained by concatenating the program fragments of +all its definitions in the order in which they occur in the document. In a +similar fashion all fragments without module name are combined into an +``unnamed module''. These are the only occasions where the order in which the +sections are given can have any effect on the final program; apart from this +the literate programmer has complete freedom of ordering the sections in a +way that facilitates understanding the program as much as possible. + +We need not discuss all aspects of \WEB. systems here, but a few points that +contribute to \lp. by improving readability should be mentioned. The proper +formatting of all program fragments is automatically taken care of by the +\WEB. system, providing a uniform style of presentation. The system also +provides a large amount of cross-reference information; this greatly +facilitates reading the program and searching for specific pieces of code. +Not only is the number of the (first) section defining a module incorporated +in its name, but in that defining section indications are also given of the +section(s) in which the module is used, and possibly of any further defining +occurrences of the same module name. At the end of the document an +alphabetically sorted index is added, listing for each identifier all the +sections in which it is defined or used; the programmer may also add +additional entries to the index by indicating in the program source that +certain sections should be referenced for particular terms. A list of all +module names is also given, which may help locating the part of the program +dealing with some issue. So in many ways the \WEB. system tries to aid human +understanding of the program, but of course the literacy of the programmer +will always remain the decisive factor in this respect. + +\beginsection What a\/ {\tentt CWEB} program looks like + +Enough now of general considerations, let us turn our attention to the +\CWEB. system this manual is really about. The best way to learn about it is +probably to read a \CWEB. document. Therefore we include a small but +complete \CWEB. program below; the program is about as small as possible +without rendering a decomposition into modules pointless. The example is not +intended as a showpiece of programming literacy, but it demonstrates various +aspects of the system. The index at the end of the program is included, but +not the list of the four module names in this program or the table of +contents. One will notice that some symbols appear to be different from +their official representation in \Cee, for instance the assignment +operator~`\.=' appears as~`$\K$', the equality operator~`\.{==}' as~`$\E$', +the logical ``and'' and ``or'' operators `\.{\&\&}'~and~`\.{||}' as +`$\W$'~and~`$\V$' respectively, the variable~`\.{f1}' as~`$f_1$', and the +null pointer~`\.{NULL}' as~`$\NULL$'; thus the possibilities of the +typesetting system are used to improve the appearance of the program. + +\vfil\eject + +\begingroup \input compare \par\endgroup + +\subsection Some remarks about the example program + +Reading the program should not cause great problems to anyone familiar with +the \Cee\ language, once one gets used to the representation of the symbols. +We mention a number of points that will have become clear in the course of +the example. + +The commentary text at the beginning of the sections is set in ordinary +paragraphs, which contrasts sufficiently with the appearance of the program +text that the dividing line between the two can be easily perceived, even +though it is only marked by a bit of white space. In case the section +defines (part of) a named module, the module name heading the program +fragment is set flush left, and is followed by~`$\EQ$', or in case this is +not the first defining occurrence of that name, by~`$\PE$' (therefore, the +occurrence of the module name `$\X5:Functions\X$' in \Sec2 is not a defining +one, whereas the occurrences of that name in \Sec5 and~\Sec7 are). The style +in which the module names and comments contained in the program fragments are +set is similar to that of ordinary text; indeed if they are too long to fit +on the line, they will be broken across lines (with proper care taken to +respect the indentation level). In \CWEB. embedded comments are always +attached to the right of a program element (usually a statement or +declaration); in the example we can see there is relatively little need for +embedded comments, because of the other means provided for documentation. +An embedded comment that is split across lines will not look very good, and +should only occur in cases of emergency; in most cases it is better to use +the commentary part at the beginning of the section for any elaborate +explanation. On the other hand, long module names (occupying up to about +four lines) are not uncommon when the task performed by the module calls for +an extensive description. + +As one can see in the example, it is common to refer to small pieces of +\Cee~code (in most cases just variables or simple expressions) from within +the commentaries, module names and comments. The \CWEB. system makes it easy +to include such pieces, by providing a variant of the formatting routines +used for the actual program fragments (differing from them by the omission +of any 2-dimensional layout features such as indentation). In many cases the +pieces of \Cee~code are so simple that they could easily be typeset directly +(using \TeX's math mode), producing the same formatted output without using +the facilities of \CWEB.. But even then it is preferable to use \CWEB. +instead, because it will then guarantee that all identifiers mentioned in +such a way in the documentation part of a section or in a comment, will be +included in the index at the end of the program. Although in many cases a +reference would have been generated anyway by the program fragment in the +same section (as happens in all cases for our example program), this +mechanism ensures that even remarks about the use of variables and functions +made in sections that contain no program fragment at all can be traced from +the index. Incidentally, identifiers that are used only in a module name are +not indexed, which is why there is no reference to \Sec2 in the index entry +for~\\{exit}. + +When an index entry is recorded, whether from within a program fragment or a +piece of \Cee~code embedded in text, the occurrence may be flagged as +`defining', depending on the context; this happens for instance in the case +of parameters in an \caps{ANSI/ISO} style function heading, of variable +declarations and of labels. If at least one occurrence of an identifier in +some section is a defining one, then the corresponding section number in the +index entry for that identifier will be underlined. Single-letter +identifiers, the special identifier \.{NULL} (appearing as `$\NULL$'), and +keywords of the language are considered so ubiquitous that no index +references for them are generated, except those that are underlined; e.g., +in the example there is no reference to \Sec6 for the variable~$n$. For +keywords this means that they will not appear in the index at all (unless +the programmer explicitly marks certain occurrences as defining); note +however that identifiers defined in a \&{typedef} declaration (like \&{bool} +in the example) will be indexed, even though they are set in boldface just +like keywords are. + +\subsection Further attributes of\/ {\tentt CWEB} programs + +An aspect of \CWEB. programs that does not stand out very clearly in our +miniature example is that it allows sets of related sections to be grouped +together into ``chapters''. Each chapter is identified by its title, which +appears in boldface after the number of its first section; in our example +sections 1~and~8 start new chapters. The division into chapters has a few +more effects on the document, which were suppressed in our example, since +they would interfere with the overall structure of this manual: each chapter +starts on a fresh page, its title appears in the running head of all its +pages, and all chapter titles are collected in the table of contents. +(Style changes such as employed in this manual are easy to obtain, since the +style is not determined by the \CWEB. system, but rather by a separate +format consisting of \TeX~macros; a few small changes to standard format can +change the overall appearance of the document, and it would be equally easy +to change for instance the page size or the symbols used to represent +operators.) + +There is one important point left to explain about the example, which is the +special position of the lines starting with \&{\#define} and \&{\#include}. +Although they look like ordinary preprocessor lines, which +could have been included in the program fragments, they are in fact separate +items that are given between the documentation part and the program part of +a section (this can be seen best in~\Sec6), forming a third type of +constituent of sections (although in most sections they will be absent). +Their place in \CWEB. is less distinctive then that of their analogues in +\WEB. systems for languages that have no preprocessor (like the original +\.{WEB} for Pascal, which provides a separate macro facility itself): indeed +the directives are just passed on to the \Cee\ preprocessor. Yet there is +some advantage in specifying them as special items to \CWEB., and in most +cases using these facilities is preferable to embedding the directives in +the \Cee~program fragments. + +One reason is that one usually wants the effects of preprocessor directives +to be visible throughout the \Cee~file that is generated, while this would +not always be the case if they were specified inside the program fragments; +for instance if the definition of \\{read\_mode} in \Sec6 had been included +in the program fragment, it could not have been validly used in \Sec7, +because that section will precede~\Sec6 in the \Cee~file produced. This +difficulty could be overcome by collecting all macro definitions in a module +that is used at the start of the program and defined in many sections +throughout the \CWEB. document. In fact this is just about how \CWEB. treats +the separately specified preprocessor directives: they are collected in +order of appearance, and placed at the very beginning of the \Cee~file. +(Some other place of insertion for the preprocessor directives can be +specified by means of a pseudo-module named `$\ATP$', but this is quite +rare.) Since a section can define only one module, the \CWEB. facility for +preprocessor directives may help avoid having to split up sections merely +because they contain such a directive. Furthermore, an important reason to +specify \&{\#include} directives to \CWEB., is that this allows it to +inspect those header files for any typedef declarations, so that programs +can be formatted properly; without this programs using typedef identifiers +defined in header files would seriously confuse the syntax analysis that +\CWEB. performs, resulting in very poor quality formatting of program +fragments. + +Preprocessor directives other than those mentioned above can only be +incorporated in a program by including them in an ordinary program module, +but there is relatively little need for such directives. In situations where +one would use conditional compilation in ordinary~\Cee, one can usually use +the ``change file'' mechanism provided by \CWEB. instead (this will be +discussed below), especially if it involves system dependent modifications; +this has the advantage that such modifications do not affect the main source +files, and only those modifications that are actually applied will be +visible in the \CWEB. document. In the rare cases that one does include a +preprocessor directive in a program fragment, the fact that it is not being +specified as a separate item to \CWEB. is usually easy to recognise in the +\CWEB. document, because the module name being defined or some program text +precedes it; however even if this should not be the case then such embedded +directives can still be distinguished by a slight difference in horizontal +and vertical spacing. + +\subsection Output to multiple files + +There is one important construction one may encounter in \CWEB. documents, that +we have not mentioned yet. There may be module names that consist of a file +name in typewriter type, like `$\X14:\.{common.h}\X$'; usually such module +names are nowhere referenced, but only have one or more defining +occurrences. \CWEB. documents containing such a module will produce a file +of that name in addition to the \Cee~program that is normally produced. The +module bearing the name of the file will form the root module of the +\Cee~code written to that file, in the same way as the unnamed module forms +the root module for the ordinary output. This feature is particularly useful +for the production of header files that can be included by other compilation +units (and even by the program produced as main output). It allows one for +instance to state function prototype declarations that go to the header file +and the matching function definitions in the \Cee~program in the immediate +vicinity of one another within the \CWEB. document. The module with the file +name can refer to submodules, and so on to any depth, just like the modules +contributing to the main output. This possibility should be used with some +restraint however, lest readers have difficulty finding out to which file +the program fragment defined by some module will be sent. The preprocessor +lines that are handled by \CWEB. will normally only become part of the main +program output, not of any additional output files; this provides one valid +reason for sometimes bypassing the facilities of \CWEB., and incorporating +\&{\#define} and \&{\#include} directives directly into program modules. + + +\beginsection How to create a\/ {\tentt CWEB} program + +In the previous section we have explained how one should read \CWEB. +documents; in this section we shall discuss how they can be written. The +``\CWEB. document'' we have been discussing is the printed text that is +eventually produced from the source file written by the programmer, but +that file does not look quite like the printed version; on the other hand +the difference in appearance is not so great that there is any difficulty +finding the place in the source file corresponding to some part of the +printed text. + +\subsection The general setup + +The programmer creates a plain text file using the format explained below, +which contains both program fragments and commentary, and has file name +extension `\.{.w}'; e.g., the file from which the example above was produced +is \.{compare.w} (it is included in the \CWEBx. distribution). The \CWEB. +system consists of two utility programs `\.{CTANGLE}' and `\.{CWEAVE}' that +can be applied to this source file. In order to create an executable +program, one issues the command `\hbox{\.{ctangle compare}}', which will +read the file \.{compare.w} and write a file \.{compare.c} containing the +corresponding \Cee~program. This file can then be processed in the ordinary +way by any \Cee~compiler to produce an executable program. To produce a +printed document on the other hand, one issues the command `\hbox{\.{cweave +compare}}', which will again read the file \.{compare.w}, and this time +write a file \.{compare.tex}. This file serves as input for the typesetting +program~\TeX: by giving the command `\hbox{\.{tex compare}}' it will be +processed, and the result is a file \.{compare.dvi}. This file can be either +previewed or converted to hardcopy output by the system dependent programs +for this purpose that accompany \TeX. Despite the somewhat elaborate +processing trajectories, it will become apparent that the programmer has +good control over the final result produced in both cases. + +A word of explanation about the names of \CWEB. and its constituent +programs. The initial \.C's stand for the programming language, of course; +the rest of the names are the same as those chosen by Knuth for the original +\.{WEB}~system (which existed long before the World Wide Web). The \CWEB. +language allows one to separately describe small parts of a \Cee~program and +their interconnections, both formal (via module references) and informal (by +some semantic relationship); with some fantasy this evokes the image of a +web of connected pieces. These parts are linearised quite differently in +their presentation for human readability than in the ``official'' form in +which they are presented to the \Cee~compiler, and it is the program +\.{CTANGLE} that does the somewhat complicated reordering to obtain the +latter from the former. This process is traditionally called ``tangling'' +the code, although one could also call it untangling if one prefers formal +to human order. The \.{CWEAVE} program intertwines the \TeX\ and \Cee~parts +of the source text and ``weaves'' them together like warp and weft, +resulting in a beautifully formatted document. Despite these pretty +metaphors, you will be forgiven if you sometimes get these names mixed up. + +This general organisation of \CWEB. has some immediate consequences. First +of all, one needs to have an operational \TeX~system and (not surprisingly) +a \Cee~compiler in order to use \CWEB.; the \CWEB. programs form only a +comparatively small part of the utilities needed. Second, the \CWEB. +language must be such that both valid \Cee~code and \TeX~input can be +derived mechanically from it, which are rather different formats. +Nevertheless the \CWEB. language is quite simple: this is because for almost +all of the \CWEB. source text the required format is either that of \TeX\ or +that of \Cee. The main function of the specific \CWEB. commands is to +structure the source file and determine which parts of the input will be +processed further in what way. Finally, a somewhat unfortunate consequence +of \CWEB.'s setup is that errors may be detected by any one of \.{CTANGLE}, +the \Cee~compiler, \.{CWEAVE} and \TeX. The knowledge about \Cee~and~\TeX\ +built into the \CWEB. programs is far from sufficient to ensure that they +will always produce error-free output code, although of course they do their +best not to introduce any errors themselves. A bright point in the case of +\Cee~errors, is that the \&{\#line} directives produced by \.{CTANGLE} enable +the compiler to refer directly to lines in the \CWEB. source file in its +errors messages, rather than to the intermediate \Cee~file (but \TeX\ does +not have a similar facility). + +It follows from these facts that the \CWEB. programmer must be acquainted +both with \Cee\ and with \TeX; however, the depth of the knowledge required +it not the same in both cases. Obviously, one cannot write a computer +program without a good understanding of the programming language used, but a +very superficial knowledge of \TeX\ will suffice: in most cases no \TeX +pertise beyond the basic facts in chapters~2--6 of {\sl The \TeX book\/} is +required (but please don't skip chapter~2, as only too many people have +done). The reason for this is that one rarely needs to instruct \TeX\ to do +sophisticated formatting. It is true that the proper typesetting of computer +programs is a subtle matter, but it is precisely this part that is taken +care of by \.{CWEAVE} (even for references to \Cee~constructs in the +commentary), and the programmer can just concentrate on writing +syntactically correct \Cee~code. On the other hand the full power of \TeX\ +is available if one wishes to use it, for instance to illuminate the program +with things like complicated tables, or math formulae of a different nature +than those occurring in a computer program. + +Since the \CWEB. commands deal only with the structure of the source file, +not with its contents, they can be very brief: they consist of `\.@' followed +by one other character, and are commonly referred to as {\sl control codes}. +For instance, \atspace. (i.e., `\.@' followed by white space) indicates the +start of a new section, and \:c marks the start of the \Cee~part of a +section that contributes to the unnamed module. Control codes may placed at +any position within the source lines, although it is customary to place the +ones defining the coarse structure of the source file at the beginning of a +line for better visibility. In some cases a control code marks the beginning +of a piece of text that will be interpreted by \CWEB. in a special way, as +for instance \:< which starts a module name; the end of these {\sl control +texts\/} is always marked by the special code \:>. The character `\.@' +was selected because it is quite uncommon both in \Cee\ and in \TeX\ source +code, but in those cases where one does need to pass on the character itself +(e.g., in \Cee~strings and comments) it should be written as \:@. + +We now discuss the various control codes, grouped by their function. Here +we shall treat only the most important control codes, which are used +regularly in ordinary programs. Treatment of a number of additional control +codes, that either serve for fine tuning in special cases, or are intended +to allow emergency fixes in unforeseen cases, is deferred to a later +section, in order not to confuse novice \CWEB. users. For the codes that +are discussed, we do however provide full details of their use; most of +these can be skipped on first reading. A summary of all \CWEB. control +codes can be found an the end of this manual. + +\subsection Sectioning codes: \:*, \atspace., \:\~ + +The most important control codes are those that specify the division of the +\CWEB. program into sections. There are three codes that indicate the start +of a new section, and are therefore called sectioning codes. Each of them +has a slightly different effect, and each section must start with one of +them (i.e., a section is never implicitly started). The three sectioning +codes are \:*, \atspace., and \:\~, of which the second one is the most +commonly used. No section numbers should be given in the source file: these +will be automatically computed and inserted by \CWEB.. A tab or newline +following `\.@' is considered equivalent to a space, and for any of these +three control codes, (further) white space separating it from the \TeX~text +that follows is ignored, as long as there is no completely blank line (which +\TeX\ would interpret as the end of the paragraph that started with the +section number). + +A section starting with \:* will start a new chapter of the \CWEB. +document; it should be followed by the title of the chapter, which is +terminated by the two-character sequence `\hbox{\.{. }}' (again the space +might be any white space character). The title is not recognised by \CWEB. +itself, but rather by \TeX, as a delimited macro argument +\unskip\footnote{${}^\dagger$} +{Therefore, if no correctly specified title follows~\:*, then + \.{CWEAVE} will find nothing wrong, but \TeX\ will complain about a + ``Runaway argument'' of a macro that the programmer did not explicitly + write (namely `\.{\\N}'); this is one of the scarier error message that + novice users can come across, so please be warned. +}. +This means that if one wants to have an occurrence of the sequence +`\hbox{\.{. }}' in the title itself, this can be achieved by enclosing the +title (but not the `\hbox{\.{. }}' terminating it) in braces. If one wants +to put other things than plain text in a chapter title, one should be aware +that it is converted to upper case in the running heads of pages and also +written to the table of contents file; only items that behave properly under +these operations should be used in a chapter title. Apart from issuing a +title that will appear in several places, a section starting a chapter will +force a page break before it, and it will cause the section number to be +printed on the terminal during the execution of \.{CTANGLE} and \.{CWEAVE}, +as a progress report. + +As a feature for advanced users of \CWEB., some extra information may be +supplied with the \:* control code: if it is immediately followed by `\.*' +or by a decimal number, than this is not included in the chapter title, but +rather interpreted as an indication of the ``level'' of the chapter. Here +`\.{@**}' indicates the start of a grouping of sections even coarser than a +chapter, and the grouping started by `\.{@*$n$}' becomes finer as $n$ +increases, with `\.{@*0}' corresponding to unadorned \:*. The effect of this +level depends on the definition of the \TeX\ macros that format the chapter +title and the lines in the table of contents, `\.{\\N}' respectively +`\.{\\contentsline}', to which the level is passed as first argument (for +`\.{@**}' the level is~$-1$); it could effect for instance the font used for +the chapter title or the amount of indentation of that title in the table of +contents. In the default definitions of these macros the level is largely +ignored, except that `\.{@*$n$}' will not force a page break for $n\geq s$, +where $s$ is the value of the `\.{\\secpagedepth}' register, which is set +initially to~2. + +In contrast to~\:*, a section starting with~\:\~ instead of~\atspace. will +tie itself to the previous section, in the sense that a page break between +these sections will be avoided. More precisely this is what happens: +normally \.{CWEAVE} will instruct \TeX\ to break pages only between sections +(except when one is too large too fit on a single page) and put as many +sections on each page as possible subject to this restriction; however, a +section starting with~\:\~ will be considered to be continuation of the +previous section for the purpose of page breaking. A situation where one +would use \:\~ is the following: suppose we define a function, and also want +to state its prototype, which will belong to a different module, since it +has to appear earlier in the program or even on a separate (header) file. A +natural place to give the prototype in the \CWEB. document is directly +before the function, so that it can easily be seen that the prototype +matches the actual definition. Now without special measures there is a +substantial chance that a page break will occur between these two sections, +since the short section with the prototype might fit on an already partially +filled page, whereas the larger section with the definition might not. By +starting the latter section with~\:\~, it can be achieved that in such cases +the former section is moved together with the latter to the new page. + +Like any other section the very first section starts with s sectioning +code (usually \:*), and any text that might precede it is not +part of any section; this text is said to be ``in limbo''. This material is +ignored by \.{CTANGLE}, and copied literally into the \TeX\ file by +\.{CWEAVE} (except for the replacement of~\:@ by~`\.@'), following the first +line which always reads `\.{\\input cwebxmac}' (in order to load the +standard format). The purpose of the text in limbo is to allow issuing +\TeX~commands that apply to the whole document (such as macro definitions, +possibly modifications or additions to the standard format), or producing a +title page or an introduction preceding the sections of the \CWEB. document. +No control codes are allowed in the limbo text (well, almost; there are two +exceptions, that will be mentioned below). The last section ends simply +at the end of the \CWEB. source file; there is no way to add material after +it (or elsewhere outside the sections). However, \.{CWEAVE} will append some +material at the end itself (unless it is invoked with a `\.{-x}' flag): an +index of identifier uses, a list of module names, and a table of contents. +Because the index is seamlessly attached to the last section, it is +customary to give that section the title ``Index'' and not to include any +program fragment in it. + + +\subsection Subsectioning codes: \:d, \:h, \:f, \:c, `\/\.{@< {\dots} @>=}' + +Each section, as delimited by the sectioning codes, contains a \TeX~part +(although it may be empty), and in addition at most one \Cee~part, which +always comes at the end of the section, and zero or more intermediate parts, +of which there are three kinds: those that specify \&{\#define} and +\&{\#include} directives, and format definitions (see below). Intermediate +parts can be given in an arbitrary order, as long as they come after the +\TeX~part and before the \Cee~part, if present. The beginning of any part +other than the \TeX~part (which starts directly after the sectioning code) +is marked by an appropriate control code, which is called a subsectioning +code; these codes are optional in the sense that they need only be given if +the corresponding part is present. The end of the \TeX~part is determined by +the first subsectioning code, or in absence of any of them by the next +sectioning code. + +The \Cee~part, if present, begins at the first occurrence of \:< or \:c; the +former starts a defining occurrence of a module name, and the latter is used +when the \Cee~part belongs to the unnamed module. The code \:c may also be +written as \:C (in fact all alphabetic codes are equivalent to their upper +case counterparts). Once the \Cee~part of a section is started, any further +module names are interpreted as modules references rather than as defining +occurrences. A module name, whether defining or not, consists of \TeX~code +between the \:< and the next occurrence of~\:>. As a measure against +accidental misinterpretation of module names, due for instance to a +forgotten \atspace. or \:c, the closing \:> of a defining occurrence must be +followed (optionally with some white space in between, but no newline) by +one of `\.=', `\.{==}', `\.{+=}' and `\.{+==}', while for a non-defining +occurrence this must not be the case. The possibilities `\.{+=}' and +`\.{+==}' are included for those who like their source code for +continuations of modules to resemble the printed output, but the distinction +is ignored by \CWEB.: it will simply print `$\EQ$' after the first defining +occurrence of a module name and `$\PE$' after any further defining +occurrences. + +The subsectioning codes that mark the beginning of intermediate parts are +\:d, \:h, and \:f. Of these the first two specify preprocessor directives +for respectively a macro definition and the inclusion of a header file, and +the last specifies a so-called format definition. The codes \:d and \:h will +be replaced by \&{\#define} respectively by \&{\#include} in both the +program and the printed document. We already mentioned how the effect of +using \:d or \:h differs from that of using \&{\#define} or \&{\#include} +directly in the \Cee~part of the section: the directive will be moved to the +beginning of the \Cee~file, and in case of \:h, the header file will be +scanned for typedef definitions. Here we mention a few more points that are +relevant when writing the source file. + +Macro definitions following \:d are not line-oriented like those in \Cee: +everything up to the next subsectioning or sectioning code is considered to +belong to the macro, and newlines need not be escaped, as \.{CTANGLE} will +take care of escaping any newlines while writing to the \Cee~file. There are +some mild restrictions on the replacement text of a \:d macro definition: +parentheses and braces should be balanced (this is a deliberate requirement, +made in order to allow detection of programming errors that would otherwise +be very hard to track; the same requirement also holds for each complete +\Cee~part of a section), and no module names should be referenced. It is not +possible to use other preprocessor directives in macro definitions either, +but that is because this is already impossible in~\Cee. After a \:h command, +at least one newline should occur before the next sectioning or +subsectioning code. + +Apart from this, \:d and \:h are followed by whatever would follow +\&{\#define} respectively \&{\#include}, with the same deviant lexical rules +as in~\Cee. So whether a macro introduced by \:d is defined with or without +arguments depends on whether the first character after the identifier +following \:d is a left parenthesis or not, where spaces {\it are\/} +significant. The file name after~\:h may be enclosed either in double quotes +or in angle brackets; the latter indicates that the header file is located in +some system include file area. After the file name a comment may be placed. + +The header file specified after \:h itself should of course contain ordinary +\Cee~code rather than \CWEB. input; after all, it will be read directly by +the \Cee~compiler. As as mentioned before, the file will be scanned by +\.{CWEAVE} as well, searching for any typedef definitions; moreover, if it +contains any lines starting with \&{\#include}, then those files will be +scanned recursively as well. In the case of system header files (specified +with angle brackets), \.{CWEAVE} will refrain from scanning the file unless +the file is found on an explicitly specified search path (see below); in +fact it is better not to scan any of the \caps{ANSI/ISO} standard header +files, since \.{CWEAVE} already knows about all typedef definitions that can +occur in such header files. It is not uncommon that a header file specified +after~\:h (using quotes) is itself an auxiliary output file produced from a +\CWEB. source file, possibly even from the very source file containing the +\:h~command. There is no circularity or other problematic aspect of such a +situation, but one should remember to run \.{CTANGLE} to produce the header +file, before the run of \.{CWEAVE} that needs it. + +The way \.{CWEAVE} searches for the header file depends on how the name +following \:h is specified: if it is enclosed in quotes then \.{CWEAVE} will +look first in the current directory. There may have been specified one or +more alternative places to look for header files, in the form of strings +that can be prefixed to the file name (given on the command line or compiled +into \.{CWEAVE}, or both). If so, these will be tried in order, regardless +of the delimiters used for the file name, until a match is found; \.{CWEAVE} +will only insist on actually finding a header file if the file name was +enclosed in quotes. + +There is one aspect of scanning header files that might cause a problem in +some cases: when scanning a header file, \.{CWEAVE} is unaware of other +preprocessor directives that may disable certain nested \&{\#include} +directives; \.{CWEAVE} will therefore obey such \&{\#include} directives +unconditionally. Such a problem is not very likely, but it could be serious +if the nested header file cannot be found (and is enclosed in quotes), or if +there are circular references between header files. Various solutions could +be found for such a problem, depending on the precise situation, varying +from creating dummy files or avoiding conditional compilation by the use of +change files to (as a last resort) avoiding the scan of the header file +altogether, by using \&{\#include} in a program fragment rather than~\:h; in +the latter case relevant information could be extracted from the header +file manually, and converted into format definitions (\:f) described below. + +When preprocessor directives are incorporated in the \Cee~part of a section, +the ordinary rules of \Cee\ apply: they should be spelled out in full, as +`\.{\#define}' or `\.{\#include}', and occur at the beginning of a line; the +directive ends at the next non-escaped newline. Although in \Cee\ it is +permissible to extend a preprocessor directive into the following line by +placing a multi-line comment that contains the newline, this should not be +done in \CWEB., since the comment will be removed by \.{CTANGLE} but the +newline will remain. If one needs a very long comment after a preprocessor +directive, one should start it on the line following the directive; in the +formatted document such a comment will be placed on the same line as the +directive. The same holds for comments placed after a \:h command. + +Format definitions, indicated by the code \:f, are entirely specific to +\CWEB., and have no effect on the \Cee~program that is defined. They are not +needed very often, but when they are, a proper use of them is essential for +obtaining acceptably formatted output. To understand why they are sometimes +needed, one has to consider the way \.{CWEAVE} formats program fragments. +The input is broken up into tokens (like identifiers, constants, operator +symbols), and a syntactic category is attached to each; the resulting +sequence of categories is then analysed according to a grammar, and formatted +correspondingly. Certain identifier tokens are recognised as reserved words +and get a corresponding the syntactic category, others are recognised as +typedef identifiers and get the same syntactic category as for instance +\&{size\_t}, and the remaining ones are treated as ordinary identifiers. This +scheme usually works fine, but occasionally there can be problems, caused by +the fact that \.{CWEAVE} is not aware of all the information that is +available to the compiler. The main reasons for this are macros (which may +cause the code seen by the compiler to be quite different from that seen by +\.{CWEAVE}), typedef declarations that are hidden from \.{CWEAVE}'s sight, +and module names that stand for a construct of a different syntactic +category than \\{statement} (which is what \.{CWEAVE} expects them to be by +default). In all these cases \CWEB. provides mechanisms for the user to put +\.{CWEAVE} on the right track, and format definitions are one such mechanism +(others will be discussed below). + +Format definitions allow the programmer to explicitly state the syntactic +category that \.{CWEAVE} should attach to a given identifier. They have the +form `\.{@f x y}', which will become `\&{format}~$x$~$y$' in the typeset +output; here $x$ and $y$ can be arbitrary identifiers or keywords. This +definition has the effect of associating to~$x$ the same syntactic category +that is associated to~$y$. Such a change of category is required when an +identifier is defined as a macro to stand for a keyword: whenever you say +`\.{@d ident keyword}', say `\.{@f ident keyword}' as well. For instance, +the author of this manual thinks the keyword \&{static} is not very +informative when applied to functions, and therefore often creates an alias +for it by saying `\hbox{\&{\#define} \\{local} \&{static}}'; this directive +is then followed by `\hbox{\&{format} \\{local} \&{static}}'. We see that +the first identifier after \:d~or~\:f is always typeset in italics; this is +so despite the fact that in the example, as a consequence of the format +definition, this identifier will be typeset as \&{local} in all other +places. Another reason to change a category could be that an identifier is +in fact a typedef identifier, but \.{CWEAVE} cannot deduce this fact +(presumably the declaration occurs in some header file that is not scanned +by \.{CWEAVE}); in such cases one can use a standard defined type like +\&{FILE} or \&{size\_t} as the second argument to \:f. Finally, it is +possible that some \Cee~implementation uses additional, non-standard +keywords (or macros that behave as a keyword); such an identifier should be +formatted like a standard keyword that has a similar syntactic function as +it (which hopefully exists). In fact the identifier \&{va\_dcl}, which is +used in a convention for functions with variable argument lists that is not +part of \caps{ANSI/ISO}~\Cee, is nevertheless built into \.{CWEAVE}, because +there is no keyword that has the required syntax category (namely +\\{declaration}), so that it would otherwise not be possible to introduce +it; one can on the other hand easily undo the reservation by saying +`\&{format}~\\{va\_dcl}~$x$'. + +Format definitions can also be used for a reason that does not have to do +with syntax analysis. There are two classes of identifiers that are parsed +like ordinary identifiers, but are nevertheless treated specially; these +classes consist initially of the identifiers \\{TeX} respectively \\{NULL}. +The main distinction of these classes is that their identifiers are typeset +differently, namely as \TeX~macros; the mentioned identifiers will therefore +be written to the \TeX~file as `\.{\\TeX}' respectively `\.{\\NULL}', which +causes them to be typeset as `\TeX' respectively `$\NULL$'. This mechanism +gives the user the ability to change the appearance of identifiers in any +desired way, simply by defining the macro appropriately. The class +of~\\{TeX} is intended for identifiers that are still alphabetic in +appearance (possibly with letters being accented or shifted), while the +class of~\\{NULL} is intended for identifiers that are represented by +mathematical symbols. Hence the \TeX~macro will be processed in +horizontal mode with italic font selected in the first case, and in math mode +in the second case. Simply saying `\.{@f alpha NULL}' suffices to make +\\{alpha} print as~$\alpha$; the format definition is typeset as +`\hbox{\&{format} \\{alpha} $\NULL$\quad($\alpha$)}' to make the +correspondence of the identifier and typeset symbol evident. + +Unlike \Cee~identifiers, \TeX~macros cannot contain underscores and digits. +On writing of the macros to the \TeX~file, underscores are replaced by~`\.x', +so that they will become part of the macro. Digits however are not changed, +so identifiers containing digits should not be put into the class of~\\{TeX} +or~\\{NULL} by a format definition, unless special care is taken: the macro +will only consist of the part up to the first digit. No index entries for +identifiers of the class of~\\{NULL} are recorded (the same holds for +keywords); on the other hand index entries for typedef identifiers are +recorded, despite the fact that they are formatted as keywords. + +\subsection Text within \Cee~program fragments: comments and module names + +Within the program part of a section, the input should basically follow the +rules of the \Cee~syntax, but amidst the \Cee~tokens there may also occur +module names and comments. In both cases the \Cee~code is temporarily +interrupted by a piece of ordinary text that is processed directly by~\TeX, +just like the \TeX~part of a section. In the case of module names this text +is delimited by \:<~and~\:>, in the case of comments by +`\.{/*}'~and~`\.{*/}'. So comments are actually valid \Cee~comments, but the +converse is not true: the contents of a comment is processed by~\TeX, so not +all \Cee~comments can be used without modification; a point to keep in mind +if one is converting ordinary \Cee~code to \CWEB.. Like \Cee~comments, the +comments of \CWEB. cannot contain the two-character sequence `\.{*/}' +(regardless of the \TeX~context, because comments are recognised before +\TeX\ even gets to see them). The sequence `\.{/*}' is forbidden as well, +which allows \.{CTANGLE} to warn the programmer about unclosed comments, +that might otherwise lead to particularly elusive errors. In the \TeX~texts +of comments and module names no control codes are allowed (except in +embedded pieces of \Cee~code, described below), but \:@ can be used to +represent the character `\.@' (this is true in all contexts); a module name +is terminated by the first occurrence of the code \:>. During the processing +of these \TeX~texts, line ends are replaced by spaces, which implies that +\TeX~comments (starting with `\.\%') cannot be used. (In the \TeX~part of a +section on the other hand, such comments can safely be used: they are +completely ignored by \.{CWEAVE}, and not even copied to the \TeX~file.) + +The text for module names serves a dual purpose: apart from determining the +text representing the module in the printed output, it also serves to +identify defining occurrences of a module name with references to it. For +the latter purpose it is irrelevant how the contents of a module name will +be further processed; there should basically be a character-by-character +match. This rule is however alleviated in two ways to make matching easier. +First, any amount of consecutive white space is replaced by a single space, +and white space at either end of a module name is discarded. Second, an +abbreviation mechanism for module names may be used. A module name may be +specified by a prefix of the full name, followed by~`\.{...}'. A few +conditions must be satisfied to allow this mechanism to work. All +specifications of one same module name must be extensions of the one among +them of minimal length, which must not be a prefix of any other (full) +module name. All specifications of the name that do not end with~`\.{...}' +must be equal; there must be at least one such specification, which defines +the full module name of which all other specifications give a prefix. +Loosely speaking, the minimal specification is used for identification +purposes, and the maximal specification is used for typesetting all +occurrences. With the help of these rules, and a text editor, there should +be little reason to choose module names any shorter than what is needed to +express the function of a module clearly. There is a limit on the length of +a module name, but it is so generous that this could hardly be a problem: +1000~characters after replacement of consecutive white space characters by +single spaces. + +The parser of \.{CWEAVE} normally assumes that references to modules stand +for (compound) statements, which is likely to be a valid assumption in the +majority of the cases (or at least one that does not upset parsing, for +instance when the module is actually a statement sequence). Occasionally +however, one of two other syntactic categories applies instead, namely +\\{declaration} or \\{expression} (the remaining categories are extremely +unlikely). When this is the case, the programmer should make it clear to +\.{CWEAVE}, lest the parser might choke on the input and produce badly +formatted output. This can be done by placing the control code \:; once (for +a \\{declaration}) respectively twice (for an \\{expression}) directly after +the module name (in the latter case this also conveniently provides a +separation from any `\.=' or `\.{+=}' that might follow). + +At the end of the \CWEB. document, after the index, a list will be placed of +all module names used. This list sorted lexicographically, with sorting +based on the source strings for the full module names, collated (unlike the +identifier index) in the order of the internal (\ASCII.) character codes. +For this reason it is a good convention to ensure that all module names are +already distinguished by a prefix consisting of alphabetic characters and +spaces only, of which the first word is capitalised; then the order of the +list will be natural and independent of any internal details that the reader +is not aware of. + +\subsection \Cee~code within text: `\pb' fragments + +In order to mention a piece of \Cee~code within \TeX~text, it can simply be +enclosed in vertical bar characters (`\.|'); then \.{CWEAVE} will format it +in a way similar to to \Cee~code of modules. This feature may be used in any +kind of \TeX~text except in limbo, i.e., in the ordinary \TeX~part of a +section, in comments and in module names. The piece of \Cee~code itself +should not contain any comment. + +The ``lightweight'' construction with vertical bars resembles the math shift +characters (`\.\$') for \TeX's math mode, and indeed in simple cases like +`\.{|a[i+3]|}' the output would be identical if the `\.|' characters were +replaced by `\.\$'. The two modes should not be confused however: the +``\Cee~mode'' is implemented by \.{CWEAVE}, which translates the +\Cee~constructs before \TeX\ ever gets to see them; it often uses math mode +itself, and as a consequence it should never be used when \TeX\ is already +in math mode. The syntax used by \.{CWEAVE} is of a stricter kind than that +of \TeX's math mode, but it can still be used for some expressions that are +not quite proper~\Cee; in particular there is no objection to writing things +like $\\{begin}\Z p<\\{end}$, which humans understand better than compilers. +On the other hand an incomplete formula like `$\leq n$' (which can be used in +sentences, with the missing operand expressed in words) is better written as +`\.{\$\\leq n\$}' than as `\.{|<=n|}': the latter is not understood by +\.{CWEAVE}'s parser, and therefore the `\.{<=}' and the `\.n' are translated +separately with an ordinary space in between; the result looks reasonable, +but \TeX\ may very well decide to break the line at the space. + +There is a lexical price to pay for using delimiters that are not control +codes: it is impossible to use character `\.|' in any piece of \TeX~text +where `\pb' constructions are allowed (even if one tries for instance to set +up a verbatim context, because \.{CWEAVE} acts before \TeX\ does). This +should not cause great problems however, since `\.|' is not a character in +ordinary text fonts, and for `\.|'~and~`\.{\\|}' in math mode, plain \TeX\ +already has the substitutes `\.{\\vert}' and~`\.{\\Vert}'; for exceptional +text fonts (like typewriter type) that do have `\.|', the standard format +for \CWEB. provides `\.{\\v}' as a substitute (by means of \.{\\chardef}) +for~`\.|'. Inside `\pb' one has a similar problem of not being able to write +the bitwise-or operator~`$\OR$' in the usual way. For this purpose \CWEB. +provides the control code~\:v to represent that operator (which you may also +use in an actual program fragment, although there is no need to do so +there). Note that the composite operators `\.{|=}'~and~`\.{||}' can be used +without problem; consequently no `\pb' should be immediately followed +by~`\.=' or by another `\pb'. + +Although \Cee~comments are forbidden inside `\pb', it is possible to mention +a module in \TeX~text by enclosing the module name in vertical bars; this +\TeX~text can either be the \TeX~part of a section or a comment, but not +another module name. Mentioning a module in this way does not imply any +inclusion of the module body, so it is not considered to be a use of the +module; in the cross-references it is referred to as a ``citation'' of the +module. For the module name itself the same rules apply as for other +occurrences of module names; in particular the abbreviation mechanism can be +used, and \.{CWEAVE} will automatically insert the relevant section number +in the module name. Citing a module may form an exception to the rule that an +occurrence of a module name when the \Cee~part of a section has not already +started must be a defining one. Since \.{CTANGLE} normally ignores the +vertical bars of `\pb' constructions together with the surrounding +\TeX~text, it needs a simple rule to decide whether a module is being cited +or defined. It does this by inspecting the next token (where a newline +counts as a token, but codes like \:; that are ignored by \.{CTANGLE} are +skipped): if this is `\.=' (or `\.{+=}' etc.), then it assumes that the +module is being defined, and if it is `\.|' that the module is being cited; +in other cases it signals an error (this could for instance happen if a \:c +code is missing). Therefore it is not really necessary that the module name +is the only item in the `\pb' construction, as long as it is the final item; +this extra freedom is not likely to be of much practical use, however. + +\subsection Modules producing additional output files: `\/\.{@( {\dots} @>}' + +As was mentioned before, there are special module names that will cause the +program produced by that module to be written to a separate output file. +Such a module name is specified by enclosing the file name in \:(~and~\:>; +in fact it is sufficient to use \:( instead of~\:< in just one occurrence of +the module name. The file name will be set in typewriter type by \.{CWEAVE}, +so that the difference with an ordinary module name is easily perceived. +Although hardly relevant for this case, the compression of white space and +the abbreviation mechanism for module names also applies to these special +module names. The file name can contain any special characters, including +`\.|' and `\.@'; the latter must as always be doubled. + +\subsection Control codes that help parsing in special situations: + \:;, \:[, \:] + +In the discussion of the \&{format} command we already mentioned the way +\.{CWEAVE} parses and formats program fragments, and the fact that some +programming constructions can confuse the parser, leading to badly formatted +output. Like \:f, the control codes in this subsection provide ways to avoid +such problems, but they do so on a local basis in the code itself, rather +than by global definitions. They are mainly used in connection with macros +with replacement texts and/or arguments that are not expressions. Since +macro invocations look like identifiers or function calls, and macro +arguments appear to be function arguments, a piece of code containing a +macro invocation whose replacement text and arguments are not all +expressions may seem syntactically incorrect when not expanded. An example +of such a scenario is a macro whose replacement text is a compound +statement; an invocation of such a macro needs no semicolon following it, +and sometimes placing a semicolon would actually cause an error (e.g., if +the invocation is used as the first branch of an \&{if}-\&{else} statement, +since the semicolon would be taken to be an empty statement {\it after\/} +the conditional statement, and the \&{else} would be unmatched). Since the +parser of \.{CWEAVE} does not expand macros, it will fail to recognise a +macro invocation without a following semicolon as a statement, and like many +parsers it is not good at recovering from such a failure. Although no error +message is usually issued, formatting can be severely disrupted; indeed, +correct formatting will only be inserted locally for constructions that do +not contain the ``error'', so one unrecognised construction can easily +destroy the layout of the entire program fragment it occurs in. + +\.{CWEAVE} provides some simple mechanisms for guiding the parser through +such unusual code, and by applying them in several ways nearly all problems +that arise in practice can be solved. One of these is the control code~\:;, +which produces no \Cee~code (nor any printed output), but which can be used +in places where the \.{CWEAVE} parser would require a semicolon for a +successful parse; another is the combination \:[,~\dots,~\:], used as +a pair of parentheses, which will cause whatever is enclosed to get the +syntactic category `expression', regardless of its actual category. + +The most obvious use of~\:;\ is in the case already mentioned of a macro +invocation that expands to a (compound) statement: placing \:; after such a +macro invocation will cause it to be recognised as a statement by \.{CWEAVE}, +keeping its parser happy while not affecting the actual \Cee~program. There +are other situations as well where one does not want to place a semicolon, +yet wishes \.{CWEAVE} to act as if it were there. If a macro stands for +statement that happens to end in a semicolon, then it is a good idea to +suppress the final semicolon in the definition: in that case all invocations +can supply the semicolon, and one does not have to remember writing \:; +instead of~`\.;' at the invocations of this macro. For instance, the macro +replacement text could be `\&{do}~\<statement>~\&{while}~(\<condition>)', or +`\&{if}~(\<condition>)~\<statement>~\&{else}~\<expression>', or even +`\&{if}~(\<condition>)~\<statement>~\&{else}', where the final \&{else} was +placed with the purpose of picking up the following semicolon as an empty +statement; in all these cases the macro invocation together with the +following semicolon is a complete statement that can be used without special +precaution, even as the first branch of an \&{if}-\&{else} statement. +However, in these cases the macro definition itself needs a bit of extra +care: a~\:; should be placed at the end to represent the semicolon that will +follow in invocations, so that \.{CWEAVE} can properly format the +replacement text of the macro. Finally, it there can be purely aesthetic +reasons for wanting to suppress a semicolon at the end of a `\pb' +construction, for instance when referring to a declaration as +`$\&{char}~\m*p$', which strictly speaking requires a final semicolon to +become a declaration; to let \.{CWEAVE} format this properly, one should +write `\.{\v char *p @;\v}'. Constructions like `$\&{return}~\\{home}$' and +`$\&{goto}~\\{sleep}$', which are fairly common to mention in module names, +would also fall into this category, but in this particular case no \:; is +necessary, since \.{CWEAVE} parses these as expressions, even though strictly +speaking they are not. + +Since \:; is invisible in the output, yet can be sensed by the parser, it +can conveniently be used to pass information to the parser, and there are a +few instances of such use where it does not stand for a semicolon. We +already mentioned placing one or two copies of \:; after a module name to +indicate the syntactic category. Another use is to place it before a typedef +identifier to cause it to be treated as an ordinary identifier; this is +useful if the identifier is locally redeclared, or used as field selector in +a \&{struct} or \&{union} specifier. When the identifier is used as a tag +immediately after \&{struct} or \&{union}, or as a selector after +`$.$'~or~`$\MG$', it is not necessary to place \:; before it. + +Unlike \:;, the control codes \:[~and~\:] themselves do not participate in +parsing. The material between them is parsed normally, which may or may not +succeed in recognising a single construct; then the pieces recognised are +concatenated (without separation), and the result is given the category +\\{expression} for the purpose of parsing further items outside. The most +obvious use of this mechanism is to encapsulate any arguments in a macro +invocation that are not expressions (e.g., some storage allocation macros +have a type as argument), so that the invocation can be parsed as a function +call. There need not be anything in between \:[~and~\:], so `\hbox{\.{@[ +@]}}' can be used as an ``invisible expression'' in the same way as \:; can +be used an invisible semicolon. An example where this is useful, is a module +standing for an initialiser list, that is moreover defined in multiple +sections (see for instance the module `$\X157:Rules\X$' in the source +document for \.{CWEAVE}): it is natural to end each program fragment +defining a part of such a module with a comma, but this will not be parsed +properly unless an expression follows, which can be achieved by adding +`\hbox{\.{@[ @]}}'. Finally, if for some tricky piece of code none of the +mentioned methods suffice to get it parsed properly by \.{CWEAVE}, one may +use \:[~and~\:] (followed by \:; if necessary) to minimise the damage: by +placing \:[~and~\:] around an appropriate part of the program containing of +the problem area, we can ignore the fact that the parser failed to recognise +it, and force it to continue as if it ad recognised an expression; thus we +can contain the problem, and prevent the effects from spreading any further. + + +\beginsection Invocation of {\tentex CTANGLE} and {\tentex CWEAVE} + +The simplest form of calling \.{CTANGLE} and \.{CWEAVE} is to supply one +command line argument, which is the name of the \CWEB. source file without +the `\.{.w}' suffix. It is possible however to modify the behaviour of the +programs by selecting certain optional settings, and small patches to the +master source file can be achieved by supplying a ``change file''. The +general syntax for invoking \.{CTANGLE} is +$$ \hbox +{ \.{ctangle}\quad + [$(\.+\mid\.-)$\<options>]\quad + \<\CWEB. file>[\.{.w}]\quad + [$(\<change file>[\.{.ch}]\mid\.+\mid\.-)$ \quad + [\<output file>[\.{.c}]]] +} +$$ +where square brackets indicate optionality, vertical bars separate +alternatives, and parentheses are used for grouping. Here \<options> is a +string of one or more characters designating options, as described below; +there may be more than one such string of options, and they may be given +between or after the files names instead of before them, with no difference +in meaning. For \.{CWEAVE} the situation is entirely similar, except that the +default extension for the output file is `\.{.tex}' instead of `\.{.c}'. + +\subsection Command line options + +A command parameter that starts with `\.+' or `\.-' and has at least one +more character, serves to control optional settings of the program being +invoked. The characters after the initial character `\.+'~or~`\.-' denote +individual options that are turned on respectively off; option characters are +case-insensitive. The character `\.i' forms an exception, since it is used +to supply a string argument rather than to set a switch; the string is the +remainder of the option string (following the `\.i'), and +`\.{+i}'~and~`\.{-i}' are equivalent. All option characters will be +accepted, but only the ones listed below have any effect on the operation of +the program. We list the switches in the direction that alters the default +setting. +$$\vcenter{\tabskip=1em \halign{\tentex#\hfil & #\hfil & #\hfil \cr +\omit\it switch& \it program& \it effect \cr +\noalign{\yskip} +-b & both & do not write a banner line to the terminal \cr +-p & both & do not show a progress report on the terminal \cr +-h & both & omit confirmation of successful completion \cr +\noalign{\yskip} +-l &\.{CTANGLE} & omit \&{\#line} directives, make \Cee~file look nice \cr +\noalign{\yskip} +-x & \.{CWEAVE} & do not attach index and other information + at the end of the document \cr ++d & \.{CWEAVE} & report failure to completely parse pieces of \Cee~code \cr ++t & \.{CWEAVE} & write three files, + with separate ones for index and list of module names \cr ++e & \.{CWEAVE} & even out number of pages before table of contents \cr +\noalign{\yskip} ++i & \.{CWEAVE} & add alternative search path for header files + (takes argument) \cr +\noalign{\yskip} ++f & \.{CWEAVE} & force a line break after each statement \cr ++a & \.{CWEAVE} & force all statements to be on a line by themselves \cr ++u & \.{CWEAVE} & ``unaligned brace style'': do not align `$\{$' and `$\}$' + vertically \cr ++w & \.{CWEAVE} & ``wide brace style'': + force line breaks before and after `$\{$' \cr ++m & \.{CWEAVE} & ``merged declarations style'': + do not force line breaks between local declarations \cr +\noalign{\yskip} + +c & both & run in compatibility mode with \LKC. \cr + +s & both & show memory usage statistics at completion \cr + ++ & both & handle \Cpp~language instead of \Cee \cr +}} +$$ +The options `\.{+d}'~and~`\.{+s}' only operate if \.{CWEAVE} or \.{CTANGLE} +was compiled with the preprocessor symbol \.{DEBUG} respectively \.{STAT} +defined (with most \Cee~compilers this can be accomplished by including a +command line parameter \.{-DDEBUG} respectively \.{-DSTAT} when compiling +the \CWEB. system). + +The options `\.b', `\.h', and `\.p' can be used to control the amount of +output that \CWEB. writes to the user terminal; the combination `\.{-bph}' +will eliminate terminal output altogether when no errors are encountered. + +The option `\.{-l}' of \.{CTANGLE} is intended either for use with broken +compilers or debuggers that cannot handle \&{\#line} directives properly, +or for cases where the \Cee~file is of more importance than just as an +intermediate file, for instance when the program is transferred to +people who do not wish to practice \lp.. Apart from omitting \&{\#line} +directives and comments that indicate the section number from which code +originates, an attempt is made to make the \Cee~file more readable to humans: +the spacing and (almost all) comments of the source file are preserved in +the \Cee~output, and when modules are substituted into others, indentation +levels are accumulated, so as to produce indentation that looks natural. +Doubtlessly the result is not perfect (and lines may get quite long), but it +is definitely more readable than the output normally produced. Since layout +and comments of the source file need to be preserved by \.{CTANGLE}, this +option consumes significantly more memory than its contrary. + +The option `\.{+d}' causes \.{CWEAVE} to issue a warning when it +could not properly parse some piece of \Cee~code; this could happen either +because a code fragment is incomplete in the sense that it does not +represent a single complete syntactic entity (as in the `\.{|<=n|}' example +above, or when a module body ends with a label without a following +statement), or because the code is actually unsyntactic, or because +\.{CWEAVE} has been fooled by an unusual construction. In all cases however +the result can be (very) badly formatted output, and a correction should be +made; users who care about the quality of the typeset output are advised to +always set this option (or at least when the document is being finalised). +Setting the `\.{+d}' switch is equivalent to placing a control code \:1 at +the beginning of the first section; the nature of the warning messages and +possible remedies will be discussed later in this manual. + +The two output files that the option `\.{+t}' will cause \.{CWEAVE} to create +in addition to its main output file, are called \.{\<name>.idx} and +\.{\<name>.scn}, where \<name> is the name of the main output file without +its extension. These files will be read by `\.{\\input}' commands in the +main output file, so that the typeset document will not be any different; on +large projects however it can be helpful to have this information on +separate files, for instance for making a global index. The option `\.{+e}' +is intended for use with two-sided printers: it ensures that the table of +contents comes out on a fresh sheet of paper, so that it can conveniently be +moved to the front. + +The option `\.{+i}' (or equivalently `\.{-i}') can be used to specify a +directory for \.{CWEAVE} to search for header files in \:h commands. +Although directory structures are system-dependent, \CWEB. assumes that a +file can be looked up in a specified directory by prefixing a string +indicating that directory to the file name (this works for many systems); +the desired prefix string should then be supplied as the remainder of the +option string after the `\.i' character. E.g., on the \caps{UNIX} system the +author uses, \.{CWEAVE} can be told about the location of the `Xlib' header +files by supplying an argument `\.{+i/usr/local/X11R5/include/}' (one could +replace `\.{+i}' by `\.{-I}' to make it look more like the similar option +passed to the \Cee~compiler); the important thing to note is the final +pathname separator `\./'. Up to~8 additional prefixes can be specified by +giving several such arguments; they will be tried in order from left to +right. It is also possible to fix one such prefix at compile time, by +defining the preprocessor symbol `\.{CWEBHEADERS}' to be the desired prefix +string when compiling the compilation unit \.{common.c} of \CWEB.; this will +behave as if it were the first prefix specified by a `\.{+i}'~argument. + +The last five options mentioned will alter layout style of program fragments. +The option `\.{+f}' will result in a more vertical style than the default, +and `\.{+a}' will do so even more; the difference between them is that +`\.{+f}' will not force a simple statement to start on a new line if it +follows a label or the condition of an \&{if} or \&{while} statement, +whereas `\.{+a}' will start a new line in such cases. The option `\.{+u}' +selects a style in which corresponding opening and closing braces are +unaligned because a line break is inserted after~`$\{$' instead of before~it. +The option `\.{+w}' on the other hand selects a brace style that has more +vertical symmetry than the default one, since opening braces will appear on +a line by themselves, like closing braces; the price is that listings will +consume more paper. The option `\.{+a}' overrides `\.{+f}', and similarly +`\.{+w}' overrides `\.{+u}'. Finally, the option `\.{+m}' is for people +(like the author) who are extremely keen on saving paper: it avoids forced +line breaks between the declarations in a compound statement, just like they +are not placed by default between the statements; the separation between +declarations and statements within a compound statement is still indicated by +a line break, that even has some extra vertical space, because this +separation is significant in the \Cee~syntax (unlike the \Cpp~syntax). + +In compatibility mode, specified by `\.{+c}', both \.{CTANGLE} and +\.{CWEAVE} modify their behaviour in such a way that they try to ensure that +they can handle any file that can be correctly processed by \LKC., and that +the output is an equivalent \Cee~program, respectively a valid \TeX~file +(this is the hard part) that produces a comparable printed document. In the +current version this claim can only be made for programs written in~\Cee; a +wholehearted attempt to do the same for \Cpp~programs would cost a +substantial amount of extra work. There are so many differences in the +details of formatting between \CWEBx. and \LKC. that one cannot expect +formatted output that is identical to what would be produced under \LKC., +but to get the best approximation, one should in addition to~`\.{+c}' +specify the options~`\.{+uft}'. + +The option `\.{+s}' is included because the \CWEB. utilities use statically +allocated memory areas, which may therefore run out; using this option one +can see how close one is to the limits of \CWEB.. The most important limited +resources that it provides information about are are: \ (a)~The name tables +in which \.{CTANGLE} and \.{CWEAVE} store all distinct identifiers and index +entires, respectively module names (the entries `identifiers', `module +names', and `bytes'); \ (b)~\.{CTANGLE}'s main memory, in which the complete +\Cee~program file processed during a single run has to be stored, albeit in a +compactified form (`replacement texts' and~`tokens'); \ (c)~\.{CWEAVE}'s +cross-reference memory, in which all the data for the index and list of +module names are stored (`cross-references'); \ (d)~its parsing buffers, +which must be able to hold any one program fragment or piece of +\Cee~code (`scraps', `texts', and~`tokens'). There should be no immediate +need to increase the size of these memory areas, since even for the main +program of \.{CWEAVE}, the largest of \CWEB.'s own compilation units, the +use of any of these resources is less than a third of the amount available. +There is one resource of which a larger fraction is used, namely `trie +nodes', but its usage depends only on the set of grammar rules used, which +is independent of the particular \CWEB. source file. When for some source +file \CWEB. is approaching its limits, one can of course try to recompile +\CWEB. with larger arrays, but alternatively one may restructure the source +file: when one of (a),~(b), or~(c) runs out, one might consider breaking up +the file into several separately processed pieces; when (d) runs out, a +remedy could be splitting up some huge module body into smaller ones, by +introducing submodules or multiple definitions of the module. + +Switching to the \Cpp~language has only a minor influence on the operation +of \CWEB.: one-line comments starting with `\.{//}' will be recognised, the +main output file produced by \.{CTANGLE} will have default extension `\.{.C}' +instead of~`\.{.c}', and \.{CWEAVE} will recognise a few more reserved words +and use a slightly different syntax. Since there is no general agreement +about the proper extension for \Cpp~files, and alternative default extension +for \Cpp~mode (instead of \.{"C"}) may be built in by setting the +preprocessor symbol \.{CPPEXT} to the desired string (that should not +contain the leading period) when compiling \.{common.c}. Currently the +\.{CWEAVE} grammar will handle only a basic subset of the \Cpp~language, +which does not include templates or exception handling. + +\subsection File name arguments + +Any command line arguments that do not have the form of an option are taken to +indicate file names; their number can vary from 1~to~3. The first one specifies +the main source file, the second (if present) indicates the change file, and +the third optionally defines a non-standard name for the main output file. +The contents and function of the change file is discussed in the next +section; here we we just indicate how the actual file names used are derived +from the given file name arguments. As far as \CWEB. is concerned a file +name is composed of a base name and an extension. Loosely speaking, the +extension of the main file defaults to~`\.{w}', that of the change file +to~`\.{ch}', and that of the output files to `\.{c}' or `\.{tex}' for +\.{CTANGLE} respectively \.{CWEAVE} (but see also the discussion of the +`\.{++}' option above); the base names of the change file and the main +output file default to that of the main file. If in place of a change file +name an argument `\.-' is specified, no change file is used; also if only +one file name argument was given, or if the change file name was specified +as `\.+', then the default change file name is tried, but if no such file +exists, processing proceeds without a change file. (Specifying the change +file as `\.+' is only useful if a third file name argument is given.) +Therefore, assuming regular naming conventions, there is no need to specify +more than the main file name without extension, whether or not a change file +is being used. + +The precise rules are as follows. On file systems where an extension is not +a standard property of file names, like that of \caps{UNIX}, it is assumed +the a period is a valid character in file names; a full file name is then +formed by concatenation of the base name, a period and the extension (note +that this implies that on such systems \CWEB. cannot access files whose name +contains no period at all). Conversely, a string designating a full file +name is broken up into a base name and an extension at the last occurrence +of a period; if no period is present, then the string is taken to specify a +base name only, and is said to have no extension. If the first file name +argument has an extension, it specifies both base name and extension of the +main source file, otherwise it specifies the base name, and the extension is +taken to be~`\.w' (if no such file is found, the extension `\.{web}' is also +tried, but this feature is obsolete). The base name of the main source file +is also the default base name of the change file and the main output file; +their default extensions are as described above. If a second and possibly +third file name argument is present and is not `\.+'~or~`\.-', it overrides +the base name, and also the extension if it has one, of the change file +respectively of the main output file. No change file will be used either if +the second file name argument is `\.-', or if no change file is found when the +second file name argument is `\.+' or absent. + + +\beginsection Subsidiary input files and change files + +As we have described it so far, the \CWEB. tools read a single source file, +from which a main output file and possibly some auxiliary output files are +produced. Since \Cee~programs can be built from several compilation units, +it is not uncommon that several \CWEB. source files contribute independently +to the same program, and there might be non-\CWEB. source files as well. +However, even what is conceptually a single \CWEB. source, described by a +single printed document, may in fact be composed from several input files. +Two mechanisms are provided for combining information from several files, +with different purposes. First, subsidiary files may be read in from the +main source file in a way similar to the way \&{\#include} files are handled +by a \Cee~compiler. In the case of \CWEB. however, the main purpose is +usually not to share information among several sources, but merely to allow +breaking up large source files into more easily manageable parts. Second +there is the change file mechanism already mentioned above, which serves to +install system dependent patches to a master source, allowing that master to +remain free of system dependencies. + +When a line of the form `\.{@i}~\<file name>' appears in a \CWEB. source +file, \CWEB. will read in the indicated file at that point, and continue +reading at the next line when it reaches the end of the subsidiary file. The +\<file name> may either be delimited by white space, or be enclosed in +double-quote characters (but not in angle brackets). Source files may be +nested in this way up to 10~levels deep. Nothing in the printed \CWEB. +document will indicate the switch from one source file to another, nor will +there be any effect on the \Cee~file(s) written by \.{CTANGLE}, except that +\&{\#line} directives will of course always point to the proper point of +origin for each piece of code written to such files. + +Like for header files, there is a way to indicate that if a file included by +\:i is not found in the current directory, an alternative place can be +tried; unlike header files however there is relatively little need to use +this facility, unless one has files that are useful to include identically +in more than one project. At most one alternative place to search can be +given, and it is specified by a prefix to be applied to the file name, in +the same way as for header files. This prefix may either be compiled into +the \CWEB. programs by setting the preprocessor symbol \.{CWEBINPUTS} equal +to that string when compiling \.{common.c} (analogously to \.{CWEBHEADERS}), +or it can be specified at run time by setting the environment variable +\.{CWEBINPUTS}; when both methods are used, the latter takes precedence. + +The change file, if present, contains a sequence of ``changes'', each of +which specifies the replacement of one or more lines from the main input +stream by another set of lines. Each change has the form `\.{@x} \<original +lines> \.{@y} \<replacement lines> \.{@z}', where each of the codes \:x, +\:y, and~\:z occupies a line by itself. The \<original lines> is a non-empty +set of lines that should match exactly with some sequence of lines in the +main input stream (except for the fact that trailing white space on any line +is ignored). Furthermore, different changes should affect non-overlapping +sets of lines, and their order in the change file should be the same as that +of the parts of the main input stream that they replace. For each change in +succession, a sequence of lines matching \<original lines> is searched for, +and replaced by the corresponding \<replacement lines>; like for \:i file +insertions, the resulting stream of lines will be processed in the usual way +as if it constituted a single \CWEB. source file. The ``main input stream'' +referred to here is the result of (recursively) inserting any auxiliary +files indicated by \:i lines into the main \CWEB. source file. It therefore +makes no sense to specify \:i in the \<original lines>, nor is \:i allowed +in the \<replacement lines>: it should simply not occur anywhere in the +change file. On the other hand it is legitimate for the \<original lines> to +match a sequence of lines coming from more than one physical source file. + +The fact that input is temporarily switched to the change file is not +entirely transparent to the \CWEB. document, as it was in the case if \:i +files: \.{CWEAVE} will mark all sections that were modified under control of +the change file, by attaching an asterisk to their section number, and to all +references to that number. (If some changes should add or remove entire +sections in the middle of the \CWEB. source, which is allowed although not +encouraged, then the section numbering will be altered, but sections for +which this is the only change will not be flagged with an asterisk.) If one +is only interested in sections that are modified, then it is even possible +to restrict printing to only those sections, by including the \TeX~command +`\.{\\changesonly}' in the text in limbo, preferably by means of the change +file. + +In order to facilitate efficient implementation of the change file +mechanism, an additional constraint is placed on the changes: once an exact +match of a line in the main input stream with the first line of a change is +found, the remaining lines of the change (up to the \:y) should also match. +Any empty lines immediately following \:x are not used for matching (and are +in fact completely ignored) so the first matching line is never an empty +one; it is preferable to choose changes such that their first line matches a +unique line of the main input. It is a good idea to start changes in the +\TeX~part of sections (after all, if the program changes, so should its +explanation); in this case uniqueness of the match of the first change line +can always be ensured (even when the \TeX~part is empty) by placing a +\TeX~comment in the main input, that serves merely as a target for +replacement by the change file. All text in the change file that is not +part of a change is ignored, except that there should be no lines starting +with \:i, \:y, or~\:z; this text can be used for instance to explain the +purpose of the change to the person installing the program on a new system, +rather than to the ordinary reader of the program. + +As we have said earlier, the change file mechanism provides an alternative +to system dependent conditional compilation, and it is usually a much more +elegant way to incorporate system dependencies. The main reason for this is +that one does not have to anticipate all possible systems that a program +could be ported to, nor is the main source polluted by such considerations: +it suffices to provide a separate change file each time the program is moved +to a system with different system dependent requirements. Users of a +particular system need to know about the change file for that system only, +and the responsibility for maintaining main source and the change file might +lie with different persons; additional effort is only required when the main +source changes in such a way that a change file fails to match. + +One should not get carried away by the benefits of change files though: they +provide only a rather crude mechanism (due to the inflexible matching +rules), and if there are many changes, they will become difficult to +maintain when the master file evolves. Portability is still best obtained by +limiting system dependent features as much as possible, and if inevitable, +confining them to some well defined part of the program. If one should wish +to create variants of a program that involve significant changes, then +writing extensive change files is probably not the best way to go. This +method could lead to a form of ``rigor mortis'' for the original version of +the program, caused by fear that any alterations could upset one of the +change files, even trivial changes that only involve the commentary, or even +just the layout of the source file. A better approach would be to collect +routines of general utility as much as possible into separate compilation +units used by all variants, and to complement these with completely +independent compilation units to define the specific behaviour of each of +the variants. It is certainly pointless to use a change file for such things +as bug fixes or further development of a program; the whole idea is that +such modifications can be made in the master file while the change files for +various systems need little or no adjustment. + +The codes \:i, \:x, \:y, and~\:z of this section have the appearance of +control codes, but they are not really part of the \CWEB. language, and +obey different rules than control codes. For instance, they are line +oriented (and rightly so, since their goal is to select which lines will be +actually processed by \CWEB.): they should appear at the beginning of a +line, and any further text on the line (in case of \:i, after the file name) +is ignored. Also they act quite independently of \CWEB.'s current mode of +operation: rules such as the one forbidding control codes in limbo do not +apply to these codes. + + +\beginsection Control codes for advanced or emergency use + +In this section we discuss control codes that are not essential for everyday +use of \CWEB., but are provided to enable either refinements in the +presentation of the \CWEB. document, or special manoeuvres to deal with +certain unusual situations or requirements. Most of them serve to allow the +programmer some form direct control over the contents of either the \CWEB. +document, the \Cee~file, or the source file, bypassing the automatic +processing by which these are normally related to each other; there are also +a few that serve as debugging aid, eliciting explicit information from the +\.{CWEAVE} parser about its actions. + +\subsection Control codes for cross-referencing: + \:!, \:\^, \:., \:?, \::, \:\# + +Some control codes are provided that allow the programmer to influence +indexing and to perform explicit cross-referencing. The codes in this +subsection are the only ones that are allowed to occur in the \TeX~part of +sections, outside `\pb'; with the exception of~\:\#, they can also be used +in \Cee~text. Control codes such as these, that are intended only to affect +the printed document, are ignored completely by \.{CTANGLE}. Incidentally, +cross-referencing in \CWEB. always means referring to section numbers rather +than to page numbers: \.{CWEAVE} cannot know about page numbers since these +are determined only at the \TeX\ processing stage. It would be possible to +have \TeX\ produce a table mapping section numbers to page numbers; in fact +the table of contents provides a coarse approximation to such a map. + +Whenever \.{CWEAVE} can determine from the context that an occurrence of an +identifier is a defining one, it will make the corresponding section +reference in the index underlined. If some case is missed by \.{CWEAVE}'s +normal rules, or if one wants to make a reference to a reserved word (which +is only made if it is underlined), then one can place the code \:! in front +of the identifier to create an underlined reference. Cases where this may be +required include arguments of functions with an old-style (pre-\caps{ANSI}) +heading for which no declaration is given before the function body (i.e., +the default type \&{int} applies), and enumeration constants that appear out +of context of the \&{enum} keyword (e.g., because the enumeration list +is given as a separate module). In general, the occasions where one needs +\:! are quite rare. + +A group of three codes serves to include additional entries in the index, +amidst those generated automatically by \.{CWEAVE} for identifiers. It may +be useful for instance to maintain references to concepts like `system +dependencies', or to all error messages that can be generated. The three +codes are \:\^, \:., and~\:?; they differ only in the way the index entry +will be typeset. In each case the index entry is specified as a control text +terminated by \:>; control code and control text will be removed by +\.{CWEAVE}, but the control text will appear in the index, followed by the +section number(s) where the control code occurred. For \:\^,~\:., and~\:?, +the index entry will be set respectively in roman type, in typewriter type, +and as argument to the control sequence `\.{\\9}' (which is undefined in the +standard format, but which the programmer may define in limbo). The first +possibility is most suited for general concepts, the second for strings that +occur in the program, and the third for any further special purpose one may +think of. These control codes can be put either in the \TeX~part of a +section or within \Cee~code; the effect will be the same, but this allows +the programmer to put the control code in such a place that it is most +likely to remain in the right place in case the section should be +reorganised and possibly subdivided. Like for references to identifiers, one +can make an index reference underlined by prefixing the corresponding +control code with~\:!. + +Unlike the control text forming a module name, the control texts discussed +here (as well as those that have not been introduced yet) should be +contained in a single line of input; also, no spaces are contracted or +removed. The control texts are passed unchanged to \TeX\ (with only `\.{@@}' +being undoubled as usual), so that they can use \TeX~commands for special +effects. Inside `\.{@.\dots@>}' one can get the special characters occurring +in `\.{\#\$\%\^\&\{\}\~\_\\}' by prepending a backslash, `\.{\\v}' gives a +vertical bar~`\.\v', and `\.{\\ }' gives a visible space `\.\ '. + +The control texts are also used as a sort key to determine the place in the +index where the entry appears. Different occurrences of these control codes +are combined in the index only if there is an exact match of both control +code and control text, and no merging takes place with identifiers whose +name happens to be equal to the control text (however, their relative order +in the index is unpredictable). In sorting, a collating sequence is used +that differs from the standard \ASCII. order: alphanumeric characters appear +at the end of the sequence, with upper and lower case being considered +equivalent, and the space character appears at the beginning of the +sequence. In case there are entries that cannot be correctly positioned by +ordinary means, the following trick has been suggested by Knuth: define +`\hbox{\.{\\def\\9\#1\{\}}}' and represent the tricky entries as +`\.{@?\<sort key>\}\{\<\TeX~code>@>}', where \<sort key> contains +sufficiently many characters to uniquely determine the position of the entry +in the index, and \<\TeX\ code> produces the index entry itself; this works +because \.{CWEAVE} will write the index entry +`\.{\\9\{\<sort~key>\}\{\<\TeX~code>\}}', which ``expands'' to +`\.{\{\<\TeX~code>\}}'. + +Besides references from the index, \.{CWEAVE} provides cross-references, in +the form of the section numbers that link the (first) defining occurrence of +a module name with the places where it is used and cited. There is also a +mechanism for the user to explicitly state similar cross-references in the +\TeX~part of a section, so that it is possible make a reference to another +section (where some related matters are treated), that will remain correct +if sections are renumbered. The mechanism is simple: in the section referred +to, one places the control code~\::, followed by a control text serving as a +label, and at the place of reference one uses \:\#, followed by the +identical control text (both control texts are terminated by~\:>). The rules +for placing \::\ are the same as for \:\^ and its relatives, except that +\:!\ has no effect here; the control text will not appear in the index, and +there is no conflict when the same string is used as an identifier or index +entry. + +For~\:\# and its control text, \.{CWEAVE} basically substitutes the section +number of the matching \::~code, but because there might be multiple +occurrences of \:: with the same control text, the precise replacement rule +is a bit more complicated. The replacing text is precisely what would follow +``See also section'' in a cross-reference for a module name: one or more +section numbers in increasing order, separated by commas and ``and'' as +appropriate, and preceded by a space and, in case there is more than one +section number, by an `\.s' before that space. This is set up so that a +reference of the form `\.{section@\#label@>}' will generate a proper +reference, whether or not there are multiple definitions of the label. One +can also use `\.{\\Sec@\#label@>}' since in the standard format `\.{\\Sec}' +expands to `\Sec' and `\.{\\Secs}' to `\Secs' (in this case the space +produced by~\:\# is ignored after the \TeX\ control sequence); by defining +other \TeX~macros one could do anything one likes with the text provided +by~\:\#. Although \:\# cannot be used directly in comments and module names, +it is possible to capture its text in a macro definition (within a +\TeX~part) and use that macro instead. + +\subsection Control codes for layout in programs: + \:,, \:|, \:/, \:), \:\\, \:+, \:; + +As we mentioned before, \.{CWEAVE} formats the program fragments and pieces +of \Cee~code by inserting formatting controls in the the output based on a +syntactic analysis of the \Cee~tokens of the program fragments; in +particular the layout of the code in the source file is completely ignored. +Although this automatic formatting usually works well provided that +\.{CWEAVE} succeeds in parsing the program fragment (possibly with help of +some codes already discussed), there may still be occasions where one is not +quite satisfied by the result. If one wishes certain constructions to be +systematically treated in a different way, then a more pleasing style might +be available by calling \.{CWEAVE} with certain options set; if not, then +there is always the possibility of changing the grammar or layout rules of +\.{CWEAVE} (that program was written in a way that tries to make this as +easy as possible, but it still requires some careful study of the relevant +chapters of the \.{CWEAVE} source document). However in some cases one +simply wants to override the general rules in specific cases by adding or +removing a few formatting controls. There are a number of control codes +which can be used to do that. These codes are ignored by~\.{CTANGLE}; since +most of them deal with line breaks, their importance for `\pb' fragments is +minimal. + +The control code \:, will insert a thinspace (a small amount of horizontal +white space) where it is placed. Within an statement \:| may be used to +indicate a place where a line break may be optionally taken (with no +associated penalty), when the statement is too long to fit on a single line. +Note however that optional breaks are already allowed at most operator +symbols, with a penalty that increases with the operator priority and the +number of enclosing parentheses, so \CWEB. will almost always succeed in +finding very a reasonable break point in long expressions. A line break can +be forced by~\:/; this can be used for instance between statements (if line +breaks are not already forced there), in order to group related statements +on one line rather than simply as much as possible. The code \:) will also +force a line break, and in addition create a bit of vertical white space to +give an even more visible separation. (\.{CWEAVE} will never issue more than +one line break on the same place, so there is no problem if a line break was +already present on that spot.) The code \:\\ is another variation: it forces +a line break and backs up the next line by one indentation unit. It is +useful before a module name that represents one or more cases in a +\&{switch} statement: this will make the name line up with the case labels. + +Finally, \:+ cancels any (forced) line break that might be inserted by +\.{CWEAVE} at the point where it is placed, and replaces it by a space with +optional line break (the kind of space that is usually inserted between +statements). Its main use is to force small conditional or loop statements +onto a single line when \.{CWEAVE} would otherwise use a multiple-line +layout. Because the line can still be broken at the inserted space, such +one-liners do not make it impossible to retypeset the program in a narrower +column. A warning is in place however if, as a result of applying \:+, a +substantial stretch of \Cee~code is void of forced breaks, and that code +contains constructions that affect the indentation level. \TeX nically +speaking, the indentation at optional breaks is governed by the hanging +indentation parameter of \TeX, whose value is constant throughout a +paragraph, which in this case is everything between two forced breaks; under +the mentioned circumstances the amount of indentation at optional breaks can +be unexpected and inappropriate. + +For convenience, an alternative method is provided to fit compound +statements on a single line, and similarly for \&{struct} and \&{union} +specifiers. Instead of writing \:+ on every place where \.{CWEAVE} would +otherwise force a line break (which incidentally depends on the chosen +layout style), it suffices to place \:; immediately after the opening brace. +This will activate a different set of layout rules than is normally used, +which will not insert forced breaks between the declarations and statements +of the compound statement, respectively between the fields of the \&{struct} +or \&{union} specifier. In the case of a compound statement, any forced +breaks caused by conditional or loop statements appearing directly inside +the compound statement are also avoided (but nested statements are not +affected, so they should be handled separately if present, possibly using +another~\:;). Compound statements starting with `\.{\{@;}' will be treated +as if they were simple statements in further parsing, which may affect +formatting; for instance, if the statement is the branch of a conditional it +will be placed on the same line as the \&{if} or \&{else} controlling it. +If this is too much of a good thing, a forced break may be explicitly +inserted at the beginning and/or end of the compound statement; in fact the +sequence `\.{@/\{@;}' is a fairly common one. + +There is another use of~\:+, which does not cause any breaks to be +cancelled, but where on the contrary the purpose is insert white space. +It applies when a long string constant is needed, for which the +string-break feature is used: a sequence of strings separated by white space +only will be concatenated by the compiler into a single string. Although +\.{CTANGLE} will correctly insert a space between any two consecutive +strings, \.{CWEAVE} (guided by syntax rather than by lexical structure) will +simply juxtapose them; by inserting \:+ between the strings, one guarantees +that in the printed document there will either be a horizontal separation or +(if the constituent strings themselves are already long) a line break. +Incidentally, if the problem of breaking a string is in the source file +rather than in the printed output, one can use the traditional solution of +an escaped newline within the string; \.{CWEAVE} will treat this as if the +parts of the string were on the same source line. If one should create a +string in this way that does no fit on a single line of output, a break will +be introduced automatically at a some point, which will be typeset as if a +string-break was used. In very long strings however it is better to write +string-breaks explicitly; for strings broken only by escaped newlines, the +same length limit holds as for module names (1000~characters). + +\subsection Codes for special items in\/ \Cee~code: + \:p, \:v, \:t, \:\&, \:=, \:' + +Contrary to \TeX~text, pieces of \Cee~code are broken up into tokens by both +\.{CTANGLE} and \.{CWEAVE}, stored internally and output at some later time +after having undergone some processing. This makes it potentially difficult +to put something into \Cee~code that \CWEB. is not prepared to handle. Since +\Cee\ is a much more regular language that \TeX, occasions where one would +need to do such a thing should be quite rare, yet some escape mechanisms +have been provided, which we treat in this subsection. + +The code \:p can be used to explicitly specify the place where the +preprocessor directives generated by \:d and \:h commands will be placed in +the \Cee~file. Multiple use of \:p is allowed; as soon as it is used at +least once, the default placement at the beginning of the \Cee~file is +cancelled. This code provides the only way to write the directives generated +by \:d and \:h to an auxiliary output file. In the formatted output this +code is represented by the pseudo-module `$\ATP$', which (like preprocessor +directives embedded in a program fragment) is set on a separate line and +does not otherwise affect the formatting of the surrounding code. + +Two other codes are intended mainly for use within `\pb'. As mentioned +earlier, \:v represents the bitwise-or operator. The code \:t is followed by +a control text, which can be used to insert any \TeX\ symbols into a +\Cee~expression; the result gets category \\{expression} but (if used in a +program fragment) does not produce any actual \Cee~code. It is for instance +possible to obtain `$\\{phi}<\hbox{$\pi$}/\T{2}$' by writing +`\hbox{\.{| phi < @t\$\\pi\$@> / 2 |}}', or if one prefers, to get +`$\\{phi}<\hbox{$\pi\over2$}$' by writing `\hbox{\.{| phi < +@t\$\\pi\\over2\$@> |}}'. The control text is put into an \.{\\hbox} that +will appear at the specified point in the formula. One might imagine using +\:t as a means to sneak in \TeX\ commands that will modify the formatting +produced by \.{CWEAVE}, but this is strongly discouraged unless one +thoroughly understands that formatting and the way it is obtained. + +The codes \:\& and \:= are intended as a means to alter or bypass the +processing of \Cee~tokens by \.{CTANGLE}; they should only be used in very +exceptional situations. The code \:\& forces \.{CTANGLE} to output the +symbols to the left and right of it directly adjacent to each other. Normally +\.{CTANGLE} inserts space between two symbols if it thinks this is necessary +for lexical reasons, regardless of whether a space was present in the input. +Items with a lexical structure unknown to \.{CTANGLE} might confuse it, so +that it would output a spurious space; this space could then be eliminated +by~\:\&. For instance, an earlier version of \.{CTANGLE} would not recognise +`\.{1000000UL}' as a constant, and consequently it output a space before +the `\.U', so that the \Cee~compiler could not recognise it either; this +problem could then be remedied by inserting~\:\&. No similar cases are known +for the current version of \.{CTANGLE}. + +The code \:= can be used to place some text in the \Cee~file that \.{CTANGLE} +will not produce by ordinary means: the control text following~\:=, up to +the next \:> is copied verbatim to the \Cee~file (with `\.{@@}' undoubled as +usual). If some special compiler activity, or some action by another tool, is +triggered by the occurrence of some special form of comment in the \Cee~code, +then such a comment can be placed using \:= (normally comments are removed +by \.{CTANGLE}). Also, should \.{CTANGLE} unjustly decide that two symbols +need no space in between them, then a space can be forced by writing +`\hbox{\.{@= @>}}' +\unskip\footnote{${}^\dagger$} +{One case where this would be necessary is the famous example + `$\T{\^123E}+\T{1}$': the \Cee~standard states that unless a space is put + between the `\.E' and the `\.+', the preprocessor should treat this as a + single number (a~kind of mixture of a hexadecimal and a floating point + constant), which turns out not to be valid, causing an error. \.{CTANGLE} + however never places a space between an identifier and an operator (even if + one was present in the input), so the way to get this expression properly + through the compiler is to write `\hbox{\.{0x123E @= @> + 1}}' (since this + bug is now documented, it has become a feature). +}. +The control text will be set in typewriter type and +framed in a box by \.{CWEAVE}, so that it stands out clearly; it is +syntactically neutral (like a comment). + +Finally, the code \:' can be used to introduce a single-character constant, +in the same way as the character `\.'' does in~\Cee. The difference between +the two ways of specifying this value is that \.{CTANGLE} will replace +\.{@'}$c$\.' by the (decimal) numeric \ASCII. value of the character~$c$, +whereas \.'$c$\.' is passed on to the \Cee~compiler, which will evaluate it +to the same value. The feature is therefore of little use in the current +version of \CWEB., which assumes the \ASCII. character set, but is provided +as an aid in writing programs that will be easier to port to non-\ASCII. +versions of \CWEB.. In such systems \.{CTANGLE} should still use the \ASCII. +code to compute \.{@'}$c$\.', while \.'$c$\.' represents the internal code +for~$c$. The idea is that one can then (as is done in the program \TeX) map +all characters on input to their \ASCII. equivalents, perform all internal +manipulations independently of the externally used character set, and +convert back to that code on output. + +\subsection Control codes behind the scenes: \:s, \:q, \:l + +The control codes of this subsection have in common that their use is never +essential, but can be convenient in some situations, and is largely or +completely invisible in the \CWEB. document. They are also the only control +codes allowed in limbo. + +The code \:s has the same effect as \:f, but produces no output in the \CWEB. +document. It can be used as a subsectioning code, just like \:f, but no +comment should follow the two identifiers it applies to in this case (since +there is nothing to attach the comments to); alternatively \:s can be used in +limbo. In either case the format definition is noted but nothing is written +to the \TeX~file. One might prefer to use \:s in situations where showing a +\&{format} definition is considered to be more distracting than informative. +Also, if a header file~$h$ is included by \&{\#include} rather than by \:h +or is located in a place where \.{CWEAVE} cannot find it, and it contains +typedef declarations, then~$h$ could be accompanied by a file containing a +line `\hbox{\.{@s ident FILE}}' for each typedef identifier defined in~$h$, +which can be read in by means of \:i by any \CWEB. file that includes~$h$. +This method of passing information between files is more error-prone than +having \.{CWEAVE} scan the header file however, so the latter method is to +be preferred whenever possible. + +The code \:q is followed by a control text, and is completely ignored both +by \.{CTANGLE} and \.{CWEAVE}; it can be used either in \TeX~text (even in +limbo) or in \Cee~code. It can be used to make comments relevant only when +the source file itself is being read, particularly within \Cee~code, where +\TeX~comments cannot be used for this purpose. For instance, it can be used +to put a descriptive or identifying comment at the beginning of a file +included using \:i. This code can also be used to accommodate any other +tools than \.{CTANGLE} and \.{CWEAVE} that might inspect the source file, +e.g., if a text editor tries to match braces and the like, it is unlikely to +correctly handle the complicated lexical structure of \CWEB. files in all +cases, and an occasional brace contained in a \:q control text may help to +keep it happy. Such occurrences of \:q are best removed however when source +files are made public. + +The code \:l is used to allow certain 8-bit characters (i.e., characters +with values in the range 128--255) to be used in identifiers. Doing so is +only useful if measures are taken to ensure that \TeX\ can handle these +characters properly. \TeX~version~3.0 and newer can handle 8-bit characters +in the input, but the standard fonts do not have any characters in positions +128--255, so one has to either load other fonts that do have characters in +those positions, or define such characters to be active characters that +somehow produce an appropriate glyph in the current font. For identifiers +the relevant font is text italic (selected by `\.{\\it}'), but if these +characters are available for identifiers, one will probably also want to use +them in \TeX~text (including module names and comments), so other fonts +should be provided for as well. \.{CWEAVE} does not take any special +measures for 8-bit characters, and just passes them on to \TeX\ (when they +occur in \Cee~code outside comments and module names, they are assumed to be +part of an identifier). However, since such characters cannot be used in +actual \Cee~identifiers, \.{CTANGLE} must replace them by characters that +are valid in \Cee~identifiers (letters, digits, and underscores). The +code~\:l can be used to specify which translation \.{CTANGLE} is to use for +a given 8-bit character. The code should only be used in limbo, and have the +form `\.{@l}~\<char number>~\<translation>', where \<char number> specifies +the character by a pair of hexadecimal digits in the range \.{80}--\.{FF} +(without leading `\.{0x}'), and \<translation> is a string of up to~9 +characters that are valid in \Cee~identifiers, terminated by a space. While +copying limbo material, \.{CWEAVE} replaces \:l by `\.{\\ATL}'; its default +definition will make `\.{@l}~fc~ue~' print a paragraph saying `{\bf letter +\\{\"u} tangles as \tentex "ue"}', assuming that `\hbox{\.{\{\\it\\char +"FC\}}}' indeed produces `\\{\"u}'; by stating `\.{\\noatl}' the definition +can be changed so that nothing appears at all. + +\subsection Control codes for tracing {\tentex CWEAVE}: + \:0, \:1, \:2, \:3 + +As will have become clear by now, the most sensitive part of \CWEB. is +\.{CWEAVE}'s parsing mechanism, and occasionally something may go wrong +with it, so that an awful result is produced. We have already discussed the +means available for corrective action, but sometimes it can be a problem to +find out just what is causing the trouble. Sometimes the reason is an actual +syntax error, which is best located by applying \.{CTANGLE} and a +\Cee~compiler to the \CWEB. source, but as noted before, \.{CWEAVE} may have +a problem that a \Cee~compiler does not experience. For this reason, its +parser can produce diagnostic messages on the terminal, showing details +about its actions and any anomalies found. The amount of diagnostics +produced is controlled by a level that may take values from~0 to~3, and can +be selected by one of the control codes \:0, \:1, \:2, and~\:3. These codes +can be placed in the \TeX~part of sections or within \Cee~code, and they +determine the level until the next such code or the end of the file; the +initial level is~0, or~1 if \.{CWEAVE} was called with the option~`\.{+d}'. +Since a complete \Cee~fragment is read in before parsing starts, the level +is constant throughout each fragment, and determined by the value at the end +of the fragment. + +The diagnostic output uses abbreviations for syntactic categories, e.g., +`\.{unop}' and `\.{binop}' stand for unary respectively binary operators, +and `\.{op}' stands for operators like `$*$' that can be used either way; +`\.{exp}' stands for an expression, `\.{decl}' for one or more declarations, +`\.{stmt}' for one or more statements. Simple symbols like braces and commas +stand for themselves, as do many keywords; `\.{for}' stands for `\&{for}' +or~`\&{while}', and `\.{int}' for a type, storage class specifier or typedef +identifier. A complete list of category abbreviations can be found in the +source code for the function $\\{print\_cat}$ in~\.{CWEAVE}. +(To fully understand the parser's diagnostic messages one has to be familiar + with the parsing algorithm and the grammar rules, but for error detection + a detailed understanding is usually not required.) + +At level~$0$ the parser will not produce any diagnostic output. At level~$1$ +it will report any \Cee~fragment that could not be recognised as a single +syntactic entity, which is a good indicator of grammatical problems and +possibly of ugly output. It can be argued that level~1 is the natural level +to use (which is why the `\.{+d}' option is provided), since not getting any +diagnostics when there are syntax problems only gives a false impression +that things are in order; after all, nobody would want to use a compiler +that would spare the programmer its diagnostics for syntax errors, but +instead would produce unreliable code. In a syntactically correct \CWEB. +program it is almost always possible to apply \:f, \:;, \:[ and~\:] in such +a way that no diagnostic output is produced at level~$1$; indeed this is has +been done for all sources of \CWEBx. itself. + +The diagnostic messages produced at level~1 print the successive categories +of the sequence of recognised items, which could not be combined into any +larger entity. Interpreting such a message takes a bit of practice, as one +has to guess which part of the program fragment corresponds to each category +printed; however, a look at the (badly) formatted output can often be +helpful. The boundaries between the entities corresponding to the printed +categories can often be recognised by the fact that some form of layout is +obviously missing, and a space appears instead; for instance, if a closing +brace of a compound statement is not preceded by a line break, then +something inside that statement must have prevented it from being recognised +by the parser, and its opening and closing brace will occur among the +printed categories. + +At levels $2$~and~$3$ the parser will print the result of every single step +it takes; this extremely verbose mode can be used to trace the exact steps +by which the parser obtains its result. Detailed knowledge of the set of +grammar rules is assumed, and these levels are mostly useful to those who +wish to study or modify the rules. The set of rules can be found as a +chapter of the \CWEB. source document for \.{CWEAVE}, or can be obtained +separately by running `\hbox{\.{cweave -x rules}}' (ignoring the warning +about an unused module) and `\hbox{\.{tex rules}}'. After each reduction +step the number of the rule used is printed, followed by a list of +categories after reduction, with the one that was formed by the reduction +step enclosed in inverted angle brackets. Tracing at level~$3$ is even more +esoteric than at level~$2$: all categories printed will have an additional +character at both ends, indicating whether \TeX\ should be in math mode +(`\.+') or in horizontal mode (`\.-') at that end of the item, or that it +doesn't matter (`\.?'); this may help to explain the positioning of math +shifts (`\.\$') in the \TeX\ output, which is controlled indirectly by the +grammar rules. + +To reduce the amount of output, all categories that have not yet been +considered by the parser are replaced by an ellipsis. The sequence of +categories before the reduction can be found by looking up the reduction +rule with the given number. Here is some sample output for a simple piece +of \Cee~code at level~$2$. +\vfil\penalty500\vfilneg\smallskip\vbox +{\narrower\obeylines\catcode`\_=12 \tentex \let\\=\BS \obeyspaces\let =\ % +Tracing after l.3: +@2 |if (n>0) printf("n-1 = \%d.\\n",n-1);| + 2: if ( >exp< ) ... + 10: if >exp< exp ... +110: >if_head< exp ( ... + 6: if_head exp ( >exp< op ... + 3: if_head exp ( >exp< ) ... + 10: if_head exp >exp< ;. + 7: if_head >exp< ;. + 80: if_head >stmt<. +117: >stmt<. +} +\smallskip +We see that the first three steps reduce `\&{if}~$(n<\T{0})$' to an +\.{if\_head}; then `$\.{"n-1\ =\ \%d.\\n"},n$' is combined to an expression, +after which `${}-1$' is incorporated as well; then the statement calling +\\{printf} is reduced in three steps, and finally it is combined with the +`\.{if\_head}' to form another statement. + +Even this small example shows that \.{CWEAVE} parses the code in a +different way than a \Cee~compiler would. This is partly due to its strict +bottom-up strategy, which is largely unaware of context: parentheses +(rule~10) and a comma (rule~6) are incorporated by the expression syntax, +even when they actually figure in a conditional statement or function call. +Furthermore, some distinctions are irrelevant for determining the +proper layout: the ``comma operator'' is unjustly given precedence over the +minus operator, but the printed output will be no different for it. + + +\beginsection Some features of the standard format + +The \TeX~file produced by \.{CWEAVE} will begin with loading the standard +format from the file \.{cwebxmac.tex}, whose definitions control the +typesetting process: \.{CWEAVE} communicates with \TeX\ mostly by using +macros defined there. Most of them have very short names, in order to limit +the size of the \TeX~file; one should be aware that most of the +single-letter control sequences and numerous two-letter ones are in use by +\.{CWEB}, and are not available for other uses (when in doubt, consult +\.{cwebxmac.tex}). All of the macros defined in plain \TeX\ for accenting +letters have retained their meaning however, except `\.{\\.}' (for the dot +accent), which is replaced by~`\.{\\:}'. Some of the macros of the standard +format can be of interest to the literate programmer, either because they +can be used directly in \TeX~text (indeed, some are not used by +\.{CWEAVE}, and are only intended for this purpose), or because they can be +redefined in limbo to alter the formatting of the program. + +In formatted \Cee~text, many operators are represented by macros that +produce the appropriate symbols; by changing the definition of these macros, +one can alter their appearance. Here is a table of the relevant cases. +$$ +\everycr{\noalign{\hrule}} +\def\:#1 #2 +{\vrule\vbox{\halign + {\strut\ \hfil##\hfil\ \cr \.{#1}\cr \.{\\#2}\cr $\csname#2\endcsname$\cr} +}} +\hbox +{\vrule\vbox + {\hrule\hbox{ \strut\it operator }\hrule + \hbox{ \strut\it macro }\hrule + \hbox{ \strut\it symbol }\hrule + }% + \:= K \:== E \:!= I \:<= Z \:>= G + \:\&\& W \:|| V \:! R + \:\& AND \:| OR \:\^ XOR \:\~ CM \:<< LL \:>> GG + \:++ PP \:-- MM \:\% MOD \:-> MG \:\#\# SS + \vrule +} +$$ +When such a macro is redefined, it is best to consult the original +definition first, since it often issues a penalty, and it is best to retain +this. Formatting of ordinary identifiers and keywords is performed by +`\.{\\\\}' and `\.{\\\&}', which have one argument, that is typeset in italic +respectively boldface type; similarly `\.{\\.}' is used for items in +typewriter type, such as strings and all-caps identifiers. In the argument of +`\.{\\.}' special characters can be used if escaped, as discussed for \:.. +For `\AM' in ordinary text `\.{\\AM}' can be used (rather than `\.{\\\&}'). +For names in all caps, like `\caps{ASCII}', or `\caps{UNIX}', the macro +`\.{\\caps}' is provided, which makes them slightly less obtrusive by +selecting a smaller font; for `\Cee' and `\Cpp' the macros `\.{\\Cee}' and +`\.{\\Cpp}' are provided. Typesetting of comments, \Cpp\ one-line comments, +and numeric constants is controlled by the macros `\.{\\C}', `\.{\\SHC}', +and~`\.{\\T}', respectively; these can be redefined if a different style is +desired. + +The dimensions of the pages can be controlled by setting the parameters +`\.{\\pagewidth}', `\.{\\pageheight}' (the height of the text area), +`\.{\\fullpageheight}' (the height including running head), and +`\.{\\pageshift}' (extra displacement of odd numbered pages with respect to +even numbered ones), and then invoking the macro `\.{\\setpage}'. A +magnification can be applied to the entire document by saying +`\.{\\magnify\{$n$\}}',where $n$ is the magnification in thousandths of the +ordinary scale; this should precede any changes of the page dimensions, +but if no changes are made, the page dimensions will be set to their +standard values, unmagnified. The unit of indentation can be set by +`\.{\\indentation\{\<size>\}}'. + +The title of the program is taken from the macro `\.{\\title}', whose +default value is the basename of the program source file, converted to upper +case. It is used in running heads and in the table of contents. Another part +of the running heads is set to the chapter title by sections starting +with~\:*, but by defining the macro `\.{\\gtitle}' in limbo the +corresponding text for the running heads on any pages before the first such +section can be set (the default is `\.{CWEB} output'). By invoking +`\.{\\titletrue}' the running head can be suppressed for one page; this is +useful if the text in limbo produces a title page. The date of processing +(by \TeX) can be included in the document before the first section by +putting `\.{\\datethis}' in limbo; it can be placed on the table of contents +by saying `\.{\\datecontentspage}'. At the end of the document one normally +has an index, a list of module names and the table of contents, in that +order, but \TeX\ can be made to stop short of any one of these by invoking +respectively `\.{\\noinx}', `\.{\\nomods}', or `\.{\\nocon}'; as already +mentioned, stating `\.{\\changesonly}' will limit the printed output to the +sections affected by the change file. The appearance of the table of +contents can be controlled by redefining `\.{\\topofcontents}' and +`\.{\\botofcontents}': these macros determine the material that comes above +the table and below it, including its title and the glue needed to fill up +the page height. The page number of the table of contents is assigned from +`\.{\\contentspagenumber}' (the default is~0), but it will not appear in +print because the running head is suppressed on that page. + +It may be noted that \CWEB. documents contain some fixed phrases in the +English language, such as the cross-references at the end of sections. These +are not produced directly by \.{CWEAVE} however: one could adapt \CWEB. to a +different language by redefining the macros `\.{\\A}', `\.{\\As}', `\.{\\Q}', +`\.{\\Qs}', `\.{\\U}', `\.{\\Us}', `\.{\\ET}', `\.{\\ETs}', `\.{\\ch}', +`\.{\\postATL}', `\.{\\ATP}', `\.{\\today}', `\.{\\now}', and parts of +`\.{\\fin}' and~`\.{\\con}'. + + +\beginsection Comparison with Levy/Knuth {\tentex CWEB} + +As was mentioned in the introduction, \CWEBx. is derived from an earlier +\CWEB. system (itself derived from Knuth's \.{WEB}), that was written and +distributed by Sylvio Levy and Donald~E. Knuth, and that \CWEB. system has +independently evolved into a version currently distributed as \CWEB.~3.3. +Both \CWEBx. and \LKC. have undergone changes with respect to their common +ancestor, although the spirit of the system has not fundamentally changed in +either case. Since we considered it undesirable to have a great divergence +between systems that both intend to be ``a \caps{WEB} system for~\Cee'', we +made a conscious effort to reduce the differences between \CWEBx. and \LKC. +by including the extensions of the latter into \CWEBx. as well. There was +one deliberate exception: we made no attempt to extend the grammar used by +\.{CWEAVE} to handle the full \Cpp~language +\unskip\footnote{${}^\dagger$} +{\Cpp\ has a significantly more complicated syntax than that of~\Cee, which + is already far from simple, and it has some forms of context dependence + that make it doubtful whether to \.{CWEAVE} could ever reliably handle + \Cpp\ in full generality (and even then, \Cpp\ is a moving target). Most + effort was spent on getting the grammar for \Cee\ correct; support for + \Cpp\ was restricted to some extensions of~\Cee\ that could be incorporated + easily. +}. +On the other hand, hoping to fully bridge the gap between \LKC. and \CWEBx. +for \Cee~programs, a compatibility mode was added to \CWEBx. in which it +tries to mimic the behaviour of \LKC. in all aspects that are relevant to +the programmer (even in cases where that behaviour is undocumented), at the +price of losing some possibilities that \CWEBx. normally has. + +The description of the differences between \LKC. and \CWEBx. can be divided +into two parts: the differences between \LKC. and the compatibility mode +of~\CWEBx., and the differences between \CWEBx. with and without +compatibility mode. The former differences are minimal, but hard to +enumerate precisely, as they are mainly a matter of difference in +implementation. The latter differences are much more significant, but they +can easily be listed, since precise details of those differences can be +found by looking up all index references of the identifier +\\{compatibility\_mode} in the sources for the programs of~\CWEBx., and (for +the differences that only involve processing by \TeX) the contents of the +file \.{cwebcmac.tex} that modifies the \.{cwebxmac} format to emulate the +environment provided by the \.{cwebmac} format of \LKC.. + +As was stated, the differences between \LKC. and the compatibility mode +of~\CWEBx. should not be relevant to the programmer, but if one uses \LKC. +in a way that relies on knowledge of intimate details of its implementation +(which are not described in the manual but can be learned from studying the +sources), then it is certainly possible to find such differences; this +applies particularly to using the \TeX~code produced by \.{CWEAVE} in +unusual ways. Unfortunately there is no clear specification of which aspects +of \CWEB. are well defined so that the user can safely rely on them, and +which aspects are implementation details. We have taken a pragmatic attitude +by reproducing all aspects that are described in the manual, and moreover +many undocumented aspects, enough to process the sources of \LKC. itself and +of the Stanford GraphBase without problems. To give an impression of the +kind of differences that remain, we shall list some of the known ones. + +The output files written by \CWEBx. are not equal to those written by \LKC., +so processing them otherwise than directly by a \Cee~compiler respectively +by~\TeX\ may reveal some deviations. For instance, entries for the index and +the list of module names are written using the control sequence `\.{\\I}' by +\LKC., but since `\.{\\I}' is also used for representing the operator +`$\I$', this causes problems for module names in which that operator is +used; therefore, \CWEBx. uses `\.{\\@}' instead. In \CWEBx. unbalanced +braces or parentheses in program fragments or macro replacement texts are +reported and corrected by \.{CTANGLE}, as an aid in catching programming +errors early; in \LKC. this is not done (but programs with such unbalanced +symbols will still bring \.{CWEAVE} into serious problems). In compatibility +mode the definition of `\.{\\PB}' ensures that `\pb' can always be used from +within math mode; in \LKC. this is true only in simple cases. Comments in +the \TeX~parts of sections (following a non-escaped `\.{\%}' character) are +ignored and removed by \CWEBx., whereas in \LKC. they are processed normally +and copied to the output, which may cause spurious index entries, and in +exceptional cases may cause part of the comment to appear in print. The +grammars used by \.{CWEAVE} in the two systems are quite unrelated; for +\CWEBx., the only guideline in constructing the grammar has been the +\caps{ANSI/ISO~C} syntax. When processed by \CWEBx. with the proper options +selected, \CWEB. documents will look similar to the result produced by +\LKC., but not identical. Unlike \LKC., \CWEBx. places optional breaks at +operators, reflecting their priority and nesting inside parentheses. In +\LKC., if \:{\ } is immediately followed by a subsectioning code, then the +output from the subsectioning code (e.g., \.{\#define} for~\:d) will be +placed on the same line as the section number, but if anything, even an +extra space, comes in between, or if the `\.@' in the sectioning code was +followed by a newline rather than by a space, then that output is moved to +the beginning of a fresh line; in \CWEBx. output from a subsectioning code +never appears on the same line as the section number. + +\CWEBx. has a number of control codes and a number of command line options +that \LKC. does not have; moreover there are some control codes that \LKC. +does have, but under different names (that are used for other purposes +in~\CWEBx.). In compatibility mode such control codes have the same +interpretation as in \LKC., but if there is no such interpretation while +there is one in \CWEBx., the latter is taken;. This means that in +compatibility mode one can use the control codes \:v, \:\\, and \:\~, which +are ignored in \LKC., while \:?~and~\:) can be used as aliases for +\::~and~\:\#, respectively; furthermore the command line options controlled +by the characters `\.l', `\.d', `\.t', `\.e', `\.a', `\.u', `\.w', `\.m', +and~`\.+' are also extras with respect to \LKC.. Finally the control code \:; +retains all its \CWEBx. uses in compatibility mode, except that of modifying +the category of module names (which is different anyway), whereas in \LKC. +it can only be used as an invisible semicolon. + +The most direct difference between \CWEBx. with and without compatibility +mode is that in compatibility mode the control codes \:h, \::, \:\#, and~\:p +are translated into respectively \:p, \:?, \:), and~\:c, which implies that +the meaning of \:h, \::, and~\:\# as described in this manual are not +available in compatibility mode. There is also an important syntactic +adjustment: in compatibility mode module names are always treated as +expressions (which means they must almost always followed by \:; to make the +combination behave as a statement, or by `\.;', which will however become an +empty statement in the \Cee~program). Then there are a few points where +compatibility mode lifts certain restrictions (thereby reducing the +diagnostic capabilities). All 8-bit characters will be accepted by +\.{CTANGLE}, whether or not an explicit translation was specified using \:l; +the default translation used corresponds to `\hbox{\.{@l \Cident{NN} +X}\\{NN}}', where \\{NN} is the 2-digit hexadecimal code, in upper case, for +the character. Module names used within `\pb' do not have to be the final +item. Rather than performing \:i inclusions before the change file is +matched, the order is more or less reversed (but if some \:i~line is not +replaced by the change file, then the included file will again be scanned +for changes); consequently \:i is allowed (and meaningful) in the change +file. + +The remaining alterations affected by compatibility mode are fairly minor. +Trailing digits in identifiers will not be set as subscripts. The \TeX\ +control sequence corresponding to identifiers that are given the category of +\\{\TeX} by means of a format definition will be processed in math mode +rather than horizontal mode. The code \:t together with the following +control text will not be treated as an expression in parsing, but as an +inert item that sticks to the token to the right of it (unlike comments that +are attached to the token to their left); this allows `\.{@/@t\\4@>}' to be +used in place of `\.{@\\}'. Compound assignment operators like `\.{+=}' are +treated as two separate tokens; this implies among other things that the +operator `\.{|=}' must be entered as `\.{@v=}' when used inside `\pb'. In +index entries produced by \:\^, \:., or~\::, the underscore character will +be automatically escaped by a backslash, unlike other special characters +(this makes it harder to enter formulas with subscripts into the index). +The output of `\pb' is made an argument to the control sequence `\.{\\PB}', +whose default definition puts its argument into an `\.{\\hbox}'; this makes +it safe to use `\pb' inside math mode when using compatibility mode. +Finally there are a few other small changes to the format used: \Cpp\ +one-line comments will be formatted as if they were ordinary \Cee~comments, +and the formatting of lines in the table of contents will be affected in +font and spacing by the ``depth'' specified for that chapter title. + + +\beginsection Summary of\/ {\tentt CWEB} codes + +For reference, we give a table with all the codes used in \CWEB. with their +main characteristics. The letters in the column {\it where\/} indicate in +which parts of the source text may immediately precede the code: +`L'~indicates text in limbo, `T'~the \TeX~part of a section, `M'~indicates +an intermediate part of a section (from \:d, \:h, ~\:f, or~\:s), `C'~the +\Cee~part of a section, and `c' pieces of \Cee~code within~`\pb' (the +letter~`M' is only used when the code terminates an intermediate part; +inside the parts after \:d, \:h, and~\:f, the letter `C' applies). The +column {\it frequency\/} indicates how commonly the code is used, with +$\\{regular} > \\{incidental} > \\{rare} > \\{emergency}$; for the codes with +$\\{frequency}\leq\\{rare}$, no sensible use could be found within any +source file for the \CWEB. system itself. The codes \:0--\:3 that are only +of temporary use are omitted from the table. + +\vfil\eject +$$\vrule\vcenter{\hrule\smallskip\tabskip 1em +\let\par=\cr\obeylines \def\title #1 + {\noalign{\smallskip\hrule\smallskip\hbox{\sl\enspace#1}}}% +\halign{\strut\.{#\unskip}\hfil & #\unskip\hfil & #\unskip\hfil & % + {\it#\unskip\/}\hfil & #\unskip\hfil +\omit\strut\it code & \it meaning & \it where & \it frequency & \it remarks +\title Sectioning codes +@* & Start of chapter & LTMC & regular % + & \.{@**}, \.{@*$n$}, or \.{@*} Title\.. +@~ & Start of section & LTMC & regular +@\~ & Start of section tied to previous one & LTMC & regular +\title Subsectioning codes +@c & Start of unnamed program fragment & TM & regular % + & also \.{@C} +@< & Start of module name & TMCc & regular % + & \.{@<} Module name \.{@>} +@d & `\.{\#define}'; start macro definition & TM & regular % + & also \.{@D} +@h & `\.{\#include}'; specify included header file & TM & regular % + & also \.{@H} +@f & Format definition; change syntactic category & TM & incidental % + & also \.{@F} +@( & Start module name defining output file & TMCc & incidental % + & \.{@(} file name \.{@>} +\title Parsing control codes +@; & Invisible semicolon, or magic wand for syntax & Cc & incidental +@[ & Start of item forced to expression & Cc & incidental +@] & End of item forced to expression & Cc & incidental +\title Cross-referencing codes +@! & Make index reference underlined & TCc & rare +@\^ & Index entry in roman type & TCc & regular % + & \.{@\^}index entry\.{@>} +@. & Index entry in typewriter type & TCc & regular % + & \.{@.}index entry\.{@>} +@? & Index entry formatted by `\.{\\9}' & TCc & rare % + & \.{@?}index entry\.{@>} +@: & Define label for explicit cross-reference & TCc & incidental % + & \.{@:}label\.{@>} +@\# & Explicit cross-reference to defined label & T & incidental % + & \.{@\#}label\.{@>} +\title Layout control codes +@, & Thin space & Cc & rare +@| & Optional line break & Cc & rare +@/ & Forced line break & Cc & incidental +@) & Forced line break with vertical white space & Cc & incidental +@\\ & Forced line break, next line backed up & Cc & incidental +@+ & Cancel any line break, replace by space & Cc & incidental +\title \Cee\ control codes +@p & Insert output from \:d and \:h & Cc & rare % + & also \.{@P} +@v & Bitwise or operator `$|$' & Cc & incidental % + & also \.{@V} +@t & \TeX~code within expression & Cc & incidental % + & \.{@t}\TeX~code\.{@>}; also \.{@T} +@\& & Glue together adjacent tokens & Cc & emergency +@= & Insert verbatim \Cee~code & Cc & emergency % + & \.{@=}verbatim \Cee~code\.{@>} +@' & \ASCII. constant converted to number & Cc & rare & \.{@'}$c$\.' +\title Silent control codes +@s & Non-printing version of \:f & LTM & rare +@q & Ignored control text & LTCc & rare % + & \.{@q}any text\.{@>}; also \.{@Q} +@l & Specify translation of 8-bit character & L & rare % + & \.{@l} $xx$ string ; also \.{@L} +%\title Debugging level switches +%@0 & Debugging off & TCc & temporary +%@1 & Report parsing failures & TCc & temporary +%@2 & Trace every parsing step & TCc & temporary % +% & grammar maintenance +%@3 & Trace every parsing step, show mathnesses & TCc & temporary % +% & implementors only +\title Miscellaneous codes (these are not control codes) +@@ & Representation of `\.@' & LTCc & incidental % + & legal in control text too +@i & Insert subsidiary source file & any & incidental % + & also \.{@I} +@x & Start of change; old lines follow & any & incidental % + & also \.{@X} +@y & Middle of change; replacement lines follow & any & incidental % + & also \.{@Y} +@z & End of change & any & incidental % + & also \.{@Z} +}\smallskip\hrule}\vrule +$$ + +\closeout\cont % the contents information has been fully gathered +\vfill\eject +\pageno=\contentspagenumber \secno=0 \titletrue +\message{Table of contents:} +\topglue 0pt plus 1.618 fil +\centerline{\ttitlefont CWEBx\titlefont\space Manual} +\vfil +\def\Z#1#2#3{\line{\kern.1\hsize\indent\llap{#1}\quad #2 + \leaders\hbox to .5em{.\hss}\hfil \enspace#3\kern.1\hsize}} +\def\z#1#2{\line{\kern.1667\hsize #1 + \leaders\hbox to .5em{.\hss}\hfil \enspace#2\kern.1\hsize}} +\readcontents +\vskip 0pt plus 2.618 fil +\eject + +\bye diff --git a/web/c_cpp/cwebx/parser.w b/web/c_cpp/cwebx/parser.w new file mode 100644 index 0000000000..ecc46d119b --- /dev/null +++ b/web/c_cpp/cwebx/parser.w @@ -0,0 +1,1233 @@ +@* Introduction to the grammar. @:title@> +The most intricate part of \.{CWEAVE} is its mechanism for converting +\Cee-like code into \TeX\ code, and we shall consider this aspect of the +program now. This parsing mechanism must be able to deal with fragmentary +constructions whose overall ``part of speech'' is not known (e.g., those +within~`\pb'), which implies that recognition must in principle be able to +proceed without any context information. Therefore a ``bottom-up'' approach +is used, that collects successive tokens, and only takes action once it has +seen enough to recognise a complete match with a syntax rule. Bottom-up +parsing is a very powerful technique (it can handle grammars that are not +tractable by many other parsing methods); it is one of the many significant +contributions to computer science that the designer of the original \.{WEB} +system, D.~E. Knuth, has made. Even so, the technique used here is less +powerful than that of traditional bottom-up parsers produced by parser +generators, since those can use information derived from the full +left-context (and a small amount of right-context) to help decide difficult +situations. In practice such context is not often required to unambiguously +parse program fragments, and most situations where there is a possibility of +incorrect recognition can be avoided be careful specification of the syntax +rules to be used; this does however make formulation of the grammar a +slightly subtle matter. + +As we have already seen, the input is represented as a sequence of +{\sl scraps}, each of which specifies a {\sl category\/} and a +{\sl translation}. The category defines a syntactic class, and the +translation is a token list that represents \TeX\ code; the latter is +effectively just a single string of characters, although it is stored in a +different form for efficiency reasons. 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 (i.e., if the input is not properly formed +for recognition by our parser), we will be left with several scraps that +don't combine; their translations will simply be output, one by one. + +The parsing process is governed by a set of reduction rules, each of which +specifies how a sequence of consecutive scraps with certain given categories +can be combined into a single new scrap, and it also tells what category the +new scrap has and how its translation is obtained from those of the original +scraps. The set of reduction rules is given by a table, which is actually +an initialiser for a static array, starting in section@#rules@>. +In many cases a rule is simply given by listing the category codes +forming its pattern (its left hand side) and the category code of the +resulting scrap (the right hand side). For example, one of the reduction +rules could be represented as +$$ +\hbox{|expression| |binop| |expression|}\Rightarrow\hbox{|expression|}. +$$ +For such simple rules the translations of the original scraps are simply +concatenated to form the translation of the resulting scrap. The line in the +table corresponding to this rule actually looks like this: +$$ +\hbox{|{2,{{expression,binop,expression}},{expression, NULL}}|}. +$$ +The `2' is the number used to identify the rule, which is useful when +tracing the activities of the parser, and the |NULL| signifies that the +default translation applies; the braces reflect the structure used to +represent syntax rules, which has some additional components that are not +specified explicitly here, and that are therefore initialised to~0. + +For other rules specific additional items are to be inserted in between the +translations of the scraps that match the pattern. For instance, here is +another rule present in the table +$$ +\hbox{|{ 6,{{expression, comma, expression}},{expression,"__p1_"}}|}. +$$ +Here the translation is specified by the ``format string'' |"__p1_"|, where +the underscores stand for the translations of the three participating +scraps, the `\.{p1}' for an |opt| token with argument~`\.1' (eventually +producing `\.{\\penalty10}') that comes after the translation of the second +scrap, with category |comma|. That translation will almost always be |","|, +since there are no rules with |comma| as their right hand side, but if a +comment follows the comma, it will have been incorporated into its scrap +while preparing for the reduction, and in this case the optional break will +follow the comment (and have no effect, because there is already a forced +break after comments). The interpretation of all characters that can appear +in the format strings can be found in section@#format@>. + +To handle all cases that can arise in programs, in combination with a large +number of formatting options that might be selected, our mechanism for rules +of a somewhat more general form than those presented above, but the +extensions are used only for a few rules. Consider the following rule: +$$ +\hbox{|{132,{{lpar,statement,statement},1}, + {statement,"_B_"},forced_statements}|}. +$$ +Here, the `1' indicates that the first scrap from the left in the pattern, +|lpar|, serves as context only, and does not take part in the actual +reduction. The rule therefore reduces a sequence of two consecutive +statements into a single one, inserting a |break_space| (from the `\.B' +format character) between their translations, but only if the statements are +preceded by a left parenthesis (category~|lpar|); this restriction means +that the rule will only apply to the first two semicolon-separated +expressions following `|for|~|(|'. The entry |forced_statements| means +moreover that the rule will not be used at all unless a `\.{+f}'~or~`\.{+a}' +command line option was specified, which forces line breaks between +consecutive statements; this rule then avoids such line breaks between the +controlling expressions of a |for|-loop, since it takes precedence (see +below) over the rule that normally combines statements. + +@ The rules are applied from left to right, as follows. Suppose we are +currently working on the sequence of scraps with categories $c_1\,c_2\ldots +c_n$. We try first to find a rule whose pattern matches an initial substring +$c_1\,c_2\ldots\,$, and if no such rule exists, we try to find a rule +applicable to the next substring $c_2\,c_3\ldots\,$; if that fails, we try +to match $c_3\,c_4\ldots\,$, etc. When several patterns match, starting at +the same scrap, the longest one is selected (no two rules can have identical +patterns). For instance, there is a rule that reduces `|struct_like| +|expression| |lbrace|' to `|struct_head|', and another one that reduces +`|struct_like| |expression|' to `|int_like|'; the latter is only applied +when no |lbrace| scrap follows. This is a sensible rule, since the longer +pattern is the more specific one, and without such a rule it could never +match. One should be aware however that this only works because the scrap +with category |lbrace| represents a single token that requires no reduction +to create it, for otherwise the two-scrap reduction would be applied before +the three-scrap reduction would have a chance to match. + +One might say that we use a left-to-right eager strategy for choosing +reductions; this strategy is chosen on heuristic grounds, and there is no +guarantee that it will find a successful sequence of reductions if one +exists. In other words, if we interchange left and right hand sides of the +rules and view them as {\sl production\/} rules of a context free grammar, +then the language generated by this grammar can be much larger than the one +recognised by our parser. For instance, the context free grammar would, from +the rules given above, generate expressions with multiple operators where +implicit parentheses are placed arbitrarily (and therefore it would be +ambiguous); our parsing strategy however will always place implicit +parentheses to the left. (We don't care if implicit parentheses are placed +incorrectly, because it does not influence typesetting.) It is an +interesting theoretic problem to find an algorithm that will transform a +set of reductions rules into a context free grammar generating exactly the +language recognised; that would help to verify that the rules will work as +intended. We must be cautious in formulating the rules, not only to give +rules that are sufficient to reduce all desired programs, but also not to +specify rules that could in certain circumstances match with precedence over +the ones we intended for the situation. + +Some of the formatting of the output is controlled by the \TeX~format in the +file \.{cwebxmac.tex}, rather than directly by \.{CWEAVE}. For instance, the +`\.{\\penalty10}' mentioned above is actually written as `\.{\\31}'; the +macro `\.{\\3}' will in fact raise the penalty for any enclosing pair of +braces or parentheses; thus breaks in argument lists or initialiser lists +will be avoided in favour of ones outside those lists. There are other +effects not visible in the grammar, like optional breaks automatically +associated with certain operators. In the whole it is a delicate interplay +between the set of reduction rules, the reduction mechanism, the macro +definitions, and the (mathematical) typesetting rules of \TeX\ that +determines how programs will be typeset. This division of labour is quite +convenient (e.g., it would tremendously complicate the task of \.{CWEAVE} if +it had to decide which optional breaks should actually be taken), but it has +the unfortunate consequence that it is not easy to gain a comprehensive +understanding of the translation process. Indeed there have been quite a few +surprises during the construction of the system, and we cannot be sure that +there are no more to come; yet we believe the situation is more transparent +than in the original |WEAVE|, since we have avoided rules that are oriented +towards the target language (\TeX) rather than towards the source language +(\Cee). + +@ Here is a list of the category codes that scraps can have. The category +codes that apply to reserved words (e.g., |while_like|, but also +|declaration| for |va_dcl|) as well as |expression| (that is used for +|type_defined| identifiers in their typedef declaration) are sufficiently +high that they can be distinguished from |ilk| values that are not category +codes, like |type_defined|, |TeX_like|, |NULL_like|, |const_like|, +and~|typedef_like|. A number of categories can occur in scraps, but do not +occur in any of the reduction rules, since they are handled by other means; +they have values exceeding |max_rule_cat|. The macro |valid_cat| checks +whether |c| is a category that might match in a rule; it uses its argument +twice, so its argument should not cause a side effect. If this section is +changed, section@#category output@> should be changed correspondingly. + +@d max_category end_expr /* largest scrap category */ +@d max_rule_cat return_like /* largest category in rules */ +@d valid_cat(c) ((c)>0 && (c)<=max_rule_cat) + +@<Typedef and enum...@>= +enum @/ @:categories@> +{ unop = 1, /* a unary operator like `|!|' */ + binop, /* a binary operator like `|<|' */ + unorbinop, /* an operator that can be either, like `|-|' */ + select, /* structure selection: `|.|' or `|->|' */ + question, /* a question mark operator */ + lbrace, rbrace, lpar, rpar, lbrack, rbrack, + /* `|{|', \ `|}|', `(', \ `)', `[', \ `]' */ + comma, semi, colon, colcol, magic, /* `,', `;', `:', `$\CC$', \:; */ + subscript, /* an array subscript, like `|[]|' or `|[i++]|' */ + struct_head, /* the beginning of a struct specifier, like `|struct s{|' */ + short_lbrace, short_struct_head, /* from `\.{\{@@;}', for one-liners */ + compound_statement, /* a complete compound statement */ + statement, /* a complete statement, possibly compound */ + function, /* a complete function definition */ + function_head, /* a function identifier followed by formal parameters */ + parameters, + /* parameters in function declaration, or casting operator like `|(int)|' */ + label, /* a statement label */ + if_head, /* `|if|' followed by a (parenthesised) expression */ + if_else_head, /* \lq|if @t\dots@>@; else|', \lq|while(@t\dots@>)|' + or \lq|switch(@t\dots@>)|' */ + do_head, /* `|do @t\dots@>@; while|' */ + mod_scrap, /* module name */ + declarator, /* abstract declarator, like `|(*)(int,char*[])|' */ + declaration, /* a complete declaration */ + expression, /* an expression, possibly a single identifier */ + while_like, /* `|for|', `|while|', `|switch|' */ + do_like, /* `|do|' */ + if_like, /* `|if|' */ + else_like, /* `|else|' */ + int_like, /* `|int|', `|char|', `|extern|', \dots */ + case_like, /* `|case|', `|default|' */ + sizeof_like, /* `|sizeof|' */ + struct_like, /* `|struct|', `|union|', `|enum|' */ + return_like, /* `|return|', `|break|', `|continue|', `|goto|' */ + lproc, /* `\&\#' and following identifier starting preprocessor directive */ + rproc, /* end of a preprocessor directive */ + insert, /* comment or other syntactically inert item */ + begin_expr, end_expr /* \:[ and \:] */ +}; + +@ As we have already seen, tokens are converted to elementary scraps by +the function |C_read|; these scraps form the `terminal symbols' of our +grammar. The translation of tokens to scraps is largely governed by the +static array |trans_ini|, whose initialisation values we shall now give. + +@d yes_math 1 /* should be in math mode */ +@d no_math 2 /* should be in horizontal mode */ +@d maybe_math 0 /* works in either horizontal or math mode */ + +@< Initialiser for |trans_ini| @>= +{ '!', unop, yes_math, "\\R" }, @/ @.\\R@> +{ '~', unop, yes_math, "\\CM" }, @/ @.\\CM@> +{ '/', binop, yes_math, "/" }, @/ +{ '<', binop, yes_math, "<" }, @/ +{ '>', binop, yes_math, ">" }, @/ +{ '.', select, yes_math, "." }, @/ +{ '=', binop, yes_math, "\\K" }, @/ @.\\K@> +{ '|', binop, yes_math, "\\OR" }, @.\\OR@> + { or, binop, yes_math, "\\OR" }, @/ +{ '^', binop, yes_math, "\\XOR" }, @/ @.\\XOR@> +{ '%', binop, yes_math, "\\MOD" }, @/ @.\\MOD@> +{ '+', unorbinop, yes_math, "+" }, @/ +{ '-', unorbinop, yes_math, "-" }, @/ +{ '*', unorbinop, yes_math, "*" }, @/ +{ '&', unorbinop, yes_math, "\\AND" }, @/ @.\\AND@> +{ '?', question, yes_math, "\\?" }, @/ +{ '(', lpar, yes_math, "(" }, @/ +{ ')', rpar, yes_math, ")" }, @/ +{ '[', lbrack, maybe_math, "[" }, @/ +{ ']', rbrack, maybe_math, "]" }, @/ +{ '{', lbrace, yes_math, "\\{" }, @/ +{ '}', rbrace, yes_math, "\\}" }, @/ +{ ',', comma, yes_math, "," }, @/ +{ ';', semi, yes_math, ";" }, @/ +{ ':', colon, maybe_math, ":" }, @/ +{ '#', insert, maybe_math, "\\#" }, @/ @.\\\#@> + /* this should occur only in macro definitions */ +{ at_sign_image, insert, maybe_math, "@@" }, + /* this should not occur in legal \Cee~text */ +@) +{ not_eq, binop, yes_math, "\\I" }, @/ @.\\I@> +{ lt_eq, binop, yes_math, "\\Z" }, @/ @.\\Z@> +{ gt_eq, binop, yes_math, "\\G" }, @/ @.\\G@> +{ eq_eq, binop, yes_math, "\\E" }, @/ @.\\E@> +{ and_and, binop, yes_math, "\\W" }, @/ @.\\W@> +{ or_or, binop, yes_math, "\\V" }, @/ @.\\V@> +{ plus_plus, unop, yes_math, "\\PP" }, @/ @.\\PP@> +{ minus_minus, unop, yes_math, "\\MM" }, @/ @.\\MM@> +{ minus_gt, select, yes_math, "\\MG" }, @/ @.\\MG@> +{ gt_gt, binop, yes_math, "\\GG" }, @/ @.\\GG@> +{ lt_lt, binop, yes_math, "\\LL" }, @/ @.\\LL@> +{ mul_assign, binop, yes_math, "\\KK*" }, @/ @.\\KK@> +{ div_assign, binop, yes_math, "\\KK/" }, @/ +{ mod_assign, binop, yes_math, "\\KK\\MOD" }, @/ @.\\MOD@> +{ plus_assign, binop, yes_math, "\\KK+" }, @/ +{ minus_assign, binop, yes_math, "\\KK-" }, @/ +{ left_assign, binop, yes_math, "\\KK\\LL" }, @/ +{ right_assign, binop, yes_math, "\\KK\\GG" }, @/ +{ and_assign, binop, yes_math, "\\KK\\AND" }, @/ @.\\AND@> +{ xor_assign, binop, yes_math, "\\KK\\XOR" }, @/ @.\\XOR@> +{ or_assign, binop, yes_math, "\\KK\\OR" }, @/ @.\\OR@> +{ thin_space, insert, yes_math, "\\," }, @/ @.\\,@> +{ pseudo_semi, magic, maybe_math, "" }, @/ +{ force_expr_open, begin_expr, maybe_math, "" }, @/ +{ force_expr_close, end_expr, maybe_math, "" }, @/ +{ join, insert, no_math, "\\J" }, @/ @.\\J@> +{ ellipsis, int_like, yes_math, "\\ldots" }, @/ +{ sh_sh, binop, yes_math, "\\SS" }, @/ @.\\SS@> +{ colon_colon, colcol, yes_math, "\\CC" } + +@ Certain tokens that lead to fixed scraps are not included in the +|trans_ini| array because their translations involve non-character tokens. +Since there are only a few of them the easiest solution is to install each +one explicitly into the |token_trans| array. +@d start_scrap(s,c,m) p=&token_trans[s],p->cat=c, p->mathness=5*(m) +@d end_scrap p->trans=text_ptr, freeze_text(); + +@< Install the translations of tokens involving line breaks @>= +{ scrap* p; + start_scrap(math_break,insert,maybe_math); /* \:\v */ + app(opt), app('0'); end_scrap; +@/start_scrap(line_break,insert,no_math); /* \:/ */ + app(force); end_scrap; +@/start_scrap(end_preproc,rproc,no_math); /* end of preprocessor directive */ + app(force); end_scrap; +@/start_scrap(' ',insert,no_math); /* space within preprocessor directive */ + app(break_space); end_scrap; +@/start_scrap(big_line_break,insert,no_math); /* \:) */ + app(big_force); end_scrap; +@/start_scrap(backup_line,insert,no_math); /* \:\\ */ + app(backup); end_scrap; +@/start_scrap(no_line_break,insert,no_math); /* \:+ */ + app(cancel),app(relax),app(break_space),app(relax),app(cancel); end_scrap; +@/start_scrap(include_preproc,insert,yes_math); /* \:p */ + app(force),app_str("\\ATP"),app(force); end_scrap; @.\\ATP@> +} + +@ When \.{CWEAVE} is compiled with the |DEBUG| switch, it can display +its parsing steps. The order of strings in |cat_name| must match that +in the |enum| declaration in section@#categories@>. + +@c +@:category output@> +#ifdef DEBUG +void print_cat (int c) /* symbolic printout of a category */ +{ static char* cat_name[]= + { "unop", "binop", "op", "select" + , "?", "{", "}", "(", ")", "[", "]", ",", ";", ":", "::", "@@;" + , "subscr", "struct_head", "short_{", "short_struct_head" + , "cmp_stmt", "stmt" + , "function", "function_head", "params", "label" + , "if_head", "if_else_head", "do_head" + , "mod_name", "declarator", "decl", "exp", "for", "do", "if", "else" + , "int", "case", "sizeof", "struct", "return" + , "#{", "#}", "insert", "@@[", "@@]" + }; + if (c<=max_category && c>0) printf(cat_name[c-1]); + else printf ("IMPOSSIBLE"); +} +#endif /* |DEBUG| */ + +@ Another major class of terminal symbols is formed by the reserved words. +If a name exists in the hash table with an |ilk| specifying a reserved word, +then |id_lookup| will return the reserved word when called with that name, +and |C_read| will use the |ilk| to set the category of the resulting scrap. +So all that has to be done is to get all the reserved words into the hash +table with the right ilks initially. The simplest way to do this is to call +|id_lookup| for all reserved words with the proper |ilk| at the beginning of +each run of \.{CWEAVE}. Fortunately there are not too many reserved words. +This code below uses the fact that instead of using pointers to beginning +and end of a string for |id_lookup|, one may also pass a single pointer to a +null-terminated string provided the other pointer is null. + +@^reserved words@> + +@<Store all the reserved words@>= +{ int i; static char* int_likes[]= + { "auto","char","double","extern","float","int","long","register" + , "short","signed","static","unsigned","void" }; + static char* defined_types[] = + { "FILE", "size_t", "ptrdiff_t", "wchar_t" + , "jmp_buf", "sig_atomic_t", "fpos_t", "div_t", "ldiv_t" + , "clock_t","time_t" + , "va_list" + }; + static char* return_likes[]= + {"break","continue","goto","return"}; + int int_like_nr=array_size(int_likes), + defined_type_nr=array_size(defined_types), + return_like_nr=array_size(return_likes); + + for (i=0; i<int_like_nr; ++i) id_lookup(int_likes[i],NULL,int_like); + for (i=0; i<defined_type_nr; ++i) + id_lookup(defined_types[i],NULL,type_defined); + for (i=0; i<return_like_nr; ++i) id_lookup(return_likes[i],NULL,return_like); + + id_lookup("case", NULL, case_like); + id_lookup("const", NULL, const_like); + id_lookup("default", NULL, case_like); + id_lookup("do", NULL, do_like); + id_lookup("else", NULL, else_like); + id_lookup("enum", NULL, struct_like); + id_lookup("for", NULL, while_like); + id_lookup("if", NULL, if_like); + id_lookup("sizeof", NULL, sizeof_like); + id_lookup("struct", NULL, struct_like); + id_lookup("switch", NULL, while_like); + id_lookup("typedef", NULL, typedef_like); + id_lookup("union", NULL, struct_like); + id_lookup("va_dcl",NULL, declaration); + id_lookup("volatile", NULL, const_like); + id_lookup("while", NULL, while_like); + id_lookup("NULL", NULL, NULL_like); + id_lookup("TeX", NULL, TeX_like); + if (C_plus_plus) @<Store reserved words for \Cpp@> +} + +@ The main difference between \Cee\ and \Cpp, as far as \.{CWEAVE} is +concerned, is that the latter has a number of additional reserved words. Most +of them are sufficiently like some \Cee-reserved word (or category) that we +can simply make it behave like that \Cee~symbol, without changing the syntax. +For `\&{new}' and `\&{operator}', some additional syntax rules will be needed +however; nevertheless we do not need to extend the set of syntactic +categories. For `\&{operator}' we abuse the category |case_like|, since its +proper use is rather restricted (`|case|' it is always followed by an +expression, while `|default|', `\&{private}' and its relatives are always +followed by a colon), so there will be no confusion with `\&{operator}', which +is always followed by an operator symbol. + +@<Store reserved words...@>= +{ id_lookup("asm", NULL, int_like); + id_lookup("class", NULL, struct_like); + id_lookup("delete", NULL, sizeof_like); + id_lookup("friend", NULL, int_like); + id_lookup("inline", NULL, int_like); + id_lookup("new", NULL, sizeof_like); + id_lookup("operator", NULL, case_like); + id_lookup("private", NULL, case_like); + id_lookup("protected", NULL, case_like); + id_lookup("public", NULL, case_like); + id_lookup("this", NULL, expression); + id_lookup("virtual", NULL, int_like); +} + +@ There are a few more kinds of elementary scraps that the functions we have +given before can produce, which we mention here for completeness. Ordinary +identifiers get category |expression|, and their names will be expanded on +output as argument to a control sequence that provides the proper formatting. +For strings, constants, verbatim constructions, and \TeX~strings, the +applicable control sequences and the constituent characters (escaped with +backslashes where necessary) are written explicitly into token memory; +their scraps also have category |expression|. Comments are converted to +scraps of category |insert|, and their contents are also stored literally; +in case of `\pb' fragments a reference to a text marked with +|inner_text_flag| is stored, for the production of which the parsing +mechanism has in fact already been invoked. By contrast module names are +stored by reference to the name table, just like identifiers, and their +scraps have category |mod_scrap|; in their case the parsing mechanism may +be called during the {\sl output\/} process, if any `\pb' constructions +occur. + +The final \TeX\ output produced for elementary scraps will often be marked +with special control sequences. Ordinary multi-character identifiers are +enclosed in `\.{\\\\\{}$\,\ldots\,$\.\}' (single character identifiers are +merely preceded by a space; they will be set in math italic), identifiers +whose |ilk| is |TeX_like| will become control sequences that are +also enclosed in `\.{\\\\\{}$\,\ldots\,$\.\}' (to establish italic type), +reserved words are enclosed in `\.{\\\&\{}$\,\ldots\,$\.\}', strings and +all-caps identifiers in `\.{\\.\{}$\,\ldots\,$\.\}', constants in +`\.{\\T\{}$\,\ldots\,$\.\}', and verbatim constructions in +`\.{\\vb\{}$\,\ldots\,$\.\}'. Comments are enclosed in +`\.{\\C\{}$\,\ldots\,$\.\}' and usually followed by `\.{\\6}' (a forced +break), and module names take the form `\.{\\X$n$:}$\,\ldots\,$\.{\\X}' +where |n| is the section number (since module names have +|mathness==yes_math|, there is no danger that the final `\.{\\X}' will +disable a following space when coming from a `\hbox{\.{\v@@< ... @@>\v}}' +construction). + +@* The parsing mechanism. @:title@> +Conceptually the working sequence of scraps is like a deck of cards, in +which we repeatedly replace a sequence of consecutive cards by a single new +card. Since such replacements never increase the number of cards, we can use +sequential allocation for the current sequence of scraps, and our only +difficulty will be how to conveniently fill the holes that might be left after +each reduction step. Now reduction usually takes place near the beginning of +the scrap sequence (assuming that the scrap sequence makes syntactical sense) +because that is where we are looking first, and we want to avoid shifting +down the whole remainder of the scrap sequence each time. Therefore the +sequence of scraps, which initially occupies the positions from |scrap_base| +to |scrap_ptr|, is allowed to have a hole in its middle, the low and high +end of which are pointed to by variables |lo_ptr| and~|hi_ptr|. There is +also a variable that points to the place where reductions are currently +taking place, which is the parsing pointer~|pp|. It will always point into +the area below the hole, and when it approaches the hole so closely that a +potential reduction might involve scraps from above, the situation is +remedied by sliding down scraps to the lower region, effectively raising the +hole. Therefore the scraps in the higher region are those that have never +been considered for a reduction yet. Eventually all scraps have been moved +down (i.e., we have |hi_ptr==scrap_ptr|), and after that has happened a +scrap with category~0 (which is not otherwise used) is copied down to signal +the imminent end of the reduction process. When finally no more rules +match the scraps in the lower region, the parsing stops. + +@< Global variables @>= +scrap_pointer pp; /* current position for reducing scraps */ +scrap_pointer lo_ptr; /* end of sequence of scraps that have been examined */ +scrap_pointer hi_ptr; /* first scrap that has not been examined */ + +@ The |mathness| is an attribute of scraps that says whether their +translation is to be processed in a math mode context or not. Since the +translation can be concatenated from a large number of other scraps, there +can be switches in and out of math inside the translation, and we need to +specify the mathness at each of the boundaries. For some scraps it either +makes no difference whether their translation is processed in math mode or +not, or the required mathness is to be determined by the grammatical context +rather than by the scrap itself. Such scraps have mathness |maybe_math| at +both ends; otherwise a definite mathness is specified at either end. The +least significant pair of bits of the |mathness| field of a scrap controls +the right boundary, and the pair of bits to its left controls the left +boundary. + +@d left_math(a) (a->mathness>>2) +@d right_math(a) (a->mathness&0x3) + +@ If we combine two scraps neither of which has mathness |maybe_math| at its +boundaries, then a `\.\$' is inserted in between if and only if the +mathnesses at the common boundary do not agree; if a scrap with |maybe_math| +joins one with a definite mathness, that mathness is propagated across the +former scrap to its other boundary. In order to implement this, we maintain +two mathness values while building up a text: |init_mathness| and +|cur_mathness| which represent the values at the left and right boundaries +of the part contributed so far; these are local variables of whatever +function is concatenating translations, but they should be called by these +names since they are addressed by the macros below. As a consequence of the +left-to-right order of combining translations, a |maybe_math| scrap that is +combined with scraps with definite mathnesses, will actually be set in the +mode inherited from its left (unless it appears as the leftmost scrap in a +reduction); this can be used to make certain symbols, such as colons, behave +in two slightly different ways depending on their syntactic function. (This +method is not infallible however, as a comment following the symbol will +always force it to be processed in horizontal mode; this happens because +|insert| scraps are tacked onto the scrap before them before any ordinary +reduction can affect it.) + +@ Before scraps requiring some definite mathness are contributed, we invoke +|set_mode(yes_math)| or |set_mode(no_math)| as appropriate; the first time +this happens will determine the value of |init_mathness|. + +@d set_mode(x) +if (cur_mathness==maybe_math) cur_mathness=init_mathness=x; +else if (cur_mathness!=x) {@; app('$'); cur_mathness=x; } +else @; /* expect semicolon */ + +@ The macro |app_trans| is invoked with a |scrap_pointer| as argument, +and appends its translation as a single token; |add_trans| will in addition +to this administrate |init_mathness| and |cur_mathness|, and interpolate +any necessary math shifts. + +@d app_trans(a) app_tok(text_flag+text_index((a)->trans)) +@d add_trans(a) +{ scrap_pointer scr=a; /* so that evaluating |a| may have side effects */ + if (left_math(scr)!=maybe_math) + { if (cur_mathness==maybe_math) init_mathness=left_math(scr); + else if (cur_mathness!=left_math(scr)) app('$'); + cur_mathness=right_math(scr); + } + app_trans(scr); +} + +@ The function call |fuse(s,n)| will concatenate the translations of |n| +scraps starting with |*s|, taking care of the mathnesses, and install the +resulting text into |s->trans|. + +@c +void fuse (scrap_pointer s, int n) +{ int cur_mathness=maybe_math, init_mathness=maybe_math; scrap_pointer p=s; + check_toks(n); check_text(); + do add_trans(p++)@; while (--n>0); /* gather all the translations */ + s->trans=text_ptr; freeze_text(); + s->mathness=(init_mathness<<2)+cur_mathness; +} + +@ An |int_like| identifier following a |struct_like| token, a selection +operator (`|.|' or `|->|'), or the special code \:;, is to be typeset as an +ordinary identifier. The function |make_nonreserved| alters the flag of the +token representing such an identifier occurrence. The scrap representing the +|int_like| identifier should not be formed by any reduction, but come +directly from |C_read|, so in principle we expect the translation of our +scrap to be an unnested text consisting of a single token. However, a +comment (or other |insert|) directly following the |int_like| identifier may +complicate this picture slightly, because |insert| scraps are tacked onto +the previous scrap before it gets the chance to take part in any reduction; +this means our token may be buried inside one or more levels of text +nesting, but still is the very first token of the translation. Any such +levels of nesting are soaked off by the |while| loop below; after this +process a single-token text should remain containing a reserved word, of +which we replace |res_flag| by |id_flag| to make it print as an ordinary +identifier. + +@c +void make_nonreserved (scrap_pointer p) +{ text_pointer q=p->trans; token t; + while (text_flag<=(t=text_begin(q)[0]) && t<inner_text_flag) + q=text_at(t-text_flag); + if (text_end(q)==text_begin(q)+1 && res_flag<=t && t<mod_flag) + text_begin(q)[0] = t-res_flag+id_flag; +} + +@ We wish to mark defining occurrences of identifiers, triggered by the +matching of certain reduction rules. Thanks to the unsurpassed +declaration syntax of~\Cee, it is a non-trivial task to locate the +identifier in question, since it may already have been wrapped up in a more +or less complicated declarator or function heading. Fortunately the +identifier is always the first one occurring in that expression +\unskip\footnote{${}^\dagger$} +{Due to the possible occurrence of the keywords |const| and |volatile| in + strange places within declarations, this is not entirely true. We will + arrange things however in such a way that unless we are forced otherwise by + enclosing parentheses, these keywords will remain outside the text that is + passed to |first_ident|. If we ignore the possibility of specifying + redundant parentheses, this only causes problems when one wishes to + declare certain peculiar things like constant function pointers. +}, so a left-to-right scan through the, possibly nested, text forming the +translation of the expression should reveal it. We accept either an ordinary +identifier or a reserved word; the latter possibility is needed for typedef +declarations. @^recursion@> + +@c +id_pointer first_ident(text_pointer p) +{ token_pointer q; token t; + if (p>=text_ptr) confusion("first_ident"); @.first\_ident@> + for (q=text_begin(p); q<text_end(p); ++q) + if (id_flag<=(t=*q) && t<mod_flag) return id_at(t%id_flag); + else if (text_flag<=t) /* text or inner text */ + {@; id_pointer r=first_ident(text_at(t%id_flag)); + if (r!=NULL) return r; + } + return NULL; +} + +@ In the following situations we want to mark the occurrence of an +identifier as a definition: in a declaration of a variable, as in +`|xref_pointer *r@;|', when a label is declared, as in `|found:|', in the +declaration of a function, as in `|char* f(int n){@t\dots@>@;}|', after a +|struct_like| token (declaring a structure-, union-, or enumeration tag) and +for all identifiers in an enumeration list. This is accomplished by the +invocation of |make_underlined| at appropriate times. + +@c +void make_underlined (scrap_pointer p) + /* underline entry for first identifier in |p->trans| */ +{ id_pointer name=first_ident(p->trans); /* name of first identifier */ + if (name==NULL) return; + /* this happens for unsyntactical things like `|int 3;|' */ + { sixteen_bits head=xref_index(name->xref),* r=&head; int n; + while ((n=xnum(*r)&num_mask)!=0 && n<section_count) r=&xlink(*r); + if (n==section_count) xnum(*r)|=def_flag; + else /* this may happen for one-letter identifiers */ + { make_xref(section_count+def_flag,*r); + if (r==&head) name->xref=xref_ptr; @+ else *r=xref_index(xref_ptr); + } + } +} + +@ We will now consider the how the reduction rules themselves are +represented and used. As we have seen, a rule must define a sequence of +categories for its left hand side, and for its right hand side a category +and a prescription for constructing its translation. In addition, some +categories of the left hand side may be marked as context, so that they will +not take part in the reduction, and there is a way to specify conditional +loading of rules. A few more pieces of information are included for +convenience and efficiency. + +Individual reduction rules are stored in a structure called |reduction|. +It is organised in a way that allows for semi-static initialisation, i.e., +the essential parts of information are stored near the beginning of the +structure or of one of its sub-structures, so that they can be defined by +an initialiser expression, while some further fields are computed from them +and assigned at startup time. Within the fields that are statically +initialised some fields that usually are~0 are put at the end, so that in +the default case they can be omitted from the initialiser. + +The field |id| holds an identification number for the rule, which is used in +debugging. Then follows the left hand side information, consisting of an +array of at most |max_lhs_length| categories (which include those of a +possible context; if less than the maximal number of categories are present +they are padded with zeros), followed by integers |context| and |length|. +The field |context| specifies which categories, if any, form the context: +this can be a sequence of one or more categories at either end of the left +hand side of the rule, but not at both ends. If |context==0| (as is the +case if no explicit initialiser is specified), there are no context +categories; when |context>0|, the first |context| categories form the left +context, and when |context<0|, the last |abs(context)| categories form the +right context. (In practice it is wise to use only token categories (ones +that do not require reduction to be formed) for a right context, unless one +can be quite sure that no unintended reduction will affect the categories +taking part in the intended reduction before the right context has been +reduced.) There must be at least one category that is not part of the +context, lest a ``reduction'' would increase the number of scraps. The right +hand side of a rule specifies a category and a string used as a format to +build up the translation; for the common case that the translation is formed +by concatenating the translations of all scraps of the left hand side (not +including those those of the context), a null pointer may be given instead +of a format string. The following field |mask| can be used to specify +selective loading of rule at startup time: any bit set in it will suppress +loading under some condition dependent on the setting of option flags in the +call of \.{CWEAVE}. The field |displacement| is computed at startup time to +record the number of positions (usually negative) by which the parsing +pointer~|pp| should be changed after application of the rule. + +@d max_lhs_length 4 + +@< Typedef and enumeration declarations @>= + +typedef struct +{ short id; /* for debugging */ + struct + {@; eight_bits category[max_lhs_length]; signed char context,length; } lhs; + struct {@; eight_bits category; char *translation; } rhs; + sixteen_bits mask; + short displacement; +} reduction; + +@ We shall organise the rules in a ``trie'' structure for fast matching. If +|q| points to a trie node reached after matching some sequence of +categories, and that sequence corresponds to the left hand side (including +context) of some rule (which should be unique), then |q->rule| points to +that rule, otherwise |q->rule==NULL|. If that sequence of categories is a +proper prefix of the left hand side of a rule (which may happen whether or +not |q->rule==NULL|), and $c$ is the next category in that left hand side +(which implies |0<c<=max_rule_cat|), then |q->next[c-1]| is the index of the +trie node reached after a further match of~$c$. The entries of |q->next| +that do not correspond to any such successor node are set to~0, which is +unambiguous because the root of the trie does not figure as a successor of +any node. We do not attempt a sparse representation (which would avoid +storage of such 0's), but we do use a compact |eight_bits| representation +for the entries of |q->next|; this saves a considerable amount of space, +since there is a total of |max_rule_cat*max_no_of_nodes| such entries. + +@<Typedef...@>= + +typedef struct {@; reduction* rule; eight_bits next[max_rule_cat]; } trie_node; + +@ Trie nodes are allocated from an array |trie_node_mem|, with the root of +the trie at |trie_node_mem[0]|. We introduce some macros that will help us +find our way around the trie. The address the successor of the trie node +pointed to by |q| for category |c| can be written as |successor(q,c)|. The +absence of such a successor can be found by testing |no_successor(q,c)|. If +|x| is the address of any node in the tree (except the root), then we can +make that node the successor of |q| for category |c| by invoking +|set_successor(q,c,x)|. + +@d trie_root (&trie_node_mem[0]) +@d successor(q,c) + (&trie_node_mem[(q)->next[(c)-1]]) /* category codes start at 1 */ +@d no_successor(q,c) ((q)->next[(c)-1]==0) +@d set_successor(q,c,x) ((q)->next[(c)-1]=(eight_bits)((x)-trie_node_mem)) + +@<Global...@>= +trie_node trie_node_mem[max_no_of_nodes]; +int node_no = 1; /* number of trie nodes allocated */ +#ifdef DEBUG +boolean install_failed=false; +#endif + +@ Trie nodes are allocated in a straightforward sequential way. We don't +trust that uninitialised statically allocated pointers will be |NULL| +(although they should), especially not on machines where |NULL| is not +represented as ``all bits cleared'', so we do a bit of extra work here. For +the entries of the array |next| we expect no problems however (since +|eight_bits| is an integral type), so we do not explicitly initialise them. + +@c trie_node *get_new_trie_node(void) +{ if (node_no>=max_no_of_nodes) + overflow("trie node"); @.trie node capacity exceeded@> + trie_node_mem[node_no].rule=NULL; + return &trie_node_mem[node_no++]; +} + +@ The function |install_rule| installs a reduction, and if \.{CWEAVE} was +compiled with |DEBUG| set, it also performs some checks on the validity of +the rule; if any check fails the variable |install_failed| is set to |true|. +Since |print| has a variable number of arguments, the macro |rule_error| +does not incorporate them but just prepends |print| to the argument list; +the replacement text of |rule_error| can therefore not be parenthesised, and +the macro should be used with some care. + +@d rule_error install_failed=true,print + /* report a problematic syntax rule */ + +@ A global variable |rule_mask| is set at startup time according to the +relevant option flags; any rules~|r| for which |rule_mask & r->mask!=0| are +suppressed. For each set of mutually exclusive settings, a number of bits in +|rule_mask| is reserved equal to the size of the set; the current setting +will have its corresponding bit set while the others are cleared. Therefore +the bits set in |r->mask| specify the option settings for which the rule is +disabled, and the default state |r->rule==0| means that the rule always +applies, regardless of any optional settings. + +@< Global... @>= sixteen_bits rule_mask; + +@ The function |install_rule| enters a rule into the trie structure, and if +|DEBUG| is defined, performs some sanity checks on the rule. + +@< Prototypes @>= +void install_rule (reduction* rule); + +@~The length of rules can be found because category~0 is not used for scraps. +@c +void install_rule(reduction *rule) +{ if ((rule_mask & rule->mask)==0) + { eight_bits* p=rule->lhs.category, i=0; + while (i<max_lhs_length && p[i]!=0) ++i; + rule->lhs.length=i; +#ifdef DEBUG + @< Check left-hand side of |rule| @> + @< Check right-hand side of |rule| @> +#endif + @< Install |rule| in the trie structure @> + @< Compute |rule->displacement|, + and modify |rule->lhs.length| and |rule->context| @> + } +} + +@ The left-hand side should not be only context, and all categories should +be legal ones. + +@< Check left... @>= +{ if (rule->lhs.length<=abs(rule->lhs.context)) + rule_error("\nNo scraps to replace in rule %d.\n", rule->id); + @.No scraps to replace...@> + for(i=0; i<rule->lhs.length; ++i) + if (!valid_cat(p[i])) + rule_error("\nUnknown category %d in LHS of rule %d.\n", p[i], rule->id); + @.Unknown category...@> +} + +@ The right-hand side should have a valid category, and unless its +translation is |NULL| (or |""| which we treat as if it were |NULL|), it +should contain as many times |'_'| as there are non-context categories in +the left-hand side. + +@< Check right... @>= +{ int c=rule->rhs.category; char* s=rule->rhs.translation; + if (!valid_cat(c)) + rule_error("\nUnknown category %d in RHS of rule %d.\n", c, rule->id); + @.Unknown category...@> + if (s!=NULL) + { if (*s=='\0') s=rule->rhs.translation=NULL; /* replace empty string */ + else + { i=0; + do + if (*s!='p') i+= *s++=='_'; /* count underscores */ + else if (++s,isdigit((eight_bits)*s)) ++s; /* skip digit and advance */ + else rule_error("\nDigit should follow 'p' in format of rule %d.\n" + @.Digit should follow 'p'...@> , rule->id); + while (*s!='\0'); + if (i!=rule->lhs.length-abs(rule->lhs.context)) + rule_error("\nCount of '_' not equal to length LHS in rule %d.\n" + @.Count of '\_' ...@> , rule->id); + } + } +} + +@ Since trie nodes are not represented sparsely, insertion is easy. + +@<Install |rule|...@>= +{ trie_node* q=trie_root; + for (i=0; i<rule->lhs.length; ++i) + { if (no_successor(q,p[i])) set_successor(q,p[i],get_new_trie_node()); + q=successor(q,p[i]); + } +#ifdef DEBUG + if (q->rule!=NULL) + rule_error("\nIdentical left-hand sides in rules %d and %d.\n" + @.Identical left-hand sides...@> , q->rule->id, rule->id); +#endif + q->rule=rule; +} + +@ We compute |displacement| conservatively, based on local considerations; +alternatively we might also consider the whole set of rules to find +larger (less negative) values that would make parsing go a bit faster. A +rule can have a left hand side of length |max_lhs_length|. This means that +it is safe to move |pp| so that it will afterwards be |max_lhs_length-1| +positions to the left of the first scrap that may have got a new category. + +After a rule has been installed, there is no need to record the full length +of the left hand side, including context, since this is implicit from the +place in the trie where the pointer to this rule is located; rather we store +the number of scraps that will be replaced. Similarly it is more useful to +know the offset of the first scrap to be replaced (which is~0 in case of a +right context) rather than the value of |context| as stored at +initialisation. + +@< Compute |rule->displacement|... @>= +{ int k=rule->lhs.context,d; + if (k<0) {@; rule->lhs.length+=k; k=0; } @+else rule->lhs.length-=k; + d=1-max_lhs_length+k; /* this cannot be positive */ + if (rule->lhs.category[k]==rule->rhs.category) /* no category change */ + { ++d; +#ifdef DEBUG + if (rule->lhs.length==1) + rule_error("\nNo categories change in rule %d.\n", rule->id); + @.No categories change...@> +#endif + } + rule->lhs.context=k; + rule->displacement=d; /* if positive, an error was reported */ +} + +@ The function |match| tests whether the category pointed to by~|p|, +and its successors, match the categories in the trie structure, starting +at the root of the trie, and up to a node that contains a rule. +Multiple matches are possible, in which case the longest one takes +precedence. We avoid using a side effect in the argument of |valid_cat|. + +@c +reduction *match (scrap_pointer p) +{ trie_node* q=trie_root; reduction* rule=NULL; int c; + while (c=p++->cat,valid_cat(c) && !no_successor(q,c)) + if ((q=successor(q,c))->rule!=NULL) rule=q->rule; + return rule; +} + +@ When a matching rule has been found, the function |reduce| is called to +perform the corresponding actions. At that point |pp| points to the first +scrap involved in the match, and the argument |rule| to |reduce| points to +the matching rule. + +If a rule has a left hand side of length~1 (not counting context) and also +the default translation (plain concatenation), then all that is to be done +is to change the category of a scrap, and part of the processing can be +skipped. + +@c +void reduce (reduction* rule) +{ int k=rule->lhs.context, l=rule->lhs.length; + scrap_pointer s = pp+k, p=s;/* position of the new scrap */ + char* f=rule->rhs.translation; /* format string for translation */ + + s->cat=rule->rhs.category; + + if (l>1 || f!=NULL) /* otherwise ready already */ + { if (f==NULL) fuse(s,l), p+=l; /* default translation */ + else @<Generate token list according to format string |f| @> + if (l>1) @<Fill vacant scrap positions@> + } + + @<Print a snapshot of the scrap list if debugging@> + @<Change |pp| to |max(pp+rule->displacement,scrap_base)| @> +} + +@ When we have applied a reduction to the sequence of scraps, we usually +remove scraps (we never create more scraps than we remove), thereby creating +a small ``hole'' in the sequence. We fix that hole by sliding scraps +downward across it, thereby moving the hole upwards, until it reaches the +``official'' hole at |lo_ptr|; then |lo_ptr| is adjusted so that the small +hole is incorporated in the official hole. During the translation process +the pointer~|p| was moved across all scraps that took part in the reduction, +so the scraps to move are at positions~|i| with |p<=i<lo_ptr|. + +@<Fill vacant...@>= +{ scrap_pointer q=s+1; /* position after the newly formed scrap */ + while (p<lo_ptr) *q++=*p++; + lo_ptr=q; +} + +@ Here we are careful not to create any pointers below |scrap_base|, +since they are not guaranteed to exist (but mentioning them in the module +name will do no harm). + +@<Change |pp| to...@>= +if (pp<scrap_base-rule->displacement) pp=scrap_base; @+ +else pp+=rule->displacement; + +@ We need no extensive coding mechanism for describing translations, since +they all follow a similar pattern. In all cases all the translations of the +scraps in the left hand side (not including the context scraps) are used +exactly once, in left to right order (violation of these principles would +result in very strange effects indeed for the printed output). The only +things that need to be added are formatting controls like |indent|, |force| +or |break_space|, math shifts (but these are already taken care of by +|add_trans|), and white space. We may also specify calls of +|make_underlined| or |make_unreserved| for certain scraps in the left hand +side. Although the items inserted are of a modest variety, one should +realise that their presence is the only reason we need to parse at all; +without them the translations could have been computed by purely lexical +means. + +In format strings an underscore indicates the translation of the next scrap +of the left hand side. Other characters each encode a formatting control; the +character `\.p' encodes |opt| and is followed by a digit that becomes its +argument. The characters `\.!' and `\.\$' respectively cause +|make_underlined| and |make_nonreserved| to be called for the next scrap; +`\.\ ' and~`\.\~' produce a space in the translation, where the latter is +non-breakable and the former forces horizontal mode. To just force +horizontal or math mode there are `\.h'~and~`\.m'; the latter avoids the +possibility of a completely empty formula by adding a space inside math +mode. The precise meaning of these and other formatting characters is easily +read off from the code below. The `\.o' and `\.r' format characters (the +latter is used only in compatibility mode) affect the math category used by +\TeX\ for the next symbol (character or control sequence); the syntax rules +using them do not put braces around that symbol, since these could also +capture a following comment, causing a \TeX\ error. + +The number of free tokens we require to be available by calling |check_toks| +is a conservative estimate, based on a hypothetic ``worst case'' reduction +rule with a left hand side of length~4 and with 8 additional items in its +translation (counting format codes with multi-character translations with +multiplicity), whose mathnesses alternate, so that a maximal number of math +shifts is required. + +@< Generate token list... @>= +{ int cur_mathness=maybe_math, init_mathness=maybe_math; + check_toks(23); check_text(); + do + switch (*f++) @:format@> + { case '+': app(indent); break; + case '-': app(outdent); break; + case 'p': app(opt); app(*f++); break; + case 'f': set_mode(no_math); app(force); break; + case 'F': set_mode(no_math); app(big_force); break; + case 'b': set_mode(no_math); app(backup); break; + case 'B': set_mode(no_math); app(break_space); break; + case 't': set_mode(yes_math); app_str("\\a"); break; @.\\a@> + /* next item in tab space */ + case ',': set_mode(yes_math); app_str("\\,"); break; @.\\,@> + /* thin space */ + case 'h': set_mode(no_math); break; /* force horizontal mode */ + case 'm': set_mode(yes_math); app(' '); break; + /* force math mode, avoid `\.{\$\$}' */ + case 'o': set_mode(yes_math); app_str("\\m"); break; @.\\m@> + /* make ``mathord'' */ + case 'r': set_mode(yes_math); app_str("\\MRL"); break; @.\\MRL@> + /* make ``mathrel'' */ + case '!': make_underlined(p); break; + case '$': make_nonreserved(p); break; + case ' ': set_mode(no_math); app(' '); break; + case '~': app('~'); break; + default: printf("%c: ",f[-1]); + confusion("illegal character in format string"); + case '_': add_trans(p++); + } + while (*f!='\0'); + s->trans=text_ptr; freeze_text(); + s->mathness=(init_mathness<<2)+cur_mathness; +} + +@ We have now seen how a match is made, and what is done once a matching +rule is found; we still have to consider how everything is set up properly, +and how rules are repeatedly applied until no more reduction is possible, +responding properly to successful and failing matches. All this is +performed by the function |translate|; as we have seen it is called by +|do_C| and |finish_C| after scraps have been stored from |scrap_base| +to |scrap_ptr| of the |scrap_info| array, and it returns a pointer to the +text representing the result of parsing all those scraps. + +We start with appending a dummy scrap if either no scraps are present at +all, or some tokens remain that have not been packed into a scrap yet; the +latter can only be due to `\.{@@t...@@>}' as a final item in compatibility +mode. Then we set |lo_ptr| and |hi_ptr| appropriately, and begin to apply +rules as long as possible. When this is done and more than one scrap +remains, their translations are wrapped rather bluntly together to a single +text. + +@c +text_pointer translate (void) /* converts a sequence of scraps */ +{ pp=lo_ptr=hi_ptr=scrap_base; + if (scrap_ptr==pp || dangling_tokens()) /* then append dummy scrap */ + {@; check_scrap(); pack_scrap(insert,no_math); } + @< If tracing, print an indication of where we are @> + @< Reduce the scraps using the rules until no more rules apply @> + @< Combine the irreducible scraps that remain, + and |return| the resulting text @> +} + +@ Before applying |match|, we must make sure it has good input (at least +|max_lhs_length| scraps). If a match at |pp| exists, |reduce| will perform +the required processing and updating of |pp| (in this case |pp| is +certainly not increased), if not, we move to the right and try again. + + +@< Reduce the scraps... @>= +do +{ reduction *rule; + @< Make sure the entries |pp| through |pp+max_lhs_length-1| + of |scrap_info| are defined, or that |lo_ptr->cat==0| @> + if ((rule=match(pp))!=NULL) reduce(rule); + else + {@; ++pp; + @< Take special action if |pp->cat| is |end_expr| or |rproc| @> + } +} +while (pp<lo_ptr); + +@ If we get to the end of the scrap list, we set |lo_ptr->cat=0|, which will +prevent any matches to scraps beyond those that are actually present. All +scraps of category |insert| pass across the hole between |lo_ptr| and +|hi_ptr|, and we take the opportunity to remove them, tacking their +translation to the scrap below the hole. We even make sure that +|hi_ptr->cat!=insert|, so that the scraps with which the |insert| scrap is +combined will not have undergone any ordinary reduction yet. The only +possible remaining |insert| scrap is one at the very start of the list; it +will be handled at the end of all reductions. + +@< Make sure the entries... @>= +{ scrap_pointer lo_min = pp+max_lhs_length; + while (lo_ptr<lo_min && lo_ptr->cat!=0) + if (hi_ptr>=scrap_ptr) lo_ptr->cat=0; + else + { *lo_ptr++ = *hi_ptr++; + while (hi_ptr<scrap_ptr && hi_ptr->cat==insert) + {@; *lo_ptr = *hi_ptr++; fuse(lo_ptr-1,2); } + } +} + +@ The category pairs |lproc|--|rproc| and |begin_expr|--|end_expr| are +special in that the don't occur in any rules, but rather serve only as +markers. When the material in between will not reduce any further the whole +construction will be wrapped up, their translations concatenated, and the +result treated as an |insert| respectively an |expression|. We do not +actually form an |insert| scrap if it is not at the start of the scrap +sequence, but rather combine everything directly with the scrap immediately +before the |lproc| scrap, leaving the category of that scrap as it is; this +is necessary, because the code that combines ordinary |insert| scraps with +their predecessor only looks at scraps when they cross the hole from |hi_ptr| +to~|lo_ptr|. We use here that |rproc-lproc==end_expr-begin_expr==1|. The +variable names |s| and |p| are chosen with the same meaning as in |reduce|, +so that we could reuse a module. + +@< Take special action... @>= +if (pp->cat==end_expr || pp->cat==rproc) +{ int start=pp->cat-1; /* the opening category matching |pp->cat| */ + scrap_pointer s=pp, p=pp+1; + while ((--s)->cat!=start && s>scrap_base) {} + if (s->cat==start) /* if opening symbol is missing, take no action */ + { if (start==begin_expr) s->cat=expression; + else if (s==scrap_base) s->cat=insert; @+ + else --s; /* position of new scrap */ + fuse(s,(int)(p-s)); + @< Fill vacant scrap positions @> /* using values of |p| and |s| */ + pp= s-scrap_base<max_lhs_length ? scrap_base : s+1-max_lhs_length; + } +} + +@ 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 surrounding the translations of scraps +where appropriate. Because of this last action we must build a new text +even if only one scrap remains. + +@< Combine the irreducible... @>= +{ scrap_pointer j; + if (scrap_base->cat==insert && lo_ptr>scrap_base+1) + { fuse(scrap_base,2); /* merge initial |insert| into the next scrap */ + j=scrap_base; j->cat=j[1].cat; --lo_ptr; while (++j<lo_ptr) *j=j[1]; + } + @<If semi-tracing, show the irreducible scraps@> + check_toks(1); + for (j=scrap_base; j<lo_ptr; j++) + { if (j!=scrap_base) app_char_tok(' '); + if (left_math(j)==yes_math) app('$'); + app_trans(j); + if (right_math(j)==yes_math) app('$'); + } + freeze_text(); + return text_ptr-1; +} + +@ If \.{CWEAVE} is compiled with |DEBUG| defined, it can be put into +debugging mode by setting |tracing| to a positive value by means of \:1, +\:2, or \:3. When |tracing| is set to level~1 any sequence of two or more +irreducible scraps remaining at the end of a call to |translate| will be +printed out. When |tracing| is set to level~2, the rule numbers and current +scrap categories will be printed out after each reduction that takes place, +and if it is set to level~3, then mathnesses at the boundaries of scraps +will also be indicated. + +@<Global...@>= +#ifdef DEBUG +int tracing; /* how much parsing details to show */ +#endif + +@ When parsing fails to reduce everything to a single scrap, pleasing results +will probably not be obtained; it is therefore advisable to run +\.{CWEAVE} with |tracing==trace1| before a final version of a |CWEB| +program is fixed. In order to allow this without changing the source file +itself, we initialise |tracing| to |trace1| if the flag `\.{+d}' is supplied +to \.{CWEAVE}. + +@< Set initial values @>= +#ifdef DEBUG +tracing = flags['d'] ? trace1 : trace0; +#endif + +@ The following code is activated by the `\.{+d}' flag or the \:1 control +code. + +@<If semi-tracing, show the irreducible scraps@>= +#ifdef DEBUG +{ if (tracing==trace1 && lo_ptr>=scrap_base+2) + { print("\nIrreducible scrap sequence at line %d in section %d:\n" + @.Irreducible scrap sequence...@> ,cur_line, section_count); + mark_harmless(); + for (j=scrap_base; j<lo_ptr-1; j++) + print_cat(j->cat), putchar(' '); + print_cat(j->cat); new_line(); /* |term_line_empty| is still valid */ + } +} +#endif + +@ When full tracing is enabled the following message indicates which piece +of \Cee~text is being parsed (but for section bodies it will generally show +the first line of the next section, since that has already been fetched). + +@<If tracing,...@>= +#ifdef DEBUG +{ if (tracing>=trace2) + { print("\nTracing after l.%d:\n", cur_line); @.Tracing after...@> + if (loc>buffer+50) /* shorten long lines to keep within margins */ + {@; printf("..."); term_write (loc-50,50); } + else term_write(buffer,loc-buffer); + new_line(); /* |term_line_empty| is still valid */ + } +} +#endif + +@ After each reduction, full tracing will print a line starting with the +rule number, followed by a display of all the categories of scraps which +have been considered until now, i.e., those at positions below |lo_ptr|. +The scrap that was produced by this reduction, which is pointed to by~|s|, +has its category highlighted by enclosing it in inverted angle brackets. +If tracing is set to~3, extra-full tracing is active, and mathnesses at the +boundaries of scraps are indicated. + +@d math_char(x) ((x)==yes_math ? '+' : (x)==no_math ? '-' : '?') + +@<Print a snapsh...@>= +#ifdef DEBUG +{ scrap_pointer k; /* pointer into |scrap_info| */ + if (tracing>=trace2) + { print("\n%3d:", rule->id); + for (k=scrap_base; k<lo_ptr; k++) + { putchar (' '); + if (tracing==trace3) putchar(math_char(left_math(k))); + if (k==s) putchar('>'), print_cat(k->cat), putchar('<'); + else print_cat(k->cat); + if (tracing==trace3) putchar(math_char(right_math(k))); + } + print("%s\n", hi_ptr<scrap_ptr ? " ..." : "."); + } +} +#endif + diff --git a/web/c_cpp/cwebx/rules.w b/web/c_cpp/cwebx/rules.w new file mode 100644 index 0000000000..d58144e99e --- /dev/null +++ b/web/c_cpp/cwebx/rules.w @@ -0,0 +1,701 @@ +@* Rules of the grammar. @:title@> +\def\:#1{`\.{@@#1}'}% for in case this file is processed in isolation +We first arrange the proper setting of |rule_mask|, which will control the +selection of rules actually used. Recall that any bits set in the mask of a +rule prescribe its {\it suppression\/} when the same bit is set in +|rule_mask|; therefore for instance the bit characterising \Cpp\ is called +|no_plus_plus|, so that rules specifying it will not be loaded for \Cpp. In +some cases two masks will be combined using the bitwise-or operator `|@v|', +this means (somewhat counterintuitively) that the rule will only be selected +if the conditions represented by the two masks are {\it both\/} satisfied. +The use of the bitwise-and operator `|&|' is even more exceptional: it is +only meaningful if its two operands both select one setting of the same +three-way switch; the rule will then be selected if that switch is in either +of the two indicated positions. The |merged_decls| flag is special in that +setting `\.{+m}' only enables an extra rule, but does no disable any rules; +therefore only one bit is used for this option, and raising this bit in +|rule_mask| suppresses the rule marked with |merged_decls|. + +@d cwebx 0x0001 /* use normally */ +@d compatibility 0x0002 /* use in compatibility mode */ +@d only_plus_plus 0x0004 /* use in \Cpp\ */ +@d no_plus_plus 0x0008 /* use in ordinary \Cee\ only */ +@d unaligned_braces 0x0050 /* use if `\.{+u}' flag was set */ +@d aligned_braces 0x0020 /* use unless `\.{+u}' flag was set */ +@d wide_braces 0x0030 /* use if `\.{+w}' set */ +@d standard_braces 0x0060 /* use unless `\.{+u}' or `\.{+w}' set */ +@d merged_decls 0x0080 /* use if `\.{+m}' set */ +@d forced_statements 0x0100 /* use if `\.{+a}' or `\.{+f}' set */ +@d no_forced_statements 0x0600 /* use unless `\.{+a}' or `\.{+f}' set */ +@d all_stats_forced 0x0300 /* use if `\.{+a}' set */ +@d not_all_stats_forced 0x0400 /* use unless `\.{+a}' set */ + +@< Set initial values @>= +rule_mask= (compatibility_mode ? 0x0001 : 0x0002) + | (C_plus_plus ? 0x0008 : 0x0004) + | (flags['w'] ? 0x0040 : flags['u'] ? 0x0020 : 0x0010) + | (flags['m'] ? 0x0000 : 0x0080) + | (flags['a'] ? 0x0400 : flags['f'] ? 0x0200 : 0x0100) + ; +{ static reduction rule[] = { @< Rules @>@;@; }; +@/int i=array_size(rule); @+ do install_rule(&rule[--i]); while (i>0); +#ifdef DEBUG + if (install_failed) fatal("inconsistent grammar",0); +#endif +} + +@ {\it Expressions}. @:rules@> +These rules should be obvious. Rule~5 allows typedef identifiers to be used +as field selectors in structures; rules 7~and~8 attach a parameter list in a +function call. In rule~14 we prefix a potentially binary operator such as +`|*|' that is used in a unary way by a `\.{\\mathord}' command to make sure +that \TeX\ will not mistake it for a binary operator. In simple cases such +as |*p| this is redundant, but if such operators are repeated more than one +level deep, as in |**p|, \TeX\ would otherwise treat the first operator as +the left operand of the second, and insert the wrong spacing. Moreover, +typical \Cee~constructions as a cast |(void*) &x| or a declaration +|char *p@;| would confuse \TeX\ even more. In rule~13 we need not insert +`\.{\\mathord}', since operators of category |unop| are already treated as +ordinary symbols by~\TeX. + +@< Rules @>= +{ 1, {{expression, unop}}, {expression, NULL}}, @/ +{ 2, {{expression, binop, expression}}, {expression, NULL}}, @/ +{ 3, {{expression, unorbinop, expression}}, {expression, NULL}}, @/ +{ 4, {{expression, select, expression}}, {expression, NULL}}, @/ +{ 5, {{expression, select, int_like}}, {expression, "__$_"}}, @/ +{ 6, {{expression, comma, expression}}, {expression, "__p1_"}}, @/ +{ 7, {{expression, expression}}, {expression, NULL}}, @/ +{ 8, {{expression, lpar, rpar}}, {expression, "__,_"}}, @/ +{ 9, {{expression, subscript}}, {expression, NULL}}, @/ +{10, {{lpar, expression, rpar}}, {expression, NULL}}, @/ +{11, {{lbrack, expression, rbrack}}, {subscript, NULL}}, @/ +{12, {{lbrack, rbrack}}, {subscript, "_,_"}}, @/ +{13, {{unop, expression}}, {expression, NULL}}, @/ +{14, {{unorbinop, expression}}, {expression, "o__"}}, @[@] + +@~Here are some less common kinds of formulae. Processing the colon belonging +to the question mark operator in math mode will give it the proper spacing, +which is different from that of a colon following a label. Rule~21 processes +casts, since the category |parameters|, which represents parenthesised lists +specifying function argument types, encompasses the case of a single +parenthesised type specification. The argument of |sizeof| may be a type +specification rather than an expression; in \Cee\ (unlike \Cpp) it then must +be parenthesised. %, but not in \Cpp\ (and |sizeof_like| might be `\&{new}'). + +@< Rules @>= +{20, {{question, expression, colon}}, {binop, "__m_"}}, @/ +{21, {{parameters, expression}}, {expression, "_,_"}}, @/ +{22, {{sizeof_like, parameters}}, {expression, NULL}}, @/ +{23, {{sizeof_like, expression}}, {expression, NULL}}, @/ +{24, {{sizeof_like, int_like}}, {expression,"_~_"},only_plus_plus}, @[@] + +@ {\it Declarations}. +In a declaration in \Cee, the identifier being declared is wrapped up in a +declarator, which looks like an expression of a restricted kind: only prefix +asterisk, postfix subscript and formal parameters, and parentheses are used. +In a bottom-up parser of the kind we are using, it is natural, and hardly +avoidable, that declarators are parsed as expressions. Therefore we start +recognising a declaration when we see a type specifier followed by the first +declarator; at that point we have a succession `|int_like| |expression| +|semi|' or `|int_like| |expression| |comma|' (rules 31~and~33). It is also +possible that there are no declarators at all, namely when a |struct|, +|union|, or~|enum| specifier is introduced without declaring any variables; +in that case we have `|int_like| |semi|' (rule~32). Because the type +specifier might be composite, like |unsigned long int|, and there might +moreover be storage class specifiers and type modifiers (like `|const|'), we +first contract any sequence of |int_like| items to a single one (rule~30). +In case the declarator was followed by a comma we reduce to |int_like|, so +that the next declarator can be matched, otherwise we reduce to +|declaration|. + +It is not quite true that declarators always look like expressions, since +the type modifiers `|const|' and `|volatile|' may penetrate into +declarators. When they do they will almost always be preceded by an +asterisk, and rule~34 will treat such cases. The choice for |int_like| as +the result category is not completely obvious, since it makes the modifier +and the preceding asterisk part of the type specifier rather than of the +declarator, which strictly speaking is not correct; the choice for |unop| or +|unorbinop| might therefore seem a more logical one. One reason for not +doing that is that a space would have to be inserted in the translation +after the modifier scrap, which would not look right in abstract declarators +for contrived cases like \hbox{|int f(char *const)@;|}; more importantly, if +the modifier would become part of the declarator, it would be a (reserved) +identifier that precedes the identifier actually being declared, and when +the declarator then receives a call from |make_underlined| by rule 31~or~33, +it would mislead |first_ident|. The current solution has a small flaw as +well, since it cannot handle the situation where the modifier is separated +from the type specifier by a parenthesis, as in +$\&{void}~(\m*\&{const}~\m*f)~(\&{int})$; such cases are quite uncommon, are +hard to handle by rules that will not spuriously match in other situations, +and even then they would still cause problems with |make_underlined|, so we +do not attempt to handle them. + +@< Rules @>= +{30, {{int_like, int_like}}, {int_like, "_~_"}}, @/ +{31, {{int_like, expression, semi}}, {declaration, "_~!__"}}, @/ +{32, {{int_like, semi}}, {declaration, NULL}}, @/ +{33, {{int_like, expression, comma}}, {int_like, "_~!__p1"}}, @/ +{34, {{unorbinop, int_like}}, {int_like, "o__"}}, @[@] + +@ If a typedef identifier is simultaneously used as a field selector in a +|struct| or |union| declaration, it must be made to parse as expression and +be printed in italic type; this can be achieved by placing the magic wand +\:; before the identifier, by rule~35. The reason that we place \:; at the +beginning rather than at the end of the construction here, is to prevent the +|int_like| identifier from combining with something before it first. +Rule~35 only applies if the \:; does not match by any rule with what comes +before it. + +Rule~36 handles the case that a function is declared with specified argument +types, which is not handled by the expression syntax given until now. It +also parses new-style (\caps{ANSI/ISO}) headings of function definitions; in +that case, the resulting |function_head| will not be incorporated into a +|declaration| (unless a comma or semicolon follows) but rather into a +|function|. If the parameter specifications include identifiers (as in the +case of function headings), the arguments look like declarations without the +final semicolon; rule~37 (with aid of rule~33) constructs such parameter +lists. Parameter specifications using abstract declarators (without +identifiers) will be treated below. In |struct| declarations we may +encounter bit-field specifications with or without an identifier; these are +handled by rules 38~and~39 (the constant expression following the colon will +later receive a spurious call from |make_underlined|, but in case of numeric +constants this does no harm). + +@< Rules @>= +{35, {{magic, int_like}}, {expression, "_$_"}}, @/ +{36, {{expression, parameters}}, {function_head, "_B_"}}, @/ +{37, {{lpar, int_like, expression, rpar}}, {parameters, "_+++_~!_---_"}},@/ +{38, {{int_like, expression, colon}}, {int_like, "_~!_m_"}}, @/ +{39, {{int_like, colon}}, {int_like, "_m_"}}, @[@] + +@ Abstract declarators are used after type specifiers in casts and for +specifying the argument types in declarations of functions or function +pointers. They are like ordinary declarators, except that the defined +identifier has been ``abstracted''; an example is `|**(* )(int)|' in `|void +g(char**(* )(int))@;|', which tells that |g| takes as argument a pointer to +a function with |int| parameter yielding a pointer to pointer to |char|. A +difficulty with abstract declarators is that they are built up around the +vacuum left by abstracting the identifier, and since for more than one +reason we cannot allow rules with empty left hand side, we have to find an +alternative way to get them started. + +The natural solution to this problem is to look for sequences that can only +occur if an identifier has been abstracted from between them, for instance +`\.{*)}' (in categories: |unorbinop| |rpar|). The most compelling reason why +in |C_read| we had to laboriously change the category of a |type_defined| +identifier to |expression| instead of |int_like| inside its defining typedef +declaration, is that it allows us to ensure that any remaining |int_like| +scrap that is followed by a |subscript| is a sure sign of an abstract +declarator. + +Here are the cases that start off abstract declarators (these are the first +examples of rules that need context categories in their left hand side). As +a visual hint to the reader we leave a little bit of white space on the spot +where the identifier has vanished. Rules 40~and~41 handle declarators for +pointer arguments, where the vanished identifier is preceded by an asterisk, +which either stands at the end of the declarator, or is parenthesised (for +function pointer arguments). In these rules there is no need to prefix the +asterisk with `\.{\\mathord}', since the right context makes an +interpretation as binary operator impossible. Rules 42~and~43 treat +declarators for arrays, possibly of pointers; there are no corresponding +rules with |parameters| instead of |subscript| since abstract declarators +never specify functions themselves, only function pointers. In fact the +``function analogue'' of rule~43 would incorrectly match a cast following an +operator like `|*|' or `|-|'. Rule~44 treats an abstract declarator +consisting of subscripts only, which are redundantly parenthesised; here too +the corresponding pattern with |parameters| is not only never needed, it +would also spuriously trigger on parenthesised expressions that start with a +cast. + +@< Rules @>= +{40, {{unorbinop, rpar}, -1}, {declarator, "_,"}}, @/ +{41, {{unorbinop, comma},-1}, {declarator, "_,"}}, @/ +{42, {{int_like, subscript},1}, {declarator, ",_"}}, @/ +{43, {{unorbinop, subscript},1}, {declarator, ",_"}}, @/ +{44, {{lpar, subscript},1}, {declarator, ",_"}}, @[@] + +@~ Abstract declarators may grow just like ordinary declarators, to include +prefixed asterisks, as well as postfixed subscripts and parameters, and +grouping parentheses. + +@< Rules @>= +{45, {{unorbinop, declarator}}, {declarator, "o__"}}, @/ +{46, {{declarator, subscript}}, {declarator, NULL}}, @/ +{47, {{declarator, parameters}}, {declarator, NULL}}, @/ +{48, {{lpar, declarator, rpar}}, {declarator, NULL}}, @[@] + +@~ Here is how abstract declarators are assembled into |parameters|, keeping +in mind that the ``abstract declarator'' might be completely empty (i.e., +absent) as in `|void f(int);|' (rules 51~and~53). We put no space after the +type specifier here, since it is followed either by an abstract declarator, +a right parenthesis or comma, so certainly not by an identifier; therefore a +space is neither necessary, nor would it improve readability. The +\caps{ANSI/ISO} syntax allows empty parentheses as a parameter specification +in abstract declarators, although this is an old-style form; rule~54 has +been included to handle this case. Fortunately a parenthesised list of +identifiers (which would parse as |expression|) is not allowed as parameter +specification. + +@< Rules @>= +{50, {{lpar, int_like, declarator, comma}}, {lpar, "____p5"}}, @/ +{51, {{lpar, int_like, comma}}, {lpar, "___p5"}}, @/ +{52, {{lpar, int_like, declarator, rpar}}, {parameters, NULL}}, @/ +{53, {{lpar, int_like, rpar}}, {parameters, NULL}}, @/ +{54, {{lpar, rpar}}, {parameters, "_,_"}}, @[@] + +@ {\it Structure, union, and enumeration specifiers}. It is permissible to +use typedef identifiers as structure, union, or enumeration tags as well, so +we include cases where an |int_like| follows a |struct_like| token. In \Cpp, +we may also find things like `\&{private}:' in a class specifier; these are +parsed just like `|default:|', i.e., as a |label| (rule~66). + +@< Rules @>= +{60, {{struct_like, lbrace}}, {struct_head, "_ft_"},standard_braces}, @/ +{60, {{struct_like, lbrace}}, {struct_head, "_~_"},unaligned_braces}, @/ +{60, {{struct_like, lbrace}}, {struct_head, "_f_"},wide_braces}, @/ +{61, {{struct_like, expression, lbrace}}, + {struct_head, "_~!_ft_"},standard_braces}, @/ +{61, {{struct_like, expression, lbrace}}, + {struct_head, "_~!_~_"},unaligned_braces}, @/ +{61, {{struct_like, expression, lbrace}}, + {struct_head, "_~!_f_"},wide_braces}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!$_ft_"},standard_braces|no_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!_ft_"},standard_braces|only_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!$_~_"},unaligned_braces|no_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!_~_"},unaligned_braces|only_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!$_f_"},wide_braces|no_plus_plus}, @/ +{62, {{struct_like, int_like, lbrace}}, + {struct_head, "_~!_f_"},wide_braces|only_plus_plus}, @/ +{63, {{struct_like, expression}}, {int_like, "_~_"}}, @/ +{64, {{struct_like, int_like}}, {int_like, "_~$_"},no_plus_plus}, @/ +{64, {{struct_like, int_like}}, {int_like, "_~_"},only_plus_plus}, @/ +{65, {{struct_head, declaration, rbrace}}, + {int_like, "_+_-f_"},standard_braces}, @/ +{65, {{struct_head, declaration, rbrace}}, + {int_like, "_+f_-f_"},unaligned_braces & wide_braces}, @/ +{66, {{label, declaration}}, {declaration, "b_f_"},only_plus_plus}, @[@] + +@ Rules 67--70 are for enumerations; they avoid forced line breaks and call +|make_underlined| for all the enumeration constants. + +@< Rules @>= +{67, {{struct_like, lbrace, expression},-1}, {struct_head, "_B_"}}, @/ +{68, {{struct_like, expression, lbrace, expression},-1}, + {struct_head, "_~_B_"}}, @/ +{69, {{struct_head, expression, comma, expression},1}, + {expression, "__B!_"}}, @/ +{70, {{struct_head, expression, rbrace}}, {int_like, "_~+!_-B_"}}, @[@] + +@ The following rules are added to allow short structure and union +specifiers to be kept on one line without having to repeatedly specify \:+. +The idea is to place \:; after the left brace; this will cause the rules +below to be invoked instead of those above, which avoids introducing forced +line breaks. + +@< Rules @>= +{71, {{struct_like, lbrace, magic}}, {short_struct_head, "_B__+"}}, @/ +{72, {{struct_like, expression, lbrace, magic}}, + {short_struct_head, "_~!_B__+"}}, @/ +{73, {{struct_like, int_like, lbrace, magic}}, + {short_struct_head, "_~!$_B__+"}, no_plus_plus}, @/ +{73, {{struct_like, int_like, lbrace, magic}}, + {short_struct_head, "_~!_B__+"}, only_plus_plus}, @/ +{74, {{short_struct_head, declaration}}, {short_struct_head, "_B_"}}, @/ +{75, {{short_struct_head, rbrace}}, {int_like, "_-B_"}}, @[@] + + +@ {\it Statements}. +Rule~80 gives the usual way statements are formed, while rule~81 handles the +anomalous case of an empty statement. Its use can always be avoided by using +an empty pair of braces instead, which much more visibly indicates the +absence of a statement (e.g., an empty loop body); when the empty statement +is used however, it will either be preceded by a space or start a new line +(like any other statement), so there is always some distinction between a +|while| loop with empty body and the |while| that ends a |do|~statement. A +rule like this with left hand side of length~1 makes the corresponding +category (viz.~|semi|) ``unstable'', and can only be useful for categories +that usually are scooped up (mostly from the left) by a longer rule. Rules +82--84 make labels (ordinary, case and default), and rules 85~and~86 attach +the labels to statements. Rule~87 makes \:; behave like an invisible +semicolon when it does not match any of the rules designed for it, for +instance if it follows an expression. + +@< Rules @>= +{80, {{expression, semi}}, {statement, NULL}}, @/ +{81, {{semi}}, {statement, NULL}}, @/ +{82, {{expression, colon}}, {label, "!_h_"}}, @/ +{83, {{case_like, expression, colon}}, {label, "_ _h_"}}, @/ +{84, {{case_like, colon}}, {label, "_h_"}}, @/ +{85, {{label, label}}, {label, "_B_"}}, @/ +{86, {{label, statement}}, {statement, "b_B_"},not_all_stats_forced}, @/ +{86, {{label, statement}}, {statement, "b_f_"},all_stats_forced}, @/ +{87, {{magic}}, {semi, NULL}}, @[@] + +@ The following rules format compound statements and aggregate initialisers. +Rules 90--94 combine declarations and statements within compound statements. +A newline is forced between declarations by rule~90, unless the declarations +are local (preceded by a left brace) and `\.{+m}' was specified (rule~91); +this rule does not apply to structure specifiers, because the left brace +will already have been captured in a |struct_head| before the rule can match. +If `\.{+f}'~or~`\.{+a}' was specified, then a newline is forced between +statements as well (rule~93). Between the declarations and statements some +extra white space appears in ordinary \Cee\ (rule~92), but not in \Cpp, where +declarations and statements may be arbitrarily mixed (rule~94). Rules +95--97 then build compound statements, where the last case is the unusual +one where a compound statement ends with a declaration; empty compound +statements are made into simple statements so that they will look better +when used in a loop statement or after a label. If compound statements are +not engulfed by a conditional or loop statement (see below) then they decay +to ordinary statements by rule~98. Rules 99~and~100 reduce aggregate +initialiser expressions, where the reduction of comma-separated lists of +expressions is already handled by the expression syntax. + +@< Rules @>= +{90, {{declaration, declaration}}, {declaration, "_f_"}}, @/ +{91, {{lbrace, declaration, declaration},1}, + {declaration, "_B_"},merged_decls}, @/ +{92, {{declaration, statement}}, {statement, "_F_"},no_plus_plus}, @/ +{92, {{declaration, statement}}, {statement, "_f_"},only_plus_plus}, @/ +{93, {{statement, statement}}, {statement, "_f_"},forced_statements}, @/ +{93, {{statement, statement}}, {statement, "_B_"},no_forced_statements},@/ +{94, {{statement, declaration}}, {declaration, "_f_"},only_plus_plus}, @/ +{95, {{lbrace, rbrace}}, {statement, "_,_"}}, @/ +{96, {{lbrace, statement, rbrace}}, + {compound_statement, "ft_+_-f_"},standard_braces}, @/ +{96, {{lbrace, statement, rbrace}}, + {compound_statement, "_+f_-f_"},unaligned_braces}, @/ +{96, {{lbrace, statement, rbrace}}, + {compound_statement, "f_+f_-f_"},wide_braces}, @/ +{97, {{lbrace, declaration, rbrace}}, + {compound_statement, "ft_+_-f_"},standard_braces}, @/ +{97, {{lbrace, declaration, rbrace}}, + {compound_statement, "_+f_-f_"},unaligned_braces}, @/ +{97, {{lbrace, declaration, rbrace}}, + {compound_statement, "f_+f_-f_"},wide_braces}, @/ +{98, {{compound_statement}}, {statement, "f_f"}}, @/ +{99, {{lbrace, expression, comma, rbrace}}, {expression, "_,__,_"}},@/ +{100, {{lbrace, expression, rbrace}}, {expression, "_,_,_"}}, @[@] + +@ Like for structure and union specifiers, we allow compound statements to +be kept on one line by inserting \:; after the left brace. Such statements +will reduce to |statement| rather that to |compound_statement|, so that they +will be treated as if they were simple statements. + +@< Rules @>= +{101, {{lbrace, magic}}, {short_lbrace, "__+"}}, @/ +{102, {{short_lbrace, declaration}}, {short_lbrace, "_B_"}}, @/ +{103, {{short_lbrace, statement}}, {short_lbrace, "_B_"}}, @/ +{104, {{short_lbrace, rbrace}}, {statement, "_-B_"}}, @[@] + +@ {\it Selection, iteration and jump statements}. +There are three intermediate categories involved in the recognition of +conditional statements. The category |if_like| stands for `|if|' or an +initial segment of a repeated if-clause, up to and including `|else|~|if|'. +An |if_head| is an |if_like| followed by its (parenthesised) condition +(rules 110~and~111). If the statement following the condition is followed by +`|else|~|if|', the whole construct reduces to |if_like| (so that the +indentation will not increase after the second condition, rules 112~and~113), +otherwise, if only `|else|' follows, reduction is to an |if_else_head| +(rules 114~and~115), and finally, if no |else| follows at all, we reduce with +only the if-branch to |statement| (rules 116~and~117). The reduction rules for +|if_else_head| differ from those for |if_head| in that it will not combine +with an |else|, even if it is present; the formatting is identical to that +of an |else|-less |if_head| (rules 118~and~119). (It might be tempting to +replace rules 116~and~117 by a reduction from |if_head| to |if_else_head| to +be applied if no matching `|else|' is found, but that would require some +subtle measures to prevent this decay at times when the right context is +insufficiently reduced to decide whether an `|else|' is present or not.) The +formatting of the if and else branches depends on whether they are compound +statements or some other kind of statement (possibly another conditional +statement), and on the flags for statement forcing and brace alignment. + +@< Rules @>= +{110, {{if_like, expression}}, {if_head, "f_~_"}}, @/ +{111, {{lbrace,if_like,expression},1}, {if_head, "_~_"},standard_braces},@/ +{112, {{if_head, compound_statement, else_like, if_like}}, + {if_like, "__f_~_"},aligned_braces}, @/ +{112, {{if_head, compound_statement, else_like, if_like}}, + {if_like, "_~_~_~_"},unaligned_braces}, @/ +{113, {{if_head, statement, else_like, if_like}}, + {if_like, "_+B_-f_~_"},not_all_stats_forced}, @/ +{113, {{if_head, statement, else_like, if_like}}, + {if_like, "_+f_-f_~_"},all_stats_forced}, @/ +{114, {{if_head, compound_statement, else_like}}, + {if_else_head, "__f_"},aligned_braces}, @/ +{114, {{if_head, compound_statement, else_like}}, + {if_else_head, "_~_~_"},unaligned_braces}, @/ +{115, {{if_head, statement, else_like}}, + {if_else_head, "_+B_-f_"},not_all_stats_forced},@/ +{115, {{if_head, statement, else_like}}, + {if_else_head, "_+f_-f_"},all_stats_forced}, @/ +{116, {{if_head, compound_statement}}, + {statement, "__f"},aligned_braces}, @/ +{116, {{if_head, compound_statement}}, + {statement, "_~_f"},unaligned_braces}, @/ +{117, {{if_head, statement}}, + {statement, "_+B_-f"},not_all_stats_forced}, @/ +{117, {{if_head, statement}}, + {statement, "_+f_-f"},all_stats_forced}, @/ +{118, {{if_else_head, compound_statement}}, + {statement, "__f"},aligned_braces}, @/ +{118, {{if_else_head, compound_statement}}, + {statement, "_~_f"},unaligned_braces}, @/ +{119, {{if_else_head, statement}}, + {statement, "_+B_-f"},not_all_stats_forced}, @/ +{119, {{if_else_head, statement}}, + {statement, "_+f_-f"},all_stats_forced}, @[@] + +@ The following rules prevent forced line breaks from conditional statements +that occur within a one-line compound statement. + +@< Rules @>= +{120, {{short_lbrace, if_like, expression},1}, + {if_head, "_~_"}}, @/ +{121, {{short_lbrace, if_head, statement, else_like}}, + {short_lbrace, "_B_B_B_"}}, @/ +{122, {{short_lbrace, if_head, statement}}, + {short_lbrace, "_B_B_"}}, @[@] + +@ Switch and loop statements make use of the syntax for conditionals by +reducing to |if_else_head| which will take one further statement and indent +it (rules 130~and~131). Recall that `|for|' and `|switch|' are both +|while_like|; the parenthesised object following `|for|' looks like nothing +we have seen before, however, so we need extra rules to come to terms with +it (rules 132--134). Rule~132 is needed to avoid a line break when these are +normally inserted between statements, and rule~134 is needed in case the +third expression is empty. The |do|-|while| loops have to be treated +separately. Because we want to distinguish the case of a +|compound_statement| as loop body from other kinds of statements, we cannot +wait until the |while| combines with the loop control condition to an +|if_else_head|, since by then a |compound_statement| will have decayed to +|statement|. Hence we pick up the unreduced `|while|' token and form a new +category |do_head| (rules 135~and~136); in case of a compound statement the +`|while|' will be on the same line as the closing brace. Rules 137~and~138 +then combine this with the condition and the ridiculous mandatory semicolon +at the end to form a |statement|. + +@< Rules @>= +{130, {{while_like, expression}}, {if_else_head, "f_~_"}}, @/ +{131, {{lbrace, while_like, expression},1}, + {if_else_head, "_~_"},standard_braces}, @/ +{132, {{lpar, statement, statement}, 1}, + {statement, "_B_"}, forced_statements}, @/ +{133, {{lpar, statement, expression, rpar}}, {expression, "__B__"}}, @/ +{134, {{lpar, statement, rpar}}, {expression, NULL}}, @/ +{135, {{do_like, compound_statement, while_like}}, + {do_head, "__~_"},standard_braces}, @/ +{135, {{do_like, compound_statement, while_like}}, + {do_head, "_~_~_"},unaligned_braces}, @/ +{135, {{do_like, compound_statement, while_like}}, + {do_head, "__f_"},wide_braces}, @/ +{136, {{do_like, statement, while_like}}, + {do_head, "_+B_-B_"},not_all_stats_forced}, @/ +{136, {{do_like, statement, while_like}}, + {do_head, "_+f_-f_"},all_stats_forced}, @/ +{137, {{do_head, expression, semi}}, {statement, "f_~__f"}}, @/ +{138, {{lbrace, do_head, expression, semi},1}, {statement, "_~__f"}}, @[@] + +@ The following rules prevent forced line breaks from loop statements +that occur within a one-line compound statement. Since no special layout is +required between the heading of a |while| loop and its body, rule~139 +incorporates the heading as if it were a separate statement. For a +|do|-|while| loop we must take a bit more effort to get the spacing +following the |while| correct. + +@< Rules @>= +{139, {{short_lbrace, while_like, expression}}, + {short_lbrace, "_B_~_"}}, @/ +{140, {{short_lbrace, do_like, statement, while_like},1}, + {do_head, "_B_B_"}}, @/ +{141, {{short_lbrace, do_head, expression, semi}}, + {short_lbrace, "_B_~__"}}, @[@] + +@ The tokens `|goto|', `|continue|', `|break|', and `|return|' are all +|return_like|; although what may follow them is not the same in all cases, +the following two rules cover all legal uses. Note that rule~146 does not +wait for a semicolon to come along; this may lead to a premature match as in +`|return a+b;|', but this does not affect formatting, while the rule allows +saying things like `|return home|' in a module name (or elsewhere) without +risking irreducible scraps. + +@< Rules @>= +{145, {{return_like, semi}}, {statement, NULL}}, @/ +{146, {{return_like, expression}}, {expression, "_~_"}}, @[@] + +@ {\it Function definitions and external declarations}. +Apart from the initial specification of the result type (which is optional, +defaulting to |int|), a new-style function heading will parse as an +|function_head| (see the declaration syntax above), while an old-style function +heading is an |expression| possibly followed by a |declaration| (specifying +the function parameters). Rules 150--152 parse these two kinds of +function headings together with the function body, yielding category +|function|; rule~153 attaches the optional result type specifier. Although +the \Cee~syntax requires that the function body is a compound statement, we +allow it to be a |statement| (to which |compound_statement| will decay), for +in case a very short function body is specified using `\.{\{@@;}'. + +At the outer level declarations and functions can be mixed; when they do a bit +of white space surrounds the functions (rules 154--156). The combination of +several declarations is already taken care of by the syntax for compound +statements; no extra white space is involved there. Rules 157--159 take care +of function declarations that are not definitions (i.e., there is no function +body); if followed by a semicolon, a comma or a right parenthesis, the +|function_head| decays to an |expression|, and the rest of the syntax will +take care of recognising a |declaration| or |parameters|. Rules 153~and~157 +will be replaced in~\Cpp, for reasons explained below (incidentally, this is +the reason the category |function_head| was introduced; it used to be simply +|expression|). + +@< Rules @>= +{150, {{function_head, statement}}, {function, "!_f_"}}, @/ +{151, {{expression, statement}}, {function, "!_f_"}}, @/ +{152, {{expression, declaration, statement}}, + {function, "!_++f_--f_"}}, @/ +{153, {{int_like, function}}, {function, "_ _"}}, @/ +{154, {{declaration, function}}, {function, "_F_"}}, @/ +{155, {{function, declaration}}, {declaration, "_F_"}}, @/ +{156, {{function, function}}, {function, "_F_"}}, @/ +{157, {{function_head, semi},-1}, {expression, NULL},no_plus_plus}, @/ +{158, {{function_head, comma},-1}, {expression, NULL}}, @/ +{159, {{function_head, rpar},-1}, {expression, NULL}}, @[@] + +@ {\it Module names}. +Although module names nearly always stand for statements, they can be made +to stand for a declaration by appending \:;, or for an expression by +appending `\.{@@;@@;}'. The latter possibility is most likely to be useful +if the module stands for (part of) an initialiser list. A module name can +also be made into an expression by enclosing it in \:[ and~\:], but in that +case rule~160 will apply first, placing a forced break after the module +name. Rules 161, 164,~and~165 prevent a module name from generating forced +breaks if it occurs on a one-line compound statement or structure or union +specifier, while rules 167~and~168 serve to prevent rules 163~and~164 from +matching with priority over rule~166. The rules given here will be replaced by +other ones in compatibility mode. + +@< Rules @>= +{160, {{mod_scrap}}, {statement, "_f"},cwebx}, @/ +{161, {{short_lbrace, mod_scrap},1}, {statement, NULL},cwebx}, @/ +{162, {{mod_scrap, magic}}, {declaration, "f__f"},cwebx}, @/ +{163, {{lbrace, mod_scrap, magic},1}, + {declaration, "__f"},cwebx|standard_braces}, @/ +{164, {{short_lbrace, mod_scrap, magic},1}, {declaration, NULL},cwebx}, @/ +{165, {{short_struct_head, mod_scrap, magic},1}, + {declaration,NULL},cwebx}, @/ +{166, {{mod_scrap, magic, magic}}, {expression, NULL},cwebx}, @/ +{167, {{lbrace, mod_scrap, magic, magic},1}, + {expression, NULL},cwebx|standard_braces}, @/ +{168, {{short_lbrace, mod_scrap, magic, magic},1}, + {expression, NULL},cwebx}, @[@] + +@ {\it Additional rules for compatibilty mode}. +@^Levy/Knuth \.{CWEB}@> +Although our grammar differs completely from the one used in \LKC., we use +most of it also in compatibility mode (the exception is formed by the rules +concerning module names). We do add a few rules in compatibility mode, mostly +do deal with circumstances that are different for some reason or other. + +We start with module names, which behave in a completely different way. In +compatibility mode, as in \LKC., a module name normally stands for an +expression (rule~164) and in practice is almost always followed by a visible +or invisible (|magic|) semicolon. Rules 160~and~161 treat these cases +explicitly, in order to insert a forced break after the semicolon; rule~161 +for the case of an invisible semicolon is needed because if we would wait for +the |magic| semicolon to decay to an ordinary one, it might instead combine +with an |int_like| token following it. Rules 162~and~163 are provided to allow +the short form of compound statements even in compatibility mode (even though +it is not present in \LKC.): they preempt rules 160~and~161, avoiding the +forced break. Since in compatibility mode one has no means of indicating that +a module name stands for a set of declarations, we add rule~165 to allow them +nevertheless to be used before a function definition. + +Rules 170~and~171 compensate for the fact that compound assignment operators +like `|+=|' are scanned as two tokens in compatibility mode (see +section@#truly stupid@> for an explanation why this is done). +Rule~172 allows types to be used in the argument lists of macros, without +enclosing them between \:[~and~\:], in compatibility mode; this is done +frequently in the Stanford GraphBase. @^Stanford GraphBase@> It is sufficient +to remove expressions from the beginning of the argument list, since types, +and more generally types followed by declarators, are already removed by the +standard rules for |parameters|. As a result the argument list will either +reduce to an |expression| or to |parameters|, depending on whether the final +item was an expression. In both cases it will combine with the macro name to +an |expression|, although the spacing will be a bit too wide in the +|parameters| case. But then, one ought to use \:[~and~\:] anyway, which avoids +this problem. + +@< Rules @>= +{160, {{mod_scrap, semi}}, {statement, "__f"},compatibility}, @/ +{161, {{mod_scrap, magic}}, {statement, "__f"},compatibility}, @/ +{162, {{short_lbrace, mod_scrap, semi},1}, + {statement, NULL},compatibility}, @/ +{163, {{short_lbrace, mod_scrap, magic},1}, + {statement, NULL},compatibility}, @/ +{164, {{mod_scrap}}, {expression, NULL},compatibility}, @/ +{165, {{statement, function}}, {function, "_F_"},compatibility}, @/ +@) +{170, {{binop, binop}}, {binop,"r__"},compatibility}, @/ +{171, {{unorbinop, binop}}, {binop,"r__"},compatibility}, @/ +{172, {{lpar, expression, comma}}, {lpar, "___p1"}, compatibility}, @[@] +@[@] + +@ {\it Additional rules for \Cpp}. +Up to this point we have included some specific rules for \Cpp, in places +where a slight deviation from the \Cee~syntax was required. There are however +a large number of syntactic possibilities of \Cpp\ that are not even remotely +similar to those of~\Cee, so it is most convenient to collect them in a +separate section. The author of \.{CWEBx} wishes to make it clear that he is +quite aware of the incompleteness of the set of rules specified below, and +that he assumes no responsibility for correcting this. One reason for this is +that he has no readable formal grammar of \Cpp, which possibly could be used +for validation (nor does he use \Cpp\ himself), another is that the pieces +of grammar that he has seen show so little coherence that he seriously doubts +whether it is possible at all to parse \Cpp\ reliably with a grammar of the +type implemented here. In fact, the rules here were merely added in an +attempt to cope with problems reported by users. + +We start with rules for `\&{operator}', which are simple: it should combine +with a following operator symbol of any type to form an expression (rules +180--182). Then rules 183--186 take care of the `::'~operator: either a class +name or nothing is expected at the left, and either an ordinary or class +identifier at the right; the resulting category is that of the right hand +side. Type identifiers may appear as the left hand side of an assignment +within a list of formal parameters, indicating a default argument; in this +case the while assignment should behave as a type identifier (rule~187). + +Next we give rules catering with constructor declarations in class +definitions. First of all we must recognise the fact that the class name is +being used as a function name here; the simplest solution is to recognise the +combination of an |int_like| followed by a (possibly empty) parameter list +(rules 190~and~191). We cannot let a |function_head| (possibly created by the +rules kust mentioned) decay to an |expression| when followed be a semicolon, +as we do for~\Cee, since declarations of constructor members of a class lack +an initial type specification, so the |expression| would fail to become part +of a |declaration|. Therefore, special measures are necessary: the simplest +solution is to simply absorb (rule~192) any preceding type specifier into the +|function_head| (thereby removing the distinction between its presence or +absence), and construct de |declaration| explicitly from the |function_head| +and the following semicolon (rule~193). + +@< Rules @>= +{180, {{case_like, binop}}, {expression, "_o_"},only_plus_plus}, @/ +{181, {{case_like, unorbinop}}, {expression, "_o_"},only_plus_plus}, @/ +{182, {{case_like, unop}}, {expression, NULL},only_plus_plus}, @/ +{183, {{int_like, colcol, expression}}, + {expression, NULL},only_plus_plus}, @/ +{184, {{colcol, expression}}, {expression, "o__"},only_plus_plus}, @/ +{185, {{int_like, colcol, int_like}}, + {int_like, NULL},only_plus_plus}, @/ +{186, {{colcol, int_like}}, {int_like, "o__"},only_plus_plus}, @/ +{187, {{int_like, binop, expression}}, + {int_like, NULL},only_plus_plus}, @/ +{190, {{int_like, parameters}}, {function_head, "_B_"},only_plus_plus}, @/ +{191, {{int_like, lpar,rpar}}, {function_head, "_B_,_"},only_plus_plus},@/ +{192, {{int_like, function_head}}, + {function_head, "_ _"},only_plus_plus}, @/ +{193, {{function_head, semi}}, {declaration, "!__"},only_plus_plus}, @[@] diff --git a/web/c_cpp/cwebx/sample.ps b/web/c_cpp/cwebx/sample.ps new file mode 100644 index 0000000000..53ae91cb63 --- /dev/null +++ b/web/c_cpp/cwebx/sample.ps @@ -0,0 +1,621 @@ +%!PS-Adobe-2.0 +%%Creator: dvipsk 5.58a Copyright 1986, 1994 Radical Eye Software +%%Pages: 2 +%%PageOrder: Ascend +%%BoundingBox: 0 0 596 842 +%%DocumentPaperSizes: a4 +%%EndComments +%DVIPSCommandLine: dvips -f -p6 -l7 +%DVIPSParameters: dpi=300, compressed, comments removed +%DVIPSSource: TeX output 1998.01.13:1225 +%%BeginProcSet: texc.pro +/TeXDict 250 dict def TeXDict begin /N{def}def /B{bind def}N /S{exch}N +/X{S N}B /TR{translate}N /isls false N /vsize 11 72 mul N /hsize 8.5 72 +mul N /landplus90{false}def /@rigin{isls{[0 landplus90{1 -1}{-1 1} +ifelse 0 0 0]concat}if 72 Resolution div 72 VResolution div neg scale +isls{landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div +hsize mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul +TR[matrix currentmatrix{dup dup round sub abs 0.00001 lt{round}if} +forall round exch round exch]setmatrix}N /@landscape{/isls true N}B +/@manualfeed{statusdict /manualfeed true put}B /@copies{/#copies X}B +/FMat[1 0 0 -1 0 0]N /FBB[0 0 0 0]N /nn 0 N /IE 0 N /ctr 0 N /df-tail{ +/nn 8 dict N nn begin /FontType 3 N /FontMatrix fntrx N /FontBBox FBB N +string /base X array /BitMaps X /BuildChar{CharBuilder}N /Encoding IE N +end dup{/foo setfont}2 array copy cvx N load 0 nn put /ctr 0 N[}B /df{ +/sf 1 N /fntrx FMat N df-tail}B /dfs{div /sf X /fntrx[sf 0 0 sf neg 0 0] +N df-tail}B /E{pop nn dup definefont setfont}B /ch-width{ch-data dup +length 5 sub get}B /ch-height{ch-data dup length 4 sub get}B /ch-xoff{ +128 ch-data dup length 3 sub get sub}B /ch-yoff{ch-data dup length 2 sub +get 127 sub}B /ch-dx{ch-data dup length 1 sub get}B /ch-image{ch-data +dup type /stringtype ne{ctr get /ctr ctr 1 add N}if}B /id 0 N /rw 0 N +/rc 0 N /gp 0 N /cp 0 N /G 0 N /sf 0 N /CharBuilder{save 3 1 roll S dup +/base get 2 index get S /BitMaps get S get /ch-data X pop /ctr 0 N ch-dx +0 ch-xoff ch-yoff ch-height sub ch-xoff ch-width add ch-yoff +setcachedevice ch-width ch-height true[1 0 0 -1 -.1 ch-xoff sub ch-yoff +.1 sub]/id ch-image N /rw ch-width 7 add 8 idiv string N /rc 0 N /gp 0 N +/cp 0 N{rc 0 ne{rc 1 sub /rc X rw}{G}ifelse}imagemask restore}B /G{{id +gp get /gp gp 1 add N dup 18 mod S 18 idiv pl S get exec}loop}B /adv{cp +add /cp X}B /chg{rw cp id gp 4 index getinterval putinterval dup gp add +/gp X adv}B /nd{/cp 0 N rw exit}B /lsh{rw cp 2 copy get dup 0 eq{pop 1}{ +dup 255 eq{pop 254}{dup dup add 255 and S 1 and or}ifelse}ifelse put 1 +adv}B /rsh{rw cp 2 copy get dup 0 eq{pop 128}{dup 255 eq{pop 127}{dup 2 +idiv S 128 and or}ifelse}ifelse put 1 adv}B /clr{rw cp 2 index string +putinterval adv}B /set{rw cp fillstr 0 4 index getinterval putinterval +adv}B /fillstr 18 string 0 1 17{2 copy 255 put pop}for N /pl[{adv 1 chg} +{adv 1 chg nd}{1 add chg}{1 add chg nd}{adv lsh}{adv lsh nd}{adv rsh}{ +adv rsh nd}{1 add adv}{/rc X nd}{1 add set}{1 add clr}{adv 2 chg}{adv 2 +chg nd}{pop nd}]dup{bind pop}forall N /D{/cc X dup type /stringtype ne{] +}if nn /base get cc ctr put nn /BitMaps get S ctr S sf 1 ne{dup dup +length 1 sub dup 2 index S get sf div put}if put /ctr ctr 1 add N}B /I{ +cc 1 add D}B /bop{userdict /bop-hook known{bop-hook}if /SI save N @rigin +0 0 moveto /V matrix currentmatrix dup 1 get dup mul exch 0 get dup mul +add .99 lt{/QV}{/RV}ifelse load def pop pop}N /eop{SI restore userdict +/eop-hook known{eop-hook}if showpage}N /@start{userdict /start-hook +known{start-hook}if pop /VResolution X /Resolution X 1000 div /DVImag X +/IE 256 array N 0 1 255{IE S 1 string dup 0 3 index put cvn put}for +65781.76 div /vsize X 65781.76 div /hsize X}N /p{show}N /RMat[1 0 0 -1 0 +0]N /BDot 260 string N /rulex 0 N /ruley 0 N /v{/ruley X /rulex X V}B /V +{}B /RV statusdict begin /product where{pop product dup length 7 ge{0 7 +getinterval dup(Display)eq exch 0 4 getinterval(NeXT)eq or}{pop false} +ifelse}{false}ifelse end{{gsave TR -.1 .1 TR 1 1 scale rulex ruley false +RMat{BDot}imagemask grestore}}{{gsave TR -.1 .1 TR rulex ruley scale 1 1 +false RMat{BDot}imagemask grestore}}ifelse B /QV{gsave newpath transform +round exch round exch itransform moveto rulex 0 rlineto 0 ruley neg +rlineto rulex neg 0 rlineto fill grestore}B /a{moveto}B /delta 0 N /tail +{dup /delta X 0 rmoveto}B /M{S p delta add tail}B /b{S p tail}B /c{-4 M} +B /d{-3 M}B /e{-2 M}B /f{-1 M}B /g{0 M}B /h{1 M}B /i{2 M}B /j{3 M}B /k{ +4 M}B /w{0 rmoveto}B /l{p -4 w}B /m{p -3 w}B /n{p -2 w}B /o{p -1 w}B /q{ +p 1 w}B /r{p 2 w}B /s{p 3 w}B /t{p 4 w}B /x{0 S rmoveto}B /y{3 2 roll p +a}B /bos{/SS save N}B /eos{SS restore}B end +%%EndProcSet +TeXDict begin 39158280 55380996 1000 300 300 () @start +/Fa 1 1 df<B512C0A212027D871A>0 D E /Fb 21 121 df<EB01FCEB0703130CEB1C07 +1403EB18001338A45B3807FFFC3800701CA3EBE038A41470EA01C0A314E2A3380380E414 +641438140090C7FC5A120612C612E412CC12701825819C17>12 D<903901FC0FEE903907 +0E307E010C136091380CE07C90391C00C01C140113381638A2EC0380A20003B612F03A00 +70038070A2EC070016E013E0A391380E01C0A2EA01C0A2ED0388141CA201801490000314 +01ED00E04A13001300A248133038C63060EAE63838CC3180D8781FC8FC2725819C25>15 +D<EA03CCEA063C120CEA181CEA383812301270A2EAE070A413E212C0A2EA61E4EA6264EA +3C380F127B9115>97 D<123F1207A2120EA45AA4EA39C0EA3E60EA3830A2EA7038A4EAE0 +70A3136013E0EAC0C012C1EA6180EA6300123C0D1D7B9C13>I<EA01F0EA0708120CEA18 +1CEA3838EA30001270A25AA513081310EA6020EA30C0EA1F000E127B9113>I<EB1F8013 +03A2EB0700A4130EA4EA03DCEA063C120CEA181CEA383812301270A2485AA413E212C0A2 +EA61E4EA6264EA3C38111D7B9C15>I<EA01E0EA0710120CEA1808EA381012701360EA7F +80EAE000A51308EA60101320EA30C0EA1F000D127B9113>I<EB03C0EB0670130CEB1C60 +1400A25BA53803FF8038007000A55BA5485AA5485AA390C7FCA25A12C612E65A12781425 +819C0D>I<13F3EA018FEA030FEA0607EA0E0E120C121CA2EA381CA413381230A2EA1878 +13F0EA0F701200A213E0A2EAC0C012E1EAC300127E101A7D9113>I<EA0FC01201A2485A +A448C7FCA4EA0E78138CEA0F0E120E121CA4485AA35B00701380A21370EB7100EAE032EA +601C111D7D9C15>I<EA01801203EA0100C7FCA7121C12261247A2128EA2120E5AA35AA2 +1271A31272A2123C091C7C9B0D>I<EA1F801203A2EA0700A4120EA45AA45AA45AA412E4 +A412681238091D7C9C0B>108 D<393C1E078039266318C0394683A0E0384703C0008E13 +80A2120EA2391C0701C0A3EC0380D8380E1388A2EC0708151039701C032039300C01C01D +127C9122>I<EA3C3CEA2646EA4687EA4707128EA2120EA2EA1C0EA35B00381340A21338 +148038701900EA300E12127C9117>I<EA01E0EA0718EA0C0C12181238EA300E1270A2EA +E01CA31318133813301360EA60C0EA3180EA1E000F127B9115>I<EA07873804D9803808 +E0C0A23811C0E0A21201A2380381C0A31480EA070314005B1306EA0E8C137090C7FCA25A +A4123CB47E131A7F9115>I<EA3C3CEA26C2EA4687EA4707EA8E061300120EA25AA45AA4 +5A123010127C9112>114 D<EA01F0EA0608120C131CEA1818EA1C00121F13C0EA0FF012 +07EA00781338EA603012E012C0EA8060EA60C0EA1F000E127D9111>I<13C01201A3EA03 +80A4EAFFE0EA0700A3120EA45AA4EA3840A31380EA1900120E0B1A7D990E>I<EA1E03EA +27071247A2EA870EA2120EA2485AA438383880A21218EB3900EA1C59EA078E11127C9116 +>I<EA070EEA19913810E38012203841C30013C01201A2485AA4EA07021267EAE70412CB +EA8B08EA70F011127D9113>120 D E /Fc 3 51 df<1360AAB512F0A238006000AA1416 +7E9119>43 D<120C121C12EC120CAFEAFFC00A137D9211>49 D<121FEA60C01360EAF070 +13301260EA0070A2136013C012011380EA02005AEA08101210EA2020EA7FE012FF0C137E +9211>I E /Fd 5 111 df<126012F0A212701210A41220A212401280040C7C830C>59 +D<130113031306A3130CA31318A31330A31360A213C0A3EA0180A3EA0300A31206A25AA3 +5AA35AA35AA35AA210297E9E15>61 D<EA01F0EA030C120CEA1C1EEA383CEA3018EA7000 +A25AA51304EA60081310EA3060EA1F800F127E9112>99 D<EB01E0EB0630EB0E7814F0EB +1C601400A45BA23803FFC038003800A25BA55BA5485AA55B1203A20063C7FC12F312F612 +E4127815257E9C14>102 D<EA3C1F384E6180384681C0EA4701128F128E120EA2381C03 +80A3EB070000381310A2130E1420387006403830038014127E9119>110 +D E /Fe 11 106 df<1203A4EAC30CEAE31CEA7338EA1FE0EA0780A2EA1FE0EA7338EAE3 +1CEAC30CEA0300A40E127D9215>3 D<EB3F80EBC0603803001800041304487F487F48EB +0080A2481440A2481420130E131FA4130E90C7FC00401440A26C1480A26CEB01006C1302 +6C5B000313183800C060EB3F801B1C7E9720>12 D<B612E07EC9FCA7B612E0A2C9FCA700 +7FB512E0B6FC1B147E9320>17 D<1306A25B131C13185B017FB512F090B612F8D80380C8 +FC48C9FC123C12F0123C120E12036C7E6CB612F86D14F00130C8FC7F7FA27FA225187E95 +2A>40 D<1460A214C0A2EB0180A3EB0300A21306A25BA25BA35BA25BA25BA2485AA248C7 +FCA31206A25AA25AA25AA35AA25A124013287A9D00>54 D<13101338A2136CA313C6A2EA +0183A238030180A2380600C0A3481360A2481330A2481318A348130CA24813061402171A +7E981C>94 D<00C0130214060060130CA26C1318A36C1330A26C1360A26C13C0A3380301 +80A238018300A2EA00C6A2136CA31338A21310171A7E981C>I<133C13E0EA01C0138012 +03AD13005A121C12F0121C12077E1380AD120113C0EA00E0133C0E297D9E15>102 +D<12F0121C12077E1380AD120113C0EA00E0133C13E0EA01C013801203AD13005A121C12 +F00E297D9E15>I<134013C0EA0180A3EA0300A21206A35AA25AA35AA25AA35AA21260A3 +7EA27EA37EA27EA37EA2EA0180A3EA00C013400A2A7D9E10>I<12C0A21260A37EA27EA3 +7EA27EA37EA2EA0180A3EA00C0A2EA0180A3EA0300A21206A35AA25AA35AA25AA35AA20A +2A7E9E10>I E /Ff 5 88 df<38C00180AEB5FCA211107E8916>32 +D<EA7FF8EAFFFE6C7EEA1C0FEB07801303A313071400EA1FFF5BA2EA1C1FEB038014C013 +01A41303EB0780EA7FFFB51200EA7FFC12197F9816>66 D<3801F180EA07FBEA0FFFEA1F +0FEA3C07EA38031270A200F0C7FC5AA77E38700380A21238383C0700EA1F0FEA0FFE6C5A +EA01F011197E9816>I<387FFFC0B5FC7EEA1C01A490C7FCA2131CA2EA1FFCA3EA1C1CA2 +90C7FC14E0A5EA7FFFB5FC7E13197F9816>69 D<38FC07E0EAFE0FEAFC07387001C0A300 +301380EA3803A313E3EA39F3A213B300191300A61313EA1B1BEA0F1EA2EA0E0E13197F98 +16>87 D E /Fg 35 118 df<126012F0A2126004047D830A>46 D<EA0F80EA30E0EA4070 +EA8030EAC03812E0124012001370A2136013C0EA0180EA03001206EA0C081208EA1018EA +3FF0127F12FF0D157E9412>50 D<EA0FE0EA3030EA6018EA701CA21200131813381360EA +07E0EA00301318130C130EA212E0A2EAC00CEA4018EA3030EA0FE00F157F9412>I<1330 +A2137013F012011370120212041208121812101220124012C0EAFFFEEA0070A5EA03FE0F +157F9412>I<EA2030EA3FE013C0EA24001220A4EA2F80EA30E0EA2070EA00301338A212 +4012E0A2EA8030EA4060EA20C0EA1F000D157E9412>I<EA01F0EA0608EA080CEA181C12 +30EA7000126012E0EAE3E0EAEC30EAF018130CEAE00EA31260A2EA300C1318EA1830EA07 +C00F157F9412>I<1240EA7FFE13FC13F8EAC008EA80101320EA00401380A2EA0100A25A +12021206A2120EA512040F167E9512>I<13101338A3135CA3138EA3EA0107A200031380 +EA0203A23807FFC0EA0401A2380800E0A21218003813F038FE03FE17177F961A>65 +D<B512E0EA1C00146014201410A3EB0400A3130CEA1FFCEA1C0C13041408A21300141814 +10A2143014F0B5FC15177F9618>69 D<EB7E0838038198380600584813384813185A0070 +1308A25A1400A4EB03FEEB00381270A212307E7E7E380380D838007F0817177E961C>71 +D<38FF83FE381C0070AA381FFFF0381C0070AA38FF83FE17177F961A>I<EAFFE0EA0E00 +B3A3EAFFE00B177F960D>I<38FF80FE381C0078146014401480EB0100130613085B1338 +1378139CEA1D0E121EEA1C07EB0380EB01C0A2EB00E014701478147C38FF80FF18177F96 +1B>75 D<EAFFC0001CC7FCAD1420A31460A2144014C01303B5FC13177F9616>I<00FEEB +03F8001E14C000171305A338138009A23811C011A33810E021A2EB7041A3EB3881A2EB1D +01A2130EA2123839FE040FF81D177F9620>I<00FC13FE001E1338001F13101217EA1380 +EA11C0A2EA10E013701338A2131C130E130F1307EB0390EB01D0A2EB00F0147014301238 +00FE131017177F961A>I<13FCEA0303380E01C0381C00E0481370003013300070133800 +60131800E0131CA700701338A200301330003813706C13E0380E01C038030300EA00FC16 +177E961B>I<EAFFFE381C0380EB00C014601470A4146014C0EB0380381FFE00001CC7FC +AAB47E14177F9618>I<EAFFFC381C0380EB00C014E01470A414E014C0EB0380381FFE00 +381C0780EB01C0EB00E0A514E1A2147238FF803C18177F961A>82 +D<EA0FC4EA302CEA601CEA400CEAC004A3EAE0001270127FEA3FE0EA0FF8EA01FCEA001C +130E13061280A3EAC004EAE008EAD810EA87E00F177E9614>I<387FFFF8386038180040 +1308A200801304A300001300AF3807FFC016177F9619>I<38FF80FE381C00381410B06C +132012066C13403801818038007E0017177F961A>I<3AFF07FC3F803A3C01E00E00D81C +001304A2EB0170000E5CA2EB023800075CA2EB041CD803845BA2EB880ED801C85BA2EBD8 +0F3900F00780A3D96003C7FCA321177F9624>87 D<EA1FC0EA38601330EA10381200EA03 +F8EA1E3812301270EAE039A21379EA70FFEA1F1E100E7F8D12>97 +D<EA07F0EA18381230EA7010EA600012E0A41260EA70081230EA1830EA07C00D0E7F8D10 +>99 D<137E130EA8EA07CEEA1C3EEA300E1270126012E0A412601270EA301EEA182E3807 +CFC012177F9614>I<EA0FC0EA1860EA3030EA7038EAE018EAFFF8EAE000A31260EA7008 +EA3010EA1830EA07C00D0E7F8D10>I<12FC121CA8137CEA1D8EEA1E07121CAA38FF9FE0 +1317809614>104 D<1218123CA212181200A5127C121CAC12FF081780960A>I<12FC121C +B3A3EAFF80091780960A>108 D<EAFC7CEA1D8EEA1E07121CAA38FF9FE0130E808D14> +110 D<EA07C0EA1830EA3018EA600CA2EAE00EA5EA701CEA3018EA1830EA07C00F0E7F8D +12>I<EA1F40EA60C0EAC040A2EAE000B4FCEA7F80EA1FC0EA01E0EA8060A212C0EAE0C0 +EA9F000B0E7F8D0E>115 D<1208A31218A21238EAFF80EA3800A71340A4EA1C80EA0F00 +0A147F930E>I<EAFC3FEA1C07AA5BEA0E173803E7E0130E808D14>I +E /Fh 38 122 df<EB3FC03801F0303803C0380007137CEA0F80A2143891C7FCA4B512FC +A2380F807CAE397FE1FF80A2191D809C1B>12 D<903801C070A349485AA490380701C0A4 +90380E0380B71280A27E26001C07C7FCEB380EA5495A007FB61280B7FCA22600E038C7FC +48485AA448485AA4380701C0A321257D9C28>35 D<127812FCA4127806067D850D>46 +D<1360EA01E0120F12FF12F31203B3A2387FFF80A2111B7D9A18>49 +D<EA07F8EA1FFE38383F8038780FC0EAFC0714E01303A21278EA000714C0A2EB0F80EB1F +00131E13385B5B38018060EA03001206000813E0381FFFC05A5AB5FCA2131B7E9A18>I< +EA03F8EA1FFEEA3C1FEB0F80387C07C0127E127C123838000F80A2EB1E005BEA03F8EA00 +1EEB0F80EB07C0A214E01230127812FCA214C038780F80EB1F00EA1FFEEA07F8131B7E9A +18>I<EB018013031307130F131F133F136F13CF138F1201EA030F1206120C1218123012 +6012C0B512F8A238000F80A63801FFF8A2151B7F9A18>I<38180180381FFF005B5B5B13 +C00018C7FCA4EA19F8EA1E0E38180F80EA1007000013C014E0A3127812F8A214C012F038 +600F8038381F00EA1FFEEA07F0131B7E9A18>I<137EEA03FF38078180380F03C0EA1E07 +123CEB038048C7FCA212F813F8EAFB0E38FA0780EAFC0314C000F813E0A41278A214C012 +3CEB0780381E0F00EA07FEEA03F8131B7E9A18>I<1260387FFFE0A214C01480A238E003 +00EAC0065B5BC65AA25B13E0A212015B1203A41207A66C5A131C7D9B18>I<EA03F8EA0F +FE381E0F80EA3807EB03C01278A2127E387F8380EBC700EA3FFE6C5A6C7E3807FF80121D +38387FC038781FE0EAF00713031301A3007813C0EB0380EA3E07381FFE00EA03F8131B7E +9A18>I<90381FE0209038FFF8E03803F80F3807C003380F800148C7FC123E1560127E12 +7C00FC1400A8007C1460127E123E15C07E390F8001803907C003003803F80E3800FFFCEB +1FE01B1C7D9B22>67 D<B6FCA2380FC01F1407801580A2EBC181A3EC800013C313FFA213 +C313C1A21560A29038C000E015C0A21401A21403EC0F80B6FCA21B1C7E9B1F>69 +D<B6FCA2380FC01F1407801580A21401EBC181A2EC8000A213C313FFA213C313C1A401C0 +C7FCA6B5FCA2191C7E9B1E>I<B5FCA2EA07E0B3A6B5FCA2101C7F9B12>73 +D<B5FCA2EA0FC0AF1403A31407A21406140E141E147EB512FEA2181C7E9B1D>76 +D<EA0FF8EA1C1E383E0F80130714C0121C1200137FEA07E7EA1F07123C127C12F8A3EA78 +0B383E13F8EA0FE115127F9117>97 D<B4FCA2121FA9EB3F80EBE1E0EB8070EB0078143C +A2143EA6143CA21478EB8070381EC1E0381C3F00171D7F9C1B>I<EA03FCEA0E0EEA1C1F +123C1278130E00F8C7FCA61278EB0180123C381C0300EA0E0EEA03F811127E9115>I<EB +0FF0A21301A9EA01F9EA0F07EA1C03EA3C011278A212F8A61278A2123CEA1C03380F0FFE +EA03F9171D7E9C1B>I<EA01FCEA0F07381C0380383C01C0127814E012F8A2B5FC00F8C7 +FCA31278007C1360123C6C13C0380F03803801FC0013127F9116>I<137F3801E3803803 +C7C0EA0787120FEB8380EB8000A5EAFFF8A2EA0F80AEEA7FF8A2121D809C0F>I<3803F8 +F0380E0F38121E381C0730003C1380A4001C1300EA1E0FEA0E0EEA1BF80010C7FC1218A2 +EA1FFF14C06C13E04813F0387801F838F00078A300701370007813F0381E03C03807FF00 +151B7F9118>I<B4FCA2121FA9EB0FC0EB31E0EB40F0EB80F8A21300AB38FFE7FFA2181D +7F9C1B>I<121E123FA4121EC7FCA6B4FCA2121FAEEAFFE0A20B1E7F9D0E>I<B4FCA2121F +B3A7EAFFE0A20B1D7F9C0E>108 D<39FF0FC07E903831E18F3A1F40F20780D980FC13C0 +A2EB00F8AB3AFFE7FF3FF8A225127F9128>I<38FF0FC0EB31E0381F40F0EB80F8A21300 +AB38FFE7FFA218127F911B>I<EA01FC380F0780381C01C0003C13E0387800F0A200F813 +F8A6007813F0A2383C01E0381E03C0380F07803801FC0015127F9118>I<38FF3F80EBE1 +E0381F80F0EB0078147C143C143EA6143C147C1478EB80F0EBC1E0EB3F0090C7FCA6EAFF +E0A2171A7F911B>I<EAFE3E1347381E8F80A3381F070090C7FCAAEAFFF0A211127F9114> +114 D<EA1FD8EA3078EA601812E0A2EAF000EAFF80EA7FE013F0EA1FF8EA07FCEA007CEA +C01CA212E01318EAF830EACFC00E127E9113>I<1203A45AA25AA2EA3FFC12FFEA1F00A9 +130CA4EA0F08EA0798EA03F00E1A7F9913>I<38FF07F8A2EA1F00AC1301120F380786FF +EA01F818127F911B>I<38FFC1FCA2381F0060EB80E0000F13C013C03807C180A23803E3 +00A2EA01F6A213FE6C5AA21378A2133016127F9119>I<39FF8FF8FEA2391F03E030A201 +831370000FEBF0601386D807C613C0EBCEF8EBEC790003EB7D80EBF83D0001EB3F00A249 +7E0000131EEBE00EA21F127F9122>I<38FFC7FCA2381F8180EA0F833807C700EA03EEEA +01FC5B1200137C13FEEA01DFEA039F38070F80380607C0380C03E038FF07FCA216127F91 +19>I<38FFC1FCA2381F0060EB80E0000F13C013C03807C180A23803E300A2EA01F713F6 +EA00FE5BA21378A21330A21370EA706012F85BEAF9800073C7FC123E161A7F9119>I +E /Fi 42 121 df<EAE038EAF078A7EAE038A2EA60300D0B7C9816>34 +D<38380180EA7C03A238EE0700A2130EA35B127C5B1238C65AA35BA2485AA3485AEB8380 +380707C0A2380E0EE0A3121CA2383807C0A23818038013207F9C16>37 +D<12301278127C123C121CA41238127812F012E01240060D789816>39 +D<1238127C127EA2123E120E121E121C127812F01260070B798416>44 +D<127012F8A312700505788416>46 D<12E0B51280A338E00F00131EEA001C5B13781370 +5BA2485AA3485AA448C7FCA7111A7E9916>55 D<EA03E0EA0FF8487EEA3C1EEA700E7F12 +E014801303A213071270EA780FEA3FFFEA1FFBEA0FE338000700A21230EA780E5BEA7078 +EA3FF06C5AEA0F8011197E9816>57 D<127012F8A312701200A8127012F8A31270051278 +9116>I<EB01801307131FEB3E0013F8485AEA07C0485A003EC7FC12FC12F012FC123EEA +0F806C7EEA01F06C7E133EEB1F801307130111157E9616>60 D<12C012F012FC123EEA0F +806C7EEA01F06C7E133EEB1F801307131FEB3E0013F8485AEA07C0485A003EC7FC12FC12 +F012C011157E9616>62 D<EA7FF8EAFFFE6C7EEA1C0FEB07801303A313071400EA1FFF5B +A2EA1C1FEB038014C01301A41303EB0780EA7FFFB51200EA7FFC12197F9816>66 +D<3801F180EA07FBEA0FFFEA1F0FEA3C07EA38031270A200F0C7FC5AA77E38700380A212 +38383C0700EA1F0FEA0FFE6C5AEA01F011197E9816>I<387FFFC0B5FC7EEA1C01A490C7 +FCA2131CA2EA1FFCA3EA1C1CA290C7FC14E0A5EA7FFFB5FC7E13197F9816>69 +D<B512E0A3EA1C00A41400A2131CA2EA1FFCA3EA1C1CA290C7FCA6B47E7F5B13197F9816 +>I<EA03E348B4FC5AEA1E1F487EEA38071270A200F0C7FC5AA4EB3F80EB7FC0EB3F8038 +F007001270A26C5A123C6C5A6CB4FCEA07F7EA03E712197E9816>I<387E1FC038FF3FE0 +387F1FC0381D07001387A313C7A2121CA213E7A31367A21377A21337A31317EA7F1FEAFF +9FEA7F0F13197F9816>78 D<EA1FFC487E487EEA780F38F00780EAE003AEEAF007A23878 +0F00EA7FFF6C5A6C5A11197E9816>I<EA07E3EA1FFF5AEA781FEAF00712E0A290C7FC7E +1278EA3F80EA1FF0EA07FCC67E130F7FEB0380A2126012E0EB0700EAF80FEAFFFE5BEAC7 +F011197E9816>83 D<387FFFE0B5FCA2EAE0E0A400001300AFEA07FC487E6C5A13197F98 +16>I<38FC07E0EAFE0FEAFC07387001C0A300301380EA3803A313E3EA39F3A213B30019 +1300A61313EA1B1BEA0F1EA2EA0E0E13197F9816>87 D<12C07EA21270A27EA27EA27EA2 +7EA26C7EA26C7EA26C7EA21370A27FA27FA27FA27FA2EB0380A2130111207E9C16>92 +D<EA1FE0487E487EEA783CEA300E1200A2EA03FE121FEA3E0E127012E0A3EA783E387FFF +E0EA3FE7EA0F8313127E9116>97 D<127E12FE127E120EA4133E13FF000F1380EB83C0EB +00E0120E1470A614E0EA0F01EB83C0EBFF80000E1300EA063C1419809816>I<EA03F8EA +0FFCEA1FFEEA3C1EEA780CEA70005AA57EEA70071278EA3E0EEA1FFCEA0FF8EA03F01012 +7D9116>I<133F5B7F1307A4EA03C7EA0FF748B4FCEA3C1F487EEA700712E0A6EA700FA2 +EA3C1F381FFFE0380FE7F03807C7E014197F9816>I<EA03E0EA0FF8EA1FFCEA3C1EEA78 +0EEA700712E0B5FCA3EAE000A2EA70071278EA3C0FEA1FFEEA0FFCEA03F010127D9116> +I<131FEB7F8013FFEA01E7EBC30013C0A2EA7FFFB5FCA2EA01C0ACEA3FFE487E6C5A1119 +7F9816>I<3803E3C03807F7E0EA0FFF381C1CC038380E00A56C5AEA0FF8485AEA1BE000 +38C7FC1218EA1FFC13FF481380387803C038E000E0A4387001C0EA7C07383FFF80380FFE +00EA03F8131C7F9116>I<127E12FE127E120EA4133C13FEEA0FFFEB87801303120EAA38 +7FC7F038FFE7F8387FC7F01519809816>I<EA0180EA03C0A2EA0180C7FCA4EA7FC0A312 +01ACEA7FFFB5FC7E101A7D9916>I<EAFFC0A31201B3B51280A311197E9816>108 +D<38F9C38038FFEFC0EBFFE0EA3C78A2EA3870AA38FE7CF8A2EB3C781512809116>I<EA +7E3CEAFEFEEA7FFF380F87801303120EAA387FC7F038FFE7F8387FC7F01512809116>I< +EA03E0EA0FF8487EEA3C1E487EEA700738E00380A5EAF00700701300EA780FEA3C1EEA1F +FC6C5AEA03E011127E9116>I<EA7E3EEAFEFF007F1380380F83C0EB00E0120E1470A614 +E0EA0F01EB83C0EBFF80000E1300133C90C7FCA6EA7FC0487E6C5A141B809116>I<38FF +0FC0EB3FE0137F3807F040EBC0005BA290C7FCA8EAFFFCA313127F9116>114 +D<EA0FECEA3FFC127FEAF03CEAE01CA2EA7000EA7F80EA1FF0EA07F8EA003CEA600E12E0 +12F0EAF81EEAFFFC13F8EAC7E00F127D9116>I<12035AA4EA7FFFB5FCA20007C7FCA75B +EB0380A3EB8700EA03FE6C5A6C5A11177F9616>I<387E1F80EAFE3FEA7E1FEA0E03AB13 +0F380FFFF03807FBF83803E3F01512809116>I<387F1FC000FF13E0007F13C0381C0700 +EA1E0FEA0E0EA36C5AA4EA03B8A3EA01F0A26C5A13127F9116>I<38FF1FE013BF131F38 +380380A413E33819F300A213B3EA1DB7A4EA0F1EA313127F9116>I<387F1FC0133F131F +380F1C00EA073CEA03B813F012016C5A12017FEA03B8EA073C131CEA0E0E387F1FC038FF +3FE0387F1FC013127F9116>I E /Fj 59 123 df<EB7E1F3901C1B180390303E3C00007 +13C3000EEBC180903801C000A6B512FC380E01C0B0387F87FC1A1D809C18>11 +D<137E3801C180EA0301380703C0120EEB018090C7FCA5B512C0EA0E01B0387F87F8151D +809C17>I<EA6060EAF0F0EAF8F8EA6868EA0808A3EA1010A2EA2020EA4040EA80800D0C +7F9C15>34 D<13E0EA0310EA0608A2120EA45BA25B6C5AEC3FE09038800F80EC06000003 +130412073809C00800115BEA30E03820F020EA607038E03840EB3C80131C90380F00207F +0070EB8040383009C0391830E180390FC03F001B1F7E9D20>38 D<1380EA010012021206 +5AA25AA25AA35AA412E0AC1260A47EA37EA27EA27E12027EEA0080092A7C9E10>40 +D<7E12407E12307EA27EA27EA37EA41380AC1300A41206A35AA25AA25A12205A5A092A7E +9E10>I<1306ADB612E0A2D80006C7FCAD1B1C7E9720>43 D<126012F0A212701210A412 +20A212401280040C7C830C>I<126012F0A2126004047C830C>46 +D<EA03C0EA0C30EA1818EA300CA2EA700EEA6006A2EAE007ADEA6006A2EA700EEA300CA2 +EA1818EA0C30EA07E0101D7E9B15>48 D<5A1207123F12C71207B3A5EAFFF80D1C7C9B15 +>I<EA07C0EA1830EA201CEA400C130EEAF00F12F81307A21270EA000F130EA2131CA213 +381370136013C0EA0180EA0300EA0601120C1218EA1002EA3FFE127F12FF101C7E9B15> +I<EA07E0EA1830EA201CA2EA781E130E131E1238EA001CA2131813301360EA07C0EA0030 +131CA2130E130FA2127012F8A3EAF00EEA401C1220EA1830EA07C0101D7E9B15>I<130C +A2131C133CA2135C13DC139CEA011C120312021204120C1208121012301220124012C0B5 +12C038001C00A73801FFC0121C7F9B15>I<EA300CEA3FF813F013C0EA2000A6EA23E0EA +2C30EA3018131CEA200E1200130FA3126012F0A3EA800EEA401E131CEA2038EA1870EA07 +C0101D7E9B15>I<13F0EA030CEA0404EA0C0EEA181E1230130CEA7000A21260EAE3E0EA +E430EAE818EAF00C130EEAE0061307A51260A2EA7006EA300E130CEA1818EA0C30EA03E0 +101D7E9B15>I<1240387FFF801400A2EA4002485AA25B485AA25B1360134013C0A21201 +5BA21203A41207A66CC7FC111D7E9B15>I<EA03E0EA0C30EA1008EA200C13061260A212 +70A2EA7C0CEA3E18EA3FB0EA1FE0EA07F013F8EA18FCEA307EEA601E130FEAC0071303A4 +EA60021304EA300CEA1C10EA07E0101D7E9B15>I<126012F0A212601200AA126012F0A2 +126004127C910C>58 D<126012F0A212601200AA126012F0A212701210A41220A2124012 +80041A7C910C>I<007FB512C0B612E0C9FCA8B612E06C14C01B0C7E8F20>61 +D<EA0FE0EA3038EA401CEAE00E12F0A3EA000C131C1330136013C0138012011300A6C7FC +A51203EA0780A2EA03000F1D7E9C14>63 D<B512F8380F007814181408A2140C1404A213 +011400A35B13FF13037FA490C7FCA8EAFFF8161C7E9B1B>70 D<EAFFF0EA0F00B3A8EAFF +F00C1C7F9B0F>73 D<EAFFF8EA0F8090C7FCB01408A31418A2141014301470EB01F0B5FC +151C7E9B1A>76 D<EB3F80EBE0E03803803848487E000E7F487F003C1480003813030078 +14C0A20070130100F014E0A8007014C000781303A200381480003C1307001C14006C130E +6C5B6C6C5A3800E0E0EB3F801B1E7E9C20>79 D<B5FC380F01E0EB007880141C141EA414 +1C143C5CEB01E001FFC7FCEB01E0EB0070147880A61510A2141CA239FFF00E20C7EA03C0 +1C1D7E9B1F>82 D<3807E080EA1C19EA30051303EA600112E01300A36C13007E127CEA7F +C0EA3FF8EA1FFEEA07FFC61380130FEB07C0130313011280A300C01380A238E00300EAD0 +02EACC0CEA83F8121E7E9C17>I<007FB512C038700F010060130000401440A200C01420 +1280A300001400B1497E3803FFFC1B1C7F9B1E>I<3AFFE1FFC0FF3A1F003E003C001E01 +3C13186C6D1310A32607801F1320A33A03C0278040A33A01E043C080A33A00F081E100A3 +9038F900F3017913F2A2017E137E013E137CA2013C133C011C1338A20118131801081310 +281D7F9B2B>87 D<12FEA212C0B3B312FEA207297C9E0C>91 D<EA0808EA1010EA2020EA +4040A2EA8080A3EAB0B0EAF8F8EA7878EA30300D0C7A9C15>I<12FEA21206B3B312FEA2 +0729809E0C>I<EA1FC0EA3070EA78387F12301200A2EA01FCEA0F1C12381270126000E0 +1340A3EA603C38304E80381F870012127E9115>97 D<12FC121CAA137CEA1D87381E0180 +381C00C014E014601470A6146014E014C0381E018038190700EA10FC141D7F9C17>I<EA +03F8EA0C0CEA181E1230EA700CEA600012E0A61260EA70021230EA1804EA0C18EA03E00F +127F9112>I<EB1F801303AAEA03F3EA0E0BEA1807EA30031270126012E0A61260127012 +30EA1807EA0E1B3803E3F0141D7F9C17>I<EA07E0EA0C30EA1818EA300CEA700EEA6006 +12E0EAFFFEEAE000A41260EA70021230EA1804EA0C18EA03E00F127F9112>I<13F8EA01 +8CEA071E1206EA0E0C1300A6EAFFE0EA0E00B0EA7FE00F1D809C0D>I<EB03803807C4C0 +EA1C783838388038301800EA701CA4EA3018EA38386C5AEA27C00020C7FCA21230EA3FF8 +6CB4FC1480EA2003386001C0EAC000A33860018038300300EA1C0EEA07F8121C7F9215> +I<12FC121CAA137C1387EA1D03001E1380121CAD38FF9FF0141D7F9C17>I<1218123CA2 +1218C7FCA712FC121CB0EAFF80091D7F9C0C>I<13C0EA01E0A2EA00C01300A7EA07E012 +00B3A21260EAF0C012F1EA6180EA3E000B25839C0D>I<12FC121CAAEB0FE0EB0780EB06 +005B13105B5B13E0121DEA1E70EA1C781338133C131C7F130F148038FF9FE0131D7F9C16 +>I<12FC121CB3A9EAFF80091D7F9C0C>I<39FC7E07E0391C838838391D019018001EEBE0 +1C001C13C0AD3AFF8FF8FF8021127F9124>I<EAFC7CEA1C87EA1D03001E1380121CAD38 +FF9FF014127F9117>I<EA03F0EA0E1CEA1806487E00701380EA600100E013C0A6006013 +80EA700300301300EA1806EA0E1CEA03F012127F9115>I<EAFC7CEA1D87381E0180001C +13C0EB00E0A21470A614E0A2EB01C0001E1380381D0700EA1CFC90C7FCA7B47E141A7F91 +17>I<3803E080EA0E19EA1805EA3807EA7003A212E0A61270A2EA38071218EA0E1BEA03 +E3EA0003A7EB1FF0141A7F9116>I<EAFCE0EA1D38EA1E78A2EA1C301300ACEAFFC00D12 +7F9110>I<EA1F90EA2070EA4030EAC010A212E0EAF800EA7F80EA3FE0EA0FF0EA00F8EA +8038131812C0A2EAE010EAD060EA8FC00D127F9110>I<1204A4120CA2121C123CEAFFE0 +EA1C00A91310A5120CEA0E20EA03C00C1A7F9910>I<38FC1F80EA1C03AD1307120CEA0E +1B3803E3F014127F9117>I<38FF07E0383C0380381C0100A2EA0E02A2EA0F06EA0704A2 +EA0388A213C8EA01D0A2EA00E0A3134013127F9116>I<39FF3FC7E0393C0703C0001CEB +01801500130B000E1382A21311000713C4A213203803A0E8A2EBC06800011370A2EB8030 +000013201B127F911E>I<38FF0FE0381E0700EA1C06EA0E046C5AEA039013B0EA01E012 +007F12011338EA021C1204EA0C0E487E003C138038FE1FF014127F9116>I<38FF07E038 +3C0380381C0100A2EA0E02A2EA0F06EA0704A2EA0388A213C8EA01D0A2EA00E0A31340A2 +5BA212F000F1C7FC12F312661238131A7F9116>I<EA7FFCEA70381260EA407013F013E0 +EA41C012031380EA0700EA0F04120E121CEA3C0CEA380812701338EAFFF80E127F9112> +I E end +%%EndProlog +%%BeginSetup +%%Feature: *Resolution 300dpi +TeXDict begin +%%PaperSize: a4 +%%BeginPaperSize: a4 +a4 +%%EndPaperSize + +%%EndSetup +%%Page: 6 1 +6 0 bop 0 42 a Fj(6)70 b Fg(WHA)m(T)14 b(A)e Ff(CWEB)f +Fg(PR)o(OGRAM)h(LOOKS)h(LIKE)893 b Fi(CWEBx)13 b Fg(MANUAL)0 +132 y Fh(1.)48 b(Compar)q(in)o(g)12 b(t)o(ext)g(\014le)q(s.)47 +b Fj(Thi)q(s)11 b(i)q(s)g(an)h(en)o(t)o(irely)f(tr)q(ivial)f(program,)g +(t)n(h)o(a)o(t)i(t)o(e)q(st)o(s)i(wh)o(et)n(h)o(er)f(t)o(w)o(o)e(t)o +(ext)h(\014le)q(s)h(are)f(equal,)0 181 y(an)o(d)i(if)f(not)g(so,)h(p)q +(oin)o(t)o(s)g(ou)o(t)g(t)n(h)o(e)h(\014rst)g(p)q(oin)o(t)e(of)g +(di\013erence.)0 244 y Fh(#includ)o(e)41 b Fi(<stdio.h>)0 +294 y Fh(#includ)o(e)g Fi(<stdlib.h>)42 357 y Fh(t)o(yp)q(e)q(d)o(ef)18 +b(c)o(h)o(ar)d(b)q(o)q(o)o(l)r Fj(;)0 457 y Fh(2.)48 +b Fj(Th)o(e)15 b(ou)o(t)n(lin)o(e)f(of)g(t)n(h)o(e)h(program)e(i)q(s)h +(s)q(imple.)j(W)m(e)d(read)h(c)o(h)o(aract)o(ers)i(f)q(rom)12 +b(b)q(ot)n(h)j(inpu)o(t)f(\014le)q(s)h(in)o(t)o(o)f Fd(c)1634 +463 y Fc(1)1667 457 y Fj(an)o(d)h Fd(c)1766 463 y Fc(2)1799 +457 y Fj(u)o(n)o(t)o(il)e(t)n(h)o(e)0 506 y(compar)q(i)q(son)f(i)q(s)h +(complet)o(e.)k(Lin)o(e)c(an)o(d)h(colu)o(mn)e(cou)o(n)o(t)o(s)k(are)e +(m)o(ain)o(t)o(ain)o(e)q(d)d(in)j Fb(line)19 b Fj(an)o(d)13 +b Fb(c)n(ol)6 b Fj(.)0 569 y Fh(#d)o(e\014n)o(e)42 b +Fb(left)p 268 569 13 2 v 18 w(mar)n(gin)26 b Fj(1)83 +b Fd(=)p Fe(\003)20 b Fj(left)o(most)13 b(colu)o(mn)f(n)n(u)o(m)n(b)q +(er;)h(c)o(h)o(an)o(ge)i(t)o(o)f(0)g(if)f(y)o(ou)g(prefer)22 +b Fe(\003)p Fd(=)42 632 y Fe(h)7 b Fj(F)m(u)o(nct)o(ions)18 +b Fg(5)6 b Fe(i)42 694 y Fh(in)o(t)14 b Fb(main)26 b +Fj(\()p Fh(in)o(t)14 b Fd(n;)20 b Fh(c)o(h)o(ar)c Fe(\003)o(\003)p +Fb(ar)n(g)6 b Fj(\))42 744 y Fe(f)20 b Fh(FILE)c Fe(\003)p +Fd(f)248 750 y Fc(1)267 744 y Fd(;)k Fe(\003)p Fd(f)340 +750 y Fc(2)359 744 y Fj(;)83 b Fd(=)p Fe(\003)20 b Fj(t)n(h)o(e)15 +b(t)o(w)o(o)e(inpu)o(t)h(\014le)q(s)21 b Fe(\003)p Fd(=)83 +794 y Fh(in)o(t)14 b Fd(c)174 800 y Fc(1)193 794 y Fd(;)20 +b(c)243 800 y Fc(2)262 794 y Fd(;)g Fb(c)n(ol)e Fe(\()11 +b Fb(left)p 477 794 V 18 w(mar)n(gin)5 b Fj(;)83 844 +y Fh(lon)o(g)16 b Fb(line)g Fe(\()11 b Fj(1;)83 907 y +Fe(h)c Fj(Op)q(en)15 b(t)n(h)o(e)f(\014le)q(s)h Fd(f)391 +913 y Fc(1)424 907 y Fj(an)o(d)f Fd(f)524 913 y Fc(2)543 +907 y Fj(,)f(t)o(akin)o(g)h(t)n(h)o(e)q(ir)g(n)o(am)o(e)q(s)g(f)q(rom)e +(t)n(h)o(e)i(comm)o(an)o(d)e(lin)o(e)h(or)h(f)q(rom)e(t)n(h)o(e)j(t)o +(ermin)o(al;)d(in)h(cas)q(e)i(of)166 957 y(an)f(error)h(for)e(whic)o(h) +h(no)f(reco)o(v)o(ery)j(i)q(s)d(p)q(oss)q(ible,)h(call)f +Fb(exit)6 b Fj(\(1\))17 b Fg(6)7 b Fe(i)83 1007 y(h)g +Fj(Searc)o(h)15 b(for)f(\014rst)h(di\013erence,)g(lea)o(vin)o(g)f +Fd(c)745 1013 y Fc(1)775 1007 y Fe(6)p Fj(=)e Fd(c)837 +1013 y Fc(2)869 1007 y Fj(if)h(an)o(d)h(only)f(if)g(a)g(di\013erence)j +(w)o(as)e(fou)o(n)o(d)k Fg(3)6 b Fe(i)83 1057 y(h)h Fj(Rep)q(ort)14 +b(t)n(h)o(e)h(ou)o(t)o(com)o(e)e(of)g(t)n(h)o(e)i(compar)q(i)q(son)g +Fg(4)7 b Fe(i)83 1107 y Fh(ret)o(ur)q(n)13 b Fj(0;)83 +b Fd(=)p Fe(\003)20 b Fj(su)o(cce)q(ssful)c(complet)o(ion)j +Fe(\003)p Fd(=)42 1157 y Fe(g)0 1257 y Fh(3.)48 b Fj(Th)o(e)12 +b(h)o(eart)h(of)f(t)n(h)o(e)h(program)d(i)q(s)i(t)n(hi)q(s)g(s)q(imple) +d(lo)q(o)o(p.)17 b(Wh)o(en)12 b(w)o(e)g(reac)o(h)i(t)n(h)o(e)f(en)o(d)f +(of)g(on)o(e)g(of)f(t)n(h)o(e)i(\014le)q(s,)g(t)n(h)o(e)f(\014le)q(s)h +(m)o(a)o(t)o(c)o(h)e(if)0 1307 y(an)o(d)i(only)g(if)g(t)n(h)o(e)h(ot)n +(h)o(er)h(\014le)f(h)o(as)f(also)g(reac)o(h)o(e)q(d)j(it)o(s)e(en)o(d.) +k(F)m(or)c(t)n(hi)q(s)f(reason)i(t)n(h)o(e)f(t)o(e)q(st)h +Fd(c)1357 1313 y Fc(1)1387 1307 y Fj(=)d Fd(c)1449 1313 +y Fc(2)1468 1307 y Fj(,)h(whic)o(h)g(require)q(s)i(c)o(h)o(aract)o(ers) +0 1357 y(t)o(o)h(b)q(e)g(read)g(f)q(rom)e(b)q(ot)n(h)i(\014le)q(s,)g(m) +n(ust)f(prece)q(d)o(e)k(t)n(h)o(e)d(t)o(e)q(st)h(for)e(\014le)h(en)o +(d;)g(wh)o(en)h(only)d(on)o(e)i(\014le)g(en)o(ds,)g(it)g(i)q(s)f(t)n(h) +o(e)h(form)o(er)f(t)o(e)q(st)0 1406 y(whic)o(h)f(breaks)h(t)n(h)o(e)f +(lo)q(o)o(p.)0 1469 y Fe(h)7 b Fj(Searc)o(h)15 b(for)f(\014rst)h +(di\013erence,)g(lea)o(vin)o(g)f Fd(c)662 1475 y Fc(1)692 +1469 y Fe(6)p Fj(=)e Fd(c)754 1475 y Fc(2)786 1469 y +Fj(if)h(an)o(d)h(only)f(if)g(a)g(di\013erence)j(w)o(as)e(fou)o(n)o(d)k +Fg(3)6 b Fe(i)12 b(\021)42 1519 y Fh(while)i Fj(\(\()p +Fd(c)217 1525 y Fc(1)248 1519 y Fe(\()d Fb(getc)t Fj(\()p +Fd(f)412 1525 y Fc(1)431 1519 y Fj(\)\))h(=)g(\()p Fd(c)553 +1525 y Fc(2)583 1519 y Fe(\()f Fb(getc)5 b Fj(\()p Fd(f)748 +1525 y Fc(2)767 1519 y Fj(\)\))k Fe(^)g Fd(c)863 1525 +y Fc(1)893 1519 y Fe(6)p Fj(=)j Fi(EOF)r Fj(\))83 1569 +y Fh(if)20 b Fj(\()p Fd(c)166 1575 y Fc(1)196 1569 y +Fj(=)12 b Fi('\\n')r Fj(\))20 b Fe(f)410 1565 y Fc(+)l(+)458 +1569 y Fb(line)5 b Fj(;)21 b Fb(c)n(ol)c Fe(\()12 b Fb(left)p +745 1569 V 18 w(mar)n(gin)5 b Fj(;)20 b Fe(g)h Fh(els)q(e)1064 +1565 y Fc(+)l(+)1113 1569 y Fb(c)n(ol)6 b Fj(;)0 1623 +y Fg(This)12 b(co)q(d)o(e)f(is)h(us)q(e)q(d)e(in)h(s)q(ect)o(ion)f(2.)0 +1723 y Fh(4.)48 b Fj(Wh)o(en)14 b(t)n(h)o(e)h(\014rst)g(di\013erence)h +(o)q(ccurs)f(a)o(t)f(t)n(h)o(e)h(en)o(d)f(of)f(on)o(e)h(of)g(t)n(h)o(e) +h(\014le)q(s,)f(or)g(a)o(t)f(t)n(h)o(e)i(en)o(d)f(of)g(a)g(lin)o(e,)e +(w)o(e)j(giv)o(e)e(a)h(m)o(e)q(ssage)0 1773 y(in)o(dica)o(t)o(in)o(g)f +(t)n(hi)q(s)g(f)q(act.)0 1836 y Fe(h)7 b Fj(Rep)q(ort)14 +b(t)n(h)o(e)h(ou)o(t)o(com)o(e)e(of)g(t)n(h)o(e)i(compar)q(i)q(son)g +Fg(4)7 b Fe(i)12 b(\021)42 1886 y Fh(if)19 b Fj(\()p +Fd(c)124 1892 y Fc(1)154 1886 y Fj(=)12 b Fd(c)216 1892 +y Fc(2)235 1886 y Fj(\))21 b Fb(printf)10 b Fj(\()p Fi("Files)p +Ff( )p Fi(match.\\n")r Fj(\);)42 1936 y Fh(els)q(e)42 +1986 y Fe(f)20 b Fb(printf)10 b Fj(\()p Fi("Files)p Ff( )p +Fi(differ.\\n")r Fj(\);)83 2036 y Fh(if)20 b Fj(\()p +Fd(c)166 2042 y Fc(1)196 2036 y Fj(=)12 b Fi(EOF)e Fe(_)f +Fd(c)371 2042 y Fc(2)401 2036 y Fj(=)j Fi(EOF)r Fj(\))125 +2086 y Fe(f)20 b Fb(the)p 226 2086 V 19 w(\014le)5 b +Fj(\()p Fd(c)331 2092 y Fc(1)361 2086 y Fj(=)12 b Fi(EOF)r +Fj(\);)20 b Fb(printf)10 b Fj(\()p Fi("is)p Ff( )p Fi(contained)p +Ff( )p Fi(in)p Ff( )p Fi(th)o(e)p Ff( )p Fi(oth)o(er)p +Ff( )p Fi(a)o(s)p Ff( )p Fi(ini)o(tial)p Ff( )o Fi(segme)o(nt.\\n)o(")r +Fj(\);)18 b Fe(g)83 2136 y Fh(els)q(e)d(if)20 b Fj(\()p +Fd(c)259 2142 y Fc(1)289 2136 y Fj(=)12 b Fi('\\n')e +Fe(_)f Fd(c)486 2142 y Fc(2)516 2136 y Fj(=)j Fi('\\n')r +Fj(\))125 2186 y Fe(f)20 b Fb(the)p 226 2186 V 19 w(\014le)5 +b Fj(\()p Fd(c)331 2192 y Fc(1)361 2186 y Fj(=)12 b Fi('\\n')r +Fj(\);)20 b Fb(printf)10 b Fj(\()p Fi("has)p Ff( )p Fi(a)p +Ff( )p Fi(shorter)p Ff( )p Fi(lin)o(e)p Ff( )p Fi(num)o(ber)p +Ff( )o Fi(\045ld)p Ff( )p Fi(t)o(han)p Ff( )p Fi(t)o(he)p +Ff( )p Fi(ot)o(her.\\)o(n")r Fd(;)d Fb(l)o(ine)s Fj(\);)20 +b Fe(g)83 2236 y Fh(els)q(e)i Fb(printf)10 b Fj(\()p +Fi("First)p Ff( )p Fi(difference)p Ff( )p Fi(a)o(t)p +Ff( )p Fi(lin)o(e)p Ff( )p Fi(\045ld)o(,)p Ff( )p Fi(co)o(lumn)p +Ff( )o Fi(\045d.\\n)o(")r Fd(;)d Fb(l)o(ine)s Fd(;)g +Fb(c)n(ol)f Fj(\);)42 2286 y Fe(g)0 2341 y Fg(This)12 +b(co)q(d)o(e)f(is)h(us)q(e)q(d)e(in)h(s)q(ect)o(ion)f(2.)0 +2440 y Fh(5.)48 b Fj(Th)o(e)13 b(fu)o(nct)o(ion)g Fb(the)p +385 2440 V 15 w(\014le)18 b Fj(st)o(art)o(s)c(a)f(s)q(en)o(t)o(ence)h +(a)o(b)q(ou)o(t)f(t)n(h)o(e)h(\014rst)g(or)f(s)q(econ)o(d)g(\014le,)g +(d)o(ep)q(en)o(din)o(g)g(on)g(it)o(s)g(b)q(o)q(olean)f(argu)o(m)o(en)o +(t.)0 2503 y Fe(h)7 b Fj(F)m(u)o(nct)o(ions)18 b Fg(5)7 +b Fe(i)k(\021)42 2553 y Fh(v)o(oid)j Fb(the)p 205 2553 +V 19 w(\014le)26 b Fj(\()p Fh(b)q(o)q(o)o(l)16 b Fb(is)p +455 2553 V 18 w(\014rst)6 b Fj(\))21 b Fe(f)f Fb(printf)10 +b Fj(\()p Fi("The)p Ff( )p Fi(\045s)p Ff( )p Fi(file)p +Ff( )p Fi(")r Fd(;)d Fb(is)p 1116 2553 V 16 w(\014rst)17 +b Fj(?)11 b Fi("first")h Fj(:)f Fi("second")r Fj(\);)19 +b Fe(g)0 2607 y Fg(See)11 b(also)f(s)q(ect)o(ion)g(7.)0 +2662 y(This)i(co)q(d)o(e)f(is)h(us)q(e)q(d)e(in)h(s)q(ect)o(ion)f(2.)p +eop +%%Page: 7 2 +7 1 bop 0 42 a Fi(CWEBx)13 b Fg(MANUAL)895 b(WHA)m(T)13 +b(A)f Ff(CWEB)f Fg(PR)o(OGRAM)i(LOOKS)g(LIKE)70 b Fj(7)0 +132 y Fh(6.)48 b Fj(Th)o(ere)16 b(can)f(b)q(e)g(b)q(e)h(zero,)f(on)o(e) +h(or)f(t)o(w)o(o)f(comm)o(an)o(d)e(lin)o(e)j(argu)o(m)o(en)o(t)o(s.)21 +b(If)15 b(t)n(h)o(ere)h(are)g(non)o(e,)f(t)n(h)o(e)h(us)q(er)g(i)q(s)e +(prompt)o(e)q(d)h(t)o(o)0 181 y(sup)o(ply)h(t)n(h)o(em,)f(an)o(d)h(if)f +(t)n(h)o(ere)j(are)f(t)o(w)o(o)f(t)n(h)o(e)q(s)q(e)h(are)g(t)o(ak)o(en) +f(as)g(t)n(h)o(e)h(\014le)g(n)o(am)o(e)q(s,)e(prompt)o(in)o(g)g(t)n(h)o +(e)i(us)q(er)g(only)e(in)h(cas)q(e)h(a)f(\014le)0 231 +y(could)d(not)g(b)q(e)h(o)o(p)q(en)o(e)q(d.)k(In)13 b(cas)q(e)h(just)g +(on)o(e)f(argu)o(m)o(en)o(t)f(i)q(s)h(pre)q(s)q(en)o(t,)h(t)n(h)o(e)g +(\014rst)h(\014le)e(i)q(s)g(assu)o(m)o(e)q(d)g(t)o(o)h(b)q(e)f(t)n(h)o +(e)h(st)o(an)o(d)o(ard)g(inpu)o(t,)0 281 y(whic)o(h)h(do)q(e)q(s)h(not) +f(h)o(a)o(v)o(e)g(t)o(o)h(b)q(e)g(o)o(p)q(en)o(e)q(d;)f(in)g(t)n(hi)q +(s)h(cas)q(e)g(h)o(o)o(w)o(ev)o(er)g(w)o(e)g(will)e(not)h(read)h(a)g +(\014le)f(n)o(am)o(e)f(f)q(rom)f(t)o(ermin)o(al)h(in)h(cas)q(e)0 +331 y(t)n(h)o(e)g(s)q(econ)o(d)f(\014le)g(cannot)g(b)q(e)g(o)o(p)q(en)o +(e)q(d.)0 393 y Fh(#d)o(e\014n)o(e)42 b Fb(r)n(e)n(ad)p +287 393 13 2 v 19 w(mo)n(de)27 b Fi("r")0 455 y Fe(h)7 +b Fj(Op)q(en)15 b(t)n(h)o(e)g(\014le)q(s)f Fd(f)308 461 +y Fc(1)341 455 y Fj(an)o(d)g Fd(f)441 461 y Fc(2)460 +455 y Fj(,)g(t)o(akin)o(g)g(t)n(h)o(e)q(ir)g(n)o(am)o(e)q(s)g(f)q(rom)e +(t)n(h)o(e)j(comm)o(an)o(d)c(lin)o(e)j(or)g(f)q(rom)e(t)n(h)o(e)j(t)o +(ermin)o(al;)d(in)i(cas)q(e)h(of)e(an)125 505 y(error)i(for)e(whic)o(h) +h(no)f(reco)o(v)o(ery)i(i)q(s)f(p)q(oss)q(ible,)f(call)h +Fb(exit)5 b Fj(\(1\))18 b Fg(6)7 b Fe(i)k(\021)44 551 +y Fa(\000)l(\000)93 555 y Fd(n)p Fj(;)153 551 y Fc(+)l(+)202 +555 y Fb(ar)n(g)5 b Fj(;)83 b Fd(=)p Fe(\003)20 b Fj(ignore)14 +b(\\argu)o(m)o(en)o(t")e(0,)h(whic)o(h)h(i)q(s)f(t)n(h)o(e)i(program)d +(n)o(am)o(e)19 b Fe(\003)p Fd(=)42 605 y Fh(if)g Fj(\()p +Fd(n)12 b Fj(=)g(0\))83 655 y Fe(f)21 b Fb(op)n(en)p +213 655 V 19 w(\014le)5 b Fj(\(&)p Fd(f)352 661 y Fc(1)371 +655 y Fd(;)i Fi("First)p Ff( )p Fi(file)p Ff( )p Fi(to)p +Ff( )p Fi(co)o(mpar)o(e")r Fd(;)g Fe(\014)p Fj(\);)17 +b Fb(op)n(en)p 1080 655 V 20 w(\014le)5 b Fj(\(&)p Fd(f)1220 +661 y Fc(2)1239 655 y Fd(;)i Fi("Second)p Ff( )p Fi(file)p +Ff( )p Fi(to)p Ff( )o Fi(compa)o(re")r Fd(;)f Fe(\014)p +Fj(\);)18 b Fe(g)42 704 y Fh(els)q(e)d(if)k Fj(\()p Fd(n)12 +b Fj(=)g(1\))42 754 y Fe(f)20 b Fd(f)103 760 y Fc(1)133 +754 y Fe(\()12 b Fb(stdin)5 b Fj(;)83 804 y Fh(if)20 +b Fj(\(\()p Fd(f)184 810 y Fc(2)214 804 y Fe(\()11 b +Fb(fop)n(en)6 b Fj(\()p Fe(\003)p Fb(ar)n(g)g Fd(;)h +Fb(r)n(e)n(ad)p 568 804 V 18 w(mo)n(de)f Fj(\)\))12 b(=)g +Fe(\014)p Fj(\))21 b Fe(f)f Fb(printf)11 b Fj(\()p Fi("Could)p +Ff( )p Fi(not)p Ff( )p Fi(open)p Ff( )o Fi(file)p Ff( )o +Fi(\045s.\\n)o(")r Fd(;)c Fe(\003)l Fb(ar)n(g)f Fj(\);)20 +b Fb(exit)6 b Fj(\(1\);)21 b Fe(g)42 854 y(g)42 904 y +Fh(els)q(e)15 b(if)k Fj(\()p Fd(n)12 b Fj(=)g(2\))42 +953 y Fe(f)20 b Fb(op)n(en)p 171 953 V 19 w(\014le)6 +b Fj(\(&)p Fd(f)311 959 y Fc(1)330 953 y Fd(;)h Fi("Give)p +Ff( )p Fi(another)p Ff( )o Fi(first)o Ff( )p Fi(file)o(")r +Fd(;)g Fe(\003)m Fb(ar)n(g)998 949 y Fc(+)l(+)1047 953 +y Fj(\);)83 1003 y Fb(op)n(en)p 171 1003 V 19 w(\014le)f +Fj(\(&)p Fd(f)311 1009 y Fc(2)330 1003 y Fd(;)h Fi("Give)p +Ff( )p Fi(another)p Ff( )o Fi(secon)o(d)p Ff( )p Fi(fil)o(e")r +Fd(;)g Fe(\003)l Fb(ar)n(g)f Fj(\);)42 1053 y Fe(g)42 +1103 y Fh(els)q(e)21 b Fe(f)g Fb(printf)10 b Fj(\()p +Fi("No)p Ff( )p Fi(more)p Ff( )p Fi(than)p Ff( )p Fi(two)p +Ff( )p Fi(c)o(omman)o(d)p Ff( )p Fi(lin)o(e)p Ff( )p +Fi(ar)o(gumen)o(ts)p Ff( )p Fi(ar)o(e)p Ff( )p Fi(all)o(owed.)o(\\n")r +Fj(\);)17 b Fb(exit)6 b Fj(\(1\);)21 b Fe(g)0 1157 y +Fg(This)12 b(co)q(d)o(e)f(is)h(us)q(e)q(d)e(in)h(s)q(ect)o(ion)f(2.)0 +1256 y Fh(7.)48 b Fj(Th)o(e)18 b(fu)o(nct)o(ion)g Fb(op)n(en)p +423 1256 V 16 w(\014le)23 b Fj(will)17 b(try)h(t)o(o)g(o)o(p)q(en)g(t)n +(h)o(e)g(\014le)g Fb(name)24 b Fj(for)18 b(readin)o(g,)h(an)o(d)e(if)g +(t)n(hi)q(s)h(f)q(ails)f(it)g(will)g(prompt)f(for)0 1306 +y(anot)n(h)o(er)h(\014le)g(n)o(am)o(e)f(u)o(n)o(t)o(il)g(it)g(h)o(as)h +(su)o(cce)q(ss.)30 b(If)16 b(calle)q(d)h(wit)n(h)f Fb(name)22 +b Fj(=)17 b Fe(\014)p Fj(,)g(t)n(h)o(e)h(fu)o(nct)o(ion)f(st)o(art)o(s) +h(wit)n(h)e(prompt)o(in)o(g)g(r)q(igh)o(t)0 1356 y(aw)o(ay)m(.)0 +1418 y Fe(h)7 b Fj(F)m(u)o(nct)o(ions)18 b Fg(5)7 b Fe(i)k +Fj(+)p Fe(\021)42 1468 y Fh(v)o(oid)j Fb(op)n(en)p 233 +1468 V 19 w(\014le)26 b Fj(\()p Fh(FILE)17 b Fe(\003\003)p +Fd(f)r(;)j Fh(c)o(h)o(ar)c Fe(\003)o Fb(pr)n(ompt)6 b +Fd(;)20 b Fh(c)o(h)o(ar)c Fe(\003)o Fb(name)6 b Fj(\))42 +1518 y Fe(f)20 b Fh(c)o(h)o(ar)15 b Fb(buf)c Fj([80)o(];)83 +1580 y Fh(if)20 b Fj(\()p Fb(name)d Fj(=)12 b Fe(\014)d(_)g +Fj(\()p Fe(\003)p Fd(f)17 b Fe(\()11 b Fb(fop)n(en)5 +b Fj(\()p Fb(name)h Fd(;)h Fb(r)n(e)n(ad)p 831 1580 V +19 w(mo)n(de)f Fj(\)\))12 b(=)f Fe(\014)p Fj(\))125 1630 +y Fh(do)22 b Fe(f)f Fb(printf)10 b Fj(\()p Fi("\045s:)p +Ff( )p Fi(")r Fd(;)d Fb(pr)n(ompt)t Fj(\);)21 b Fb(\017ush)5 +b Fj(\()p Fb(stdout)i Fj(\);)20 b Fb(sc)n(anf)12 b Fj(\()p +Fi("\04579s")r Fd(;)7 b Fb(buf)i Fj(\);)21 b Fe(g)125 +1680 y Fh(while)14 b Fj(\(\()p Fe(\003)p Fd(f)j Fe(\()11 +b Fb(fop)n(en)5 b Fj(\()p Fb(buf)12 b Fd(;)7 b Fb(r)n(e)n(ad)p +676 1680 V 18 w(mo)n(de)f Fj(\)\))12 b(=)g Fe(\014)p +Fj(\);)42 1730 y Fe(g)0 1829 y Fh(8.)48 b(In)o(d)o(ex.)0 +1896 y Fb(ar)n(g)5 b Fj(:)42 b(2)p 115 1903 21 2 v -1 +w(,)23 b(6.)0 1946 y Fh(b)q(o)q(o)o(l)r Fj(:)41 b(1)p +145 1953 V(,)22 b(5.)0 1996 y Fb(buf)11 b Fj(:)41 b(7)p +118 2003 V(.)0 2047 y Fb(c)n(ol)6 b Fj(:)41 b(2)p 108 +2054 V(,)22 b(3,)g(4.)0 2097 y Fd(c)18 2103 y Fc(1)37 +2097 y Fj(:)41 b(2)p 90 2104 V -1 w(,)23 b(3,)f(4.)0 +2147 y Fd(c)18 2153 y Fc(2)37 2147 y Fj(:)41 b(2)p 90 +2154 V -1 w(,)23 b(3,)f(4.)0 2197 y Fi(EOF)r Fj(:)41 +b(3,)22 b(4.)0 2248 y Fb(exit)6 b Fj(:)41 b(6.)0 2298 +y Fd(f)t Fj(:)h(7)p 78 2305 V(.)0 2348 y Fb(\017ush)6 +b Fj(:)41 b(7.)0 2398 y Fb(fop)n(en)6 b Fj(:)41 b(6,)22 +b(7.)0 2449 y Fd(f)20 2455 y Fc(1)39 2449 y Fj(:)41 b(2)p +92 2456 V(,)22 b(3,)g(6.)0 2499 y Fd(f)20 2505 y Fc(2)39 +2499 y Fj(:)41 b(2)p 92 2506 V(,)22 b(3,)g(6.)0 2549 +y Fb(getc)5 b Fj(:)41 b(3.)0 2599 y Fb(is)p 33 2599 13 +2 v 15 w(\014rst)6 b Fj(:)41 b(5)p 176 2606 21 2 v -1 +w(.)0 2650 y Fb(left)p 60 2650 13 2 v 14 w(mar)n(gin)6 +b Fj(:)41 b(2)p 256 2657 21 2 v -1 w(,)21 b(3.)0 2700 +y Fb(line)5 b Fj(:)41 b(2)p 124 2707 V(,)22 b(3,)g(4.)1017 +1896 y Fb(main)5 b Fj(:)41 b(2)p 1166 1903 V(.)1017 1946 +y Fd(n)p Fj(:)f(2)p 1094 1953 V(.)1017 1995 y Fb(name)5 +b Fj(:)41 b(7)p 1172 2002 V(.)1017 2045 y Fb(op)n(en)p +1102 2045 13 2 v 16 w(\014le)5 b Fj(:)41 b(6,)21 b(7)p +1280 2052 21 2 v(.)1017 2095 y Fb(printf)10 b Fj(:)41 +b(4,)20 b(5,)h(6,)g(7.)1017 2145 y Fb(pr)n(ompt)5 b Fj(:)41 +b(7)p 1202 2152 V(.)1017 2195 y Fb(r)n(e)n(ad)p 1095 +2195 13 2 v 15 w(mo)n(de)5 b Fj(:)41 b(6)p 1258 2202 +21 2 v(,)21 b(7.)1017 2244 y Fb(sc)n(anf)11 b Fj(:)41 +b(7.)1017 2294 y Fb(stdin)5 b Fj(:)41 b(6.)1017 2344 +y Fb(stdout)6 b Fj(:)41 b(7.)1017 2394 y Fb(the)p 1074 +2394 13 2 v 15 w(\014le)5 b Fj(:)41 b(4,)22 b(5)p 1252 +2401 21 2 v(.)p eop +%%Trailer +end +userdict /end-hook known{end-hook}if +%%EOF |