% This program by D. E. Knuth is not copyrighted and can be used freely.% Version 0 was released in December, 1981.% Version 1 was released in September, 1982, with version 0 of TeX.% Slight changes were made in October, 1982, for version 0.6 of TeX.% Version 1.2 added @@= and @@\ and introduced {:nnn} comments (December, 1982).% Version 1.4 added "history" (February, 1983).% Version 1.5 conformed to TeX version 0.96 and fixed @@\ (March, 1983).% Version 1.7 introduced the new change file format (June, 1983).% Version 2.0 was released in July, 1983, with version 0.999 of TeX.% Version 2.5 was released in November, 1983, with version 1.0 of TeX.% Version 2.6 fixed a bug: force-line-break after a constant (August, 1984).% Version 2.7 fixed the definition of check_sum_prime (May, 1985).% Version 2.8 fixed a bug in change_buffer movement (August, 1985).% Version 2.9 allows nonnumeric macros before their def (December, 1988).% Version 3, for Sewell's book, fixed long-line bug in input_ln (March, 1989).% Version 4 was major change to allow 8-bit input (September, 1989).% Version 4.1 conforms to ANSI standard for-loop rules (September, 1990).% Version 4.2 fixes stat report if phase one dies (March, 1991).% Version 4.3 fixes @@ bug in verbatim, catches extra } (September, 1991). % Here is TeX material that gets inserted after \input webmac\def\hang{\hangindent 3em\indent\ignorespaces}\font\ninerm=cmr9\let\mc=\ninerm % medium caps for names like SAIL\def\PASCAL{Pascal}\def\pb{$\.|\ldots\.|$} % Pascal brackets (|...|)\def\v{\.{\char'174}} % vertical (|) in typewriter font\mathchardef\BA="3224 % double arrow\def\({} % kludge for alphabetizing certain module names \def\title{TANGLE}\def\contentspagenumber{123} % should be odd\def\topofcontents{\null\vfill \titlefalse % include headline on the contents page \def\rheader{\mainfont Appendix E\hfil \contentspagenumber} \centerline{\titlefont The {\ttitlefont TANGLE} processor} \vskip 15pt \centerline{(Version 4.3)} \vfill}\pageno=\contentspagenumber \advance\pageno by 1 @* Introduction.This program converts a \.{WEB} file to a \PASCAL\ file. It was writtenby D. E. Knuth in September, 1981; a somewhat similar {\mc SAIL} program hadbeen developed in March, 1979. Since this program describes itself, abootstrapping process involving hand-translation had to be used to get started. For large \.{WEB} files one should have a large memory, since \.{TANGLE} keepsall the \PASCAL\ text in memory (in an abbreviated form). The program usesa few features of the local \PASCAL\ compiler that may need to be changed inother installations: \yskip\item{1)} Case statements have a default.\item{2)} Input-output routines may need to be adapted for use with a particularcharacter set and/or for printing messages on the user's terminal. \yskip\noindentThese features are also present in the \PASCAL\ version of \TeX, where theyare used in a similar (but more complex) way. System-dependent portionsof \.{TANGLE} can be identified by looking at the entries for `systemdependencies' in the index below.@!@^system dependencies@> The ``banner line'' defined here should be changed whenever \.{TANGLE}is modified. @d banner=='This is TANGLE, Version 4.3' @ The program begins with a fairly normal header, made up of pieces that@^system dependencies@>will mostly be filled in later. The \.{WEB} input comes from files |web_file|and |change_file|, the \PASCAL\ output goes to file |Pascal_file|,and the string pool output goes to file |pool|. If it is necessary to abort the job because of a fatal error, the programcalls the `|jump_out|' procedure, which goes to the label |end_of_TANGLE|. @d end_of_TANGLE = 9999 {go here to wrap it up} @p @t\4@>@<Compiler directives@>@/program TANGLE(@!web_file,@!change_file,@!Pascal_file,@!pool);label end_of_TANGLE; {go here to finish}const @<Constants in the outer block@>@/type @<Types in the outer block@>@/var @<Globals in the outer block@>@/@<Error handling procedures@>@/procedure initialize; var @<Local variables for initialization@>@/ begin @<Set initial values@>@/ end; @ Some of this code is optional for use when debugging only;such material is enclosed between the delimiters |debug| and $|gubed|$.Other parts, delimited by |stat| and $|tats|$, are optionally included ifstatistics about \.{TANGLE}'s memory usage are desired. @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}@f debug==begin@f gubed==end@#@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering usage statistics}@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering usage statistics}@f stat==begin@f tats==end @ The \PASCAL\ compiler used to develop this system has ``compilerdirectives'' that can appear in comments whose first character is a dollar sign.In production versions of \.{TANGLE} these directives tell the compiler that@^system dependencies@>it is safe to avoid range checks and to leave out the extra code it insertsfor the \PASCAL\ debugger's benefit, although interrupts will occur ifthere is arithmetic overflow. @<Compiler directives@>=@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging} @ Labels are given symbolic names by the following definitions. We insertthe label `|exit|:' just before the `\ignorespaces|end|\unskip' of aprocedure in which we have used the `|return|' statement defined below;the label `|restart|' is occasionally used at the very beginning of aprocedure; and the label `|reswitch|' is occasionally used just prior toa \&{case} statement in which some cases change the conditions and we wish tobranch to the newly applicable case.Loops that are set up with the \&{loop} construction defined below arecommonly exited by going to `|done|' or to `|found|' or to `|not_found|',and they are sometimes repeated by going to `|continue|'. @d exit=10 {go here to leave a procedure}@d restart=20 {go here to start a procedure again}@d reswitch=21 {go here to start a case statement again}@d continue=22 {go here to resume a loop}@d done=30 {go here to exit a loop}@d found=31 {go here when you've found it}@d not_found=32 {go here when you've found something else} @ Here are some macros for common programming idioms. @d incr(#) == #:=#+1 {increase a variable by unity}@d decr(#) == #:=#-1 {decrease a variable by unity}@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}@d do_nothing == {empty statement}@d return == goto exit {terminate a procedure call}@f return == nil@f loop == xclause @ We assume that |case| statements may include a default case that appliesif no matching label is found. Thus, we shall use constructions like@^system dependencies@>$$\vbox{\halign{#\hfil\cr|case x of|\cr1: $\langle\,$code for $x=1\,\rangle$;\cr3: $\langle\,$code for $x=3\,\rangle$;\cr|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr|endcases|\cr}}$$since most \PASCAL\ compilers have plugged this hole in the language byincorporating some sort of default mechanism. For example, the compilerused to develop \.{WEB} and \TeX\ allows `|others|:' as a default label,and other \PASCAL s allow syntaxes like `\ignorespaces|else|\unskip' or`\&{otherwise}' or `\\{otherwise}:', etc. The definitions of |othercases|and |endcases| should be changed to agree with local conventions. (Ofcourse, if no default mechanism is available, the |case| statements ofthis program must be extended by listing all remaining cases. The authorwould have taken the trouble to modify \.{TANGLE} so that such extensionswere done automatically, if he had not wanted to encourage \PASCAL\compiler writers to make this important change in \PASCAL, where it belongs.) @d othercases == others: {default for cases not listed explicitly}@d endcases == @+end {follows the default case in an extended |case| statement}@f othercases == else@f endcases == end @ The following parameters are set big enough to handle \TeX, so theyshould be sufficient for most applications of \.{TANGLE}. @<Constants...@>=@!buf_size=100; {maximum length of input line}@!max_bytes=45000; {|1/ww| times the number of bytes in identifiers, strings, and module names; must be less than 65536}@!max_toks=50000; {|1/zz| times the number of bytes in compressed \PASCAL\ code; must be less than 65536}@!max_names=4000; {number of identifiers, strings, module names; must be less than 10240}@!max_texts=2000; {number of replacement texts, must be less than 10240}@!hash_size=353; {should be prime}@!longest_name=400; {module names shouldn't be longer than this}@!line_length=72; {lines of \PASCAL\ output have at most this many characters}@!out_buf_size=144; {length of output buffer, should be twice |line_length|}@!stack_size=50; {number of simultaneous levels of macro expansion}@!max_id_length=12; {long identifiers are chopped to this length, which must not exceed |line_length|}@!unambig_length=7; {identifiers must be unique if chopped to this length} {note that 7 is more strict than \PASCAL's 8, but this can be varied} @ A global variable called |history| will contain one of four valuesat the end of every run: |spotless| means that no unusual messages wereprinted; |harmless_message| means that a message of possible interestwas printed but no serious errors were detected; |error_message| means thatat least one error was found; |fatal_message| means that the programterminated abnormally. The value of |history| does not influence thebehavior of the program; it is simply computed for the convenienceof systems that might want to use such information. @d spotless=0 {|history| value for normal jobs}@d harmless_message=1 {|history| value when non-serious info was printed}@d error_message=2 {|history| value when an error was noted}@d fatal_message=3 {|history| value when we had to stop prematurely}@#@d mark_harmless==@t@>@+if history=spotless then history:=harmless_message@d mark_error==history:=error_message@d mark_fatal==history:=fatal_message @<Glob...@>=@!history:spotless..fatal_message; {how bad was this run?} @ @<Set init...@>=history:=spotless; @* The character set.One of the main goals in the design of \.{WEB} has been to make it readilyportable between a wide variety of computers. Yet \.{WEB} by its verynature must use a greater variety of characters than most computerprograms deal with, and character encoding is one of the areas in whichexisting machines differ most widely from each other. To resolve this problem, all input to \.{WEAVE} and \.{TANGLE} is convertedto an internal eight-bit code that is essentially standard ASCII, the ``AmericanStandard Code for Information Interchange.'' The conversion is doneimmediately when each character is read in. Conversely, characters areconverted from ASCII to the user's external representation just beforethey are output. (The original ASCII code was seven bits only; \.{WEB} nowallows eight bits in an attempt to keep up with modern times.) Such an internal code is relevant to users of \.{WEB} only because it isthe code used for preprocessed constants like \.{"A"}. If you are writinga program in \.{WEB} that makes use of such one-character constants, youshould convert your input to ASCII form, like \.{WEAVE} and \.{TANGLE} do.Otherwise \.{WEB}'s internal coding scheme does not affect you.@^ASCII code@> Here is a table of the standard visible ASCII codes:$$\def\:{\char\count255\global\advance\count255 by 1}\count255='40\vbox{\hbox{\hbox to 40pt{\it\hfill0\/\hfill}%\hbox to 40pt{\it\hfill1\/\hfill}%\hbox to 40pt{\it\hfill2\/\hfill}%\hbox to 40pt{\it\hfill3\/\hfill}%\hbox to 40pt{\it\hfill4\/\hfill}%\hbox to 40pt{\it\hfill5\/\hfill}%\hbox to 40pt{\it\hfill6\/\hfill}%\hbox to 40pt{\it\hfill7\/\hfill}}\vskip 4pt\hrule\def\^{\vrule height 10.5pt depth 4.5pt}\halign{\hbox to 0pt{\hskip -24pt\O{#0}\hfill}&\^\hbox to 40pt{\tt\hfill#\hfill\^}&&\hbox to 40pt{\tt\hfill#\hfill\^}\cr04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}17&\:&\:&\:&\:&\:&\:&\:\cr}\hrule width 280pt}$$(Actually, of course, code @'040 is an invisible blank space.) Code @'136was once an upward arrow (\.{\char'13}), and code @'137 wasonce a left arrow (\.^^X), in olden times when the first draftof ASCII code was prepared; but \.{WEB} works with today's standardASCII in which those codes represent circumflex and underline as shown. @<Types...@>=@!ASCII_code=0..255; {eight-bit numbers, a subrange of the integers} @ The original \PASCAL\ compiler was designed in the late 60s, when six-bitcharacter sets were common, so it did not make provision for lowercaseletters. Nowadays, of course, we need to deal with both capital and smallletters in a convenient way, so \.{WEB} assumes that it is being usedwith a \PASCAL\ whose character set contains at least the characters ofstandard ASCII as listed above. Some \PASCAL\ compilers use the originalname |char| for the data type associated with the characters in text files,while other \PASCAL s consider |char| to be a 64-element subrange of a largerdata type that has some other name. In order to accommodate this difference, we shall use the name |text_char|to stand for the data type of the characters in the input and outputfiles. We shall also assume that |text_char| consists of the elements|chr(first_text_char)| through |chr(last_text_char)|, inclusive. Thefollowing definitions should be adjusted if necessary.@^system dependencies@> @d text_char == char {the data type of characters in text files}@d first_text_char=0 {ordinal number of the smallest element of |text_char|}@d last_text_char=255 {ordinal number of the largest element of |text_char|} @<Types...@>=@!text_file=packed file of text_char; @ The \.{WEAVE} and \.{TANGLE} processors convert between ASCII code andthe user's external character set by means of arrays |xord| and |xchr|that are analogous to \PASCAL's |ord| and |chr| functions. @<Globals...@>=@!xord: array [text_char] of ASCII_code; {specifies conversion of input characters}@!xchr: array [ASCII_code] of text_char; {specifies conversion of output characters} @ If we assume that every system using \.{WEB} is able to read and write thevisible characters of standard ASCII (although not necessarily using theASCII codes to represent them), the following assignment statements initializemost of the |xchr| array properly, without needing any system-dependentchanges. For example, the statement \.{xchr[@@\'101]:=\'A\'} that appearsin the present \.{WEB} file might be encoded in, say, {\mc EBCDIC} codeon the external medium on which it resides, but \.{TANGLE} will convert fromthis external code to ASCII and back again. Therefore the assignmentstatement \.{XCHR[65]:=\'A\'} will appear in the corresponding \PASCAL\ file,and \PASCAL\ will compile this statement so that |xchr[65]| receives thecharacter \.A in the external (|char|) code. Note that it would be quiteincorrect to say \.{xchr[@@\'101]:="A"}, because |"A"| is a constant oftype |integer|, not |char|, and because we have $|"A"|=65$ regardless ofthe external character set. @<Set init...@>=xchr[@'40]:=' ';xchr[@'41]:='!';xchr[@'42]:='"';xchr[@'43]:='#';xchr[@'44]:='$';xchr[@'45]:='%';xchr[@'46]:='&';xchr[@'47]:='''';@/xchr[@'50]:='(';xchr[@'51]:=')';xchr[@'52]:='*';xchr[@'53]:='+';xchr[@'54]:=',';xchr[@'55]:='-';xchr[@'56]:='.';xchr[@'57]:='/';@/xchr[@'60]:='0';xchr[@'61]:='1';xchr[@'62]:='2';xchr[@'63]:='3';xchr[@'64]:='4';xchr[@'65]:='5';xchr[@'66]:='6';xchr[@'67]:='7';@/xchr[@'70]:='8';xchr[@'71]:='9';xchr[@'72]:=':';xchr[@'73]:=';';xchr[@'74]:='<';xchr[@'75]:='=';xchr[@'76]:='>';xchr[@'77]:='?';@/xchr[@'100]:='@@';xchr[@'101]:='A';xchr[@'102]:='B';xchr[@'103]:='C';xchr[@'104]:='D';xchr[@'105]:='E';xchr[@'106]:='F';xchr[@'107]:='G';@/xchr[@'110]:='H';xchr[@'111]:='I';xchr[@'112]:='J';xchr[@'113]:='K';xchr[@'114]:='L';xchr[@'115]:='M';xchr[@'116]:='N';xchr[@'117]:='O';@/xchr[@'120]:='P';xchr[@'121]:='Q';xchr[@'122]:='R';xchr[@'123]:='S';xchr[@'124]:='T';xchr[@'125]:='U';xchr[@'126]:='V';xchr[@'127]:='W';@/xchr[@'130]:='X';xchr[@'131]:='Y';xchr[@'132]:='Z';xchr[@'133]:='[';xchr[@'134]:='\';xchr[@'135]:=']';xchr[@'136]:='^';xchr[@'137]:='_';@/xchr[@'140]:='`';xchr[@'141]:='a';xchr[@'142]:='b';xchr[@'143]:='c';xchr[@'144]:='d';xchr[@'145]:='e';xchr[@'146]:='f';xchr[@'147]:='g';@/xchr[@'150]:='h';xchr[@'151]:='i';xchr[@'152]:='j';xchr[@'153]:='k';xchr[@'154]:='l';xchr[@'155]:='m';xchr[@'156]:='n';xchr[@'157]:='o';@/xchr[@'160]:='p';xchr[@'161]:='q';xchr[@'162]:='r';xchr[@'163]:='s';xchr[@'164]:='t';xchr[@'165]:='u';xchr[@'166]:='v';xchr[@'167]:='w';@/xchr[@'170]:='x';xchr[@'171]:='y';xchr[@'172]:='z';xchr[@'173]:='{';xchr[@'174]:='|';xchr[@'175]:='}';xchr[@'176]:='~';@/xchr[0]:=' '; xchr[@'177]:=' '; {these ASCII codes are not used} @ Some of the ASCII codes below @'40 have been given symbolic names in\.{WEAVE} and \.{TANGLE} because they are used with a special meaning. @d and_sign=@'4 {equivalent to `\.{and}'}@d not_sign=@'5 {equivalent to `\.{not}'}@d set_element_sign=@'6 {equivalent to `\.{in}'}@d tab_mark=@'11 {ASCII code used as tab-skip}@d line_feed=@'12 {ASCII code thrown away at end of line}@d form_feed=@'14 {ASCII code used at end of page}@d carriage_return=@'15 {ASCII code used at end of line}@d left_arrow=@'30 {equivalent to `\.{:=}'}@d not_equal=@'32 {equivalent to `\.{<>}'}@d less_or_equal=@'34 {equivalent to `\.{<=}'}@d greater_or_equal=@'35 {equivalent to `\.{>=}'}@d equivalence_sign=@'36 {equivalent to `\.{==}'}@d or_sign=@'37 {equivalent to `\.{or}'} @ When we initialize the |xord| array and the remaining parts of |xchr|,it will be convenient to make use of an index variable, |i|. @<Local variables for init...@>=@!i:0..255; @ Here now is the system-dependent part of the character set.If \.{WEB} is being implemented on a garden-variety \PASCAL\ for whichonly standard ASCII codes will appear in the input and output files, youdon't need to make any changes here. But if you have, for example, an extendedcharacter set like the one in Appendix~C of {\sl The \TeX book}, the firstline of code in this module should be changed to$$\hbox{|for i:=1 to @'37 do xchr[i]:=chr(i);|}$$\.{WEB}'s character set is essentially identical to \TeX's, even with respect tocharacters less than @'40.@^system dependencies@> Changes to the present module will make \.{WEB} more friendly on computersthat have an extended character set, so that one can type things like\.^^Z\ instead of \.{<>}. If you have an extended set of characters thatare easily incorporated into text files, you can assign codes arbitrarilyhere, giving an |xchr| equivalent to whatever characters the users of\.{WEB} are allowed to have in their input files, provided that unsuitablecharacters do not correspond to special codes like |carriage_return|that are listed above. (The present file \.{TANGLE.WEB} does not contain any of the non-ASCIIcharacters, because it is intended to be used with all implementations of\.{WEB}. It was originally created on a Stanford system that has aconvenient extended character set, then ``sanitized'' by applying anotherprogram that transliterated all of the non-standard characters intostandard equivalents.) @<Set init...@>=for i:=1 to @'37 do xchr[i]:=' ';for i:=@'200 to @'377 do xchr[i]:=' '; @ The following system-independent code makes the |xord| array contain asuitable inverse to the information in |xchr|. @<Set init...@>=for i:=first_text_char to last_text_char do xord[chr(i)]:=" ";for i:=1 to @'377 do xord[xchr[i]]:=i;xord[' ']:=" "; @* Input and output.The input conventions of this program are intended to be very much like thoseof \TeX\ (except, of course, that they are much simpler, because much lessneeds to be done). Furthermore they are identical to those of \.{WEAVE}.Therefore people who need to make modifications to all three systemsshould be able to do so without too many headaches. We use the standard \PASCAL\ input/output procedures in several places that\TeX\ cannot, since \.{TANGLE} does not have to deal with files that are nameddynamically by the user, and since there is no input from the terminal.Lines 1–500 of 3315. Load more
% This program by D. E. Knuth is not copyrighted and can be used freely. % Version 0 was released in December, 1981. % Version 1 was released in September, 1982, with version 0 of TeX. % Slight changes were made in October, 1982, for version 0.6 of TeX. % Version 1.2 added @@= and @@\ and introduced {:nnn} comments (December, 1982). % Version 1.4 added "history" (February, 1983). % Version 1.5 conformed to TeX version 0.96 and fixed @@\ (March, 1983). % Version 1.7 introduced the new change file format (June, 1983). % Version 2.0 was released in July, 1983, with version 0.999 of TeX. % Version 2.5 was released in November, 1983, with version 1.0 of TeX. % Version 2.6 fixed a bug: force-line-break after a constant (August, 1984). % Version 2.7 fixed the definition of check_sum_prime (May, 1985). % Version 2.8 fixed a bug in change_buffer movement (August, 1985). % Version 2.9 allows nonnumeric macros before their def (December, 1988). % Version 3, for Sewell's book, fixed long-line bug in input_ln (March, 1989). % Version 4 was major change to allow 8-bit input (September, 1989). % Version 4.1 conforms to ANSI standard for-loop rules (September, 1990). % Version 4.2 fixes stat report if phase one dies (March, 1991). % Version 4.3 fixes @@ bug in verbatim, catches extra } (September, 1991). % Here is TeX material that gets inserted after \input webmac \def\hang{\hangindent 3em\indent\ignorespaces} \font\ninerm=cmr9 \let\mc=\ninerm % medium caps for names like SAIL \def\PASCAL{Pascal} \def\pb{$\.|\ldots\.|$} % Pascal brackets (|...|) \def\v{\.{\char'174}} % vertical (|) in typewriter font \mathchardef\BA="3224 % double arrow \def\({} % kludge for alphabetizing certain module names \def\title{TANGLE} \def\contentspagenumber{123} % should be odd \def\topofcontents{\null\vfill \titlefalse % include headline on the contents page \def\rheader{\mainfont Appendix E\hfil \contentspagenumber} \centerline{\titlefont The {\ttitlefont TANGLE} processor} \vskip 15pt \centerline{(Version 4.3)} \vfill} \pageno=\contentspagenumber \advance\pageno by 1 @* Introduction. This program converts a \.{WEB} file to a \PASCAL\ file. It was written by D. E. Knuth in September, 1981; a somewhat similar {\mc SAIL} program had been developed in March, 1979. Since this program describes itself, a bootstrapping process involving hand-translation had to be used to get started. For large \.{WEB} files one should have a large memory, since \.{TANGLE} keeps all the \PASCAL\ text in memory (in an abbreviated form). The program uses a few features of the local \PASCAL\ compiler that may need to be changed in other installations: \yskip\item{1)} Case statements have a default. \item{2)} Input-output routines may need to be adapted for use with a particular character set and/or for printing messages on the user's terminal. \yskip\noindent These features are also present in the \PASCAL\ version of \TeX, where they are used in a similar (but more complex) way. System-dependent portions of \.{TANGLE} can be identified by looking at the entries for `system dependencies' in the index below. @!@^system dependencies@> The ``banner line'' defined here should be changed whenever \.{TANGLE} is modified. @d banner=='This is TANGLE, Version 4.3' @ The program begins with a fairly normal header, made up of pieces that @^system dependencies@> will mostly be filled in later. The \.{WEB} input comes from files |web_file| and |change_file|, the \PASCAL\ output goes to file |Pascal_file|, and the string pool output goes to file |pool|. If it is necessary to abort the job because of a fatal error, the program calls the `|jump_out|' procedure, which goes to the label |end_of_TANGLE|. @d end_of_TANGLE = 9999 {go here to wrap it up} @p @t\4@>@<Compiler directives@>@/ program TANGLE(@!web_file,@!change_file,@!Pascal_file,@!pool); label end_of_TANGLE; {go here to finish} const @<Constants in the outer block@>@/ type @<Types in the outer block@>@/ var @<Globals in the outer block@>@/ @<Error handling procedures@>@/ procedure initialize; var @<Local variables for initialization@>@/ begin @<Set initial values@>@/ end; @ Some of this code is optional for use when debugging only; such material is enclosed between the delimiters |debug| and $|gubed|$. Other parts, delimited by |stat| and $|tats|$, are optionally included if statistics about \.{TANGLE}'s memory usage are desired. @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} @f debug==begin @f gubed==end @# @d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering usage statistics} @d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering usage statistics} @f stat==begin @f tats==end @ The \PASCAL\ compiler used to develop this system has ``compiler directives'' that can appear in comments whose first character is a dollar sign. In production versions of \.{TANGLE} these directives tell the compiler that @^system dependencies@> it is safe to avoid range checks and to leave out the extra code it inserts for the \PASCAL\ debugger's benefit, although interrupts will occur if there is arithmetic overflow. @<Compiler directives@>= @{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead} @!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging} @ Labels are given symbolic names by the following definitions. We insert the label `|exit|:' just before the `\ignorespaces|end|\unskip' of a procedure in which we have used the `|return|' statement defined below; the label `|restart|' is occasionally used at the very beginning of a procedure; and the label `|reswitch|' is occasionally used just prior to a \&{case} statement in which some cases change the conditions and we wish to branch to the newly applicable case. Loops that are set up with the \&{loop} construction defined below are commonly exited by going to `|done|' or to `|found|' or to `|not_found|', and they are sometimes repeated by going to `|continue|'. @d exit=10 {go here to leave a procedure} @d restart=20 {go here to start a procedure again} @d reswitch=21 {go here to start a case statement again} @d continue=22 {go here to resume a loop} @d done=30 {go here to exit a loop} @d found=31 {go here when you've found it} @d not_found=32 {go here when you've found something else} @ Here are some macros for common programming idioms. @d incr(#) == #:=#+1 {increase a variable by unity} @d decr(#) == #:=#-1 {decrease a variable by unity} @d loop == @+ while true do@+ {repeat over and over until a |goto| happens} @d do_nothing == {empty statement} @d return == goto exit {terminate a procedure call} @f return == nil @f loop == xclause @ We assume that |case| statements may include a default case that applies if no matching label is found. Thus, we shall use constructions like @^system dependencies@> $$\vbox{\halign{#\hfil\cr |case x of|\cr 1: $\langle\,$code for $x=1\,\rangle$;\cr 3: $\langle\,$code for $x=3\,\rangle$;\cr |othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr |endcases|\cr}}$$ since most \PASCAL\ compilers have plugged this hole in the language by incorporating some sort of default mechanism. For example, the compiler used to develop \.{WEB} and \TeX\ allows `|others|:' as a default label, and other \PASCAL s allow syntaxes like `\ignorespaces|else|\unskip' or `\&{otherwise}' or `\\{otherwise}:', etc. The definitions of |othercases| and |endcases| should be changed to agree with local conventions. (Of course, if no default mechanism is available, the |case| statements of this program must be extended by listing all remaining cases. The author would have taken the trouble to modify \.{TANGLE} so that such extensions were done automatically, if he had not wanted to encourage \PASCAL\ compiler writers to make this important change in \PASCAL, where it belongs.) @d othercases == others: {default for cases not listed explicitly} @d endcases == @+end {follows the default case in an extended |case| statement} @f othercases == else @f endcases == end @ The following parameters are set big enough to handle \TeX, so they should be sufficient for most applications of \.{TANGLE}. @<Constants...@>= @!buf_size=100; {maximum length of input line} @!max_bytes=45000; {|1/ww| times the number of bytes in identifiers, strings, and module names; must be less than 65536} @!max_toks=50000; {|1/zz| times the number of bytes in compressed \PASCAL\ code; must be less than 65536} @!max_names=4000; {number of identifiers, strings, module names; must be less than 10240} @!max_texts=2000; {number of replacement texts, must be less than 10240} @!hash_size=353; {should be prime} @!longest_name=400; {module names shouldn't be longer than this} @!line_length=72; {lines of \PASCAL\ output have at most this many characters} @!out_buf_size=144; {length of output buffer, should be twice |line_length|} @!stack_size=50; {number of simultaneous levels of macro expansion} @!max_id_length=12; {long identifiers are chopped to this length, which must not exceed |line_length|} @!unambig_length=7; {identifiers must be unique if chopped to this length} {note that 7 is more strict than \PASCAL's 8, but this can be varied} @ A global variable called |history| will contain one of four values at the end of every run: |spotless| means that no unusual messages were printed; |harmless_message| means that a message of possible interest was printed but no serious errors were detected; |error_message| means that at least one error was found; |fatal_message| means that the program terminated abnormally. The value of |history| does not influence the behavior of the program; it is simply computed for the convenience of systems that might want to use such information. @d spotless=0 {|history| value for normal jobs} @d harmless_message=1 {|history| value when non-serious info was printed} @d error_message=2 {|history| value when an error was noted} @d fatal_message=3 {|history| value when we had to stop prematurely} @# @d mark_harmless==@t@>@+if history=spotless then history:=harmless_message @d mark_error==history:=error_message @d mark_fatal==history:=fatal_message @<Glob...@>=@!history:spotless..fatal_message; {how bad was this run?} @ @<Set init...@>=history:=spotless; @* The character set. One of the main goals in the design of \.{WEB} has been to make it readily portable between a wide variety of computers. Yet \.{WEB} by its very nature must use a greater variety of characters than most computer programs deal with, and character encoding is one of the areas in which existing machines differ most widely from each other. To resolve this problem, all input to \.{WEAVE} and \.{TANGLE} is converted to an internal eight-bit code that is essentially standard ASCII, the ``American Standard Code for Information Interchange.'' The conversion is done immediately when each character is read in. Conversely, characters are converted from ASCII to the user's external representation just before they are output. (The original ASCII code was seven bits only; \.{WEB} now allows eight bits in an attempt to keep up with modern times.) Such an internal code is relevant to users of \.{WEB} only because it is the code used for preprocessed constants like \.{"A"}. If you are writing a program in \.{WEB} that makes use of such one-character constants, you should convert your input to ASCII form, like \.{WEAVE} and \.{TANGLE} do. Otherwise \.{WEB}'s internal coding scheme does not affect you. @^ASCII code@> Here is a table of the standard visible ASCII codes: $$\def\:{\char\count255\global\advance\count255 by 1} \count255='40 \vbox{ \hbox{\hbox to 40pt{\it\hfill0\/\hfill}% \hbox to 40pt{\it\hfill1\/\hfill}% \hbox to 40pt{\it\hfill2\/\hfill}% \hbox to 40pt{\it\hfill3\/\hfill}% \hbox to 40pt{\it\hfill4\/\hfill}% \hbox to 40pt{\it\hfill5\/\hfill}% \hbox to 40pt{\it\hfill6\/\hfill}% \hbox to 40pt{\it\hfill7\/\hfill}} \vskip 4pt \hrule \def\^{\vrule height 10.5pt depth 4.5pt} \halign{\hbox to 0pt{\hskip -24pt\O{#0}\hfill}&\^ \hbox to 40pt{\tt\hfill#\hfill\^}& &\hbox to 40pt{\tt\hfill#\hfill\^}\cr 04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} 17&\:&\:&\:&\:&\:&\:&\:\cr} \hrule width 280pt}$$ (Actually, of course, code @'040 is an invisible blank space.) Code @'136 was once an upward arrow (\.{\char'13}), and code @'137 was once a left arrow (\.^^X), in olden times when the first draft of ASCII code was prepared; but \.{WEB} works with today's standard ASCII in which those codes represent circumflex and underline as shown. @<Types...@>= @!ASCII_code=0..255; {eight-bit numbers, a subrange of the integers} @ The original \PASCAL\ compiler was designed in the late 60s, when six-bit character sets were common, so it did not make provision for lowercase letters. Nowadays, of course, we need to deal with both capital and small letters in a convenient way, so \.{WEB} assumes that it is being used with a \PASCAL\ whose character set contains at least the characters of standard ASCII as listed above. Some \PASCAL\ compilers use the original name |char| for the data type associated with the characters in text files, while other \PASCAL s consider |char| to be a 64-element subrange of a larger data type that has some other name. In order to accommodate this difference, we shall use the name |text_char| to stand for the data type of the characters in the input and output files. We shall also assume that |text_char| consists of the elements |chr(first_text_char)| through |chr(last_text_char)|, inclusive. The following definitions should be adjusted if necessary. @^system dependencies@> @d text_char == char {the data type of characters in text files} @d first_text_char=0 {ordinal number of the smallest element of |text_char|} @d last_text_char=255 {ordinal number of the largest element of |text_char|} @<Types...@>= @!text_file=packed file of text_char; @ The \.{WEAVE} and \.{TANGLE} processors convert between ASCII code and the user's external character set by means of arrays |xord| and |xchr| that are analogous to \PASCAL's |ord| and |chr| functions. @<Globals...@>= @!xord: array [text_char] of ASCII_code; {specifies conversion of input characters} @!xchr: array [ASCII_code] of text_char; {specifies conversion of output characters} @ If we assume that every system using \.{WEB} is able to read and write the visible characters of standard ASCII (although not necessarily using the ASCII codes to represent them), the following assignment statements initialize most of the |xchr| array properly, without needing any system-dependent changes. For example, the statement \.{xchr[@@\'101]:=\'A\'} that appears in the present \.{WEB} file might be encoded in, say, {\mc EBCDIC} code on the external medium on which it resides, but \.{TANGLE} will convert from this external code to ASCII and back again. Therefore the assignment statement \.{XCHR[65]:=\'A\'} will appear in the corresponding \PASCAL\ file, and \PASCAL\ will compile this statement so that |xchr[65]| receives the character \.A in the external (|char|) code. Note that it would be quite incorrect to say \.{xchr[@@\'101]:="A"}, because |"A"| is a constant of type |integer|, not |char|, and because we have $|"A"|=65$ regardless of the external character set. @<Set init...@>= xchr[@'40]:=' '; xchr[@'41]:='!'; xchr[@'42]:='"'; xchr[@'43]:='#'; xchr[@'44]:='$'; xchr[@'45]:='%'; xchr[@'46]:='&'; xchr[@'47]:='''';@/ xchr[@'50]:='('; xchr[@'51]:=')'; xchr[@'52]:='*'; xchr[@'53]:='+'; xchr[@'54]:=','; xchr[@'55]:='-'; xchr[@'56]:='.'; xchr[@'57]:='/';@/ xchr[@'60]:='0'; xchr[@'61]:='1'; xchr[@'62]:='2'; xchr[@'63]:='3'; xchr[@'64]:='4'; xchr[@'65]:='5'; xchr[@'66]:='6'; xchr[@'67]:='7';@/ xchr[@'70]:='8'; xchr[@'71]:='9'; xchr[@'72]:=':'; xchr[@'73]:=';'; xchr[@'74]:='<'; xchr[@'75]:='='; xchr[@'76]:='>'; xchr[@'77]:='?';@/ xchr[@'100]:='@@'; xchr[@'101]:='A'; xchr[@'102]:='B'; xchr[@'103]:='C'; xchr[@'104]:='D'; xchr[@'105]:='E'; xchr[@'106]:='F'; xchr[@'107]:='G';@/ xchr[@'110]:='H'; xchr[@'111]:='I'; xchr[@'112]:='J'; xchr[@'113]:='K'; xchr[@'114]:='L'; xchr[@'115]:='M'; xchr[@'116]:='N'; xchr[@'117]:='O';@/ xchr[@'120]:='P'; xchr[@'121]:='Q'; xchr[@'122]:='R'; xchr[@'123]:='S'; xchr[@'124]:='T'; xchr[@'125]:='U'; xchr[@'126]:='V'; xchr[@'127]:='W';@/ xchr[@'130]:='X'; xchr[@'131]:='Y'; xchr[@'132]:='Z'; xchr[@'133]:='['; xchr[@'134]:='\'; xchr[@'135]:=']'; xchr[@'136]:='^'; xchr[@'137]:='_';@/ xchr[@'140]:='`'; xchr[@'141]:='a'; xchr[@'142]:='b'; xchr[@'143]:='c'; xchr[@'144]:='d'; xchr[@'145]:='e'; xchr[@'146]:='f'; xchr[@'147]:='g';@/ xchr[@'150]:='h'; xchr[@'151]:='i'; xchr[@'152]:='j'; xchr[@'153]:='k'; xchr[@'154]:='l'; xchr[@'155]:='m'; xchr[@'156]:='n'; xchr[@'157]:='o';@/ xchr[@'160]:='p'; xchr[@'161]:='q'; xchr[@'162]:='r'; xchr[@'163]:='s'; xchr[@'164]:='t'; xchr[@'165]:='u'; xchr[@'166]:='v'; xchr[@'167]:='w';@/ xchr[@'170]:='x'; xchr[@'171]:='y'; xchr[@'172]:='z'; xchr[@'173]:='{'; xchr[@'174]:='|'; xchr[@'175]:='}'; xchr[@'176]:='~';@/ xchr[0]:=' '; xchr[@'177]:=' '; {these ASCII codes are not used} @ Some of the ASCII codes below @'40 have been given symbolic names in \.{WEAVE} and \.{TANGLE} because they are used with a special meaning. @d and_sign=@'4 {equivalent to `\.{and}'} @d not_sign=@'5 {equivalent to `\.{not}'} @d set_element_sign=@'6 {equivalent to `\.{in}'} @d tab_mark=@'11 {ASCII code used as tab-skip} @d line_feed=@'12 {ASCII code thrown away at end of line} @d form_feed=@'14 {ASCII code used at end of page} @d carriage_return=@'15 {ASCII code used at end of line} @d left_arrow=@'30 {equivalent to `\.{:=}'} @d not_equal=@'32 {equivalent to `\.{<>}'} @d less_or_equal=@'34 {equivalent to `\.{<=}'} @d greater_or_equal=@'35 {equivalent to `\.{>=}'} @d equivalence_sign=@'36 {equivalent to `\.{==}'} @d or_sign=@'37 {equivalent to `\.{or}'} @ When we initialize the |xord| array and the remaining parts of |xchr|, it will be convenient to make use of an index variable, |i|. @<Local variables for init...@>= @!i:0..255; @ Here now is the system-dependent part of the character set. If \.{WEB} is being implemented on a garden-variety \PASCAL\ for which only standard ASCII codes will appear in the input and output files, you don't need to make any changes here. But if you have, for example, an extended character set like the one in Appendix~C of {\sl The \TeX book}, the first line of code in this module should be changed to $$\hbox{|for i:=1 to @'37 do xchr[i]:=chr(i);|}$$ \.{WEB}'s character set is essentially identical to \TeX's, even with respect to characters less than @'40. @^system dependencies@> Changes to the present module will make \.{WEB} more friendly on computers that have an extended character set, so that one can type things like \.^^Z\ instead of \.{<>}. If you have an extended set of characters that are easily incorporated into text files, you can assign codes arbitrarily here, giving an |xchr| equivalent to whatever characters the users of \.{WEB} are allowed to have in their input files, provided that unsuitable characters do not correspond to special codes like |carriage_return| that are listed above. (The present file \.{TANGLE.WEB} does not contain any of the non-ASCII characters, because it is intended to be used with all implementations of \.{WEB}. It was originally created on a Stanford system that has a convenient extended character set, then ``sanitized'' by applying another program that transliterated all of the non-standard characters into standard equivalents.) @<Set init...@>= for i:=1 to @'37 do xchr[i]:=' '; for i:=@'200 to @'377 do xchr[i]:=' '; @ The following system-independent code makes the |xord| array contain a suitable inverse to the information in |xchr|. @<Set init...@>= for i:=first_text_char to last_text_char do xord[chr(i)]:=" "; for i:=1 to @'377 do xord[xchr[i]]:=i; xord[' ']:=" "; @* Input and output. The input conventions of this program are intended to be very much like those of \TeX\ (except, of course, that they are much simpler, because much less needs to be done). Furthermore they are identical to those of \.{WEAVE}. Therefore people who need to make modifications to all three systems should be able to do so without too many headaches. We use the standard \PASCAL\ input/output procedures in several places that \TeX\ cannot, since \.{TANGLE} does not have to deal with files that are named dynamically by the user, and since there is no input from the terminal.